diff --git a/.gitattributes b/.gitattributes
index 0644d84..bea3f3f 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,4 +1,5 @@
*.png filter=lfs diff=lfs merge=lfs -text
*.gif filter=lfs diff=lfs merge=lfs -text
*.webp filter=lfs diff=lfs merge=lfs -text
+*.avif filter=lfs diff=lfs merge=lfs -text
*.kra filter=lfs diff=lfs merge=lfs -text
diff --git a/Makefile b/Makefile
index b05d476..d993c19 100644
--- a/Makefile
+++ b/Makefile
@@ -1,19 +1,20 @@
-CSS = $(wildcard style/*.css) $(shell find fonts -type f)
-PAGES = index.html pubkey.txt
+CSS = $(wildcard style/*.css) $(shell find fonts -type f) \
+ rainbow-quox/style.css
+PAGES = index.html pubkey.txt rainbow-quox/index.html
MEDIA = \
$(wildcard media/*.png) $(wildcard media/*.gif) $(wildcard media/*.webp) \
$(wildcard media/flags/*) $(wildcard media/buttons/*) \
- $(wildcard media/icons/*) $(wildcard media/bg/*) 8831.png 8831-quox.png
-SCRIPTS = $(patsubst %.ts,%.js,$(wildcard script/*.ts))
+ $(wildcard media/icons/*) $(wildcard media/bg/*) 8831.png 8831-quox.png \
+ $(wildcard rainbow-quox/front/*) $(wildcard rainbow-quox/back/*)
+SCRIPTS = $(patsubst %.ts,%.js,$(wildcard script/*.ts)) rainbow-quox/colour.js
MISC = $(shell find .well-known -type f)
ALL = $(CSS) $(PAGES) $(MEDIA) $(SCRIPTS) $(MISC)
-BUILDDIR ?= _build
+BUILDDIR ?= /srv/www/yummy
TMPDIR ?= _tmp
HOST ?= yummy.cricket
REMOTE_USER ?= www-data
-IDFILE ?= ~/.ssh/xyz
REMOTE_DIR ?= yummy
all: build
@@ -24,7 +25,7 @@ upload: build
@rsync --recursive --partial --progress --copy-links \
--compress --human-readable --hard-links --size-only \
--delete --delete-after \
- --rsh='ssh -l $(REMOTE_USER) -i $(IDFILE)' \
+ --rsh='ssh -l $(REMOTE_USER)' \
$(BUILDDIR)/ $(HOST):$(REMOTE_DIR)/
$(BUILDDIR)/%: %
diff --git a/index.html b/index.html
index e3b5ea8..713c8dd 100644
--- a/index.html
+++ b/index.html
@@ -429,6 +429,11 @@
+
+
+
+
+
diff --git a/media/buttons/blau.png b/media/buttons/blau.png
new file mode 100644
index 0000000..0b9e0cb
--- /dev/null
+++ b/media/buttons/blau.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e18ee746676a2e3b31780c1603e0faecfac2be1b7a662ab9144a3dfb1e6b1079
+size 3011
diff --git a/media/buttons/clip.png b/media/buttons/clip.png
new file mode 100644
index 0000000..c4d9443
--- /dev/null
+++ b/media/buttons/clip.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fa4a658acc9f0d9f2557bace361c9bc41acdf06eb2d8f4fda45365a658c9e8d3
+size 2098
diff --git a/media/buttons/gabu.png b/media/buttons/gabu.png
new file mode 100644
index 0000000..9daf840
--- /dev/null
+++ b/media/buttons/gabu.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:009b7fc831c0ff7ad57fe71979a0c45be3a4ccd0560e41d3fddc465a8c586ced
+size 835
diff --git a/media/buttons/hell-labs-still.png b/media/buttons/hell-labs-still.png
new file mode 100644
index 0000000..a23a3f2
--- /dev/null
+++ b/media/buttons/hell-labs-still.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b23dbc5031f29fb0cb383eb3acda6e0dce805b34a570506a79cae333fa39b05d
+size 2394
diff --git a/media/buttons/hell-labs.gif b/media/buttons/hell-labs.gif
new file mode 100644
index 0000000..8692d9c
--- /dev/null
+++ b/media/buttons/hell-labs.gif
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5e5947ba3c94e3c584e12044a3b6326228aa5ec6fd350f8bdfec3c48d09a6e7c
+size 12092
diff --git a/media/buttons/katja.png b/media/buttons/katja.png
new file mode 100644
index 0000000..5f470b3
--- /dev/null
+++ b/media/buttons/katja.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d17d6314c864556208031d7d1103fab509cc894b195db98a82685261c96c8299
+size 4027
diff --git a/media/buttons/lifning.png b/media/buttons/lifning.png
new file mode 100644
index 0000000..807b50e
--- /dev/null
+++ b/media/buttons/lifning.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:4260ff94fd4a16073c76b1d76ef041de85802f166872d24e4d8c723555a9b951
+size 1528
diff --git a/media/buttons/mathie.png b/media/buttons/mathie.png
new file mode 100644
index 0000000..5e5f6a1
--- /dev/null
+++ b/media/buttons/mathie.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c3a76a9d2333fa62c75da7444c0c14218a1f58a2689efa0b2d2676c7ecf6b4ee
+size 10046
diff --git a/media/buttons/odoben.png b/media/buttons/odoben.png
new file mode 100644
index 0000000..5cc1928
--- /dev/null
+++ b/media/buttons/odoben.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:200c2ced55ce39d5d395fdec14053938fc61c1a8679d18298a699a1c9538e3f5
+size 1209
diff --git a/media/buttons/therra.png b/media/buttons/therra.png
new file mode 100644
index 0000000..a7d19aa
--- /dev/null
+++ b/media/buttons/therra.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6f4471f97ace4b2365e6cfc02cafde7e666d31a8fbcf049441bfc008e4f8ac07
+size 882
diff --git a/rainbow-quox/back/belly1.webp b/rainbow-quox/back/belly1.webp
new file mode 100644
index 0000000..d1db413
--- /dev/null
+++ b/rainbow-quox/back/belly1.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:7bffff72e5f1148e7f998dce53a6ec61cd5bd495f43000c5aa8d4d07c2e50fa3
+size 30778
diff --git a/rainbow-quox/back/belly2.webp b/rainbow-quox/back/belly2.webp
new file mode 100644
index 0000000..789349f
--- /dev/null
+++ b/rainbow-quox/back/belly2.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2a3020a8e4d0720721ba65601c1b9f7aa38ba2c7efd5d37d33fec38ede68ef5f
+size 25104
diff --git a/rainbow-quox/back/claws.webp b/rainbow-quox/back/claws.webp
new file mode 100644
index 0000000..6f1187d
--- /dev/null
+++ b/rainbow-quox/back/claws.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:85675981f3a2590bc8ed6c7d4a9819dd7ec3dad64c7b6a9d762ef7818137f3a7
+size 21514
diff --git a/rainbow-quox/back/cuffs.webp b/rainbow-quox/back/cuffs.webp
new file mode 100644
index 0000000..357c395
--- /dev/null
+++ b/rainbow-quox/back/cuffs.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f9f4b1dd9ec4a0ac0b68d58fbeab77155674f38795c7bdf7c5a1a439dda6d8c3
+size 16610
diff --git a/rainbow-quox/back/eyes.webp b/rainbow-quox/back/eyes.webp
new file mode 100644
index 0000000..7715872
--- /dev/null
+++ b/rainbow-quox/back/eyes.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:53f1cb3f3273904b0cfcff7be1c04aac7f4291e783b6b5b872704bce5402adc1
+size 6724
diff --git a/rainbow-quox/back/fins1.webp b/rainbow-quox/back/fins1.webp
new file mode 100644
index 0000000..f5b4a55
--- /dev/null
+++ b/rainbow-quox/back/fins1.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:dd447feb938240b6f3aaf5c719275ccc085426fbce6da555676eb12f382d386e
+size 20576
diff --git a/rainbow-quox/back/fins2.webp b/rainbow-quox/back/fins2.webp
new file mode 100644
index 0000000..56da277
--- /dev/null
+++ b/rainbow-quox/back/fins2.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:be2a469f0b785afc48d5723fe43d924342c5dc956aafc9b43848e59b4516da5c
+size 21326
diff --git a/rainbow-quox/back/fins3.webp b/rainbow-quox/back/fins3.webp
new file mode 100644
index 0000000..26604d2
--- /dev/null
+++ b/rainbow-quox/back/fins3.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2dd85c26aa10c22ff34b0406fbe27f5a9694e64919d119e00c6e6efa5513eda5
+size 7922
diff --git a/rainbow-quox/back/lines.webp b/rainbow-quox/back/lines.webp
new file mode 100644
index 0000000..5a0851b
--- /dev/null
+++ b/rainbow-quox/back/lines.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa2bb28145d83875af044e62a787bd67e34b4d3f8db7dd4bd538726e83031bbf
+size 205184
diff --git a/rainbow-quox/back/masks.webp b/rainbow-quox/back/masks.webp
new file mode 100644
index 0000000..c2d0a41
--- /dev/null
+++ b/rainbow-quox/back/masks.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6942c6cd3182e027bf69c5a46c1f8dbbc49af4baf1f32a1f48ad57c55d143bd5
+size 11346
diff --git a/rainbow-quox/back/outer.webp b/rainbow-quox/back/outer.webp
new file mode 100644
index 0000000..23ad25f
--- /dev/null
+++ b/rainbow-quox/back/outer.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:51e7660d2bf26cd8c162abd872ecd07be7375b78917e5e6830651397cd9aef33
+size 54632
diff --git a/rainbow-quox/back/spines.webp b/rainbow-quox/back/spines.webp
new file mode 100644
index 0000000..f42ef83
--- /dev/null
+++ b/rainbow-quox/back/spines.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2958d56981dcf50d4c504ac695dcca1d87425837b41026371f36d5aa5ea1aebe
+size 18330
diff --git a/rainbow-quox/back/static.webp b/rainbow-quox/back/static.webp
new file mode 100644
index 0000000..8801a41
--- /dev/null
+++ b/rainbow-quox/back/static.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9628e09947c9a87901c80ba08465301a277b72a6e4d0e8b188c5e1a8d3d57c54
+size 36238
diff --git a/rainbow-quox/back/stripes.webp b/rainbow-quox/back/stripes.webp
new file mode 100644
index 0000000..3e71565
--- /dev/null
+++ b/rainbow-quox/back/stripes.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:79cd316e8329cc967147ab937a4169279338ef0b15d5b9ecfd3cd25f90997cae
+size 38228
diff --git a/rainbow-quox/back/vitiligo1.webp b/rainbow-quox/back/vitiligo1.webp
new file mode 100644
index 0000000..1194365
--- /dev/null
+++ b/rainbow-quox/back/vitiligo1.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0175ff974fa83dd08f0912e0a31dbfbaf7fd4ef3556338d4d25aad87f0f715b2
+size 30564
diff --git a/rainbow-quox/back/vitiligo2.webp b/rainbow-quox/back/vitiligo2.webp
new file mode 100644
index 0000000..ceafc0b
--- /dev/null
+++ b/rainbow-quox/back/vitiligo2.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:393a7da7efcd74d476f72620cba80102cbfd309e2e4a2eeb86081eccdcd329c6
+size 6668
diff --git a/rainbow-quox/back/vitiligo3.webp b/rainbow-quox/back/vitiligo3.webp
new file mode 100644
index 0000000..b3b1000
--- /dev/null
+++ b/rainbow-quox/back/vitiligo3.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:bfe6882054b792e44fa4216ec58aa3a5918ad8d526ad878b7f1438a4838ee22f
+size 6078
diff --git a/rainbow-quox/back/vitiligo4.webp b/rainbow-quox/back/vitiligo4.webp
new file mode 100644
index 0000000..bfbdb63
--- /dev/null
+++ b/rainbow-quox/back/vitiligo4.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:aa8ed23495929d2cf817377b653b3f68d80af0b0d6ae00704bb5ca8cc1a9bb6c
+size 14358
diff --git a/rainbow-quox/colour.ts b/rainbow-quox/colour.ts
new file mode 100644
index 0000000..6d489ce
--- /dev/null
+++ b/rainbow-quox/colour.ts
@@ -0,0 +1,290 @@
+type Rand = () => number;
+
+
+/*
+let randomData = new Uint32Array(100);
+let next: number = 0;
+function reset() { self.crypto.getRandomValues(randomData); next = 0; }
+function rand() {
+ if (next >= 100) { reset(); }
+ return randomData[next++] / 4_294_967_295; // u32 max
+}
+reset();
+*/
+
+const rand: Rand = Math.random; // [todo]
+
+const max = Math.max;
+const min = Math.min;
+
+type Oklch = { l: number, c: number, h: number };
+
+type LD = 'light' | 'dark';
+
+const MAXL = 0.9;
+const MINL = 0.4;
+const MINL_LIGHT = 0.7;
+const MAXL_DARK = 0.65;
+
+const MINC_LIGHT = 0.08;
+const MAXC_LIGHT = 0.125;
+const MINC_DARK = 0.12;
+const MAXC_DARK = 0.2;
+
+// max spread for a sequence of analogous colours. unless that would put them
+// too close together
+const MAXH_WIDTH = 80;
+
+// minimum distance between adjacent analogous colours
+const MINH_SEP = 5;
+
+// how far away from 180° a "complementary" colour can be
+const MAXH_COMPL = 40;
+
+// how far away from 120° a "triad" colour can be
+const MAXH_TRIAD = 20;
+
+function randBetween(x: number, y: number): number {
+ const lo = min(x, y), hi = max(x, y);
+ return lo + rand() * (hi - lo);
+}
+
+function oneOf(...xs: A[]): A {
+ return xs[Math.floor(rand() * xs.length)]!;
+}
+
+
+function isLight(l: number): boolean { return l >= MINL_LIGHT; }
+
+function baseLuma(ld?: LD): number {
+ if (ld == 'light') {
+ return randBetween(MINL_LIGHT, MAXL);
+ } else if (ld == 'dark') {
+ return randBetween(MINL, MAXL_DARK);
+ } else {
+ return randBetween(MINL, MAXL);
+ }
+}
+
+function baseChroma(l: number): number {
+ if (l >= MINL_LIGHT) {
+ return randBetween(MINC_LIGHT, MAXC_LIGHT);
+ } else {
+ return randBetween(MINC_DARK, MAXC_DARK);
+ }
+}
+
+function baseHue(): number { return rand() * 360; }
+
+function baseOklch(ld?: LD): Oklch {
+ const l = baseLuma(ld);
+ return {l, c: baseChroma(l), h: baseHue()};
+}
+
+function lightFor(baseL: number): number {
+ return randBetween(baseL, MAXL);
+}
+function darkFor(baseL: number): number {
+ return randBetween(MINL, baseL);
+}
+
+function brightFor(l: number, baseC: number): number {
+ if (isLight(l)) { return randBetween(baseC, MAXC_LIGHT); }
+ else { return randBetween(baseC, MAXC_DARK); }
+}
+
+function dullFor(l: number, baseC: number): number {
+ if (isLight(l)) { return randBetween(baseC, MINC_LIGHT); }
+ else { return randBetween(baseC, MINC_DARK); }
+}
+
+
+type Numbers = number[];
+
+function upto(end: number): Numbers {
+ function* go(next: number): Iterable {
+ if (next < end) { yield next; yield* go(next+1) }
+ }
+ return Array.from(go(0));
+}
+
+function analogous(baseH: number, count: number): Numbers {
+ const minWidth = count * MINH_SEP;
+ const width =
+ MAXH_WIDTH < minWidth ? minWidth : randBetween(minWidth, MAXH_WIDTH);
+ const sep = width / (count - 1);
+ const start = baseH - (width / 2);
+ let numbers = Array.from(upto(count).map(i => start + i * sep));
+ return rand() > 0.5 ? numbers : numbers.reverse();
+}
+
+function complementary(baseH: number, count: number): Numbers {
+ const angle = randBetween(180 - MAXH_COMPL, 180 + MAXH_COMPL);
+ return analogous(baseH + angle, count);
+}
+
+function triad(baseH: number): [number, number] {
+ const angle = randBetween(120 - MAXH_TRIAD, 120 + MAXH_TRIAD);
+ return [baseH - angle, baseH + angle];
+}
+
+type Layer =
+ 'outer' | 'spines' | 'vitiligo1' |
+ 'stripes' | 'cuffs' |
+ 'fins1' | 'fins2' | 'fins3' | 'vitiligo4' |
+ 'belly1' | 'vitiligo3' | 'belly2' | 'vitiligo2' |
+ 'eyes' | 'masks' | 'claws' | 'lines';
+
+type Colours = { [l in Layer]: Oklch };
+
+function colours(): Colours {
+ let cols: Partial = {};
+
+ cols.outer = baseOklch('dark'); // [todo]
+ cols.spines = mkSpines(cols.outer);
+ cols.vitiligo1 = mkVitiligo(cols.outer);
+
+ cols.stripes = mkStripes();
+ cols.cuffs = mkCuffs(cols.stripes);
+
+ // fins, belly
+ const whichBody = rand();
+ if (whichBody > 0.85) {
+ // triad
+ const hs = triad(cols.outer.h);
+ fins(hs[0]); belly(hs[1]);
+ } else if (whichBody > 0.4) {
+ // fins like belly
+ const [f, b] = complementary(cols.outer.h, 2);
+ fins(f!); belly(b!);
+ } else {
+ // fins like outer
+ fins(analogous(cols.outer.h, 3)[2]!);
+ belly(complementary(cols.outer.h, 3)[2]!);
+ }
+
+ cols.eyes = {
+ l: baseLuma('light'),
+ c: randBetween(0.28, MAXC_LIGHT),
+ h: oneOf(analogous, complementary)(cols.outer.h, 3)[2]!
+ };
+
+ cols.masks = {
+ l: randBetween(0.8, MAXL),
+ c: randBetween(0.01, 0.06),
+ h: analogous(oneOf(cols.outer, cols.belly1, cols.fins1)!.h, 3)[2]!
+ };
+ cols.claws = {
+ l: min(MAXL, cols.masks!.l + randBetween(0.1, -0.1)),
+ c: randBetween(0.01, 0.06),
+ h: analogous(cols.masks!.h, 3)[2]!
+ };
+
+ cols.lines = {
+ l: randBetween(0.01, 0.06),
+ c: baseChroma(0),
+ h: analogous(cols.outer!.h, 3)[2]!
+ }
+
+ return cols as Colours;
+
+
+ function fins(h: number) {
+ const [fin1Hue, fin2Hue, fin3Hue] = analogous(h, 3);
+ const d = direction();
+ cols.fins1 = doDirection(cols.outer!, fin1Hue!, d);
+ cols.fins2 = doDirection(cols.fins1, fin2Hue!, d);
+ cols.fins3 = doDirection(cols.fins2, fin3Hue!, d);
+ cols.vitiligo4 = mkVitiligo(cols.fins1);
+ }
+
+ function belly(h: number) {
+ const [belly1Hue, belly2Hue] = analogous(h, 2);
+ cols.belly1 = {
+ l: randBetween(0.7, MAXL),
+ c: baseChroma(1),
+ h: belly1Hue!
+ };
+ cols.belly2 = {
+ l: min(MAXL, cols.belly1!.l * 1.1),
+ c: cols.belly1!.c * 0.9,
+ h: belly2Hue!
+ };
+ cols.vitiligo3 = mkVitiligo(cols.belly1); // oops sorry
+ cols.vitiligo2 = mkVitiligo(cols.belly2);
+ }
+
+ type LFun = (l: number) => number;
+ type CFun = (l: number, c: number) => number;
+ function direction(): [LFun, CFun] {
+ return oneOf([lightFor, dullFor], [darkFor, brightFor]);
+ }
+ function doDirection(col: Oklch, h: number, [ll, cc]: [LFun, CFun]) {
+ return { l: ll(col.l), c: cc(col.l, col.c), h };
+ }
+}
+
+function mkVitiligo(outer: Oklch): Oklch {
+ return {
+ l: randBetween(max(outer.l, 0.8), MAXL),
+ c: randBetween(min(outer.c, 0.1), MINC_LIGHT),
+ h: randBetween(outer.h + 20, outer.h - 20)
+ };
+}
+
+function mkSpines(outer: Oklch): Oklch {
+ return {
+ l: outer.l * 0.8, c: outer.c * 1.1,
+ h: randBetween(outer.h + 12, outer.h - 12)
+ };
+}
+
+function mkStripes(): Oklch {
+ return {
+ l: randBetween(0.8, MAXL),
+ c: randBetween(MINC_LIGHT, MAXC_LIGHT),
+ h: rand() * 360
+ };
+}
+
+function mkCuffs(sock: Oklch): Oklch {
+ return {
+ l: sock.l * 0.7,
+ c: randBetween(sock.c, MAXC_LIGHT),
+ h: randBetween(sock.h + 8, sock.h - 8)
+ };
+}
+
+
+function setColours(cols: Colours) {
+ for (const k in cols) {
+ const c = cols[k as keyof Colours];
+ for (const elem of Array.from(document.getElementsByClassName(k))) {
+ (elem as HTMLElement).style.background = `oklch(${c.l} ${c.c} ${c.h})`;
+ }
+ }
+ document.documentElement.style.setProperty('--hue', `${cols.outer.h}deg`);
+}
+
+
+document.addEventListener('DOMContentLoaded', function() {
+ const reroll = document.getElementById('reroll')!;
+ const swap = document.getElementById('swap')!;
+ const pic = document.getElementById('pic')!;
+
+ reroll.addEventListener('click', doReroll);
+ swap.addEventListener('click', doSwap);
+
+ doReroll();
+ setTimeout(setTransition);
+
+ function doReroll() { setColours(colours()); }
+ function doSwap() { pic.classList.toggle('back'); }
+ function setTransition() {
+ document.documentElement.style.setProperty('--transition',
+ 'background 0.4s ease-in-out, color 0.4s ease-in-out');
+ }
+});
+
+
+export {}
diff --git a/rainbow-quox/front/belly1.webp b/rainbow-quox/front/belly1.webp
new file mode 100644
index 0000000..a30d9e1
--- /dev/null
+++ b/rainbow-quox/front/belly1.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2f5515bcd8865df62a88f0dc246282a22aa6434fe82dc62a026a6530ede05b2f
+size 33482
diff --git a/rainbow-quox/front/belly2.webp b/rainbow-quox/front/belly2.webp
new file mode 100644
index 0000000..0c14054
--- /dev/null
+++ b/rainbow-quox/front/belly2.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:643cfcceac30ccffb73b5038d88317c30c66c931f25ff8db3aaed9a3f47f9bd9
+size 34988
diff --git a/rainbow-quox/front/claws.webp b/rainbow-quox/front/claws.webp
new file mode 100644
index 0000000..5a95d1a
--- /dev/null
+++ b/rainbow-quox/front/claws.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c4a568b8f63b1e4ab8e2ffd10fb430990af4c62d4ab9fc1452f9c94a7fc2d017
+size 18770
diff --git a/rainbow-quox/front/cuffs.webp b/rainbow-quox/front/cuffs.webp
new file mode 100644
index 0000000..b9df330
--- /dev/null
+++ b/rainbow-quox/front/cuffs.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e8bfd541943e0ba2a4b22e2cb388547a252570cf671d10f291e29d9f1201dc06
+size 19030
diff --git a/rainbow-quox/front/eyes.webp b/rainbow-quox/front/eyes.webp
new file mode 100644
index 0000000..a427132
--- /dev/null
+++ b/rainbow-quox/front/eyes.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:321341953d8afd71a7147019beac8852179454cd60d9e45b8d2d1d6924c70f23
+size 6874
diff --git a/rainbow-quox/front/fins1.webp b/rainbow-quox/front/fins1.webp
new file mode 100644
index 0000000..6375bb2
--- /dev/null
+++ b/rainbow-quox/front/fins1.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:39bdaee56915a8aed70c2cb772a3d389ce16a61e64540e34890d25c933aab8cd
+size 14174
diff --git a/rainbow-quox/front/fins2.webp b/rainbow-quox/front/fins2.webp
new file mode 100644
index 0000000..907f261
--- /dev/null
+++ b/rainbow-quox/front/fins2.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:353fadde80ca1443681df10725c6dddf4670fec0671f79e117170dd84cd30a71
+size 13806
diff --git a/rainbow-quox/front/fins3.webp b/rainbow-quox/front/fins3.webp
new file mode 100644
index 0000000..f01fe5d
--- /dev/null
+++ b/rainbow-quox/front/fins3.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3049390a7a2f844a24f08ebc3f6c29fb85193358a0237ab88608a827b3b35631
+size 5726
diff --git a/rainbow-quox/front/lines.webp b/rainbow-quox/front/lines.webp
new file mode 100644
index 0000000..0b1a447
--- /dev/null
+++ b/rainbow-quox/front/lines.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:5ae65212ecd5b68e9e03a5e5d9bcb3bc36492d311f0e86038469683fba94d285
+size 220226
diff --git a/rainbow-quox/front/masks.webp b/rainbow-quox/front/masks.webp
new file mode 100644
index 0000000..afacfdd
--- /dev/null
+++ b/rainbow-quox/front/masks.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:07707af55f8e41ab0156ecdc490f3ae7faf2ecb369df54a8fd696325594932ad
+size 11180
diff --git a/rainbow-quox/front/outer.webp b/rainbow-quox/front/outer.webp
new file mode 100644
index 0000000..3ef1531
--- /dev/null
+++ b/rainbow-quox/front/outer.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6afb5783c828ce56e3f590a2015859f82e841411305b9eab5f2a5bc87f99fd83
+size 28322
diff --git a/rainbow-quox/front/spines.webp b/rainbow-quox/front/spines.webp
new file mode 100644
index 0000000..36f4910
--- /dev/null
+++ b/rainbow-quox/front/spines.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:213ca5194a9471eabd99e81d6438816dc91fe9fd037e59b8c7dd663cc0332151
+size 11778
diff --git a/rainbow-quox/front/static.webp b/rainbow-quox/front/static.webp
new file mode 100644
index 0000000..7da6697
--- /dev/null
+++ b/rainbow-quox/front/static.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:584d2500af71f04385c0c23e8ef07be759e28d1633e750944fccb7ec5910b25c
+size 60422
diff --git a/rainbow-quox/front/stripes.webp b/rainbow-quox/front/stripes.webp
new file mode 100644
index 0000000..12d8b9b
--- /dev/null
+++ b/rainbow-quox/front/stripes.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab005f96e30bc1c4616df9fb13b3e942e3c665f1ff97e10b3c46b2d158e6e2c9
+size 52846
diff --git a/rainbow-quox/front/vitiligo1.webp b/rainbow-quox/front/vitiligo1.webp
new file mode 100644
index 0000000..a7481af
--- /dev/null
+++ b/rainbow-quox/front/vitiligo1.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:26b0af713721f8f8d324af2d5a4ce73b02614e57c36a33d7c11ac1f02cff95d4
+size 17448
diff --git a/rainbow-quox/front/vitiligo2.webp b/rainbow-quox/front/vitiligo2.webp
new file mode 100644
index 0000000..6e70fd4
--- /dev/null
+++ b/rainbow-quox/front/vitiligo2.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:39072c20f657bb4c395e35f82e3e107c49f0a2a1e6de0cb1262e5b462fc9f59c
+size 5342
diff --git a/rainbow-quox/front/vitiligo3.webp b/rainbow-quox/front/vitiligo3.webp
new file mode 100644
index 0000000..5bbf21b
--- /dev/null
+++ b/rainbow-quox/front/vitiligo3.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:af3434048fae9d2eac0c3d76342a32357020e729a020ae38a7ba3bcf1acaa7f2
+size 3098
diff --git a/rainbow-quox/front/vitiligo4.webp b/rainbow-quox/front/vitiligo4.webp
new file mode 100644
index 0000000..0fafd49
--- /dev/null
+++ b/rainbow-quox/front/vitiligo4.webp
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:771f1ad3217fe3530fb7560220b0ac4a9eda375b00a1909ea9ac27a41d045b19
+size 4974
diff --git a/rainbow-quox/index.html b/rainbow-quox/index.html
new file mode 100644
index 0000000..c6b08b2
--- /dev/null
+++ b/rainbow-quox/index.html
@@ -0,0 +1,63 @@
+
+
+rainbow quox
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/rainbow-quox/kra/back.kra b/rainbow-quox/kra/back.kra
new file mode 100644
index 0000000..54e67ce
--- /dev/null
+++ b/rainbow-quox/kra/back.kra
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:462a140e5301d5ed8cff14ec5b019ddad5207f5e4aaadd7de637fa3a4d7dcfbe
+size 10079930
diff --git a/rainbow-quox/kra/compl1.png b/rainbow-quox/kra/compl1.png
new file mode 100644
index 0000000..23687ea
--- /dev/null
+++ b/rainbow-quox/kra/compl1.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:52e46b628ed2026246eb7cc928bc026b7f269a91c9f980db6825353d6e688dee
+size 561799
diff --git a/rainbow-quox/kra/compl2.png b/rainbow-quox/kra/compl2.png
new file mode 100644
index 0000000..0fdf791
--- /dev/null
+++ b/rainbow-quox/kra/compl2.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0c6e5426f8d94cb92dd0ca9b9c553e50e4ee2976adb988339a5009aed73fd887
+size 560816
diff --git a/rainbow-quox/kra/front.kra b/rainbow-quox/kra/front.kra
new file mode 100644
index 0000000..7fd8554
--- /dev/null
+++ b/rainbow-quox/kra/front.kra
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:579ca5af9a79a8577f62013418f58c729c646cfda9f2fd2bed54688b59de2ddd
+size 11346742
diff --git a/rainbow-quox/kra/triad.png b/rainbow-quox/kra/triad.png
new file mode 100644
index 0000000..eb2300a
--- /dev/null
+++ b/rainbow-quox/kra/triad.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9b59f4c7198b8782bdc16dc9012a25af0979948630390df9b1865238c46e1a66
+size 554043
diff --git a/rainbow-quox/style.css b/rainbow-quox/style.css
new file mode 100644
index 0000000..e05ef37
--- /dev/null
+++ b/rainbow-quox/style.css
@@ -0,0 +1,164 @@
+@import url(/fonts/muller/muller.css);
+
+@property --transition {
+ syntax: "*";
+ inherits: true;
+ initial-value: none;
+}
+
+* {
+ box-sizing: border-box;
+ transition: var(--transition);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ * { transition: none; }
+}
+
+:root {
+ --hue: 300deg;
+ min-height: 100vh; display: flex;
+ align-items: center; justify-content: center;
+
+ --font: Muller, sans-serif;
+ font-family: var(--font);
+
+ color-scheme: light dark;
+ background:
+ light-dark(oklch(0.9 0.08 var(--hue)),
+ oklch(0.3 0.12 var(--hue)));
+
+ overflow-x: hidden;
+}
+
+#pic {
+ position: relative;
+ width: min(1000px, 75vw);
+ margin: auto;
+ aspect-ratio: 2000/1312;
+}
+#pic div {
+ position: absolute; inset: 0;
+ mask-size: contain; mask-repeat: no-repeat;
+}
+
+
+#front, #back {
+ transition: all 0.45s cubic-bezier(.23,-0.03,.68,1.11);
+}
+
+.back > #front { translate: 125vw; }
+:not(.back) > #back { translate: -125vw; }
+
+@media (prefers-reduced-motion: reduce) {
+ #front, #back { transition: none; }
+
+ .back > #front { translate: none; rotate: none; display: none; }
+ :not(.back) > #back { translate: none; rotate: none; display: none; }
+}
+
+
+#front .static { background: url(front/static.webp) 0 0 / contain; }
+#back .static { background: url(back/static.webp) 0 0 / contain; }
+
+.spines { background: oklch(30.77% 0.1306 298.19); }
+#front .spines { mask-image: url(front/spines.webp); }
+#back .spines { mask-image: url(back/spines.webp); }
+
+.stripes { background: oklch(88.2% 0.0613 209.07); }
+#front .stripes { mask-image: url(front/stripes.webp); }
+#back .stripes { mask-image: url(back/stripes.webp); }
+
+.cuffs { background: oklch(72.74% 0.1008 225.69); }
+#front .cuffs { mask-image: url(front/cuffs.webp); }
+#back .cuffs { mask-image: url(back/cuffs.webp); }
+
+.outer { background: oklch(38.37% 0.1437 306.27); }
+#front .outer { mask-image: url(front/outer.webp); }
+#back .outer { mask-image: url(back/outer.webp); }
+
+.fins1 { background: oklch(85.06% 0.0961 81.4); }
+#front .fins1 { mask-image: url(front/fins1.webp); }
+#back .fins1 { mask-image: url(back/fins1.webp); }
+
+.fins2 { background: oklch(79.59% 0.1208 73.56); }
+#front .fins2 { mask-image: url(front/fins2.webp); }
+#back .fins2 { mask-image: url(back/fins2.webp); }
+
+.fins3 { background: oklch(74.11% 0.1268 64.62); }
+#front .fins3 { mask-image: url(front/fins3.webp); }
+#back .fins3 { mask-image: url(back/fins3.webp); }
+
+.belly1 { background: oklch(87.26% 0.0699 80.94); }
+#front .belly1 { mask-image: url(front/belly1.webp); }
+#back .belly1 { mask-image: url(back/belly1.webp); }
+
+.belly2 { background: oklch(76.92% 0.0954 58.36); }
+#front .belly2 { mask-image: url(front/belly2.webp); }
+#back .belly2 { mask-image: url(back/belly2.webp); }
+
+.eyes { background: oklch(36.26% 0.1107 145.78); }
+#front .eyes { mask-image: url(front/eyes.webp); }
+#back .eyes { mask-image: url(back/eyes.webp); }
+
+.masks { background: oklch(77.1% 0.0243 317.12); }
+#front .masks { mask-image: url(front/masks.webp); }
+#back .masks { mask-image: url(back/masks.webp); }
+
+.claws { background: oklch(88.92% 0.0207 325.75); }
+#front .claws { mask-image: url(front/claws.webp); }
+#back .claws { mask-image: url(back/claws.webp); }
+
+.vitiligo1 { background: oklch(79.4% 0.0588 309.27); }
+#front .vitiligo1 { mask-image: url(front/vitiligo1.webp); }
+#back .vitiligo1 { mask-image: url(back/vitiligo1.webp); }
+
+.vitiligo2 { background: oklch(93.14% 0.0331 83.71); }
+#front .vitiligo2 { mask-image: url(front/vitiligo2.webp); }
+#back .vitiligo2 { mask-image: url(back/vitiligo2.webp); }
+
+.vitiligo3 { background: oklch(95.7% 0.0296 94.35); }
+#front .vitiligo3 { mask-image: url(front/vitiligo3.webp); }
+#back .vitiligo3 { mask-image: url(back/vitiligo3.webp); }
+
+.vitiligo4 { background: oklch(95.7% 0.0296 94.35); }
+#front .vitiligo4 { mask-image: url(front/vitiligo4.webp); }
+#back .vitiligo4 { mask-image: url(back/vitiligo4.webp); }
+
+.lines { background: oklch(21.96% 0.0661 296.36); }
+#front .lines { mask-image: url(front/lines.webp); }
+#back .lines { mask-image: url(back/lines.webp); }
+
+.eye-shine { mask: none; mix-blend-mode: luminosity; }
+#front .eye-shine { background: url(front/eyes.webp) 0 0 / contain; }
+#back .eye-shine { background: url(back/eyes.webp) 0 0 / contain; }
+
+
+#buttons {
+ margin-top: 3rem;
+ display: flex; gap: 20px;
+ z-index: -1;
+}
+
+button {
+ font: 700 25pt var(--font);
+ flex: 30%;
+ background: oklch(0.5 0.2 var(--hue));
+ color: oklch(0.95 0.075 calc(180deg + var(--hue)));
+ border: 3px solid oklch(0.2 0.05 var(--hue));
+ padding: 0.2em 0.5em;
+ filter: drop-shadow(0 0 10px oklch(0.4 0.2 var(--hue) / 0.45));
+}
+
+nav {
+ position: absolute;
+ bottom: 15px; left: 22px;
+}
+
+nav a {
+ color: light-dark(oklch(0.4 0.15 calc(180deg + var(--hue))),
+ oklch(0.9 0.19 calc(180deg + var(--hue))));
+ text-decoration: 3px solid underline;
+ text-decoration-color:
+ oklch(0.6 0.1 calc(180deg + var(--hue)));
+}