diff --git a/go.mod b/go.mod index 5f90c83..e8bc5ba 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( github.com/charmbracelet/x/ansi v0.8.0 github.com/charmbracelet/x/term v0.2.1 // indirect github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect diff --git a/go.sum b/go.sum index ec3d8c0..448b055 100644 --- a/go.sum +++ b/go.sum @@ -40,6 +40,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= +github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b h1:EY/KpStFl60qA17CptGXhwfZ+k1sFNJIUNR8DdbcuUk= +github.com/gomarkdown/markdown v0.0.0-20250311123330-531bef5e742b/go.mod h1:JDGcbDT52eL4fju3sZ4TeHGsQwhG9nbDV21aMyhwPoA= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= diff --git a/internal/webserver/webserver.go b/internal/webserver/webserver.go new file mode 100644 index 0000000..0e52515 --- /dev/null +++ b/internal/webserver/webserver.go @@ -0,0 +1,877 @@ +package webserver + +import ( + "fmt" + "io" + "log/slog" + "net/http" + "strings" + + "github.com/gomarkdown/markdown" + "github.com/gomarkdown/markdown/html" + "github.com/gomarkdown/markdown/parser" + + "github.com/kyren223/eko/embeds" +) + +func ServeEkoWebsite() { + slog.Info("starting web-server...") + + http.HandleFunc("/style.css", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/css") + w.Write([]byte(css)) + }) + + http.HandleFunc("/terms-of-service", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + tos := embeds.TermsOfService.Load().(string) + tos = strings.ReplaceAll(tos, "Privacy Policy", "[Privacy Policy](../privacy-policy)") + html := mdToHTML(tos) + writeLegalLayoutHtml(w, html) + }) + + http.HandleFunc("/privacy-policy", func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/html") + privacy := embeds.PrivacyPolicy.Load().(string) + privacy = strings.ReplaceAll(privacy, "Terms of Service", "[Terms of Service](../terms-of-service)") + html := mdToHTML(privacy) + writeLegalLayoutHtml(w, html) + }) + + slog.Error("webserver error", "error", http.ListenAndServe(":7443", nil)) +} + +func writeLegalLayoutHtml(w io.Writer, html string) { + fmt.Fprintf(w, ` + + + + + Terms of Service + + + +
+
+ %s +
+
+ + + `, html) +} + +func mdToHTML(md string) string { + // create markdown parser with extensions + extensions := parser.CommonExtensions | parser.AutoHeadingIDs | parser.NoEmptyLineBeforeBlock + p := parser.NewWithExtensions(extensions) + doc := p.Parse([]byte(md)) + + // create HTML renderer with extensions + htmlFlags := html.CommonFlags | html.HrefTargetBlank + opts := html.RendererOptions{Flags: htmlFlags} + renderer := html.NewRenderer(opts) + + return string(markdown.Render(doc, renderer)) +} + +// Yes this is horrible, but it works and I don't like web dev :) +// Feel free to improve it and open a PR +// Probably most of these are unncessary, I just copy pasted it from my website css +var css = ` +.sl-markdown-content + :not(.expressive-code *) + + :not(a, strong, em, del, span, input, code, br) + + :not(a, strong, em, del, span, input, code, br, :where(.not-content *)) { + margin-top: 1rem; +} + +/* Headings after non-headings have more spacing. */ +.sl-markdown-content + :not(h1, h2, h3, h4, h5, h6) + + :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *)) { + margin-top: 1.5em; +} + +.sl-markdown-content li + li:not(:where(.not-content *)), +.sl-markdown-content dt + dt:not(:where(.not-content *)), +.sl-markdown-content dt + dd:not(:where(.not-content *)), +.sl-markdown-content dd + dd:not(:where(.not-content *)) { + margin-top: 0.25rem; +} + +.sl-markdown-content li:not(:where(.not-content *)) { + overflow-wrap: anywhere; +} + +.sl-markdown-content + li + > :last-child:not(li, ul, ol):not( + a, + strong, + em, + del, + span, + input, + :where(.not-content *) + ) { + margin-bottom: 1.25rem; +} + +.sl-markdown-content dt:not(:where(.not-content *)) { + font-weight: 700; +} +.sl-markdown-content dd:not(:where(.not-content *)) { + padding-inline-start: 1rem; +} + +.sl-markdown-content :is(h1, h2, h3, h4, h5, h6):not(:where(.not-content *)) { + color: var(--sl-color-white); + line-height: var(--sl-line-height-headings); + font-weight: 600; + font-size: 10000%; +} + +.sl-markdown-content + :is(img, picture, video, canvas, svg, iframe):not(:where(.not-content *)) { + display: block; + max-width: 100%; + height: auto; +} + +.sl-markdown-content h1:not(:where(.not-content *)) { + font-size: var(--sl-text-h1); +} +.sl-markdown-content h2:not(:where(.not-content *)) { + font-size: var(--sl-text-h2); +} +.sl-markdown-content h3:not(:where(.not-content *)) { + font-size: var(--sl-text-h3); +} +.sl-markdown-content h4:not(:where(.not-content *)) { + font-size: var(--sl-text-h4); +} +.sl-markdown-content h5:not(:where(.not-content *)) { + font-size: var(--sl-text-h5); +} +.sl-markdown-content h6:not(:where(.not-content *)) { + font-size: var(--sl-text-h6); +} + +.sl-markdown-content a:not(:where(.not-content *)) { + color: var(--sl-color-text-accent); +} +.sl-markdown-content a:hover:not(:where(.not-content *)) { + color: var(--sl-color-white); +} + +.sl-markdown-content code:not(:where(.not-content *)) { + background-color: var(--sl-color-bg-inline-code); + margin-block: -0.125rem; + padding: 0.125rem 0.375rem; + font-size: var(--sl-text-code-sm); +} +.sl-markdown-content :is(h1, h2, h3, h4, h5, h6) code { + font-size: inherit; +} + +.sl-markdown-content pre:not(:where(.not-content *)) { + border: 1px solid var(--sl-color-gray-5); + padding: 0.75rem 1rem; + font-size: var(--sl-text-code); + tab-size: 2; +} + +.sl-markdown-content pre code:not(:where(.not-content *)) { + all: unset; + font-family: var(--__sl-font-mono); +} + +.sl-markdown-content blockquote:not(:where(.not-content *)) { + border-inline-start: 1px solid var(--sl-color-gray-5); + padding-inline-start: 1rem; +} + +/* Table styling */ +.sl-markdown-content table:not(:where(.not-content *)) { + display: block; + overflow: auto; + border-spacing: 0; +} +.sl-markdown-content :is(th, td):not(:where(.not-content *)) { + border-bottom: 1px solid var(--sl-color-gray-5); + padding: 0.5rem 1rem; + /* Align text to the top of the row in multiline tables. */ + vertical-align: baseline; +} +.sl-markdown-content + :is(th:first-child, td:first-child):not(:where(.not-content *)) { + padding-inline-start: 0; +} +.sl-markdown-content + :is(th:last-child, td:last-child):not(:where(.not-content *)) { + padding-inline-end: 0; +} +.sl-markdown-content th:not(:where(.not-content *)) { + color: var(--sl-color-white); + font-weight: 600; +} +/* Align headings to the start of the line unless set by the align attribute. */ +.sl-markdown-content th:not([align]):not(:where(.not-content *)) { + text-align: start; +} +/* s,
s, and
s inside asides */ +.sl-markdown-content + .starlight-aside + :is(th, td, hr, blockquote):not(:where(.not-content *)) { + border-color: var(--sl-color-gray-4); +} +@supports ( + border-color: + color-mix(in srgb, var(--sl-color-asides-text-accent) 30%, transparent) +) { + .sl-markdown-content + .starlight-aside + :is(th, td, hr, blockquote):not(:where(.not-content *)) { + border-color: color-mix( + in srgb, + var(--sl-color-asides-text-accent) 30%, + transparent + ); + } +} + +/* inside asides */ +@supports ( + border-color: + color-mix(in srgb, var(--sl-color-asides-text-accent) 12%, transparent) +) { + .sl-markdown-content .starlight-aside code:not(:where(.not-content *)) { + background-color: color-mix( + in srgb, + var(--sl-color-asides-text-accent) 12%, + transparent + ); + } +} + +.sl-markdown-content hr:not(:where(.not-content *)) { + border: 0; + border-bottom: 1px solid var(--sl-color-hairline); +} + +/*
and styles */ +.sl-markdown-content details:not(:where(.not-content *)) { + --sl-details-border-color: var(--sl-color-gray-5); + --sl-details-border-color--hover: var(--sl-color-text-accent); + + border-inline-start: 2px solid var(--sl-details-border-color); + padding-inline-start: 1rem; +} +.sl-markdown-content details:not([open]):hover:not(:where(.not-content *)), +.sl-markdown-content details:has(> summary:hover):not(:where(.not-content *)) { + border-color: var(--sl-details-border-color--hover); +} +.sl-markdown-content summary:not(:where(.not-content *)) { + color: var(--sl-color-white); + cursor: pointer; + display: block; /* Needed to hide the default marker in some browsers. */ + font-weight: 600; + /* Expand the outline so that the marker cannot distort it. */ + margin-inline-start: -0.5rem; + padding-inline-start: 0.5rem; +} +.sl-markdown-content details[open] > summary:not(:where(.not-content *)) { + margin-bottom: 1rem; +} + +/* marker styles */ +.sl-markdown-content summary:not(:where(.not-content *))::marker, +.sl-markdown-content + summary:not(:where(.not-content *))::-webkit-details-marker { + display: none; +} +.sl-markdown-content summary:not(:where(.not-content *))::before { + --sl-details-marker-size: 1.25rem; + + background-color: currentColor; + content: ""; + display: inline-block; + height: var(--sl-details-marker-size); + width: var(--sl-details-marker-size); + margin-inline: calc((var(--sl-details-marker-size) / 4) * -1) 0.25rem; + vertical-align: middle; + -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A"); + mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M14.8 11.3 10.6 7a1 1 0 1 0-1.4 1.5l3.5 3.5-3.5 3.5a1 1 0 0 0 0 1.4 1 1 0 0 0 .7.3 1 1 0 0 0 .7-.3l4.2-4.2a1 1 0 0 0 0-1.4Z'/%3E%3C/svg%3E%0A"); + -webkit-mask-repeat: no-repeat; + mask-repeat: no-repeat; +} +@media (prefers-reduced-motion: no-preference) { + .sl-markdown-content summary:not(:where(.not-content *))::before { + transition: transform 0.2s ease-in-out; + } +} +.sl-markdown-content + details[open] + > summary:not(:where(.not-content *))::before { + transform: rotateZ(90deg); +} +[dir="rtl"] .sl-markdown-content summary:not(:where(.not-content *))::before, +.sl-markdown-content [dir="rtl"] summary:not(:where(.not-content *))::before { + transform: rotateZ(180deg); +} +/* with only a paragraph automatically added when using MDX */ +.sl-markdown-content summary:not(:where(.not-content *)) p:only-child { + display: inline; +} + +/*
styles inside asides */ +.sl-markdown-content .starlight-aside details:not(:where(.not-content *)) { + --sl-details-border-color: var(--sl-color-asides-border); + --sl-details-border-color--hover: var(--sl-color-asides-text-accent); +} + + +.starlight-aside { + padding: 1rem; + border-inline-start: 0.25rem solid var(--sl-color-asides-border); + color: var(--sl-color-white); +} +.starlight-aside--note { + --sl-color-asides-text-accent: var(--sl-color-blue-high); + --sl-color-asides-border: var(--sl-color-blue); + background-color: var(--sl-color-blue-low); +} +.starlight-aside--tip { + --sl-color-asides-text-accent: var(--sl-color-purple-high); + --sl-color-asides-border: var(--sl-color-purple); + background-color: var(--sl-color-purple-low); +} +.starlight-aside--caution { + --sl-color-asides-text-accent: var(--sl-color-orange-high); + --sl-color-asides-border: var(--sl-color-orange); + background-color: var(--sl-color-orange-low); +} +.starlight-aside--danger { + --sl-color-asides-text-accent: var(--sl-color-red-high); + --sl-color-asides-border: var(--sl-color-red); + background-color: var(--sl-color-red-low); +} + +.starlight-aside__title { + display: flex; + gap: 0.5rem; + align-items: center; + font-size: var(--sl-text-h5); + font-weight: 600; + line-height: var(--sl-line-height-headings); + color: var(--sl-color-asides-text-accent); +} + +.starlight-aside__icon { + font-size: 1.333em; + width: 1em; + height: 1em; +} + +.starlight-aside__title + .starlight-aside__content { + margin-top: 0.5rem; +} + +.starlight-aside__content a { + color: var(--sl-color-asides-text-accent); +} + +:root, +::backdrop { + /* Colors (dark mode) */ + --sl-color-white: hsl(0, 0%, 100%); /* “white” */ + --sl-color-gray-1: hsl(224, 20%, 94%); + --sl-color-gray-2: hsl(224, 6%, 77%); + --sl-color-gray-3: hsl(224, 6%, 56%); + --sl-color-gray-4: hsl(224, 7%, 36%); + --sl-color-gray-5: hsl(224, 10%, 23%); + --sl-color-gray-6: hsl(224, 14%, 16%); + --sl-color-black: hsl(224, 10%, 10%); + + --sl-hue-orange: 41; + --sl-color-orange-low: hsl(var(--sl-hue-orange), 39%, 22%); + --sl-color-orange: hsl(var(--sl-hue-orange), 82%, 63%); + --sl-color-orange-high: hsl(var(--sl-hue-orange), 82%, 87%); + --sl-hue-green: 101; + --sl-color-green-low: hsl(var(--sl-hue-green), 39%, 22%); + --sl-color-green: hsl(var(--sl-hue-green), 82%, 63%); + --sl-color-green-high: hsl(var(--sl-hue-green), 82%, 80%); + --sl-hue-blue: 234; + --sl-color-blue-low: hsl(var(--sl-hue-blue), 54%, 20%); + --sl-color-blue: hsl(var(--sl-hue-blue), 100%, 60%); + --sl-color-blue-high: hsl(var(--sl-hue-blue), 100%, 87%); + --sl-hue-purple: 281; + --sl-color-purple-low: hsl(var(--sl-hue-purple), 39%, 22%); + --sl-color-purple: hsl(var(--sl-hue-purple), 82%, 63%); + --sl-color-purple-high: hsl(var(--sl-hue-purple), 82%, 89%); + --sl-hue-red: 339; + --sl-color-red-low: hsl(var(--sl-hue-red), 39%, 22%); + --sl-color-red: hsl(var(--sl-hue-red), 82%, 63%); + --sl-color-red-high: hsl(var(--sl-hue-red), 82%, 87%); + + --sl-color-accent-low: hsl(224, 54%, 20%); + --sl-color-accent: hsl(224, 100%, 60%); + --sl-color-accent-high: hsl(224, 100%, 85%); + + --sl-color-text: var(--sl-color-gray-2); + --sl-color-text-accent: var(--sl-color-accent-high); + --sl-color-text-invert: var(--sl-color-accent-low); + --sl-color-bg: var(--sl-color-black); + --sl-color-bg-nav: var(--sl-color-gray-6); + --sl-color-bg-sidebar: var(--sl-color-gray-6); + --sl-color-bg-inline-code: var(--sl-color-gray-5); + --sl-color-bg-accent: var(--sl-color-accent-high); + --sl-color-hairline-light: var(--sl-color-gray-5); + --sl-color-hairline: var(--sl-color-gray-6); + --sl-color-hairline-shade: var(--sl-color-black); + + --sl-color-backdrop-overlay: hsla(223, 13%, 10%, 0.66); + + /* Shadows (dark mode) */ + --sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, 0.12), + 0px 2px 1px hsla(0, 0%, 0%, 0.24); + --sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, 0.08), + 0px 5px 2px hsla(0, 0%, 0%, 0.08), 0px 3px 2px hsla(0, 0%, 0%, 0.12), + 0px 1px 1px hsla(0, 0%, 0%, 0.15); + --sl-shadow-lg: 0px 25px 7px hsla(0, 0%, 0%, 0.03), + 0px 16px 6px hsla(0, 0%, 0%, 0.1), 0px 9px 5px hsla(223, 13%, 10%, 0.33), + 0px 4px 4px hsla(0, 0%, 0%, 0.75), 0px 4px 2px hsla(0, 0%, 0%, 0.25); + + /* Text size and line height */ + --sl-text-2xs: 0.75rem; /* 12px */ + --sl-text-xs: 0.8125rem; /* 13px */ + --sl-text-sm: 0.875rem; /* 14px */ + --sl-text-base: 1rem; /* 16px */ + --sl-text-lg: 1.125rem; /* 18px */ + --sl-text-xl: 1.25rem; /* 20px */ + --sl-text-2xl: 1.5rem; /* 24px */ + --sl-text-3xl: 1.8125rem; /* 29px */ + --sl-text-4xl: 2.1875rem; /* 35px */ + --sl-text-5xl: 2.625rem; /* 42px */ + --sl-text-6xl: 4rem; /* 64px */ + + --sl-text-body: var(--sl-text-base); + --sl-text-body-sm: var(--sl-text-xs); + --sl-text-code: var(--sl-text-sm); + --sl-text-code-sm: var(--sl-text-xs); + --sl-text-h1: var(--sl-text-4xl); + --sl-text-h2: var(--sl-text-3xl); + --sl-text-h3: var(--sl-text-2xl); + --sl-text-h4: var(--sl-text-xl); + --sl-text-h5: var(--sl-text-lg); + + --sl-line-height: 1.75; + --sl-line-height-headings: 1.2; + + --sl-font-system: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + --sl-font-system-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + --__sl-font: var(--sl-font, var(--sl-font-system)), var(--sl-font-system); + --__sl-font-mono: var(--sl-font-mono, var(--sl-font-system-mono)), + var(--sl-font-system-mono); + + /** Key layout values */ + --sl-nav-height: 3.5rem; + --sl-nav-pad-x: 1rem; + --sl-nav-pad-y: 0.75rem; + --sl-mobile-toc-height: 3rem; + --sl-sidebar-width: 18.75rem; + --sl-sidebar-pad-x: 1rem; + --sl-content-width: 45rem; + --sl-content-pad-x: 1rem; + --sl-menu-button-size: 2rem; + --sl-nav-gap: var(--sl-content-pad-x); + /* Offset required to show outline inside an element instead of round the outside */ + --sl-outline-offset-inside: -0.1875rem; + + /* Global z-index values */ + --sl-z-index-toc: 4; + --sl-z-index-menu: 5; + --sl-z-index-navbar: 10; + --sl-z-index-skiplink: 20; +} + +:root[data-theme="light"], +[data-theme="light"] ::backdrop { + /* Colours (light mode) */ + --sl-color-white: hsl(224, 10%, 10%); + --sl-color-gray-1: hsl(224, 14%, 16%); + --sl-color-gray-2: hsl(224, 10%, 23%); + --sl-color-gray-3: hsl(224, 7%, 36%); + --sl-color-gray-4: hsl(224, 6%, 56%); + --sl-color-gray-5: hsl(224, 6%, 77%); + --sl-color-gray-6: hsl(224, 20%, 94%); + --sl-color-gray-7: hsl(224, 19%, 97%); + --sl-color-black: hsl(0, 0%, 100%); + + --sl-color-orange-high: hsl(var(--sl-hue-orange), 80%, 25%); + --sl-color-orange: hsl(var(--sl-hue-orange), 90%, 60%); + --sl-color-orange-low: hsl(var(--sl-hue-orange), 90%, 88%); + --sl-color-green-high: hsl(var(--sl-hue-green), 80%, 22%); + --sl-color-green: hsl(var(--sl-hue-green), 90%, 46%); + --sl-color-green-low: hsl(var(--sl-hue-green), 85%, 90%); + --sl-color-blue-high: hsl(var(--sl-hue-blue), 80%, 30%); + --sl-color-blue: hsl(var(--sl-hue-blue), 90%, 60%); + --sl-color-blue-low: hsl(var(--sl-hue-blue), 88%, 90%); + --sl-color-purple-high: hsl(var(--sl-hue-purple), 90%, 30%); + --sl-color-purple: hsl(var(--sl-hue-purple), 90%, 60%); + --sl-color-purple-low: hsl(var(--sl-hue-purple), 80%, 90%); + --sl-color-red-high: hsl(var(--sl-hue-red), 80%, 30%); + --sl-color-red: hsl(var(--sl-hue-red), 90%, 60%); + --sl-color-red-low: hsl(var(--sl-hue-red), 80%, 90%); + + --sl-color-accent-high: hsl(234, 80%, 30%); + --sl-color-accent: hsl(234, 90%, 60%); + --sl-color-accent-low: hsl(234, 88%, 90%); + + --sl-color-text-accent: var(--sl-color-accent); + --sl-color-text-invert: var(--sl-color-black); + --sl-color-bg-nav: var(--sl-color-gray-7); + --sl-color-bg-sidebar: var(--sl-color-bg); + --sl-color-bg-inline-code: var(--sl-color-gray-6); + --sl-color-bg-accent: var(--sl-color-accent); + --sl-color-hairline-light: var(--sl-color-gray-6); + --sl-color-hairline-shade: var(--sl-color-gray-6); + + --sl-color-backdrop-overlay: hsla(225, 9%, 36%, 0.66); + + /* Shadows (light mode) */ + --sl-shadow-sm: 0px 1px 1px hsla(0, 0%, 0%, 0.06), + 0px 2px 1px hsla(0, 0%, 0%, 0.06); + --sl-shadow-md: 0px 8px 4px hsla(0, 0%, 0%, 0.03), + 0px 5px 2px hsla(0, 0%, 0%, 0.03), 0px 3px 2px hsla(0, 0%, 0%, 0.06), + 0px 1px 1px hsla(0, 0%, 0%, 0.06); + --sl-shadow-lg: 0px 25px 7px rgba(0, 0, 0, 0.01), + 0px 16px 6px hsla(0, 0%, 0%, 0.03), 0px 9px 5px hsla(223, 13%, 10%, 0.08), + 0px 4px 4px hsla(0, 0%, 0%, 0.16), 0px 4px 2px hsla(0, 0%, 0%, 0.04); +} + +@media (min-width: 50em) { + :root { + --sl-nav-height: 4rem; + --sl-nav-pad-x: 1.5rem; + --sl-text-h1: var(--sl-text-5xl); + --sl-text-h2: var(--sl-text-4xl); + --sl-text-h3: var(--sl-text-3xl); + --sl-text-h4: var(--sl-text-2xl); + } +} + +@media (min-width: 72rem) { + :root { + --sl-content-pad-x: 1.5rem; + --sl-mobile-toc-height: 0rem; + } +} + +*, +*::before, +*::after { + box-sizing: border-box; +} + +* { + margin: 0; +} + +html { + color-scheme: dark; + accent-color: var(--sl-color-accent); +} + +html[data-theme="light"] { + color-scheme: light; +} + +body { + font-family: var(--__sl-font); + line-height: var(--sl-line-height); + -webkit-font-smoothing: antialiased; + color: var(--sl-color-text); + background-color: var(--sl-color-bg); +} + +input, +button, +textarea, +select { + font: inherit; +} + +p, +h1, +h2, +h3, +h4, +h5, +h6, +code { + overflow-wrap: anywhere; +} + +code { + font-family: var(--__sl-font-mono); +} + +:root { + --astro-code-color-text: var(--sl-color-white); + --astro-code-color-background: var(--sl-color-gray-6); + --astro-code-token-constant: var(--sl-color-blue-high); + --astro-code-token-string: var(--sl-color-green-high); + --astro-code-token-comment: var(--sl-color-gray-2); + --astro-code-token-keyword: var(--sl-color-purple-high); + --astro-code-token-parameter: var(--sl-color-red-high); + --astro-code-token-function: var(--sl-color-red-high); + --astro-code-token-string-expression: var(--sl-color-green-high); + --astro-code-token-punctuation: var(--sl-color-gray-2); + --astro-code-token-link: var(--sl-color-blue-high); +} + +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +.sl-hidden { + display: none; +} +.sl-flex { + display: flex; +} +.sl-block { + display: block; +} +@media (min-width: 50rem) { + .md\:sl-hidden { + display: none; + } + .md\:sl-flex { + display: flex; + } + .md\:sl-block { + display: block; + } +} +@media (min-width: 72rem) { + .lg\:sl-hidden { + display: none; + } + .lg\:sl-flex { + display: flex; + } + .lg\:sl-block { + display: block; + } +} +[data-theme="light"] .light\:sl-hidden { + display: none; +} +[data-theme="dark"] .dark\:sl-hidden { + display: none; +} + +/* +Flip an element around the y-axis when in an RTL context. +Primarily useful for things where we can’t rely on writing direction like icons. + + + +In a LTR context: → In a RTL context: ← +*/ +[dir="rtl"] .rtl\:flip:not(:where([dir="rtl"] [dir="ltr"] *)) { + transform: matrix(-1, 0, 0, 1, 0, 0); +} + +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-Thin.woff2") format("woff2"); + font-weight: 100; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-ThinItalic.woff2") format("woff2"); + font-weight: 100; + font-style: italic; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-ExtraLight.woff2") format("woff2"); + font-weight: 200; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-ExtraLightItalic.woff2") format("woff2"); + font-weight: 200; + font-style: italic; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-Light.woff2") format("woff2"); + font-weight: 300; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-LightItalic.woff2") format("woff2"); + font-weight: 300; + font-style: italic; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-Regular.woff2") format("woff2"); + font-weight: 400; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-Italic.woff2") format("woff2"); + font-weight: 400; + font-style: italic; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-Medium.woff2") format("woff2"); + font-weight: 500; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-MediumItalic.woff2") format("woff2"); + font-weight: 500; + font-style: italic; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-SemiBold.woff2") format("woff2"); + font-weight: 600; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-SemiBoldItalic.woff2") format("woff2"); + font-weight: 600; + font-style: italic; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-Bold.woff2") format("woff2"); + font-weight: 700; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-BoldItalic.woff2") format("woff2"); + font-weight: 700; + font-style: italic; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-ExtraBold.woff2") format("woff2"); + font-weight: 800; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-ExtraBoldItalic.woff2") format("woff2"); + font-weight: 800; + font-style: italic; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-ExtraBold.woff2") format("woff2"); + font-weight: 900; + font-style: normal; +} +@font-face { + font-family: "Jetbrains Mono"; + src: url("/fonts/JetBrainsMono-ExtraBoldItalic.woff2") format("woff2"); + font-weight: 900; + font-style: italic; +} + +@font-face { + font-family: 'Monocraft'; + src: url('/fonts/Monocraft.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} + +:root { + --primary: #54d7a9; + --secondary: #9b7eca; + --accent: #7ed4fb; + --extra: #8fcc75; + --text: #ffffff; + --alt: #999999; + --background: #000000; + --font: "Jetbrains Mono", monospace; +} + +* { + margin: 0; + padding: 0; +} + +html { + background-color: var(--background); + color: var(--text); + font-family: var(--font); +} + +body { + zoom: 1; +} + +h1, +h2, +h3 { + font-size: 1rem; + font-weight: bold; +} + +a { + font-weight: 300; +} +a:link, +a:visited, +a:active { + background-color: transparent; + text-decoration: none; + color: var(--secondary); +} +a:hover { + transition-duration: 0.2s; + transition-property: color, text-decoration; + text-decoration: underline; + color: var(--accent); +} + +main { + display: block; + position: absolute; + padding-top: 10%; + padding-bottom: 10%; + left: 50%; + transform: translate(-50%, 0%); + background: var(--background); + font-weight: 300; +} +`