summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig21
-rw-r--r--.gitignore25
-rw-r--r--.husky/pre-commit1
-rw-r--r--.well-known/atproto-did1
-rw-r--r--README.md62
-rw-r--r--package.json56
-rw-r--r--sass/global.scss28
-rw-r--r--sass/uchu/_base.scss178
-rw-r--r--sass/uchu/_form.scss77
-rw-r--r--sass/uchu/_reset.scss708
-rw-r--r--sass/uchu/core/_animation.scss23
-rw-r--r--sass/uchu/core/_mixin.scss84
-rw-r--r--sass/uchu/core/_root.scss38
-rw-r--r--sass/uchu/uchu.scss4
-rw-r--r--src/app.html66
-rw-r--r--src/lib/component/About.svelte98
-rw-r--r--src/lib/component/Blog.svelte244
-rw-r--r--src/lib/component/CV.svelte78
-rw-r--r--src/lib/component/Music.svelte354
-rw-r--r--src/lib/component/MyMusic.svelte173
-rw-r--r--src/lib/component/Project.svelte241
-rw-r--r--src/lib/component/Promo.svelte66
-rw-r--r--src/lib/component/Remarks.svelte179
-rw-r--r--src/lib/component/Ring.svelte312
-rw-r--r--src/lib/component/Social.svelte112
-rw-r--r--src/lib/component/Support.svelte70
-rw-r--r--src/lib/component/Uses.svelte145
-rw-r--r--src/routes/+layout.svelte147
-rw-r--r--src/routes/+page.svelte115
-rw-r--r--src/routes/api/blog.json/+server.ts51
-rw-r--r--src/routes/api/cv.json/+server.ts75
-rw-r--r--src/routes/api/mastodon.json/+server.ts55
-rw-r--r--src/routes/api/remarks.json/+server.ts51
-rw-r--r--static/88x31/a.gifbin0 -> 2353 bytes
-rw-r--r--static/88x31/b.gifbin0 -> 3226 bytes
-rw-r--r--static/88x31/c.gifbin0 -> 70729 bytes
-rw-r--r--static/88x31/d.pngbin0 -> 666 bytes
-rw-r--r--static/88x31/elle.webpbin0 -> 1348 bytes
-rw-r--r--static/apple-touch-icon.pngbin0 -> 317 bytes
-rw-r--r--static/base.css201
-rw-r--r--static/blogroll.opml121
-rw-r--r--static/favicon.svg1
-rwxr-xr-xstatic/humans.txt16
-rw-r--r--static/images/bandcamp/1085756242.jpgbin0 -> 70927 bytes
-rwxr-xr-xstatic/images/bandcamp/1120176299.jpgbin0 -> 1554355 bytes
-rw-r--r--static/images/bandcamp/1349219244.jpgbin0 -> 460947 bytes
-rw-r--r--static/images/bandcamp/1387178982.jpgbin0 -> 537155 bytes
-rw-r--r--static/images/bandcamp/1488235027.jpgbin0 -> 99101 bytes
-rwxr-xr-xstatic/images/bandcamp/1537466475.jpgbin0 -> 417574 bytes
-rw-r--r--static/images/bandcamp/1584151539.jpgbin0 -> 579079 bytes
-rwxr-xr-xstatic/images/bandcamp/1773399509.jpgbin0 -> 1935354 bytes
-rwxr-xr-xstatic/images/bandcamp/1870167199.jpgbin0 -> 1606170 bytes
-rwxr-xr-xstatic/images/bandcamp/1898404266.jpgbin0 -> 1436092 bytes
-rw-r--r--static/images/bandcamp/1931742968.jpgbin0 -> 422707 bytes
-rw-r--r--static/images/bandcamp/1972128885.jpgbin0 -> 160123 bytes
-rw-r--r--static/images/bandcamp/2006853030.jpgbin0 -> 306948 bytes
-rw-r--r--static/images/bandcamp/2247255572.jpgbin0 -> 149260 bytes
-rw-r--r--static/images/bandcamp/2376445182.jpgbin0 -> 359183 bytes
-rw-r--r--static/images/bandcamp/2493506758.jpgbin0 -> 432923 bytes
-rwxr-xr-xstatic/images/bandcamp/2629563639.jpgbin0 -> 502929 bytes
-rwxr-xr-xstatic/images/bandcamp/2689738227.jpgbin0 -> 2274805 bytes
-rw-r--r--static/images/bandcamp/2714432574.jpgbin0 -> 60522 bytes
-rw-r--r--static/images/bandcamp/2723488137.jpgbin0 -> 162316 bytes
-rw-r--r--static/images/bandcamp/2759787857.jpgbin0 -> 95898 bytes
-rwxr-xr-xstatic/images/bandcamp/2804828422.jpgbin0 -> 1630495 bytes
-rw-r--r--static/images/bandcamp/3091487133.jpgbin0 -> 204160 bytes
-rw-r--r--static/images/bandcamp/3211763084.jpgbin0 -> 79010 bytes
-rw-r--r--static/images/bandcamp/3230467359.jpgbin0 -> 651903 bytes
-rwxr-xr-xstatic/images/bandcamp/3332776732.jpgbin0 -> 1554355 bytes
-rw-r--r--static/images/bandcamp/3478474368.jpgbin0 -> 396222 bytes
-rw-r--r--static/images/bandcamp/3479413388.jpgbin0 -> 467885 bytes
-rw-r--r--static/images/bandcamp/3524388493.jpgbin0 -> 1800961 bytes
-rw-r--r--static/images/bandcamp/3603991383.jpgbin0 -> 662656 bytes
-rwxr-xr-xstatic/images/bandcamp/3624438326.jpgbin0 -> 1606178 bytes
-rw-r--r--static/images/bandcamp/3667963750.jpgbin0 -> 93070 bytes
-rw-r--r--static/images/bandcamp/3685680305.jpgbin0 -> 1070888 bytes
-rwxr-xr-xstatic/images/bandcamp/3794237863.jpgbin0 -> 488803 bytes
-rwxr-xr-xstatic/images/bandcamp/4001491411.jpgbin0 -> 957224 bytes
-rwxr-xr-xstatic/images/bandcamp/4182307863.jpgbin0 -> 1936355 bytes
-rw-r--r--static/images/bandcamp/4219807572.jpgbin0 -> 132861 bytes
-rwxr-xr-xstatic/images/bandcamp/422810680.jpgbin0 -> 2561417 bytes
-rwxr-xr-xstatic/images/bandcamp/4237561960.jpgbin0 -> 953689 bytes
-rwxr-xr-xstatic/images/bandcamp/435475786.jpgbin0 -> 237704 bytes
-rwxr-xr-xstatic/images/bandcamp/483965118.jpgbin0 -> 2050459 bytes
-rw-r--r--static/images/bandcamp/512177084.jpgbin0 -> 162169 bytes
-rw-r--r--static/images/bandcamp/635810172.jpgbin0 -> 317024 bytes
-rwxr-xr-xstatic/images/bandcamp/640549190.jpgbin0 -> 1232837 bytes
-rwxr-xr-xstatic/images/bandcamp/692190634.jpgbin0 -> 1015291 bytes
-rwxr-xr-xstatic/images/bandcamp/71750021.jpgbin0 -> 2159041 bytes
-rwxr-xr-xstatic/images/bandcamp/751538854.jpgbin0 -> 96227 bytes
-rwxr-xr-xstatic/images/bandcamp/803715127.jpgbin0 -> 1591858 bytes
-rwxr-xr-xstatic/images/bandcamp/953490707.jpgbin0 -> 1401312 bytes
-rw-r--r--static/images/bandcamp/955213907.jpgbin0 -> 392738 bytes
-rw-r--r--static/images/netopwibby/01.jpgbin0 -> 859169 bytes
-rw-r--r--static/images/netopwibby/02.jpgbin0 -> 2371705 bytes
-rw-r--r--static/images/netopwibby/03.jpgbin0 -> 273827 bytes
-rw-r--r--static/images/netopwibby/04.jpgbin0 -> 272383 bytes
-rw-r--r--static/images/netopwibby/05.jpgbin0 -> 214136 bytes
-rw-r--r--static/images/netopwibby/06.jpgbin0 -> 1151132 bytes
-rw-r--r--static/images/netopwibby/07.jpgbin0 -> 1909003 bytes
-rw-r--r--static/images/netopwibby/08.jpgbin0 -> 1496793 bytes
-rw-r--r--static/images/netopwibby/09.jpgbin0 -> 1023077 bytes
-rw-r--r--static/images/netopwibby/10.jpgbin0 -> 939911 bytes
-rw-r--r--static/images/netopwibby/11.jpgbin0 -> 1973589 bytes
-rw-r--r--static/images/netopwibby/12.jpgbin0 -> 2742084 bytes
-rw-r--r--static/images/netopwibby/13.jpgbin0 -> 4484825 bytes
-rw-r--r--static/images/projects/aries_01.jpgbin0 -> 391848 bytes
-rw-r--r--static/images/projects/aries_02.jpgbin0 -> 413359 bytes
-rw-r--r--static/images/projects/beachfront_01.jpgbin0 -> 1117645 bytes
-rw-r--r--static/images/projects/beachfront_02.jpgbin0 -> 802668 bytes
-rw-r--r--static/images/projects/chronver.jpgbin0 -> 1285403 bytes
-rw-r--r--static/images/projects/dap.jpgbin0 -> 1102205 bytes
-rw-r--r--static/images/projects/neuenet_01.jpgbin0 -> 551510 bytes
-rw-r--r--static/images/projects/neuenet_02.jpgbin0 -> 817449 bytes
-rw-r--r--static/images/projects/nickel_01.jpgbin0 -> 1222917 bytes
-rw-r--r--static/images/projects/socii_01.jpgbin0 -> 1097300 bytes
-rw-r--r--static/images/projects/uchu_01.jpgbin0 -> 573153 bytes
-rw-r--r--static/key.pub244
-rw-r--r--static/og.pngbin0 -> 1320332 bytes
-rw-r--r--static/robots.txt7
-rw-r--r--static/sounds/click/click_01.oggbin0 -> 10126 bytes
-rw-r--r--static/sounds/click/click_02.oggbin0 -> 9578 bytes
-rw-r--r--static/sounds/click/click_03.oggbin0 -> 5751 bytes
-rw-r--r--static/sounds/click/click_04.oggbin0 -> 5627 bytes
-rw-r--r--static/sounds/click/click_05.oggbin0 -> 5493 bytes
-rw-r--r--static/sounds/click/click_06.oggbin0 -> 5685 bytes
-rw-r--r--static/sounds/click/click_07.oggbin0 -> 5995 bytes
-rw-r--r--static/sounds/click/click_08.oggbin0 -> 5769 bytes
-rw-r--r--static/sounds/click/click_09.oggbin0 -> 5709 bytes
-rw-r--r--static/sounds/click/click_10.oggbin0 -> 5052 bytes
-rw-r--r--static/sounds/click/click_11.oggbin0 -> 6630 bytes
-rw-r--r--static/sounds/click/click_12.oggbin0 -> 6238 bytes
-rw-r--r--static/sounds/click/click_13.oggbin0 -> 8229 bytes
-rw-r--r--static/sounds/click/click_14.oggbin0 -> 5985 bytes
-rw-r--r--static/sounds/click/click_15.oggbin0 -> 5227 bytes
-rw-r--r--static/sounds/click/click_16.oggbin0 -> 6004 bytes
-rw-r--r--static/sounds/click/click_17.oggbin0 -> 6263 bytes
-rw-r--r--static/sounds/click/click_18.oggbin0 -> 5992 bytes
-rw-r--r--static/sounds/click/click_19.oggbin0 -> 7902 bytes
-rw-r--r--static/sounds/click/click_20.oggbin0 -> 6138 bytes
-rw-r--r--static/sounds/click/click_21.oggbin0 -> 6149 bytes
-rw-r--r--static/sounds/error/error_01.oggbin0 -> 15704 bytes
-rw-r--r--static/sounds/error/error_02.oggbin0 -> 11169 bytes
-rw-r--r--static/sounds/error/error_03.oggbin0 -> 12070 bytes
-rw-r--r--static/sounds/error/error_04.oggbin0 -> 9707 bytes
-rw-r--r--static/sounds/error/error_05.oggbin0 -> 11001 bytes
-rw-r--r--static/sounds/error/error_06.oggbin0 -> 13367 bytes
-rw-r--r--static/sounds/error/error_07.oggbin0 -> 19064 bytes
-rw-r--r--static/sounds/error/error_08.oggbin0 -> 13514 bytes
-rw-r--r--static/sounds/error/error_09.oggbin0 -> 13714 bytes
-rw-r--r--static/sounds/error/error_10.oggbin0 -> 7814 bytes
-rw-r--r--static/sounds/error/error_11.oggbin0 -> 9195 bytes
-rw-r--r--static/sounds/error/error_12.oggbin0 -> 8521 bytes
-rw-r--r--static/sounds/error/error_13.oggbin0 -> 12699 bytes
-rw-r--r--static/sounds/error/error_14.oggbin0 -> 14502 bytes
-rw-r--r--static/sounds/error/error_15.oggbin0 -> 11829 bytes
-rw-r--r--static/sounds/error/error_16.oggbin0 -> 10948 bytes
-rw-r--r--static/sounds/error/error_17.oggbin0 -> 13881 bytes
-rw-r--r--static/sounds/error/error_18.oggbin0 -> 12217 bytes
-rw-r--r--static/sounds/error/error_19.oggbin0 -> 15293 bytes
-rw-r--r--static/sounds/error/error_20.oggbin0 -> 16276 bytes
-rw-r--r--static/sounds/error/error_21.oggbin0 -> 14168 bytes
-rw-r--r--static/sounds/pop/pop_01.oggbin0 -> 13112 bytes
-rw-r--r--static/sounds/pop/pop_02.oggbin0 -> 17230 bytes
-rw-r--r--static/sounds/pop/pop_03.oggbin0 -> 15870 bytes
-rw-r--r--static/sounds/pop/pop_04.oggbin0 -> 9714 bytes
-rw-r--r--static/sounds/pop/pop_05.oggbin0 -> 9088 bytes
-rw-r--r--static/sounds/pop/pop_06.oggbin0 -> 9448 bytes
-rw-r--r--static/sounds/pop/pop_07.oggbin0 -> 6414 bytes
-rw-r--r--static/sounds/pop/pop_08.oggbin0 -> 6346 bytes
-rw-r--r--static/sounds/pop/pop_09.oggbin0 -> 6291 bytes
-rw-r--r--static/sounds/pop/pop_10.oggbin0 -> 9724 bytes
-rw-r--r--static/sounds/pop/pop_11.oggbin0 -> 9150 bytes
-rw-r--r--static/sounds/pop/pop_12.oggbin0 -> 9167 bytes
-rw-r--r--static/sounds/pop/pop_13.oggbin0 -> 9765 bytes
-rw-r--r--static/sounds/pop/pop_14.oggbin0 -> 8713 bytes
-rw-r--r--static/sounds/pop/pop_15.oggbin0 -> 9263 bytes
-rw-r--r--static/sounds/pop/pop_16.oggbin0 -> 11238 bytes
-rw-r--r--static/sounds/pop/pop_17.oggbin0 -> 12208 bytes
-rw-r--r--static/sounds/pop/pop_18.oggbin0 -> 13315 bytes
-rw-r--r--static/sounds/pop/pop_19.oggbin0 -> 10582 bytes
-rw-r--r--static/sounds/pop/pop_20.oggbin0 -> 9219 bytes
-rw-r--r--static/sounds/pop/pop_21.oggbin0 -> 9462 bytes
-rw-r--r--static/sounds/tadah/tadah_01.oggbin0 -> 22590 bytes
-rw-r--r--static/sounds/tadah/tadah_02.oggbin0 -> 29268 bytes
-rw-r--r--static/sounds/tadah/tadah_03.oggbin0 -> 27959 bytes
-rw-r--r--static/sounds/tadah/tadah_04.oggbin0 -> 21638 bytes
-rw-r--r--static/sounds/tadah/tadah_05.oggbin0 -> 25069 bytes
-rw-r--r--static/sounds/tadah/tadah_06.oggbin0 -> 31840 bytes
-rw-r--r--static/sounds/tadah/tadah_07.oggbin0 -> 23184 bytes
-rw-r--r--static/sounds/tadah/tadah_08.oggbin0 -> 20409 bytes
-rw-r--r--static/sounds/tadah/tadah_09.oggbin0 -> 30063 bytes
-rw-r--r--static/sounds/tadah/tadah_10.oggbin0 -> 19023 bytes
-rw-r--r--static/sounds/tadah/tadah_11.oggbin0 -> 21179 bytes
-rw-r--r--static/sounds/tadah/tadah_12.oggbin0 -> 19646 bytes
-rw-r--r--static/sounds/tadah/tadah_13.oggbin0 -> 20826 bytes
-rw-r--r--static/sounds/tadah/tadah_14.oggbin0 -> 24069 bytes
-rw-r--r--static/sounds/tadah/tadah_15.oggbin0 -> 19674 bytes
-rw-r--r--static/sounds/tadah/tadah_16.oggbin0 -> 11784 bytes
-rw-r--r--static/sounds/tadah/tadah_17.oggbin0 -> 24850 bytes
-rw-r--r--static/sounds/tadah/tadah_18.oggbin0 -> 18898 bytes
-rw-r--r--static/sounds/tadah/tadah_19.oggbin0 -> 20506 bytes
-rw-r--r--static/sounds/tadah/tadah_20.oggbin0 -> 25213 bytes
-rw-r--r--static/sounds/tadah/tadah_21.oggbin0 -> 29648 bytes
-rw-r--r--static/sounds/woosh/woosh_01.oggbin0 -> 10475 bytes
-rw-r--r--static/sounds/woosh/woosh_02.oggbin0 -> 9106 bytes
-rw-r--r--static/sounds/woosh/woosh_03.oggbin0 -> 14343 bytes
-rw-r--r--static/sounds/woosh/woosh_04.oggbin0 -> 10264 bytes
-rw-r--r--static/sounds/woosh/woosh_05.oggbin0 -> 10828 bytes
-rw-r--r--static/sounds/woosh/woosh_06.oggbin0 -> 16306 bytes
-rw-r--r--static/sounds/woosh/woosh_07.oggbin0 -> 8353 bytes
-rw-r--r--static/sounds/woosh/woosh_08.oggbin0 -> 7961 bytes
-rw-r--r--static/sounds/woosh/woosh_09.oggbin0 -> 9681 bytes
-rw-r--r--static/sounds/woosh/woosh_10.oggbin0 -> 9037 bytes
-rw-r--r--static/sounds/woosh/woosh_11.oggbin0 -> 12470 bytes
-rw-r--r--static/sounds/woosh/woosh_12.oggbin0 -> 18660 bytes
-rw-r--r--static/sounds/woosh/woosh_13.oggbin0 -> 9075 bytes
-rw-r--r--static/sounds/woosh/woosh_14.oggbin0 -> 14009 bytes
-rw-r--r--static/sounds/woosh/woosh_15.oggbin0 -> 13494 bytes
-rw-r--r--static/sounds/woosh/woosh_16.oggbin0 -> 12866 bytes
-rw-r--r--static/sounds/woosh/woosh_17.oggbin0 -> 6726 bytes
-rw-r--r--static/sounds/woosh/woosh_18.oggbin0 -> 15182 bytes
-rw-r--r--static/sounds/woosh/woosh_19.oggbin0 -> 10954 bytes
-rw-r--r--static/sounds/woosh/woosh_20.oggbin0 -> 12851 bytes
-rw-r--r--static/sounds/woosh/woosh_21.oggbin0 -> 15623 bytes
-rw-r--r--static/type/sans/400.woff2bin0 -> 44416 bytes
-rw-r--r--static/type/sans/400i.woff2bin0 -> 41152 bytes
-rw-r--r--static/type/sans/600.woff2bin0 -> 45252 bytes
-rw-r--r--static/type/sans/600i.woff2bin0 -> 41616 bytes
-rw-r--r--static/type/sans/typeface.txt4
-rw-r--r--static/uchu.css1
-rw-r--r--static/uchu.css.map1
-rw-r--r--svelte.config.js22
-rw-r--r--tsconfig.json15
-rw-r--r--vite.config.ts26
235 files changed, 4597 insertions, 0 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..71114bf
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,21 @@
+# EditorConfig configuration for netop://ウエブ's projects
+# http://editorconfig.org
+
+# Top-most EditorConfig file
+root = true
+
+# Unix-style newlines with a newline ending every file, utf-8 charset
+[*]
+charset = utf-8
+end_of_line = lf
+indent_size = 2
+indent_style = space
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.md]
+trim_trailing_whitespace = false
+
+# Match diffs, avoid to trim trailing whitespace
+[*.{diff,patch}]
+trim_trailing_whitespace = false
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..63f6f85
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+node_modules
+
+# Output
+.output
+.vercel
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+*.lockb
+*.lockdb
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
+
+deploy.sh
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..5cb81a8
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1 @@
+bun run pre-commit
diff --git a/.well-known/atproto-did b/.well-known/atproto-did
new file mode 100644
index 0000000..bda9dfc
--- /dev/null
+++ b/.well-known/atproto-did
@@ -0,0 +1 @@
+did:plc:3ll36iljzlprcdriaki2ddqp
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6467647
--- /dev/null
+++ b/README.md
@@ -0,0 +1,62 @@
+# homepage
+
+> source for the coolest homepage with the coolest domain name, **webb.page**.
+
+
+
+## Prerequisites
+
+- [Bun](https://bun.sh)
+
+
+
+## Installation
+
+```sh
+bun i
+```
+
+```sh
+bunx husky init
+```
+
+`.husky/pre-commit` is created now. Open that file and input: `bun run pre-commit`.
+
+Every time a commit is made to this repo now, the pre-commit script will run. It builds the site so it's a nice sanity check to ensure you don't upload a broken site to your server.
+
+
+
+## Development
+
+```sh
+bun run watch
+````
+
+
+
+## Production
+
+- ensure Bun is installed on your server
+- make note of `which bun` to find the path of your install
+- `nano /etc/systemd/system/homepage.service`
+ ```service
+ [Unit]
+ After=network.target
+ Description=My cool homepage
+ Documentation=https://webb.page
+
+ [Service]
+ # your Bun path
+ ExecStart=/root/.bun/bin/bun start
+ Restart=on-failure
+ Type=simple
+ User=root
+ # the path of your homepage
+ WorkingDirectory=/var/www/html
+
+ [Install]
+ WantedBy=multi-user.target
+ ```
+- `systemctl start homepage`
+- `systemctl enable homepage`
+- when making changes to your `homepage.service` file, you'll need to run `systemctl daemon-reload`
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ec2ddad
--- /dev/null
+++ b/package.json
@@ -0,0 +1,56 @@
+{
+ "author": {
+ "name": "netop://ウィビ",
+ "url": "https://webb.page"
+ },
+ "dependencies": {
+ "marked": "^17.0.1",
+ "plyr": "^3.8.4",
+ "timeago.js": "^4.0.2"
+ },
+ "devDependencies": {
+ "@sveltejs/adapter-auto": "^7.0.0",
+ "@sveltejs/kit": "^2.49.5",
+ "@sveltejs/vite-plugin-svelte": "^6.2.4",
+ "chronver": "*",
+ "del-cli": "^7.0.0",
+ "husky": "^9.1.7",
+ "npm-run-all": "^4.1.5",
+ "prettier": "^3.8.0",
+ "sass": "^1.97.2",
+ "sass-lint": "^1.13.1",
+ "stylelint": "^17.0.0",
+ "stylelint-order": "^7.0.1",
+ "svelte": "^5.46.4",
+ "svelte-adapter-bun": "^1.0.1",
+ "svelte-check": "^4.3.5",
+ "svelte-preprocess": "^6.0.3",
+ "typescript": "^5.9.3",
+ "updates": "^17.0.7",
+ "vite": "^7.3.1"
+ },
+ "name": "homepage",
+ "private": true,
+ "scripts": {
+ "build": "bun run clean && bun run render && bunx vite build",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
+ "clean": "del './build' './static/uchu*' '.svelte-kit' 'bun.lock' 'bun.lockb'",
+ "increment": "chronver --increment package",
+ "lint": "bun run test:svelte && bun run test:typescript",
+ "pre-commit": "bun run build && bun run increment && git add -A :/",
+ "prepare": "husky",
+ "preview": "bunx vite preview",
+ "render": "sass sass/uchu:static --style compressed",
+ "start": "bun i && bun run build && PORT=6433 SETTINGS=production bun ./build/index.js",
+ "test": "run-s test:*",
+ "test:dependencies": "updates --update ./",
+ "test:svelte": "svelte-check",
+ "test:typescript": "tsc --noEmit",
+ "watch": "bun run render && run-p watch:*",
+ "watch:develop": "SETTINGS=development bunx vite dev --port 6433 --host --open",
+ "watch:sass": "sass --watch sass/uchu:static --style compressed"
+ },
+ "type": "module",
+ "version": "2026.04.05"
+}
diff --git a/sass/global.scss b/sass/global.scss
new file mode 100644
index 0000000..a229bf9
--- /dev/null
+++ b/sass/global.scss
@@ -0,0 +1,28 @@
+@use "./uchu/core/mixin" as mixin;
+
+@include mixin.font-plus-italics(400, "/type/sans", "webb sans");
+@include mixin.font-plus-italics(600, "/type/sans", "webb sans");
+
+:root {
+ --color-background: var(--uchu-gray-1);
+ --color-border: var(--uchu-gray-3);
+ --color-foreground: var(--uchu-yin-8);
+ --font-mono: "webb mono";
+ --font-sans: "webb sans";
+ --font-size: 16px;
+ --padding: 0.5rem;
+}
+
+html,
+body {
+ width: 100%; height: 100%;
+}
+
+body {
+ background-color: var(--color-background);
+ color: var(--color-foreground);
+ font-family: var(--font-sans);
+ font-size: var(--font-size);
+ font-variant-numeric: slashed-zero tabular-nums;
+ padding: var(--line-height) var(--line-height) calc(var(--line-height) * 2);
+}
diff --git a/sass/uchu/_base.scss b/sass/uchu/_base.scss
new file mode 100644
index 0000000..ab6efbd
--- /dev/null
+++ b/sass/uchu/_base.scss
@@ -0,0 +1,178 @@
+/// based on Raster v20 (release package) (rsms.me/raster)
+
+u-grid {
+ --grid-tc: repeat(4, 1fr);
+ --grid-ce: -1;
+ --grid-cs: 1;
+ display: grid;
+ grid-column-gap: var(--column-gap);
+ grid-row-gap: var(--row-gap);
+ grid-template-columns: var(--grid-tc);
+
+ &.compact { grid-row-gap: 0; }
+
+ &.debug {
+ > * {
+ --color: rgba(248, 110, 91, 0.3);
+ background-image: linear-gradient(180deg, var(--color) 0, var(--color));
+ }
+
+ > :nth-child(6n+2) {
+ --color: rgba(103, 126, 208, 0.3);
+ }
+
+ > :nth-child(6n+3) {
+ --color: rgba(224, 174, 72, 0.3);
+ }
+
+ > :nth-child(6n+4) {
+ --color: rgba(77, 214, 115, 0.3);
+ }
+
+ > :nth-child(6n+5) {
+ --color: rgba(217, 103, 219, 0.3);
+ }
+
+ > :nth-child(6n+6) {
+ --color: rgba(94, 204, 211, 0.3);
+ }
+
+ > :nth-child(6n+7) {
+ --color: rgba(248, 110, 91, 0.3);
+ }
+ }
+
+ > hr { grid-column: 1 / -1; }
+
+ > hr,
+ > hr:not(:first-child):not(:last-child) {
+ margin-top: calc(var(--line-height) - var(--hr-thickness));
+ margin-bottom: 0;
+ }
+
+ > u-cell {
+ appearance: none;
+ display: block;
+
+ &[span] { grid-column-end: span var(--grid-ce); }
+ &[span*="+"], &[span*="-"], &[span*=".."] { grid-column-start: var(--grid-cs); }
+ &[span*="-"], &[span*=".."] { grid-column-end: var(--grid-ce); }
+ &[span=row] { grid-column: var(--grid-cs) / var(--grid-ce); }
+
+ &[span^="1"] { --grid-cs: 1; }
+ &[span^="2"] { --grid-cs: 2; }
+ &[span^="3"] { --grid-cs: 3; }
+ &[span^="4"] { --grid-cs: 4; }
+ &[span^="5"] { --grid-cs: 5; }
+ &[span^="6"] { --grid-cs: 6; }
+ &[span^="7"] { --grid-cs: 7; }
+ &[span^="8"] { --grid-cs: 8; }
+ &[span^="9"] { --grid-cs: 9; }
+ &[span^="10"] { --grid-cs: 10; }
+ &[span^="11"] { --grid-cs: 11; }
+ &[span^="12"] { --grid-cs: 12; }
+ &[span^="13"] { --grid-cs: 13; }
+ &[span^="14"] { --grid-cs: 14; }
+ &[span^="15"] { --grid-cs: 15; }
+ &[span^="16"] { --grid-cs: 16; }
+ &[span^="17"] { --grid-cs: 17; }
+ &[span^="18"] { --grid-cs: 18; }
+ &[span^="19"] { --grid-cs: 19; }
+ &[span^="20"] { --grid-cs: 20; }
+ &[span^="21"] { --grid-cs: 21; }
+ &[span^="22"] { --grid-cs: 22; }
+ &[span^="23"] { --grid-cs: 23; }
+ &[span^="24"] { --grid-cs: 24; }
+ &[span^="25"] { --grid-cs: 25; }
+ &[span^="26"] { --grid-cs: 26; }
+ &[span^="27"] { --grid-cs: 27; }
+ &[span^="28"] { --grid-cs: 28; }
+ &[span^="29"] { --grid-cs: 29; }
+ &[span^="30"] { --grid-cs: 30; }
+
+ &[span$="+1"], &[span="1"] { --grid-ce: 1; }
+ &[span$="+2"], &[span$="-1"], &[span="2"] { --grid-ce: 2; }
+ &[span$="+3"], &[span$="-2"], &[span="3"] { --grid-ce: 3; }
+ &[span$="+4"], &[span$="-3"], &[span="4"] { --grid-ce: 4; }
+ &[span$="+5"], &[span$="-4"], &[span="5"] { --grid-ce: 5; }
+ &[span$="+6"], &[span$="-5"], &[span="6"] { --grid-ce: 6; }
+ &[span$="+7"], &[span$="-6"], &[span="7"] { --grid-ce: 7; }
+ &[span$="+8"], &[span$="-7"], &[span="8"] { --grid-ce: 8; }
+ &[span$="+9"], &[span$="-8"], &[span="9"] { --grid-ce: 9; }
+ &[span$="+10"], &[span$="-9"], &[span="10"] { --grid-ce: 10; }
+ &[span$="+11"], &[span$="-10"], &[span="11"] { --grid-ce: 11; }
+ &[span$="+12"], &[span$="-11"], &[span="12"] { --grid-ce: 12; }
+ &[span$="+13"], &[span$="-12"], &[span="13"] { --grid-ce: 13; }
+ &[span$="+14"], &[span$="-13"], &[span="14"] { --grid-ce: 14; }
+ &[span$="+15"], &[span$="-14"], &[span="15"] { --grid-ce: 15; }
+ &[span$="+16"], &[span$="-15"], &[span="16"] { --grid-ce: 16; }
+ &[span$="+17"], &[span$="-16"], &[span="17"] { --grid-ce: 17; }
+ &[span$="+18"], &[span$="-17"], &[span="18"] { --grid-ce: 18; }
+ &[span$="+19"], &[span$="-18"], &[span="19"] { --grid-ce: 19; }
+ &[span$="+20"], &[span$="-19"], &[span="20"] { --grid-ce: 20; }
+ &[span$="+21"], &[span$="-20"], &[span="21"] { --grid-ce: 21; }
+ &[span$="+22"], &[span$="-21"], &[span="22"] { --grid-ce: 22; }
+ &[span$="+23"], &[span$="-22"], &[span="23"] { --grid-ce: 23; }
+ &[span$="+24"], &[span$="-23"], &[span="24"] { --grid-ce: 24; }
+ &[span$="+25"], &[span$="-24"], &[span="25"] { --grid-ce: 25; }
+ &[span$="+26"], &[span$="-25"], &[span="26"] { --grid-ce: 26; }
+ &[span$="+27"], &[span$="-26"], &[span="27"] { --grid-ce: 27; }
+ &[span$="+28"], &[span$="-27"], &[span="28"] { --grid-ce: 28; }
+ &[span$="+29"], &[span$="-28"], &[span="29"] { --grid-ce: 29; }
+ &[span$="+30"], &[span$="-29"], &[span="30"] { --grid-ce: 30; }
+ &[span$="-30"] { --grid-ce: 31; }
+
+ &.h1, &.h2, &.h3, &.h4, &.h5, &.h6 { margin-bottom: 0; }
+
+ > img,
+ > p > img {
+ max-width: 100%;
+ object-fit: contain;
+ }
+ }
+
+ &[columns="1"] { --grid-tc: repeat(1, 1fr); }
+ &[columns="2"] { --grid-tc: repeat(2, 1fr); }
+ &[columns="3"] { --grid-tc: repeat(3, 1fr); }
+ &[columns="4"] { --grid-tc: repeat(4, 1fr); }
+ &[columns="5"] { --grid-tc: repeat(5, 1fr); }
+ &[columns="6"] { --grid-tc: repeat(6, 1fr); }
+ &[columns="7"] { --grid-tc: repeat(7, 1fr); }
+ &[columns="8"] { --grid-tc: repeat(8, 1fr); }
+ &[columns="9"] { --grid-tc: repeat(9, 1fr); }
+ &[columns="10"] { --grid-tc: repeat(10, 1fr); }
+ &[columns="11"] { --grid-tc: repeat(11, 1fr); }
+ &[columns="12"] { --grid-tc: repeat(12, 1fr); }
+ &[columns="13"] { --grid-tc: repeat(13, 1fr); }
+ &[columns="14"] { --grid-tc: repeat(14, 1fr); }
+ &[columns="15"] { --grid-tc: repeat(15, 1fr); }
+ &[columns="16"] { --grid-tc: repeat(16, 1fr); }
+ &[columns="17"] { --grid-tc: repeat(17, 1fr); }
+ &[columns="18"] { --grid-tc: repeat(18, 1fr); }
+ &[columns="19"] { --grid-tc: repeat(19, 1fr); }
+ &[columns="20"] { --grid-tc: repeat(20, 1fr); }
+ &[columns="21"] { --grid-tc: repeat(21, 1fr); }
+ &[columns="22"] { --grid-tc: repeat(22, 1fr); }
+ &[columns="23"] { --grid-tc: repeat(23, 1fr); }
+ &[columns="24"] { --grid-tc: repeat(24, 1fr); }
+ &[columns="25"] { --grid-tc: repeat(25, 1fr); }
+ &[columns="26"] { --grid-tc: repeat(26, 1fr); }
+ &[columns="27"] { --grid-tc: repeat(27, 1fr); }
+ &[columns="28"] { --grid-tc: repeat(28, 1fr); }
+ &[columns="29"] { --grid-tc: repeat(29, 1fr); }
+ &[columns="30"] { --grid-tc: repeat(30, 1fr); }
+}
+
+[flow-cols] {
+ column-fill: balance;
+ column-gap: var(--column-gap);
+}
+
+[flow-cols="1"] { column-count: 1; }
+[flow-cols="2"] { column-count: 2; }
+[flow-cols="3"] { column-count: 3; }
+[flow-cols="4"] { column-count: 4; }
+[flow-cols="5"] { column-count: 5; }
+[flow-cols="6"] { column-count: 6; }
+[flow-cols="7"] { column-count: 7; }
+[flow-cols="8"] { column-count: 8; }
diff --git a/sass/uchu/_form.scss b/sass/uchu/_form.scss
new file mode 100644
index 0000000..e8d3194
--- /dev/null
+++ b/sass/uchu/_form.scss
@@ -0,0 +1,77 @@
+fieldset {
+ position: relative;
+
+ button {
+ margin-top: 1rem; padding: 0.5rem calc(0.75rem + 2px);
+
+ border: none;
+ display: block;
+ text-transform: lowercase;
+
+ &:not(:disabled) {
+ background-color: var(--uchu-gray-9);
+ color: var(--uchu-gray-2);
+ cursor: pointer;
+ }
+
+ &:disabled {
+ background-color: var(--uchu-gray-2);
+ color: var(--uchu-gray-6);
+ cursor: not-allowed;
+ }
+ }
+
+ label {
+ color: var(--uchu-gray-5);
+ font-size: 80%;
+ position: absolute;
+ padding-top: 4px;
+ padding-left: calc(0.75rem + 2px);
+ text-transform: lowercase;
+
+ + input,
+ + select {
+ background-color: var(--uchu-gray-2);
+ border: 2px solid;
+ transition: border-color 0.2s;
+ width: 400px;
+
+ @media (prefers-reduced-motion: reduce) {
+ transition: none;
+ }
+
+ &::placeholder {
+ color: var(--uchu-gray-5);
+ opacity: 1;
+ }
+
+ &:not(:focus) {
+ border-color: var(--uchu-gray-2);
+ }
+
+ &:focus {
+ border-color: var(--uchu-blue-4);
+ box-shadow: none;
+ outline: none;
+ }
+ }
+
+ + select {
+ background-image: url("/media/svg/caret-down.svg");
+ background-position: right 1rem bottom 0.7rem;
+ background-repeat: no-repeat;
+ background-size: 1rem;
+ padding: 1.5rem 0.75rem 0.5rem 0.5rem;
+ }
+
+ + input {
+ padding: 1.5rem 0.75rem 0.5rem 0.75rem;
+
+ &:invalid {
+ border-color: var(--uchu-red-5);
+ box-shadow: none;
+ outline: none;
+ }
+ }
+ }
+}
diff --git a/sass/uchu/_reset.scss b/sass/uchu/_reset.scss
new file mode 100644
index 0000000..a270848
--- /dev/null
+++ b/sass/uchu/_reset.scss
@@ -0,0 +1,708 @@
+* {
+ box-sizing: border-box;
+ font: inherit;
+ line-height: inherit;
+
+ + hr:last-child { margin-top: calc(var(--hr-thickness) / -2); }
+
+ + h2 {
+ margin-top: var(--line-height);
+ margin-bottom: 0;
+ padding-top: calc(var(--line-height) * 0.5);
+ padding-bottom: calc(var(--line-height) * 0.5);
+ }
+
+ + img { margin-top: calc(var(--baseline) * -1); }
+
+ @media (prefers-reduced-motion: reduce) {
+ transition: none;
+ }
+}
+
+a, abbr, acronym, address, applet, article, aside, audio, b, big, blockquote, body, canvas, caption, center, cite, code, dd, del, details, dfn, div, dl, dt, em, embed, fieldset, figcaption, figure, footer, form, grid, h1, h2, h3, h4, h5, h6, header, hgroup, hr, html, i, iframe, img, ins, kbd, label, legend, li, main, mark, menu, nav, noscript, object, ol, output, p, pre, q, s, samp, section, small, span, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, time, tr, tt, u, ul, var, video {
+ margin: 0; padding: 0;
+
+ border: 0;
+ vertical-align: baseline;
+}
+
+address, article, aside, blockquote, dd, dl, dt, fieldset, figure, form, h1, h2, h3, h4, h5, h6, li, nav, ol, p, pre, r-grid, table, tfoot, ul, video {
+ margin-top: var(--block-spacing-top);
+ margin-bottom: var(--block-spacing-bottom);
+}
+
+a {
+ color: inherit;
+ cursor: pointer;
+ text-decoration: underline oklch(var(--uchu-yin-raw) / 30%);
+ transition: all 0.2s;
+ white-space: nowrap;
+ -webkit-text-decoration: underline oklch(var(--uchu-yin-raw) / 30%); /// safari needs a hand
+
+ &:hover {
+ color: var(--uchu-blue-4);
+ }
+}
+
+b, strong {
+ font-weight: 600;
+
+ code, pre, tt {
+ font-weight: inherit;
+ }
+}
+
+blockquote, q {
+ quotes: none;
+
+ &::after,
+ &::before {
+ content: none;
+ }
+}
+
+button {
+ margin: 0; padding: 0;
+
+ appearance: none;
+ background-color: transparent;
+ border: none;
+}
+
+html {
+ background-color: var(--background-color);
+ color: var(--foreground-color);
+ font-family: var(--font-sans), -system-ui, system-ui, sans-serif;
+ font-feature-settings: "kern" 1, "liga" 1, "calt" 1, "cv10" 1;
+ font-size: var(--font-size);
+ font-variant-ligatures: contextual common-ligatures;
+ letter-spacing: -0.01rem;
+ line-height: var(--line-height);
+}
+
+body {
+ overflow-x: hidden;
+ padding: calc(var(--line-height) * 2);
+ padding-bottom: calc(var(--line-height) * 3);
+ scroll-behavior: smooth;
+
+ @media only screen and (max-width: 600px) {
+ padding: var(--line-height);
+ padding-bottom: calc(var(--line-height) * 2);
+ }
+}
+
+:first-child { margin-top: unset; }
+:last-child { margin-bottom: unset; }
+
+hr {
+ background-color: var(--foreground-color);
+ border: none;
+ height: var(--hr-thickness);
+ margin-top: calc(var(--line-height) - var(--hr-thickness) / 2);
+ margin-bottom: calc(var(--line-height) - var(--hr-thickness) / 2);
+ opacity: 0.05;
+
+ &:not(:first-child) {
+ margin-top: var(--line-height);
+ margin-bottom: calc(var(--line-height) - var(--hr-thickness));
+ }
+
+ &:last-child,
+ &:only-child {
+ margin-top: calc(var(--line-height) - var(--hr-thickness) / 2);
+ }
+
+ &:first-child {
+ margin-top: calc(var(--hr-thickness) / -2);
+ margin-bottom: calc(var(--line-height) - var(--hr-thickness) / 2);
+ }
+
+ &:last-child {
+ margin-bottom: calc(var(--hr-thickness) / -2);
+ }
+
+ &:only-child {
+ margin-bottom: calc(var(--line-height) - var(--hr-thickness) / 2);
+ }
+}
+
+
+
+em, i {
+ font-style: italic;
+}
+
+code,
+pre,
+tt {
+ font-family: var(--mono-font), Inconsolata, Menlo, monospace;
+ font-variant-numeric: slashed-zero; /// best zero
+ font-weight: 430;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+
+ b, strong {
+ font-weight: 580;
+ }
+}
+
+pre {
+ display: block;
+ overflow-x: auto;
+ white-space: pre;
+ word-wrap: normal;
+
+ code,
+ tt {
+ white-space: pre;
+ word-wrap: normal;
+ }
+}
+
+
+
+h1 {
+ font-size: var(--h1-size);
+ font-weight: 720;
+ letter-spacing: -0.05rem;
+ line-height: calc(var(--line-height) * 2);
+ margin-top: calc(var(--line-height) * 2);
+ margin-bottom: var(--line-height);
+ margin-left: calc(var(--h1-size) / -22);
+ word-break: break-word;
+
+ &.single-line {
+ margin-top: var(--line-height);
+ padding-top: calc(var(--line-height) * 0.5);
+ padding-bottom: calc(var(--line-height) * 0.5);
+
+ &:first-child {
+ margin-top: 0;
+ padding-bottom: calc(var(--line-height) * 0.5);
+ }
+ }
+
+ &.large {
+ --h1-size: 4rem;
+ font-weight: 730;
+ line-height: calc(var(--line-height) * 3);
+ }
+
+ &.xlarge {
+ --h1-size: 5.5rem;
+ font-weight: 740;
+ line-height: calc(var(--line-height) * 4);
+ }
+
+ &.xxlarge {
+ --h1-size: 7.5rem;
+ font-weight: 750;
+ line-height: calc(var(--line-height) * 5);
+ }
+
+ &.xxxlarge {
+ --h1-size: 10.5rem;
+ font-weight: 760;
+ line-height: calc(var(--line-height) * 7);
+ }
+}
+
+h2 {
+ font-size: var(--h2-size);
+ font-weight: 700;
+ letter-spacing: -0.03rem;
+ line-height: calc(var(--line-height) * 2);
+ margin-bottom: var(--line-height);
+ margin-left: calc(var(--h2-size) / -26);
+
+ &.single-line {
+ margin-top: var(--line-height);
+ margin-bottom: 0;
+ padding-top: calc(var(--line-height) * 0.5);
+ padding-bottom: calc(var(--line-height) * 0.5);
+ &:first-child { margin-top: unset; }
+ }
+}
+
+h3, h4 {
+ font-size: var(--h3-size);
+ font-weight: 700;
+ letter-spacing: -0.02rem;
+ line-height: calc(var(--line-height) * 1.15);
+ margin-bottom: var(--baseline);
+ padding-top: calc(var(--baseline) * 0.75);
+ padding-bottom: calc(var(--baseline) * 0.25);
+
+ &.single-line {
+ margin-bottom: 0;
+ padding-bottom: calc(var(--baseline) * 1.25);
+ }
+
+ + h1,
+ + h1.single-line {
+ margin-top: calc(var(--baseline) * 3);
+ }
+
+ &.single-line + h1,
+ &.single-line + h1.single-line,
+ &.single-line + h2,
+ &.single-line + h2.single-line {
+ margin-top: var(--line-height);
+ }
+
+ + h2,
+ + h2.single-line {
+ margin-top: var(--baseline);
+ }
+}
+
+h4 {
+ font-size: var(--h4-size);
+ font-weight: 700;
+ letter-spacing: -0.012rem;
+}
+
+h5, h6 {
+ font-weight: 670;
+ letter-spacing: -0.015rem;
+ margin-bottom: 0;
+}
+
+h1 > a,
+h2 > a,
+h3 > a,
+h4 > a,
+h5 > a,
+h6 > a {
+ text-decoration: none;
+
+ &:hover {
+ color: inherit;
+ text-decoration: underline oklch(var(--uchu-yin-raw) / 30%);
+ }
+}
+
+img {
+ display: block;
+ margin-top: var(--baseline);
+ margin-bottom: var(--baseline);
+
+ &:first-child,
+ &:last-child {
+ display: block;
+ margin-top: var(--baseline);
+ margin-bottom: var(--baseline);
+ }
+
+ &:only-child { margin: 0; }
+
+ &.cover,
+ &.fill {
+ object-fit: cover;
+ }
+
+ &.bottom,
+ &.center,
+ &.top {
+ align-self: center;
+ }
+
+ &.bottom { object-position: center bottom; }
+ &.center { object-position: center center; }
+ &.top { object-position: center top; }
+
+ &.left.bottom,
+ &.left.center,
+ &.left.top {
+ align-self: flex-start;
+ }
+
+ &.left.bottom { object-position: left bottom; }
+ &.left.center { object-position: left center; }
+ &.left.top { object-position: left top; }
+
+ &.right.bottom,
+ &.right.center,
+ &.right.top {
+ align-self: flex-end;
+ }
+
+ &.right.bottom { object-position: right bottom; }
+ &.right.center { object-position: right center; }
+ &.right.top { object-position: right top; }
+}
+
+input,
+select {
+ appearance: none;
+ border: none;
+ letter-spacing: -0.01rem; /// to match html rule
+
+ -moz-appearance: none;
+ -webkit-appearance: none;
+
+ &:focus-visible {
+ outline: 2px solid var(--uchu-blue-4);
+ }
+}
+
+ol,
+ul {
+ list-style-position: outside;
+ --list-indentation: 2rem;
+ &.compact > li { margin-bottom: 0; }
+}
+
+ol {
+ &:not([start]) {
+ counter-reset: ol-counter;
+ list-style: none;
+ padding-left: var(--list-indentation);
+
+ > li {
+ counter-increment: ol-counter;
+ position: relative;
+
+ &::before {
+ --space: 0.5rem;
+ --width: calc(var(--list-indentation) - var(--space));
+ width: var(--width); height: var(--line-height);
+
+ content: counter(ol-counter) ". ";
+ font-variant-numeric: tabular-nums;
+ font-weight: 500;
+ left: calc(-1 * (var(--width) + var(--space)));
+ position: absolute;
+ text-align: left;
+ }
+ }
+ }
+
+ &[start] { padding-inline-start: var(--list-indentation); }
+}
+
+ul { padding-left: 1.3rem; }
+
+li {
+ margin-bottom: var(--baseline);
+ margin-left: 0.2rem;
+
+ > p + ol,
+ > p + ul {
+ margin-top: calc(var(--baseline) * -1);
+ }
+
+ &.task-list-item {
+ list-style-type: none;
+
+ > input[type=checkbox] {
+ width: 1.5rem; height: var(--baseline);
+
+ appearance: none;
+ background: none;
+ border: none;
+ display: inline-block;
+ list-style: none;
+ margin-right: 0.5rem;
+ margin-left: -1.4rem;
+ opacity: 1;
+ position: relative;
+
+ --check-svg-url: url('data:image/svg+xml;utf8,<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.7 9.3L8.1 12.6L13.8 6.9L12.3 5.3L8.1 9.5L6.3 7.7L4.7 9.3Z" fill="black"/></svg>');
+ --outline-svg-url: url('data:image/svg+xml;utf8,<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="1.75" y="1.75" width="14.5" height="14.5" rx="0.5" stroke="black" stroke-width="1.5"/></svg>');
+
+ &::after,
+ &::before {
+ top: -0.25rem; bottom: -0.25rem; left: 0;
+
+ background-position: 50%;
+ background-repeat: no-repeat;
+ background-size: contain;
+ color: transparent;
+ content: "×";
+ display: block;
+ position: absolute;
+ width: 1.1rem;
+
+ @supports ((mask-image: linear-gradient(var(--uchu-yin), red))) {
+ mask-position: center center;
+ mask-repeat: no-repeat;
+ mask-size: contain;
+ }
+ }
+
+ &::after {
+ background-image: var(--outline-svg-url);
+
+ @supports ((mask-image: linear-gradient(var(--uchu-yin), red))) {
+ background-color: var(--foreground-color);
+ mask-image: var(--outline-svg-url);
+ }
+ }
+
+ &[checked]::before {
+ background-image: var(--check-svg-url);
+
+ @supports ((mask-image: linear-gradient(var(--uchu-yin), red))) {
+ background-color: var(--foreground-color);
+ mask-image: var(--check-svg-url);
+ }
+ }
+ }
+ }
+}
+
+table {
+ --border-opacity: 0.15;
+ --border-color: rgba(var(--foreground-color-rgb), calc(var(--foreground-color-a) * var(--border-opacity)));
+ --border-width: 1px;
+
+ border-collapse: collapse;
+ border-right: var(--border-width) solid var(--border-color);
+ border-spacing: 0;
+ border-top: var(--border-width) solid var(--border-color);
+ overflow: auto;
+ position: relative;
+ margin-top: calc(var(--line-height) * 1 + var(--border-width) * -1);
+ margin-bottom: calc(var(--line-height) * 1.5);
+
+ * {
+ box-sizing: border-box;
+ }
+
+ &:first-child {
+ margin-top: calc(var(--line-height) * 0.5 + var(--border-width) * -1);
+ margin-bottom: calc(var(--line-height) * 0.5);
+ }
+
+ td,
+ th {
+ background-image: linear-gradient(90deg, var(--border-color), var(--border-color) 1px, transparent 0, transparent calc(var(--baseline) / 2));
+ background-position: 0 -1px;
+ background-repeat: no-repeat;
+ background-size: 100% 100%;
+ padding: var(--baseline) 1rem;
+ position: relative;
+
+ &::after {
+ right: 0; bottom: 0; left: 0;
+
+ background-color: var(--border-color);
+ color: transparent;
+ content: "A";
+ height: var(--border-width);
+ pointer-events: none;
+ position: absolute;
+ z-index: 1;
+ }
+ }
+
+ th {
+ font-weight: 600;
+ text-align: left;
+
+ &[align=center] { text-align: center; }
+ &[align=right] { text-align: right; }
+ }
+}
+
+p + table {
+ margin-top: calc(var(--line-height) * 1.5 + var(--border-width) * -1);
+}
+
+
+
+@media only screen and (max-width: 600px) {
+ .only-large-window {
+ display: none;
+ }
+}
+
+@media only screen and (min-width: 601px) {
+ .only-small-window {
+ display: none;
+ }
+}
+
+.compact {
+ > li > p + ol,
+ > li > p + ul {
+ margin-top: calc(var(--block-spacing-bottom) * -1);
+ }
+}
+
+.small {
+ font-size: 0.85rem;
+ line-height: var(--line-height);
+}
+
+.xsmall {
+ font-size: 0.8rem;
+ line-height: calc(var(--line-height) * 0.75);
+ padding-top: calc(var(--line-height) * 0.25);
+}
+
+.xxsmall {
+ font-size: 0.65rem;
+ line-height: calc(var(--line-height) * 0.7);
+ padding-top: calc(var(--line-height) * 0.3);
+}
+
+.xxxsmall {
+ font-size: 0.5rem;
+ line-height: calc(var(--line-height) * 0.5);
+ padding-bottom: calc(var(--line-height) * 0.25);
+}
+
+.show-base-grid {
+ background-image: repeating-linear-gradient(
+ 0deg, var(--base-grid-color2),
+ var(--base-grid-color2) 1px,
+ transparent 0,
+ transparent calc(var(--baseline) / 2),
+ var(--base-grid-color1) calc(var(--baseline) / 2),
+ var(--base-grid-color1) calc(var(--baseline) / 2 + 1px),
+ transparent calc(var(--baseline) / 2 + 1px),
+ transparent var(--baseline)
+ );
+ background-position: 0 0.5px;
+ background-repeat: repeat-y;
+ background-size: 100% var(--baseline);
+}
+
+.single-line {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.block { display: block; }
+.inline { display: inline-block; }
+
+.flex-h, .flex-v {
+ display: flex;
+}
+
+.flex-h {
+ flex-direction: row;
+ .bottom { align-self: flex-end; }
+}
+
+.flex-v {
+ flex-direction: column;
+ &.center { align-self: center; }
+}
+
+.center { text-align: center; }
+.left { text-align: left; }
+.right { text-align: right; }
+
+.margin0 { margin: 0; }
+.margin1 { margin: calc(var(--line-height) * 1); }
+.margin2 { margin: calc(var(--line-height) * 2); }
+.margin3 { margin: calc(var(--line-height) * 3); }
+.margin4 { margin: calc(var(--line-height) * 4); }
+.margin5 { margin: calc(var(--line-height) * 8); }
+
+.padding0 { padding: 0; }
+.padding1 { padding: calc(var(--line-height) * 1); }
+.padding2 { padding: calc(var(--line-height) * 2); }
+.padding3 { padding: calc(var(--line-height) * 3); }
+.padding4 { padding: calc(var(--line-height) * 4); }
+.padding5 { padding: calc(var(--line-height) * 8); }
+
+.w-1 { width: calc(var(--line-height) * 1); }
+.w-2 { width: calc(var(--line-height) * 2); }
+.w-3 { width: calc(var(--line-height) * 3); }
+.w-4 { width: calc(var(--line-height) * 4); }
+.w-5 { width: calc(var(--line-height) * 5); }
+.w-6 { width: calc(var(--line-height) * 6); }
+.w-7 { width: calc(var(--line-height) * 7); }
+.w-8 { width: calc(var(--line-height) * 8); }
+.w-9 { width: calc(var(--line-height) * 9); }
+.w-10 { width: calc(var(--line-height) * 10); }
+.w-11 { width: calc(var(--line-height) * 11); }
+.w-12 { width: calc(var(--line-height) * 12); }
+.w-13 { width: calc(var(--line-height) * 13); }
+.w-14 { width: calc(var(--line-height) * 14); }
+.w-15 { width: calc(var(--line-height) * 15); }
+.w-16 { width: calc(var(--line-height) * 16); }
+.w-17 { width: calc(var(--line-height) * 17); }
+.w-18 { width: calc(var(--line-height) * 18); }
+.w-19 { width: calc(var(--line-height) * 19); }
+.w-20 { width: calc(var(--line-height) * 20); }
+.w-21 { width: calc(var(--line-height) * 21); }
+.w-22 { width: calc(var(--line-height) * 22); }
+.w-23 { width: calc(var(--line-height) * 23); }
+.w-24 { width: calc(var(--line-height) * 24); }
+.w-25 { width: calc(var(--line-height) * 25); }
+.w-26 { width: calc(var(--line-height) * 26); }
+.w-27 { width: calc(var(--line-height) * 27); }
+.w-28 { width: calc(var(--line-height) * 28); }
+.w-29 { width: calc(var(--line-height) * 29); }
+.w-30 { width: calc(var(--line-height) * 30); }
+.w-31 { width: calc(var(--line-height) * 31); }
+.w-32 { width: calc(var(--line-height) * 32); }
+.w-33 { width: calc(var(--line-height) * 33); }
+.w-34 { width: calc(var(--line-height) * 34); }
+.w-35 { width: calc(var(--line-height) * 35); }
+.w-36 { width: calc(var(--line-height) * 36); }
+.w-37 { width: calc(var(--line-height) * 37); }
+.w-38 { width: calc(var(--line-height) * 38); }
+.w-39 { width: calc(var(--line-height) * 39); }
+.w-40 { width: calc(var(--line-height) * 40); }
+.w-full { width: 100%; }
+
+.h-1 { height: calc(var(--line-height) * 1); }
+.h-2 { height: calc(var(--line-height) * 2); }
+.h-3 { height: calc(var(--line-height) * 3); }
+.h-4 { height: calc(var(--line-height) * 4); }
+.h-5 { height: calc(var(--line-height) * 5); }
+.h-6 { height: calc(var(--line-height) * 6); }
+.h-7 { height: calc(var(--line-height) * 7); }
+.h-8 { height: calc(var(--line-height) * 8); }
+.h-9 { height: calc(var(--line-height) * 9); }
+.h-10 { height: calc(var(--line-height) * 10); }
+.h-11 { height: calc(var(--line-height) * 11); }
+.h-12 { height: calc(var(--line-height) * 12); }
+.h-13 { height: calc(var(--line-height) * 13); }
+.h-14 { height: calc(var(--line-height) * 14); }
+.h-15 { height: calc(var(--line-height) * 15); }
+.h-16 { height: calc(var(--line-height) * 16); }
+.h-17 { height: calc(var(--line-height) * 17); }
+.h-18 { height: calc(var(--line-height) * 18); }
+.h-19 { height: calc(var(--line-height) * 19); }
+.h-20 { height: calc(var(--line-height) * 20); }
+.h-21 { height: calc(var(--line-height) * 21); }
+.h-22 { height: calc(var(--line-height) * 22); }
+.h-23 { height: calc(var(--line-height) * 23); }
+.h-24 { height: calc(var(--line-height) * 24); }
+.h-25 { height: calc(var(--line-height) * 25); }
+.h-26 { height: calc(var(--line-height) * 26); }
+.h-27 { height: calc(var(--line-height) * 27); }
+.h-28 { height: calc(var(--line-height) * 28); }
+.h-29 { height: calc(var(--line-height) * 29); }
+.h-30 { height: calc(var(--line-height) * 30); }
+.h-31 { height: calc(var(--line-height) * 31); }
+.h-32 { height: calc(var(--line-height) * 32); }
+.h-33 { height: calc(var(--line-height) * 33); }
+.h-34 { height: calc(var(--line-height) * 34); }
+.h-35 { height: calc(var(--line-height) * 35); }
+.h-36 { height: calc(var(--line-height) * 36); }
+.h-37 { height: calc(var(--line-height) * 37); }
+.h-38 { height: calc(var(--line-height) * 38); }
+.h-39 { height: calc(var(--line-height) * 39); }
+.h-40 { height: calc(var(--line-height) * 40); }
+
+.opacity0 { opacity: 0; }
+.opacity1 { opacity: 0.1; }
+.opacity2 { opacity: 0.2; }
+.opacity3 { opacity: 0.3; }
+.opacity4 { opacity: 0.4; }
+.opacity5 { opacity: 0.5; }
+.opacity6 { opacity: 0.6; }
+.opacity7 { opacity: 0.7; }
+.opacity8 { opacity: 0.8; }
+.opacity9 { opacity: 0.9; }
+.opacity10 { opacity: 1; }
diff --git a/sass/uchu/core/_animation.scss b/sass/uchu/core/_animation.scss
new file mode 100644
index 0000000..5487ce4
--- /dev/null
+++ b/sass/uchu/core/_animation.scss
@@ -0,0 +1,23 @@
+@keyframes hovering {
+ 0% {
+ transform: translate(0, 0);
+ }
+
+ 50% {
+ transform: translate(0, 8px);
+ }
+
+ 100% {
+ transform: translate(0, 0);
+ }
+}
+
+@keyframes marquee {
+ 0% {
+ transform: translateX(0);
+ }
+
+ to {
+ transform: translateX(-100%);
+ }
+}
diff --git a/sass/uchu/core/_mixin.scss b/sass/uchu/core/_mixin.scss
new file mode 100644
index 0000000..618cbe6
--- /dev/null
+++ b/sass/uchu/core/_mixin.scss
@@ -0,0 +1,84 @@
+@mixin center {
+ align-items: center;
+ display: inline-flex;
+ justify-content: center;
+}
+
+@mixin clearfix {
+ clear: both;
+ content: "";
+ display: block;
+}
+
+@mixin ellipsis {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+/// Smart font include
+/// Simply pass in the font-weight you want to use and the normal/italicized versions will be added
+/// No more weighing down the front-end with references to unused weights
+@mixin font($font-weight, $relative-font-path, $font-name) {
+ @font-face {
+ font-family: $font-name;
+ font-style: normal;
+ font-weight: $font-weight;
+ src: url("#{$relative-font-path}/#{$font-weight}.woff2") format("woff2");
+ }
+}
+
+@mixin font-plus-italics($font-weight, $relative-font-path, $font-name) {
+ @font-face {
+ font-family: $font-name;
+ font-style: normal;
+ font-weight: $font-weight;
+ src: url("#{$relative-font-path}/#{$font-weight}.woff2") format("woff2");
+ }
+
+ @font-face {
+ font-family: $font-name;
+ font-style: italic;
+ font-weight: $font-weight;
+ src: url("#{$relative-font-path}/#{$font-weight}i.woff2") format("woff2");
+ }
+}
+
+@mixin mono-plus-italics($font-weight, $relative-font-path, $font-name) {
+ @font-face {
+ font-family: $font-name;
+ font-style: normal;
+ font-weight: $font-weight;
+ src: url("#{$relative-font-path}/#{$font-weight}.woff2") format("woff2");
+ unicode-range: U+24, U+30-39, U+A2-A5; // $, 0-9, ¢£¤¥
+ }
+
+ @font-face {
+ font-family: $font-name;
+ font-style: italic;
+ font-weight: $font-weight;
+ src: url("#{$relative-font-path}/#{$font-weight}i.woff2") format("woff2");
+ unicode-range: U+24, U+30-39, U+A2-A5; // $, 0-9, ¢£¤¥
+ }
+}
+
+@mixin hide-text {
+ border: none;
+ color: transparent;
+ font: 0 / 0 a;
+ text-shadow: none;
+}
+
+@mixin selection($background-color: var(--uchu-yang), $text-color: var(--uchu-gray-9)) {
+ &::selection {
+ background-color: $background-color;
+ color: $text-color;
+ text-shadow: none;
+ }
+
+ &::-moz-selection {
+ background-color: $background-color;
+ color: $text-color;
+ text-shadow: none;
+ }
+}
diff --git a/sass/uchu/core/_root.scss b/sass/uchu/core/_root.scss
new file mode 100644
index 0000000..ff0dc09
--- /dev/null
+++ b/sass/uchu/core/_root.scss
@@ -0,0 +1,38 @@
+:root {
+ --background-color: var(--uchu-gray-1);
+ --base-grid-color1: oklch(var(--uchu-blue-3-raw) / 30%);
+ --base-grid-color2: oklch(var(--uchu-purple-3-raw) / 30%);
+ --baseline: calc(var(--line-height) / 2);
+ --block-spacing-bottom: var(--line-height);
+ --block-spacing-top: 0;
+ --column-gap: calc(var(--line-height) * 2);
+ --display-scale: 1;
+ --font-mono: null;
+ --font-sans: null;
+ --font-serif: null;
+ --font-size: 12px;
+ --foreground-color: oklch(var(--uchu-yin-raw) / 80%);
+ --h1-size: 2.8rem;
+ --h2-size: 2.2rem;
+ --h3-size: 1.4rem;
+ --h4-size: 1.1rem;
+ --hr-thickness: 2px;
+ --line-height: calc(var(--font-size) * 1.5);
+ --pixel: 1px;
+ --row-gap: var(--line-height);
+
+ @media only screen and (min-device-pixel-ratio: 1.5), only screen and (min-resolution: 1.5dppx) {
+ --display-scale: 2;
+ --pixel: 0.5px;
+ }
+
+ @media only screen and (min-device-pixel-ratio: 2.5), only screen and (min-resolution: 2.5dppx) {
+ --display-scale: 3;
+ --pixel: 0.34px;
+ }
+
+ @media only screen and (min-device-pixel-ratio: 3.5), only screen and (min-resolution: 3.5dppx) {
+ --display-scale: 4;
+ --pixel: 0.25px;
+ }
+}
diff --git a/sass/uchu/uchu.scss b/sass/uchu/uchu.scss
new file mode 100644
index 0000000..146bfe6
--- /dev/null
+++ b/sass/uchu/uchu.scss
@@ -0,0 +1,4 @@
+@forward "./core/root";
+@forward "reset";
+@forward "base";
+@forward "form";
diff --git a/src/app.html b/src/app.html
new file mode 100644
index 0000000..cdd05c3
--- /dev/null
+++ b/src/app.html
@@ -0,0 +1,66 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+
+ <meta name="applicable-device" content="pc, mobile"/>
+ <meta name="description" content="There are many webpages but only one webbpage and it’s mine. Welcome to Paul Anthony Webb’s home on the ’Net."/>
+ <meta name="fediverse:creator" content="@netopwibby@social.coop"/>
+ <meta name="referrer" content="strict-origin"/>
+ <meta name="theme-color" content="oklch(84.61% 0.004 286.31)"/>
+ <meta name="viewport" content="initial-scale=1, viewport-fit=cover, width=device-width"/>
+
+ <meta property="og:description" content="There are many webpages but only one webbpage and it’s mine. Welcome to Paul Anthony Webb’s home on the ’Net."/>
+ <meta property="og:image" content="https://webb.page/og.png"/>
+ <meta property="og:image:height" content="600"/>
+ <meta property="og:image:secure_url" content="https://webb.page/og.png"/>
+ <meta property="og:image:type" content="image/png"/>
+ <meta property="og:image:width" content="1200"/>
+ <meta property="og:locale" content="en_US"/>
+ <meta property="og:site_name" content="the webb page"/>
+ <meta property="og:title" content="the webb page"/>
+ <meta property="og:type" content="website"/>
+ <meta property="og:url" content="https://webb.page"/>
+
+ <link rel="canonical" href="https://webb.page"/>
+ <link rel="icon" href="%sveltekit.assets%/favicon.svg"/>
+ <link rel="me" href="https://nickel.video/@netopwibby"/>
+ <link rel="me" href="https://social.coop/@netopwibby"/>
+ <link rel="pgpkey" href="%sveltekit.assets%/key.pub"/>
+ <link rel="pingback" href="https://webmention.io/webb.page/xmlrpc"/>
+ <link rel="stylesheet" href="https://uchu.style/color_expanded.css"/>
+ <link rel="stylesheet" href="%sveltekit.assets%/base.css"/>
+ <link rel="stylesheet" href="%sveltekit.assets%/uchu.css"/>
+ <link rel="webmention" href="https://webmention.io/webb.page/webmention"/>
+
+ <title>the webb page</title>
+
+ %sveltekit.head%
+
+ <style>
+ noscript {
+ background-color: var(--uchu-red-3);
+ color: var(--uchu-yin);
+ padding: 1rem;
+ position: fixed;
+ text-align: center;
+ width: calc(100% - 3rem);
+ z-index: 10;
+
+ a {
+ font-weight: 600;
+
+ &:hover {
+ color: inherit;
+ text-decoration-color: var(--uchu-yin);
+ }
+ }
+ }
+ </style>
+ </head>
+
+ <body data-sveltekit-preload-data="tap">
+ <noscript>Hey there! You should check out <a href="https://lite.webb.page">lite.webb.page</a>, instead of this JS nonsense.</noscript>
+ <div style="display: contents">%sveltekit.body%</div>
+ </body>
+</html>
diff --git a/src/lib/component/About.svelte b/src/lib/component/About.svelte
new file mode 100644
index 0000000..c2a076d
--- /dev/null
+++ b/src/lib/component/About.svelte
@@ -0,0 +1,98 @@
+<script lang="ts">
+ //// import
+ import { onMount } from "svelte";
+
+ //// var
+ const portraits = [
+ "/images/netopwibby/01.jpg",
+ "/images/netopwibby/02.jpg",
+ "/images/netopwibby/03.jpg",
+ "/images/netopwibby/04.jpg",
+ "/images/netopwibby/05.jpg",
+ "/images/netopwibby/06.jpg",
+ "/images/netopwibby/07.jpg",
+ "/images/netopwibby/08.jpg",
+ "/images/netopwibby/09.jpg",
+ "/images/netopwibby/10.jpg",
+ "/images/netopwibby/11.jpg",
+ "/images/netopwibby/12.jpg",
+ "/images/netopwibby/13.jpg"
+ ];
+
+ let selfie = "";
+
+ //// function
+ function randomPortrait() {
+ return portraits[Math.floor(Math.random() * portraits.length)];
+ }
+
+ onMount(async() => {
+ selfie = randomPortrait();
+ });
+</script>
+
+<style lang="scss">
+ :root {
+ --about-padding: calc(var(--padding) * 2);
+ }
+
+ h2 {
+ margin: 0 0 var(--about-padding); padding: var(--padding) var(--about-padding);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ p {
+ margin: 0; padding: 0 var(--about-padding) var(--about-padding);
+ white-space: break-spaces;
+ }
+
+ hr {
+ margin: 0 0 calc(var(--line-height) / 2) 0;
+ }
+
+ figure {
+ padding: 0 var(--about-padding) var(--about-padding) var(--about-padding);
+
+ img {
+ margin: 0;
+ }
+
+ figcaption {
+ padding-top: var(--about-padding);
+ text-align: center;
+ }
+ }
+</style>
+
+<h2>about.webb.page</h2>
+
+<p><strong>TL;DR bio:</strong> Architech × Excessively Black × East Coast kid on the West Coast × JU$T × Building a better Internet</p>
+
+<p>I go by NetOperator Wibby, NetOpWibby, or netop://ウエブ pretty much everywhere online. "NetOperator" is the term given to <del>chronically online people</del> internet enthusiasts in the Megaman Battle Network series, one of my favorite game series of all time and constant source of inspiration.</p>
+
+<p><strong>TL;DR career bio:</strong> From college dropout to self-taught designer turned developer during the 2008 recession → Stopped working on music to focus on family and career → lots of startup work → Now I work at a major "fruit" company. Not bad, eh? Bless the Internet.</p>
+
+<p><strong>Short life bio:</strong> Voracious reader, absolutely loved mystery books: Encyclopedia Brown, Hardy Boys, Nancy Drew, Archie Comics, &amp;c. Frequently took things apart and collected scraps to build "inventions." Avid sketcher and jotter of ideas.</p>
+<p>Majorly influenced by Pharrell Williams, Chad Hugo, Timbaland, Clipse, N.E.R.D, Nujabes, Shing02, Fat Jon, Five Deez, &amp;c and wanted to make music and beats. Performed exactly once and was recognized on the street afterwards ("your performance was <em>really</em> good"). Developed a perfectionist mindset, which is to blame for my debut album still under wraps (lyrics still hold up though), among other things.</p>
+<p>Married the girl of my dreams in 2017 (maiden name? Page). We met on a web page in 9th grade, a little known social network called "Classface." Webb + Page. You can't make this up. Oh, and I lost my job four days after our wedding (IoT company imploded and we were the last to know). You wanna hear God laugh? Make plans. Now we have a loquat tree in our backyard in sunny California&hellip;LOL (Love Our Loquats)!</p>
+<p>I try to live life according to The Golden Rule, and to stick to my ethics and morals as much as possible. It's bothersome when I find that others do not. But like&hellip;that's life? It's annoying though. You all should think like me!! 😤</p>
+
+<hr/>
+
+<p>Anyhoo, I love the internet. The internet has empowered me to learn, explore, and get inspired by so many cool people around the world. If you love yourself, you probably love others and aren't bigoted. That makes you cool as fuck. If this isn't you, I hope you find peace.</p>
+
+{#if selfie === ""}
+ <p>loading&hellip;</p>
+{:else}
+ <figure>
+ <img alt="selfie" src={selfie}/>
+ <figcaption>the most fantabulous</figcaption>
+ </figure>
+{/if}
diff --git a/src/lib/component/Blog.svelte b/src/lib/component/Blog.svelte
new file mode 100644
index 0000000..8469701
--- /dev/null
+++ b/src/lib/component/Blog.svelte
@@ -0,0 +1,244 @@
+<script lang="ts">
+ /*** STATE -------------------------------------------- ***/
+ const memos = [
+ "WM-088.txt",
+ "WM-087.txt",
+ "WM-086.txt",
+ "WM-085.txt",
+ "WM-084.txt",
+ "WM-083.txt",
+ "WM-082.txt",
+ "WM-081.txt",
+ "WM-080.txt",
+ "WM-079.txt",
+ "WM-078.txt",
+ "WM-077.txt",
+ "WM-076.txt",
+ "WM-075.txt",
+ "WM-074.txt",
+ "WM-073.txt",
+ "WM-072.txt",
+ "WM-071.txt",
+ "WM-070.txt",
+ "WM-069.txt",
+ "WM-068.txt",
+ "WM-067.txt",
+ "WM-066.txt",
+ "WM-065.txt",
+ "WM-064.txt",
+ "WM-063.txt",
+ "WM-062.txt",
+ "WM-061.txt",
+ "WM-060.txt",
+ "WM-059.txt",
+ "WM-058.txt",
+ "WM-057.txt",
+ "WM-056.txt",
+ "WM-055.txt",
+ "WM-054.txt",
+ "WM-053.txt",
+ "WM-052.txt",
+ "WM-051.txt",
+ "WM-050.txt",
+ "WM-049.txt",
+ "WM-048.txt",
+ "WM-047.txt",
+ "WM-046.txt",
+ "WM-045.txt",
+ "WM-044.txt",
+ "WM-043.txt",
+ "WM-042.txt",
+ "WM-041.txt",
+ "WM-040.txt",
+ "WM-039.txt",
+ "WM-038.txt",
+ "WM-037.txt",
+ "WM-036.txt",
+ "WM-035.txt",
+ "WM-034.txt",
+ "WM-033.txt",
+ "WM-032.txt",
+ "WM-031.txt",
+ "WM-030.txt",
+ "WM-029.txt",
+ "WM-028.txt",
+ "WM-027.txt",
+ "WM-026.txt",
+ "WM-025.txt",
+ "WM-024.txt",
+ "WM-023.txt",
+ "WM-022.txt",
+ "WM-021.txt",
+ "WM-020.txt",
+ "WM-019.txt",
+ "WM-018.txt",
+ "WM-017.txt",
+ "WM-016.txt",
+ "WM-015.txt",
+ "WM-014.txt",
+ "WM-013.txt",
+ "WM-012.txt",
+ "WM-011.txt",
+ "WM-010.txt",
+ "WM-009.txt",
+ "WM-008.txt",
+ "WM-007.txt",
+ "WM-006.txt",
+ "WM-005.txt",
+ "WM-004.txt",
+ "WM-003.txt",
+ "WM-002.txt",
+ "WM-001.txt"
+ ];
+
+ let selectedMemo: string;
+ let selectedMemoContent: string;
+
+ /*** HELPER ------------------------------------------- ***/
+ function processMemo(filename: string): string {
+ const extensionRegex = /\.[^.]+$/; /*** file extension ***/
+ const specialCharsRegex = /[^a-zA-Z0-9]/g; /*** special characters ***/
+ const extensionMatch = filename.match(extensionRegex);
+ let processedFilename = filename;
+ let wrappedExtension = "";
+
+ if (extensionMatch) {
+ processedFilename = filename.slice(0, extensionMatch.index);
+ wrappedExtension = extensionMatch[0].replace(extensionRegex, (match) => `<span class="special-char">${match}</span>`);
+ }
+
+ return processedFilename.replace(specialCharsRegex, (match) => `<span class="special-char">${match}</span>`) + wrappedExtension;
+ }
+
+ async function showMemo(slug: string) {
+ if (slug === selectedMemo) {
+ document.querySelector("li.active")!.classList.remove("active");
+ selectedMemo = ""; /*** toggle ***/
+ } else {
+ selectedMemoContent = "\nloading…\n";
+ selectedMemo = slug;
+
+ try {
+ const response = await fetch("/api/blog.json", {
+ body: JSON.stringify({ filename: slug }),
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/json"
+ },
+ method: "POST"
+ });
+
+ const { content } = await response.json();
+ selectedMemoContent = content;
+ } catch(error) {
+ console.error(error);
+ }
+ }
+ }
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding) * 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) calc(var(--list-indentation) * 4) 0;
+
+ li {
+ margin: 0; padding: 0 0 var(--baseline) calc(var(--baseline) * 2);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 4);
+ }
+
+ &::after {
+ top: calc(var(--list-indentation) * -0.75);
+ width: 1px;
+ }
+
+ &:not(.active) {
+ &::after {
+ height: var(--list-indentation);
+ }
+ }
+
+ &.active::after {
+ height: 100%;
+ }
+ }
+ }
+
+ button {
+ cursor: pointer;
+
+ &:hover {
+ color: var(--uchu-yin-4);
+ text-decoration: underline var(--uchu-yin-2);
+ }
+ }
+
+ .content {
+ line-height: 1.55;
+ position: relative;
+ white-space: pre-wrap;
+
+ text-overflow: ellipsis;
+ overflow-x: hidden;
+
+ &::before {
+ width: 1px; height: calc(100% + 0.75rem);
+ top: -0.75rem; left: calc(var(--list-indentation) * -0.75);
+
+ background-color: var(--color-border);
+ content: "";
+ position: absolute;
+ }
+ }
+
+ :global(.date) {
+ color: var(--uchu-yin-3);
+ }
+
+ :global(.special-char) {
+ color: var(--uchu-yin-3);
+ }
+</style>
+
+<h2>
+ <a href="https://blog.webb.page" target="_blank">blog.webb.page</a>
+</h2>
+
+<ul>
+ {#each memos as memo}
+ <li class:active={selectedMemo === memo}>
+ <button on:click={() => showMemo(memo)}>{@html processMemo(memo)}</button>
+
+ {#if selectedMemo === memo}
+ <div class="content">
+ {@html selectedMemoContent}
+ </div>
+ {/if}
+ </li>
+ {/each}
+</ul>
diff --git a/src/lib/component/CV.svelte b/src/lib/component/CV.svelte
new file mode 100644
index 0000000..b24d2cf
--- /dev/null
+++ b/src/lib/component/CV.svelte
@@ -0,0 +1,78 @@
+<script lang="ts">
+ //// import
+ import { onMount } from "svelte";
+
+ //// var
+ let cv = "";
+
+ //// function
+ async function showContent() {
+ let cvContent = "";
+
+ try {
+ const response = await fetch("/api/cv.json", {
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/json"
+ },
+ method: "POST"
+ });
+
+ const { content } = await response.json();
+ cvContent = content;
+ } catch(error) {
+ console.error(error);
+ }
+
+ return cvContent;
+ }
+
+ onMount(async() => {
+ cv = await showContent();
+ });
+</script>
+
+<style lang="scss">
+ :root {
+ --cv-padding: calc(var(--padding) * 2);
+ }
+
+ h2 {
+ margin: 0 0 var(--cv-padding); padding: var(--padding) var(--cv-padding);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ .content {
+ font-family: monospace;
+ line-height: 1.55;
+ overflow-x: hidden;
+ padding-left: var(--cv-padding);
+ padding-right: var(--cv-padding);
+ position: relative;
+ text-overflow: ellipsis;
+ white-space: pre-line;
+
+ &.loading {
+ padding-bottom: var(--cv-padding);
+ }
+ }
+</style>
+
+<h2>
+ <a href="https://cv.webb.page" target="_blank">cv.webb.page</a>
+</h2>
+
+{#if cv === ""}
+ <div class="content loading">loading&hellip;</div>
+{:else}
+ <div class="content">
+ {cv}
+ </div>
+{/if}
diff --git a/src/lib/component/Music.svelte b/src/lib/component/Music.svelte
new file mode 100644
index 0000000..01b731a
--- /dev/null
+++ b/src/lib/component/Music.svelte
@@ -0,0 +1,354 @@
+<script lang="ts">
+ //// var
+ const bandcamp = [
+ {
+ by: "ViRiX Dreamcore, DV-i",
+ id: "3230467359",
+ title: "UPLINK - CROSSNIQ+ Original Sound",
+ type: "album"
+ },
+ {
+ by: "Pizza Hotline",
+ id: "2804828422",
+ title: "Polygon Island",
+ type: "album"
+ },
+ {
+ by: "leon chang",
+ id: "3685680305",
+ title: "re​:​treat",
+ type: "album"
+ },
+ {
+ by: "Sebdoom",
+ id: "640549190",
+ title: "Cyberpunk 2077 Fanmade Soundtrack",
+ type: "album"
+ },
+ {
+ by: "Teebs",
+ id: "1931742968",
+ title: "Ardour",
+ type: "album"
+ },
+ {
+ by: "Kamasi Washington",
+ id: "3603991383",
+ title: "The Epic",
+ type: "album"
+ },
+ {
+ by: "Thundercat",
+ id: "3794237863",
+ title: "Drunk",
+ type: "album"
+ },
+ {
+ by: "TEEEL",
+ id: "1898404266",
+ title: "Overtime",
+ type: "album"
+ },
+ {
+ by: "MACROSS 82-99",
+ id: "2376445182",
+ title: "It's Own Way [​マ​ク​ロ​ス​MACROSS 82​-​99 Remix]",
+ type: "single"
+ },
+ {
+ by: "LAKIM",
+ id: "635810172",
+ title: "mi_amigos_",
+ type: "single"
+ },
+ {
+ by: "LAKIM",
+ id: "1488235027",
+ title: "the dash",
+ type: "single"
+ },
+ {
+ by: "LAKIM",
+ id: "2759787857",
+ title: "20 Steps",
+ type: "single"
+ },
+ {
+ by: "LAKIM",
+ id: "3667963750",
+ title: "Forward March",
+ type: "single"
+ },
+ {
+ by: "Alexander Brandon",
+ id: "953490707",
+ title: "Space Noir (Original Game Soundtrack)",
+ type: "album"
+ },
+ {
+ by: "Funk Fiction",
+ id: "4237561960",
+ title: "Sonic BeATS",
+ type: "album"
+ },
+ {
+ by: "Introverted Dancefloor",
+ id: "751538854",
+ title: "When I'm Lonely",
+ type: "album"
+ },
+ {
+ by: "C418",
+ id: "1349219244",
+ title: "Minecraft - Volume Alpha",
+ type: "album"
+ },
+ {
+ by: "Cyberpunk 2077",
+ id: "1773399509",
+ title: "Cyberpunk 2077: Radio, Vol. 1 (Original Soundtrack)",
+ type: "album"
+ },
+ {
+ by: "Cyberpunk 2077",
+ id: "1870167199",
+ title: "Cyberpunk 2077: Radio, Vol. 2 (Original Soundtrack)",
+ type: "album"
+ },
+ {
+ by: "Cyberpunk 2077",
+ id: "4182307863",
+ title: "Cyberpunk 2077: Radio, Vol. 3 (Original Soundtrack)",
+ type: "album"
+ },
+ {
+ by: "Cyberpunk 2077",
+ id: "3624438326",
+ title: "Cyberpunk 2077: Radio, Vol. 4 (Original Soundtrack)",
+ type: "album"
+ },
+
+ {
+ by: "Danny Scott Lane",
+ id: "422810680",
+ title: "Shower",
+ type: "album"
+ },
+ {
+ by: "Danny Scott Lane",
+ id: "1584151539",
+ title: "Home Decor",
+ type: "album"
+ },
+ {
+ by: "ALFAXYSM",
+ id: "803715127",
+ title: "CRYSTALLOMANIA (Ver. 1​.​0)",
+ type: "album"
+ },
+ {
+ by: "ALFAXYSM",
+ id: "4001491411",
+ title: "i​-​port",
+ type: "album"
+ },
+ {
+ by: "Limousine",
+ id: "2493506758",
+ title: "Skymall",
+ type: "album"
+ },
+ {
+ by: "nuphory",
+ id: "3332776732",
+ title: "ditherdream1",
+ type: "album"
+ },
+ {
+ by: "AFTA-1",
+ id: "3211763084",
+ title: "Silica_01",
+ type: "track"
+ },
+ {
+ by: "AFTA-1",
+ id: "692190634",
+ title: "103120",
+ type: "album"
+ },
+ {
+ by: "bo en",
+ id: "1387178982",
+ title: "pale machine 10 year anniversary re​-​master",
+ type: "album"
+ },
+ {
+ by: "Mndsgn.",
+ id: "3524388493",
+ title: "Snaxxx",
+ type: "album"
+ },
+ {
+ by: "Zai Kowen",
+ id: "1537466475",
+ title: "Ocean View",
+ type: "album"
+ },
+ {
+ by: "Jungle Fatigue Kru",
+ id: "3478474368",
+ title: "Jungle Fatigue Vol. 3",
+ type: "album"
+ },
+ {
+ by: "DEVIL KILLER",
+ id: "3479413388",
+ title: "Pick A Fucking Car",
+ type: "album"
+ },
+ {
+ by: "Pizza Hotline",
+ id: "71750021",
+ title: "DELIVERY BOY 2099 // 配​達​少​年​2099",
+ type: "album"
+ },
+ {
+ by: "Pizza Hotline",
+ id: "483965118",
+ title: "Pressing Business",
+ type: "album"
+ },
+ {
+ by: "nuphory & Pizza Hotline",
+ id: "1120176299",
+ title: "startup tune feat. nuphory",
+ type: "track"
+ },
+ {
+ by: "Pizza Hotline",
+ id: "2629563639",
+ title: "LEVEL SELECT",
+ type: "album"
+ },
+ {
+ by: "Alice Auer",
+ id: "2689738227",
+ title: "Baby, Cry EP",
+ type: "album"
+ },
+ {
+ by: "DV-i",
+ id: "435475786",
+ title: "Perpetual",
+ type: "track"
+ },
+ ];
+
+ let activeRelease: string;
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding) * 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) calc(var(--list-indentation) * 4) 0;
+
+ li {
+ margin: 0; padding: 0 0 var(--baseline) calc(var(--baseline) * 2);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 4);
+ }
+
+ &::after {
+ width: 1px; height: var(--list-indentation);
+ top: calc(var(--list-indentation) * -0.75);
+ }
+
+ button {
+ cursor: pointer;
+ position: relative;
+
+ &.active {
+ padding-left: calc(var(--baseline) * 2);
+
+ &::before {
+ width: calc(var(--list-indentation) * 1.25); height: 1px;
+ top: 50%; left: calc(calc(var(--baseline) * 2) * -1);
+
+ background-color: var(--color-border);
+ content: "";
+ position: absolute;
+ }
+ }
+ }
+ }
+ }
+
+ .player {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+
+ iframe {
+ max-width: 700px;
+ width: 100%;
+ }
+
+ &::before {
+ width: 1px; height: calc(100% + 0.75rem);
+ top: -0.75rem; left: calc(var(--list-indentation) * -0.75);
+
+ background-color: var(--color-border);
+ content: "";
+ position: absolute;
+ }
+ }
+</style>
+
+<h2>music.webb.page/bandcamp</h2>
+
+<ul>
+ {#each bandcamp as release (release.id)}
+ <li>
+ <button
+ class:active={activeRelease === release.id}
+ on:click={() => activeRelease === release.id ? activeRelease = "" : activeRelease = release.id}>{release.title} by {release.by}</button>
+
+ {#if activeRelease === release.id}
+ <div class="player">
+ <img alt={release.title + " art"} src={"/images/bandcamp/" + release.id + ".jpg"}/>
+ <!-- svelte-ignore a11y-missing-attribute -->
+ {#if release.type === "album"}
+ <iframe style="height: 300px;" src="https://bandcamp.com/EmbeddedPlayer/album={release.id}/size=large/bgcol=ffffff/linkcol=333333/artwork=none/transparent=true/" seamless></iframe>
+ {:else}
+ <iframe style="height: 42px;" src="https://bandcamp.com/EmbeddedPlayer/track={release.id}/size=small/bgcol=ffffff/linkcol=333333/artwork=none/transparent=true/" seamless></iframe>
+ {/if}
+ </div>
+ {/if}
+ </li>
+ {/each}
+</ul>
diff --git a/src/lib/component/MyMusic.svelte b/src/lib/component/MyMusic.svelte
new file mode 100644
index 0000000..aed9098
--- /dev/null
+++ b/src/lib/component/MyMusic.svelte
@@ -0,0 +1,173 @@
+<script lang="ts">
+ //// var
+ const bandcamp = [
+ {
+ by: "Yung Sun",
+ id: "2006853030",
+ title: "Past Future Artifacts",
+ type: "album"
+ },
+ {
+ by: "Yung Sun",
+ id: "1972128885",
+ title: "Don't Leave Me This Way (FRSH×RMX)",
+ type: "single"
+ },
+ {
+ by: "Yung Sun",
+ id: "4219807572",
+ title: "Music from the Love Lounge, Vol. 04",
+ type: "album"
+ },
+ {
+ by: "Yung Sun",
+ id: "3091487133",
+ title: "Spaceman Fresh",
+ type: "album"
+ },
+ {
+ by: "Yung Sun",
+ id: "2247255572",
+ title: "Music from the Love Lounge, Vol. 03",
+ type: "album"
+ },
+ {
+ by: "Yung Sun",
+ id: "955213907",
+ title: "Music from the Love Lounge, Vol. 02",
+ type: "album"
+ },
+ {
+ by: "Yung Sun",
+ id: "1085756242",
+ title: "The Aerobyss Files",
+ type: "album"
+ },
+ {
+ by: "Yung Sun",
+ id: "2714432574",
+ title: "The Construct",
+ type: "album"
+ },
+ {
+ by: "Yung Sun",
+ id: "2723488137",
+ title: "P.I. Noir - The Disappearance of Madamé Sélour",
+ type: "album"
+ },
+ {
+ by: "Yung Sun",
+ id: "512177084",
+ title: "Music from the Love Lounge, Vol. 01",
+ type: "album"
+ }
+ ];
+
+ let activeRelease = "3091487133";
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding)* 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) calc(var(--list-indentation) * 4) 0;
+
+ li {
+ margin: 0; padding: 0 0 var(--baseline) calc(var(--baseline) * 2);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 4);
+ }
+
+ &::after {
+ width: 1px; height: var(--list-indentation);
+ top: calc(var(--list-indentation) * -0.75);
+ }
+
+ button {
+ cursor: pointer;
+ position: relative;
+
+ &.active {
+ padding-left: calc(var(--baseline) * 2);
+
+ &::before {
+ width: calc(var(--list-indentation) * 1.25); height: 1px;
+ top: 50%; left: calc(calc(var(--baseline) * 2) * -1);
+
+ background-color: var(--color-border);
+ content: "";
+ position: absolute;
+ }
+ }
+ }
+ }
+ }
+
+ .player {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+
+ iframe {
+ max-width: 700px;
+ width: 100%;
+ }
+
+ &::before {
+ width: 1px; height: calc(100% + 0.75rem);
+ top: -0.75rem; left: calc(var(--list-indentation) * -0.75);
+
+ background-color: var(--color-border);
+ content: "";
+ position: absolute;
+ }
+ }
+</style>
+
+<h2>music.webb.page</h2>
+
+<ul>
+ {#each bandcamp as release (release.id)}
+ <li>
+ <button
+ class:active={activeRelease === release.id}
+ on:click={() => activeRelease === release.id ? activeRelease = "" : activeRelease = release.id}>{release.title} by {release.by}</button>
+
+ {#if activeRelease === release.id}
+ <div class="player">
+ <img alt={release.title + " art"} src={"/images/bandcamp/" + release.id + ".jpg"}/>
+ <!-- svelte-ignore a11y-missing-attribute -->
+ {#if release.type === "album"}
+ <iframe style="height: 300px;" src="https://bandcamp.com/EmbeddedPlayer/album={release.id}/size=large/bgcol=ffffff/linkcol=333333/artwork=none/transparent=true/" seamless></iframe>
+ {:else}
+ <iframe style="height: 42px;" src="https://bandcamp.com/EmbeddedPlayer/track={release.id}/size=small/bgcol=ffffff/linkcol=333333/artwork=none/transparent=true/" seamless></iframe>
+ {/if}
+ </div>
+ {/if}
+ </li>
+ {/each}
+</ul>
diff --git a/src/lib/component/Project.svelte b/src/lib/component/Project.svelte
new file mode 100644
index 0000000..024045e
--- /dev/null
+++ b/src/lib/component/Project.svelte
@@ -0,0 +1,241 @@
+<script lang="ts">
+ //// var
+ const videoRegex = /\.(mp4|mov|avi|wmv|flv|mkv|webm|m4v)$/i;
+
+ const projects = [
+ {
+ content: "<p>This project was a proof-of-concept utilizing node-webkit (circa 2013) to see if it was possible to create a web browser.</p><p>Performance wasn't great but damn it looks good.</p>",
+ id: "001",
+ media: [
+ "/images/projects/aries_01.jpg",
+ "/images/projects/aries_02.jpg"
+ ],
+ tagline: "web browser",
+ title: "Aries"
+ },
+ {
+ content: `<p>This registrar was built upon the Handshake blockchain to sell domain names and provide hosting. I poured everything I've ever wanted in a registrar into this.</p><p>I've stopped working on it due to <a href="https://blog.neuenet.com/post/devlog-014" target="_blank">happenings I didn't agree with</a> happening in the Handshake community. I will come back to beachfront/ when I feel things are better or I <a href="https://dap.sh?ref=webb.page" target="_blank">fork Handshake</a>.</p>`,
+ id: "002",
+ media: [
+ "/images/projects/beachfront_01.jpg",
+ "/images/projects/beachfront_02.jpg"
+ ],
+ tagline: "next-generation registrar",
+ title: "beachfront/"
+ },
+ {
+ content: `<p>In 2019 I was sick of using SemVer so I made something better (for me). Check it out at <a href="https://chronver.org?ref=webb.page">chronver.org</a>!</p>`,
+ id: "009",
+ media: [
+ "/images/projects/chronver.jpg"
+ ],
+ tagline: "chronologic/calendar-based versioning system",
+ title: "ChronVer"
+ },
+ {
+ content: `<p>I didn’t like the way things were being run on <a href="https://archive.is/vpZCG?ref=webb.page" target="_blank">the Handshake blockchain</a> but <a href="https://archive.is/TMycF?ref=webb.page" target="_blank">I believe in the vision</a> so I made my own. Dap is the better handshake and it’s entering testnet soon.</p><p>Check it out at <a href="https://dap.sh?ref=webb.page">dap.sh</a>!</p>`,
+ id: "010",
+ media: [
+ "/images/projects/dap.jpg"
+ ],
+ tagline: "a secure foundation for the neue internet",
+ title: "Dap"
+ },
+ {
+ content: "<p>In 2014 I had the idea for a &ldquo;responsive operating system&rdquo; that could run on any device from a Raspberry Pi to a desktop PC.</p><p>There's no reason why a neat OS GUI shouldn't look as good as anime interfaces.</p>",
+ id: "003",
+ media: [
+ "https://nickel.video/embed/9Ik01L_lK5n1"
+ ],
+ tagline: "responsive operating system",
+ title: "hikari"
+ },
+ // {
+ // content: `<p>This registry was built upon the Handshake blockchain to provide infrastructure for my TLDs. I wrote a nameserver in Deno, created a DNSSEC tool to secure said TLDs, and so on. I built and blogged a <strong>lot</strong> about my vision for the internet and freely gave away ideas and a roadmap for others to adopt.</p><p>I've stopped working on it due to <a href="https://archive.is/vpZCG?ref=webb.page" target="_blank">happenings I didn't agree with</a> happening in the Handshake community. I will come back to beachfront/ when I feel things are better or I <a href="https://dap.sh?ref=webb.page" target="_blank">fork Handshake</a>.</p>`,
+ // id: "004",
+ // media: [
+ // "/images/projects/neuenet_01.jpg",
+ // "/images/projects/neuenet_02.jpg"
+ // ],
+ // tagline: "registry for the Neue Internet",
+ // title: "Neuenet"
+ // },
+ {
+ content: `<p>I just want a place to upload my Fortnite clips and <del>fuck</del> <em>I'm not a fan of</em> Google. No other platform out there makes sense either so once again, I gotta build what I want to use.</p><p>Check it out at <a href="https://nickel.video?ref=webb.page">nickel.video</a>!</p>`,
+ id: "005",
+ media: [
+ "/images/projects/nickel_01.jpg"
+ ],
+ tagline: "short-form video platform",
+ title: "Nickel"
+ },
+ {
+ content: "<p>There's no point in competing with Google, they've got a 20 year headstart&hellip;but what if you could just ask someone who's knowledgeable af? You do realize Google only has data because of us. Right?</p><p>Anyhoo, there's a lot of parts to make this work and I'm busy with side quests at the moment.</p>",
+ id: "006",
+ media: [],
+ tagline: "P2P search",
+ title: "queree"
+ },
+ {
+ content: `<p>Around 2017 or so I had the idea of a new kind of social network that brought back the great parts of Myspace while empowering people with comprehensive customization, data, and moderation tools. Bluesky does an excellent job with the latter but being VC-backed means it's only a matter of time before enshittification.</p><p><del>Work in progress.</del> Mastodon and Bluesky are good enough so I decided to stop working on this.</p>`,
+ id: "007",
+ media: [
+ "/images/projects/socii_01.jpg"
+ ],
+ tagline: "the social network",
+ title: "socii"
+ },
+ {
+ content: `<p>After I learned about <a href="https://evilmartians.com/chronicles/oklch-in-css-why-quit-rgb-hsl?ref=webb.page" target="_blank">the OKLCH color space</a> and saw how much more vibrant colors were than HEX, I delved into creating the perfect color palette for me. I spent nearly a year copy/pasting my CSS variables in my projects until I decided to have a unified place for them.</p><p><em>For some reason</em> <a href="https://news.ycombinator.com/item?id=43072338?ref=webb.page" target="_blank">my post on HackerNews</a> about uchū got super popular, which was neat. 600+ points and 900+ Github stars is wild. A handful of weirdos got irate, which was&hellip;alarming. Maybe touch grass?</p><p>Check out the site at <a href="https://uchu.style?ref=webb.page" target="_blank">uchu.style</a>!</p>`,
+ id: "008",
+ media: [
+ "/images/projects/uchu_01.jpg"
+ ],
+ tagline: "the color palette for internet lovers",
+ title: "uchū"
+ }
+ ];
+
+ let activeProject = "005";
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding) * 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) calc(var(--list-indentation) * 4) 0;
+
+ li {
+ margin: 0; padding: 0 0 var(--baseline) calc(var(--baseline) * 2);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 4);
+ }
+
+ &::after {
+ width: 1px; height: var(--list-indentation);
+ top: calc(var(--list-indentation) * -0.75);
+ }
+
+ button {
+ cursor: pointer;
+ position: relative;
+
+ &.active {
+ padding-left: calc(var(--baseline) * 2);
+
+ &::before {
+ width: calc(var(--list-indentation) * 1.25); height: 1px;
+ top: 50%; left: calc(calc(var(--baseline) * 2) * -1);
+
+ background-color: var(--color-border);
+ content: "";
+ position: absolute;
+ }
+ }
+ }
+ }
+ }
+
+ .player {
+ align-items: center;
+ display: flex;
+ flex-direction: column;
+ position: relative;
+
+ &::before {
+ width: 1px; height: calc(100% + 0.75rem);
+ top: -0.75rem; left: calc(var(--list-indentation) * -0.75);
+
+ background-color: var(--color-border);
+ content: "";
+ position: absolute;
+ }
+ }
+
+ :global(p) {
+ line-height: var(--line-height);
+ margin-bottom: 0;
+ padding-top: calc(var(--padding) * 2);
+ text-align: left;
+ white-space: normal;
+ width: 100%;
+
+ :global(a) {
+ text-decoration: underline var(--uchu-yin-2);
+ }
+ }
+
+ figure {
+ margin-bottom: 0;
+ }
+
+ iframe {
+ aspect-ratio: 16 / 9;
+ margin-top: calc(var(--padding) * 2);
+ width: 100%;
+ }
+
+ video {
+ padding-top: calc(var(--padding) * 2);
+ width: 100%;
+ }
+</style>
+
+<h2>projects.webb.page</h2>
+
+<ul>
+ {#each projects as project (project.id)}
+ <li>
+ <button
+ class:active={activeProject === project.id}
+ on:click={() => activeProject === project.id ? activeProject = "" : activeProject = project.id}>{project.title} &middot; {project.tagline}</button>
+
+ {#if activeProject === project.id}
+ <div class="player">
+ {@html project.content}
+
+ {#each project.media as mediaItem}
+ {#if videoRegex.test(mediaItem)}
+ <figure>
+ <!-- svelte-ignore a11y-media-has-caption -->
+ <video controls>
+ <source src={mediaItem}/>
+ </video>
+ </figure>
+ {:else if mediaItem.includes("nickel.video")}
+ <iframe src={mediaItem} title=""></iframe>
+ {:else}
+ <figure>
+ <img alt="" src={mediaItem}/>
+ <figcaption></figcaption>
+ </figure>
+ {/if}
+ {/each}
+ </div>
+ {/if}
+ </li>
+ {/each}
+</ul>
diff --git a/src/lib/component/Promo.svelte b/src/lib/component/Promo.svelte
new file mode 100644
index 0000000..54f57bd
--- /dev/null
+++ b/src/lib/component/Promo.svelte
@@ -0,0 +1,66 @@
+<script lang="ts"></script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding)* 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) 0.25rem 0;
+
+ li {
+ margin: 0; padding: 0 0 var(--baseline) calc(var(--baseline) * 2);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 4);
+ }
+
+ &::after {
+ width: 1px; height: var(--list-indentation);
+ top: calc(var(--list-indentation) * -0.75);
+ }
+ }
+
+ a {
+ text-decoration: underline var(--uchu-yin-2);
+
+ span {
+ padding-left: 0.2rem;
+ position: relative;
+ text-decoration: underline var(--uchu-gray-2);
+ }
+ }
+ }
+</style>
+
+<h2>promo.webb.page</h2>
+
+<ul>
+ <li>
+ <a href="https://www.eyebuydirect.com/referral/5217807">EyeBuyDirect<span> (prescription eyewear)</span></a>
+ </li>
+
+ <li>
+ <a href="https://lnk.rise-ai.com/68SlyIVHzDVybhw">Moment<span> (iPhone photography/videography tools)</span></a>
+ </li>
+</ul>
diff --git a/src/lib/component/Remarks.svelte b/src/lib/component/Remarks.svelte
new file mode 100644
index 0000000..6633654
--- /dev/null
+++ b/src/lib/component/Remarks.svelte
@@ -0,0 +1,179 @@
+<script lang="ts">
+ /*** STATE -------------------------------------------- ***/
+ const remarks = [
+ "WR-023.txt",
+ "WR-022.txt",
+ "WR-021.txt",
+ "WR-020.txt",
+ "WR-019.txt",
+ "WR-018.txt",
+ "WR-017.txt",
+ "WR-016.txt",
+ "WR-015.txt",
+ "WR-014.txt",
+ "WR-013.txt",
+ "WR-012.txt",
+ "WR-011.txt",
+ "WR-010.txt",
+ "WR-009.txt",
+ "WR-008.txt",
+ "WR-007.txt",
+ "WR-006.txt",
+ "WR-005.txt",
+ "WR-004.txt",
+ "WR-003.txt",
+ "WR-002.txt",
+ "WR-001.txt"
+ ];
+
+ let selectedRemark: string;
+ let selectedRemarkContent: string;
+
+ /*** HELPER ------------------------------------------- ***/
+ function processNote(filename: string): string {
+ const extensionRegex = /\.[^.]+$/; /*** file extension ***/
+ const specialCharsRegex = /[^a-zA-Z0-9]/g; /*** special characters ***/
+ const extensionMatch = filename.match(extensionRegex);
+ let processedFilename = filename;
+ let wrappedExtension = "";
+
+ if (extensionMatch) {
+ processedFilename = filename.slice(0, extensionMatch.index);
+ wrappedExtension = extensionMatch[0].replace(extensionRegex, (match) => `<span class="special-char">${match}</span>`);
+ }
+
+ return processedFilename.replace(specialCharsRegex, (match) => `<span class="special-char">${match}</span>`) + wrappedExtension;
+ }
+
+ async function showNote(slug: string) {
+ if (slug === selectedRemark) {
+ document.querySelector("li.active")!.classList.remove("active");
+ selectedRemark = ""; /*** toggle ***/
+ } else {
+ selectedRemarkContent = "\nloading…\n";
+ selectedRemark = slug;
+
+ try {
+ const response = await fetch("/api/remarks.json", {
+ body: JSON.stringify({ filename: slug }),
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/json"
+ },
+ method: "POST"
+ });
+
+ const { content } = await response.json();
+ selectedRemarkContent = content;
+ } catch(error) {
+ console.error(error);
+ }
+ }
+ }
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding)* 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) calc(var(--list-indentation) / 2) 0;
+
+ li {
+ margin: 0; padding: 0 0 var(--baseline) calc(var(--baseline) * 2);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 4);
+ }
+
+ &::after {
+ top: calc(var(--list-indentation) * -0.75);
+ width: 1px;
+ }
+
+ &:not(.active) {
+ &::after {
+ height: var(--list-indentation);
+ }
+ }
+
+ &.active::after {
+ height: 100%;
+ }
+ }
+ }
+
+ button {
+ cursor: pointer;
+
+ &:hover {
+ color: var(--uchu-yin-4);
+ text-decoration: underline var(--uchu-yin-2);
+ }
+ }
+
+ .content {
+ line-height: 1.55;
+ position: relative;
+ white-space: pre-wrap;
+
+ text-overflow: ellipsis;
+ overflow-x: hidden;
+
+ &::before {
+ width: 1px; height: calc(100% + 0.75rem);
+ top: -0.75rem; left: calc(var(--list-indentation) * -0.75);
+
+ background-color: var(--color-border);
+ content: "";
+ position: absolute;
+ }
+ }
+
+ :global(.date) {
+ color: var(--uchu-yin-3);
+ }
+
+ :global(.special-char) {
+ color: var(--uchu-yin-3);
+ }
+</style>
+
+<h2>
+ <a href="https://blog.webb.page/remarks" target="_blank">blog.webb.page/remarks</a>
+</h2>
+
+<ul>
+ {#each remarks as remark}
+ <li class:active={selectedRemark === remark}>
+ <button on:click={() => showNote(remark)}>{@html processNote(remark)}</button>
+
+ {#if selectedRemark === remark}
+ <div class="content">
+ {@html selectedRemarkContent}
+ </div>
+ {/if}
+ </li>
+ {/each}
+</ul>
diff --git a/src/lib/component/Ring.svelte b/src/lib/component/Ring.svelte
new file mode 100644
index 0000000..01e4910
--- /dev/null
+++ b/src/lib/component/Ring.svelte
@@ -0,0 +1,312 @@
+<script lang="ts">
+ //// var
+ const protocolRegex = /^(https?:\/\/)(www\.)?/;
+ const referrer = "?ref=webb.page";
+
+ const friends = [
+ {
+ alias: "",
+ name: "Anna Maria",
+ urls: ["https://annamariaperez.com"]
+ },
+ {
+ alias: "Astro✨Dim",
+ name: "",
+ urls: ["https://www.astrodim.com"]
+ },
+ {
+ alias: "bias",
+ name: "",
+ urls: [
+ "https://www.mendicantbias.xyz",
+ "https://www.punk.energy",
+ "https://www.punkstep.com"
+ ]
+ },
+ {
+ alias: "",
+ name: "Brad Barrish",
+ urls: ["https://bradbarrish.me"]
+ },
+ {
+ alias: "",
+ name: "Cassandra Heart",
+ urls: ["https://quilibrium.com"]
+ },
+ {
+ alias: "",
+ name: "Cornelius Toole",
+ urls: ["https://micro.corntoole.com"]
+ },
+ {
+ alias: "",
+ name: "David Yarde",
+ urls: ["https://davidyarde.com"]
+ },
+ {
+ alias: "",
+ name: "Dewon Hall",
+ urls: ["https://thatcreativeguy.com"]
+ },
+ {
+ alias: "riotgoools",
+ name: "elle",
+ urls: [
+ "https://ellesho.me/page",
+ "https://riotgoools.com",
+ "https://veryinter.net/person"
+ ]
+ },
+ {
+ alias: "",
+ name: "EmmoLei Sankofa",
+ urls: ["https://www.e-sankofa.com"]
+ },
+ {
+ alias: "eskimo",
+ name: "",
+ urls: ["https://eskimo.software"]
+ },
+ {
+ alias: "",
+ name: "Iheanyi Ekechukwu",
+ urls: ["https://iheanyi.com"]
+ },
+ {
+ alias: "",
+ name: "Jacky Alciné",
+ urls: ["https://jacky.wtf"]
+ },
+ {
+ alias: "",
+ name: "Jamar Torres",
+ urls: ["https://www.jamar.dev"]
+ },
+ {
+ alias: "",
+ name: "Jesse Youngblood",
+ urls: ["https://jesseyoungblood.com"]
+ },
+ {
+ alias: "",
+ name: "Jonathon Toon",
+ urls: ["https://jonathontoon.com"]
+ },
+ {
+ alias: "",
+ name: "Jordan Greene",
+ urls: ["https://www.jordankgreen.com"]
+ },
+ {
+ alias: "",
+ name: "Josh Sender",
+ urls: ["https://www.joshsender.com"]
+ },
+ {
+ alias: "",
+ name: "Justin Edmund",
+ urls: ["https://www.jedmund.com"]
+ },
+ {
+ alias: "",
+ name: "Kathy Varela",
+ urls: ["https://www.kathyvarela.com"]
+ },
+ {
+ alias: "DJ Brisseaux",
+ name: "Karl Brisseaux",
+ urls: ["https://kbrissy.com"]
+ },
+ {
+ alias: "brisseaux",
+ name: "Kervin Brisseaux",
+ urls: ["https://www.brisseaux.com"]
+ },
+ {
+ alias: "",
+ name: "Kumail Hunaid",
+ urls: ["https://kumailht.com"]
+ },
+ {
+ alias: "my wife",
+ name: "Lakeisha Webb",
+ urls: ["https://sincerelyshantelle.com"]
+ },
+ {
+ alias: "",
+ name: "Lauren Dorman",
+ urls: ["https://laurendorman.io"]
+ },
+ {
+ alias: "",
+ name: "Matt Thomas",
+ urls: ["https://insidae.com"]
+ },
+ {
+ alias: "",
+ name: "Maurice Cherry",
+ urls: ["https://www.mauricecherry.com"]
+ },
+ {
+ alias: "Fresh Daily",
+ name: "Michael Richardson",
+ urls: ["https://freshdaily.godaddysites.com"]
+ },
+ {
+ alias: "",
+ name: "Mike Toney",
+ urls: ["https://miketoney.com"]
+ },
+ {
+ alias: "",
+ name: "Ollie Barker",
+ urls: ["https://www.olliebarker.co.uk"]
+ },
+ {
+ alias: "",
+ name: "Rasmus Andersson",
+ urls: ["https://rsms.me"]
+ },
+ {
+ alias: "",
+ name: "Reginé Gilbert",
+ urls: ["https://reginegilbert.com"]
+ },
+ {
+ alias: "",
+ name: "Samantha",
+ urls: ["https://humankind.place"]
+ },
+ {
+ alias: "",
+ name: "Sang Lee",
+ urls: ["https://www.sanglee.me"]
+ },
+ {
+ alias: "SegamanXero",
+ name: "",
+ urls: ["https://www.segamanxero.com"]
+ },
+ {
+ alias: "",
+ name: "Selena Rox",
+ urls: ["https://selenarox.com"]
+ },
+ {
+ alias: "",
+ name: "Shomi Patwary",
+ urls: ["https://www.illusivemedia.com"]
+ },
+ {
+ alias: "Skunkie",
+ name: "Tara",
+ urls: ["https://punklabs.com"]
+ },
+ {
+ alias: "pugson",
+ name: "Wojtek Witkowski",
+ urls: ["https://wojtek.im"]
+ },
+ {
+ alias: "Z",
+ name: "",
+ urls: ["https://blacksocialists.us"]
+ }
+ ];
+
+ //// function
+ function processUrl(url: string) {
+ const ancillaryParts = url.match(protocolRegex);
+
+ if (!ancillaryParts)
+ return url;
+
+ const ancillaryPartsMatch = ancillaryParts[0];
+ const restOfUrl = url.slice(ancillaryPartsMatch.length);
+
+ return `<span class="special-char">${ancillaryPartsMatch}</span>${restOfUrl}`;
+ }
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding)* 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) calc(var(--list-indentation) * 4) 0;
+
+ li {
+ margin: 0; padding: 0 0 var(--baseline) calc(var(--baseline) * 2);
+
+ line-height: var(--line-height);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 2.5);
+ }
+
+ &::after {
+ width: 1px; height: var(--list-indentation);
+ top: calc(var(--list-indentation) * -0.6);
+ }
+
+ &:not(:last-of-type) {
+ &::after {
+ height: 100%;
+ }
+ }
+ }
+
+ a {
+ text-decoration: underline var(--uchu-yin-2);
+ }
+ }
+
+ :global(.special-char) {
+ color: var(--uchu-yin-3);
+ letter-spacing: -0.05rem;
+ }
+</style>
+
+<h2>ring.webb.page</h2>
+
+<ul>
+ {#each friends as friend}
+ <li>
+ {#if friend.name.length}
+ {friend.name}
+ {#if friend.alias}
+ ({friend.alias})
+ {/if}
+ {:else}
+ {friend.alias}
+ {/if}
+
+ <div>
+ {#each friend.urls as url}
+ <a href={url + referrer} target="_blank">{@html processUrl(url)}</a><br/>
+ {/each}
+ </div>
+ </li>
+ {/each}
+</ul>
diff --git a/src/lib/component/Social.svelte b/src/lib/component/Social.svelte
new file mode 100644
index 0000000..1e469eb
--- /dev/null
+++ b/src/lib/component/Social.svelte
@@ -0,0 +1,112 @@
+<script lang="ts">
+ //// import
+ import { format as formatDate } from "timeago.js";
+ import { onMount } from "svelte";
+
+ //// var
+ const videoRegex = /\.(mp4|mov|avi|wmv|flv|mkv|webm|m4v)$/i;
+ let mastodonLoaded = false;
+ let mastodonPostContent = "";
+ let mastodonPostDate = "";
+ let mastodonPostLink = "";
+ let mastodonPostMedia: string[] = [];
+
+ //// function
+ async function mastodon() {
+ const response = await fetch("/api/mastodon.json", {
+ headers: {
+ "Accept": "application/json",
+ "Content-Type": "application/json"
+ },
+ method: "POST"
+ });
+
+ const { created, content, link, media } = await response.json();
+
+ mastodonPostContent = content;
+ mastodonPostDate = created;
+ mastodonPostLink = link;
+ mastodonPostMedia = media;
+ mastodonLoaded = true;
+ }
+
+ //// ready
+ onMount(() => {
+ mastodon();
+ });
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding)* 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ blockquote {
+ padding: 0 calc(var(--padding) * 2) calc(var(--padding) * 2);
+ white-space: normal;
+ }
+
+ .media {
+ display: flex;
+ flex-direction: row;
+ margin-bottom: var(--padding);
+
+ figure {
+ margin: 0 var(--padding) var(--padding) 0;
+ }
+
+ img {
+ width: 100%; height: 100%;
+ object-fit: contain;
+ }
+ }
+
+ :global(blockquote p) {
+ margin-bottom: calc(var(--block-spacing-bottom) / 2);
+ padding-top: 0;
+ }
+</style>
+
+<h2>social.webb.page/mastodon</h2>
+
+{#if mastodonLoaded}
+ <blockquote cite={mastodonPostLink}>
+ {@html mastodonPostContent}
+
+ {#if mastodonPostMedia.length}
+ <div class="media">
+ {#each mastodonPostMedia as media}
+ {#if videoRegex.test(media)}
+ <!-- svelte-ignore a11y-media-has-caption -->
+ <video controls>
+ <source src={media}/>
+ </video>
+ {:else}
+ <figure>
+ <img alt="mastodon media" src={media}/>
+ <figcaption></figcaption>
+ </figure>
+ {/if}
+ {/each}
+ </div>
+ {/if}
+
+ <p>&mdash;
+ <a href={mastodonPostLink} rel="nofollow noopener noreferrer" target="_blank">
+ <time datetime={mastodonPostDate}>{formatDate(mastodonPostDate)}</time>
+ </a>
+ </p>
+ </blockquote>
+{:else}
+ <blockquote>
+ <p>loading&hellip;</p>
+ </blockquote>
+{/if}
diff --git a/src/lib/component/Support.svelte b/src/lib/component/Support.svelte
new file mode 100644
index 0000000..ceff0fd
--- /dev/null
+++ b/src/lib/component/Support.svelte
@@ -0,0 +1,70 @@
+<script lang="ts">
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding)* 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-bottom: 0;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) 0.25rem 0;
+
+ li {
+ margin: 0; padding: 0 0 calc(var(--baseline) / 2) calc(var(--baseline) * 2);
+
+ line-height: var(--line-height);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 2.5);
+ }
+
+ &::after {
+ width: 1px; height: var(--list-indentation);
+ top: calc(var(--list-indentation) * -0.6);
+ }
+
+ code {
+ line-height: inherit;
+ white-space: nowrap;
+ }
+ }
+ }
+
+ p {
+ margin: 0; padding: 0 var(--about-padding) var(--about-padding);
+ white-space: break-spaces;
+ }
+</style>
+
+<h2>support.webb.page</h2>
+
+<ul>
+ <li>Bitcoin: <code>bc1q7znsdk9eudlctcmhlel3d9vmxz4vmgedndx5qn</code></li>
+ <li>Cardano: <code>addr1q89wumdmfsjn9hsk57cw2g4wwyv2uz5g97c6pyxux03vpylju9njft3tr69mheqh5k3wh3d5h9f88hkecn4h6h2jll5sdncex7</code></li>
+ <li>Ethereum: <code>0x9c94aafF71e4A58Cd9fb5bE2005b8Dc952C4c2e3</code></li>
+ <li>Solana: <code>DbJ7peGAyrNNH8J1yJ82S7ZnK3BJenefjVQtGLM9pMny</code></li>
+ <li>Tezos: <code>tz1UVwR1MhqfytRbypB1jeFbFKTLu4LSFE9A</code></li>
+</ul>
+
+<p>I have a number of domains for sale. Past projects never embarked on but names I don’t want to let expire just yet; <a href="https://dsgn.io">dsgn.io</a>, <a href="https://hackerspace.wtf">hackerspace.wtf</a>, <a href="https://imagination.industries">imagination.industries</a>, and <a href="https://phoenix.systems">phoenix.systems</a>.</p>
diff --git a/src/lib/component/Uses.svelte b/src/lib/component/Uses.svelte
new file mode 100644
index 0000000..4c91944
--- /dev/null
+++ b/src/lib/component/Uses.svelte
@@ -0,0 +1,145 @@
+<script lang="ts">
+ //// var
+ const referrer = "?ref=webb.page";
+
+ const products = [
+ {
+ comment: "why the hell would I remember a password?",
+ name: "1Password",
+ url: "https://1password.com"
+ },
+ {
+ comment: "Terminal emulator",
+ name: "Alacritty",
+ url: "https://alacritty.org"
+ },
+ {
+ comment: "when songs aren’t on Bandcamp",
+ name: "Apple Music",
+ url: "https://music.apple.com"
+ },
+ {
+ comment: "default browser",
+ name: "Arc",
+ url: "https://arc.net"
+ },
+ {
+ comment: "because not having a backup hurts",
+ name: "Backblaze",
+ url: "https://www.backblaze.com"
+ },
+ {
+ comment: "better than ChatGPT",
+ name: "Claude",
+ url: "https://claude.ai"
+ },
+ {
+ comment: "the best database on the planet",
+ name: "Gel",
+ url: "https://www.geldata.com"
+ },
+ {
+ comment: "Terminal emulator with tab support",
+ name: "Ghostty",
+ url: "https://ghostty.org"
+ },
+ {
+ comment: "best search engine after Neeva sold out",
+ name: "Kagi",
+ url: "https://kagi.com"
+ },
+ {
+ comment: "taking notes on the go",
+ name: "Lumen",
+ url: "https://uselumen.com"
+ },
+ {
+ comment: "one day I’ll graduate to hosting my own IMAP software instead",
+ name: "Mail-in-a-Box",
+ url: "https://mailinabox.email"
+ },
+ {
+ comment: "RSS is alive and well",
+ name: "NetNewsWire",
+ url: "https://netnewswire.com"
+ },
+ {
+ comment: "when Safari is being dumb",
+ name: "Orion",
+ url: "https://kagi.com/orion"
+ },
+ {
+ comment: "taking notes at my computer",
+ name: "Obsidian",
+ url: "https://obsidian.md"
+ },
+ {
+ comment: "code editor",
+ name: "Sublime Text",
+ url: "https://www.sublimetext.com"
+ },
+ ];
+</script>
+
+<style lang="scss">
+ h2 {
+ margin: 0 0 calc(var(--padding)* 2); padding: var(--padding) calc(var(--padding) * 2);
+
+ background-color: var(--color-border);
+ color: var(--uchu-yin-7);
+ font-size: 1rem;
+ line-height: inherit;
+ position: sticky;
+ top: 0;
+ z-index: 1;
+ }
+
+ ul {
+ line-height: 1;
+ margin-left: calc(var(--list-indentation) / 2);
+ padding: 0 var(--list-indentation) 0.25rem 0;
+
+ li {
+ margin: 0; padding: 0 0 var(--baseline) calc(var(--baseline) * 2);
+ position: relative;
+
+ &::before,
+ &::after {
+ background-color: var(--color-border);
+ content: "";
+ left: 0;
+ position: absolute;
+ }
+
+ &::before {
+ width: calc(var(--list-indentation) / 2); height: 1px;
+ top: calc(var(--list-indentation) / 4);
+ }
+
+ &::after {
+ width: 1px; height: var(--list-indentation);
+ top: calc(var(--list-indentation) * -0.75);
+ }
+ }
+
+ a {
+ text-decoration: underline var(--uchu-yin-2);
+
+ span {
+ padding-left: 0.2rem;
+ position: relative;
+ text-decoration: underline var(--uchu-gray-2);
+ }
+ }
+ }
+</style>
+
+<h2>uses.webb.page</h2>
+
+<ul>
+ {#each products as product}
+ <li>
+ <a href={product.url + referrer}>{product.name}<span> ({product.comment})</span></a>
+ </li>
+ {/each}
+</ul>
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
new file mode 100644
index 0000000..be635e8
--- /dev/null
+++ b/src/routes/+layout.svelte
@@ -0,0 +1,147 @@
+<script lang="ts">
+ //// import
+ import { browser } from "$app/environment";
+ import { onMount } from "svelte";
+
+ //// util
+ import "./../../sass/global.scss";
+
+ //// var
+ const scrollControl = createScrollPreventer();
+
+ //// function
+ function horizontalScroll(event: WheelEvent): void {
+ const target = (event.target as HTMLElement).closest("section");
+
+ if (target && isVerticallyScrollable(target) && !isAtVerticalScrollLimit(target, event.deltaY) && isFullyInView(target))
+ return; /// ignore vertically scrollable elements
+
+ if (event.deltaY !== 0) {
+ event.preventDefault();
+ window.scrollBy({ left: event.deltaY });
+ }
+ }
+
+ function isAtVerticalScrollLimit(element: HTMLElement, deltaY: number) {
+ if (!element || !deltaY)
+ return false;
+
+ return (deltaY > 0 && Math.abs(element.scrollTop + element.clientHeight - element.scrollHeight) < 1) ||
+ (deltaY < 0 && element.scrollTop <= 0);
+ }
+
+ function isFullyInView(element: HTMLElement) {
+ if (!element)
+ return false;
+
+ const rect = element.getBoundingClientRect();
+
+ return (
+ rect.top >= 0 &&
+ rect.left >= 24 && // 0
+ rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
+ rect.right <= (window.innerWidth || document.documentElement.clientWidth)
+ );
+ }
+
+ function isVerticallyScrollable(element: HTMLElement) {
+ if (!element)
+ return false;
+
+ return element.scrollHeight > element.clientHeight;
+ }
+
+ function createScrollPreventer(): { prevent: () => void; allow: () => void } {
+ return {
+ prevent: () => {
+ window.removeEventListener("wheel", horizontalScroll);
+ },
+ allow: () => {
+ window.addEventListener("wheel", horizontalScroll, { passive: false });
+ }
+ };
+ }
+
+ /// ready
+ onMount(() => {
+ browser && scrollControl.allow();
+ });
+</script>
+
+<style lang="scss">
+ div {
+ width: fit-content; height: 100%;
+
+ display: flex;
+ white-space: nowrap;
+ }
+
+ aside {
+ width: 100%; height: calc(var(--line-height) * 2);
+ bottom: 0; left: 0;
+
+ align-items: center;
+ display: flex;
+ font-size: 0.8rem;
+ margin: 0;
+ padding-left: calc(var(--padding) * 3);
+ position: fixed;
+ text-transform: lowercase;
+
+ > * {
+ display: block;
+ margin-right: calc(var(--padding) * 2);
+ position: relative;
+ user-select: none;
+
+ &:not(:last-child) {
+ &::after {
+ content: "/";
+ font-weight: normal;
+ opacity: 0.15;
+ position: absolute;
+ right: calc(var(--padding) * -1.5);
+ }
+ }
+ }
+
+ em {
+ white-space: nowrap;
+ }
+
+ img {
+ image-rendering: pixelated;
+ }
+ }
+
+ :global(a) {
+ transition: none;
+
+ &:hover {
+ color: var(--uchu-yin-4);
+ text-decoration: underline var(--uchu-yin-2);
+ }
+ }
+</style>
+
+<div>
+ <slot></slot>
+</div>
+
+<aside>
+ <strong>Contact</strong>
+ <span>paul@webb.page</span>
+ <!-- <a href="https://netopwibby.socii.network" target="_blank">socii@netopwibby</a> -->
+ <a href="https://social.coop/@netopwibby" target="_blank">@netopwibby@social.coop</a>
+ <a href="https://blacksky.community/profile/webb.page" target="_blank">@webb.page</a>
+ <em>scroll vertically to scroll horizontally</em>
+ <img alt="" src="/88x31/a.gif"/>
+ <img alt="" src="/88x31/b.gif"/>
+ <img alt="" src="/88x31/c.gif"/>
+ <a class="banner-wrapper" href="https://www.webb.page">
+ <img alt="" src="/88x31/d.png"/>
+ </a>
+ <a class="banner-wrapper" href="https://ellesho.me/page/?ref=https://webb.page">
+ <img alt="" src="/88x31/elle.webp"/>
+ </a>
+</aside>
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
new file mode 100644
index 0000000..a0f1a90
--- /dev/null
+++ b/src/routes/+page.svelte
@@ -0,0 +1,115 @@
+<script lang="ts">
+ //// import
+ import { version } from "../../package.json";
+
+ //// component
+ import About from "$lib/component/About.svelte";
+ import Blog from "$lib/component/Blog.svelte";
+ import CV from "$lib/component/CV.svelte";
+ import Music from "$lib/component/Music.svelte";
+ import MyMusic from "$lib/component/MyMusic.svelte";
+ import Remarks from "$lib/component/Remarks.svelte";
+ import Project from "$lib/component/Project.svelte";
+ import Promo from "$lib/component/Promo.svelte";
+ import Ring from "$lib/component/Ring.svelte";
+ import Social from "$lib/component/Social.svelte";
+ import Support from "$lib/component/Support.svelte";
+ import Uses from "$lib/component/Uses.svelte";
+</script>
+
+<style lang="scss">
+ :root {
+ --color-border: var(--uchu-yin-2);
+ }
+
+ section {
+ max-width: 700px;
+ overflow-y: auto;
+
+ @media (min-width: 601px) {
+ min-width: 500px;
+ width: 45vw;
+ }
+
+ @media (max-width: 600px) {
+ width: 75vw;
+ }
+
+ &:not(:last-of-type) {
+ margin-right: 30px;
+ }
+
+ &:last-of-type {
+ margin-right: var(--line-height);
+ }
+ }
+
+ .full-height {
+ height: 100%;
+ }
+
+ .with-background {
+ background-color: var(--uchu-gray-2);
+ overflow-x: hidden;
+ }
+</style>
+
+<svelte:head>
+ <meta name="version" content={version}/>
+</svelte:head>
+
+<section>
+ <aside class="with-background">
+ <About/>
+ </aside>
+</section>
+
+<section>
+ <aside class="with-background">
+ <Support/>
+ </aside>
+
+ <aside class="with-background">
+ <CV/>
+ </aside>
+</section>
+
+<section>
+ <aside class="with-background">
+ <Remarks/>
+ </aside>
+
+ <aside class="with-background">
+ <Blog/>
+ </aside>
+</section>
+
+<section>
+ <aside class="with-background">
+ <Social/>
+ </aside>
+
+ <aside class="with-background">
+ <Promo/>
+ </aside>
+
+ <aside class="with-background">
+ <Uses/>
+ </aside>
+</section>
+
+<section class="full-height with-background">
+ <Project/>
+</section>
+
+<section class="full-height with-background">
+ <MyMusic/>
+</section>
+
+<section class="full-height with-background">
+ <Ring/>
+</section>
+
+<section class="full-height with-background">
+ <Music/>
+</section>
diff --git a/src/routes/api/blog.json/+server.ts b/src/routes/api/blog.json/+server.ts
new file mode 100644
index 0000000..f416647
--- /dev/null
+++ b/src/routes/api/blog.json/+server.ts
@@ -0,0 +1,51 @@
+
+
+
+/*** IMPORT ------------------------------------------- ***/
+
+import { error, json } from "@sveltejs/kit";
+
+/*** EXPORT ------------------------------------------- ***/
+
+export const POST = async({ fetch, request }) => {
+ try {
+ const { filename } = await request.json();
+
+ const response = await fetch(`https://blog.webb.page/${String(filename)}`, {
+ headers: { "Content-Type": "text/plain" },
+ method: "GET"
+ });
+
+ const memo = parseMemo(await response.text());
+ memo.push(` <span class="special-char">[</span><a href="https://blog.webb.page/${String(filename).split(".txt")[0]}">READ</a><span class="special-char">]</span>`, "\n");
+
+ return json({ content: memo.join("\n") });
+ } catch(welp) {
+ console.error(`Error fetching memo: ${String(welp)}`);
+ return error(500);
+ }
+};
+
+/*** HELPER ------------------------------------------- ***/
+
+function parseMemo(text: string): string {
+ const intro = text.split(/^Body$/m)[0];
+ const lines = intro.split("\n").filter(Boolean);
+ const format = [""];
+ let firstLine = "";
+
+ lines.map((line, index) => {
+ if (index === 1)
+ firstLine = " " + line.split(/\s+\B/)[1].trim();
+
+ if (index === 2) {
+ firstLine += ` • ${line.trim()}`;
+ format.push(firstLine);
+ }
+
+ if (index === 4)
+ format.push(" " + line.trim());
+ });
+
+ return format;
+}
diff --git a/src/routes/api/cv.json/+server.ts b/src/routes/api/cv.json/+server.ts
new file mode 100644
index 0000000..956525f
--- /dev/null
+++ b/src/routes/api/cv.json/+server.ts
@@ -0,0 +1,75 @@
+
+
+
+/*** IMPORT ------------------------------------------- ***/
+
+import { error, json } from "@sveltejs/kit";
+
+/*** EXPORT ------------------------------------------- ***/
+
+export const POST = async({ fetch, request }) => {
+ try {
+ const response = await fetch("https://cv.webb.page", {
+ headers: { "Content-Type": "text/plain" },
+ method: "GET"
+ });
+
+ const content = await response.text();
+ return json({ content: processCV(content) });
+ } catch(welp) {
+ console.error(`Error fetching CV: ${String(welp)}`);
+ return error(500);
+ }
+};
+
+/*** HELPER ------------------------------------------- ***/
+
+function processCV(text) {
+ const sections = text.split(/(^░▒▓[\s\S]*?^---$)/m);
+ const intro = sections[1];
+ const body = sections[2];
+ const lines = body.split(/\n{2,}/);
+ const table = processTable(body);
+
+ const processedLines = lines.map(line => {
+ if (line.includes("Proficiency") && line.includes("Notes"))
+ return table.join("").trim();
+
+ return line.replace(/\s+/g, " ");
+ });
+
+ return intro + processedLines.join("\n\n") + "\n\n\n";
+}
+
+function processTable(text) {
+ const lines = text.split(/\r?\n/);
+ const tableStartIndex = lines.findIndex(line => line.includes("|"));
+
+ if (tableStartIndex === -1)
+ return null;
+
+ const separatorLineIndex = lines.findIndex((line, index) => index > tableStartIndex && line.includes("|-") || line.includes("| -"));
+
+ if (separatorLineIndex === -1)
+ return null;
+
+ let tableEndIndex = separatorLineIndex + 1;
+
+ while (tableEndIndex < lines.length && lines[tableEndIndex].includes("|")) {
+ tableEndIndex++;
+ }
+
+ const contentRows = lines.slice(separatorLineIndex + 1, tableEndIndex);
+
+ return contentRows.map(row => {
+ let cells = row.split("|").map(cell => cell.trim());
+
+ if (cells[0] === "")
+ cells.shift();
+
+ if (cells[cells.length - 1] === "")
+ cells.pop();
+
+ return cells.join(" / ") + "\n";
+ });
+}
diff --git a/src/routes/api/mastodon.json/+server.ts b/src/routes/api/mastodon.json/+server.ts
new file mode 100644
index 0000000..72cbd38
--- /dev/null
+++ b/src/routes/api/mastodon.json/+server.ts
@@ -0,0 +1,55 @@
+
+
+
+/*** IMPORT ------------------------------------------- ***/
+
+import { error, json } from "@sveltejs/kit";
+
+/*** EXPORT ------------------------------------------- ***/
+
+export const POST = async({ fetch }) => {
+ try {
+ const response = await fetch("https://social.coop/users/netopwibby/outbox?page=true", {
+ headers: { "Content-Type": "application/json" },
+ method: "GET"
+ });
+
+ const { orderedItems } = await response.json();
+ const { object } = findCreateType(orderedItems);
+
+ return json({
+ created: object.published,
+ content: object.content,
+ link: object.url,
+ media: processAttachments(object.attachment)
+ });
+ } catch(welp) {
+ console.error(`Error fetching latest Mastodon post: ${String(welp)}`);
+ return error(500);
+ }
+};
+
+/*** HELPER ------------------------------------------- ***/
+
+function findCreateType(arr: Array<{ [key: string]: any }>): { [key: string]: any } | undefined {
+ return arr.find(obj => obj.type === "Create");
+}
+
+function processAttachments(attachments: Array<{ [key: string]: any }>): Array<string> {
+ const media = [];
+
+ if (attachments) {
+ for (const attachment of attachments) {
+ // TODO
+ // : use blurhash given to us by mastodon
+ // : https://github.com/woltapp/blurhash/tree/master/TypeScript#example
+ // : `attachment` comes with blurhash, height, mediaType, width
+ // if (attachment && attachment.url)
+
+ // @ts-ignore | Argument of type "any" is not assignable to parameter of type "never".
+ media.push(attachment.url);
+ }
+ }
+
+ return media;
+}
diff --git a/src/routes/api/remarks.json/+server.ts b/src/routes/api/remarks.json/+server.ts
new file mode 100644
index 0000000..c708207
--- /dev/null
+++ b/src/routes/api/remarks.json/+server.ts
@@ -0,0 +1,51 @@
+
+
+
+/*** IMPORT ------------------------------------------- ***/
+
+import { error, json } from "@sveltejs/kit";
+
+/*** EXPORT ------------------------------------------- ***/
+
+export const POST = async({ fetch, request }) => {
+ try {
+ const { filename } = await request.json();
+
+ const response = await fetch("https://blog.webb.page/remarks/" + String(filename), {
+ headers: { "Content-Type": "text/plain" },
+ method: "GET"
+ });
+
+ const remark = parseRemark(await response.text());
+ remark.push(` <span class="special-char">[</span><a href="https://blog.webb.page/remarks/${String(filename).split(".txt")[0]}">READ</a><span class="special-char">]</span>`, "\n");
+
+ return json({ content: remark.join("\n") });
+ } catch(welp) {
+ console.error(`Error fetching remark: ${String(welp)}`);
+ return error(500);
+ }
+};
+
+/*** HELPER ------------------------------------------- ***/
+
+function parseRemark(text: string): string {
+ const intro = text.split(/^Body$/m)[0];
+ const lines = intro.split("\n").filter(Boolean);
+ const format = [""];
+ let firstLine = "";
+
+ lines.map((line, index) => {
+ if (index === 1)
+ firstLine = " " + line.split(/\s+\B/)[1].trim();
+
+ if (index === 2) {
+ firstLine += ` • ${line.trim()}`;
+ format.push(firstLine);
+ }
+
+ if (index === 4)
+ format.push(" " + line.trim());
+ });
+
+ return format;
+}
diff --git a/static/88x31/a.gif b/static/88x31/a.gif
new file mode 100644
index 0000000..d335be3
--- /dev/null
+++ b/static/88x31/a.gif
Binary files differ
diff --git a/static/88x31/b.gif b/static/88x31/b.gif
new file mode 100644
index 0000000..b6398cb
--- /dev/null
+++ b/static/88x31/b.gif
Binary files differ
diff --git a/static/88x31/c.gif b/static/88x31/c.gif
new file mode 100644
index 0000000..9800206
--- /dev/null
+++ b/static/88x31/c.gif
Binary files differ
diff --git a/static/88x31/d.png b/static/88x31/d.png
new file mode 100644
index 0000000..0135925
--- /dev/null
+++ b/static/88x31/d.png
Binary files differ
diff --git a/static/88x31/elle.webp b/static/88x31/elle.webp
new file mode 100644
index 0000000..72948d6
--- /dev/null
+++ b/static/88x31/elle.webp
Binary files differ
diff --git a/static/apple-touch-icon.png b/static/apple-touch-icon.png
new file mode 100644
index 0000000..65636f8
--- /dev/null
+++ b/static/apple-touch-icon.png
Binary files differ
diff --git a/static/base.css b/static/base.css
new file mode 100644
index 0000000..8ff31a1
--- /dev/null
+++ b/static/base.css
@@ -0,0 +1,201 @@
+html {
+ box-sizing: border-box;
+ text-rendering: optimizeLegibility;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+}
+
+*,
+*::before,
+*::after {
+ margin: 0; padding: 0;
+
+ border: none;
+ box-sizing: inherit;
+ outline: 0;
+}
+
+[readonly] {
+ cursor: default !important;
+}
+
+
+
+button {
+ background-color: transparent;
+ color: inherit;
+}
+
+[type="button"]:not(:disabled),
+[type="submit"]:not(:disabled) {
+ cursor: pointer;
+}
+
+
+
+input {
+ background-color: transparent;
+ color: inherit;
+}
+
+input::placeholder {
+ color: inherit;
+ opacity: 0.3;
+}
+
+input:not(:disabled) {
+ color: inherit;
+}
+
+input:not([type="button"]),
+input:not([type="checkbox"]),
+input:not([type="file"]),
+input:not([type="radio"]),
+input:not([type="select"]),
+input:not([type="submit"]) {
+ cursor: text;
+}
+
+input[type="button"]:not(:disabled),
+input[type="checkbox"]:not(:disabled),
+input[type="file"]:not(:disabled),
+input[type="radio"]:not(:disabled),
+input[type="select"]:not(:disabled),
+input[type="submit"]:not(:disabled) {
+ cursor: pointer;
+}
+
+
+
+textarea {
+ width: 100%;
+ /* border-color should be added in apps for blur/focus */
+ border: 1px solid;
+}
+
+textarea:not([disabled]) {
+ resize: vertical;
+}
+
+textarea[disabled] {
+ resize: none;
+}
+
+
+
+a,
+area,
+button,
+[role="button"],
+input,
+label,
+select,
+summary,
+textarea {
+ /* Remove touch delay on supported devices */
+ touch-action: manipulation;
+}
+
+
+
+button,
+input,
+select,
+textarea {
+ background-color: transparent;
+ font-family: inherit;
+ font-size: inherit;
+ font-weight: inherit;
+}
+
+button:disabled,
+input:disabled,
+select:disabled,
+textarea:disabled {
+ pointer-events: none;
+}
+
+
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
+ font-weight: 400;
+}
+
+
+
+ol,
+ul {
+ list-style-position: inside;
+}
+
+ol > li,
+ul > li {
+ list-style-position: inside;
+}
+
+ul {
+ list-style-type: none;
+}
+
+
+
+label[for] {
+ cursor: pointer;
+}
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
+
+
+
+img {
+ max-width: 100%;
+ display: block;
+ font-size: 0;
+ position: relative;
+}
+
+img::after {
+ width: 100%; height: 100%;
+ top: 0; left: 0;
+
+ background-color: #333;
+ background-image: url("data:image/png;base64,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");
+ background-position: center;
+ background-repeat: no-repeat;
+ background-size: contain;
+ box-shadow: 0 0 1px 0 rgba(255, 255, 255, 0.3);
+ content: "";
+ display: block;
+ position: absolute;
+}
+
+
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+a:not([class]) {
+ text-decoration-skip-ink: auto;
+}
+
+
+
+@media (prefers-reduced-motion: reduce) {
+ /* Remove all animations and transitions for people that prefer not to see them */
+ * {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ scroll-behavior: auto !important;
+ transition-duration: 0.01ms !important;
+ }
+}
diff --git a/static/blogroll.opml b/static/blogroll.opml
new file mode 100644
index 0000000..56fc5be
--- /dev/null
+++ b/static/blogroll.opml
@@ -0,0 +1,121 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<opml version="1.1">
+ <head>
+ <docs>https://webb.page</docs>
+ <ownerEmail>paul@webb.page</ownerEmail>
+ <ownerName>Paul Anthony Webb</ownerName>
+ <title>webb.blog.opml</title>
+ </head>
+
+ <body>
+ <outline
+ description=""
+ htmlUrl="https://annamariaperez.com/blog"
+ text="AnnaMaria Perez"
+ title="AnnaMaria Perez"
+ type="rss"
+ version="RSS"
+ xmlUrl="https://annamariaperez.com/blog?format=rss"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://www.astrodim.com"
+ text="Astro*dim"
+ title="Astro*dim"
+ type="atom"
+ version="Atom"
+ xmlUrl="https://www.astrodim.com/blog-feed.xml"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://metanorm.mirror.xyz"
+ text="Metanorm"
+ title="Metanorm"
+ type="atom"
+ version="Atom"
+ xmlUrl="https://metanorm.mirror.xyz/feed/atom"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://micro.corntoole.com"
+ text="Cornelius Toole"
+ title="Cornelius Toole"
+ type="json"
+ version="JSON"
+ xmlUrl="https://micro.corntoole.com/feed.json"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://ellesho.me/page/website/now"
+ text="elle's homepage"
+ title="elle's homepage"
+ type="rss"
+ version="RSS"
+ xmlUrl="https://ellesho.me/page/rss.xml"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://mirror.xyz/riotgoools.eth"
+ text="elle on Mirror"
+ title="elle on Mirror"
+ type="atom"
+ version="Atom"
+ xmlUrl="https://mirror.xyz/riotgoools.eth/feed/atom"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://iheanyi.com/journal"
+ text="Iheanyi Ekechukwu's Journal"
+ title="Iheanyi Ekechukwu's Journal"
+ type="atom"
+ version="Atom"
+ xmlUrl="https://iheanyi.com/feed.xml"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://jacky.wtf/essays"
+ text="Jacky Alciné's Essays"
+ title="Jacky Alciné's Essays"
+ type="atom"
+ version="Atom"
+ xmlUrl="https://jacky.wtf/essays/feed.atom"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://blog.jesseyoungblood.com"
+ text="Software, Startups, &amp; Sandwiches"
+ title="Software, Startups, &amp; Sandwiches"
+ type="rss"
+ version="RSS"
+ xmlUrl="https://blog.jesseyoungblood.com/feed"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://www.brisseaux.com/blog"
+ text="BRISSEAUX"
+ title="BRISSEAUX"
+ type="rss"
+ version="RSS"
+ xmlUrl="https://www.brisseaux.com/blog?format=rss"
+ />
+
+ <outline
+ description=""
+ htmlUrl="https://sincerelyshantelle.com"
+ text="Sincerely, Shantelle"
+ title="Sincerely, Shantelle"
+ type="rss"
+ version="RSS"
+ xmlUrl="https://sincerelyshantelle.com/feed/"
+ />
+ </body>
+</opml>
diff --git a/static/favicon.svg b/static/favicon.svg
new file mode 100644
index 0000000..be092c1
--- /dev/null
+++ b/static/favicon.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" shape-rendering="crispEdges" viewBox="0 -0.5 16 16"><path stroke="#fff" d="M3 1h7M1 2h3m5 0h3M1 3h1m9 0h2M0 4h2m10 0h1M0 5h1m7 0h1m1 0h1m1 0h3M0 6h1m5 0h1m7 0h2M0 7h1m14 0h1M0 8h1m9 0h1m2 0h3M0 9h2m5 0h1m5 0h1M1 10h1m11 0h1M1 11h2m4 0h2m4 0h1M2 12h2m9 0h1M3 13h2m7 0h2M4 14h3m4 0h2m-7 1h6"/><path stroke="#000" d="M4 2h1M2 3h1m2 0h6M2 4h1m2 0h3m3 0h1M1 5h1m2 0h1m6 0h1M1 6h1m2 0h1M1 7h2m2 0h1m2 0h3m1 0h2M1 8h3m1 0h3m3 0h1M8 9h1m1 0h2m-8 1h1m2 0h2m3 0h1m-9 1h2m3 0h4m-9 1h4m4 0h1m-8 1h4m1 0h2m-5 1h4"/><path stroke="#2e2e2c" d="M5 2h4M3 3h2M3 4h2M2 5h2M2 6h2m4 0h6M3 7h2m1 0h2m6 0h1M4 8h1m3 4h2m1 0h1m-3 1h1"/><path stroke="#f28798" d="M8 4h3M5 5h3m1 0h1M5 6h1m1 0h1"/><path stroke="#ccc" d="M11 7h1M8 8h2m2 0h1M6 9h1"/><path stroke="#b6a69f" d="M2 9h1m0 1h1m2 1h1"/><path stroke="#c0b3ad" d="M3 9h1m6 1h1m-1 2h1"/><path stroke="#eeeded" d="M4 9h2m3 0h1m2 0h1m-7 1h1m2 0h1m1 0h1"/><path stroke="#a08a7f" d="M2 10h1m2 0h1m-3 1h1"/></svg> \ No newline at end of file
diff --git a/static/humans.txt b/static/humans.txt
new file mode 100755
index 0000000..cf60057
--- /dev/null
+++ b/static/humans.txt
@@ -0,0 +1,16 @@
+/* TEAM */
+
+Paul Anthony Webb - Architech
+
+
+
+/* THANKS */
+
+Viewers like you
+
+
+
+/* SITE */
+
+Standards: CSS3, HTML5, ES6/7/whatever
+Components: SvelteKit, Sass, TypeScript
diff --git a/static/images/bandcamp/1085756242.jpg b/static/images/bandcamp/1085756242.jpg
new file mode 100644
index 0000000..ede9de7
--- /dev/null
+++ b/static/images/bandcamp/1085756242.jpg
Binary files differ
diff --git a/static/images/bandcamp/1120176299.jpg b/static/images/bandcamp/1120176299.jpg
new file mode 100755
index 0000000..611219e
--- /dev/null
+++ b/static/images/bandcamp/1120176299.jpg
Binary files differ
diff --git a/static/images/bandcamp/1349219244.jpg b/static/images/bandcamp/1349219244.jpg
new file mode 100644
index 0000000..21c45e0
--- /dev/null
+++ b/static/images/bandcamp/1349219244.jpg
Binary files differ
diff --git a/static/images/bandcamp/1387178982.jpg b/static/images/bandcamp/1387178982.jpg
new file mode 100644
index 0000000..a285ef4
--- /dev/null
+++ b/static/images/bandcamp/1387178982.jpg
Binary files differ
diff --git a/static/images/bandcamp/1488235027.jpg b/static/images/bandcamp/1488235027.jpg
new file mode 100644
index 0000000..246e0b0
--- /dev/null
+++ b/static/images/bandcamp/1488235027.jpg
Binary files differ
diff --git a/static/images/bandcamp/1537466475.jpg b/static/images/bandcamp/1537466475.jpg
new file mode 100755
index 0000000..ecde328
--- /dev/null
+++ b/static/images/bandcamp/1537466475.jpg
Binary files differ
diff --git a/static/images/bandcamp/1584151539.jpg b/static/images/bandcamp/1584151539.jpg
new file mode 100644
index 0000000..1c4fb73
--- /dev/null
+++ b/static/images/bandcamp/1584151539.jpg
Binary files differ
diff --git a/static/images/bandcamp/1773399509.jpg b/static/images/bandcamp/1773399509.jpg
new file mode 100755
index 0000000..d6490a1
--- /dev/null
+++ b/static/images/bandcamp/1773399509.jpg
Binary files differ
diff --git a/static/images/bandcamp/1870167199.jpg b/static/images/bandcamp/1870167199.jpg
new file mode 100755
index 0000000..8a7f8bc
--- /dev/null
+++ b/static/images/bandcamp/1870167199.jpg
Binary files differ
diff --git a/static/images/bandcamp/1898404266.jpg b/static/images/bandcamp/1898404266.jpg
new file mode 100755
index 0000000..02bbbd9
--- /dev/null
+++ b/static/images/bandcamp/1898404266.jpg
Binary files differ
diff --git a/static/images/bandcamp/1931742968.jpg b/static/images/bandcamp/1931742968.jpg
new file mode 100644
index 0000000..4c03904
--- /dev/null
+++ b/static/images/bandcamp/1931742968.jpg
Binary files differ
diff --git a/static/images/bandcamp/1972128885.jpg b/static/images/bandcamp/1972128885.jpg
new file mode 100644
index 0000000..aedb2bd
--- /dev/null
+++ b/static/images/bandcamp/1972128885.jpg
Binary files differ
diff --git a/static/images/bandcamp/2006853030.jpg b/static/images/bandcamp/2006853030.jpg
new file mode 100644
index 0000000..76a627b
--- /dev/null
+++ b/static/images/bandcamp/2006853030.jpg
Binary files differ
diff --git a/static/images/bandcamp/2247255572.jpg b/static/images/bandcamp/2247255572.jpg
new file mode 100644
index 0000000..cd43f5a
--- /dev/null
+++ b/static/images/bandcamp/2247255572.jpg
Binary files differ
diff --git a/static/images/bandcamp/2376445182.jpg b/static/images/bandcamp/2376445182.jpg
new file mode 100644
index 0000000..773573b
--- /dev/null
+++ b/static/images/bandcamp/2376445182.jpg
Binary files differ
diff --git a/static/images/bandcamp/2493506758.jpg b/static/images/bandcamp/2493506758.jpg
new file mode 100644
index 0000000..e89f851
--- /dev/null
+++ b/static/images/bandcamp/2493506758.jpg
Binary files differ
diff --git a/static/images/bandcamp/2629563639.jpg b/static/images/bandcamp/2629563639.jpg
new file mode 100755
index 0000000..6738704
--- /dev/null
+++ b/static/images/bandcamp/2629563639.jpg
Binary files differ
diff --git a/static/images/bandcamp/2689738227.jpg b/static/images/bandcamp/2689738227.jpg
new file mode 100755
index 0000000..04de924
--- /dev/null
+++ b/static/images/bandcamp/2689738227.jpg
Binary files differ
diff --git a/static/images/bandcamp/2714432574.jpg b/static/images/bandcamp/2714432574.jpg
new file mode 100644
index 0000000..4f69791
--- /dev/null
+++ b/static/images/bandcamp/2714432574.jpg
Binary files differ
diff --git a/static/images/bandcamp/2723488137.jpg b/static/images/bandcamp/2723488137.jpg
new file mode 100644
index 0000000..bfcf6f5
--- /dev/null
+++ b/static/images/bandcamp/2723488137.jpg
Binary files differ
diff --git a/static/images/bandcamp/2759787857.jpg b/static/images/bandcamp/2759787857.jpg
new file mode 100644
index 0000000..bd545ff
--- /dev/null
+++ b/static/images/bandcamp/2759787857.jpg
Binary files differ
diff --git a/static/images/bandcamp/2804828422.jpg b/static/images/bandcamp/2804828422.jpg
new file mode 100755
index 0000000..28a5339
--- /dev/null
+++ b/static/images/bandcamp/2804828422.jpg
Binary files differ
diff --git a/static/images/bandcamp/3091487133.jpg b/static/images/bandcamp/3091487133.jpg
new file mode 100644
index 0000000..b3660b0
--- /dev/null
+++ b/static/images/bandcamp/3091487133.jpg
Binary files differ
diff --git a/static/images/bandcamp/3211763084.jpg b/static/images/bandcamp/3211763084.jpg
new file mode 100644
index 0000000..47a0707
--- /dev/null
+++ b/static/images/bandcamp/3211763084.jpg
Binary files differ
diff --git a/static/images/bandcamp/3230467359.jpg b/static/images/bandcamp/3230467359.jpg
new file mode 100644
index 0000000..f13a331
--- /dev/null
+++ b/static/images/bandcamp/3230467359.jpg
Binary files differ
diff --git a/static/images/bandcamp/3332776732.jpg b/static/images/bandcamp/3332776732.jpg
new file mode 100755
index 0000000..611219e
--- /dev/null
+++ b/static/images/bandcamp/3332776732.jpg
Binary files differ
diff --git a/static/images/bandcamp/3478474368.jpg b/static/images/bandcamp/3478474368.jpg
new file mode 100644
index 0000000..b2f04c1
--- /dev/null
+++ b/static/images/bandcamp/3478474368.jpg
Binary files differ
diff --git a/static/images/bandcamp/3479413388.jpg b/static/images/bandcamp/3479413388.jpg
new file mode 100644
index 0000000..3ee496d
--- /dev/null
+++ b/static/images/bandcamp/3479413388.jpg
Binary files differ
diff --git a/static/images/bandcamp/3524388493.jpg b/static/images/bandcamp/3524388493.jpg
new file mode 100644
index 0000000..c11aa1d
--- /dev/null
+++ b/static/images/bandcamp/3524388493.jpg
Binary files differ
diff --git a/static/images/bandcamp/3603991383.jpg b/static/images/bandcamp/3603991383.jpg
new file mode 100644
index 0000000..5b3e661
--- /dev/null
+++ b/static/images/bandcamp/3603991383.jpg
Binary files differ
diff --git a/static/images/bandcamp/3624438326.jpg b/static/images/bandcamp/3624438326.jpg
new file mode 100755
index 0000000..5260d4f
--- /dev/null
+++ b/static/images/bandcamp/3624438326.jpg
Binary files differ
diff --git a/static/images/bandcamp/3667963750.jpg b/static/images/bandcamp/3667963750.jpg
new file mode 100644
index 0000000..e4f7493
--- /dev/null
+++ b/static/images/bandcamp/3667963750.jpg
Binary files differ
diff --git a/static/images/bandcamp/3685680305.jpg b/static/images/bandcamp/3685680305.jpg
new file mode 100644
index 0000000..dcafd1e
--- /dev/null
+++ b/static/images/bandcamp/3685680305.jpg
Binary files differ
diff --git a/static/images/bandcamp/3794237863.jpg b/static/images/bandcamp/3794237863.jpg
new file mode 100755
index 0000000..6254094
--- /dev/null
+++ b/static/images/bandcamp/3794237863.jpg
Binary files differ
diff --git a/static/images/bandcamp/4001491411.jpg b/static/images/bandcamp/4001491411.jpg
new file mode 100755
index 0000000..5c4cd83
--- /dev/null
+++ b/static/images/bandcamp/4001491411.jpg
Binary files differ
diff --git a/static/images/bandcamp/4182307863.jpg b/static/images/bandcamp/4182307863.jpg
new file mode 100755
index 0000000..3340d8d
--- /dev/null
+++ b/static/images/bandcamp/4182307863.jpg
Binary files differ
diff --git a/static/images/bandcamp/4219807572.jpg b/static/images/bandcamp/4219807572.jpg
new file mode 100644
index 0000000..6599ee7
--- /dev/null
+++ b/static/images/bandcamp/4219807572.jpg
Binary files differ
diff --git a/static/images/bandcamp/422810680.jpg b/static/images/bandcamp/422810680.jpg
new file mode 100755
index 0000000..010d975
--- /dev/null
+++ b/static/images/bandcamp/422810680.jpg
Binary files differ
diff --git a/static/images/bandcamp/4237561960.jpg b/static/images/bandcamp/4237561960.jpg
new file mode 100755
index 0000000..1c05096
--- /dev/null
+++ b/static/images/bandcamp/4237561960.jpg
Binary files differ
diff --git a/static/images/bandcamp/435475786.jpg b/static/images/bandcamp/435475786.jpg
new file mode 100755
index 0000000..96bcc1e
--- /dev/null
+++ b/static/images/bandcamp/435475786.jpg
Binary files differ
diff --git a/static/images/bandcamp/483965118.jpg b/static/images/bandcamp/483965118.jpg
new file mode 100755
index 0000000..fb985b9
--- /dev/null
+++ b/static/images/bandcamp/483965118.jpg
Binary files differ
diff --git a/static/images/bandcamp/512177084.jpg b/static/images/bandcamp/512177084.jpg
new file mode 100644
index 0000000..826d709
--- /dev/null
+++ b/static/images/bandcamp/512177084.jpg
Binary files differ
diff --git a/static/images/bandcamp/635810172.jpg b/static/images/bandcamp/635810172.jpg
new file mode 100644
index 0000000..b2b9387
--- /dev/null
+++ b/static/images/bandcamp/635810172.jpg
Binary files differ
diff --git a/static/images/bandcamp/640549190.jpg b/static/images/bandcamp/640549190.jpg
new file mode 100755
index 0000000..7130cfc
--- /dev/null
+++ b/static/images/bandcamp/640549190.jpg
Binary files differ
diff --git a/static/images/bandcamp/692190634.jpg b/static/images/bandcamp/692190634.jpg
new file mode 100755
index 0000000..f7d0758
--- /dev/null
+++ b/static/images/bandcamp/692190634.jpg
Binary files differ
diff --git a/static/images/bandcamp/71750021.jpg b/static/images/bandcamp/71750021.jpg
new file mode 100755
index 0000000..6368edf
--- /dev/null
+++ b/static/images/bandcamp/71750021.jpg
Binary files differ
diff --git a/static/images/bandcamp/751538854.jpg b/static/images/bandcamp/751538854.jpg
new file mode 100755
index 0000000..a69ff67
--- /dev/null
+++ b/static/images/bandcamp/751538854.jpg
Binary files differ
diff --git a/static/images/bandcamp/803715127.jpg b/static/images/bandcamp/803715127.jpg
new file mode 100755
index 0000000..9b99f7e
--- /dev/null
+++ b/static/images/bandcamp/803715127.jpg
Binary files differ
diff --git a/static/images/bandcamp/953490707.jpg b/static/images/bandcamp/953490707.jpg
new file mode 100755
index 0000000..76750d5
--- /dev/null
+++ b/static/images/bandcamp/953490707.jpg
Binary files differ
diff --git a/static/images/bandcamp/955213907.jpg b/static/images/bandcamp/955213907.jpg
new file mode 100644
index 0000000..9b7fe2f
--- /dev/null
+++ b/static/images/bandcamp/955213907.jpg
Binary files differ
diff --git a/static/images/netopwibby/01.jpg b/static/images/netopwibby/01.jpg
new file mode 100644
index 0000000..9802cfa
--- /dev/null
+++ b/static/images/netopwibby/01.jpg
Binary files differ
diff --git a/static/images/netopwibby/02.jpg b/static/images/netopwibby/02.jpg
new file mode 100644
index 0000000..54706a1
--- /dev/null
+++ b/static/images/netopwibby/02.jpg
Binary files differ
diff --git a/static/images/netopwibby/03.jpg b/static/images/netopwibby/03.jpg
new file mode 100644
index 0000000..1419799
--- /dev/null
+++ b/static/images/netopwibby/03.jpg
Binary files differ
diff --git a/static/images/netopwibby/04.jpg b/static/images/netopwibby/04.jpg
new file mode 100644
index 0000000..d4b7896
--- /dev/null
+++ b/static/images/netopwibby/04.jpg
Binary files differ
diff --git a/static/images/netopwibby/05.jpg b/static/images/netopwibby/05.jpg
new file mode 100644
index 0000000..d040fca
--- /dev/null
+++ b/static/images/netopwibby/05.jpg
Binary files differ
diff --git a/static/images/netopwibby/06.jpg b/static/images/netopwibby/06.jpg
new file mode 100644
index 0000000..3bfb8cb
--- /dev/null
+++ b/static/images/netopwibby/06.jpg
Binary files differ
diff --git a/static/images/netopwibby/07.jpg b/static/images/netopwibby/07.jpg
new file mode 100644
index 0000000..48b9da9
--- /dev/null
+++ b/static/images/netopwibby/07.jpg
Binary files differ
diff --git a/static/images/netopwibby/08.jpg b/static/images/netopwibby/08.jpg
new file mode 100644
index 0000000..addbb9a
--- /dev/null
+++ b/static/images/netopwibby/08.jpg
Binary files differ
diff --git a/static/images/netopwibby/09.jpg b/static/images/netopwibby/09.jpg
new file mode 100644
index 0000000..b69998e
--- /dev/null
+++ b/static/images/netopwibby/09.jpg
Binary files differ
diff --git a/static/images/netopwibby/10.jpg b/static/images/netopwibby/10.jpg
new file mode 100644
index 0000000..c02d82a
--- /dev/null
+++ b/static/images/netopwibby/10.jpg
Binary files differ
diff --git a/static/images/netopwibby/11.jpg b/static/images/netopwibby/11.jpg
new file mode 100644
index 0000000..b96cae7
--- /dev/null
+++ b/static/images/netopwibby/11.jpg
Binary files differ
diff --git a/static/images/netopwibby/12.jpg b/static/images/netopwibby/12.jpg
new file mode 100644
index 0000000..761c32f
--- /dev/null
+++ b/static/images/netopwibby/12.jpg
Binary files differ
diff --git a/static/images/netopwibby/13.jpg b/static/images/netopwibby/13.jpg
new file mode 100644
index 0000000..9e9ba43
--- /dev/null
+++ b/static/images/netopwibby/13.jpg
Binary files differ
diff --git a/static/images/projects/aries_01.jpg b/static/images/projects/aries_01.jpg
new file mode 100644
index 0000000..79a3d80
--- /dev/null
+++ b/static/images/projects/aries_01.jpg
Binary files differ
diff --git a/static/images/projects/aries_02.jpg b/static/images/projects/aries_02.jpg
new file mode 100644
index 0000000..8762e38
--- /dev/null
+++ b/static/images/projects/aries_02.jpg
Binary files differ
diff --git a/static/images/projects/beachfront_01.jpg b/static/images/projects/beachfront_01.jpg
new file mode 100644
index 0000000..a451998
--- /dev/null
+++ b/static/images/projects/beachfront_01.jpg
Binary files differ
diff --git a/static/images/projects/beachfront_02.jpg b/static/images/projects/beachfront_02.jpg
new file mode 100644
index 0000000..fe6625f
--- /dev/null
+++ b/static/images/projects/beachfront_02.jpg
Binary files differ
diff --git a/static/images/projects/chronver.jpg b/static/images/projects/chronver.jpg
new file mode 100644
index 0000000..68ca8f7
--- /dev/null
+++ b/static/images/projects/chronver.jpg
Binary files differ
diff --git a/static/images/projects/dap.jpg b/static/images/projects/dap.jpg
new file mode 100644
index 0000000..dd36213
--- /dev/null
+++ b/static/images/projects/dap.jpg
Binary files differ
diff --git a/static/images/projects/neuenet_01.jpg b/static/images/projects/neuenet_01.jpg
new file mode 100644
index 0000000..e086803
--- /dev/null
+++ b/static/images/projects/neuenet_01.jpg
Binary files differ
diff --git a/static/images/projects/neuenet_02.jpg b/static/images/projects/neuenet_02.jpg
new file mode 100644
index 0000000..12989c1
--- /dev/null
+++ b/static/images/projects/neuenet_02.jpg
Binary files differ
diff --git a/static/images/projects/nickel_01.jpg b/static/images/projects/nickel_01.jpg
new file mode 100644
index 0000000..52c8592
--- /dev/null
+++ b/static/images/projects/nickel_01.jpg
Binary files differ
diff --git a/static/images/projects/socii_01.jpg b/static/images/projects/socii_01.jpg
new file mode 100644
index 0000000..0d11063
--- /dev/null
+++ b/static/images/projects/socii_01.jpg
Binary files differ
diff --git a/static/images/projects/uchu_01.jpg b/static/images/projects/uchu_01.jpg
new file mode 100644
index 0000000..dd8de5c
--- /dev/null
+++ b/static/images/projects/uchu_01.jpg
Binary files differ
diff --git a/static/key.pub b/static/key.pub
new file mode 100644
index 0000000..6adf7b1
--- /dev/null
+++ b/static/key.pub
@@ -0,0 +1,244 @@
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+
+mQINBGhVfj8BEAC8V1U78KIbd2IHzU5vAGrBgn4TWvjKgonvHXdrctPoquQ9nyDO
+aqodX6nrtkn3j3QSpft1jR/NSWgUGuPzIMu7fo1PwE6T99utVjXfA7O0ScF4K/aj
+/rZY0JfgrORILTHdqiy7vFc9B4kh8qQi0Y4lmYmItDMz2oeowB5JWrkMDhwKea04
+ORGlhy63Tz3MUQKumNTxK6ayLkIOfEIGVvso2CFGl68jB76XZfiEny3ifzxd6z3Y
+VLuikH1Q9Rec6tvIk0boMOXNjP5g/31eG6nUDWzJDcxofS5DY5Uf0GGvCUYMdxbm
+U27Wy724293SqkMITSXz3WeOero8qRSHfFDy1mj4BvmvoQMhsFxJQsf0/M5wpHOF
+j3ci8TGOV1dGnkElJEZ94hofr5cpkyqMIr3vG6I4CtNtoQT6yo8KQGdETT4JqSnh
+v3ghSEqtmP7pqSKT/XRC/HhP4cGYxrLngScZaBpkVmNoENKOFAOn5WjhZMzx2Man
+WVv1afPxxWc+KY34+hyWaj0gU/L26BPGOLv1ox8Crf0ODME4YLB3Ptc5Cds3TUTQ
+EIl/F9EpzCMdY0QivxWgiNk9wRH5XFpSCYmjTcfcW0r66k9ajCV/BoSfrYbdf3pO
+/XOsrds9v0m67zZvBd0xXtp39V5oVlh6cbqcu1Q2gS97yHU2A/D6P51TpQARAQAB
+tI1uZXRvcDovL+OCpuOCo+ODkyAoQXJjaGl0ZWNoIMOXIEV4Y2Vzc2l2ZWx5IEJs
+YWNrIMOXIEVhc3QgQ29hc3Qga2lkIG9uIHRoZSBXZXN0IENvYXN0IMOXIEpVJFQg
+w5cgQnVpbGRpbmcgYSBiZXR0ZXIgSW50ZXJuZXQpIDxwYXVsQHdlYmIucGFnZT6J
+AlQEEwEIAD4WIQSdMRM+sRReWIIAtMP/7XPf6GB1+gUCaFV+PwIbAwUJB4YfZQUL
+CQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRD/7XPf6GB1+nH/D/wL8D66BRMBSdHq
+teDb4DUZH395T1/0xWrRXpkLW/bAQgex4A/UPVFpvPAZzYR5ljNd4n1tiQ0XYZ6L
+46R1Cg4NygFUHQ55+FZR+eDkdv0D8WMIC0VF1W9dM/hcxppgStdMewy6moLJmVFS
+6OEZ4ggk8Km4TeoDmMBrymFzeRpQmZg8E+6wYQrsch8n17RysSuALtvSE1KIUJ1x
+5z8+oLXWC0eqf2ydUT6Ya+kYXBhB+pqeFDDe2iZNCEnKb+dTqOzMObC7mpDVgd6T
+JLd6HOf3DUtMJGrW+PRhA6zvyZrNANaLUJMvr7ucS6wb/nNKSUP624NcaLXHCsg0
+XIc0fiPTfnAl4q9kDpEiw2xEApYPwJ1Wfe8UrtrWZCJ45VIxUZamiTdkQ+Bk79JU
+sFHV0o9/EZw93GP3uyY1knR+L+pltCvLIy7R2KkW5V1fSv0VY+u4SrKfVu0PJBMs
+GNVM/+s0wKcdNloqRRzNWt/zBEXv+pisedAppeXi8utq+qnQCyenxtrBpILv0b8x
+579LGQlQSxCprJig19zzu8SucpDWOvjra+fw/hadKPPdkbBDtM+D8QFEOpR8F7ky
+87mvV9EC3rfDzi6Qxdrgyt6+MlBxdpjbSZOdNwmvUsT/SMliTWIfx0ULD80pgY3p
+ELVNSXxdqSnJCVLZtqB5pRM60H9hItH/AAAhTf8AACFIARAAAQEAAAAAAAAAAAAA
+AAD/2P/gABBKRklGAAEBAABIAEgAAP/hAEBFeGlmAABNTQAqAAAACAABh2kABAAA
+AAEAAAAaAAAAAAACoAIABAAAAAEAAAD6oAMABAAAAAEAAAD6AAAAAP/tADhQaG90
+b3Nob3AgMy4wADhCSU0EBAAAAAAAADhCSU0EJQAAAAAAENQdjNmPALIE6YAJmOz4
+Qn7/wgARCAD6APoDASIAAhEBAxEB/8QAHwAAAQUBAQEBAQEAAAAAAAAAAwIEAQUA
+BgcICQoL/8QAwxAAAQMDAgQDBAYEBwYECAZzAQIAAxEEEiEFMRMiEAZBUTIUYXEj
+B4EgkUIVoVIzsSRiMBbBctFDkjSCCOFTQCVjFzXwk3OiUESyg/EmVDZklHTCYNKE
+oxhw4idFN2WzVXWklcOF8tNGdoDjR1ZmtAkKGRooKSo4OTpISUpXWFlaZ2hpand4
+eXqGh4iJipCWl5iZmqClpqeoqaqwtba3uLm6wMTFxsfIycrQ1NXW19jZ2uDk5ebn
+6Onq8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAABAgADBAUGBwgJCgv/xADD
+EQACAgEDAwMCAwUCBQIEBIcBAAIRAxASIQQgMUETBTAiMlEUQAYzI2FCFXFSNIFQ
+JJGhQ7EWB2I1U/DRJWDBROFy8ReCYzZwJkVUkiei0ggJChgZGigpKjc4OTpGR0hJ
+SlVWV1hZWmRlZmdoaWpzdHV2d3h5eoCDhIWGh4iJipCTlJWWl5iZmqCjpKWmp6ip
+qrCys7S1tre4ubrAwsPExcbHyMnK0NPU1dbX2Nna4OLj5OXm5+jp6vLz9PX29/j5
++v/bAEMADAwMDAwMFAwMFB0UFBQdJx0dHR0nMScnJycnMTsxMTExMTE7Ozs7Ozs7
+O0dHR0dHR1NTU1NTXV1dXV1dXV1dXf/bAEMBDg8PGBYYKBYWKGFCNkJhYWFhYWFh
+YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYf/aAAwD
+AQACEQMRAAABpVKSMFIjQUnLimcgsqNBplMCmdEZiCQSrICnQPALSrEpnQX2mITG
+1aY0dClwHp1baK0Tq2hICkxo6UwzrlEBSQmILlExVKJpWTMF5EhZ2klMzM0KhIRS
+diZjabaIhlITNMTopVC4xCsJMLxKcrQTlakwuKQraE6FSytEBCZGpUEkQ9oZ1InB
+EpIgvGTM8TC607A7HtJqTXempNd6q6wNYh6jdE2qm1w6rnK7qeelrFoluYmQuG21
+TssKJUjrIyZp2x1xImsYLkG2f6yG4wuq0qp42s4VslYhrpiAcHT+mJRead1BAUrQ
+cVLFMi5iamJUFElUFkxEzbZc07YWctlR6kvM4a9IKg0ehLzOh0ddVhM5zHWT7MdT
+gSMQuE6oUmZCSNQRUKSxhMpmTMSG0wuM7YW2ipw8ZaNq22rbattNRl6kKmajQml5
+C4bTgszGKwiUi2yzqggyVoyajL1Iy9SMvUlWipyNS8jUpKppGt7AGsaXNKVbShVE
+20J2mSIUMCNoOxEKu6oo69pXObqrmvPN6Hq883oerzxz2bETJ2V1ETw9CRb7hpru
+1cF0tJ5r0Dj4UykqlmcqtM4ZoSuCyLukme6eJDNZqVZChlPLmZT13G0S1ppr0Rtz
+vWVTunuBoQ9JzxCOemKyk4XV1VpWkUa5kZzKdDTsTEKglGlIN455zqBotwwfg2de
+viWWI2Ftpq367k+jMECcrN+a7esK81lak5XTmmt6zkpaYg1yL0SqxCksy4TgqUuo
+E3WhJbrjcf01tfMQ2cPP96Bjef2vV6pbZiDtkg50z1XWq4NbIraKC6dKzmqUql20
+1C0aC8iVWInM2iYAQRKZuoPy3QWzpTcoJxomJQKYQcsKyxIrbG3ik7DBmlmvKZUK
+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
+=Cppl
+-----END PGP PUBLIC KEY BLOCK-----
diff --git a/static/og.png b/static/og.png
new file mode 100644
index 0000000..356705f
--- /dev/null
+++ b/static/og.png
Binary files differ
diff --git a/static/robots.txt b/static/robots.txt
new file mode 100644
index 0000000..40e72d7
--- /dev/null
+++ b/static/robots.txt
@@ -0,0 +1,7 @@
+# https://www.robotstxt.org/robotstxt.html
+
+User-agent: Google
+Disallow: /
+
+User-agent: *
+Allow: /
diff --git a/static/sounds/click/click_01.ogg b/static/sounds/click/click_01.ogg
new file mode 100644
index 0000000..91550ba
--- /dev/null
+++ b/static/sounds/click/click_01.ogg
Binary files differ
diff --git a/static/sounds/click/click_02.ogg b/static/sounds/click/click_02.ogg
new file mode 100644
index 0000000..5305cf4
--- /dev/null
+++ b/static/sounds/click/click_02.ogg
Binary files differ
diff --git a/static/sounds/click/click_03.ogg b/static/sounds/click/click_03.ogg
new file mode 100644
index 0000000..afa1038
--- /dev/null
+++ b/static/sounds/click/click_03.ogg
Binary files differ
diff --git a/static/sounds/click/click_04.ogg b/static/sounds/click/click_04.ogg
new file mode 100644
index 0000000..7ab1314
--- /dev/null
+++ b/static/sounds/click/click_04.ogg
Binary files differ
diff --git a/static/sounds/click/click_05.ogg b/static/sounds/click/click_05.ogg
new file mode 100644
index 0000000..7e0a42b
--- /dev/null
+++ b/static/sounds/click/click_05.ogg
Binary files differ
diff --git a/static/sounds/click/click_06.ogg b/static/sounds/click/click_06.ogg
new file mode 100644
index 0000000..209b466
--- /dev/null
+++ b/static/sounds/click/click_06.ogg
Binary files differ
diff --git a/static/sounds/click/click_07.ogg b/static/sounds/click/click_07.ogg
new file mode 100644
index 0000000..8d89c3c
--- /dev/null
+++ b/static/sounds/click/click_07.ogg
Binary files differ
diff --git a/static/sounds/click/click_08.ogg b/static/sounds/click/click_08.ogg
new file mode 100644
index 0000000..161412e
--- /dev/null
+++ b/static/sounds/click/click_08.ogg
Binary files differ
diff --git a/static/sounds/click/click_09.ogg b/static/sounds/click/click_09.ogg
new file mode 100644
index 0000000..e28ac0a
--- /dev/null
+++ b/static/sounds/click/click_09.ogg
Binary files differ
diff --git a/static/sounds/click/click_10.ogg b/static/sounds/click/click_10.ogg
new file mode 100644
index 0000000..fc3c164
--- /dev/null
+++ b/static/sounds/click/click_10.ogg
Binary files differ
diff --git a/static/sounds/click/click_11.ogg b/static/sounds/click/click_11.ogg
new file mode 100644
index 0000000..6f7a6b6
--- /dev/null
+++ b/static/sounds/click/click_11.ogg
Binary files differ
diff --git a/static/sounds/click/click_12.ogg b/static/sounds/click/click_12.ogg
new file mode 100644
index 0000000..f098d41
--- /dev/null
+++ b/static/sounds/click/click_12.ogg
Binary files differ
diff --git a/static/sounds/click/click_13.ogg b/static/sounds/click/click_13.ogg
new file mode 100644
index 0000000..e12c96e
--- /dev/null
+++ b/static/sounds/click/click_13.ogg
Binary files differ
diff --git a/static/sounds/click/click_14.ogg b/static/sounds/click/click_14.ogg
new file mode 100644
index 0000000..872537c
--- /dev/null
+++ b/static/sounds/click/click_14.ogg
Binary files differ
diff --git a/static/sounds/click/click_15.ogg b/static/sounds/click/click_15.ogg
new file mode 100644
index 0000000..f5b8de3
--- /dev/null
+++ b/static/sounds/click/click_15.ogg
Binary files differ
diff --git a/static/sounds/click/click_16.ogg b/static/sounds/click/click_16.ogg
new file mode 100644
index 0000000..2535521
--- /dev/null
+++ b/static/sounds/click/click_16.ogg
Binary files differ
diff --git a/static/sounds/click/click_17.ogg b/static/sounds/click/click_17.ogg
new file mode 100644
index 0000000..b697ba0
--- /dev/null
+++ b/static/sounds/click/click_17.ogg
Binary files differ
diff --git a/static/sounds/click/click_18.ogg b/static/sounds/click/click_18.ogg
new file mode 100644
index 0000000..3b14326
--- /dev/null
+++ b/static/sounds/click/click_18.ogg
Binary files differ
diff --git a/static/sounds/click/click_19.ogg b/static/sounds/click/click_19.ogg
new file mode 100644
index 0000000..6b3f03d
--- /dev/null
+++ b/static/sounds/click/click_19.ogg
Binary files differ
diff --git a/static/sounds/click/click_20.ogg b/static/sounds/click/click_20.ogg
new file mode 100644
index 0000000..b625eeb
--- /dev/null
+++ b/static/sounds/click/click_20.ogg
Binary files differ
diff --git a/static/sounds/click/click_21.ogg b/static/sounds/click/click_21.ogg
new file mode 100644
index 0000000..ac33622
--- /dev/null
+++ b/static/sounds/click/click_21.ogg
Binary files differ
diff --git a/static/sounds/error/error_01.ogg b/static/sounds/error/error_01.ogg
new file mode 100644
index 0000000..dda8168
--- /dev/null
+++ b/static/sounds/error/error_01.ogg
Binary files differ
diff --git a/static/sounds/error/error_02.ogg b/static/sounds/error/error_02.ogg
new file mode 100644
index 0000000..71d4655
--- /dev/null
+++ b/static/sounds/error/error_02.ogg
Binary files differ
diff --git a/static/sounds/error/error_03.ogg b/static/sounds/error/error_03.ogg
new file mode 100644
index 0000000..85baec3
--- /dev/null
+++ b/static/sounds/error/error_03.ogg
Binary files differ
diff --git a/static/sounds/error/error_04.ogg b/static/sounds/error/error_04.ogg
new file mode 100644
index 0000000..4072667
--- /dev/null
+++ b/static/sounds/error/error_04.ogg
Binary files differ
diff --git a/static/sounds/error/error_05.ogg b/static/sounds/error/error_05.ogg
new file mode 100644
index 0000000..6f8664e
--- /dev/null
+++ b/static/sounds/error/error_05.ogg
Binary files differ
diff --git a/static/sounds/error/error_06.ogg b/static/sounds/error/error_06.ogg
new file mode 100644
index 0000000..d0baac8
--- /dev/null
+++ b/static/sounds/error/error_06.ogg
Binary files differ
diff --git a/static/sounds/error/error_07.ogg b/static/sounds/error/error_07.ogg
new file mode 100644
index 0000000..f199b9c
--- /dev/null
+++ b/static/sounds/error/error_07.ogg
Binary files differ
diff --git a/static/sounds/error/error_08.ogg b/static/sounds/error/error_08.ogg
new file mode 100644
index 0000000..c12a469
--- /dev/null
+++ b/static/sounds/error/error_08.ogg
Binary files differ
diff --git a/static/sounds/error/error_09.ogg b/static/sounds/error/error_09.ogg
new file mode 100644
index 0000000..63dc5f5
--- /dev/null
+++ b/static/sounds/error/error_09.ogg
Binary files differ
diff --git a/static/sounds/error/error_10.ogg b/static/sounds/error/error_10.ogg
new file mode 100644
index 0000000..d283de8
--- /dev/null
+++ b/static/sounds/error/error_10.ogg
Binary files differ
diff --git a/static/sounds/error/error_11.ogg b/static/sounds/error/error_11.ogg
new file mode 100644
index 0000000..a9aae46
--- /dev/null
+++ b/static/sounds/error/error_11.ogg
Binary files differ
diff --git a/static/sounds/error/error_12.ogg b/static/sounds/error/error_12.ogg
new file mode 100644
index 0000000..c142d91
--- /dev/null
+++ b/static/sounds/error/error_12.ogg
Binary files differ
diff --git a/static/sounds/error/error_13.ogg b/static/sounds/error/error_13.ogg
new file mode 100644
index 0000000..571d61a
--- /dev/null
+++ b/static/sounds/error/error_13.ogg
Binary files differ
diff --git a/static/sounds/error/error_14.ogg b/static/sounds/error/error_14.ogg
new file mode 100644
index 0000000..453cbdd
--- /dev/null
+++ b/static/sounds/error/error_14.ogg
Binary files differ
diff --git a/static/sounds/error/error_15.ogg b/static/sounds/error/error_15.ogg
new file mode 100644
index 0000000..b5a68e4
--- /dev/null
+++ b/static/sounds/error/error_15.ogg
Binary files differ
diff --git a/static/sounds/error/error_16.ogg b/static/sounds/error/error_16.ogg
new file mode 100644
index 0000000..8146fe6
--- /dev/null
+++ b/static/sounds/error/error_16.ogg
Binary files differ
diff --git a/static/sounds/error/error_17.ogg b/static/sounds/error/error_17.ogg
new file mode 100644
index 0000000..6c85064
--- /dev/null
+++ b/static/sounds/error/error_17.ogg
Binary files differ
diff --git a/static/sounds/error/error_18.ogg b/static/sounds/error/error_18.ogg
new file mode 100644
index 0000000..1fef288
--- /dev/null
+++ b/static/sounds/error/error_18.ogg
Binary files differ
diff --git a/static/sounds/error/error_19.ogg b/static/sounds/error/error_19.ogg
new file mode 100644
index 0000000..958a6b2
--- /dev/null
+++ b/static/sounds/error/error_19.ogg
Binary files differ
diff --git a/static/sounds/error/error_20.ogg b/static/sounds/error/error_20.ogg
new file mode 100644
index 0000000..b3f28d5
--- /dev/null
+++ b/static/sounds/error/error_20.ogg
Binary files differ
diff --git a/static/sounds/error/error_21.ogg b/static/sounds/error/error_21.ogg
new file mode 100644
index 0000000..243ed95
--- /dev/null
+++ b/static/sounds/error/error_21.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_01.ogg b/static/sounds/pop/pop_01.ogg
new file mode 100644
index 0000000..3b298d4
--- /dev/null
+++ b/static/sounds/pop/pop_01.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_02.ogg b/static/sounds/pop/pop_02.ogg
new file mode 100644
index 0000000..1062397
--- /dev/null
+++ b/static/sounds/pop/pop_02.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_03.ogg b/static/sounds/pop/pop_03.ogg
new file mode 100644
index 0000000..9a4d63c
--- /dev/null
+++ b/static/sounds/pop/pop_03.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_04.ogg b/static/sounds/pop/pop_04.ogg
new file mode 100644
index 0000000..0252bb9
--- /dev/null
+++ b/static/sounds/pop/pop_04.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_05.ogg b/static/sounds/pop/pop_05.ogg
new file mode 100644
index 0000000..8499dc7
--- /dev/null
+++ b/static/sounds/pop/pop_05.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_06.ogg b/static/sounds/pop/pop_06.ogg
new file mode 100644
index 0000000..2fc6fe8
--- /dev/null
+++ b/static/sounds/pop/pop_06.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_07.ogg b/static/sounds/pop/pop_07.ogg
new file mode 100644
index 0000000..fc06191
--- /dev/null
+++ b/static/sounds/pop/pop_07.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_08.ogg b/static/sounds/pop/pop_08.ogg
new file mode 100644
index 0000000..aca022d
--- /dev/null
+++ b/static/sounds/pop/pop_08.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_09.ogg b/static/sounds/pop/pop_09.ogg
new file mode 100644
index 0000000..1418fb5
--- /dev/null
+++ b/static/sounds/pop/pop_09.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_10.ogg b/static/sounds/pop/pop_10.ogg
new file mode 100644
index 0000000..9df2f12
--- /dev/null
+++ b/static/sounds/pop/pop_10.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_11.ogg b/static/sounds/pop/pop_11.ogg
new file mode 100644
index 0000000..b083a1f
--- /dev/null
+++ b/static/sounds/pop/pop_11.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_12.ogg b/static/sounds/pop/pop_12.ogg
new file mode 100644
index 0000000..e6414f5
--- /dev/null
+++ b/static/sounds/pop/pop_12.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_13.ogg b/static/sounds/pop/pop_13.ogg
new file mode 100644
index 0000000..1c75f3a
--- /dev/null
+++ b/static/sounds/pop/pop_13.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_14.ogg b/static/sounds/pop/pop_14.ogg
new file mode 100644
index 0000000..8f4da09
--- /dev/null
+++ b/static/sounds/pop/pop_14.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_15.ogg b/static/sounds/pop/pop_15.ogg
new file mode 100644
index 0000000..60b942e
--- /dev/null
+++ b/static/sounds/pop/pop_15.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_16.ogg b/static/sounds/pop/pop_16.ogg
new file mode 100644
index 0000000..10c7098
--- /dev/null
+++ b/static/sounds/pop/pop_16.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_17.ogg b/static/sounds/pop/pop_17.ogg
new file mode 100644
index 0000000..2ce928d
--- /dev/null
+++ b/static/sounds/pop/pop_17.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_18.ogg b/static/sounds/pop/pop_18.ogg
new file mode 100644
index 0000000..1fb5da6
--- /dev/null
+++ b/static/sounds/pop/pop_18.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_19.ogg b/static/sounds/pop/pop_19.ogg
new file mode 100644
index 0000000..dcbc232
--- /dev/null
+++ b/static/sounds/pop/pop_19.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_20.ogg b/static/sounds/pop/pop_20.ogg
new file mode 100644
index 0000000..cdceb38
--- /dev/null
+++ b/static/sounds/pop/pop_20.ogg
Binary files differ
diff --git a/static/sounds/pop/pop_21.ogg b/static/sounds/pop/pop_21.ogg
new file mode 100644
index 0000000..e4d65b4
--- /dev/null
+++ b/static/sounds/pop/pop_21.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_01.ogg b/static/sounds/tadah/tadah_01.ogg
new file mode 100644
index 0000000..949860d
--- /dev/null
+++ b/static/sounds/tadah/tadah_01.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_02.ogg b/static/sounds/tadah/tadah_02.ogg
new file mode 100644
index 0000000..8f0ff6c
--- /dev/null
+++ b/static/sounds/tadah/tadah_02.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_03.ogg b/static/sounds/tadah/tadah_03.ogg
new file mode 100644
index 0000000..7fa0af2
--- /dev/null
+++ b/static/sounds/tadah/tadah_03.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_04.ogg b/static/sounds/tadah/tadah_04.ogg
new file mode 100644
index 0000000..dd59b60
--- /dev/null
+++ b/static/sounds/tadah/tadah_04.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_05.ogg b/static/sounds/tadah/tadah_05.ogg
new file mode 100644
index 0000000..2af166a
--- /dev/null
+++ b/static/sounds/tadah/tadah_05.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_06.ogg b/static/sounds/tadah/tadah_06.ogg
new file mode 100644
index 0000000..f1d01f4
--- /dev/null
+++ b/static/sounds/tadah/tadah_06.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_07.ogg b/static/sounds/tadah/tadah_07.ogg
new file mode 100644
index 0000000..c3ac262
--- /dev/null
+++ b/static/sounds/tadah/tadah_07.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_08.ogg b/static/sounds/tadah/tadah_08.ogg
new file mode 100644
index 0000000..d0e50ce
--- /dev/null
+++ b/static/sounds/tadah/tadah_08.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_09.ogg b/static/sounds/tadah/tadah_09.ogg
new file mode 100644
index 0000000..adec55a
--- /dev/null
+++ b/static/sounds/tadah/tadah_09.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_10.ogg b/static/sounds/tadah/tadah_10.ogg
new file mode 100644
index 0000000..cb0328d
--- /dev/null
+++ b/static/sounds/tadah/tadah_10.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_11.ogg b/static/sounds/tadah/tadah_11.ogg
new file mode 100644
index 0000000..fb67c80
--- /dev/null
+++ b/static/sounds/tadah/tadah_11.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_12.ogg b/static/sounds/tadah/tadah_12.ogg
new file mode 100644
index 0000000..3a298af
--- /dev/null
+++ b/static/sounds/tadah/tadah_12.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_13.ogg b/static/sounds/tadah/tadah_13.ogg
new file mode 100644
index 0000000..70d1c04
--- /dev/null
+++ b/static/sounds/tadah/tadah_13.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_14.ogg b/static/sounds/tadah/tadah_14.ogg
new file mode 100644
index 0000000..aaca047
--- /dev/null
+++ b/static/sounds/tadah/tadah_14.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_15.ogg b/static/sounds/tadah/tadah_15.ogg
new file mode 100644
index 0000000..79e0e9a
--- /dev/null
+++ b/static/sounds/tadah/tadah_15.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_16.ogg b/static/sounds/tadah/tadah_16.ogg
new file mode 100644
index 0000000..97fafe2
--- /dev/null
+++ b/static/sounds/tadah/tadah_16.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_17.ogg b/static/sounds/tadah/tadah_17.ogg
new file mode 100644
index 0000000..58dae6b
--- /dev/null
+++ b/static/sounds/tadah/tadah_17.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_18.ogg b/static/sounds/tadah/tadah_18.ogg
new file mode 100644
index 0000000..3ceb59b
--- /dev/null
+++ b/static/sounds/tadah/tadah_18.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_19.ogg b/static/sounds/tadah/tadah_19.ogg
new file mode 100644
index 0000000..25abcea
--- /dev/null
+++ b/static/sounds/tadah/tadah_19.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_20.ogg b/static/sounds/tadah/tadah_20.ogg
new file mode 100644
index 0000000..4d3e26d
--- /dev/null
+++ b/static/sounds/tadah/tadah_20.ogg
Binary files differ
diff --git a/static/sounds/tadah/tadah_21.ogg b/static/sounds/tadah/tadah_21.ogg
new file mode 100644
index 0000000..55ea644
--- /dev/null
+++ b/static/sounds/tadah/tadah_21.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_01.ogg b/static/sounds/woosh/woosh_01.ogg
new file mode 100644
index 0000000..90c95c2
--- /dev/null
+++ b/static/sounds/woosh/woosh_01.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_02.ogg b/static/sounds/woosh/woosh_02.ogg
new file mode 100644
index 0000000..1d9a374
--- /dev/null
+++ b/static/sounds/woosh/woosh_02.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_03.ogg b/static/sounds/woosh/woosh_03.ogg
new file mode 100644
index 0000000..6b1eeca
--- /dev/null
+++ b/static/sounds/woosh/woosh_03.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_04.ogg b/static/sounds/woosh/woosh_04.ogg
new file mode 100644
index 0000000..dcf04f7
--- /dev/null
+++ b/static/sounds/woosh/woosh_04.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_05.ogg b/static/sounds/woosh/woosh_05.ogg
new file mode 100644
index 0000000..7af825c
--- /dev/null
+++ b/static/sounds/woosh/woosh_05.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_06.ogg b/static/sounds/woosh/woosh_06.ogg
new file mode 100644
index 0000000..d802aab
--- /dev/null
+++ b/static/sounds/woosh/woosh_06.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_07.ogg b/static/sounds/woosh/woosh_07.ogg
new file mode 100644
index 0000000..dfd8852
--- /dev/null
+++ b/static/sounds/woosh/woosh_07.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_08.ogg b/static/sounds/woosh/woosh_08.ogg
new file mode 100644
index 0000000..70a5ba4
--- /dev/null
+++ b/static/sounds/woosh/woosh_08.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_09.ogg b/static/sounds/woosh/woosh_09.ogg
new file mode 100644
index 0000000..82b5d17
--- /dev/null
+++ b/static/sounds/woosh/woosh_09.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_10.ogg b/static/sounds/woosh/woosh_10.ogg
new file mode 100644
index 0000000..3c426a5
--- /dev/null
+++ b/static/sounds/woosh/woosh_10.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_11.ogg b/static/sounds/woosh/woosh_11.ogg
new file mode 100644
index 0000000..693fa73
--- /dev/null
+++ b/static/sounds/woosh/woosh_11.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_12.ogg b/static/sounds/woosh/woosh_12.ogg
new file mode 100644
index 0000000..0187c60
--- /dev/null
+++ b/static/sounds/woosh/woosh_12.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_13.ogg b/static/sounds/woosh/woosh_13.ogg
new file mode 100644
index 0000000..1bcbfd2
--- /dev/null
+++ b/static/sounds/woosh/woosh_13.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_14.ogg b/static/sounds/woosh/woosh_14.ogg
new file mode 100644
index 0000000..8f6a01d
--- /dev/null
+++ b/static/sounds/woosh/woosh_14.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_15.ogg b/static/sounds/woosh/woosh_15.ogg
new file mode 100644
index 0000000..e0e0384
--- /dev/null
+++ b/static/sounds/woosh/woosh_15.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_16.ogg b/static/sounds/woosh/woosh_16.ogg
new file mode 100644
index 0000000..5ff32ec
--- /dev/null
+++ b/static/sounds/woosh/woosh_16.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_17.ogg b/static/sounds/woosh/woosh_17.ogg
new file mode 100644
index 0000000..e420b0a
--- /dev/null
+++ b/static/sounds/woosh/woosh_17.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_18.ogg b/static/sounds/woosh/woosh_18.ogg
new file mode 100644
index 0000000..bde4be5
--- /dev/null
+++ b/static/sounds/woosh/woosh_18.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_19.ogg b/static/sounds/woosh/woosh_19.ogg
new file mode 100644
index 0000000..980826a
--- /dev/null
+++ b/static/sounds/woosh/woosh_19.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_20.ogg b/static/sounds/woosh/woosh_20.ogg
new file mode 100644
index 0000000..8cfd35d
--- /dev/null
+++ b/static/sounds/woosh/woosh_20.ogg
Binary files differ
diff --git a/static/sounds/woosh/woosh_21.ogg b/static/sounds/woosh/woosh_21.ogg
new file mode 100644
index 0000000..0b323b2
--- /dev/null
+++ b/static/sounds/woosh/woosh_21.ogg
Binary files differ
diff --git a/static/type/sans/400.woff2 b/static/type/sans/400.woff2
new file mode 100644
index 0000000..a25cdbc
--- /dev/null
+++ b/static/type/sans/400.woff2
Binary files differ
diff --git a/static/type/sans/400i.woff2 b/static/type/sans/400i.woff2
new file mode 100644
index 0000000..ae1630c
--- /dev/null
+++ b/static/type/sans/400i.woff2
Binary files differ
diff --git a/static/type/sans/600.woff2 b/static/type/sans/600.woff2
new file mode 100644
index 0000000..d4c3f63
--- /dev/null
+++ b/static/type/sans/600.woff2
Binary files differ
diff --git a/static/type/sans/600i.woff2 b/static/type/sans/600i.woff2
new file mode 100644
index 0000000..fb9324d
--- /dev/null
+++ b/static/type/sans/600i.woff2
Binary files differ
diff --git a/static/type/sans/typeface.txt b/static/type/sans/typeface.txt
new file mode 100644
index 0000000..419bc64
--- /dev/null
+++ b/static/type/sans/typeface.txt
@@ -0,0 +1,4 @@
+iA Writer Quattro S
+
+400 - Regular
+600 - SemiBold
diff --git a/static/uchu.css b/static/uchu.css
new file mode 100644
index 0000000..459bd36
--- /dev/null
+++ b/static/uchu.css
@@ -0,0 +1 @@
+:root{--background-color: var(--uchu-gray-1);--base-grid-color1: oklch(var(--uchu-blue-3-raw) / 30%);--base-grid-color2: oklch(var(--uchu-purple-3-raw) / 30%);--baseline: calc(var(--line-height) / 2);--block-spacing-bottom: var(--line-height);--block-spacing-top: 0;--column-gap: calc(var(--line-height) * 2);--display-scale: 1;--font-mono: null;--font-sans: null;--font-serif: null;--font-size: 12px;--foreground-color: oklch(var(--uchu-yin-raw) / 80%);--h1-size: 2.8rem;--h2-size: 2.2rem;--h3-size: 1.4rem;--h4-size: 1.1rem;--hr-thickness: 2px;--line-height: calc(var(--font-size) * 1.5);--pixel: 1px;--row-gap: var(--line-height)}@media only screen and (min-device-pixel-ratio: 1.5),only screen and (min-resolution: 1.5dppx){:root{--display-scale: 2;--pixel: 0.5px}}@media only screen and (min-device-pixel-ratio: 2.5),only screen and (min-resolution: 2.5dppx){:root{--display-scale: 3;--pixel: 0.34px}}@media only screen and (min-device-pixel-ratio: 3.5),only screen and (min-resolution: 3.5dppx){:root{--display-scale: 4;--pixel: 0.25px}}*{box-sizing:border-box;font:inherit;line-height:inherit}*+hr:last-child{margin-top:calc(var(--hr-thickness)/-2)}*+h2{margin-top:var(--line-height);margin-bottom:0;padding-top:calc(var(--line-height)*.5);padding-bottom:calc(var(--line-height)*.5)}*+img{margin-top:calc(var(--baseline)*-1)}@media(prefers-reduced-motion: reduce){*{transition:none}}a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,grid,h1,h2,h3,h4,h5,h6,header,hgroup,hr,html,i,iframe,img,ins,kbd,label,legend,li,main,mark,menu,nav,noscript,object,ol,output,p,pre,q,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;vertical-align:baseline}address,article,aside,blockquote,dd,dl,dt,fieldset,figure,form,h1,h2,h3,h4,h5,h6,li,nav,ol,p,pre,r-grid,table,tfoot,ul,video{margin-top:var(--block-spacing-top);margin-bottom:var(--block-spacing-bottom)}a{color:inherit;cursor:pointer;text-decoration:underline oklch(var(--uchu-yin-raw)/30%);transition:all .2s;white-space:nowrap;-webkit-text-decoration:underline oklch(var(--uchu-yin-raw)/30%)}a:hover{color:var(--uchu-blue-4)}b,strong{font-weight:600}b code,b pre,b tt,strong code,strong pre,strong tt{font-weight:inherit}blockquote,q{quotes:none}blockquote::after,blockquote::before,q::after,q::before{content:none}button{margin:0;padding:0;appearance:none;background-color:rgba(0,0,0,0);border:none}html{background-color:var(--background-color);color:var(--foreground-color);font-family:var(--font-sans),-system-ui,system-ui,sans-serif;font-feature-settings:"kern" 1,"liga" 1,"calt" 1,"cv10" 1;font-size:var(--font-size);font-variant-ligatures:contextual common-ligatures;letter-spacing:-0.01rem;line-height:var(--line-height)}body{overflow-x:hidden;padding:calc(var(--line-height)*2);padding-bottom:calc(var(--line-height)*3);scroll-behavior:smooth}@media only screen and (max-width: 600px){body{padding:var(--line-height);padding-bottom:calc(var(--line-height)*2)}}:first-child{margin-top:unset}:last-child{margin-bottom:unset}hr{background-color:var(--foreground-color);border:none;height:var(--hr-thickness);margin-top:calc(var(--line-height) - var(--hr-thickness)/2);margin-bottom:calc(var(--line-height) - var(--hr-thickness)/2);opacity:.05}hr:not(:first-child){margin-top:var(--line-height);margin-bottom:calc(var(--line-height) - var(--hr-thickness))}hr:last-child,hr:only-child{margin-top:calc(var(--line-height) - var(--hr-thickness)/2)}hr:first-child{margin-top:calc(var(--hr-thickness)/-2);margin-bottom:calc(var(--line-height) - var(--hr-thickness)/2)}hr:last-child{margin-bottom:calc(var(--hr-thickness)/-2)}hr:only-child{margin-bottom:calc(var(--line-height) - var(--hr-thickness)/2)}em,i{font-style:italic}code,pre,tt{font-family:var(--mono-font),Inconsolata,Menlo,monospace;font-variant-numeric:slashed-zero;font-weight:430;white-space:pre-wrap;word-wrap:break-word}code b,code strong,pre b,pre strong,tt b,tt strong{font-weight:580}pre{display:block;overflow-x:auto;white-space:pre;word-wrap:normal}pre code,pre tt{white-space:pre;word-wrap:normal}h1{font-size:var(--h1-size);font-weight:720;letter-spacing:-0.05rem;line-height:calc(var(--line-height)*2);margin-top:calc(var(--line-height)*2);margin-bottom:var(--line-height);margin-left:calc(var(--h1-size)/-22);word-break:break-word}h1.single-line{margin-top:var(--line-height);padding-top:calc(var(--line-height)*.5);padding-bottom:calc(var(--line-height)*.5)}h1.single-line:first-child{margin-top:0;padding-bottom:calc(var(--line-height)*.5)}h1.large{--h1-size: 4rem;font-weight:730;line-height:calc(var(--line-height)*3)}h1.xlarge{--h1-size: 5.5rem;font-weight:740;line-height:calc(var(--line-height)*4)}h1.xxlarge{--h1-size: 7.5rem;font-weight:750;line-height:calc(var(--line-height)*5)}h1.xxxlarge{--h1-size: 10.5rem;font-weight:760;line-height:calc(var(--line-height)*7)}h2{font-size:var(--h2-size);font-weight:700;letter-spacing:-0.03rem;line-height:calc(var(--line-height)*2);margin-bottom:var(--line-height);margin-left:calc(var(--h2-size)/-26)}h2.single-line{margin-top:var(--line-height);margin-bottom:0;padding-top:calc(var(--line-height)*.5);padding-bottom:calc(var(--line-height)*.5)}h2.single-line:first-child{margin-top:unset}h3,h4{font-size:var(--h3-size);font-weight:700;letter-spacing:-0.02rem;line-height:calc(var(--line-height)*1.15);margin-bottom:var(--baseline);padding-top:calc(var(--baseline)*.75);padding-bottom:calc(var(--baseline)*.25)}h3.single-line,h4.single-line{margin-bottom:0;padding-bottom:calc(var(--baseline)*1.25)}h3+h1,h3+h1.single-line,h4+h1,h4+h1.single-line{margin-top:calc(var(--baseline)*3)}h3.single-line+h1,h3.single-line+h1.single-line,h3.single-line+h2,h3.single-line+h2.single-line,h4.single-line+h1,h4.single-line+h1.single-line,h4.single-line+h2,h4.single-line+h2.single-line{margin-top:var(--line-height)}h3+h2,h3+h2.single-line,h4+h2,h4+h2.single-line{margin-top:var(--baseline)}h4{font-size:var(--h4-size);font-weight:700;letter-spacing:-0.012rem}h5,h6{font-weight:670;letter-spacing:-0.015rem;margin-bottom:0}h1>a,h2>a,h3>a,h4>a,h5>a,h6>a{text-decoration:none}h1>a:hover,h2>a:hover,h3>a:hover,h4>a:hover,h5>a:hover,h6>a:hover{color:inherit;text-decoration:underline oklch(var(--uchu-yin-raw)/30%)}img{display:block;margin-top:var(--baseline);margin-bottom:var(--baseline)}img:first-child,img:last-child{display:block;margin-top:var(--baseline);margin-bottom:var(--baseline)}img:only-child{margin:0}img.cover,img.fill{object-fit:cover}img.bottom,img.center,img.top{align-self:center}img.bottom{object-position:center bottom}img.center{object-position:center center}img.top{object-position:center top}img.left.bottom,img.left.center,img.left.top{align-self:flex-start}img.left.bottom{object-position:left bottom}img.left.center{object-position:left center}img.left.top{object-position:left top}img.right.bottom,img.right.center,img.right.top{align-self:flex-end}img.right.bottom{object-position:right bottom}img.right.center{object-position:right center}img.right.top{object-position:right top}input,select{appearance:none;border:none;letter-spacing:-0.01rem;-moz-appearance:none;-webkit-appearance:none}input:focus-visible,select:focus-visible{outline:2px solid var(--uchu-blue-4)}ol,ul{list-style-position:outside;--list-indentation: 2rem}ol.compact>li,ul.compact>li{margin-bottom:0}ol:not([start]){counter-reset:ol-counter;list-style:none;padding-left:var(--list-indentation)}ol:not([start])>li{counter-increment:ol-counter;position:relative}ol:not([start])>li::before{--space: 0.5rem;--width: calc(var(--list-indentation) - var(--space));width:var(--width);height:var(--line-height);content:counter(ol-counter) ". ";font-variant-numeric:tabular-nums;font-weight:500;left:calc(-1*(var(--width) + var(--space)));position:absolute;text-align:left}ol[start]{padding-inline-start:var(--list-indentation)}ul{padding-left:1.3rem}li{margin-bottom:var(--baseline);margin-left:.2rem}li>p+ol,li>p+ul{margin-top:calc(var(--baseline)*-1)}li.task-list-item{list-style-type:none}li.task-list-item>input[type=checkbox]{width:1.5rem;height:var(--baseline);appearance:none;background:none;border:none;display:inline-block;list-style:none;margin-right:.5rem;margin-left:-1.4rem;opacity:1;position:relative;--check-svg-url: url('data:image/svg+xml;utf8,<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M4.7 9.3L8.1 12.6L13.8 6.9L12.3 5.3L8.1 9.5L6.3 7.7L4.7 9.3Z" fill="black"/></svg>');--outline-svg-url: url('data:image/svg+xml;utf8,<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="1.75" y="1.75" width="14.5" height="14.5" rx="0.5" stroke="black" stroke-width="1.5"/></svg>')}li.task-list-item>input[type=checkbox]::after,li.task-list-item>input[type=checkbox]::before{top:-0.25rem;bottom:-0.25rem;left:0;background-position:50%;background-repeat:no-repeat;background-size:contain;color:rgba(0,0,0,0);content:"×";display:block;position:absolute;width:1.1rem}@supports(mask-image: linear-gradient(var(--uchu-yin), red)){li.task-list-item>input[type=checkbox]::after,li.task-list-item>input[type=checkbox]::before{mask-position:center center;mask-repeat:no-repeat;mask-size:contain}}li.task-list-item>input[type=checkbox]::after{background-image:var(--outline-svg-url)}@supports(mask-image: linear-gradient(var(--uchu-yin), red)){li.task-list-item>input[type=checkbox]::after{background-color:var(--foreground-color);mask-image:var(--outline-svg-url)}}li.task-list-item>input[type=checkbox][checked]::before{background-image:var(--check-svg-url)}@supports(mask-image: linear-gradient(var(--uchu-yin), red)){li.task-list-item>input[type=checkbox][checked]::before{background-color:var(--foreground-color);mask-image:var(--check-svg-url)}}table{--border-opacity: 0.15;--border-color: rgba(var(--foreground-color-rgb), calc(var(--foreground-color-a) * var(--border-opacity)));--border-width: 1px;border-collapse:collapse;border-right:var(--border-width) solid var(--border-color);border-spacing:0;border-top:var(--border-width) solid var(--border-color);overflow:auto;position:relative;margin-top:calc(var(--line-height)*1 + var(--border-width)*-1);margin-bottom:calc(var(--line-height)*1.5)}table *{box-sizing:border-box}table:first-child{margin-top:calc(var(--line-height)*.5 + var(--border-width)*-1);margin-bottom:calc(var(--line-height)*.5)}table td,table th{background-image:linear-gradient(90deg, var(--border-color), var(--border-color) 1px, transparent 0, transparent calc(var(--baseline) / 2));background-position:0 -1px;background-repeat:no-repeat;background-size:100% 100%;padding:var(--baseline) 1rem;position:relative}table td::after,table th::after{right:0;bottom:0;left:0;background-color:var(--border-color);color:rgba(0,0,0,0);content:"A";height:var(--border-width);pointer-events:none;position:absolute;z-index:1}table th{font-weight:600;text-align:left}table th[align=center]{text-align:center}table th[align=right]{text-align:right}p+table{margin-top:calc(var(--line-height)*1.5 + var(--border-width)*-1)}@media only screen and (max-width: 600px){.only-large-window{display:none}}@media only screen and (min-width: 601px){.only-small-window{display:none}}.compact>li>p+ol,.compact>li>p+ul{margin-top:calc(var(--block-spacing-bottom)*-1)}.small{font-size:.85rem;line-height:var(--line-height)}.xsmall{font-size:.8rem;line-height:calc(var(--line-height)*.75);padding-top:calc(var(--line-height)*.25)}.xxsmall{font-size:.65rem;line-height:calc(var(--line-height)*.7);padding-top:calc(var(--line-height)*.3)}.xxxsmall{font-size:.5rem;line-height:calc(var(--line-height)*.5);padding-bottom:calc(var(--line-height)*.25)}.show-base-grid{background-image:repeating-linear-gradient(0deg, var(--base-grid-color2), var(--base-grid-color2) 1px, transparent 0, transparent calc(var(--baseline) / 2), var(--base-grid-color1) calc(var(--baseline) / 2), var(--base-grid-color1) calc(var(--baseline) / 2 + 1px), transparent calc(var(--baseline) / 2 + 1px), transparent var(--baseline));background-position:0 .5px;background-repeat:repeat-y;background-size:100% var(--baseline)}.single-line{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.block{display:block}.inline{display:inline-block}.flex-h,.flex-v{display:flex}.flex-h{flex-direction:row}.flex-h .bottom{align-self:flex-end}.flex-v{flex-direction:column}.flex-v.center{align-self:center}.center{text-align:center}.left{text-align:left}.right{text-align:right}.margin0{margin:0}.margin1{margin:calc(var(--line-height)*1)}.margin2{margin:calc(var(--line-height)*2)}.margin3{margin:calc(var(--line-height)*3)}.margin4{margin:calc(var(--line-height)*4)}.margin5{margin:calc(var(--line-height)*8)}.padding0{padding:0}.padding1{padding:calc(var(--line-height)*1)}.padding2{padding:calc(var(--line-height)*2)}.padding3{padding:calc(var(--line-height)*3)}.padding4{padding:calc(var(--line-height)*4)}.padding5{padding:calc(var(--line-height)*8)}.w-1{width:calc(var(--line-height)*1)}.w-2{width:calc(var(--line-height)*2)}.w-3{width:calc(var(--line-height)*3)}.w-4{width:calc(var(--line-height)*4)}.w-5{width:calc(var(--line-height)*5)}.w-6{width:calc(var(--line-height)*6)}.w-7{width:calc(var(--line-height)*7)}.w-8{width:calc(var(--line-height)*8)}.w-9{width:calc(var(--line-height)*9)}.w-10{width:calc(var(--line-height)*10)}.w-11{width:calc(var(--line-height)*11)}.w-12{width:calc(var(--line-height)*12)}.w-13{width:calc(var(--line-height)*13)}.w-14{width:calc(var(--line-height)*14)}.w-15{width:calc(var(--line-height)*15)}.w-16{width:calc(var(--line-height)*16)}.w-17{width:calc(var(--line-height)*17)}.w-18{width:calc(var(--line-height)*18)}.w-19{width:calc(var(--line-height)*19)}.w-20{width:calc(var(--line-height)*20)}.w-21{width:calc(var(--line-height)*21)}.w-22{width:calc(var(--line-height)*22)}.w-23{width:calc(var(--line-height)*23)}.w-24{width:calc(var(--line-height)*24)}.w-25{width:calc(var(--line-height)*25)}.w-26{width:calc(var(--line-height)*26)}.w-27{width:calc(var(--line-height)*27)}.w-28{width:calc(var(--line-height)*28)}.w-29{width:calc(var(--line-height)*29)}.w-30{width:calc(var(--line-height)*30)}.w-31{width:calc(var(--line-height)*31)}.w-32{width:calc(var(--line-height)*32)}.w-33{width:calc(var(--line-height)*33)}.w-34{width:calc(var(--line-height)*34)}.w-35{width:calc(var(--line-height)*35)}.w-36{width:calc(var(--line-height)*36)}.w-37{width:calc(var(--line-height)*37)}.w-38{width:calc(var(--line-height)*38)}.w-39{width:calc(var(--line-height)*39)}.w-40{width:calc(var(--line-height)*40)}.w-full{width:100%}.h-1{height:calc(var(--line-height)*1)}.h-2{height:calc(var(--line-height)*2)}.h-3{height:calc(var(--line-height)*3)}.h-4{height:calc(var(--line-height)*4)}.h-5{height:calc(var(--line-height)*5)}.h-6{height:calc(var(--line-height)*6)}.h-7{height:calc(var(--line-height)*7)}.h-8{height:calc(var(--line-height)*8)}.h-9{height:calc(var(--line-height)*9)}.h-10{height:calc(var(--line-height)*10)}.h-11{height:calc(var(--line-height)*11)}.h-12{height:calc(var(--line-height)*12)}.h-13{height:calc(var(--line-height)*13)}.h-14{height:calc(var(--line-height)*14)}.h-15{height:calc(var(--line-height)*15)}.h-16{height:calc(var(--line-height)*16)}.h-17{height:calc(var(--line-height)*17)}.h-18{height:calc(var(--line-height)*18)}.h-19{height:calc(var(--line-height)*19)}.h-20{height:calc(var(--line-height)*20)}.h-21{height:calc(var(--line-height)*21)}.h-22{height:calc(var(--line-height)*22)}.h-23{height:calc(var(--line-height)*23)}.h-24{height:calc(var(--line-height)*24)}.h-25{height:calc(var(--line-height)*25)}.h-26{height:calc(var(--line-height)*26)}.h-27{height:calc(var(--line-height)*27)}.h-28{height:calc(var(--line-height)*28)}.h-29{height:calc(var(--line-height)*29)}.h-30{height:calc(var(--line-height)*30)}.h-31{height:calc(var(--line-height)*31)}.h-32{height:calc(var(--line-height)*32)}.h-33{height:calc(var(--line-height)*33)}.h-34{height:calc(var(--line-height)*34)}.h-35{height:calc(var(--line-height)*35)}.h-36{height:calc(var(--line-height)*36)}.h-37{height:calc(var(--line-height)*37)}.h-38{height:calc(var(--line-height)*38)}.h-39{height:calc(var(--line-height)*39)}.h-40{height:calc(var(--line-height)*40)}.opacity0{opacity:0}.opacity1{opacity:.1}.opacity2{opacity:.2}.opacity3{opacity:.3}.opacity4{opacity:.4}.opacity5{opacity:.5}.opacity6{opacity:.6}.opacity7{opacity:.7}.opacity8{opacity:.8}.opacity9{opacity:.9}.opacity10{opacity:1}u-grid{--grid-tc: repeat(4, 1fr);--grid-ce: -1;--grid-cs: 1;display:grid;grid-column-gap:var(--column-gap);grid-row-gap:var(--row-gap);grid-template-columns:var(--grid-tc)}u-grid.compact{grid-row-gap:0}u-grid.debug>*{--color: rgba(248, 110, 91, 0.3);background-image:linear-gradient(180deg, var(--color) 0, var(--color))}u-grid.debug>:nth-child(6n+2){--color: rgba(103, 126, 208, 0.3)}u-grid.debug>:nth-child(6n+3){--color: rgba(224, 174, 72, 0.3)}u-grid.debug>:nth-child(6n+4){--color: rgba(77, 214, 115, 0.3)}u-grid.debug>:nth-child(6n+5){--color: rgba(217, 103, 219, 0.3)}u-grid.debug>:nth-child(6n+6){--color: rgba(94, 204, 211, 0.3)}u-grid.debug>:nth-child(6n+7){--color: rgba(248, 110, 91, 0.3)}u-grid>hr{grid-column:1/-1}u-grid>hr,u-grid>hr:not(:first-child):not(:last-child){margin-top:calc(var(--line-height) - var(--hr-thickness));margin-bottom:0}u-grid>u-cell{appearance:none;display:block}u-grid>u-cell[span]{grid-column-end:span var(--grid-ce)}u-grid>u-cell[span*="+"],u-grid>u-cell[span*="-"],u-grid>u-cell[span*=".."]{grid-column-start:var(--grid-cs)}u-grid>u-cell[span*="-"],u-grid>u-cell[span*=".."]{grid-column-end:var(--grid-ce)}u-grid>u-cell[span=row]{grid-column:var(--grid-cs)/var(--grid-ce)}u-grid>u-cell[span^="1"]{--grid-cs: 1}u-grid>u-cell[span^="2"]{--grid-cs: 2}u-grid>u-cell[span^="3"]{--grid-cs: 3}u-grid>u-cell[span^="4"]{--grid-cs: 4}u-grid>u-cell[span^="5"]{--grid-cs: 5}u-grid>u-cell[span^="6"]{--grid-cs: 6}u-grid>u-cell[span^="7"]{--grid-cs: 7}u-grid>u-cell[span^="8"]{--grid-cs: 8}u-grid>u-cell[span^="9"]{--grid-cs: 9}u-grid>u-cell[span^="10"]{--grid-cs: 10}u-grid>u-cell[span^="11"]{--grid-cs: 11}u-grid>u-cell[span^="12"]{--grid-cs: 12}u-grid>u-cell[span^="13"]{--grid-cs: 13}u-grid>u-cell[span^="14"]{--grid-cs: 14}u-grid>u-cell[span^="15"]{--grid-cs: 15}u-grid>u-cell[span^="16"]{--grid-cs: 16}u-grid>u-cell[span^="17"]{--grid-cs: 17}u-grid>u-cell[span^="18"]{--grid-cs: 18}u-grid>u-cell[span^="19"]{--grid-cs: 19}u-grid>u-cell[span^="20"]{--grid-cs: 20}u-grid>u-cell[span^="21"]{--grid-cs: 21}u-grid>u-cell[span^="22"]{--grid-cs: 22}u-grid>u-cell[span^="23"]{--grid-cs: 23}u-grid>u-cell[span^="24"]{--grid-cs: 24}u-grid>u-cell[span^="25"]{--grid-cs: 25}u-grid>u-cell[span^="26"]{--grid-cs: 26}u-grid>u-cell[span^="27"]{--grid-cs: 27}u-grid>u-cell[span^="28"]{--grid-cs: 28}u-grid>u-cell[span^="29"]{--grid-cs: 29}u-grid>u-cell[span^="30"]{--grid-cs: 30}u-grid>u-cell[span$="+1"],u-grid>u-cell[span="1"]{--grid-ce: 1}u-grid>u-cell[span$="+2"],u-grid>u-cell[span$="-1"],u-grid>u-cell[span="2"]{--grid-ce: 2}u-grid>u-cell[span$="+3"],u-grid>u-cell[span$="-2"],u-grid>u-cell[span="3"]{--grid-ce: 3}u-grid>u-cell[span$="+4"],u-grid>u-cell[span$="-3"],u-grid>u-cell[span="4"]{--grid-ce: 4}u-grid>u-cell[span$="+5"],u-grid>u-cell[span$="-4"],u-grid>u-cell[span="5"]{--grid-ce: 5}u-grid>u-cell[span$="+6"],u-grid>u-cell[span$="-5"],u-grid>u-cell[span="6"]{--grid-ce: 6}u-grid>u-cell[span$="+7"],u-grid>u-cell[span$="-6"],u-grid>u-cell[span="7"]{--grid-ce: 7}u-grid>u-cell[span$="+8"],u-grid>u-cell[span$="-7"],u-grid>u-cell[span="8"]{--grid-ce: 8}u-grid>u-cell[span$="+9"],u-grid>u-cell[span$="-8"],u-grid>u-cell[span="9"]{--grid-ce: 9}u-grid>u-cell[span$="+10"],u-grid>u-cell[span$="-9"],u-grid>u-cell[span="10"]{--grid-ce: 10}u-grid>u-cell[span$="+11"],u-grid>u-cell[span$="-10"],u-grid>u-cell[span="11"]{--grid-ce: 11}u-grid>u-cell[span$="+12"],u-grid>u-cell[span$="-11"],u-grid>u-cell[span="12"]{--grid-ce: 12}u-grid>u-cell[span$="+13"],u-grid>u-cell[span$="-12"],u-grid>u-cell[span="13"]{--grid-ce: 13}u-grid>u-cell[span$="+14"],u-grid>u-cell[span$="-13"],u-grid>u-cell[span="14"]{--grid-ce: 14}u-grid>u-cell[span$="+15"],u-grid>u-cell[span$="-14"],u-grid>u-cell[span="15"]{--grid-ce: 15}u-grid>u-cell[span$="+16"],u-grid>u-cell[span$="-15"],u-grid>u-cell[span="16"]{--grid-ce: 16}u-grid>u-cell[span$="+17"],u-grid>u-cell[span$="-16"],u-grid>u-cell[span="17"]{--grid-ce: 17}u-grid>u-cell[span$="+18"],u-grid>u-cell[span$="-17"],u-grid>u-cell[span="18"]{--grid-ce: 18}u-grid>u-cell[span$="+19"],u-grid>u-cell[span$="-18"],u-grid>u-cell[span="19"]{--grid-ce: 19}u-grid>u-cell[span$="+20"],u-grid>u-cell[span$="-19"],u-grid>u-cell[span="20"]{--grid-ce: 20}u-grid>u-cell[span$="+21"],u-grid>u-cell[span$="-20"],u-grid>u-cell[span="21"]{--grid-ce: 21}u-grid>u-cell[span$="+22"],u-grid>u-cell[span$="-21"],u-grid>u-cell[span="22"]{--grid-ce: 22}u-grid>u-cell[span$="+23"],u-grid>u-cell[span$="-22"],u-grid>u-cell[span="23"]{--grid-ce: 23}u-grid>u-cell[span$="+24"],u-grid>u-cell[span$="-23"],u-grid>u-cell[span="24"]{--grid-ce: 24}u-grid>u-cell[span$="+25"],u-grid>u-cell[span$="-24"],u-grid>u-cell[span="25"]{--grid-ce: 25}u-grid>u-cell[span$="+26"],u-grid>u-cell[span$="-25"],u-grid>u-cell[span="26"]{--grid-ce: 26}u-grid>u-cell[span$="+27"],u-grid>u-cell[span$="-26"],u-grid>u-cell[span="27"]{--grid-ce: 27}u-grid>u-cell[span$="+28"],u-grid>u-cell[span$="-27"],u-grid>u-cell[span="28"]{--grid-ce: 28}u-grid>u-cell[span$="+29"],u-grid>u-cell[span$="-28"],u-grid>u-cell[span="29"]{--grid-ce: 29}u-grid>u-cell[span$="+30"],u-grid>u-cell[span$="-29"],u-grid>u-cell[span="30"]{--grid-ce: 30}u-grid>u-cell[span$="-30"]{--grid-ce: 31}u-grid>u-cell.h1,u-grid>u-cell.h2,u-grid>u-cell.h3,u-grid>u-cell.h4,u-grid>u-cell.h5,u-grid>u-cell.h6{margin-bottom:0}u-grid>u-cell>img,u-grid>u-cell>p>img{max-width:100%;object-fit:contain}u-grid[columns="1"]{--grid-tc: repeat(1, 1fr)}u-grid[columns="2"]{--grid-tc: repeat(2, 1fr)}u-grid[columns="3"]{--grid-tc: repeat(3, 1fr)}u-grid[columns="4"]{--grid-tc: repeat(4, 1fr)}u-grid[columns="5"]{--grid-tc: repeat(5, 1fr)}u-grid[columns="6"]{--grid-tc: repeat(6, 1fr)}u-grid[columns="7"]{--grid-tc: repeat(7, 1fr)}u-grid[columns="8"]{--grid-tc: repeat(8, 1fr)}u-grid[columns="9"]{--grid-tc: repeat(9, 1fr)}u-grid[columns="10"]{--grid-tc: repeat(10, 1fr)}u-grid[columns="11"]{--grid-tc: repeat(11, 1fr)}u-grid[columns="12"]{--grid-tc: repeat(12, 1fr)}u-grid[columns="13"]{--grid-tc: repeat(13, 1fr)}u-grid[columns="14"]{--grid-tc: repeat(14, 1fr)}u-grid[columns="15"]{--grid-tc: repeat(15, 1fr)}u-grid[columns="16"]{--grid-tc: repeat(16, 1fr)}u-grid[columns="17"]{--grid-tc: repeat(17, 1fr)}u-grid[columns="18"]{--grid-tc: repeat(18, 1fr)}u-grid[columns="19"]{--grid-tc: repeat(19, 1fr)}u-grid[columns="20"]{--grid-tc: repeat(20, 1fr)}u-grid[columns="21"]{--grid-tc: repeat(21, 1fr)}u-grid[columns="22"]{--grid-tc: repeat(22, 1fr)}u-grid[columns="23"]{--grid-tc: repeat(23, 1fr)}u-grid[columns="24"]{--grid-tc: repeat(24, 1fr)}u-grid[columns="25"]{--grid-tc: repeat(25, 1fr)}u-grid[columns="26"]{--grid-tc: repeat(26, 1fr)}u-grid[columns="27"]{--grid-tc: repeat(27, 1fr)}u-grid[columns="28"]{--grid-tc: repeat(28, 1fr)}u-grid[columns="29"]{--grid-tc: repeat(29, 1fr)}u-grid[columns="30"]{--grid-tc: repeat(30, 1fr)}[flow-cols]{column-fill:balance;column-gap:var(--column-gap)}[flow-cols="1"]{column-count:1}[flow-cols="2"]{column-count:2}[flow-cols="3"]{column-count:3}[flow-cols="4"]{column-count:4}[flow-cols="5"]{column-count:5}[flow-cols="6"]{column-count:6}[flow-cols="7"]{column-count:7}[flow-cols="8"]{column-count:8}fieldset{position:relative}fieldset button{margin-top:1rem;padding:.5rem calc(.75rem + 2px);border:none;display:block;text-transform:lowercase}fieldset button:not(:disabled){background-color:var(--uchu-gray-9);color:var(--uchu-gray-2);cursor:pointer}fieldset button:disabled{background-color:var(--uchu-gray-2);color:var(--uchu-gray-6);cursor:not-allowed}fieldset label{color:var(--uchu-gray-5);font-size:80%;position:absolute;padding-top:4px;padding-left:calc(.75rem + 2px);text-transform:lowercase}fieldset label+input,fieldset label+select{background-color:var(--uchu-gray-2);border:2px solid;transition:border-color .2s;width:400px}@media(prefers-reduced-motion: reduce){fieldset label+input,fieldset label+select{transition:none}}fieldset label+input::placeholder,fieldset label+select::placeholder{color:var(--uchu-gray-5);opacity:1}fieldset label+input:not(:focus),fieldset label+select:not(:focus){border-color:var(--uchu-gray-2)}fieldset label+input:focus,fieldset label+select:focus{border-color:var(--uchu-blue-4);box-shadow:none;outline:none}fieldset label+select{background-image:url("/media/svg/caret-down.svg");background-position:right 1rem bottom .7rem;background-repeat:no-repeat;background-size:1rem;padding:1.5rem .75rem .5rem .5rem}fieldset label+input{padding:1.5rem .75rem .5rem .75rem}fieldset label+input:invalid{border-color:var(--uchu-red-5);box-shadow:none;outline:none}/*# sourceMappingURL=uchu.css.map */
diff --git a/static/uchu.css.map b/static/uchu.css.map
new file mode 100644
index 0000000..9aadd3f
--- /dev/null
+++ b/static/uchu.css.map
@@ -0,0 +1 @@
+{"version":3,"sourceRoot":"","sources":["../sass/uchu/core/_root.scss","../sass/uchu/_reset.scss","../sass/uchu/_base.scss","../sass/uchu/_form.scss"],"names":[],"mappings":"CAAA,MACE,uCACA,wDACA,0DACA,yCACA,2CACA,uBACA,2CACA,mBACA,kBACA,kBACA,mBACA,kBACA,qDACA,kBACA,kBACA,kBACA,kBACA,oBACA,4CACA,aACA,8BAEA,+FAvBF,MAwBI,mBACA,gBAGF,+FA5BF,MA6BI,mBACA,iBAGF,+FAjCF,MAkCI,mBACA,iBCnCJ,EACE,sBACA,aACA,oBAEA,wDAEA,KACE,8BACA,gBACA,wCACA,2CAGF,0CAEA,uCAhBF,EAiBI,iBAIJ,4aACE,mBAEA,SACA,wBAGF,6HACE,oCACA,0CAGF,EACE,cACA,eACA,yDACA,mBACA,mBACA,iEAEA,QACE,yBAIJ,SACE,gBAEA,mDACE,oBAIJ,aACE,YAEA,wDAEE,aAIJ,OACE,mBAEA,gBACA,+BACA,YAGF,KACE,yCACA,8BACA,6DACA,0DACA,2BACA,mDACA,wBACA,+BAGF,KACE,kBACA,mCACA,0CACA,uBAEA,0CANF,KAOI,2BACA,2CAIJ,8BACA,gCAEA,GACE,yCACA,YACA,2BACA,4DACA,+DACA,YAEA,qBACE,8BACA,6DAGF,4BAEE,4DAGF,eACE,wCACA,+DAGF,cACE,2CAGF,cACE,+DAMJ,KACE,kBAGF,YAGE,yDACA,kCACA,gBACA,qBACA,qBAEA,mDACE,gBAIJ,IACE,cACA,gBACA,gBACA,iBAEA,gBAEE,gBACA,iBAMJ,GACE,yBACA,gBACA,wBACA,uCACA,sCACA,iCACA,qCACA,sBAEA,eACE,8BACA,wCACA,2CAEA,2BACE,aACA,2CAIJ,SACE,gBACA,gBACA,uCAGF,UACE,kBACA,gBACA,uCAGF,WACE,kBACA,gBACA,uCAGF,YACE,mBACA,gBACA,uCAIJ,GACE,yBACA,gBACA,wBACA,uCACA,iCACA,qCAEA,eACE,8BACA,gBACA,wCACA,2CACA,4CAIJ,MACE,yBACA,gBACA,wBACA,0CACA,8BACA,sCACA,yCAEA,8BACE,gBACA,0CAGF,gDAEE,mCAGF,gMAIE,8BAGF,gDAEE,2BAIJ,GACE,yBACA,gBACA,yBAGF,MACE,gBACA,yBACA,gBAGF,8BAME,qBAEA,kEACE,cACA,yDAIJ,IACE,cACA,2BACA,8BAEA,+BAEE,cACA,2BACA,8BAGF,wBAEA,mBAEE,iBAGF,8BAGE,kBAGF,yCACA,yCACA,mCAEA,6CAGE,sBAGF,4CACA,4CACA,sCAEA,gDAGE,oBAGF,8CACA,8CACA,wCAGF,aAEE,gBACA,YACA,wBAEA,qBACA,wBAEA,yCACE,qCAIJ,MAEE,4BACA,yBACA,4CAIA,gBACE,yBACA,gBACA,qCAEA,mBACE,6BACA,kBAEA,2BACE,gBACA,sDACA,6CAEA,iCACA,kCACA,gBACA,4CACA,kBACA,gBAKN,uDAGF,uBAEA,GACE,8BACA,kBAEA,gBAEE,oCAGF,kBACE,qBAEA,uCACE,oCAEA,gBACA,gBACA,YACA,qBACA,gBACA,mBACA,oBACA,UACA,kBAEA,2OACA,uPAEA,6FAEE,oCAEA,wBACA,4BACA,wBACA,oBACA,YACA,cACA,kBACA,aAEA,6DAbF,6FAcI,4BACA,sBACA,mBAIJ,8CACE,wCAEA,6DAHF,8CAII,yCACA,mCAIJ,wDACE,sCAEA,6DAHF,wDAII,yCACA,iCAOV,MACE,uBACA,2GACA,oBAEA,yBACA,2DACA,iBACA,yDACA,cACA,kBACA,+DACA,2CAEA,QACE,sBAGF,kBACE,gEACA,0CAGF,kBAEE,4IACA,2BACA,4BACA,0BACA,6BACA,kBAEA,gCACE,wBAEA,qCACA,oBACA,YACA,2BACA,oBACA,kBACA,UAIJ,SACE,gBACA,gBAEA,yCACA,uCAIJ,QACE,iEAKF,0CACE,mBACE,cAIJ,0CACE,mBACE,cAKF,kCAEE,gDAIJ,OACE,iBACA,+BAGF,QACE,gBACA,yCACA,yCAGF,SACE,iBACA,wCACA,wCAGF,UACE,gBACA,wCACA,4CAGF,gBACE,mVAUA,2BACA,2BACA,qCAGF,aACE,gBACA,uBACA,mBAGF,qBACA,6BAEA,gBACE,aAGF,QACE,mBACA,oCAGF,QACE,sBACA,iCAGF,0BACA,sBACA,wBAEA,kBACA,2CACA,2CACA,2CACA,2CACA,2CAEA,oBACA,6CACA,6CACA,6CACA,6CACA,6CAEA,sCACA,sCACA,sCACA,sCACA,sCACA,sCACA,sCACA,sCACA,sCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,mBAEA,uCACA,uCACA,uCACA,uCACA,uCACA,uCACA,uCACA,uCACA,uCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCACA,yCAEA,oBACA,qBACA,qBACA,qBACA,qBACA,qBACA,qBACA,qBACA,qBACA,qBACA,qBCjsBA,OACE,0BACA,cACA,aACA,aACA,kCACA,4BACA,qCAEA,8BAGE,eACE,iCACA,uEAGF,8BACE,kCAGF,8BACE,iCAGF,8BACE,iCAGF,8BACE,kCAGF,8BACE,iCAGF,8BACE,iCAIJ,2BAEA,uDAEE,0DACA,gBAGF,cACE,gBACA,cAEA,wDACA,6GACA,kFACA,kEAEA,sCACA,sCACA,sCACA,sCACA,sCACA,sCACA,sCACA,sCACA,sCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCACA,wCAEA,+DACA,yFACA,yFACA,yFACA,yFACA,yFACA,yFACA,yFACA,yFACA,4FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,6FACA,yCAEA,sHAEA,sCAEE,eACA,mBAIJ,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,8CACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDACA,gDAGF,YACE,oBACA,6BAGF,+BACA,+BACA,+BACA,+BACA,+BACA,+BACA,+BACA,+BCjLA,SACE,kBAEA,gBACE,iDAEA,YACA,cACA,yBAEA,+BACE,oCACA,yBACA,eAGF,yBACE,oCACA,yBACA,mBAIJ,eACE,yBACA,cACA,kBACA,gBACA,gCACA,yBAEA,2CAEE,oCACA,iBACA,4BACA,YAEA,uCAPF,2CAQI,iBAGF,qEACE,yBACA,UAGF,mEACE,gCAGF,uDACE,gCACA,gBACA,aAIJ,sBACE,kDACA,4CACA,4BACA,qBACA,kCAGF,qBACE,mCAEA,6BACE,+BACA,gBACA","file":"uchu.css"} \ No newline at end of file
diff --git a/svelte.config.js b/svelte.config.js
new file mode 100644
index 0000000..241c987
--- /dev/null
+++ b/svelte.config.js
@@ -0,0 +1,22 @@
+
+
+
+//// import
+
+import adapter from "svelte-adapter-bun";
+import preprocess from "svelte-preprocess";
+
+
+
+//// export
+
+/** @type {import("@sveltejs/kit").Config} */
+export default {
+ kit: {
+ adapter: adapter(),
+ prerender: {
+ handleHttpError: "warn"
+ }
+ },
+ preprocess: [preprocess()]
+};
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..7ff9085
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,15 @@
+{
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "moduleResolution": "bundler",
+ "noImplicitAny": false,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true
+ },
+ "extends": "./.svelte-kit/tsconfig.json"
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..d2c0d07
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,26 @@
+
+
+
+//// import
+
+import { sveltekit } from "@sveltejs/kit/vite";
+import type { UserConfig } from "vite";
+
+
+
+//// export
+
+export default {
+ json: {
+ namedExports: true
+ },
+ plugins: [sveltekit()],
+ server: {
+ fs: {
+ allow: [
+ "package.json",
+ "sass"
+ ]
+ }
+ }
+} satisfies UserConfig;