my page ollie.earth
0
fork

Configure Feed

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

basically redo the whole thing

ollie 1336ed0a 2233e068

+694 -46
+3
.envrc
··· 1 + if nix flake show &> /dev/null; then 2 + use flake 3 + fi
+7
.gitignore
··· 2 2 *.ez 3 3 /build 4 4 erl_crash.dump 5 + 6 + #Added automatically by Lustre Dev Tools 7 + /.lustre 8 + /dist 9 + 10 + # Ignore direnv cache 11 + .direnv/
+61
flake.lock
··· 1 + { 2 + "nodes": { 3 + "nixpkgs": { 4 + "locked": { 5 + "lastModified": 1770562336, 6 + "narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=", 7 + "owner": "NixOS", 8 + "repo": "nixpkgs", 9 + "rev": "d6c71932130818840fc8fe9509cf50be8c64634f", 10 + "type": "github" 11 + }, 12 + "original": { 13 + "owner": "NixOS", 14 + "ref": "nixos-unstable", 15 + "repo": "nixpkgs", 16 + "type": "github" 17 + } 18 + }, 19 + "root": { 20 + "inputs": { 21 + "nixpkgs": "nixpkgs", 22 + "utils": "utils" 23 + } 24 + }, 25 + "systems": { 26 + "locked": { 27 + "lastModified": 1681028828, 28 + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", 29 + "owner": "nix-systems", 30 + "repo": "default", 31 + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", 32 + "type": "github" 33 + }, 34 + "original": { 35 + "owner": "nix-systems", 36 + "repo": "default", 37 + "type": "github" 38 + } 39 + }, 40 + "utils": { 41 + "inputs": { 42 + "systems": "systems" 43 + }, 44 + "locked": { 45 + "lastModified": 1731533236, 46 + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 47 + "owner": "numtide", 48 + "repo": "flake-utils", 49 + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 50 + "type": "github" 51 + }, 52 + "original": { 53 + "owner": "numtide", 54 + "repo": "flake-utils", 55 + "type": "github" 56 + } 57 + } 58 + }, 59 + "root": "root", 60 + "version": 7 61 + }
+11 -5
flake.nix
··· 6 6 utils.url = "github:numtide/flake-utils"; 7 7 }; 8 8 9 - outputs = { self, nixpkgs, utils }: 10 - utils.lib.eachDefaultSystem (system: 9 + outputs = 10 + { 11 + self, 12 + nixpkgs, 13 + utils, 14 + }: 15 + utils.lib.eachDefaultSystem ( 16 + system: 11 17 let 12 18 pkgs = import nixpkgs { inherit system; }; 13 19 in ··· 17 23 gleam 18 24 erlang_28 19 25 rebar3 20 - bun 21 26 just 27 + inotify-tools 22 28 ]; 23 29 24 30 shellHook = '' ··· 27 33 echo "Use just to run them." 28 34 ''; 29 35 }; 30 - }); 36 + } 37 + ); 31 38 } 32 -
+4 -6
gleam.toml
··· 1 - name = "doodler" 1 + name = "page" 2 2 version = "1.0.0" 3 - target = "javascript" 3 + target = "erlang" 4 4 5 5 # Fill out these fields if you intend to generate HTML documentation or publish 6 6 # your project to the Hex package manager. ··· 13 13 # For a full reference of all the available options, you can have a look at 14 14 # https://gleam.run/writing-gleam/gleam-toml/. 15 15 16 - [javascript] 17 - runtime = "bun" 18 - 19 16 [dependencies] 20 17 gleam_stdlib = ">= 0.44.0 and < 2.0.0" 21 18 lustre = ">= 5.3.5 and < 6.0.0" 19 + blogatto = ">= 5.1.1 and < 6.0.0" 20 + glaze_oat = ">= 3.0.0 and < 4.0.0" 22 21 23 22 [dev-dependencies] 24 23 gleeunit = ">= 1.0.0 and < 2.0.0" 25 - lustre_dev_tools = ">= 2.1.3 and < 3.0.0"
+8 -20
justfile
··· 1 1 default: 2 2 @just --list 3 3 4 - # Run a dev server using lustre/dev start 5 - dev: 6 - gleam run -m lustre/dev start 7 4 8 - # Build the SPA 5 + # Build 9 6 build: 10 - gleam run -m lustre/dev build 11 - 7 + gleam run 12 8 13 - roll-back: 14 - # move copy from /srv/doodler/previous to /srv/doodler/ 15 - ssh deploy-doodler@gitlab "cp /srv/doodler/previous/* /srv/doodler/" 16 9 17 - # Deploy doodler to my server 10 + # Deploy to my server 18 11 deploy: build 19 - #!/bin/zsh 20 - ssh deploy-doodler@gitlab " 21 - # ignore errors since the following 2 steps would otherwise have issues 22 - set +e 23 - # create copy of previous version in /srv/doodler/previous 24 - cp /srv/doodler/* /srv/doodler/previous 25 - # remove previous version -- /srv/doodler/previous will be ignored 26 - rm /srv/doodler/* 12 + #!/usr/bin/env zsh 13 + ssh deploy-page@gitlab " 14 + rm -r /srv/page/* 27 15 " 28 16 29 - # copy current version to /srv/doodler/ 30 - scp ./dist/* deploy-doodler@gitlab:/srv/doodler 17 + # copy current version to /srv/page/ 18 + scp -r ./dist/* deploy-page@gitlab:/srv/page 31 19 32 20
+51
manifest.toml
··· 1 + # This file was generated by Gleam 2 + # You typically do not need to edit this file 3 + 4 + packages = [ 5 + { name = "blogatto", version = "5.1.1", build_tools = ["gleam"], requirements = ["filepath", "filespy", "frontmatter", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "gleam_time", "gtempo", "houdini", "lustre", "marceau", "maud", "mist", "mork", "simplifile", "smalto", "smalto_lustre", "str", "tzif", "webls", "zones"], otp_app = "blogatto", source = "hex", outer_checksum = "E4674B291DB402CBEA1E82C458EADBAAA27133A07ACA9BC8FA4395ED30D2933F" }, 6 + { name = "casefold", version = "2.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "casefold", source = "hex", outer_checksum = "F09530B6F771BB7B0BCACD3014089C20DFDA31775BA4793266C3814607C0A468" }, 7 + { name = "exception", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "exception", source = "hex", outer_checksum = "329D269D5C2A314F7364BD2711372B6F2C58FA6F39981572E5CA68624D291F8C" }, 8 + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, 9 + { name = "filespy", version = "0.7.0", build_tools = ["gleam"], requirements = ["fs", "gleam_erlang", "gleam_otp", "gleam_stdlib"], otp_app = "filespy", source = "hex", outer_checksum = "1136C7DB512B83DF544CDA4632A633412281B48489428391DB0356F66423D7C0" }, 10 + { name = "frontmatter", version = "1.0.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "frontmatter", source = "hex", outer_checksum = "D7FF83A8D22A52867809681EE254A3F989169F92539299958879FE47737DAB7A" }, 11 + { name = "fs", version = "11.4.1", build_tools = ["rebar3"], requirements = [], otp_app = "fs", source = "hex", outer_checksum = "DD00A61D89EAC01D16D3FC51D5B0EB5F0722EF8E3C1A3A547CD086957F3260A9" }, 12 + { name = "glaze_oat", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib", "lustre"], otp_app = "glaze_oat", source = "hex", outer_checksum = "B1373CA720EBB9D7FBDA4D2E28CAAD2B1928F6845313570AE72CA1D93B069706" }, 13 + { name = "gleam_community_ansi", version = "1.4.4", build_tools = ["gleam"], requirements = ["gleam_community_colour", "gleam_regexp", "gleam_stdlib"], otp_app = "gleam_community_ansi", source = "hex", outer_checksum = "1B3AEA6074AB34D5F0674744F36DDC7290303A03295507E2DEC61EDD6F5777FE" }, 14 + { name = "gleam_community_colour", version = "2.0.4", build_tools = ["gleam"], requirements = ["gleam_json", "gleam_stdlib"], otp_app = "gleam_community_colour", source = "hex", outer_checksum = "6DB4665555D7D2B27F0EA32EF47E8BEBC4303821765F9C73D483F38EE24894F0" }, 15 + { name = "gleam_crypto", version = "1.5.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_crypto", source = "hex", outer_checksum = "50774BAFFF1144E7872814C566C5D653D83A3EBF23ACC3156B757A1B6819086E" }, 16 + { name = "gleam_erlang", version = "1.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_erlang", source = "hex", outer_checksum = "1124AD3AA21143E5AF0FC5CF3D9529F6DB8CA03E43A55711B60B6B7B3874375C" }, 17 + { name = "gleam_http", version = "4.3.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_http", source = "hex", outer_checksum = "82EA6A717C842456188C190AFB372665EA56CE13D8559BF3B1DD9E40F619EE0C" }, 18 + { name = "gleam_json", version = "3.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_json", source = "hex", outer_checksum = "44FDAA8847BE8FC48CA7A1C089706BD54BADCC4C45B237A992EDDF9F2CDB2836" }, 19 + { name = "gleam_otp", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_stdlib"], otp_app = "gleam_otp", source = "hex", outer_checksum = "BA6A294E295E428EC1562DC1C11EA7530DCB981E8359134BEABC8493B7B2258E" }, 20 + { name = "gleam_regexp", version = "1.1.1", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_regexp", source = "hex", outer_checksum = "9C215C6CA84A5B35BB934A9B61A9A306EC743153BE2B0425A0D032E477B062A9" }, 21 + { name = "gleam_stdlib", version = "0.71.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "702F3BC2A14793906880B1078B19A6165F87323AEE8D0C4A34085846336FCAAE" }, 22 + { name = "gleam_time", version = "1.8.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleam_time", source = "hex", outer_checksum = "533D8723774D61AD4998324F5DD1DABDCDBFABAFB9E87CB5D03C6955448FC97D" }, 23 + { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, 24 + { name = "glisten", version = "9.0.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_otp", "gleam_stdlib", "logging"], otp_app = "glisten", source = "hex", outer_checksum = "D92808C66F7D3F22F2289CD04CBA8151757AAE9CB3D86992F0C6DE32A41205E1" }, 25 + { name = "gramps", version = "6.0.0", build_tools = ["gleam"], requirements = ["gleam_crypto", "gleam_erlang", "gleam_http", "gleam_stdlib"], otp_app = "gramps", source = "hex", outer_checksum = "8B7195978FBFD30B43DF791A8A272041B81E45D245314D7A41FC57237AA882A0" }, 26 + { name = "gtempo", version = "7.2.4", build_tools = ["gleam"], requirements = ["gleam_regexp", "gleam_stdlib", "gleam_time"], otp_app = "gtempo", source = "hex", outer_checksum = "997964753BB7E9D9EA7023B9DB401871410D17C8ED8AC897CB9F7BFC33B277ED" }, 27 + { name = "houdini", version = "1.2.0", build_tools = ["gleam"], requirements = [], otp_app = "houdini", source = "hex", outer_checksum = "5DB1053F1AF828049C2B206D4403C18970ABEF5C18671CA3C2D2ED0DD64F6385" }, 28 + { name = "hpack_erl", version = "0.3.0", build_tools = ["rebar3"], requirements = [], otp_app = "hpack", source = "hex", outer_checksum = "D6137D7079169D8C485C6962DFE261AF5B9EF60FBC557344511C1E65E3D95FB0" }, 29 + { name = "logging", version = "1.5.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "logging", source = "hex", outer_checksum = "BC5F18CE5DD9686100229FE5409BDC3DD5C46D5A7DF2F804AD2D8F0DD6C5060E" }, 30 + { name = "lustre", version = "5.6.0", build_tools = ["gleam"], requirements = ["gleam_erlang", "gleam_json", "gleam_otp", "gleam_stdlib", "houdini"], otp_app = "lustre", source = "hex", outer_checksum = "EE558CD4DB9F09FCC16417ADF0183A3C2DAC3E4B21ED3AC0CAE859792AB810CA" }, 31 + { name = "marceau", version = "1.3.0", build_tools = ["gleam"], requirements = [], otp_app = "marceau", source = "hex", outer_checksum = "2D1C27504BEF45005F5DFB18591F8610FB4BFA91744878210BDC464412EC44E9" }, 32 + { name = "maud", version = "1.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "lustre", "mork"], otp_app = "maud", source = "hex", outer_checksum = "3CD10B85CC436DD338E1AFAAF5C1CEA2B1D789689205A48374E9126E052F04F1" }, 33 + { name = "mist", version = "6.0.2", build_tools = ["gleam"], requirements = ["exception", "gleam_erlang", "gleam_http", "gleam_otp", "gleam_stdlib", "glisten", "gramps", "hpack_erl", "logging"], otp_app = "mist", source = "hex", outer_checksum = "6B03DEEA38A02F276333CB27B53B16D3D45BD741B89599085A601BAF635F2006" }, 34 + { name = "mork", version = "1.11.1", build_tools = ["gleam"], requirements = ["casefold", "gleam_regexp", "gleam_stdlib", "splitter"], otp_app = "mork", source = "hex", outer_checksum = "2DB4900FCCBB2CF9AB8C8438620FE00FC38CC6D340D52275D10769A4B43B3E5B" }, 35 + { name = "odysseus", version = "1.0.0", build_tools = ["gleam"], requirements = [], otp_app = "odysseus", source = "hex", outer_checksum = "6A97DA1075BDDEA8B60F47B1DFFAD49309FA27E73843F13A0AF32EA7087BA11C" }, 36 + { name = "simplifile", version = "2.4.0", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "7C18AFA4FED0B4CE1FA5B0B4BAC1FA1744427054EA993565F6F3F82E5453170D" }, 37 + { name = "smalto", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_community_ansi", "gleam_stdlib", "houdini"], otp_app = "smalto", source = "hex", outer_checksum = "13EA8935A9E60A770E326FB850F5A5B3612A62E0A6C62C3F850AD04C842E3769" }, 38 + { name = "smalto_lustre", version = "3.0.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "lustre", "smalto"], otp_app = "smalto_lustre", source = "hex", outer_checksum = "63C5F2CE886D7CDFEAABAAC5A0E7B3EDBF69F9776A08AACA0360B0948745EF2A" }, 39 + { name = "splitter", version = "1.2.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "splitter", source = "hex", outer_checksum = "3DFD6B6C49E61EDAF6F7B27A42054A17CFF6CA2135FF553D0CB61C234D281DD0" }, 40 + { name = "str", version = "2.1.0", build_tools = ["gleam"], requirements = ["gleam_stdlib", "houdini", "odysseus"], otp_app = "str", source = "hex", outer_checksum = "8414C7B6221F1E5244AADC837AC19C47D71AF606E394A82FCA53357C705156FA" }, 41 + { name = "tzif", version = "1.1.2", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib", "gleam_time", "simplifile"], otp_app = "tzif", source = "hex", outer_checksum = "04C8EBAFB7F6A6E61B8B0C16E06F96E7C7F76C31172EF07204CE9813B2E54AD2" }, 42 + { name = "webls", version = "1.6.2", build_tools = ["gleam"], requirements = ["gleam_stdlib", "gleam_time", "gtempo"], otp_app = "webls", source = "hex", outer_checksum = "515CA019E3B09FF7605D44ABBCDCF8FF56EC528AA29017741E0184278E3423DD" }, 43 + { name = "zones", version = "1.26010.1", build_tools = ["gleam"], requirements = ["tzif"], otp_app = "zones", source = "hex", outer_checksum = "6F79507E564D1F578C282BFE9241558744818D5957FE5CB859D64B5096F4211D" }, 44 + ] 45 + 46 + [requirements] 47 + blogatto = { version = ">= 5.1.1 and < 6.0.0" } 48 + glaze_oat = { version = ">= 3.0.0 and < 4.0.0" } 49 + gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } 50 + gleeunit = { version = ">= 1.0.0 and < 2.0.0" } 51 + lustre = { version = ">= 5.3.5 and < 6.0.0" }
+401 -2
src/page.gleam
··· 1 - import gleam/io 1 + import blogatto 2 + import blogatto/config 3 + import blogatto/config/markdown 4 + import blogatto/config/robots 5 + import blogatto/post 6 + import glaze/oat/badge 7 + import gleam/list 8 + import gleam/option.{None, Some} 9 + import gleam/time/timestamp 10 + import lustre/attribute.{attribute} 11 + import lustre/element.{type Element} 12 + import lustre/element/html 13 + import lustre/element/svg 14 + 15 + const out_dir: String = "dist/" 16 + 17 + const assets_dir = "./assets/" 2 18 3 19 pub fn main() -> Nil { 4 - io.println("Hello from page!") 20 + let robots = robots.new("https://ollie.earth") 21 + 22 + let md = 23 + markdown.default() 24 + |> markdown.markdown_path("./blog") 25 + 26 + let cfg = 27 + config.new("https://page.ollie.earth") 28 + |> config.output_dir("./dist") 29 + |> config.static_dir("./static") 30 + |> config.markdown(md) 31 + |> config.robots(robots) 32 + |> config.route("/", home_view) 33 + 34 + let assert Ok(Nil) = blogatto.build(cfg) 35 + Nil 36 + } 37 + 38 + fn home_view(posts: List(post.Post(Nil))) -> Element(Nil) { 39 + let sorted = list.sort(posts, fn(a, b) { timestamp.compare(b.date, a.date) }) 40 + 41 + html.html([attribute.lang("en")], [ 42 + html.head([], [ 43 + html.title([], "ollie.earth"), 44 + html.link([ 45 + attribute.rel("stylesheet"), 46 + attribute.href("/oat.min.css"), 47 + ]), 48 + html.script( 49 + [ 50 + attribute.attribute("defer", "defer"), 51 + attribute.src("/oat.min.js"), 52 + ], 53 + "", 54 + ), 55 + html.link([ 56 + attribute.rel("stylesheet"), 57 + attribute.href("/style.css"), 58 + ]), 59 + ]), 60 + html.body([], [ 61 + html.nav([attribute.attribute("data-topnav", "")], [ 62 + html.div([attribute.class("hstack")], [ 63 + html.div([attribute.class("hstack")], [ 64 + link_icon( 65 + to: "ollie.earth", 66 + at: "https://ollie.earth", 67 + with_image: "/media/snoot.png", 68 + size: Small, 69 + scaling: None, 70 + ), 71 + html.div([attribute.style("font-size", "var(--text-2)")], [ 72 + element.text("ollie.earth"), 73 + ]), 74 + ]), 75 + html.div( 76 + [ 77 + attribute.class("hstack"), 78 + ], 79 + [ 80 + html.a([attribute.href("https://en.pronouns.page/@liiv")], [ 81 + badge.badge([], [element.text("pronouns")]), 82 + ]), 83 + html.a([attribute.href("https://www.last.fm/user/nnuuvv")], [ 84 + badge.badge([], [element.text("lastfm")]), 85 + ]), 86 + ], 87 + ), 88 + ]), 89 + ]), 90 + html.main([], [ 91 + html.div([attribute.class("container")], [ 92 + html.p([], [ 93 + element.text("this page was made as a replacement to "), 94 + html.a([attribute.href("https://homarr.dev")], [ 95 + element.text("homarr"), 96 + ]), 97 + element.text(", which didn't like me changing my domain :/"), 98 + ]), 99 + html.p([], [ 100 + element.text( 101 + "i mainly use this as a link list to jump to my services / hosted code", 102 + ), 103 + ]), 104 + 105 + html.hr([]), 106 + 107 + html.section([], [ 108 + html.h3([], [element.text("my code")]), 109 + html.div([attribute.class("hstack")], [ 110 + link_icon( 111 + to: "tangled", 112 + at: "https://tangled.org/ollie.earth", 113 + with_image: "/media/dolly.svg", 114 + size: Large, 115 + scaling: Some(Scaling(width: Some("auto"), height: Some("80%"))), 116 + ), 117 + link_icon( 118 + to: "forgejo", 119 + at: "https://git.ollie.earth/nuv", 120 + with_image: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/forgejo.png", 121 + size: Large, 122 + scaling: Some(Scaling(width: Some("auto"), height: Some("80%"))), 123 + ), 124 + link_icon( 125 + to: "github", 126 + at: "https://github.com/nnuuvv", 127 + with_image: "/media/github.svg", 128 + size: Large, 129 + scaling: None, 130 + ), 131 + // git 132 + // tangled 133 + // ci 134 + // doodler 135 + ]), 136 + ]), 137 + 138 + html.hr([]), 139 + 140 + html.section([], [ 141 + html.h3([], [element.text("stuff hosted by me")]), 142 + html.div([attribute.class("hstack")], [ 143 + link_icon( 144 + to: "tangled", 145 + at: "https://tangled.org/ollie.earth", 146 + with_image: "/media/dolly.svg", 147 + size: Large, 148 + scaling: Some(Scaling(width: Some("auto"), height: Some("80%"))), 149 + ), 150 + link_icon( 151 + to: "forgejo", 152 + at: "https://git.ollie.earth/nuv", 153 + with_image: "https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/forgejo.png", 154 + size: Large, 155 + scaling: Some(Scaling(width: Some("auto"), height: Some("80%"))), 156 + ), 157 + // github 158 + // git 159 + // tangled 160 + // ci 161 + // doodler 162 + ]), 163 + ]), 164 + 165 + html.hr([]), 166 + 167 + html.section([], [ 168 + html.h3([], [element.text("people and projects i like")]), 169 + html.div([attribute.class("hstack")], [ 170 + // louis 171 + // giacomo 172 + // hayleigh 173 + // mar 174 + // gleam 175 + // lustre 176 + ]), 177 + ]), 178 + 179 + html.hr([]), 180 + 181 + html.section([], [ 182 + html.h3([], [element.text("me elsewhere")]), 183 + html.div([attribute.class("hstack")], [ 184 + bluesky(), 185 + matrix(), 186 + email(), 187 + // bsky 188 + // tangled 189 + // github 190 + // matrix 191 + // discord 192 + // email 193 + ]), 194 + ]), 195 + 196 + html.hr([]), 197 + html.h3([], [element.text("this is where i'd put my blog posts...")]), 198 + element.text("IF I HAD ANY!"), 199 + 200 + html.ul( 201 + [], 202 + list.map(sorted, fn(p) { 203 + html.li([], [ 204 + html.a([attribute.href("/blog/" <> p.slug)], [ 205 + element.text(p.title), 206 + ]), 207 + ]) 208 + }), 209 + ), 210 + ]), 211 + ]), 212 + ]), 213 + ]) 214 + } 215 + 216 + type Size { 217 + Small 218 + Normal 219 + Large 220 + } 221 + 222 + type Scaling { 223 + Scaling(width: option.Option(String), height: option.Option(String)) 224 + } 225 + 226 + fn scaling_to_style(scaling: Scaling) { 227 + let Scaling(width:, height:) = scaling 228 + 229 + let width = width |> option.unwrap("100%") 230 + let height = height |> option.unwrap("100%") 231 + 232 + attribute.styles([#("width", width), #("height", height)]) 233 + } 234 + 235 + fn link_icon( 236 + to target_name: String, 237 + at target_link: String, 238 + with_image image_source: String, 239 + size size: Size, 240 + scaling scaling: option.Option(Scaling), 241 + ) -> Element(_) { 242 + html.a( 243 + [ 244 + attribute.href(target_link), 245 + attribute.target("_blank"), 246 + attribute.style("width", "fit-content"), 247 + attribute.style("height", "fit-content"), 248 + ], 249 + [ 250 + html.figure( 251 + [ 252 + attribute.attribute("data-variant", "avatar"), 253 + attribute.style("display", "flex"), 254 + attribute.class(case size { 255 + Small -> "small" 256 + Normal -> "normal" 257 + Large -> "large" 258 + }), 259 + attribute.aria_label(target_name), 260 + ], 261 + [ 262 + html.img([ 263 + option.map(scaling, scaling_to_style) 264 + |> option.unwrap(attribute.none()), 265 + attribute.src(image_source), 266 + attribute.alt(target_name), 267 + ]), 268 + ], 269 + ), 270 + ], 271 + ) 272 + } 273 + 274 + // logo links ------------------------------------------------------------------- 275 + 276 + fn matrix() { 277 + html.a( 278 + [ 279 + attribute.aria_label("Matrix"), 280 + attribute.target("_blank"), 281 + attribute.href("https://matrix.to/#/@me:ollie.earth"), 282 + 283 + attribute.attribute("data-hover-color", "var(--background)"), 284 + attribute.attribute("data-hover-background", "var(--foreground)"), 285 + attribute.style("cursor", "pointer"), 286 + attribute.style("width", "26px"), 287 + attribute.style("height", "26px"), 288 + ], 289 + [ 290 + svg.svg( 291 + [ 292 + attribute("xmlns", "http://www.w3.org/2000/svg"), 293 + attribute("xml:space", "preserve"), 294 + attribute("viewBox", "0 0 26 26"), 295 + attribute("version", "1.1"), 296 + attribute("height", "26"), 297 + attribute("width", "26"), 298 + ], 299 + [ 300 + svg.g( 301 + [ 302 + attribute("stroke-width", ".05"), 303 + attribute("fill", "currentColor"), 304 + ], 305 + [ 306 + svg.path([ 307 + attribute( 308 + "d", 309 + "m0.685 0.595v24.81h1.785v0.59499h-2.47v-26h2.47v0.595z", 310 + ), 311 + ]), 312 + svg.path([ 313 + attribute( 314 + "d", 315 + "m8.315 8.46v1.255h0.035007c0.33499-0.48 0.73999-0.85 1.21-1.11 0.47-0.265 1.015-0.395 1.625-0.395 0.585 0 1.12 0.115 1.605 0.34 0.48501 0.225 0.85001 0.63 1.105 1.2 0.275-0.405 0.65-0.765 1.12-1.075 0.47001-0.31 1.03-0.465 1.675-0.465 0.49001 0 0.94501 0.060005 1.365 0.18 0.42001 0.12 0.775 0.31 1.075 0.575 0.3 0.265 0.52999 0.605 0.69999 1.03 0.16501 0.425 0.25001 0.935 0.25001 1.535v6.205h-2.545v-5.255c0-0.31-0.0093-0.605-0.03492-0.88001-0.02507-0.27499-0.09007-0.51499-0.19506-0.71499-0.11002-0.205-0.26503-0.365-0.47502-0.485-0.21-0.12-0.495-0.18-0.85-0.18-0.36001 0-0.65 0.06999-0.87002 0.205-0.22 0.14-0.395 0.315-0.52 0.54-0.12498 0.22-0.20999 0.47-0.24999 0.755-0.03993 0.28-0.065 0.565-0.065 0.85v5.165h-2.545v-5.2c0-0.275-0.0047-0.54501-0.02005-0.81501-0.0099-0.27-0.06498-0.515-0.15497-0.745-0.08994-0.225-0.23997-0.41-0.44996-0.545-0.21-0.135-0.515-0.205-0.925-0.205-0.12 0-0.28 0.02497-0.475 0.08-0.195 0.05501-0.39 0.155-0.575 0.305-0.185 0.15-0.345 0.365-0.475 0.645-0.13 0.28-0.195 0.65-0.195 1.105v5.38h-2.545v-9.28z", 316 + ), 317 + ]), 318 + svg.path([ 319 + attribute( 320 + "d", 321 + "m25.315 25.405v-24.81h-1.785v-0.595h2.47v26h-2.47v-0.59499z", 322 + ), 323 + ]), 324 + ], 325 + ), 326 + ], 327 + ), 328 + ], 329 + ) 330 + } 331 + 332 + fn email() { 333 + html.a( 334 + [ 335 + attribute.aria_label("Email"), 336 + attribute.target("_blank"), 337 + attribute.href("mailto:web@ollie.earth"), 338 + attribute.attribute("data-hover-color", "#cc3856"), 339 + attribute.style("cursor", "pointer"), 340 + attribute.style("width", "26px"), 341 + attribute.style("height", "26px"), 342 + ], 343 + [ 344 + svg.svg( 345 + [ 346 + attribute("xmlns", "http://www.w3.org/2000/svg"), 347 + attribute("viewBox", "0 0 .72 .72"), 348 + attribute("version", "1.1"), 349 + attribute("fill", "none"), 350 + attribute("height", "24"), 351 + attribute("width", "24"), 352 + ], 353 + [ 354 + svg.path([ 355 + attribute("stroke-width", ".072"), 356 + attribute("stroke-linejoin", "round"), 357 + attribute("stroke-linecap", "round"), 358 + attribute("stroke", "currentColor"), 359 + attribute( 360 + "d", 361 + "m0.074646 0.1641 0.24984 0.15615c0.023346 0.014591 0.052974 0.014591 0.07632 0l0.24984-0.15615m-0.54 0.396h0.504c0.039766 0 0.072-0.032234 0.072-0.072v-0.288c0-0.039764-0.032234-0.072-0.072-0.072h-0.504c-0.039764 0-0.072 0.032236-0.072 0.072v0.288c0 0.039766 0.032236 0.072 0.072 0.072z", 362 + ), 363 + ]), 364 + ], 365 + ), 366 + ], 367 + ) 368 + } 369 + 370 + fn bluesky() { 371 + html.a( 372 + [ 373 + attribute.aria_label("Bluesky"), 374 + attribute.target("_blank"), 375 + attribute.href("https://bsky.app/profile/ollie.earth"), 376 + attribute.attribute("data-hover-color", "#006aff"), 377 + attribute.style("cursor", "pointer"), 378 + attribute.style("width", "26px"), 379 + attribute.style("height", "26px"), 380 + ], 381 + [ 382 + svg.svg( 383 + [ 384 + attribute("xmlns", "http://www.w3.org/2000/svg"), 385 + attribute("viewBox", "0 0 48 24"), 386 + attribute("version", "1.1"), 387 + attribute("fill", "none"), 388 + attribute("height", "24"), 389 + attribute("width", "24"), 390 + ], 391 + [ 392 + svg.path([ 393 + attribute("stroke-width", ".65268"), 394 + attribute("fill", "currentColor"), 395 + attribute( 396 + "d", 397 + "m12.083-4.7208c4.7887 3.6074 9.9404 10.921 11.831 14.845v10.366c0-0.22061-0.08485 0.02872-0.2676 0.56588-0.98686 2.9084-4.8416 14.259-13.656 5.1849-4.6412-4.7776-2.4926-9.5553 5.9557-10.998-4.8331 0.82499-10.267-0.53846-11.757-5.8839-0.42947-1.5377-1.1605-11.009-1.1605-12.289 0-6.408 5.5994-4.3939 9.0547-1.7916zm23.662 0c-4.7887 3.6074-9.9404 10.921-11.831 14.845v10.366c0-0.22061 0.08485 0.02872 0.2676 0.56588 0.98686 2.9084 4.8416 14.259 13.656 5.1849 4.6412-4.7776 2.4926-9.5553-5.9557-10.998 4.8331 0.82499 10.267-0.53846 11.757-5.8839 0.42946-1.5377 1.1605-11.009 1.1605-12.289 0-6.408-5.5987-4.3939-9.0547-1.7916z", 398 + ), 399 + ]), 400 + ], 401 + ), 402 + ], 403 + ) 5 404 }
+27
static/media/discord.svg
··· 1 + <svg version="1.2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1396 1070" width="1396" height="1070"> 2 + <title>Discord_logo-svg</title> 3 + <defs> 4 + <clipPath clipPathUnits="userSpaceOnUse" id="cp1"> 5 + <path d="m0 0h5586.5v1069.8h-5586.5z"/> 6 + </clipPath> 7 + <clipPath clipPathUnits="userSpaceOnUse" id="cp2"> 8 + <path d="m0 0h5586.5v1069.8h-5586.5z"/> 9 + </clipPath> 10 + </defs> 11 + <style> 12 + .s0 { fill: #5865f2 } 13 + </style> 14 + <g id="layer1"> 15 + <g id="g866"> 16 + <g id="Clip-Path: g835" clip-path="url(#cp1)"> 17 + <g id="g835"> 18 + <g id="Clip-Path: g833" clip-path="url(#cp2)"> 19 + <g id="g833"> 20 + <path id="path815" fill-rule="evenodd" class="s0" d="m1389.7 890.5c-120.8 89.5-238.1 143.8-353.3 179.3-28.6-38.7-53.8-80-75.7-123.3 41.6-15.7 81.6-35 119.4-57.6-9.9-7.3-19.7-14.9-29.2-22.8-226.9 106.3-476.5 106.3-706.1 0-9.4 7.9-19.2 15.5-29.2 22.8 37.7 22.5 77.5 41.8 119.1 57.4-21.8 43.5-47.2 84.6-75.6 123.4-115.2-35.5-232.3-89.8-353.2-179.2-24.7-262.1 24.7-528 207-800.7 90.3-41.9 187-72.5 288.1-89.8 12.5 22.2 27.3 52.1 37.3 75.8q158.1-24 319.1 0c10-23.7 24.5-53.6 36.9-75.8 101 17.3 197.6 47.7 288 89.6 157.9 233.6 236.4 497 207.4 800.9zm-798.2-302.6c0-78.2-56.1-141.4-125.5-141.4-69.4 0-125.5 63.2-125.5 141.4 0 78.2 56.1 141.4 125.5 141.4 69.4 0 125.5-63.2 125.5-141.4zm463.7 0c0-78.2-56.1-141.4-125.5-141.4-69.4 0-125.5 63.2-125.5 141.4 0 78.2 56.1 141.4 125.5 141.4 69.4 0 125.5-63.2 125.5-141.4z"/> 21 + </g> 22 + </g> 23 + </g> 24 + </g> 25 + </g> 26 + </g> 27 + </svg>
+95
static/media/dolly.svg
··· 1 + 2 + <svg 3 + version="1.1" 4 + id="svg1" 5 + class="" 6 + width="25" 7 + height="25" 8 + viewBox="0 0 25 25" 9 + sodipodi:docname="tangled_dolly_face_only_black_on_trans.svg" 10 + inkscape:export-filename="tangled_logotype_black_on_trans.svg" 11 + inkscape:export-xdpi="96" 12 + inkscape:export-ydpi="96" 13 + inkscape:version="1.4 (e7c3feb100, 2024-10-09)" 14 + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" 15 + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" 16 + xmlns="http://www.w3.org/2000/svg" 17 + xmlns:svg="http://www.w3.org/2000/svg" 18 + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 19 + xmlns:cc="http://creativecommons.org/ns#"> 20 + <style> 21 + .dolly { 22 + color: #000000; 23 + } 24 + 25 + @media (prefers-color-scheme: dark) { 26 + .dolly { 27 + color: #ffffff; 28 + } 29 + } 30 + </style> 31 + <sodipodi:namedview 32 + id="namedview1" 33 + pagecolor="#ffffff" 34 + bordercolor="#000000" 35 + borderopacity="0.25" 36 + inkscape:showpageshadow="2" 37 + inkscape:pageopacity="0.0" 38 + inkscape:pagecheckerboard="true" 39 + inkscape:deskcolor="#d5d5d5" 40 + inkscape:zoom="45.254834" 41 + inkscape:cx="3.1377863" 42 + inkscape:cy="8.9382717" 43 + inkscape:window-width="3840" 44 + inkscape:window-height="2160" 45 + inkscape:window-x="0" 46 + inkscape:window-y="0" 47 + inkscape:window-maximized="0" 48 + inkscape:current-layer="g1" 49 + borderlayer="true"> 50 + <inkscape:page 51 + x="0" 52 + y="0" 53 + width="25" 54 + height="25" 55 + id="page2" 56 + margin="0" 57 + bleed="0" /> 58 + </sodipodi:namedview> 59 + <g 60 + inkscape:groupmode="layer" 61 + inkscape:label="Image" 62 + id="g1" 63 + transform="translate(-0.42924038,-0.87777209)"> 64 + <path 65 + class="dolly" 66 + fill="currentColor" 67 + style="stroke-width:0.111183;" 68 + d="m 16.775491,24.987061 c -0.78517,-0.0064 -1.384202,-0.234614 -2.033994,-0.631295 -0.931792,-0.490188 -1.643475,-1.31368 -2.152014,-2.221647 C 11.781409,23.136647 10.701392,23.744942 9.4922931,24.0886 8.9774725,24.238111 8.0757679,24.389777 6.5811304,23.84827 4.4270703,23.124679 2.8580086,20.883331 3.0363279,18.599583 3.0037061,17.652919 3.3488675,16.723769 3.8381157,15.925061 2.5329485,15.224503 1.4686756,14.048584 1.0611184,12.606459 0.81344502,11.816973 0.82385989,10.966486 0.91519098,10.154906 1.2422711,8.2387903 2.6795811,6.5725716 4.5299585,5.9732484 5.2685364,4.290122 6.8802592,3.0349975 8.706276,2.7794663 c 1.2124148,-0.1688264 2.46744,0.084987 3.52811,0.7011837 1.545426,-1.7139736 4.237779,-2.2205077 6.293579,-1.1676231 1.568222,0.7488935 2.689625,2.3113526 2.961888,4.0151464 1.492195,0.5977882 2.749007,1.8168898 3.242225,3.3644951 0.329805,0.9581836 0.340709,2.0135956 0.127128,2.9974286 -0.381606,1.535184 -1.465322,2.842146 -2.868035,3.556463 0.0034,0.273204 0.901506,2.243045 0.751284,3.729647 -0.03281,1.858525 -1.211631,3.619894 -2.846433,4.475452 -0.953967,0.556812 -2.084452,0.546309 -3.120531,0.535398 z m -4.470079,-5.349839 c 1.322246,-0.147248 2.189053,-1.300106 2.862307,-2.338363 0.318287,-0.472954 0.561404,-1.002348 0.803,-1.505815 0.313265,0.287151 0.578698,0.828085 1.074141,0.956909 0.521892,0.162542 1.133743,0.03052 1.45325,-0.443554 0.611414,-1.140449 0.31004,-2.516537 -0.04602,-3.698347 C 18.232844,11.92927 17.945151,11.232927 17.397785,10.751793 17.514522,9.9283111 17.026575,9.0919791 16.332883,8.6609491 15.741721,9.1323278 14.842258,9.1294949 14.271975,8.6252369 13.178927,9.7400102 12.177239,9.7029996 11.209704,8.8195135 10.992255,8.6209543 10.577326,10.031484 9.1211947,9.2324497 8.2846288,9.9333947 7.6359672,10.607693 7.0611981,11.578553 6.5026891,12.62523 5.9177873,13.554793 5.867393,14.69141 c -0.024234,0.66432 0.4948601,1.360337 1.1982269,1.306329 0.702996,0.06277 1.1815208,-0.629091 1.7138087,-0.916491 0.079382,0.927141 0.1688108,1.923227 0.4821259,2.828358 0.3596254,1.171275 1.6262605,1.915695 2.8251855,1.745211 0.08481,-0.0066 0.218672,-0.01769 0.218672,-0.0176 z m 0.686342,-3.497495 c -0.643126,-0.394168 -0.33365,-1.249599 -0.359402,-1.870938 0.064,-0.749774 0.115321,-1.538054 0.452402,-2.221125 0.356724,-0.487008 1.226721,-0.299139 1.265134,0.325689 -0.02558,0.628509 -0.314101,1.25416 -0.279646,1.9057 -0.07482,0.544043 0.05418,1.155133 -0.186476,1.652391 -0.197455,0.275121 -0.599638,0.355105 -0.892012,0.208283 z m -2.808766,-0.358124 c -0.605767,-0.328664 -0.4133176,-1.155655 -0.5083256,-1.73063 0.078762,-0.66567 0.013203,-1.510085 0.5705316,-1.976886 0.545037,-0.380109 1.286917,0.270803 1.029164,0.868384 -0.274913,0.755214 -0.09475,1.580345 -0.08893,2.34609 -0.104009,0.451702 -0.587146,0.691508 -1.002445,0.493042 z" 69 + id="path4" 70 + sodipodi:nodetypes="sccccccccccccccccccsscccccccccsccccccccccccccccccccccc" /> 71 + </g> 72 + <metadata 73 + id="metadata1"> 74 + <rdf:RDF> 75 + <cc:Work 76 + rdf:about=""> 77 + <cc:license 78 + rdf:resource="http://creativecommons.org/licenses/by/4.0/" /> 79 + </cc:Work> 80 + <cc:License 81 + rdf:about="http://creativecommons.org/licenses/by/4.0/"> 82 + <cc:permits 83 + rdf:resource="http://creativecommons.org/ns#Reproduction" /> 84 + <cc:permits 85 + rdf:resource="http://creativecommons.org/ns#Distribution" /> 86 + <cc:requires 87 + rdf:resource="http://creativecommons.org/ns#Notice" /> 88 + <cc:requires 89 + rdf:resource="http://creativecommons.org/ns#Attribution" /> 90 + <cc:permits 91 + rdf:resource="http://creativecommons.org/ns#DerivativeWorks" /> 92 + </cc:License> 93 + </rdf:RDF> 94 + </metadata> 95 + </svg>
static/media/forgejo.png

This is a binary file and will not be displayed.

+16
static/media/github.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 512 512"> 2 + <style> 3 + .github { 4 + color: #000000; 5 + } 6 + 7 + @media (prefers-color-scheme: dark) { 8 + .github { 9 + color: #ffffff; 10 + } 11 + } 12 + </style> 13 + <path 14 + class="github" 15 + fill="currentColor" 16 + d="M256 6.3C114.6 6.3 0 120.9 0 262.3c0 113.3 73.3 209 175 242.9 12.8 2.2 17.6-5.4 17.6-12.2 0-6.1-.3-26.2-.3-47.7-64.3 11.8-81-15.7-86.1-30.1-2.9-7.4-15.4-30.1-26.2-36.2-9-4.8-21.8-16.6-.3-17 20.2-.3 34.6 18.6 39.4 26.2 23 38.7 59.8 27.8 74.6 21.1 2.2-16.6 9-27.8 16.3-34.2-57-6.4-116.5-28.5-116.5-126.4 0-27.8 9.9-50.9 26.2-68.8-2.6-6.4-11.5-32.6 2.6-67.8 0 0 21.4-6.7 70.4 26.2 20.5-5.8 42.2-8.6 64-8.6s43.5 2.9 64 8.6c49-33.3 70.4-26.2 70.4-26.2 14.1 35.2 5.1 61.4 2.6 67.8 16.3 17.9 26.2 40.6 26.2 68.8 0 98.2-59.8 120-116.8 126.4 9.3 8 17.3 23.4 17.3 47.4 0 34.2-.3 61.8-.3 70.4 0 6.7 4.8 14.7 17.6 12.2C438.7 471.3 512 375.3 512 262.3c0-141.4-114.6-256-256-256" style="fill-rule:evenodd;clip-rule:evenodd;fill:currentColor"/></svg>
static/media/snoot.png

This is a binary file and will not be displayed.

+1
static/oat.min.css
··· 1 + @layer theme,base,components,animations,utilities;@layer base{*,*:before,*:after{box-sizing:border-box;-webkit-tap-highlight-color:transparent}*{margin:0}html{tab-size:4}body,dialog,[popover]{font-family:var(--font-sans);font-size:var(--text-regular);line-height:var(--leading-normal);color:var(--foreground)}body{background-color:var(--background);color:var(--foreground);-webkit-font-smoothing:antialiased}main{padding-block-start:var(--space-8)}img,picture,video,canvas,svg{max-width:100%}p,h1,h2,h3,h4,h5,h6{overflow-wrap:break-word}h1,h2,h3,h4,h5,h6{font-weight:var(--font-semibold);line-height:1.25;&:first-child{margin-block-start:0}}h1{font-size:var(--text-1);margin:var(--space-10) 0 var(--space-6)}h2{font-size:var(--text-2);margin:var(--space-8) 0 var(--space-5)}h3{font-size:var(--text-3);margin:var(--space-6) 0 var(--space-4)}h4{font-size:var(--text-4);margin:var(--space-5) 0 var(--space-3)}h5{font-size:var(--text-5);margin:var(--space-4) 0 var(--space-2)}h6{font-size:var(--text-regular);margin:var(--space-4) 0 var(--space-2)}p{margin-block-end:var(--space-4);&:last-child{margin-block-end:0}}a{color:var(--primary);text-decoration:underline;text-underline-offset:2px;transition:color var(--transition-fast);&:hover{color:rgb(from var(--primary) r g b / .8)}}strong,b{font-weight:var(--font-semibold)}em,i{font-style:italic}small{font-size:var(--text-7)}code{font-family:var(--font-mono);font-size:.875em;padding:calc(var(--space-1) / 2) var(--space-1);background-color:var(--faint);border-radius:var(--radius-small)}pre{font-family:var(--font-mono);padding:var(--space-4);background-color:var(--faint);border-radius:var(--radius-medium);overflow-x:auto;margin-block-end:var(--space-4);code{padding:0;background:none;border-radius:0}}blockquote{border-inline-start:4px solid var(--border);padding-inline-start:var(--space-4);margin:var(--space-4) 0;color:var(--muted-foreground);font-style:italic}hr{border:none;border-top:1px solid var(--border);margin:var(--space-8) 0}ul,ol{padding-inline-start:var(--space-6);margin-block-end:var(--space-4)}ul{list-style-type:disc}ol{list-style-type:decimal}li{margin-block-end:var(--space-1)}mark{background-color:rgb(from var(--warning) r g b / .3);padding:calc(var(--space-1) / 2) var(--space-1);border-radius:var(--radius-small)}[hidden]{display:none}:focus-visible{outline:2px solid var(--ring);outline-offset:2px}:disabled{opacity:.5;cursor:not-allowed}}@layer theme{:root{color-scheme:light dark;--background: light-dark(#fff, #09090b);--foreground: light-dark(#09090b, #fafafa);--card: light-dark(#fff, #18181b);--card-foreground: light-dark(#09090b, #fafafa);--primary: light-dark(#574747, #fafafa);--primary-foreground: light-dark(#fafafa, #18181b);--secondary: light-dark(#f4f4f5, #27272a);--secondary-foreground: light-dark(#574747, #fafafa);--muted: light-dark(#f4f4f5, #27272a);--muted-foreground: light-dark(#71717a, #a1a1aa);--faint: light-dark(#fafafa, #1e1e21);--faint-foreground: light-dark(#a1a1aa, #71717a);--accent: light-dark(#f4f4f5, #27272a);--danger: light-dark(#d32f2f, #f4807b);--danger-foreground: light-dark(#fafafa, #18181b);--success: light-dark(#008032, #6cc070);--success-foreground: light-dark(#fafafa, #18181b);--warning: light-dark(#a65b00, #f0a030);--warning-foreground: #09090b;--border: light-dark(#d4d4d8, #52525b);--input: light-dark(#d4d4d8, #52525b);--ring: light-dark(#574747, #d4d4d8);--space-1: .25rem;--space-2: .5rem;--space-3: .75rem;--space-4: 1rem;--space-5: 1.25rem;--space-6: 1.5rem;--space-8: 2rem;--space-10: 2.5rem;--space-12: 3rem;--space-14: 3.5rem;--space-16: 4rem;--space-18: 4.5rem;--radius-small: .125rem;--radius-medium: .375rem;--radius-large: .75rem;--radius-full: 9999px;--bar-height: .5rem;--font-sans: system-ui, sans-serif;--font-mono: ui-monospace, Consolas, monospace;--text-1: clamp(1.75rem, 1.5rem + 1.1vw, 2.25rem);--text-2: clamp(1.5rem, 1.3rem + .8vw, 1.875rem);--text-3: clamp(1.25rem, 1.1rem + .5vw, 1.5rem);--text-4: clamp(1.125rem, 1.05rem + .3vw, 1.25rem);--text-5: 1.125rem;--text-6: 1rem;--text-7: .875rem;--text-8: .75rem;--text-regular: var(--text-6);--leading-normal: 1.5;--font-normal: 400;--font-medium: 500;--font-semibold: 600;--font-bold: 600;--shadow-small: 0 1px 2px 0 rgb(0 0 0 / .05);--shadow-medium: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--shadow-large: 0 4px 6px -1px rgb(0 0 0 / .1), 0 2px 4px -2px rgb(0 0 0 / .1);--transition-fast: .12s cubic-bezier(.4, 0, .2, 1);--transition: .2s cubic-bezier(.4, 0, .2, 1);--z-dropdown: 50;--z-modal: 200}}@layer animations{@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}.animate-pop-in{opacity:1;transform:perspective(1000px) rotateX(0) translateZ(0);transition:opacity .15s cubic-bezier(.4,0,.2,1),transform .15s cubic-bezier(.4,0,.2,1),overlay .15s cubic-bezier(.4,0,.2,1) allow-discrete,display .15s cubic-bezier(.4,0,.2,1) allow-discrete;@starting-style{opacity:0;transform:perspective(1000px) rotateX(-15deg) translateZ(-80px)}&[data-state=closing]{opacity:0;transform:perspective(1000px) rotateX(-15deg) translateZ(-80px)}&[data-state=closing]::backdrop{opacity:0}}dialog::backdrop{opacity:1;transition:opacity .15s cubic-bezier(.4,0,.2,1);@starting-style{opacity:0}}.animate-slide-in{opacity:1;transform:translate(0);transition:opacity .15s cubic-bezier(.16,1,.3,1),transform .15s cubic-bezier(.16,1,.3,1);@starting-style{opacity:0;transform:translate(100%)}&[data-state=closing]{opacity:0;transform:translate(100%)}}}@layer components{figure[data-variant=avatar]:not([role=group]){display:inline-flex;align-items:center;justify-content:center;width:var(--sz, 2.5rem);height:var(--sz, 2.5rem);color:var(--primary);background-color:var(--muted);border-radius:var(--radius-full);font-weight:var(--font-medium);overflow:hidden;>img{width:100%;height:100%;object-fit:cover}&.small{--sz: 2rem}&.large{--sz: 3.25rem}}figure[data-variant=avatar][role=group]{display:inline-flex;align-items:center;margin:0;& figure[data-variant=avatar]{isolation:isolate;margin-inline-end:calc(var(--space-5) * -1);border:2px solid var(--background);&:last-child{margin-inline-end:0}}&.small{--sz: 2rem;& figure[data-variant=avatar]{margin-inline-end:calc(var(--space-4) * -.8);border-width:1px}}&.large{--sz: 3.25rem;& figure[data-variant=avatar]{margin-inline-end:calc(var(--space-6) * -1)}}}}@layer base{:is(button,[type=submit],[type=reset],[type=button],a.button),::file-selector-button{--_hov: color-mix(in srgb, var(--primary), white 25%);display:inline-flex;align-items:center;justify-content:center;gap:var(--space-2);padding:var(--space-2) var(--space-4);font-size:var(--text-7);font-weight:var(--font-medium);line-height:var(--leading-normal);white-space:nowrap;text-decoration:none;background-color:var(--primary);color:var(--primary-foreground);border-radius:var(--radius-medium);border:1px solid;border-color:rgb(from #fff r g b / .15) rgb(from #000 r g b / .2) rgb(from #000 r g b / .2) rgb(from #fff r g b / .15);transition:background-color var(--transition-fast),opacity var(--transition-fast),transform var(--transition-fast);&:not(:disabled){cursor:pointer}&:hover:not(:disabled){background-color:var(--_hov)}&:active:not(:disabled){transform:translate(1px,1px)}&[data-variant=secondary]{--_hov: color-mix(in srgb, var(--secondary), black 10%);background-color:var(--secondary);color:var(--secondary-foreground);border-color:rgb(from #fff r g b / .5) rgb(from #000 r g b / .1) rgb(from #000 r g b / .1) rgb(from #fff r g b / .5)}&[data-variant=danger]{--_hov: color-mix(in srgb, var(--danger), black 15%);background-color:var(--danger);color:var(--danger-foreground)}&:is(.outline,.ghost){--_hov: var(--accent);background-color:transparent;color:var(--foreground);&[data-variant=danger]{--_hov: color-mix(in srgb, var(--danger), transparent 90%);color:var(--danger)}&[data-variant=secondary]{--_hov: color-mix(in srgb, var(--secondary), transparent 80%);color:var(--secondary-foreground)}}&.outline{border-color:var(--border);&[data-variant=danger]{border-color:var(--danger)}&[data-variant=secondary]{border-color:var(--secondary)}}&.ghost{border-color:transparent}&.small{padding:var(--space-1) var(--space-3);font-size:var(--text-8)}&.large{height:3rem;padding:0 var(--space-6);font-size:var(--text-regular)}&.icon{width:2.5rem;padding:0;&.small{width:2rem}&.large{width:3rem}}}::file-selector-button{background-color:transparent;color:var(--foreground);border:1px solid var(--border)}::file-selector-button:hover{background-color:var(--accent)}}@layer components{menu.buttons{list-style-type:none;padding-inline-start:0;display:inline-flex;>li{&:first-child>*{border-start-start-radius:var(--radius-medium);border-end-start-radius:var(--radius-medium)}&:last-child>*{border-start-end-radius:var(--radius-medium);border-end-end-radius:var(--radius-medium)}>*{border-radius:0}&:not(:last-child)>*{border-inline-end:1px solid rgb(from var(--primary-foreground) r g b / .2)}}}}@layer base{label{display:block;font-size:var(--text-7);font-weight:var(--font-medium);&:has(input:where([type=checkbox],[type=radio])){display:inline-flex;align-items:center;gap:var(--space-2);font-weight:var(--font-normal)}}:where(input:not([type=checkbox],[type=radio],[type=range],[type=file],[type=color]),textarea,select){width:100%;margin-block-start:var(--space-1);padding:var(--space-2) var(--space-3);font-size:var(--text-7);line-height:var(--leading-normal);background-color:var(--background);color:var(--foreground);border:1px solid var(--input);border-radius:var(--radius-medium);transition:border-color var(--transition-fast),box-shadow var(--transition-fast);&::placeholder{color:var(--muted-foreground)}&:focus{outline:none;border-color:var(--ring);box-shadow:0 0 0 2px rgb(from var(--ring) r g b / .2);z-index:1}&:disabled{background-color:var(--muted)}&:is([aria-invalid=true],:user-invalid){border-color:var(--danger);&:focus{box-shadow:0 0 0 2px rgb(from var(--danger) r g b / .2)}}}textarea{height:auto;min-height:5rem;padding:var(--space-3);resize:vertical}select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2371717a' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right var(--space-2) center;padding-inline-end:var(--space-6)}input:where([type=checkbox],[type=radio]){appearance:none;width:1rem;height:1rem;margin:0;position:relative;background-color:var(--background);border:1px solid var(--input);transition:background-color var(--transition-fast),border-color var(--transition-fast);&:checked{background-color:var(--primary);border-color:var(--primary);&:after{content:"";position:absolute;inset:0;background-color:var(--primary-foreground);mask-position:center;mask-repeat:no-repeat;mask-size:100%}}}input[type=checkbox]{border-radius:var(--radius-small);&:checked:after{mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='4'%3E%3Cpolyline points='20 6 9 17 4 12'/%3E%3C/svg%3E")}&[role=switch]{--switch-height: calc(var(--bar-height) * 3);--switch-inset: 2px;--switch-thumb: calc(var(--switch-height) - var(--switch-inset) * 3);width:calc(var(--switch-height) * 2);height:var(--switch-height);border-radius:var(--radius-full);background-color:var(--input);&:before{content:"";position:absolute;top:50%;left:var(--switch-inset);transform:translateY(-50%);width:var(--switch-thumb);height:var(--switch-thumb);background-color:var(--background);border-radius:var(--radius-full);transition:transform var(--transition);box-shadow:var(--shadow-small)}&:checked{background-color:var(--primary);&:after{content:none}&:before{transform:translateY(-50%) translate(var(--switch-height))}}}}input[type=radio]{border-radius:var(--radius-full);&:checked:after{mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Ccircle cx='8' cy='8' r='4' fill='currentColor'/%3E%3C/svg%3E")}}:where(input:where([type=checkbox],[type=radio],[type=range]),select):not(:disabled),label:has(input:where([type=checkbox],[type=radio]):not(:disabled)){cursor:pointer}input[type=range]{width:100%;height:var(--bar-height);appearance:none;background:var(--muted);border-radius:var(--radius-full);&::-webkit-slider-thumb{appearance:none;width:1.25rem;height:1.25rem;background:var(--primary);border-radius:var(--radius-full);transition:transform var(--transition-fast);&:hover{transform:scale(1.1)}}&::-moz-range-thumb{width:1.25rem;height:1.25rem;background:var(--primary);border:none;border-radius:var(--radius-full)}}fieldset{border:1px solid var(--border);border-radius:var(--radius-medium);padding:var(--space-4);margin-block-end:var(--space-4)}legend{font-size:var(--text-7);font-weight:var(--font-medium);padding:0 var(--space-2)}}@layer components{fieldset.group{display:flex;align-items:stretch;border:none;padding:0;margin:0;>:is(input,textarea,select){flex:1;margin-block-start:0;&:not(:focus):not(:last-child){border-inline-end-color:transparent}}>:is(input,textarea,select,button){border-radius:0;&:first-child{border-radius:var(--radius-medium) 0 0 var(--radius-medium)}&:last-child{border-radius:0 var(--radius-medium) var(--radius-medium) 0}}>legend{float:inline-start;display:inline-flex;align-items:center;padding:0 var(--space-3);line-height:var(--leading-normal);font-weight:var(--font-normal);color:var(--muted-foreground);background-color:var(--muted);border:1px solid var(--input);border-inline-end:none;border-radius:var(--radius-medium) 0 0 var(--radius-medium)}}[data-field]{margin-block-end:var(--space-4);[data-hint],.error{font-size:var(--text-8);font-weight:var(--font-normal);color:var(--muted-foreground);margin-block-start:var(--space-1)}.error{display:none}&[data-field=error] .error{display:block;color:var(--danger)}}}@layer base{.table{min-width:320px;width:100%;overflow-x:auto}table{border-collapse:collapse;width:100%;font-size:var(--text-7)}thead{border-bottom:1px solid var(--border)}th,td{overflow-wrap:break-word}th{padding:var(--space-3) var(--space-2);text-align:start;font-weight:var(--font-medium);color:var(--muted-foreground)}td{padding:var(--space-3) var(--space-2)}tbody tr{border-bottom:1px solid var(--border);transition:background-color var(--transition-fast);&:last-child{border-bottom:none}&:hover{background-color:rgb(from var(--muted) r g b / .5)}}}@layer base{progress{appearance:none;width:100%;height:var(--bar-height);border:none;border-radius:var(--radius-full);overflow:hidden;background-color:var(--muted);&::-webkit-progress-bar{background-color:var(--muted);border-radius:var(--radius-full)}&::-webkit-progress-value{background-color:var(--primary);border-radius:var(--radius-full);transition:width var(--transition)}&::-moz-progress-bar{background-color:var(--primary);border-radius:var(--radius-full)}}meter{appearance:none;width:100%;height:var(--bar-height);border:none;border-radius:var(--radius-full);overflow:hidden;background:var(--muted);&::-webkit-meter-bar{background:var(--muted);border:none;border-radius:var(--radius-full);height:var(--bar-height)}&::-webkit-meter-optimum-value,&::-webkit-meter-suboptimum-value,&::-webkit-meter-even-less-good-value{border-radius:var(--radius-full)}&::-webkit-meter-optimum-value{background:var(--success)}&::-webkit-meter-suboptimum-value{background:var(--warning)}&::-webkit-meter-even-less-good-value{background:var(--danger)}&::-moz-meter-bar{background:var(--success);border-radius:var(--radius-full)}&:-moz-meter-sub-optimum::-moz-meter-bar{background:var(--warning)}&:-moz-meter-sub-sub-optimum::-moz-meter-bar{background:var(--danger)}}}@layer components{[aria-busy=true]{&:before{content:"";display:inline-block;inset:0;margin:auto;width:1.5rem;height:1.5rem;border:2px solid var(--muted);border-top-color:var(--primary);border-radius:var(--radius-full);animation:spin 1s linear infinite;text-align:center}&[data-spinner~=small]:before{width:1rem;height:1rem}&[data-spinner~=large]:before{width:2rem;height:2rem;border-width:3px}&[data-spinner~=overlay]{position:relative;>*{opacity:.3;pointer-events:none}&:before{position:absolute;inset:0;margin:auto;z-index:1}}}@keyframes spin{to{transform:rotate(360deg)}}}@layer components{:root{--grid-cols: 12;--grid-gap: 1.5rem;--container-max: 1280px;--container-pad: 1rem}.container{width:100%;max-width:var(--container-max);margin-inline:auto;padding-inline:var(--container-pad)}.row{display:grid;grid-template-columns:repeat(var(--grid-cols),1fr);gap:var(--grid-gap);width:100%}.col,[class*=col-]{grid-column-end:span calc(var(--span, var(--grid-cols)) + var(--offset, 0))}.col-1{--span: 1}.col-2{--span: 2}.col-3{--span: 3}.col-4{--span: 4}.col-5{--span: 5}.col-6{--span: 6}.col-7{--span: 7}.col-8{--span: 8}.col-9{--span: 9}.col-10{--span: 10}.col-11{--span: 11}.col-12{--span: 12}.offset-1{--offset: 1}.offset-2{--offset: 2}.offset-3{--offset: 3}.offset-4{--offset: 4}.offset-5{--offset: 5}.offset-6{--offset: 6}[class*=offset-]{margin-inline-start:calc(var(--offset) * (100% + var(--grid-gap)) / (var(--span) + var(--offset)))}.col-end{grid-column-start:span var(--span, 1);grid-column-end:-1}@media(max-width:768px){.row{--grid-cols: 4;--grid-gap: 1rem}.col,[class*=col-]{--span: 4}[class*=offset-]{--offset: 0;margin-inline-start:0}}}@layer components{.card{background-color:var(--card);color:var(--card-foreground);border:1px solid var(--border);border-radius:var(--radius-medium);box-shadow:var(--shadow-small);padding:var(--space-6)}}@layer components{[role=alert]{position:relative;display:flex;gap:var(--space-3);padding:var(--space-4) var(--space-6);background-color:var(--background);border:1px solid var(--border);border-radius:var(--radius-medium);font-size:var(--text-7);&[data-variant]{border:none}&[data-variant=error],&[data-variant=danger]{color:var(--danger);background-color:light-dark(color-mix(in srgb,var(--danger) 8%,transparent),color-mix(in srgb,var(--danger) 20%,transparent));& a{color:var(--danger)}}&[data-variant=success]{color:var(--success);background-color:light-dark(color-mix(in srgb,var(--success) 8%,transparent),color-mix(in srgb,var(--success) 20%,transparent));& a{color:var(--success)}}&[data-variant=warning]{color:var(--warning);background-color:light-dark(color-mix(in srgb,var(--warning) 8%,transparent),color-mix(in srgb,var(--warning) 20%,transparent));& a{color:var(--warning)}}}}@layer components{.badge{display:inline-flex;align-items:center;gap:var(--space-1);padding:var(--space-1) var(--space-4);font-size:var(--text-8);font-weight:var(--font-medium);line-height:var(--leading-normal);background-color:var(--primary);color:var(--primary-foreground);border-radius:var(--radius-full);&.secondary{background-color:var(--secondary);color:var(--secondary-foreground)}&.outline{background-color:transparent;color:var(--foreground);border:1px solid var(--border)}&.success{color:var(--success);background-color:light-dark(color-mix(in srgb,var(--success) 10%,transparent),color-mix(in srgb,var(--success) 30%,transparent))}&.warning{color:var(--warning);background-color:light-dark(color-mix(in srgb,var(--warning) 10%,transparent),color-mix(in srgb,var(--warning) 30%,transparent))}&.danger{color:var(--danger);background-color:light-dark(color-mix(in srgb,var(--danger) 10%,transparent),color-mix(in srgb,var(--danger) 30%,transparent))}}}@layer components{details{border:1px solid var(--border);border-radius:var(--radius-medium);overflow:hidden;+details{margin-top:-1px;border-start-start-radius:0;border-start-end-radius:0}&:has(+details){border-end-start-radius:0;border-end-end-radius:0}&[open] summary{border-bottom:1px solid var(--border)}}summary{display:flex;align-items:center;justify-content:space-between;gap:var(--space-2);padding:var(--space-4);font-weight:var(--font-medium);cursor:pointer;user-select:none;transition:background-color var(--transition-fast);&:hover{background-color:var(--muted)}&::-webkit-details-marker,&::marker{display:none}&:after{content:"";width:1em;height:1em;flex-shrink:0;background-color:currentColor;mask-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");mask-size:contain;mask-repeat:no-repeat;transition:transform var(--transition-fast)}details[open] &:after{transform:rotate(180deg)}}details>*:not(summary){margin:var(--space-4)}}@layer components{[role=tablist]{display:inline-flex;align-items:center;gap:var(--space-1);padding:var(--space-1);background-color:var(--muted);border-radius:var(--radius-medium)}[role=tab]{display:inline-flex;align-items:center;justify-content:center;padding:var(--space-2) var(--space-3);font-size:var(--text-7);font-weight:var(--font-medium);white-space:nowrap;background-color:transparent;color:var(--foreground);border:none;border-radius:calc(var(--radius-medium) - 2px);cursor:pointer;transition:background-color var(--transition-fast),color var(--transition-fast);&:hover{color:var(--muted-foreground)}&[aria-selected=true]{background-color:var(--background);box-shadow:var(--shadow-small)}}[role=tabpanel]{padding:var(--space-4) 0;&:focus-visible{outline:none}}}@layer components{dialog{position:fixed;inset:0;z-index:var(--z-modal);width:min(100% - 2rem,32rem);max-height:85vh;margin:auto;padding:0;background-color:var(--card);border:1px solid var(--border);border-radius:var(--radius-large);box-shadow:var(--shadow-large);overflow:hidden;opacity:0;transform:scale(.95);transition:opacity .15s ease,transform .15s ease,overlay .15s ease allow-discrete,display .15s ease allow-discrete;&:is([open],:popover-open){opacity:1;transform:scale(1)}@starting-style{&:is([open],:popover-open){opacity:0;transform:scale(.95)}}&::backdrop{background-color:#0000;transition:background-color .15s ease,overlay .15s ease allow-discrete,display .15s ease allow-discrete}&:is([open],:popover-open)::backdrop{background-color:#00000080}@starting-style{&:is([open],:popover-open)::backdrop{background-color:#0000}}>header,>form>header{display:flex;flex-direction:column;gap:var(--space-1);padding:var(--space-6);padding-block-end:0;>h1,>h2,>h3,>h4,>h5,>h6{margin-block-end:0}>p{font-size:var(--text-7);color:var(--muted-foreground);margin-block-end:0}}>p,>div,>section,>form>p,>form>div,>form>section{padding:var(--space-6);overflow-y:auto}>footer,>form>footer{display:flex;justify-content:flex-end;gap:var(--space-2);padding:var(--space-6);padding-block-start:0}}}@layer components{ot-dropdown{[popover]{position:fixed;margin:0;min-width:12rem;background-color:var(--background);border:1px solid var(--border);border-radius:var(--radius-medium);box-shadow:var(--shadow-medium);opacity:0;transform:translateY(-4px);transition:opacity .15s ease-out,transform .15s ease-out,display .15s allow-discrete,overlay .15s allow-discrete;&:popover-open{opacity:1;transform:translateY(0)}@starting-style{&:popover-open{opacity:0;transform:translateY(-4px)}}}[role=menuitem]{display:flex;align-items:center;justify-content:start;gap:var(--space-2);width:100%;padding:var(--space-2) var(--space-3);font-size:var(--text-7);text-align:start;color:var(--foreground);background:none;border:none;border-radius:var(--radius-small);cursor:pointer;&:hover,&:focus{background-color:var(--accent);outline:none}}}}@layer components{.toast-container{position:fixed;display:flex;flex-direction:column;pointer-events:none;margin:0;padding:0;border:none;background:transparent;overflow:visible;&::backdrop{display:none}&[data-placement=top-left]{inset:var(--space-4) auto auto var(--space-4)}&[data-placement=top-center]{inset:var(--space-4) auto auto 50%;transform:translate(-50%)}&[data-placement=top-right]{inset:var(--space-4) var(--space-4) auto auto}&[data-placement=bottom-left]{inset:auto auto var(--space-4) var(--space-4);flex-direction:column-reverse}&[data-placement=bottom-center]{inset:auto auto var(--space-4) 50%;transform:translate(-50%);flex-direction:column-reverse}&[data-placement=bottom-right]{inset:auto var(--space-4) var(--space-4) auto;flex-direction:column-reverse}}.toast{--transition: .3s;--transition-in: calc(var(--transition) - 50ms);padding:var(--space-5) var(--space-4);max-width:28rem;min-width:20rem;pointer-events:auto;background-color:var(--card);border:1px solid var(--border);border-inline-start-width:var(--space-1);border-inline-start-style:solid;border-radius:var(--radius-medium);box-shadow:var(--shadow-small);transition:opacity var(--transition-in),transform var(--transition-in),margin var(--transition-in);line-height:1;.toast-title{font-weight:600;margin:0 0 var(--space-3) 0}.toast-message{color:var(--muted-foreground)}&[data-variant=success]{border-inline-start-color:var(--success);.toast-title{color:var(--success)}}&[data-variant=danger]{border-inline-start-color:var(--danger);.toast-title{color:var(--danger)}}&[data-variant=warning]{border-inline-start-color:var(--warning);.toast-title{color:var(--warning)}}>[data-close]{margin-inline-start:auto;background:none;border:none;padding:0;cursor:pointer;opacity:.5;&:hover{opacity:1}}margin:var(--space-2) 0;&[data-entering]{opacity:0;transform:translateY(-1rem)}&[data-exiting]{opacity:0;margin:0;padding-block:0;max-height:0;overflow:hidden;transition:opacity var(--transition),margin var(--transition),padding var(--transition),max-height var(--transition)}}}@layer components{[data-sidebar-layout]{display:grid;grid-template-columns:14rem 1fr;grid-template-rows:auto 1fr;height:100dvh;>main{grid-row:2;min-width:0;overflow-y:auto}>aside[data-sidebar]{grid-row:2;min-height:0;z-index:1;background-color:var(--background);border-inline-end:1px solid var(--border);box-shadow:var(--shadow-medium);display:flex;flex-direction:column;>:is(header,footer){flex-shrink:0;padding:var(--space-3)}>footer{margin-block-start:auto}>nav{flex:1;min-height:0;overflow-y:auto;padding:var(--space-3) var(--space-2);font-size:var(--text-7);ul{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:var(--space-1);li{margin:0}}a{display:flex;gap:var(--space-2);padding:var(--space-1) var(--space-3);color:var(--foreground);text-decoration:none;border-radius:var(--radius-small);transition:background-color var(--transition-fast);&:is(:hover,[aria-current]){background-color:var(--accent)}}details{border:none;overflow:visible;+details{margin-top:0}&[open] summary{border-bottom:none}>ul{margin-inline-start:var(--space-4);padding:var(--space-1) 0}}summary{justify-content:flex-start;padding:var(--space-2) var(--space-3);border-radius:var(--radius-small);&:after{width:.75rem;height:.75rem;margin-inline-start:auto}}}}>nav[data-topnav]{grid-column:1 / -1}}nav[data-topnav]{position:sticky;top:0;z-index:5;display:flex;align-items:center;gap:var(--space-3);padding:var(--space-2) var(--space-4);background-color:var(--background);border-bottom:1px solid var(--border);box-shadow:var(--shadow-small);a{text-decoration:none}}:is([data-sidebar-toggle],[data-sidebar-header]){display:none}[data-sidebar-toggle]{padding:0 var(--space-1);background:none;border:1px solid var(--border);border-radius:var(--radius-small)}@media(min-width:769px){[data-sidebar-layout=always]{transition:grid-template-columns var(--transition);[data-sidebar-toggle]{display:inline-block}>aside[data-sidebar]{transform:translate(0);opacity:1;transition:transform var(--transition),opacity var(--transition),visibility var(--transition)}&[data-sidebar-open]{grid-template-columns:0px 1fr;gap:0;>aside[data-sidebar]{overflow:hidden;min-width:0;transform:translate(-100%);opacity:0;visibility:hidden;border-inline-end:none}}}}@media(max-width:768px){[data-sidebar-layout]{grid-template-columns:1fr;>main{grid-column:1}>aside[data-sidebar]{grid-column:1;z-index:2;width:16rem;transform:translate(-100%);transition:transform var(--transition);box-shadow:var(--shadow-large)}&[data-sidebar-open]>aside[data-sidebar]{transform:translate(0)}}[data-sidebar-toggle]{display:inline-block}[data-sidebar-header]{display:flex;align-items:center;gap:var(--space-3);padding:var(--space-3) var(--space-4);border-bottom:1px solid var(--border)}}}@layer components{[role=status].skeleton{--_c: light-dark( color-mix(in srgb, var(--muted) 15%, white), color-mix(in srgb, var(--muted) 90%, var(--foreground)) );margin-block-end:var(--space-3);background:var(--muted);border-radius:var(--radius-medium);animation:anim 2s infinite;background-size:200% 100%;background-image:linear-gradient(90deg,var(--muted) 0%,var(--_c) 50%,var(--muted) 100%);&.box{width:4rem;height:4rem}&.line{height:1rem;width:100%}}[role=status].skeleton:last-child{margin-block-end:0}@keyframes anim{0%{background-position:200% 0}to{background-position:-200% 0}}}@layer components{[data-tooltip]{position:relative}[data-tooltip]:before,[data-tooltip]:after{position:absolute;inset-inline-start:50%;opacity:0;visibility:hidden;transition:opacity var(--transition-fast),transform var(--transition-fast),visibility var(--transition-fast);pointer-events:none;z-index:calc(var(--z-modal) + 10)}[data-tooltip]:after{content:attr(data-tooltip);inset-block-end:calc(100% + 10px);transform:translate(-50%) translateY(4px);padding:var(--space-2) var(--space-3);font-size:var(--text-7);line-height:1;white-space:nowrap;background:var(--foreground);color:var(--background);border-radius:var(--radius-medium)}[data-tooltip]:before{content:"";inset-block-end:calc(100% - 5px);transform:translate(-50%) translateY(4px);border:8px solid transparent;border-top-color:var(--foreground)}[data-tooltip]:is(:hover,:focus-visible):before,[data-tooltip]:is(:hover,:focus-visible):after{opacity:1;visibility:visible;transition-delay:.7s;transform:translate(-50%) translateY(0)}}@layer utilities{.align-left{text-align:start}.align-center{text-align:center}.align-right{text-align:end}.text-light{color:var(--muted-foreground)}.text-lighter{color:var(--faint-foreground)}.flex{display:flex}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-end{justify-content:flex-end}.hstack{display:flex;align-items:center;gap:var(--space-3);flex-wrap:wrap;align-content:flex-start;height:auto;>*{margin:0}}.vstack{display:flex;flex-direction:column;gap:var(--space-3)}.gap-1{gap:var(--space-1)}.gap-2{gap:var(--space-2)}.gap-4{gap:var(--space-4)}.mt-2{margin-block-start:var(--space-2)}.mt-4{margin-block-start:var(--space-4)}.mt-6{margin-block-start:var(--space-6)}.mb-2{margin-block-end:var(--space-2)}.mb-4{margin-block-end:var(--space-4)}.mb-6{margin-block-end:var(--space-6)}.p-4{padding:var(--space-4)}.w-100{width:100%}:is(ul,ol,a).unstyled{list-style:none;text-decoration:none;padding:0}}
+1
static/oat.min.js
··· 1 + (()=>{var l=class extends HTMLElement{#t=!1;connectedCallback(){this.#t||(document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>this.#e(),{once:!0}):this.#e())}#e(){this.#t||(this.#t=!0,this.init())}disconnectedCallback(){this.cleanup()}cleanup(){}handleEvent(t){let e=this[`on${t.type}`];e&&e.call(this,t)}keyNav(t,e,n,o,s,a=!1){let{key:r}=t,c=-1;return r===s?c=(e+1)%n:r===o?c=(e-1+n)%n:a&&(r==="Home"?c=0:r==="End"&&(c=n-1)),c>=0&&t.preventDefault(),c}emit(t,e=null){return this.dispatchEvent(new CustomEvent(t,{bubbles:!0,composed:!0,cancelable:!0,detail:e}))}$(t){return this.querySelector(t)}$$(t){return Array.from(this.querySelectorAll(t))}uid(){return Math.random().toString(36).slice(2,10)}};"commandForElement"in HTMLButtonElement.prototype||document.addEventListener("click",i=>{let t=i.target.closest("button[commandfor]");if(!t)return;let e=document.getElementById(t.getAttribute("commandfor"));if(!e)return;let n=t.getAttribute("command")||"toggle";e instanceof HTMLDialogElement&&(n==="show-modal"?e.showModal():n==="close"||e.open?e.close():e.showModal())});document.addEventListener("touchstart",i=>{i.target instanceof HTMLDialogElement&&(i.preventDefault(),i.target.close())},{passive:!1});var u=class extends l{#t=[];#e=[];init(){let t=this.$(':scope > [role="tablist"]');if(this.#t=t?[...t.querySelectorAll('[role="tab"]')]:[],this.#e=this.$$(':scope > [role="tabpanel"]'),this.#t.length===0||this.#e.length===0){console.warn("ot-tabs: Missing tab or tabpanel elements");return}this.#t.forEach((n,o)=>{let s=this.#e[o];if(!s)return;let a=n.id||`ot-tab-${this.uid()}`,r=s.id||`ot-panel-${this.uid()}`;n.id=a,s.id=r,n.setAttribute("aria-controls",r),s.setAttribute("aria-labelledby",a)}),t.addEventListener("click",this),t.addEventListener("keydown",this);let e=this.#t.findIndex(n=>n.ariaSelected==="true");this.#i(e>=0?e:0)}onclick(t){let e=this.#t.indexOf(t.target.closest('[role="tab"]'));e>=0&&this.#i(e)}onkeydown(t){if(!t.target.closest('[role="tab"]'))return;let e=this.keyNav(t,this.activeIndex,this.#t.length,"ArrowLeft","ArrowRight");e>=0&&(this.#i(e),this.#t[e].focus())}#i(t){this.#t.forEach((e,n)=>{let o=n===t;e.ariaSelected=String(o),e.tabIndex=o?0:-1}),this.#e.forEach((e,n)=>{e.hidden=n!==t}),this.emit("ot-tab-change",{index:t,tab:this.#t[t]})}get activeIndex(){return this.#t.findIndex(t=>t.ariaSelected==="true")}set activeIndex(t){t>=0&&t<this.#t.length&&this.#i(t)}};customElements.define("ot-tabs",u);var h=class extends l{#t;#e;#i;#n;init(){this.#t=this.$("[popover]"),this.#e=this.$("[popovertarget]"),!(!this.#t||!this.#e)&&(this.#t.addEventListener("toggle",this),this.#t.addEventListener("keydown",this),this.#i=()=>{let t=this.#e.getBoundingClientRect(),e=this.#t.getBoundingClientRect();this.#t.style.top=`${t.bottom+e.height>window.innerHeight?t.top-e.height:t.bottom}px`,this.#t.style.left=`${t.left+e.width>window.innerWidth?t.right-e.width:t.left}px`})}ontoggle(t){t.newState==="open"?(this.#i(),window.addEventListener("scroll",this.#i,!0),window.addEventListener("resize",this.#i),this.#n=this.$$('[role="menuitem"]'),this.#n[0]?.focus(),this.#e.ariaExpanded="true"):(this.cleanup(),this.#n=null,this.#e.ariaExpanded="false",this.#e.focus())}onkeydown(t){if(!t.target.matches('[role="menuitem"]'))return;let e=this.#n.indexOf(t.target),n=this.keyNav(t,e,this.#n.length,"ArrowUp","ArrowDown",!0);n>=0&&this.#n[n].focus()}cleanup(){window.removeEventListener("scroll",this.#i,!0),window.removeEventListener("resize",this.#i)}};customElements.define("ot-dropdown",h);document.addEventListener("DOMContentLoaded",()=>{let i="title",t="[title]",e=n=>{let o=n.getAttribute(i);o&&(n.setAttribute("data-tooltip",o),n.hasAttribute("aria-label")||n.setAttribute("aria-label",o),n.removeAttribute(i))};document.querySelectorAll(t).forEach(e),new MutationObserver(n=>{for(let o of n){e(o.target);for(let s of o.addedNodes)s.nodeType===1&&(e(s),s.querySelectorAll(t).forEach(e))}}).observe(document.body,{childList:!0,subtree:!0,attributes:!0,attributeFilter:[i]})});document.addEventListener("click",i=>{let t=i.target.closest("[data-sidebar-toggle]");if(t){t.closest("[data-sidebar-layout]")?.toggleAttribute("data-sidebar-open");return}if(!i.target.closest("[data-sidebar]")){let e=document.querySelector("[data-sidebar-layout][data-sidebar-open]");e&&window.matchMedia("(max-width: 768px)").matches&&e.removeAttribute("data-sidebar-open")}});var d={};function E(i){if(!d[i]){let t=document.createElement("div");t.className="toast-container",t.setAttribute("popover","manual"),t.setAttribute("data-placement",i),document.body.appendChild(t),d[i]=t}return d[i]}function p(i,t={}){let{placement:e="top-right",duration:n=4e3}=t,o=E(e);i.classList.add("toast");let s;return i.onmouseenter=()=>clearTimeout(s),i.onmouseleave=()=>{n>0&&(s=setTimeout(()=>f(i,o),n))},i.setAttribute("data-entering",""),o.appendChild(i),o.showPopover(),requestAnimationFrame(()=>{requestAnimationFrame(()=>{i.removeAttribute("data-entering")})}),n>0&&(s=setTimeout(()=>f(i,o),n)),i}function f(i,t){if(i.hasAttribute("data-exiting"))return;i.setAttribute("data-exiting","");let e=()=>{i.remove(),t.children.length||t.hidePopover()};i.addEventListener("transitionend",e,{once:!0});let n=getComputedStyle(i).getPropertyValue("--transition").trim(),o=parseFloat(n),s=n.endsWith("ms")?o:o*1e3;setTimeout(e,s)}function g(i,t,e={}){let{variant:n="info",...o}=e,s=document.createElement("output");if(s.setAttribute("data-variant",n),t){let r=document.createElement("h6");r.className="toast-title",r.textContent=t,s.appendChild(r)}let a=document.createElement("div");return a.className="toast-message",a.textContent=i,s.appendChild(a),p(s,o)}function b(i,t={}){let e;if(i instanceof HTMLTemplateElement?e=i.content.firstElementChild?.cloneNode(!0):i&&(e=i.cloneNode(!0)),!!e)return e.removeAttribute("id"),p(e,t)}function v(i){i&&d[i]?(d[i].innerHTML="",d[i].hidePopover()):Object.values(d).forEach(t=>{t.innerHTML="",t.hidePopover()})}var m=window.ot||(window.ot={});m.toast=g;m.toast.el=b;m.toast.clear=v;})();
+8
static/style.css
··· 1 + nav[data-topnav] { 2 + font-size: var(--text-7) 3 + } 4 + 5 + a:hover { 6 + color: attr(data-hover-color type(<color>), var(--foreground)); 7 + background: attr(data-hover-background type(<color>), var(--background)); 8 + }
-13
test/page_test.gleam
··· 1 - import gleeunit 2 - 3 - pub fn main() -> Nil { 4 - gleeunit.main() 5 - } 6 - 7 - // gleeunit test functions end in `_test` 8 - pub fn hello_world_test() { 9 - let name = "Joe" 10 - let greeting = "Hello, " <> name <> "!" 11 - 12 - assert greeting == "Hello, Joe!" 13 - }