commit d9858adf16e319f08674255e9c0862f34027507b Author: Hellings Date: Fri Oct 23 08:53:07 2026 -0400 Initial commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..76e888b --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# @muddle/amezing + +Port of the Ametrine theme (by Daudix) as a muddle module. \ No newline at end of file diff --git a/components/alert.spug b/components/alert.spug new file mode 100644 index 0000000..40b4184 --- /dev/null +++ b/components/alert.spug @@ -0,0 +1,8 @@ +props + type: 'note' | 'tip' | 'important' | 'warning' | 'caution' + content?: string | (() => string | Promise) +blockquote(class='markdown-alert markdown-alert-' + type) + if typeof content === 'function' + != await content() + else + = content ?? '' diff --git a/components/article-list.spug b/components/article-list.spug new file mode 100644 index 0000000..01c62d9 --- /dev/null +++ b/components/article-list.spug @@ -0,0 +1,29 @@ +props + pages: { title: string; href: string; description?: string; date?: string; updated?: string; banner?: string; bannerStyle?: string; articleClass?: string; draft?: boolean; categories?: { name: string; href: string; description?: string }[] }[] +div.article-list + each page in pages + article(class=page.articleClass style=page.bannerStyle) + if page.banner + a.open-post(href=page.href aria-label=page.title) + img.has-alpha.no-hover(src=page.banner decoding="async" loading="lazy") + div + a(href=page.href) + h3= page.title + if page.description + div.description + != page.description + div.article-details(data-slot="article-details") + div.items + if page.date + div.item + i.ph.ph-calendar(aria-hidden="true") + time(datetime=page.date pubdate)= page.date + if page.updated + div.item + i.ph.ph-clock-clockwise(aria-hidden="true") + time(datetime=page.updated pubdate)= page.updated + if page.categories + div.item + i.ph.ph-book-bookmark(aria-hidden="true") + each category in page.categories + a(href=category.href title=category.description)= category.name diff --git a/components/article-nav.spug b/components/article-nav.spug new file mode 100644 index 0000000..295512e --- /dev/null +++ b/components/article-nav.spug @@ -0,0 +1,14 @@ +props + previousHref?: string + previousTitle?: string + nextHref?: string + nextTitle?: string +nav.article-nav + if previousHref + a.article-nav-item.prev(href=previousHref) + div.nav-arrow Previous + span.article-title= previousTitle + if nextHref + a.article-nav-item.next(href=nextHref) + div.nav-arrow Next + span.article-title= nextTitle diff --git a/components/article.spug b/components/article.spug new file mode 100644 index 0000000..083afb9 --- /dev/null +++ b/components/article.spug @@ -0,0 +1,31 @@ +props + title: string + description?: string +article + h1 + = title + div.article-details(data-slot="article-details") + div.items + if date + div.item + i.ph.ph-calendar(aria-hidden="true") + time(datetime=isoDate pubdate)= date + if updated + div.item + i.ph.ph-clock-clockwise(aria-hidden="true") + time(datetime=isoUpdated pubdate)= updated + if authors + div.item + i.ph.ph-users(aria-hidden="true") + span= authors + if readingTime + div.item + i.ph.ph-clock-countdown(aria-hidden="true") + span= readingTime + ' min read' + if body + div.article-body + != body + if notes + ul.statements + each statement in notes + li= statement diff --git a/components/audio-button.spug b/components/audio-button.spug new file mode 100644 index 0000000..4980a7c --- /dev/null +++ b/components/audio-button.spug @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/components/audio.spug b/components/audio.spug new file mode 100644 index 0000000..1c0abae --- /dev/null +++ b/components/audio.spug @@ -0,0 +1,7 @@ +props + url: string + name?: string +button.audio(type="button" data-audio=url) + if name + span= name + i.ph.ph-speaker-high(aria-hidden="true") diff --git a/components/banner.spug b/components/banner.spug new file mode 100644 index 0000000..91a2761 --- /dev/null +++ b/components/banner.spug @@ -0,0 +1,7 @@ +props + src: string + alt?: string + bannerPixels?: boolean +div#banner(data-slot="banner" class=bannerPixels ? 'pixels' : '') + div + img.has-alpha.no-hover(src=src decoding="async" loading="lazy" alt=alt ?? '') diff --git a/components/base.spug b/components/base.spug new file mode 100644 index 0000000..27527e4 --- /dev/null +++ b/components/base.spug @@ -0,0 +1,133 @@ +doctype html +html(lang="en" style=accentStyle ?? '') + head + meta(charset="UTF-8") + meta(name="viewport" content="width=device-width, initial-scale=1") + meta(name="description" content=description ?? '') + title= title + link(rel="stylesheet" href="/_muddle/amezing.css") + link(rel="stylesheet" href="/_muddle/styles.css") + link(rel="stylesheet" href="https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/style.css") + body + a#skip-to-content.link(href="#main-content" tabindex="0") Skip to content + header#site-header.firefox-unblur + ul#site-sidebar-controls + li#expand-sidebar-li + input#expand-sidebar.visually-hidden(type="checkbox" name="expand-sidebar" autocomplete="off") + label(for="expand-sidebar" title="Toggle sidebar") + i.ph.ph-sidebar(aria-hidden="true") + ul#site-sidebar-modes(role="radiogroup") + li + input#mode-nav.visually-hidden(type="radio" name="mode" checked) + label(for="mode-nav" title="Navigation mode") + i.ph.ph-compass(aria-hidden="true") + li + input#mode-meta.visually-hidden(type="radio" name="mode") + label(for="mode-meta" title="Meta mode") + i.ph.ph-info(aria-hidden="true") + span.title= title + ul.buttons + li + details.closable + summary(title="Feed") + i.ph.ph-rss-simple(aria-hidden="true") + ul.dropdown + li + a(href="/atom.xml") Atom + li + a(href="/rss.xml") RSS + if searchHref + li + button#search(title="Search" aria-haspopup="dialog") + i.ph.ph-magnifying-glass(aria-hidden="true") + div#site-sidebar + a#site-sidebar-header(href="#top" class=logo ? 'has-icon' : '') + if logo + img(width="36" height="36" class="has-alpha no-hover" src=logo alt="") + else + i.ph.ph-arrow-line-up(aria-hidden="true") + div + span= siteTitle ?? title + span Back to top + div#site-sidebar-content + nav#sidebar-nav.overshoot + ul + li + a(href="/" aria-current=(currentPath ?? '/') === '/' ? 'page' : '') + i.ph.ph-house(aria-hidden="true") + = siteTitle ?? title + each entry in nav + if entry.children + li + details.closable(open=entry.open) + summary + = entry.name + div.line + ul.dropdown + each child in entry.children + li + a(href=child.href aria-current=child.active ? 'page' : '' class=child.external ? 'external' : '') + if child.icon + i.ph.ph-(child.icon)(aria-hidden="true") + if child.label + span= child.label + else + span= child.name + else + li + a(href=entry.href aria-current=entry.active ? 'page' : '' class=(entry.external ?? entry.href.indexOf('http') === 0) ? 'external' : '') + if entry.icon + i.ph.ph-(entry.icon)(aria-hidden="true") + if entry.label + span= entry.label + else + span= entry.name + div#sidebar-meta.overshoot + if metaTitle ?? note + div.card + div.card-title + div.line + span.title= metaTitle ?? 'Meta' + div.line + if note + div.note= note + main#main-content + if content + != await content() + footer#site-footer + div + if copyright + p.copyright= copyright + if poweredBy ?? undefined + p= 'Powered by ' + a.link.external(href="https://www.getzola.org") Zola + = ' and ' + a.link.external(href="https://ametrine.daudix.one") Ametrine + if sourceHref ?? undefined + p + a.link.external(href=sourceHref) Source + = ' | Updated ' + timestamp + ' UTC' + if socials + ul.socials + each social in socials + li + a(href=social.href rel="me external") + i.ph.ph-(social.name)(aria-hidden="true") + if webrings + ul.webrings + each webring in webrings + li.webring + a.link.external(href=webring.href)= webring.name + a.link.previous(href=webring.previous title="Previous") + i.ph.ph-arrow-left + if webring.random + a.link.random(href=webring.random title="Random") + i.ph.ph-dice-three + a.link.next(href=webring.next title="Next") + i.ph.ph-arrow-right + if note + div.note= note + script(src="/_muddle/amezing/closable.js" defer) + script(src="/_muddle/amezing/paginator.js" defer) + script(src="/_muddle/amezing/deanchor.js" defer) + script(src="/_muddle/amezing/marquee.js" defer) diff --git a/components/comments.spug b/components/comments.spug new file mode 100644 index 0000000..6ac2b62 --- /dev/null +++ b/components/comments.spug @@ -0,0 +1,32 @@ +{%- set alert_icon = load_data(path="icons/phosphor/" ~ icon ~ ".svg", required=false) -%} +{%- if not alert_icon -%} + {%- set alert_icon = "info" -%} +{%- endif -%} +
+ +
+ {%- set comments = load_data(path="data/comments.toml", required=false) -%} + {%- if comments -%} + {%- for comment in comments -%} +
+
+ {{ comment.author }} + {{ comment.date }} +
+

{{ comment.content }}

+
+ {%- endfor -%} + {%- else -%} +
+
💭
+

No comments yet. Be the first to share your thoughts!

+
+ {%- endif -%} +
+
\ No newline at end of file diff --git a/components/crt.spug b/components/crt.spug new file mode 100644 index 0000000..4f17fdd --- /dev/null +++ b/components/crt.spug @@ -0,0 +1,11 @@ +props + fontSize?: string + label?: string + content?: string | (() => string | Promise) +pre.crt(style=fontSize ? 'font-size: ' + fontSize + ';' : '' role=label ? 'img' : false aria-label=label ? label : false) + code(aria-hidden=label ? false : true) + span + if typeof content === 'function' + != await content() + else + != content ?? '' diff --git a/components/deanchor.spug b/components/deanchor.spug new file mode 100644 index 0000000..0fea7f5 --- /dev/null +++ b/components/deanchor.spug @@ -0,0 +1 @@ +{{ text | striptags }} \ No newline at end of file diff --git a/components/emoji.spug b/components/emoji.spug new file mode 100644 index 0000000..f25fe29 --- /dev/null +++ b/components/emoji.spug @@ -0,0 +1,5 @@ +props + path: string + big?: boolean + title?: string +img.emoji(class=big ? 'big' : false src=path title=title ?? '' width="24" height="24") diff --git a/components/footnotes.spug b/components/footnotes.spug new file mode 100644 index 0000000..a99881a --- /dev/null +++ b/components/footnotes.spug @@ -0,0 +1,15 @@ +
+ {%- set footnotes = load_data(path="data/footnotes.toml", required=false) -%} + {%- if footnotes -%} +
+ {%- for footnote in footnotes -%} +
+ + {{ loop.index }} + +

{{ footnote.content }}

+
+ {%- endfor -%} +
+ {%- endif -%} +
\ No newline at end of file diff --git a/components/fuse.spug b/components/fuse.spug new file mode 100644 index 0000000..7f6c9f0 --- /dev/null +++ b/components/fuse.spug @@ -0,0 +1,4 @@ +
+ +
+
\ No newline at end of file diff --git a/components/icon.spug b/components/icon.spug new file mode 100644 index 0000000..c9414cb --- /dev/null +++ b/components/icon.spug @@ -0,0 +1,3 @@ +props + name: string +i.ph.ph-(name)(aria-hidden="true") diff --git a/components/image.spug b/components/image.spug new file mode 100644 index 0000000..268960b --- /dev/null +++ b/components/image.spug @@ -0,0 +1,22 @@ +props + src: string + alt?: string + url?: string + caption?: string + width?: number + height?: number + lazy?: boolean +if caption + figure + if url + a(href=url) + img(src=src alt=alt ?? '' decoding=lazy ? 'async' : false loading=lazy ? 'lazy' : false) + else + img(src=src alt=alt ?? '' decoding=lazy ? 'async' : false loading=lazy ? 'lazy' : false) + figcaption!= caption +else + if url + a(href=url) + img(src=src alt=alt ?? '' decoding=lazy ? 'async' : false loading=lazy ? 'lazy' : false) + else + img(src=src alt=alt ?? '' decoding=lazy ? 'async' : false loading=lazy ? 'lazy' : false) diff --git a/components/link.spug b/components/link.spug new file mode 100644 index 0000000..5e59bbb --- /dev/null +++ b/components/link.spug @@ -0,0 +1,14 @@ +props + name: string + href: string + icon?: string + external?: boolean + active?: boolean + afterActive?: boolean +li + a(href=href + (!external && !active && afterActive ? '#↓' : !external && !active ? '#↑' : '') aria-current=active ? 'page' : false class=external ? 'external' : false) + if icon + i.ph.ph-(icon)(aria-hidden="true") + else + i.icon.fallback= name.slice(0, 2) + = name diff --git a/components/mastodon.spug b/components/mastodon.spug new file mode 100644 index 0000000..09b7636 --- /dev/null +++ b/components/mastodon.spug @@ -0,0 +1,5 @@ +props + host: string + user: string + id: string +iframe.mastodon-embed(src='https://' + host + '/@' + user + '/' + id + '/embed') diff --git a/components/overflow-auto.spug b/components/overflow-auto.spug new file mode 100644 index 0000000..3667d16 --- /dev/null +++ b/components/overflow-auto.spug @@ -0,0 +1,9 @@ +props + overshoot?: boolean + overshootRow?: boolean + content?: string | (() => string | Promise) +div.overflow-auto(class=overshoot ? 'overshoot' : false class=overshootRow ? 'overshoot-row' : false) + if typeof content === 'function' + != await content() + else + != content ?? '' diff --git a/components/paginator.spug b/components/paginator.spug new file mode 100644 index 0000000..ead0a48 --- /dev/null +++ b/components/paginator.spug @@ -0,0 +1,32 @@ +props + counter: string + first?: string + previous?: string + next?: string + last?: string +nav.paginator + if first + a.paginator-item.first(href=first title="First page") + i.ph.ph-caret-line-left(aria-hidden="true") + else + span.paginator-item.first(aria-disabled="true") + i.ph.ph-caret-line-left(aria-hidden="true") + if previous + a.paginator-item.previous(href=previous rel="prev" title="Previous page") + i.ph.ph-caret-left(aria-hidden="true") + else + span.paginator-item.previous(aria-disabled="true") + i.ph.ph-caret-left(aria-hidden="true") + span.paginator-counter= counter + if next + a.paginator-item.next(href=next rel="next" title="Next page") + i.ph.ph-caret-right(aria-hidden="true") + else + span.paginator-item.next(aria-disabled="true") + i.ph.ph-caret-right(aria-hidden="true") + if last + a.paginator-item.last(href=last title="Last page") + i.ph.ph-caret-line-right(aria-hidden="true") + else + span.paginator-item.last(aria-disabled="true") + i.ph.ph-caret-line-right(aria-hidden="true") diff --git a/components/search-fuse.spug b/components/search-fuse.spug new file mode 100644 index 0000000..5c1bf7e --- /dev/null +++ b/components/search-fuse.spug @@ -0,0 +1,10 @@ +
+
+ + + + + +
+
+
\ No newline at end of file diff --git a/components/search-modal.spug b/components/search-modal.spug new file mode 100644 index 0000000..1a34e1e --- /dev/null +++ b/components/search-modal.spug @@ -0,0 +1,11 @@ +props + indexHref?: string +dialog#search-modal(data-slot="search-modal" closed ? aria-hidden="true") + div#search-modal-content + label(for="search-input" class="visually-hidden") Search + div + input#search-input(placeholder="Search" autocomplete="off" type="search") + div(title="Press / to search") + i.ph.ph-keyboard(aria-hidden="true") + div#search-results.overshoot + span#more-matches-text(hidden) More matches diff --git a/components/skip-to-content.spug b/components/skip-to-content.spug new file mode 100644 index 0000000..ffde958 --- /dev/null +++ b/components/skip-to-content.spug @@ -0,0 +1,4 @@ + + {%- set icon = load_data(path="icons/phosphor/" ~ "arrow-up-right" ~ ".svg", required=false) -%} + {%- if icon -%}{{ icon | safe }}{{ else }}Skip to content{{ end }} + \ No newline at end of file diff --git a/components/statement.spug b/components/statement.spug new file mode 100644 index 0000000..f72e5c9 --- /dev/null +++ b/components/statement.spug @@ -0,0 +1,11 @@ +props + kind: 'archive' | 'trigger' | 'disclaimer' + content?: string | (() => string | Promise) +div.statement-container(class=kind) + strong.title + i.ph.ph-(kind === 'archive' ? 'box-arrow-down' : 'warning')(aria-hidden="true") + span= kind === 'archive' ? 'Archived' : kind === 'trigger' ? 'Trigger warning' : 'Disclaimer' + if typeof content === 'function' + != await content() + else + != content ?? '' diff --git a/components/tag-chips.spug b/components/tag-chips.spug new file mode 100644 index 0000000..3a99494 --- /dev/null +++ b/components/tag-chips.spug @@ -0,0 +1,9 @@ +props + items: { name: string; href: string; count?: number }[] +ul#tag-chips + each item in items + li + a(href=item.href) + = item.name + if item.count + span.counter= item.count diff --git a/components/video.spug b/components/video.spug new file mode 100644 index 0000000..b0de71f --- /dev/null +++ b/components/video.spug @@ -0,0 +1,21 @@ +props + sources: { src: string, type?: string }[] + caption?: string + width?: number + height?: number + autoplay?: boolean + controls?: boolean + loop?: boolean + muted?: boolean + playsinline?: boolean + alt?: string +if caption + figure + video(preload="auto" aria-title=alt ?? '' width=width ?? false height=height ?? false autoplay=autoplay ?? false controls=controls ?? true loop=loop ?? false muted=muted ?? false playsinline=playsinline ?? false) + each source in sources + source(src=source.src type='video/' + (source.type ?? source.src.split('.').pop())) + figcaption!= caption +else + video(preload="auto" aria-title=alt ?? '' width=width ?? false height=height ?? false autoplay=autoplay ?? false controls=controls ?? true loop=loop ?? false muted=muted ?? false playsinline=playsinline ?? false) + each source in sources + source(src=source.src type='video/' + (source.type ?? source.src.split('.').pop())) diff --git a/components/vimeo.spug b/components/vimeo.spug new file mode 100644 index 0000000..b8befb8 --- /dev/null +++ b/components/vimeo.spug @@ -0,0 +1,4 @@ +props + id: string + autoplay?: boolean +iframe.vimeo-embed(src='https://player.vimeo.com/video/' + id + (autoplay ? '?autoplay=1' : '') allow="autoplay; fullscreen; picture-in-picture" allowfullscreen=true) diff --git a/components/youtube.spug b/components/youtube.spug new file mode 100644 index 0000000..909cc1e --- /dev/null +++ b/components/youtube.spug @@ -0,0 +1,5 @@ +props + id: string + autoplay?: boolean + start?: number +iframe.youtube-embed(src='https://www.youtube-nocookie.com/embed/' + id + (autoplay && start ? '?autoplay=1&start=' + start : autoplay ? '?autoplay=1' : start ? '?start=' + start : '') allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=true) diff --git a/fonts/commissioner.woff2 b/fonts/commissioner.woff2 new file mode 100644 index 0000000..f02d31b Binary files /dev/null and b/fonts/commissioner.woff2 differ diff --git a/fonts/commit-mono.woff2 b/fonts/commit-mono.woff2 new file mode 100644 index 0000000..f9ac1ee Binary files /dev/null and b/fonts/commit-mono.woff2 differ diff --git a/index.ts b/index.ts new file mode 100644 index 0000000..2c358c5 --- /dev/null +++ b/index.ts @@ -0,0 +1,161 @@ +/** + * Muddle integration of the Ametrine theme. + * + * Registers the theme's component set, exposes the compiled theme css and + * serves the vendored behavior scripts / self-hosted fonts. + */ +import { fileURLToPath } from "node:url"; +import { readFile } from "node:fs/promises"; +import type { + Integration, + Mounts, +} from "muddle"; +import type { Views } from "muddle/views"; + + +export interface AmezingAccentPreset { name: string; light: string; dark: string } + +/** Accent presets from ametrine/data/accent_presets.toml. */ +export const ACCENT_PRESETS: Record = { + red: { name: "red", light: "hsl(342.0779, 72%, 58%)", dark: "hsl(345.18988, 100%, 69%)" }, + orange: { name: "orange", light: "hsl(15.771428, 74%, 54%)", dark: "hsl(19.731544, 96%, 70%)" }, + yellow: { name: "yellow", light: "hsl(34.639175, 91%, 42%)", dark: "hsl(44.70588, 100%, 70%)" }, + green: { name: "green", light: "hsl(153.7815, 61%, 38%)", dark: "hsl(90, 59%, 66%)" }, + blue: { name: "blue", light: "hsl(192, 71%, 38%)", dark: "hsl(186.42857, 71%, 69%)" }, + purple: { name: "purple", light: "hsl(254.11766, 44%, 55%)", dark: "hsl(249.88234, 77%, 78%)" }, + teal: { name: "teal", light: "hsl(180, 65%, 35%)", dark: "hsl(183, 85%, 65%)" }, + pink: { name: "pink", light: "hsl(333.88236, 69%, 58%)", dark: "hsl(336.78634, 100%, 70%)" }, + cyan: { name: "cyan", light: "hsl(195, 71%, 35%)", dark: "hsl(193.41046, 72%, 70%)" }, + indigo: { name: "indigo", light: "hsl(232.05884, 61%, 42%)", dark: "hsl(234.61538, 73%, 71%)" }, + amber: { name: "amber", light: "hsl(38.57695, 94%, 44%)", dark: "hsl(40.35484, 100%, 72%)" }, + ochrea: { name: "ochrea", light: "hsl(45.75298, 95%, 45%)", dark: "hsl(46.09091, 100%, 78%)" }, + lime: { name: "lime", light: "hsl(86.25406, 60%, 42%)", dark: "hsl(86.84211, 93%, 71%)" }, + maroon: { name: "maroon", light: "hsl(321.81819, 26%, 41%)", dark: "hsl(322.10526, 66%, 66%)" }, + navy: { name: "navy", light: "hsl(216, 44%, 38%)", dark: "hsl(218, 69%, 69%)" }, + olive: { name: "olive", light: "hsl(81, 36%, 41%)", dark: "hsl(81, 64%, 65%)" }, + slate: { name: "slate", light: "hsl(212.53197, 15%, 44%)", dark: "hsl(212.90323, 61%, 72%)" }, +}; + +function parseHsl(value: string): [string, string, string] | undefined { + const match = /hsl\(\s*([\d.]+%?)\s*[, ]\s*([\d.]+%?)\s*[, ]\s*([\d.]+%?)\s*\)/.exec(value); + return match ? [match[1]!, match[2]!, match[3]!] : undefined; +} + +/** Builds the inline accent variables used by the theme, mirrors components/accent_color.html. */ +export function accentStyle(accent: string | [string, string?]): string { + const inputs = Array.isArray(accent) ? accent : [accent]; + const light = ACCENT_PRESETS[inputs[0] ?? ""]?.light ?? inputs[0]; + const dark = ACCENT_PRESETS[inputs[1] ?? inputs[0] ?? ""]?.dark ?? inputs[1] ?? ACCENT_PRESETS[inputs[0] ?? ""]?.dark ?? inputs[0]; + const lightHsl = parseHsl(light ?? ""); + const darkHsl = parseHsl(dark ?? ""); + if (!lightHsl || !darkHsl) throw new Error(`Unknown accent color: ${accent}`); + return `--accent-light-h:${lightHsl[0]};--accent-light-s:${lightHsl[1]};--accent-light-l:${lightHsl[2]};--accent-dark-h:${darkHsl[0]};--accent-dark-s:${darkHsl[1]};--accent-dark-l:${darkHsl[2]};`; +} + +const COMPONENTS = [ + "base", + "article", + "article-list", + "article-details", + "article-nav", + "paginator", + "alert", + "banner", + "icon", + "search-modal", + "tag-chips", + "audio", + "image", + "video", + "vimeo", + "youtube", + "mastodon", + "link", + "overflow-auto", + "crt", + "emoji", + "statement", + "comments", + "footnotes", + "skip-to-content", + "marquee", + "audio-button", + "deanchor", + "fuse", + "search-fuse", +] as const; + +function componentTemplates(): [string, string][] { + return COMPONENTS.map((name) => [`amezing-${name}`, fileURLToPath(new URL(`./components/${name}.spug`, import.meta.url))]); +} + +interface TemplateViews { + component(name: string, component: unknown): unknown; + engine?: { renderComponent(path: string, props: Record, context: { locals: Record; params: Record }): Promise }; +} + +function registerAll(views: TemplateViews, accent: string | undefined): void { + for (const [name, template] of componentTemplates()) { + views.component(name, { + async render(props: Record, context: { locals: Record; params: Record }) { + return views.engine!.renderComponent(template, { accentStyle: accent, ...props }, context); + }, + }); + } +} + +/** Registers the Ametrine components on the views registry. */ +export function registerAmezing(views: Views, options: { accentColor?: string | [string, string?] } = {}): void { + registerAll(views as unknown as Parameters[0], options.accentColor ? accentStyle(options.accentColor) : undefined); +} + +/** Compiled theme stylesheet (Citrine base + Ametrine layout). */ +export async function amezingStyles(): Promise { + const [theme, vars] = await Promise.all([ + readFile(new URL("./styles/ametrine.css", import.meta.url), "utf8"), + readFile(new URL("./styles/vars.css", import.meta.url), "utf8"), + ]); + return vars + "\n" + theme; +} + +export const amezingScripts: Record = { + "closable.js": "scripts/closable.js", + "paginator.js": "scripts/paginator.js", + "deanchor.js": "scripts/deanchor.js", + "marquee.js": "scripts/marquee.js", + "audio-button.js": "scripts/audio-button.js", + "fuse.js": "scripts/fuse.js", + "search-fuse.js": "scripts/search-fuse.js", +}; + +export const amezingFonts: Record = { + "/fonts/commissioner.woff2": "fonts/commissioner.woff2", + "/fonts/commit-mono.woff2": "fonts/commit-mono.woff2", +}; + +export function amezingMount(options: { accentColor?: string | [string, string?] } = {}): Mounts { + const accent = options.accentColor ? accentStyle(options.accentColor) : undefined; + return { + views: (views) => registerAll(views as unknown as Parameters[0], accent), + routes: (route) => { + for (const [routePath, file] of Object.entries(amezingScripts)) { + route("GET", `/_muddle/amezing/${routePath}`, async () => + new Response(await readFile(new URL(`./${file}`, import.meta.url), "utf8"), { + headers: { "content-type": "text/javascript; charset=utf-8" }, + })); + } + for (const [routePath, file] of Object.entries(amezingFonts)) { + route("GET", routePath, async () => + new Response(await readFile(new URL(`./${file}`, import.meta.url)), { + headers: { "content-type": "font/woff2" }, + })); + } + }, + styles: { "/_muddle/amezing.css": () => amezingStyles() }, + }; +} + +/** Convenience integration. */ +export function amezingIntegration(options: { accentColor?: string | [string, string?] } = {}): Integration { + return { name: "amezing", mounts: [amezingMount(options)] }; +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..f153655 --- /dev/null +++ b/package.json @@ -0,0 +1,7 @@ +{ + "name": "@muddle/amezing", + "version": "0.1.0", + "type": "module", + "exports": { ".": "./index.ts", "./styles": "./styles/ametrine.css" }, + "peerDependencies": { "muddle": "*" } +} diff --git a/sass/amezing.scss b/sass/amezing.scss new file mode 100644 index 0000000..224f2d1 --- /dev/null +++ b/sass/amezing.scss @@ -0,0 +1,7 @@ +/* +* Ametrine port for muddle. Combines the Citrine base with the Ametrine layout. +*/ + +@use "fonts"; +@use "citrine/style"; +@use "style" as ametrine; diff --git a/sass/citrine/abstracts/_functions.scss b/sass/citrine/abstracts/_functions.scss new file mode 100644 index 0000000..dc7e8f9 --- /dev/null +++ b/sass/citrine/abstracts/_functions.scss @@ -0,0 +1,5 @@ +// Converts px values to rem +// rpx(1) --> 0.0625rem +@function rpx($px-value) { + @return ($px-value / 16) + rem; +} diff --git a/sass/citrine/abstracts/_mixins.scss b/sass/citrine/abstracts/_mixins.scss new file mode 100644 index 0000000..6a0739b --- /dev/null +++ b/sass/citrine/abstracts/_mixins.scss @@ -0,0 +1,82 @@ +@mixin icon($mode: normal) { + @if $mode == normal { + font-style: normal; + font-variant: normal; + font-weight: normal; + line-height: 1; + user-select: none; + text-transform: none; + -webkit-user-select: none; + display: inline-block; + box-sizing: content-box; + } @else if $mode == pseudo { + -webkit-mask-image: var(--icon); + -webkit-mask-size: cover; + display: block; + mask-image: var(--icon); + mask-size: cover; + background-color: currentColor; + width: 1em; + height: 1em; + content: ""; + } +} + +@mixin gradient-fill($mode: normal) { + @if $mode == normal { + box-shadow: var(--edge-highlight), var(--shadow-glow); + background-image: linear-gradient( + to bottom right, + var(--accent-color), + var(--accent-highlight) + ); + color: var(--contrast-color); + text-shadow: var(--text-shadow-glow); + } @else if $mode == hover { + box-shadow: var(--edge-highlight); + background-color: var(--accent-color); + color: var(--contrast-color); + } +} + +@mixin card { + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + padding: 1rem; +} + +@mixin chip { + transition: + all var(--transition), + transform var(--transition-bezier); + box-shadow: var(--edge-highlight); + border-radius: var(--rounded-corner-small); + background-color: var(--bg-muted-1); + padding-inline: 0.375rem; + text-decoration: none; +} + +@mixin easing-mask($direction: to bottom) { + --mask-image: linear-gradient( + to #{$direction}, + hsl(0, 0%, 0%) 0%, + hsla(0, 0%, 0%, 0.987) 8.1%, + hsla(0, 0%, 0%, 0.951) 15.5%, + hsla(0, 0%, 0%, 0.896) 22.5%, + hsla(0, 0%, 0%, 0.825) 29%, + hsla(0, 0%, 0%, 0.741) 35.3%, + hsla(0, 0%, 0%, 0.648) 41.2%, + hsla(0, 0%, 0%, 0.55) 47.1%, + hsla(0, 0%, 0%, 0.45) 52.9%, + hsla(0, 0%, 0%, 0.352) 58.8%, + hsla(0, 0%, 0%, 0.259) 64.7%, + hsla(0, 0%, 0%, 0.175) 71%, + hsla(0, 0%, 0%, 0.104) 77.5%, + hsla(0, 0%, 0%, 0.049) 84.5%, + hsla(0, 0%, 0%, 0.013) 91.9%, + hsla(0, 0%, 0%, 0) 100% + ); + mask-image: var(--mask-image); + -webkit-mask-image: var(--mask-image); +} diff --git a/sass/citrine/abstracts/_variables.scss b/sass/citrine/abstracts/_variables.scss new file mode 100644 index 0000000..0a7019d --- /dev/null +++ b/sass/citrine/abstracts/_variables.scss @@ -0,0 +1,570 @@ +@use "functions" as *; +@use "mixins" as *; + +@mixin variables($modes: "light") { + @each $mode in $modes { + @if $mode == "light" { + // COLORS + --bg-overlay: linear-gradient( + rgba(255, 255, 255, var(--dim-opacity)), + rgba(255, 255, 255, var(--dim-opacity)) + ); + --fg-color: #29242a; + --fg-contrast: #fcfcfa; + --red-fg: hsl(342.0779, 72%, 58%); + --red-h: 342.0779; + --red-s: 72%; + --red-l: 58%; + --orange-fg: hsl(15.771428, 74%, 54%); + --orange-h: 15.771428; + --orange-s: 74%; + --orange-l: 54%; + --yellow-fg: hsl(34.639175, 91%, 42%); + --yellow-h: 34.639175; + --yellow-s: 91%; + --yellow-l: 42%; + --green-fg: hsl(153.7815, 61%, 38%); + --green-h: 153.7815; + --green-s: 61%; + --green-l: 38%; + --blue-fg: hsl(192, 71%, 38%); + --blue-h: 192; + --blue-s: 71%; + --blue-l: 38%; + --purple-fg: hsl(254.11766, 44%, 55%); + --purple-h: 254.11766; + --purple-s: 44%; + --purple-l: 55%; + --red-bg-1: hsla( + var(--red-h), + var(--red-s), + var(--red-l), + var(--color-opacity-1) + ); + --red-bg-2: hsla( + var(--red-h), + var(--red-s), + var(--red-l), + var(--color-opacity-2) + ); + --orange-bg-1: hsla( + var(--orange-h), + var(--orange-s), + var(--orange-l), + var(--color-opacity-1) + ); + --orange-bg-2: hsla( + var(--orange-h), + var(--orange-s), + var(--orange-l), + var(--color-opacity-2) + ); + --yellow-bg-1: hsla( + var(--yellow-h), + var(--yellow-s), + var(--yellow-l), + var(--color-opacity-1) + ); + --yellow-bg-2: hsla( + var(--yellow-h), + var(--yellow-s), + var(--yellow-l), + var(--color-opacity-2) + ); + --green-bg-1: hsla( + var(--green-h), + var(--green-s), + var(--green-l), + var(--color-opacity-1) + ); + --green-bg-2: hsla( + var(--green-h), + var(--green-s), + var(--green-l), + var(--color-opacity-2) + ); + --blue-bg-1: hsla( + var(--blue-h), + var(--blue-s), + var(--blue-l), + var(--color-opacity-1) + ); + --blue-bg-2: hsla( + var(--blue-h), + var(--blue-s), + var(--blue-l), + var(--color-opacity-2) + ); + --purple-bg-1: hsla( + var(--purple-h), + var(--purple-s), + var(--purple-l), + var(--color-opacity-1) + ); + --purple-bg-2: hsla( + var(--purple-h), + var(--purple-s), + var(--purple-l), + var(--color-opacity-2) + ); + --red-fg-muted-1: hsla( + var(--red-h), + var(--red-s), + calc(var(--red-l) - var(--red-l) * 0.85), + var(--fg-muted-1-opacity) + ); + --red-fg-muted-2: hsla( + var(--red-h), + var(--red-s), + calc(var(--red-l) - var(--red-l) * 0.85), + var(--fg-muted-2-opacity) + ); + --orange-fg-muted-1: hsla( + var(--orange-h), + var(--orange-s), + calc(var(--orange-l) - var(--orange-l) * 0.85), + var(--fg-muted-1-opacity) + ); + --orange-fg-muted-2: hsla( + var(--orange-h), + var(--orange-s), + calc(var(--orange-l) - var(--orange-l) * 0.85), + var(--fg-muted-2-opacity) + ); + --yellow-fg-muted-1: hsla( + var(--yellow-h), + var(--yellow-s), + calc(var(--yellow-l) - var(--yellow-l) * 0.85), + var(--fg-muted-1-opacity) + ); + --yellow-fg-muted-2: hsla( + var(--yellow-h), + var(--yellow-s), + calc(var(--yellow-l) - var(--yellow-l) * 0.85), + var(--fg-muted-2-opacity) + ); + --green-fg-muted-1: hsla( + var(--green-h), + var(--green-s), + calc(var(--green-l) - var(--green-l) * 0.85), + var(--fg-muted-1-opacity) + ); + --green-fg-muted-2: hsla( + var(--green-h), + var(--green-s), + calc(var(--green-l) - var(--green-l) * 0.85), + var(--fg-muted-2-opacity) + ); + --blue-fg-muted-1: hsla( + var(--blue-h), + var(--blue-s), + calc(var(--blue-l) - var(--blue-l) * 0.85), + var(--fg-muted-1-opacity) + ); + --blue-fg-muted-2: hsla( + var(--blue-h), + var(--blue-s), + calc(var(--blue-l) - var(--blue-l) * 0.85), + var(--fg-muted-2-opacity) + ); + --purple-fg-muted-1: hsla( + var(--purple-h), + var(--purple-s), + calc(var(--purple-l) - var(--purple-l) * 0.85), + var(--fg-muted-1-opacity) + ); + --purple-fg-muted-2: hsla( + var(--purple-h), + var(--purple-s), + calc(var(--purple-l) - var(--purple-l) * 0.85), + var(--fg-muted-2-opacity) + ); + --shade-color: rgba(0, 0, 0, 0.07); + + // COLORS (NEUTRAL) + --bg-muted-neutral-1: rgba(0, 0, 0, var(--color-opacity-1)); + --bg-muted-neutral-2: rgba(0, 0, 0, var(--color-opacity-2)); + --fg-muted-neutral-1: rgba(0, 0, 0, var(--fg-muted-1-opacity)); + --fg-muted-neutral-2: rgba(0, 0, 0, var(--fg-muted-2-opacity)); + + // OPACITY + --scanlines-opacity: 0.4; + --fg-muted-1-opacity: 0.5; + --fg-muted-2-opacity: 0.6; + + // SHADOWS + --edge-highlight: inset + 0 + #{rpx(-1)} + 0 + rgba(0, 0, 0, var(--color-opacity-1)); + } @else if $mode == "dark" { + // COLORS + --bg-overlay: linear-gradient( + rgba(0, 0, 0, var(--dim-opacity)), + rgba(0, 0, 0, var(--dim-opacity)) + ); + --fg-color: #fcfcfa; + --fg-contrast: #29242a; + --red-fg: hsl(345.18988, 100%, 69%); + --red-h: 345.18988; + --red-s: 100%; + --red-l: 69%; + --orange-fg: hsl(19.731544, 96%, 70%); + --orange-h: 19.731544; + --orange-s: 96%; + --orange-l: 70%; + --yellow-fg: hsl(44.70588, 100%, 70%); + --yellow-h: 44.70588; + --yellow-s: 100%; + --yellow-l: 70%; + --green-fg: hsl(90, 59%, 66%); + --green-h: 90; + --green-s: 59%; + --green-l: 66%; + --blue-fg: hsl(186.42857, 71%, 69%); + --blue-h: 186.42857; + --blue-s: 71%; + --blue-l: 69%; + --purple-fg: hsl(249.88234, 77%, 78%); + --purple-h: 249.88234; + --purple-s: 77%; + --purple-l: 78%; + + --red-fg-muted-1: hsla( + var(--red-h), + var(--red-s), + calc(var(--red-l) + (100% - var(--red-l)) * 0.85), + var(--fg-muted-1-opacity) + ); + --red-fg-muted-2: hsla( + var(--red-h), + var(--red-s), + calc(var(--red-l) + (100% - var(--red-l)) * 0.85), + var(--fg-muted-2-opacity) + ); + --orange-fg-muted-1: hsla( + var(--orange-h), + var(--orange-s), + calc(var(--orange-l) + (100% - var(--orange-l)) * 0.85), + var(--fg-muted-1-opacity) + ); + --orange-fg-muted-2: hsla( + var(--orange-h), + var(--orange-s), + calc(var(--orange-l) + (100% - var(--orange-l)) * 0.85), + var(--fg-muted-2-opacity) + ); + --yellow-fg-muted-1: hsla( + var(--yellow-h), + var(--yellow-s), + calc(var(--yellow-l) + (100% - var(--yellow-l)) * 0.85), + var(--fg-muted-1-opacity) + ); + --yellow-fg-muted-2: hsla( + var(--yellow-h), + var(--yellow-s), + calc(var(--yellow-l) + (100% - var(--yellow-l)) * 0.85), + var(--fg-muted-2-opacity) + ); + --green-fg-muted-1: hsla( + var(--green-h), + var(--green-s), + calc(var(--green-l) + (100% - var(--green-l)) * 0.85), + var(--fg-muted-1-opacity) + ); + --green-fg-muted-2: hsla( + var(--green-h), + var(--green-s), + calc(var(--green-l) + (100% - var(--green-l)) * 0.85), + var(--fg-muted-2-opacity) + ); + --blue-fg-muted-1: hsla( + var(--blue-h), + var(--blue-s), + calc(var(--blue-l) + (100% - var(--blue-l)) * 0.85), + var(--fg-muted-1-opacity) + ); + --blue-fg-muted-2: hsla( + var(--blue-h), + var(--blue-s), + calc(var(--blue-l) + (100% - var(--blue-l)) * 0.85), + var(--fg-muted-2-opacity) + ); + --purple-fg-muted-1: hsla( + var(--purple-h), + var(--purple-s), + calc(var(--purple-l) + (100% - var(--purple-l)) * 0.85), + var(--fg-muted-1-opacity) + ); + --purple-fg-muted-2: hsla( + var(--purple-h), + var(--purple-s), + calc(var(--purple-l) + (100% - var(--purple-l)) * 0.85), + var(--fg-muted-2-opacity) + ); + --shade-color: rgba(0, 0, 0, 0.25); + + // COLORS (NEUTRAL) + --bg-muted-neutral-1: rgba(255, 255, 255, var(--color-opacity-1)); + --bg-muted-neutral-2: rgba(255, 255, 255, var(--color-opacity-2)); + --fg-muted-neutral-1: rgba(255, 255, 255, var(--fg-muted-1-opacity)); + --fg-muted-neutral-2: rgba(255, 255, 255, var(--fg-muted-2-opacity)); + + // OPACITY + --scanlines-opacity: 1; + --fg-muted-1-opacity: 0.6; + --fg-muted-2-opacity: 0.7; + + // SHADOWS + --edge-highlight: inset + 0 + #{rpx(1)} + 0 + rgba(255, 255, 255, var(--color-opacity-1)); + } @else if $mode == "common" { + // WIDTHS + --content-width: #{rpx(720)}; + --sidebar-width: clamp( + calc(var(--content-width) / 3), + 20vw, + calc(var(--content-width) / 2) + ); + + // CORNERS + --rounded-corner: 0.75rem; + --rounded-corner-small: 0.5rem; + + // FILTERS + --blur: blur(0.75rem) saturate(180%); + --printed: contrast(2) brightness(0.75) grayscale(1); + --drop-shadow: drop-shadow(0 0.08rem 0.1rem rgba(0, 0, 0, 0.35)) + drop-shadow(0 0.4rem 0.7rem rgba(0, 0, 0, 0.18)); + + // FONTS + --font-system-ui: system-ui, sans-serif; + --font-ui-monospace: ui-monospace, monospace; + --font-emoji: + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", + "Noto Color Emoji"; + + // FONT SIZES + --font-size-xx-small: 0.625rem; + --font-size-x-small: 0.75rem; + --font-size-small: 0.875rem; + --font-size-medium: 1rem; + --font-size-large: 1.25rem; + --font-size-x-large: 1.5rem; + --font-size-xx-large: 2rem; + --font-size-xxx-large: 3rem; + + // FONT WEIGHTS + --font-weight-thin: 100; + --font-weight-extra-light: 200; + --font-weight-light: 300; + --font-weight-normal: 400; + --font-weight-medium: 500; + --font-weight-semi-bold: 600; + --font-weight-bold: 700; + --font-weight-extra-bold: 800; + --font-weight-black: 900; + + // OPACITY + --color-opacity-1: 0.1; + --color-opacity-2: 0.2; + --dim-opacity: 0.8; + --disabled-opacity: 0.4; + + // SHADOWS + --shadow: + 0 0 0 #{rpx(1)} rgba(0, 0, 0, 0.03), + 0 #{rpx(1)} #{rpx(3)} #{rpx(1)} rgba(0, 0, 0, 0.07), + 0 #{rpx(2)} #{rpx(6)} #{rpx(2)} rgba(0, 0, 0, 0.03); + --shadow-inset: + inset 0 0 0 #{rpx(1)} rgba(0, 0, 0, 0.03), + inset 0 #{rpx(1)} #{rpx(3)} #{rpx(1)} rgba(0, 0, 0, 0.07), + inset 0 #{rpx(2)} #{rpx(6)} #{rpx(2)} rgba(0, 0, 0, 0.03); + --shadow-raised: + 0 0 0 #{rpx(1)} rgba(0, 0, 0, 0.06), + 0 #{rpx(2)} #{rpx(6)} #{rpx(2)} rgba(0, 0, 0, 0.14), + 0 #{rpx(4)} #{rpx(12)} #{rpx(4)} rgba(0, 0, 0, 0.06); + --shadow-glass: 0 #{rpx(6)} #{rpx(12)} #{rpx(-6)} rgba(0, 0, 0, 0.1); + + // STATES + --hover: scale(1.1); + --active: scale(0.9); + + // TRANSITIONS + --transition: 0.2s; + --transition-longer: 0.4s; + --transition-long: 0.8s; + --transition-bezier: 0.35s cubic-bezier(0.17, 0.89, 0.32, 1.28); + --transition-bezier-longer: 0.525s cubic-bezier(0.17, 0.89, 0.32, 1.28); + + // ICONS + --icon-arrow-square-out: url(data:image/svg+xml,%3Csvg%20role%3D%22img%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2232%22%20height%3D%2232%22%20fill%3D%22currentColor%22%20viewBox%3D%220%200%20256%20256%22%3E%3Cpath%20d%3D%22M228%2C104a12%2C12%2C0%2C0%2C1-24%2C0V69l-59.51%2C59.51a12%2C12%2C0%2C0%2C1-17-17L187%2C52H152a12%2C12%2C0%2C0%2C1%2C0-24h64a12%2C12%2C0%2C0%2C1%2C12%2C12Zm-44%2C24a12%2C12%2C0%2C0%2C0-12%2C12v64H52V84h64a12%2C12%2C0%2C0%2C0%2C0-24H48A20%2C20%2C0%2C0%2C0%2C28%2C80V208a20%2C20%2C0%2C0%2C0%2C20%2C20H176a20%2C20%2C0%2C0%2C0%2C20-20V140A12%2C12%2C0%2C0%2C0%2C184%2C128Z%22/%3E%3C/svg%3E); + --icon-caret-down: url(data:image/svg+xml,%3Csvg%20role%3D%22img%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2232%22%20height%3D%2232%22%20fill%3D%22currentColor%22%20viewBox%3D%220%200%20256%20256%22%3E%3Cpath%20d%3D%22M216.49%2C104.49l-80%2C80a12%2C12%2C0%2C0%2C1-17%2C0l-80-80a12%2C12%2C0%2C0%2C1%2C17-17L128%2C159l71.51-71.52a12%2C12%2C0%2C0%2C1%2C17%2C17Z%22/%3E%3C/svg%3E); + --icon-check: url(data:image/svg+xml,%3Csvg%20role%3D%22img%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2232%22%20height%3D%2232%22%20fill%3D%22currentColor%22%20viewBox%3D%220%200%20256%20256%22%3E%3Cpath%20d%3D%22M232.49%2C80.49l-128%2C128a12%2C12%2C0%2C0%2C1-17%2C0l-56-56a12%2C12%2C0%2C1%2C1%2C17-17L96%2C183%2C215.51%2C63.51a12%2C12%2C0%2C0%2C1%2C17%2C17Z%22/%3E%3C/svg%3E); + } + } +} + +@mixin variables-accent($modes: "light") { + @each $mode in $modes { + @if $mode == "light" { + --accent-color: hsl( + var(--accent-light-h), + var(--accent-light-s), + var(--accent-light-l) + ); + --accent-h: var(--accent-light-h); + --accent-s: var(--accent-light-s); + --accent-l: var(--accent-light-l); + + --bg-color: hsl( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.6), + calc(var(--accent-l) + (100% - var(--accent-l)) * 0.8) + ); + --fg-muted-1: hsla( + var(--accent-h), + var(--accent-s), + calc(var(--accent-l) - var(--accent-l) * 0.85), + var(--fg-muted-1-opacity) + ); + --fg-muted-2: hsla( + var(--accent-h), + var(--accent-s), + calc(var(--accent-l) - var(--accent-l) * 0.85), + var(--fg-muted-2-opacity) + ); + --glass-bg-1: hsla( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.7), + calc(var(--accent-l) + (100% - var(--accent-l)) * 0.9), + var(--dim-opacity) + ); + --glass-bg-2: hsla( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.6), + calc(var(--accent-l) + (100% - var(--accent-l)) * 0.8), + var(--dim-opacity) + ); + --crt-bg: radial-gradient( + hsl( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.5), + calc(var(--accent-l) + (100% - var(--accent-l)) * 0.9) + ), + hsl( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.5), + calc(var(--accent-l) + (100% - var(--accent-l)) * 0.75) + ) + 80%, + hsl( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.5), + calc(var(--accent-l) + (100% - var(--accent-l)) * 0.65) + ) + ); + } @else if $mode == "dark" { + --accent-color: hsl( + var(--accent-dark-h), + var(--accent-dark-s), + var(--accent-dark-l) + ); + --accent-h: var(--accent-dark-h); + --accent-s: var(--accent-dark-s); + --accent-l: var(--accent-dark-l); + + --bg-color: hsl( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.7), + calc(var(--accent-l) - var(--accent-l) * 0.9) + ); + --fg-muted-1: hsla( + var(--accent-h), + var(--accent-s), + calc(var(--accent-l) + (100% - var(--accent-l)) * 0.85), + var(--fg-muted-1-opacity) + ); + --fg-muted-2: hsla( + var(--accent-h), + var(--accent-s), + calc(var(--accent-l) + (100% - var(--accent-l)) * 0.85), + var(--fg-muted-2-opacity) + ); + --glass-bg-1: hsla( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.6), + calc(var(--accent-l) - var(--accent-l) * 0.8), + var(--dim-opacity) + ); + --glass-bg-2: hsla( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.5), + calc(var(--accent-l) - var(--accent-l) * 0.7), + var(--dim-opacity) + ); + --crt-bg: radial-gradient( + hsl( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.5), + calc(var(--accent-l) - var(--accent-l) * 0.7) + ), + hsl( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.5), + calc(var(--accent-l) - var(--accent-l) * 0.9) + ) + 80%, + hsl( + var(--accent-h), + calc(var(--accent-s) - var(--accent-s) * 0.5), + calc(var(--accent-l) - var(--accent-l) * 0.95) + ) + ); + } @else if $mode == "common" { + // COLORS + --accent-highlight: hsla( + var(--accent-h), + var(--accent-s), + var(--accent-l), + var(--disabled-opacity) + ); + + // TODO: put this all in sub-mixins + --bg-muted-1: hsla( + var(--accent-h), + var(--accent-s), + var(--accent-l), + var(--color-opacity-1) + ); + + --bg-muted-2: hsla( + var(--accent-h), + var(--accent-s), + var(--accent-l), + var(--color-opacity-2) + ); + + // CONTRAST COLOR + --l: clamp(0, (l / 0.623 - 1) * -infinity, 1); + --a: calc(var(--l) + (var(--dim-opacity) * (1 - var(--l)))); + --contrast-color: oklch(from var(--accent-color) var(--l) 0 h / var(--a)); + + // SHADOWS + --shadow-glow: + 0 0 0 #{rpx(1)} var(--bg-muted-2), + 0 #{rpx(2)} #{rpx(6)} #{rpx(2)} var(--bg-muted-1), + 0 #{rpx(4)} #{rpx(24)} #{rpx(4)} var(--bg-muted-2); + --shadow-glow-inset: + inset 0 0 0 #{rpx(1)} var(--bg-muted-2), + inset 0 #{rpx(2)} #{rpx(6)} #{rpx(2)} var(--bg-muted-1), + inset 0 #{rpx(4)} #{rpx(24)} #{rpx(4)} var(--bg-muted-2); + --text-shadow-glow: + var(--bg-muted-2) 0 0 #{rpx(4)}, var(--accent-color) 0 0 #{rpx(12)}; + } + } +} diff --git a/sass/citrine/base/_animations.scss b/sass/citrine/base/_animations.scss new file mode 100644 index 0000000..1c61ed2 --- /dev/null +++ b/sass/citrine/base/_animations.scss @@ -0,0 +1,83 @@ +@keyframes slide-in-up { + from { + transform: translateY(-1rem); + opacity: 0; + } +} + +@keyframes slide-in-left { + from { + transform: translateX(1rem); + opacity: 0; + } +} + +@keyframes slide-in-right { + from { + transform: translateX(-1rem); + opacity: 0; + } +} + +@keyframes slide-in-up-longer { + from { + transform: translateY(-2rem); + opacity: 0; + } +} + +@keyframes slide-in-down-longer { + from { + transform: translateY(2rem); + opacity: 0; + } +} + +@keyframes slide-in-left-longer { + from { + transform: translateX(-2rem); + opacity: 0; + } +} + +@keyframes slide-in-right-longer { + from { + transform: translateX(2rem); + opacity: 0; + } +} + +@keyframes slide-out-down-longer { + to { + transform: translateY(-2rem); + opacity: 0; + } +} + +@keyframes expand-up-left { + from { + transform: scale(0.5) translate(-1rem, -1rem); + opacity: 0; + } +} + +@keyframes expand-up-right { + from { + transform: scale(0.5) translate(1rem, -1rem); + opacity: 0; + } +} + +@keyframes expand-down-left { + from { + transform: scale(0.5) translate(-1rem, 1rem); + opacity: 0; + } +} + +@keyframes expand-down-right { + from { + transform: scale(0.5) translate(1rem, 1rem); + opacity: 0; + } +} diff --git a/sass/citrine/base/_base.scss b/sass/citrine/base/_base.scss new file mode 100644 index 0000000..01ea5eb --- /dev/null +++ b/sass/citrine/base/_base.scss @@ -0,0 +1,59 @@ +@use "../abstracts/functions" as *; + +// Make focused anchor not get covered by header +:target:not(#main-content) { + scroll-margin-block-start: 6.5rem; +} + +:focus-visible, +:target:not(#main-content) { + animation: focus-in var(--transition); + outline: 0.125rem solid var(--accent-color); + outline-offset: 0.125rem; + + @keyframes focus-in { + from { + outline: 0.5rem solid transparent; + outline-offset: 0.25rem; + } + } +} + +// Style text selection to use accent color +::selection { + background-color: var(--accent-color); + color: var(--contrast-color); +} + +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + background-color: var(--bg-color); + color: var(--fg-color); + font-family: var(--font-sans-serif), var(--font-system-ui), var(--font-emoji); + overflow-wrap: break-word; + + @supports (min-height: 100dvh) { + min-height: 100dvh; + } +} + +@media (prefers-reduced-motion) { + *, + *::before, + *::after { + animation-duration: 0s !important; + transition-duration: 0s !important; + } +} + +// Disable blur on Firefox Mobile +@-moz-document url-prefix() { + @media only screen and (max-width: 960px) { + .firefox-unblur, + .firefox-unblur::before, + .firefox-unblur::after { + backdrop-filter: none !important; + } + } +} diff --git a/sass/citrine/base/_layout.scss b/sass/citrine/base/_layout.scss new file mode 100644 index 0000000..ffbe1e5 --- /dev/null +++ b/sass/citrine/base/_layout.scss @@ -0,0 +1,36 @@ +@use "../abstracts/functions" as *; + +body > main, +body > nav, +body > footer { + --padding: 1rem; + margin-inline: auto; + padding-inline: 1rem; + max-width: calc(var(--content-width) + var(--padding) * 2); +} + +body > footer { + margin-block-start: 4rem; +} + +body > nav { + margin-block-end: 4rem; + border-block-end: rpx(1) solid var(--bg-muted-2); + + ul { + display: flex; + gap: 0.25rem; + margin-block: 0.625rem; + padding-inline-start: 0; + list-style: none; + + @media only screen and (max-width: 480px) { + flex-direction: column; + } + } + + li { + margin-block: 0; + padding-inline-start: 0; + } +} diff --git a/sass/citrine/base/_reset.scss b/sass/citrine/base/_reset.scss new file mode 100644 index 0000000..2365866 --- /dev/null +++ b/sass/citrine/base/_reset.scss @@ -0,0 +1,135 @@ +// Based on css-init, with a few important adjustments +// https://github.com/sir-sharkey/css-init + +*, +:after, +:before { + vertical-align: baseline; + box-sizing: border-box; + margin: 0; + border: none; + background: none; + padding: 0; + color: inherit; + font: inherit; + -webkit-tap-highlight-color: transparent; +} + +button, +select { + appearance: none; +} + +body { + -webkit-text-size-adjust: none; + text-size-adjust: none; + width: 100%; + min-height: 100vh; + line-height: 1.5; +} + +a, +button, +summary, +input[type="button"], +input[type="checkbox"], +input[type="color"], +input[type="file"], +input[type="image"], +input[type="radio"], +input[type="range"], +input[type="reset"], +input[type="submit"], +label, +select { + cursor: pointer; +} + +article, +aside, +audio, +canvas, +details, +figcaption, +figure, +footer, +header, +main, +nav, +rt, +ruby, +section, +summary { + display: block; +} + +bdi, +mark, +meter, +output, +progress, +time { + display: inline-block; +} + +[hidden], +datalist, +dialog:not([open]), +rp { + display: none !important; +} + +embed, +iframe, +img, +object, +picture, +svg, +video { + display: block; + max-width: 100%; + height: auto; +} + +blockquote, +q { + quotes: none; +} + +blockquote:after, +blockquote:before, +q:after, +q:before { + content: none; +} + +strong { + font-weight: 700; +} + +em { + font-style: italic; +} + +code, +kbd, +pre, +samp { + font-family: monospace; +} + +sub { + vertical-align: sub; +} + +sup { + vertical-align: super; +} + +small { + font-size: small; +} + +table { + border-collapse: collapse; +} diff --git a/sass/citrine/base/_root.scss b/sass/citrine/base/_root.scss new file mode 100644 index 0000000..e245c1d --- /dev/null +++ b/sass/citrine/base/_root.scss @@ -0,0 +1,20 @@ +@use "../abstracts/variables" as *; + +:root { + @include variables(("light", "common")); + @include variables-accent(("light", "common")); + scrollbar-color: var(--accent-color) transparent; + accent-color: var(--accent-color); + + @media (prefers-reduced-motion: no-preference) { + scroll-behavior: smooth; + } + + @media (prefers-color-scheme: dark) { + & { + @include variables("dark"); + @include variables-accent(("dark")); + color-scheme: dark; + } + } +} diff --git a/sass/citrine/base/_typography.scss b/sass/citrine/base/_typography.scss new file mode 100644 index 0000000..0415adb --- /dev/null +++ b/sass/citrine/base/_typography.scss @@ -0,0 +1,400 @@ +@use "../abstracts/functions" as *; +@use "../abstracts/mixins" as *; + +p, +ul, +ol, +dl, +blockquote, +pre { + margin-block: 1rem; +} + +ul ul, +ul ol, +ol ul, +ol ol { + margin-block: 0; +} + +p { + text-wrap: pretty; + overflow-wrap: break-word; +} + +:disabled, +[aria-disabled="true"] { + opacity: var(--disabled-opacity); + cursor: not-allowed; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + transition-duration: var(--transition); + transition-property: text-shadow, font-weight; + margin-block: 2rem 1rem; + color: var(--accent-color); + font-weight: var(--font-weight-bold); + line-height: 1; + font-variation-settings: "FLAR" 100; + letter-spacing: -0.05em; + text-decoration: underline; + text-decoration-color: var(--bg-muted-1); + text-decoration-thickness: 0.125em; + text-wrap: balance; + overflow-wrap: break-word; + + &:target { + animation: none !important; + outline: none !important; + font-weight: var(--font-weight-light); + text-shadow: var(--text-shadow-glow); + } +} + +h1 { + font-size: var(--font-size-xxx-large); +} + +h2 { + font-size: var(--font-size-xx-large); +} + +h3 { + font-size: var(--font-size-x-large); +} + +h4 { + font-size: var(--font-size-large); +} + +h5 { + font-size: var(--font-size-medium); +} + +h6 { + font-size: var(--font-size-small); +} + +@media only screen and (max-width: 480px) { + h1 { + font-size: var(--font-size-xx-large); + } + + h2 { + font-size: var(--font-size-x-large); + } + + h3 { + font-size: var(--font-size-large); + } + + h4 { + font-size: var(--font-size-medium); + } + + h5 { + font-size: var(--font-size-small); + } + + h6 { + font-size: var(--font-size-x-small); + } +} + +small { + color: var(--fg-muted-2); + font-size: var(--font-size-small); +} + +abbr[title] { + cursor: help; + text-decoration: underline; + text-decoration-style: dotted; + text-decoration-thickness: rpx(1); +} + +figcaption { + color: var(--fg-muted-2); + font-size: var(--font-size-small); + text-align: center; +} + +blockquote { + margin: 1rem 0; + border-inline-start: 0.25rem solid var(--accent-color); + border-start-start-radius: 0.25rem; + border-end-start-radius: 0.25rem; + padding-inline-start: 0.75rem; + color: var(--fg-muted-2); +} + +pre, +code, +kbd, +samp { + font-family: var(--font-monospace), var(--font-ui-monospace); +} + +pre { + margin: 1rem 0; + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + padding: 0.75rem 1rem; + overflow-x: auto; + scrollbar-width: thin; + line-height: 1.25; +} + +code:not(pre code), +mark, +samp, +q { + @include chip; +} + +code:not(pre code) { + background-color: var(--red-bg-1); + padding-block: rpx(3); + color: var(--red-fg); + font-size: var(--font-size-small); +} + +mark { + color: var(--accent-color); +} + +del { + color: var(--red-fg); + text-decoration: line-through; + text-decoration-thickness: rpx(1); +} + +ins { + color: var(--green-fg); + text-decoration: underline; + text-decoration-thickness: rpx(1); +} + +samp { + color: var(--fg-muted-2); + font-size: var(--font-size-small); +} + +q { + color: var(--fg-muted-2); + font-style: italic; +} + +u { + text-decoration: underline; + text-decoration-style: wavy; + text-decoration-color: var(--red-fg); + text-decoration-thickness: rpx(1); +} + +progress { + appearance: none; + box-shadow: var(--edge-highlight); + border: none; + border-radius: 9999px; + background-color: var(--bg-muted-1); + padding: 0.25rem; + width: 100%; + height: 1rem; + color: var(--accent-color); + + &:indeterminate { + background-color: var(--bg-muted-1); + } + + &:indeterminate::-moz-progress-bar { + background-color: transparent; + } + + &::-webkit-progress-bar { + background-color: transparent; + } + + &::-moz-progress-bar { + border-radius: 9999px; + background-color: var(--accent-color); + } + + &::-webkit-progress-value { + border-radius: 9999px; + background-color: var(--accent-color); + } +} + +kbd { + display: inline-block; + transition-duration: var(--transition); + transition-property: transform, box-shadow, background-color; + cursor: pointer; + box-shadow: + var(--edge-highlight), + inset 0 -0.125rem 0 var(--bg-muted-2); + border-radius: var(--rounded-corner-small); + background-color: var(--bg-muted-1); + padding: 0 0.375rem; + font-size: var(--font-size-small); + + // Small nice thingy, keys can be pressed! + &:active { + transform: translateY(0.125rem); + box-shadow: inset 0 rpx(1) 0 var(--bg-muted-2); + background-color: var(--bg-muted-2); + } +} + +a { + color: var(--accent-color); + font-weight: var(--font-weight-semi-bold); + text-decoration-color: var(--accent-highlight); + text-decoration-thickness: rpx(1); + + &:hover { + text-decoration-style: wavy; + } +} + +hr { + margin: 2rem auto; + border: 0.125rem solid var(--bg-muted-2); + border-radius: 9999px; +} + +dt { + font-weight: var(--font-weight-semi-bold); +} + +dd { + margin-inline-start: 1.5rem; + margin-block-end: 1rem; +} + +aside { + float: inline-end; + margin-inline-start: 1rem; + margin-block-end: 1rem; + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + padding: 1rem; + width: calc(var(--content-width) / 3); + + @media only screen and (max-width: 720px) { + float: none; + margin-inline-start: 0; + width: 100%; + } + + :first-child { + margin-block-start: 0; + } + + :last-child { + margin-block-end: 0; + } +} + +details { + display: flow-root; + transition: padding-block-end var(--transition); + margin-block-end: 1rem; + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + padding: 0.75rem 1rem; + + &::details-content { + display: block; + opacity: 0; + transition-behavior: allow-discrete; + transition-duration: var(--transition); + transition-property: opacity, block-size, content-visibility; + block-size: 0; + overflow: hidden; + } + + &[open] { + padding-block-end: 1rem; + + &::details-content { + opacity: 1; + block-size: auto; + block-size: calc-size(auto, size); + } + + summary { + color: var(--accent-color); + + &::before { + transform: scaleY(-1); + } + + @supports not (block-size: calc-size(auto, size)) { + & ~ * { + animation: slide-in-up var(--transition-bezier); + } + } + } + } + + summary { + transition: var(--transition); + color: var(--fg-muted-1); + font-weight: var(--font-weight-semi-bold); + list-style: none; + + &::-webkit-details-marker { + display: none; + } + + &::before { + @include icon(pseudo); + --icon: var(--icon-caret-down); + display: inline-block; + vertical-align: rpx(-2); + transition: + all var(--transition), + transform var(--transition-bezier); + margin-inline-end: 0.25rem; + } + } + + & > :last-child, + & > :last-child img { + margin-block-end: 0; + } +} + +figure { + margin-inline: 0; +} + +ol, +ul { + padding-inline-start: 1.5rem; +} + +li { + margin-block: 0.25rem; + padding-inline-start: 0.25rem; + + &::marker { + color: var(--accent-color); + font-weight: var(--font-weight-semi-bold); + } +} + +b, +strong { + font-weight: var(--font-weight-semi-bold); +} diff --git a/sass/citrine/components/_buttons.scss b/sass/citrine/components/_buttons.scss new file mode 100644 index 0000000..9667b7e --- /dev/null +++ b/sass/citrine/components/_buttons.scss @@ -0,0 +1,100 @@ +@use "../abstracts/mixins" as *; + +.buttons { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 0.25rem; + margin-block-start: 2rem; + + &.centered { + justify-content: center; + } + + &.start { + justify-content: flex-start; + } + + &.end { + justify-content: flex-end; + } + + &.big { + gap: 0.5rem; + } +} + +.buttons a, +a.button, +button { + display: inline-block; + transition: + background-color var(--transition), + color var(--transition), + box-shadow var(--transition), + transform var(--transition-bezier); + box-shadow: var(--edge-highlight); + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + padding: 0.5rem 1rem; + color: var(--fg-muted-1); + font-weight: var(--font-weight-semi-bold); + font-size: var(--font-size-small); + line-height: 1.5rem; + text-decoration: none; + + &:not(:disabled, [aria-disabled="true"]):hover { + background-color: var(--bg-muted-2); + color: var(--fg-muted-2); + } + + &:not(:disabled, [aria-disabled="true"]):active { + transform: var(--active); + } + + &.suggested { + @include gradient-fill; + + &:not(:disabled, [aria-disabled="true"]):hover { + @include gradient-fill(hover); + } + } + + &.subtle { + box-shadow: none; + background-color: transparent; + + &:not(:disabled, [aria-disabled="true"]):hover { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1); + color: var(--fg-muted-2); + } + } + + &[aria-busy="true"] { + animation: progress-shimmer var(--transition-long) ease-in-out alternate + infinite; + background-image: linear-gradient( + to right, + transparent 50%, + var(--bg-muted-1) 75%, + transparent 100% + ); + background-size: 200%; + + @keyframes progress-shimmer { + to { + background-position-x: -200%; + } + } + } +} + +.buttons.big a, +.buttons.big button, +a.button.big, +button.big { + border-radius: 9999px; + padding: 0.625rem 1.25rem; +} diff --git a/sass/citrine/components/_card.scss b/sass/citrine/components/_card.scss new file mode 100644 index 0000000..6b39ea9 --- /dev/null +++ b/sass/citrine/components/_card.scss @@ -0,0 +1,5 @@ +@use "../abstracts/mixins" as *; + +.card { + @include card; +} diff --git a/sass/citrine/components/_crt.scss b/sass/citrine/components/_crt.scss new file mode 100644 index 0000000..5ea199c --- /dev/null +++ b/sass/citrine/components/_crt.scss @@ -0,0 +1,90 @@ +.crt, +pre:has(code[data-crt], code[data-lang=""]) { + position: relative; + animation: flicker 0.25s alternate infinite; + margin-inline: auto; + box-shadow: var(--shadow-glow); + background-image: var(--crt-bg); + background-color: transparent !important; + padding: 0; + width: fit-content; + max-width: 100%; + overflow: hidden; + color: var(--accent-color) !important; + font-weight: var(--font-weight-medium); + text-shadow: var(--text-shadow-glow); + + @keyframes flicker { + 25% { + opacity: 0.95; + } + + 50% { + opacity: 0.85; + } + + 75% { + opacity: 1; + } + + to { + opacity: 0.9; + } + } + + &::before, + &::after { + position: absolute; + inset: 0; + content: ""; + } + + &::before { + opacity: var(--scanlines-opacity); + z-index: 1; + animation: scanlines 0.1s linear infinite; + background-image: repeating-linear-gradient( + to bottom, + rgba(0, 0, 0, 0.25), + rgba(0, 0, 0, 0.25) 0.125rem, + transparent 0.125rem, + transparent 0.25rem + ); + background-position-y: -0.125rem; + background-repeat: no-repeat; + pointer-events: none; + + @keyframes scanlines { + to { + background-position-y: 0.125rem; + } + } + } + + &::after { + opacity: 0.05; + animation: scanline 5s linear infinite; + background-image: linear-gradient( + to bottom, + transparent, + var(--accent-color) 16rem + ); + background-position-y: -16rem; + background-size: 100% 16rem; + pointer-events: none; + + @keyframes scanline { + to { + background-position-y: calc(100% + 16rem); + } + } + } + + & > code { + display: block; + border-radius: inherit; + padding: 0.75rem 1rem; + overflow-x: auto; + scrollbar-width: thin; + } +} diff --git a/sass/citrine/components/_external.scss b/sass/citrine/components/_external.scss new file mode 100644 index 0000000..494f317 --- /dev/null +++ b/sass/citrine/components/_external.scss @@ -0,0 +1,12 @@ +@use "../abstracts/functions" as *; +@use "../abstracts/mixins" as *; + +a.external:not(:has(img, svg, video, picture, figure))::after { + @include icon(pseudo); + --icon: var(--icon-arrow-square-out); + display: inline-block; + vertical-align: -0.125rem; + opacity: var(--dim-opacity); + margin-inline-start: 0.125rem; + font-size: max(1rem, 1em); +} diff --git a/sass/citrine/components/_iframe.scss b/sass/citrine/components/_iframe.scss new file mode 100644 index 0000000..2334efe --- /dev/null +++ b/sass/citrine/components/_iframe.scss @@ -0,0 +1,20 @@ +iframe { + display: block; + margin: 1rem auto; + box-shadow: var(--edge-highlight), var(--shadow); + border: none; + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + width: 100%; + max-width: 100%; + + &:fullscreen { + box-shadow: none; + border-radius: 0; + } + + &:-webkit-full-screen { + box-shadow: none; + border-radius: 0; + } +} diff --git a/sass/citrine/components/_input.scss b/sass/citrine/components/_input.scss new file mode 100644 index 0000000..d696a13 --- /dev/null +++ b/sass/citrine/components/_input.scss @@ -0,0 +1,261 @@ +@use "../abstracts/mixins" as *; + +input { + appearance: none; + background-color: var(--bg-muted-1); +} + +input:not([type]), +input[type="text"], +input[type="search"], +input[type="password"], +input[type="submit"] { + box-shadow: var(--edge-highlight); + border: none; + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + font-size: inherit; + line-height: inherit; + font-family: inherit; +} + +input:not([type]), +input[type="text"], +input[type="search"], +input[type="password"] { + padding: 0.5rem 0.75rem; + width: 100%; + + &::placeholder { + opacity: 1; + color: var(--fg-muted-1); + } +} + +input[type="submit"] { + transition: + background-color var(--transition), + transform var(--transition-bezier); + box-shadow: var(--edge-highlight); + border-radius: var(--rounded-corner); + padding: 0.375rem 0.75rem; + color: var(--fg-muted-1); + font-weight: var(--font-weight-semi-bold); + font-size: var(--font-size-small); + + &:enabled:hover { + background-color: var(--bg-muted-2); + } + + &:enabled:active { + transform: var(--active); + } +} + +input[type="radio"], +input[type="checkbox"], +input[type="color"] { + position: relative; + transition: background-color var(--transition); + border: 0.125rem solid var(--bg-muted-2); + width: 1rem; + height: 1rem; + + &:not(:checked):enabled:hover { + background-color: var(--bg-muted-2); + } +} + +input[type="radio"], +input[type="checkbox"] { + vertical-align: -0.125em; + margin-inline-end: 0.25rem; + + &::before { + display: block; + position: absolute; + transform: scale(0.5); + opacity: 0; + transition: + opacity var(--transition), + transform var(--transition-bezier); + content: ""; + } + + &:checked { + border: 0.15rem solid transparent; + background-color: var(--accent-color); + + &::before { + transform: scale(1); + opacity: 1; + } + } +} + +input[type="radio"] { + border-radius: 50%; + + &::before { + inset-block-start: 0.125rem; + inset-inline-start: 0.125rem; + border-radius: 50%; + background-color: var(--contrast-color); + width: 0.5rem; + height: 0.5rem; + } +} + +input[type="checkbox"] { + border-radius: calc(var(--rounded-corner-small) / 2); + + &::before { + @include icon(pseudo); + --icon: var(--icon-check); + transform-origin: bottom left; + inset-block-start: -0.125rem; + inset-inline-start: -0.125rem; + color: var(--contrast-color); + font-size: 1rem; + } + + &.switch { + vertical-align: -0.375rem; + box-shadow: var(--edge-highlight); + border: none; + border-radius: 9999px; + width: 2.5rem; + height: 1.5rem; + + &.big { + vertical-align: -0.625rem; + width: 3rem; + height: 2rem; + + &::before { + width: 1.5rem; + height: 1.5rem; + } + } + + &::before { + transform: none; + transform-origin: center; + opacity: 1; + mask-image: none; + inset-block-start: 0.25rem; + inset-inline-start: 0.25rem; + box-shadow: var(--shadow); + border-radius: 50%; + background-color: white; + width: 1rem; + height: 1rem; + content: ""; + } + + &:checked { + background-color: var(--accent-color); + + &::before { + transform: translateX(1rem); + background-color: var(--contrast-color); + + :root[dir*="rtl"] & { + transform: translateX(-1rem); + } + } + } + + &:disabled::before { + box-shadow: none; + } + } +} + +input[type="color"] { + vertical-align: -0.375em; + box-shadow: var(--edge-highlight); + border: none; + border-radius: var(--rounded-corner-small); + padding: 0.25rem; + width: 3rem; + height: 2rem; + + &::-moz-color-swatch { + border: none; + border-radius: calc(var(--rounded-corner-small) - 0.25rem); + } + + &::-webkit-color-swatch-wrapper { + padding: 0; + } + + &::-webkit-color-swatch { + border-radius: calc(var(--rounded-corner-small) - 0.25rem); + } +} + +input[type="range"] { + appearance: none; + box-shadow: var(--edge-highlight); + border-radius: 9999px; + background: var(--bg-muted-1); + padding: 0.5rem 0.25rem; + width: 100%; + height: 1rem; + + &::-webkit-slider-runnable-track { + cursor: pointer; + border-radius: 9999px; + background: var(--accent-color); + width: 100%; + } + + &::-moz-range-track { + cursor: pointer; + border-radius: 9999px; + background: var(--accent-color); + width: 100%; + height: 0.5rem; + } + + &::-webkit-slider-thumb { + appearance: none; + cursor: ew-resize; + margin-block: -0.5rem; + box-shadow: var(--shadow); + border: none; + border-radius: 0.25rem; + background-color: white; + width: 0.5rem; + height: 1.5rem; + } + + &::-moz-range-thumb { + appearance: none; + cursor: ew-resize; + margin-block: -0.5rem; + box-shadow: var(--shadow); + border: none; + border-radius: 0.25rem; + background-color: white; + width: 0.5rem; + height: 1.5rem; + } + + &.hollow { + &::-webkit-slider-thumb { + margin-block: 0; + outline: 0.25rem solid white; + background-color: transparent; + height: 0.5rem; + } + + &::-moz-range-thumb { + margin-block: 0; + outline: 0.25rem solid white; + background-color: transparent; + height: 0.5rem; + } + } +} diff --git a/sass/citrine/components/_media.scss b/sass/citrine/components/_media.scss new file mode 100644 index 0000000..3409071 --- /dev/null +++ b/sass/citrine/components/_media.scss @@ -0,0 +1,165 @@ +img, +video { + display: block; + margin: 1rem auto; + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + max-width: 100%; + max-height: calc(100vh - 2rem); + + &.full, + &[src*="#full"] { + width: 100%; + } + + &.start, + &.end, + &[src*="#start"], + &[src*="#end"] { + width: calc(var(--content-width) / 3); + } + + &.start, + &[src*="#start"] { + float: inline-start; + transform-origin: left; + margin-inline-end: 1rem; + + :root[dir*="rtl"] & { + transform-origin: right; + } + } + + &.end, + &[src*="#end"] { + float: inline-end; + transform-origin: right; + margin-inline-start: 1rem; + + :root[dir*="rtl"] & { + transform-origin: left; + } + } + + &.pixels, + &[src*="#pixels"] { + image-rendering: pixelated; + } + + &.has-alpha, + &[src*="#has-alpha"] { + box-shadow: none; + border-radius: 0; + background-color: transparent; + } + + &.drop-shadow, + &[src*="#drop-shadow"] { + filter: var(--drop-shadow); + } + + &.spoiler, + &[src*="#spoiler"] { + opacity: var(--dim-opacity); + clip-path: inset(0 0 0 0 round var(--rounded-corner)); + filter: blur(1.5rem); + + &:hover, + &:active { + opacity: 1; + clip-path: inset( + -0.75rem -0.75rem -0.75rem -0.75rem round var(--rounded-corner-small) + ); + filter: none; + } + + &.solid, + &[src*="#solid"] { + clip-path: none; + filter: brightness(0) contrast(0.5); + box-shadow: none; + + &:hover, + &:active { + filter: none; + } + } + } + + @media only screen and (max-width: 720px) { + &.start, + &.end, + &[src*="#start"], + &[src*="#end"] { + float: none; + margin-inline-start: auto; + margin-inline-end: auto; + width: auto; + } + } +} + +img { + transition: + box-shadow var(--transition), + border-radius var(--transition), + transform var(--transition-bezier); + + &:not(.no-hover, [src*="#no-hover"], .emoji) { + cursor: zoom-in; + + &:hover { + position: relative; + transform: var(--hover); + z-index: 1; + box-shadow: var(--edge-highlight), var(--shadow-raised); + border-radius: var(--rounded-corner-small); + } + + &.start, + &.end, + &[src*="#start"], + &[src*="#end"] { + transition: transform var(--transition-bezier-longer); + + &:hover { + transform: scale(2); + } + + @media only screen and (max-width: 720px) { + transform-origin: center; + transition: transform var(--transition-bezier); + + &:hover { + transform: var(--hover); + } + } + } + + &.has-alpha, + &[src*="#has-alpha"] { + &:hover { + box-shadow: none; + } + } + } +} + +a img:not(.no-hover, [src*="#no-hover"]) { + cursor: pointer; +} + +video:fullscreen { + box-shadow: none; + border-radius: 0; +} + +video:-webkit-full-screen { + box-shadow: none; + border-radius: 0; +} + +picture { + display: inline-block; +} diff --git a/sass/citrine/components/_overflow-auto.scss b/sass/citrine/components/_overflow-auto.scss new file mode 100644 index 0000000..712ba4b --- /dev/null +++ b/sass/citrine/components/_overflow-auto.scss @@ -0,0 +1,13 @@ +.overflow-auto { + overflow: auto; + + &.overshoot { + margin-block: -1rem; + padding-block: 1rem; + } + + &.overshoot-row { + margin-inline: -1rem; + padding-inline: 1rem; + } +} diff --git a/sass/citrine/components/_overshoot.scss b/sass/citrine/components/_overshoot.scss new file mode 100644 index 0000000..af8447f --- /dev/null +++ b/sass/citrine/components/_overshoot.scss @@ -0,0 +1,23 @@ +.overshoot { + --mask: linear-gradient( + to bottom, + transparent, + black 1rem, + black calc(100% - 1rem), + transparent + ); + -webkit-mask-image: var(--mask); + mask-image: var(--mask); + + &-row { + --mask: linear-gradient( + to right, + transparent, + black 1rem, + black calc(100% - 1rem), + transparent + ); + -webkit-mask-image: var(--mask); + mask-image: var(--mask); + } +} diff --git a/sass/citrine/components/_skip-to-content.scss b/sass/citrine/components/_skip-to-content.scss new file mode 100644 index 0000000..1e217af --- /dev/null +++ b/sass/citrine/components/_skip-to-content.scss @@ -0,0 +1,23 @@ +#skip-to-content { + position: absolute; + transform: translateX(-50%) translateY(calc(-100% - 1rem)); + z-index: 9999; + transition: + backdrop-filter var(--transition), + box-shadow var(--transition), + transform var(--transition-bezier); + inset-inline-start: 50vw; + border-radius: 9999px; + background-color: var(--glass-bg-1); + padding: 0.375rem 0.75rem; + width: max-content; + text-decoration: none; + + &:focus-visible, + &:focus { + -webkit-backdrop-filter: var(--blur); + transform: translateX(-50%) translateY(1rem); + backdrop-filter: var(--blur); + box-shadow: var(--edge-highlight), var(--shadow); + } +} diff --git a/sass/citrine/components/_spoiler.scss b/sass/citrine/components/_spoiler.scss new file mode 100644 index 0000000..3b3c91b --- /dev/null +++ b/sass/citrine/components/_spoiler.scss @@ -0,0 +1,25 @@ +span.spoiler { + filter: blur(0.25rem); + transition: filter var(--transition); + + &:focus, + &:hover { + filter: none; + } + + &.solid { + filter: none; + transition: + background-color var(--transition), + color var(--transition); + border-radius: var(--rounded-corner-small); + background-color: var(--fg-muted-1); + color: transparent; + + &:focus, + &:hover { + background-color: transparent; + color: inherit; + } + } +} diff --git a/sass/citrine/components/_table.scss b/sass/citrine/components/_table.scss new file mode 100644 index 0000000..569a914 --- /dev/null +++ b/sass/citrine/components/_table.scss @@ -0,0 +1,36 @@ +@use "../abstracts/functions" as *; + +table { + width: 100%; + overflow: hidden; +} + +thead { + border-block-start: none; + color: var(--fg-muted-2); + font-weight: var(--font-weight-semi-bold); +} + +tr { + &:nth-child(even) { + background-color: var(--bg-muted-1); + } + + th, + td { + padding: 0.5rem 1rem; + } +} + +tbody tr { + border-block: rpx(1) solid var(--bg-muted-2); + + &:last-child { + border-block-end: none; + } +} + +.overflow-auto table { + width: max-content; + min-width: 100%; +} diff --git a/sass/citrine/components/_textarea.scss b/sass/citrine/components/_textarea.scss new file mode 100644 index 0000000..58c355a --- /dev/null +++ b/sass/citrine/components/_textarea.scss @@ -0,0 +1,17 @@ +textarea { + appearance: none; + box-shadow: var(--edge-highlight); + border: none; + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + padding: 1rem; + resize: none; + font-size: inherit; + line-height: inherit; + font-family: inherit; + + &::placeholder { + opacity: 1; + color: var(--fg-muted-1); + } +} diff --git a/sass/citrine/components/_title.scss b/sass/citrine/components/_title.scss new file mode 100644 index 0000000..16b8c89 --- /dev/null +++ b/sass/citrine/components/_title.scss @@ -0,0 +1,5 @@ +strong.title { + color: var(--accent-color); + font-size: var(--font-size-large); + line-height: 1; +} diff --git a/sass/citrine/components/_tooltip.scss b/sass/citrine/components/_tooltip.scss new file mode 100644 index 0000000..72aa4c1 --- /dev/null +++ b/sass/citrine/components/_tooltip.scss @@ -0,0 +1,41 @@ +// attr() hack from https://cyrneko.eu/css-tricks.html +@media (pointer: coarse), (hover: none) { + .tooltip, + abbr[title] { + display: inline-flex; + position: relative; + justify-content: center; + + &::after { + position: absolute; + top: calc(100% + 0.25rem); + z-index: 9999; + backdrop-filter: var(--blur); + transition: + visibility var(--transition), + opacity var(--transition), + transform var(--transition-bezier); + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner-small); + background-color: var(--glass-bg-1); + padding: 0.125rem 0.375rem; + width: max-content; + content: attr(title); + -webkit-backdrop-filter: var(--blur); + transform: translateY(-0.5rem) scale(0.5); + transform-origin: top; + visibility: hidden; + opacity: 0; + max-width: calc(var(--content-width) / 3); + color: var(--fg-muted-2); + font-size: var(--font-size-small); + } + + &:focus::after, + &:hover::after { + transform: none; + visibility: visible; + opacity: 1; + } + } +} diff --git a/sass/citrine/components/_visually-hidden.scss b/sass/citrine/components/_visually-hidden.scss new file mode 100644 index 0000000..fbf292a --- /dev/null +++ b/sass/citrine/components/_visually-hidden.scss @@ -0,0 +1,11 @@ +.visually-hidden { + position: absolute !important; + clip-path: inset(50%) !important; + margin: -1px !important; + border-width: 0 !important; + padding: 0 !important; + width: 1px !important; + height: 1px !important; + overflow: hidden !important; + white-space: nowrap !important; +} diff --git a/sass/citrine/style.scss b/sass/citrine/style.scss new file mode 100644 index 0000000..77ddca1 --- /dev/null +++ b/sass/citrine/style.scss @@ -0,0 +1,29 @@ +/* +* Citrine v1.0.0 (https://citrine.daudix.one) +* Copyright 2026 David "Daudix" Lapshin and contributors +* Licensed under MIT (https://codeberg.org/daudix/citrine/raw/branch/main/LICENSE.txt) +*/ + +@use "base/root"; +@use "base/reset"; +@use "base/animations"; +@use "base/base"; +@use "base/layout"; +@use "base/typography"; + +@use "components/buttons"; +@use "components/card"; +@use "components/crt"; +@use "components/external"; +@use "components/iframe"; +@use "components/input"; +@use "components/media"; +@use "components/overflow-auto"; +@use "components/overshoot"; +@use "components/skip-to-content"; +@use "components/spoiler"; +@use "components/table"; +@use "components/textarea"; +@use "components/title"; +@use "components/tooltip"; +@use "components/visually-hidden"; diff --git a/sass/components/_alerts.scss b/sass/components/_alerts.scss new file mode 100644 index 0000000..d9f5ed5 --- /dev/null +++ b/sass/components/_alerts.scss @@ -0,0 +1,153 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; +@use "../citrine/abstracts/variables" as *; + +[class^="markdown-alert"] { + display: flow-root; + position: relative; + + &::before { + margin-inline-start: 1.5rem; + content: var(--alert-title); + color: var(--accent-color); + font-weight: var(--font-weight-semi-bold); + } + + &::after { + @include icon(pseudo); + --icon: var(--alert-icon); + position: absolute; + margin-inline-end: 0.25rem; + inset-block-start: rpx(1); + inset-inline-start: 0.75rem; + color: var(--accent-color); + font-size: 1.25rem; + } + + & > :first-child { + margin-block-start: 0.25rem; + } + + & > :last-child { + margin-block-end: 0; + } +} + +.markdown-alert { + // GitHub flavored alerts + &-note, + &-tip, + &-important, + &-warning, + &-caution, + // Generic colors + &-red, + &-orange, + &-yellow, + &-green, + &-blue, + &-purple { + @include variables-accent(("light", "common")); + + @media (prefers-color-scheme: dark) { + @include variables-accent("dark"); + } + } + + &-note { + &::before { + content: var(--alert-note-title); + } + + &::after { + --icon: var(--icon-info); + } + } + + &-tip { + &::before { + content: var(--alert-tip-title); + } + + &::after { + --icon: var(--icon-lightbulb); + } + } + + &-important { + &::before { + content: var(--alert-important-title); + } + + &::after { + --icon: var(--icon-warning-diamond); + } + } + + &-warning { + &::before { + content: var(--alert-warning-title); + } + + &::after { + --icon: var(--icon-warning); + } + } + + &-caution { + &::before { + content: var(--alert-caution-title); + } + + &::after { + --icon: var(--icon-warning-octagon); + } + } + + &-caution, + &-red { + --accent-h: var(--red-h); + --accent-s: var(--red-s); + --accent-l: var(--red-l); + --accent-color: var(--red-fg); + } + + &-orange { + --accent-h: var(--orange-h); + --accent-s: var(--orange-s); + --accent-l: var(--orange-l); + --accent-color: var(--orange-fg); + } + + &-warning, + &-yellow { + --accent-h: var(--yellow-h); + --accent-s: var(--yellow-s); + --accent-l: var(--yellow-l); + --accent-color: var(--yellow-fg); + } + + &-tip, + &-green { + --accent-h: var(--green-h); + --accent-s: var(--green-s); + --accent-l: var(--green-l); + --accent-color: var(--green-fg); + } + + &-note, + &-blue { + --accent-h: var(--blue-h); + --accent-s: var(--blue-s); + --accent-l: var(--blue-l); + --accent-color: var(--blue-fg); + } + + &-important, + &-purple { + --accent-h: var(--purple-h); + --accent-s: var(--purple-s); + --accent-l: var(--purple-l); + --accent-color: var(--purple-fg); + } +} diff --git a/sass/components/_article-details.scss b/sass/components/_article-details.scss new file mode 100644 index 0000000..b3ca1ff --- /dev/null +++ b/sass/components/_article-details.scss @@ -0,0 +1,71 @@ +@use "../citrine/abstracts/functions" as *; + +.article-details { + display: flex; + flex-direction: column; + gap: 0.75rem; + margin-block-end: 2rem; + color: var(--fg-muted-2); + font-size: var(--font-size-small); + + .items { + display: flex; + flex-wrap: wrap; + align-items: flex-start; + gap: 0.25rem 0.5rem; + } + + .item { + display: inline-flex; + align-items: center; + gap: 0.375rem; + border-radius: var(--rounded-corner-small); + } + + a { + color: inherit; + font-weight: var(--font-weight-normal); + font-size: var(--font-size-small); + text-decoration-color: transparent; + + &:hover { + text-decoration-style: solid; + text-decoration-color: var(--fg-muted-1); + } + } + + .tooltip { + cursor: help; + } + + .icon { + box-shadow: var(--edge-highlight); + border-radius: var(--rounded-corner-small); + background-color: var(--bg-muted-1); + padding: 0.375rem; + color: var(--accent-color); + font-size: 1.25rem; + } + + .tags { + display: flex; + flex-wrap: wrap; + gap: 0 0.5rem; + margin: 0; + border-block-start: rpx(1) solid var(--bg-muted-2); + padding-inline-start: 0; + padding-block-start: 0.5rem; + width: 100%; + list-style: none; + + li { + display: flex; + margin-block: 0; + padding: 0; + } + + a::before { + content: "#"; + } + } +} diff --git a/sass/components/_article-list.scss b/sass/components/_article-list.scss new file mode 100644 index 0000000..e597ecc --- /dev/null +++ b/sass/components/_article-list.scss @@ -0,0 +1,254 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/variables" as *; +@use "../citrine/abstracts/mixins" as *; + +.article-list { + display: grid; + grid-template-columns: repeat( + auto-fill, + minmax(calc(var(--content-width) / 2.5), 1fr) + ); + gap: 1rem; + margin-block-start: 2rem; + + &.categories article { + position: relative; + + &:hover .icon:last-child { + transform: translateY(-50%) rotate(-10deg) scale(1.5); + } + + .icon:last-child { + position: absolute; + transform: translateY(-50%); + z-index: -1; + transition: transform var(--transition-bezier); + inset-block-start: 50%; + inset-inline-end: -3rem; + color: var(--bg-muted-1); + font-size: 12rem; + } + } + + article { + display: flex; + position: relative; + flex-direction: column; + justify-content: space-between; + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: 1.25rem; + background-color: var(--bg-muted-1); + padding: 0; + overflow: hidden; + + &.draft { + opacity: var(--disabled-opacity); + filter: grayscale(1); + transition-duration: var(--transition); + transition-property: opacity, filter; + + &:hover { + opacity: 1; + filter: none; + } + } + + &.has-banner.has-accent-color { + background-image: var(--bg-overlay), var(--blurnail); + background-position: center; + background-size: cover; + } + + &.has-accent-color { + @include variables-accent(("light", "common")); + + @media (prefers-color-scheme: dark) { + & { + @include variables-accent("dark"); + } + } + } + + & > div { + display: flex; + flex: 1; + flex-direction: column; + margin: 1rem; + + & > a { + text-decoration: none; + } + } + + h3 { + margin: 0; + border-inline-start: 0.25rem solid; + border-start-start-radius: 0.25rem; + border-end-start-radius: 0.25rem; + padding-inline-start: 0.5rem; + color: var(--accent-color); + font-weight: var(--font-weight-semi-bold); + text-decoration: none; + + &:hover::after { + opacity: 1; + } + + &::after { + opacity: 0; + transition: opacity var(--transition); + content: " →"; + } + } + + .description p { + margin-block-end: 0.75rem; + } + + .open-post { + @include easing-mask(bottom); + display: flex; + position: relative; + justify-content: center; + align-items: center; + margin-block-end: -2rem; + box-shadow: var(--edge-highlight); + border-radius: 0; + aspect-ratio: 2 / 1; + overflow: hidden; + text-decoration: none; + + &:hover { + &::before { + background-color: var(--shade-color); + } + + img { + transform: var(--hover); + filter: blur(0.25rem); + } + + .icon { + transform: none; + opacity: 1; + } + } + + &:active img { + transform: none; + } + + &::before { + position: absolute; + z-index: 1; + transition: background-color var(--transition); + inset: 0; + border-radius: inherit; + content: ""; + } + + img { + position: absolute; + transition-duration: var(--transition); + transition-property: transform, filter; + margin: 0; + inset: 0; + width: 100%; + height: 100%; + object-fit: cover; + } + + .icon { + transform: scale(1.5); + opacity: 0; + z-index: 1; + transition: + transform var(--transition-bezier), + opacity var(--transition); + color: white; + font-size: 1.25rem; + } + } + + .article-details { + flex: 1; + justify-content: flex-end; + margin-block-end: 0; + + .tags { + margin-block-start: auto; + margin-block-end: -0.25rem; + } + } + } +} + +.paginator { + display: flex; + justify-content: center; + align-items: center; + gap: 0.25rem; + margin-block-start: 4rem; + + &:has(a.paginator-item.previous:active) .paginator-counter { + border-start-start-radius: 1rem; + border-end-start-radius: 1rem; + } + + &:has(a.paginator-item.next:active) .paginator-counter { + border-start-end-radius: 1rem; + border-end-end-radius: 1rem; + } + + &-item { + display: inline-block; + transition: + border-radius var(--transition), + border-start-end-radius var(--transition), + border-end-end-radius var(--transition), + border-start-start-radius var(--transition), + border-end-start-radius var(--transition), + background-color var(--transition), + transform var(--transition-bezier); + box-shadow: var(--edge-highlight); + border-radius: 1rem; + background-color: var(--bg-muted-1); + padding: 0.5rem; + color: var(--accent-color); + line-height: 0; + text-decoration: none; + + &:not([aria-disabled="true"]):hover { + background-color: var(--bg-muted-2); + } + + &:not([aria-disabled="true"]):active { + transform: var(--active); + border-radius: 1rem; + } + + &-previous { + border-start-end-radius: var(--rounded-corner-small); + border-end-end-radius: var(--rounded-corner-small); + } + + &-next { + border-start-start-radius: var(--rounded-corner-small); + border-end-start-radius: var(--rounded-corner-small); + } + } + + &-counter { + @include gradient-fill; + display: inline-block; + transition: var(--transition-bezier); + transition-property: + border-start-start-radius, border-end-start-radius, + border-start-end-radius, border-end-end-radius; + border-radius: var(--rounded-corner-small); + background-color: var(--bg-muted-1); + padding: 0.25rem 0.5rem; + font-weight: var(--font-weight-semi-bold); + font-variant-numeric: tabular-nums; + } +} diff --git a/sass/components/_article-nav.scss b/sass/components/_article-nav.scss new file mode 100644 index 0000000..217e350 --- /dev/null +++ b/sass/components/_article-nav.scss @@ -0,0 +1,66 @@ +.article-nav { + display: flex; + gap: 0.25rem; + + @media only screen and (max-width: 720px) { + flex-direction: column; + } + + &-item { + flex: 1; + transition: + box-shadow var(--transition), + background-color var(--transition), + transform var(--transition-bezier); + border-radius: var(--rounded-corner); + padding: 0.75rem 1rem; + min-width: 0; + text-decoration: none; + + &:hover { + box-shadow: var(--edge-highlight), var(--shadow); + background-color: var(--bg-muted-1); + + .article-title { + color: var(--accent-color); + } + } + + &:active { + transform: var(--active); + } + + &.prev { + text-align: start; + + .nav-arrow::before { + content: "← "; + } + } + + &.next { + text-align: end; + + .nav-arrow::after { + content: " →"; + } + } + } + + .nav-arrow { + margin-block-end: 0.5rem; + color: var(--fg-muted-2); + font-weight: var(--font-weight-normal); + font-size: var(--font-size-small); + } + + .article-title { + display: block; + transition: color var(--transition); + max-width: 100%; + overflow: hidden; + color: var(--fg-muted-1); + text-overflow: ellipsis; + white-space: nowrap; + } +} diff --git a/sass/components/_audio-button.scss b/sass/components/_audio-button.scss new file mode 100644 index 0000000..1f4fe43 --- /dev/null +++ b/sass/components/_audio-button.scss @@ -0,0 +1,42 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +.audio-button { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 1rem; + border-radius: 0.5rem; + font-size: var(--font-size-small); + font-weight: var(--font-weight-semi-bold); + color: var(--accent-color); + background-color: var(--accent-bg-1); + border: none; + cursor: pointer; + transition: + background-color var(--transition), + transform var(--transition-bezier), + box-shadow var(--transition); + + &:hover { + background-color: var(--accent-bg-2); + transform: scale(1.03); + } + + &:active { + transform: var(--active); + } + + &:focus-visible { + outline: 0.125rem solid var(--accent-color); + outline-offset: 0.25rem; + } + + .audio-button-label { + display: none; + + @media (min-width: 480px) { + display: inline; + } + } +} diff --git a/sass/components/_audio.scss b/sass/components/_audio.scss new file mode 100644 index 0000000..18d8397 --- /dev/null +++ b/sass/components/_audio.scss @@ -0,0 +1,41 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +// Who doesn't like a party popper mid article? +.audio { + @include chip; + padding-block: 0; + color: var(--accent-color); + font-size: var(--font-size-medium); + + &:hover { + color: var(--accent-color) !important; + + .icon { + animation: speaker var(--transition-long) ease-out infinite; + + @keyframes speaker { + from { + --icon: var(--icon-speaker-none); + } + + 50% { + --icon: var(--icon-speaker-low); + } + + to { + --icon: var(--icon-speaker-high); + } + } + } + } + + &:active { + transform: var(--active); + } + + .icon { + vertical-align: rpx(-3); + margin-inline-start: 0.25rem; + } +} diff --git a/sass/components/_banner.scss b/sass/components/_banner.scss new file mode 100644 index 0000000..e261d07 --- /dev/null +++ b/sass/components/_banner.scss @@ -0,0 +1,98 @@ +@use "../citrine/abstracts/functions" as *; + +body.has-banner { + background-image: var(--bg-overlay), var(--blurnail); + background-position: center; + background-size: cover; + background-attachment: fixed; +} + +#banner { + --mask: linear-gradient(black calc(100% - 2rem), transparent); + -webkit-mask-image: var(--mask); + display: flex; + grid-area: banner; + justify-content: center; + mask-image: var(--mask); + margin-block-end: -2.5rem; + padding-block: 3.25rem 2.5rem; + width: 100%; + + div { + position: relative; + margin-inline: 1rem; + aspect-ratio: 2 / 1; + width: 100%; + max-width: var(--content-width); + } + + img { + position: absolute; + margin: 0; + inset: 0; + border-radius: var(--rounded-corner) var(--rounded-corner) 0 0 !important; + aspect-ratio: 2 / 1; + width: 100%; + object-fit: cover; + } + + img:nth-of-type(1) { + opacity: var(--dim-opacity); + z-index: -1; + filter: blur(1.5rem); + animation: banner-glow-load calc(0.525s + var(--transition-long)); + inset-block-start: 1rem; + + @keyframes banner-glow-load { + from, + 52.5% { + opacity: 0; + } + } + } + + img:nth-of-type(2) { + transform-origin: bottom; + animation: banner-load calc(0.525s + var(--transition-longer)); + + @keyframes banner-load { + from, + 52.5% { + transform: translateY(2rem) scale(0.95); + opacity: 0; + } + } + } + + & + #main-content { + margin-block-start: 0; + + article > h1:first-of-type, + h1:first-of-type { + margin-inline: -1rem; + margin-block: 0; + border-radius: var(--rounded-corner) var(--rounded-corner) 0 0; + padding: 1rem; + width: unset; + -webkit-backdrop-filter: blur(1rem); + backdrop-filter: blur(1rem); + animation: banner-heading calc(0.525s + var(--transition-longer)); + box-shadow: inset 0 rpx(1) 0 var(--accent-highlight); + background-image: linear-gradient( + to bottom, + var(--bg-muted-1), + transparent 50% + ); + background-size: auto 200%; + text-decoration: none; + + @keyframes banner-heading { + from, + 52.5% { + box-shadow: none; + background-position-y: 100%; + } + } + } + } +} diff --git a/sass/components/_comments.scss b/sass/components/_comments.scss new file mode 100644 index 0000000..e5cc946 --- /dev/null +++ b/sass/components/_comments.scss @@ -0,0 +1,459 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +#comments { + #qrcode { + float: inline-end; + cursor: help; + box-sizing: content-box; + margin-inline-start: 1rem; + margin-block-start: 3rem; + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner); + background-color: white; + padding: 0.75rem; + width: rpx(116); + height: rpx(116); + + svg { + pointer-events: none; + } + + @media only screen and (max-width: 720px) { + display: none; + } + } + + #comments-wrapper { + display: flex; + flex-direction: column; + gap: 2rem; + margin-block-start: 2rem; + + #comments-status { + color: var(--fg-muted-1); + font-weight: var(--font-weight-semi-bold); + font-size: var(--font-size-x-large); + text-align: center; + } + + .comment { + display: grid; + // grid-template-columns: min-content; + grid-template-columns: auto 1fr; + grid-template-areas: + "avatar header " + "...... post " + "...... media " + "...... card " + "...... interactions"; + column-gap: 1rem; + justify-items: start; + animation: slide-in-right var(--transition-bezier); + + :root[dir*="rtl"] & { + animation: slide-in-left var(--transition-bezier); + } + + @media only screen and (max-width: 720px) { + grid-template-areas: + "avatar header " + "post post " + "media media " + "card card " + "interactions interactions"; + align-items: start; + } + + &.comment-reply { + position: relative; + border-inline-start: 0.25rem solid var(--bg-muted-2); + border-start-start-radius: 0.25rem; + border-end-start-radius: 0.25rem; + padding-inline-start: 1rem; + + &:has(+ .comment-reply) { + border-end-start-radius: 0; + } + + & + .comment-reply { + margin-block-start: -2rem; + border-start-start-radius: 0; + padding-block-start: 2rem; + } + } + + .avatar-link { + grid-area: avatar; + width: 4rem; + height: 4rem; + + @media only screen and (max-width: 480px) { + width: 3rem; + height: 3rem; + } + + .avatar { + transition: transform var(--transition-bezier); + margin: 0; + background-size: cover; + width: 100%; + height: 100%; + + &:hover { + transform: rotate(10deg) var(--hover); + border-radius: var(--rounded-corner); + } + + &:active { + transform: var(--active); + } + } + } + + .mention { + @include chip; + display: inline-block; + + &:hover { + background-color: var(--bg-muted-2); + } + + &:active { + transform: var(--active); + } + } + + .invisible { + display: none; + } + + .ellipsis::after { + content: "…"; + } + + header { + display: flex; + grid-area: header; + flex-direction: column; + width: 100%; + min-width: 0; + color: var(--fg-muted-1); + line-height: 1; + + .display, + .instance { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .display { + color: var(--fg-color); + line-height: 1.5rem; + text-decoration: none; + + .emoji { + cursor: unset; + + &:hover { + transform: none; + } + } + } + + .instance { + margin-block-start: 0.25rem; + font-weight: var(--font-weight-semi-bold); + font-size: var(--font-size-x-small); + + &.op { + cursor: help; + + &::before { + @include icon(pseudo); + --icon: var(--icon-seal-check); + display: inline-block; + vertical-align: text-top; + margin-inline-end: 0.25rem; + font-size: 1rem; + } + } + } + + .timestamp { + margin-block-start: 0.25rem; + + a { + color: inherit; + font-size: var(--font-size-x-small); + text-decoration-color: transparent; + + &:hover { + text-decoration-style: solid; + text-decoration-color: inherit; + } + + &::after { + vertical-align: top; + } + } + } + } + + details { + border-radius: var(--rounded-corner-small); + background-image: + linear-gradient( + to right, + transparent, + transparent 0.5rem, + var(--bg-muted-1) 0.5rem, + var(--bg-muted-1) calc(100% - 0.5rem), + transparent calc(100% - 0.5rem), + transparent + ), + linear-gradient( + to right, + transparent, + transparent 0.5rem, + var(--bg-color) 0.5rem, + var(--bg-color) calc(100% - 0.5rem), + transparent calc(100% - 0.5rem), + transparent + ), + repeating-linear-gradient( + 45deg, + var(--contrast-color), + var(--contrast-color) 0.25rem, + var(--accent-color) 0.25rem, + var(--accent-color) 0.5rem + ); + width: 100%; + + summary { + border-radius: 0; + background-image: none; + } + } + + main { + grid-area: post; + justify-self: stretch; + margin-block-start: 1rem; + padding: 0; + + :first-child { + margin-block-start: 0; + } + + :last-child { + margin-block-end: 0; + } + } + + .attachments { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr)); + grid-area: media; + gap: 0.5rem; + margin-block-start: 1rem; + + img, + video { + margin: 0; + } + } + + .meta-card { + grid-area: card; + transition: transform var(--transition-bezier); + margin-block-start: 1rem; + width: min(calc(var(--content-width) / 2), 100%); + font-weight: var(--font-weight-normal); + text-decoration: none; + + &:hover { + img { + transform: var(--hover); + box-shadow: var(--edge-highlight), var(--shadow-raised); + border-radius: var(--rounded-corner-small); + } + + figcaption { + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-2); + } + } + + &:active { + transform: var(--active); + } + + figure { + display: flex; + flex-direction: column; + gap: 0.25rem; + margin: 0; + + img { + margin: 0; + border-radius: var(--rounded-corner) var(--rounded-corner) + var(--rounded-corner-small) var(--rounded-corner-small); + aspect-ratio: 16 / 9; + object-fit: cover; + + & + figcaption { + border-radius: var(--rounded-corner-small) + var(--rounded-corner-small) var(--rounded-corner) + var(--rounded-corner); + } + } + + figcaption { + display: flex; + flex-direction: column; + gap: 0.25rem; + transition-duration: var(--transition); + transition-property: border-radius, background-color; + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + padding: 1rem; + color: var(--fg-color); + font-size: var(--font-size-medium); + text-align: start; + + p { + margin: 0; + color: var(--fg-muted-2); + font-size: var(--font-size-small); + } + } + } + } + + footer { + display: flex; + grid-area: interactions; + align-items: center; + gap: 0.25rem; + margin-block-start: 1rem; + width: 100%; + min-width: 0; + + @media only screen and (max-width: 480px) { + flex-wrap: wrap; + } + + .boosts, + .faves, + .reaction { + padding: 0.375rem 0.75rem; + font-variant-numeric: tabular-nums; + white-space: nowrap; + } + + .boosts, + .faves { + transition: + box-shadow var(--transition), + border-radius var(--transition), + transform var(--transition-bezier); + border-radius: rpx(18); + text-decoration: none; + + .icon { + vertical-align: rpx(-2); + transition: transform var(--transition-bezier); + margin-inline-end: 0.25rem; + } + + &:hover { + box-shadow: var(--edge-highlight); + text-decoration: none; + } + + &:active { + transform: var(--active); + border-radius: rpx(18); + } + } + + .boosts { + border-start-end-radius: var(--rounded-corner-small); + border-end-end-radius: var(--rounded-corner-small); + padding-inline-end: 0.625rem; + color: var(--purple-fg); + + .icon { + --icon: var(--icon-repeat); + } + + &:hover { + background-color: var(--purple-bg-1); + + .icon { + transform: rotate(180deg); + } + } + } + + .faves { + border-start-start-radius: var(--rounded-corner-small); + border-end-start-radius: var(--rounded-corner-small); + padding-inline-start: 0.625rem; + color: var(--yellow-fg); + + .icon { + --icon: var(--icon-star); + } + + &:hover { + background-color: var(--yellow-bg-1); + + .icon { + transform: rotate(72deg); + } + } + } + + .reactions { + display: flex; + // flex-wrap: nowrap; + gap: 0.25rem; + margin: -0.5rem -1rem; + padding: 0.5rem 1rem; + min-width: 0; + overflow-x: auto; + scrollbar-width: none; + color: var(--accent-color); + font-weight: var(--font-weight-semi-bold); + + .reaction { + display: flex; + flex-shrink: 0; + align-items: center; + gap: 0.25rem; + cursor: zoom-in; + border: rpx(1) solid var(--bg-muted-2); + border-radius: rpx(19); + + &:hover .emoji { + transform: scale(2); + } + + .emoji { + cursor: unset; + } + + span { + font-variant-numeric: tabular-nums; + } + } + } + } + } + } +} diff --git a/sass/components/_deanchor.scss b/sass/components/_deanchor.scss new file mode 100644 index 0000000..1fd7c89 --- /dev/null +++ b/sass/components/_deanchor.scss @@ -0,0 +1,20 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +.deanchor { + display: inline; + font-family: var(--font-family-mono); + font-size: var(--font-size-small); + color: var(--fg-muted-2); + background-color: var(--bg-muted-1); + padding: 0.125rem 0.375rem; + border-radius: 0.25rem; + transition: + background-color var(--transition), + color var(--transition); + + &:hover { + background-color: var(--bg-muted-2); + color: var(--fg-muted-1); + } +} diff --git a/sass/components/_emoji.scss b/sass/components/_emoji.scss new file mode 100644 index 0000000..93570ae --- /dev/null +++ b/sass/components/_emoji.scss @@ -0,0 +1,21 @@ +.emoji { + display: inline-block; + vertical-align: bottom; + transition: transform var(--transition-bezier); + cursor: zoom-in; + margin: 0; + box-shadow: none; + border-radius: 0; + background-color: transparent; + width: 1.5rem; + height: 1.5rem; + + &:hover { + transform: scale(2); + } + + &.big { + width: 3rem; + height: 3rem; + } +} diff --git a/sass/components/_footnotes.scss b/sass/components/_footnotes.scss new file mode 100644 index 0000000..c083897 --- /dev/null +++ b/sass/components/_footnotes.scss @@ -0,0 +1,62 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +.footnotes-list p { + margin-block: 0; +} + +.footnote-reference { + display: inline-flex; + vertical-align: baseline; + transition: + background-color var(--transition), + outline-offset calc(var(--transition-long) * 3), + transform var(--transition-bezier); + margin-inline-start: 0.25rem; + outline-offset: calc(var(--content-width) / 2) !important; + border-radius: 0.6875em; + background-color: var(--fg-muted-1); + // width: 1.375em; + line-height: 1.375em; + + &:hover { + background-color: var(--accent-color); + + a::after { + mask-position: center rpx(18); + } + } + + &:active { + transform: var(--active); + } + + &:target { + animation: none !important; + outline-offset: rpx(-3) !important; + background-color: var(--accent-color); + } + + a { + flex: 1; + border-radius: inherit; + padding-inline: rpx(9) rpx(6); + color: var(--bg-color); + font-size: var(--font-size-small); + font-variant-numeric: tabular-nums; + text-align: center; + text-decoration: none; + + &::after { + @include icon(pseudo); + --icon: var(--icon-caret-down); + display: inline-block; + vertical-align: rpx(-7); + mask-position: center; + mask-size: 1em; + transition: mask-position var(--transition); + margin-inline-start: 0.25rem; + height: rpx(22); + } + } +} diff --git a/sass/components/_fuse.scss b/sass/components/_fuse.scss new file mode 100644 index 0000000..494ef74 --- /dev/null +++ b/sass/components/_fuse.scss @@ -0,0 +1,50 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +.fuse { + display: flex; + flex-direction: column; + gap: 0.5rem; + max-width: 30rem; + + .fuse-input { + width: 100%; + padding: 0.75rem 1rem; + border-radius: 0.5rem; + border: 1px solid var(--bg-muted-2); + background-color: var(--bg-muted-1); + color: var(--fg-color); + font-size: var(--font-size-medium); + transition: + border-color var(--transition), + box-shadow var(--transition); + + &:focus { + outline: none; + border-color: var(--accent-color); + box-shadow: 0 0 0 2px var(--accent-bg-1), 0 0 0 4px var(--accent-color); + } + } + + .fuse-results { + display: flex; + flex-direction: column; + gap: 0.25rem; + + .fuse-result { + padding: 0.5rem 0.75rem; + border-radius: 0.25rem; + background-color: var(--bg-muted-1); + text-decoration: none; + color: var(--fg-color); + transition: + background-color var(--transition), + padding-left var(--transition); + + &:hover { + background-color: var(--bg-muted-2); + padding-left: 1rem; + } + } + } +} diff --git a/sass/components/_giallo.scss b/sass/components/_giallo.scss new file mode 100644 index 0000000..0c228b4 --- /dev/null +++ b/sass/components/_giallo.scss @@ -0,0 +1,16 @@ +.giallo:has(.giallo-ln) { + padding-inline: 0; +} + +.giallo-l { + display: inline-block; + width: 100%; +} + +.giallo-ln { + display: inline-block; + margin-right: 1ch; + min-width: 3ch; + user-select: none; + text-align: right; +} diff --git a/sass/components/_icon.scss b/sass/components/_icon.scss new file mode 100644 index 0000000..6768a12 --- /dev/null +++ b/sass/components/_icon.scss @@ -0,0 +1,9 @@ +@use "../citrine/abstracts/mixins" as *; + +.icon { + @include icon; + + &::before { + @include icon(pseudo); + } +} diff --git a/sass/components/_iframe.scss b/sass/components/_iframe.scss new file mode 100644 index 0000000..550da8d --- /dev/null +++ b/sass/components/_iframe.scss @@ -0,0 +1,6 @@ +iframe { + &.vimeo-embed, + &.youtube-embed { + aspect-ratio: 16 / 9; + } +} diff --git a/sass/components/_index.scss b/sass/components/_index.scss new file mode 100644 index 0000000..61889d1 --- /dev/null +++ b/sass/components/_index.scss @@ -0,0 +1,25 @@ +@use "alerts"; +@use "article-details"; +@use "article-list"; +@use "article-nav"; +@use "audio"; +@use "banner"; +@use "comments"; +@use "emoji"; +@use "footnotes"; +@use "giallo"; +@use "icon"; +@use "iframe"; +@use "marquee"; +@use "not-found"; +@use "pre-container"; +@use "search-modal"; +@use "skip-to-content"; +@use "statements"; +@use "tag-chips"; +@use "zola-anchor"; +@use "zola-error"; +@use "audio-button"; +@use "deanchor"; +@use "fuse"; +@use "search-fuse"; \ No newline at end of file diff --git a/sass/components/_marquee.scss b/sass/components/_marquee.scss new file mode 100644 index 0000000..e995c33 --- /dev/null +++ b/sass/components/_marquee.scss @@ -0,0 +1,35 @@ +.marquee { + margin-inline: -1rem; + padding-inline: 1rem; + overflow: hidden; + + &.overshoot-row > * { + animation: marquee-scroll + calc((var(--text-width) + var(--container-width)) / 40 * 1s) linear + infinite; + + @keyframes marquee-scroll { + 0%, + 10% { + transform: translateX(0); + } + + 45%, + 55% { + transform: translateX( + calc((var(--container-width) - var(--text-width)) * 1px) + ); + } + + 90%, + 100% { + transform: translateX(0); + } + } + } + + & > * { + display: inline-block; + white-space: nowrap; + } +} diff --git a/sass/components/_not-found.scss b/sass/components/_not-found.scss new file mode 100644 index 0000000..34b92bf --- /dev/null +++ b/sass/components/_not-found.scss @@ -0,0 +1,11 @@ +picture#not-found { + display: block; + + img { + width: 8rem; + } +} + +img#not-found { + width: 8rem; +} diff --git a/sass/components/_pre-container.scss b/sass/components/_pre-container.scss new file mode 100644 index 0000000..d011d77 --- /dev/null +++ b/sass/components/_pre-container.scss @@ -0,0 +1,141 @@ +@use "../citrine/abstracts/functions" as *; + +.pre-container { + position: relative; + margin: 1rem 0 1rem; + box-shadow: var(--shadow); + border-radius: var(--rounded-corner); + + &::after { + position: absolute; + inset: 0; + box-shadow: var(--edge-highlight); + border-radius: inherit; + pointer-events: none; + content: ""; + } + + .header { + display: flex; + justify-content: space-between; + align-items: center; + transition: border-block-end-color var(--transition); + border-radius: var(--rounded-corner) var(--rounded-corner) 0 0; + border-block-end: rpx(1) solid transparent; + background-image: linear-gradient( + to right, + transparent 50%, + var(--bg-muted-neutral-1) 75%, + transparent 100% + ); + background-size: 200%; + padding: 0.25rem; + + &:not(.z-l-code), + &:not(.z-d-code) { + background-color: var(--bg-muted-1); + color: var(--accent-color); + + button:hover { + background-color: var(--bg-muted-1); + } + + &.active { + border-block-end-color: var(--bg-muted-1); + + button { + background-color: var(--bg-muted-1); + } + } + } + + span { + margin-inline-start: 0.75rem; + font-weight: var(--font-weight-semi-bold); + } + + button { + position: relative; + transition: + box-shadow var(--transition), + background-color var(--transition), + transform var(--transition-bezier); + cursor: pointer; + box-shadow: none; + border-radius: var(--rounded-corner-small); + background-color: transparent; + padding: 0.5rem; + color: inherit; + line-height: 0; + + &:hover { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-neutral-1); + } + + &:active { + transform: var(--active); + } + + &:disabled { + cursor: not-allowed; + + &:active { + transform: none; + } + } + + .icon { + transition: + opacity var(--transition-longer), + transform var(--transition-bezier-longer); + font-size: 1rem; + + &.copy { + --icon: var(--icon-copy); + transform-origin: bottom left; + } + + &.check { + --icon: var(--icon-check-square-offset); + position: absolute; + transform: scale(0.5); + transform-origin: top right; + opacity: 0; + inset: 0.5rem; + } + } + } + + &.active { + animation: active-shimmer var(--transition-long); + border-block-end-color: var(--bg-muted-neutral-1); + + button { + opacity: 1; + + .icon.copy { + transform: scale(0.5); + opacity: 0; + } + + .icon.check { + transform: none; + opacity: 1; + } + } + + @keyframes active-shimmer { + to { + background-position-x: -200%; + } + } + } + } + + pre { + margin: 0; + box-shadow: none; + border-radius: 0 0 var(--rounded-corner) var(--rounded-corner); + } +} diff --git a/sass/components/_search-fuse.scss b/sass/components/_search-fuse.scss new file mode 100644 index 0000000..20bb5c2 --- /dev/null +++ b/sass/components/_search-fuse.scss @@ -0,0 +1,73 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +.search-fuse { + display: flex; + flex-direction: column; + gap: 0.75rem; + max-width: 40rem; + margin: 0 auto; + + &-input-wrap { + position: relative; + display: flex; + align-items: center; + } + + &-icon { + position: absolute; + left: 0.75rem; + color: var(--fg-muted-2); + pointer-events: none; + transition: + color var(--transition), + opacity var(--transition-bezier); + opacity: 0.7; + } + + &-input { + width: 100%; + padding: 0.75rem 1rem 0.75rem 2.5rem; + border-radius: 0.5rem; + border: 1px solid var(--bg-muted-2); + background-color: var(--bg-muted-1); + color: var(--fg-color); + font-size: var(--font-size-medium); + transition: + border-color var(--transition), + box-shadow var(--transition), + background-color var(--transition); + + &:focus { + outline: none; + border-color: var(--accent-color); + box-shadow: 0 0 0 2px var(--accent-bg-1), 0 0 0 4px var(--accent-color); + } + + &::placeholder { + color: var(--fg-muted-2); + } + } + + &-results { + display: flex; + flex-direction: column; + gap: 0.25rem; + + .search-fuse-result { + padding: 0.5rem 0.75rem; + border-radius: 0.25rem; + background-color: var(--bg-muted-1); + text-decoration: none; + color: var(--fg-color); + transition: + background-color var(--transition), + padding-left var(--transition); + + &:hover { + background-color: var(--bg-muted-2); + padding-left: 1rem; + } + } + } +} diff --git a/sass/components/_search-modal.scss b/sass/components/_search-modal.scss new file mode 100644 index 0000000..2174293 --- /dev/null +++ b/sass/components/_search-modal.scss @@ -0,0 +1,128 @@ +#search-modal { + display: flex; + position: fixed; + flex-direction: column; + align-items: center; + visibility: hidden; + opacity: 0; + z-index: 9999; + transition: + visibility var(--transition), + opacity var(--transition); + inset: 0; + background-color: var(--shade-color); + padding-block-start: 15vh; + width: 100%; + height: 100vh; + + &.active { + visibility: visible; + opacity: 1; + + #search-modal-content { + transform: none; + } + } + + &-content { + display: flex; + flex-direction: column; + gap: 1rem; + transform: var(--hover); + backdrop-filter: var(--blur); + -webkit-backdrop-filter: var(--blur); + transition: + border-radius var(--transition), + padding var(--transition), + max-width var(--transition), + transform var(--transition-bezier-longer); + margin: -0.5rem; + box-shadow: var(--edge-highlight), var(--shadow-glass); + border-radius: 2.25rem 2.25rem 1.75rem 1.75rem; + background-color: var(--glass-bg-1); + padding: 1rem; + width: calc(100% - 2rem); + max-width: calc(var(--content-width) / 1.5); + overflow: hidden; + + &:has(#search-results:empty) { + border-radius: 1.75rem; + padding: 0.5rem; + max-width: calc(var(--content-width) / 2); + } + + & > div { + position: relative; + } + } + + #search-input { + flex-shrink: 0; + box-shadow: var(--edge-highlight); + border-radius: 9999px; + padding: 0 1rem; + width: 100%; + height: 2.5rem; + + &:placeholder-shown + div .icon { + transform: none; + opacity: var(--disabled-opacity); + pointer-events: all; + } + + & + div .icon { + position: absolute; + transform: translateX(0.5rem); + opacity: 0; + transition: + opacity var(--transition), + transform var(--transition-bezier); + cursor: help; + inset-block-start: 0.625rem; + inset-inline-end: 1rem; + pointer-events: none; + color: var(--fg-muted-2); + font-size: 1.25rem; + } + } + + #search-results { + display: none; + flex: 1; + flex-direction: column; + gap: 0.5rem; + margin: -1rem; + padding: 1rem; + max-height: 50vh; + overflow: auto; + scrollbar-width: thin; + + .item { + display: inline-flex; + flex-direction: column; + box-shadow: var(--edge-highlight); + border-radius: var(--rounded-corner); + background-color: var(--bg-muted-1); + padding: 0.75rem 1rem; + + a { + text-decoration: none; + + &::after { + content: " →"; + } + } + + span { + margin-block-start: 0.25rem; + border-block-start: rpx(1) solid var(--bg-muted-2); + padding-block-start: 0.25rem; + color: var(--fg-muted-2); + + strong { + color: var(--fg-color); + } + } + } + } +} diff --git a/sass/components/_skip-to-content.scss b/sass/components/_skip-to-content.scss new file mode 100644 index 0000000..1e217af --- /dev/null +++ b/sass/components/_skip-to-content.scss @@ -0,0 +1,23 @@ +#skip-to-content { + position: absolute; + transform: translateX(-50%) translateY(calc(-100% - 1rem)); + z-index: 9999; + transition: + backdrop-filter var(--transition), + box-shadow var(--transition), + transform var(--transition-bezier); + inset-inline-start: 50vw; + border-radius: 9999px; + background-color: var(--glass-bg-1); + padding: 0.375rem 0.75rem; + width: max-content; + text-decoration: none; + + &:focus-visible, + &:focus { + -webkit-backdrop-filter: var(--blur); + transform: translateX(-50%) translateY(1rem); + backdrop-filter: var(--blur); + box-shadow: var(--edge-highlight), var(--shadow); + } +} diff --git a/sass/components/_statements.scss b/sass/components/_statements.scss new file mode 100644 index 0000000..f76e587 --- /dev/null +++ b/sass/components/_statements.scss @@ -0,0 +1,61 @@ +@use "../citrine/abstracts/functions" as *; + +.statement-container { + margin: 1rem 0; + box-shadow: var(--edge-highlight), var(--shadow); + border-radius: var(--rounded-corner); + padding: 1rem; + + :last-child { + margin-block-end: 0; + } + + & > :nth-child(2) { + margin-block-start: 0.5rem; + } + + .title { + font-weight: var(--font-weight-semi-bold); + + .icon { + vertical-align: rpx(-2); + margin-inline-end: 0.25rem; + } + } + + &.archive { + --accent-color: var(--purple-fg); + --accent-highlight: hsla( + var(--purple-h), + var(--purple-s), + var(--purple-l), + var(--disabled-opacity) + ); + background-color: var(--purple-bg-1); + color: var(--purple-fg); + } + + &.disclaimer { + --accent-color: var(--red-fg); + --accent-highlight: hsla( + var(--red-h), + var(--red-s), + var(--red-l), + var(--disabled-opacity) + ); + background-color: var(--red-bg-1); + color: var(--red-fg); + } + + &.trigger { + --accent-color: var(--yellow-fg); + --accent-highlight: hsla( + var(--yellow-h), + var(--yellow-s), + var(--yellow-l), + var(--disabled-opacity) + ); + background-color: var(--yellow-bg-1); + color: var(--yellow-fg); + } +} diff --git a/sass/components/_tag-chips.scss b/sass/components/_tag-chips.scss new file mode 100644 index 0000000..df38adc --- /dev/null +++ b/sass/components/_tag-chips.scss @@ -0,0 +1,59 @@ +@use "../citrine/abstracts/functions" as *; + +#tag-chips { + display: flex; + flex-wrap: wrap; + gap: 0.25rem; + margin: 0; + padding-inline-start: 0; + list-style: none; + + li { + display: flex; + margin-block: 0; + padding-inline-start: 0; + } + + a { + display: flex; + transition: + background-color var(--transition), + color var(--transition), + transform var(--transition-bezier); + box-shadow: var(--edge-highlight); + border-radius: 1rem; + background-color: var(--bg-muted-1); + padding: 0.25rem; + padding-inline-start: 0.75rem; + color: var(--fg-muted-1); + font-size: var(--font-size-small); + line-height: 1.5rem; + text-decoration: none; + + &:hover { + background-color: var(--bg-muted-2); + color: var(--fg-muted-2); + } + + &:active { + transform: var(--active); + } + + &::before { + content: "#"; + } + + .counter { + transition: inherit; + margin-inline-start: 0.375rem; + box-shadow: var(--edge-highlight); + border-radius: inherit; + background-color: inherit; + aspect-ratio: 1 / 1; + height: 1.5rem; + line-height: 1.5rem; + font-variant-numeric: tabular-nums; + text-align: center; + } + } +} diff --git a/sass/components/_zola-anchor.scss b/sass/components/_zola-anchor.scss new file mode 100644 index 0000000..f942600 --- /dev/null +++ b/sass/components/_zola-anchor.scss @@ -0,0 +1,50 @@ +h1, +h2, +h3, +h4, +h5, +h6 { + &:hover .zola-anchor { + transform: none; + opacity: 1; + } + + &:has(.zola-anchor) { + position: relative; + } +} + +.zola-anchor { + position: absolute; + transform: translateX(0.25em); + opacity: 0; + transition: + color var(--transition), + opacity var(--transition), + transform var(--transition-bezier); + inset-inline-start: -1.25em; + padding-inline-end: 0.25em; + color: var(--fg-muted-1); + line-height: 1; + + @media only screen and (max-width: 720px) { + display: none; + } + + &:hover { + color: var(--fg-muted-2); + } + + &:active { + transform: var(--active); + } + + &:focus-visible { + opacity: 1; + } +} + +:target .zola-anchor { + transform: none; + opacity: var(--disabled-opacity); +} diff --git a/sass/components/_zola-error.scss b/sass/components/_zola-error.scss new file mode 100644 index 0000000..433f6cc --- /dev/null +++ b/sass/components/_zola-error.scss @@ -0,0 +1,55 @@ +// Based on https://github.com/welpo/tabi/blob/68c35b02f8c09e21ecff48f61bad6da3d96ce337/sass/parts/_zola-error.scss + +body > div:last-child:has(div:last-child[style]:not([class]):not([id])) { + z-index: 9999 !important; + background-color: var(--shade-color) !important; +} + +body > div:last-child > div:last-child[style]:not([class]):not([id]) { + box-shadow: var(--edge-highlight), var(--shadow-glass); + border-radius: calc(var(--rounded-corner) + 1rem) !important; + background-color: hsla( + var(--red-h), + calc(var(--red-s) - var(--red-s) * 0.7), + calc(var(--red-l) + (100% - var(--red-l)) * 0.9), + var(--dim-opacity) + ) !important; + padding: 1rem !important; + width: fit-content; + max-width: calc(100% - 2rem); + -webkit-backdrop-filter: var(--blur); + backdrop-filter: var(--blur); + filter: none !important; + + @media (prefers-color-scheme: dark) { + & { + background-color: hsla( + var(--red-h), + calc(var(--red-s) - var(--red-s) * 0.6), + calc(var(--red-l) - var(--red-l) * 0.8), + var(--dim-opacity) + ) !important; + } + } +} + +body + > div:last-child + > div:last-child[style]:not([class]):not([id]) + > p[style]:first-child { + margin-block-end: 1rem !important; + color: var(--red-fg) !important; + font-weight: var(--font-weight-semi-bold) !important; + font-size: var(--font-size-large) !important; +} + +body + > div:last-child + > div:last-child[style]:not([class]):not([id]) + > pre[style]:last-child { + border-radius: var(--rounded-corner) !important; + background-color: var(--red-bg-1) !important; + padding: 0.75rem 1rem !important; + color: var(--red-fg) !important; + font-weight: var(--font-weight-normal) !important; +} diff --git a/sass/fonts.scss b/sass/fonts.scss new file mode 100644 index 0000000..a148e87 --- /dev/null +++ b/sass/fonts.scss @@ -0,0 +1,52 @@ +$commissioner-version: "1.012.1"; +$commit-mono-version: "1.143"; + +:root { + --font-sans-serif: "Commissioner"; + --font-monospace: "Commit Mono"; +} + +// Commissioner +@font-face { + font-style: normal; + font-weight: 100 900; + src: url("/fonts/commissioner.woff2?v=#{$commissioner-version}") + format("woff2"); + font-family: "Commissioner"; + font-display: swap; + // Fix vertical alignment, + // See https://github.com/kosbarts/Commissioner/issues/55 + ascent-override: 96.3%; + descent-override: 25%; +} + +@font-face { + font-style: italic; + font-weight: 100 900; + src: url("/fonts/commissioner.woff2?v=#{$commissioner-version}") + format("woff2"); + font-family: "Commissioner"; + font-display: swap; + font-variation-settings: "slnt" -12; + // Ditto + ascent-override: 96.3%; + descent-override: 25%; +} + +// Commit Mono +@font-face { + font-style: normal; + font-weight: 100 900; + src: url("/fonts/commit-mono.woff2?v=#{$commit-mono-version}") format("woff2"); + font-family: "Commit Mono"; + font-display: swap; +} + +@font-face { + font-style: italic; + font-weight: 100 900; + src: url("/fonts/commit-mono.woff2?v=#{$commit-mono-version}") format("woff2"); + font-family: "Commit Mono"; + font-display: swap; + font-variation-settings: "ital" 1; +} diff --git a/sass/layout/_footer.scss b/sass/layout/_footer.scss new file mode 100644 index 0000000..25a09fe --- /dev/null +++ b/sass/layout/_footer.scss @@ -0,0 +1,140 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +#site-footer { + --padding: 1rem; + display: flex; + grid-area: footer; + gap: 1rem; + margin-block: 3.25rem 1rem; + border-block-start: rpx(1) solid var(--bg-muted-2); + padding: 1rem; + width: 100%; + min-width: 0; + max-width: calc(var(--content-width) + var(--padding) * 2); + + @media only screen and (max-width: 480px) { + flex-direction: column-reverse; + } + + & > div { + display: flex; + flex: 1; + flex-direction: column; + gap: 1rem; + + p { + margin: 0; + } + + &:nth-of-type(1) { + color: var(--fg-muted-2); + font-size: var(--font-size-small); + } + + &:nth-of-type(2) { + justify-content: space-between; + align-items: flex-end; + text-align: end; + + @media only screen and (max-width: 480px) { + align-items: normal; + text-align: start; + } + } + } + + a.external::after { + margin-inline-start: 0.25rem; + } + + .link { + @include chip; + display: inline-block; + line-height: 1.5rem; + + &:hover { + background-color: var(--bg-muted-2); + } + + &:active { + transform: var(--active); + } + } + + // IMO it's pretty cute + .webrings { + display: flex; + flex-direction: column; + gap: 0.25rem; + margin-block: 0; + padding-inline-start: 0; + list-style: none; + } + + .webring { + display: flex; + gap: 0.25rem; + margin-block: 0; + padding-inline-start: 0; + + .random, + .previous, + .next { + display: inline-flex; + justify-content: center; + align-items: center; + border-radius: 9999px; + padding: 0.25rem; + font-size: 1rem; + } + } + + .timestamp { + color: var(--fg-muted-1); + font-size: var(--font-size-xx-small); + font-family: var(--font-monospace); + text-transform: uppercase; + } + + .socials { + display: flex; + justify-content: flex-end; + gap: 0.75rem; + margin: 0; + padding-inline-start: 0; + list-style: none; + + @media only screen and (max-width: 480px) { + justify-content: normal; + } + + li { + margin-block: 0; + padding-inline-start: 0; + } + + a { + display: flex; + transition: + color var(--transition), + transform var(--transition-bezier); + height: fit-content; + color: var(--fg-muted-1); + + &:hover { + color: var(--fg-muted-2); + } + + &:active { + transform: var(--active); + } + } + + svg { + fill: currentColor; + width: 1.5rem; + height: 1.5rem; + } + } +} diff --git a/sass/layout/_full-bleed.scss b/sass/layout/_full-bleed.scss new file mode 100644 index 0000000..85e9fd3 --- /dev/null +++ b/sass/layout/_full-bleed.scss @@ -0,0 +1,13 @@ +.full-bleed { + transform: translateX(-50%); + margin-inline-start: 50%; + width: calc(100vw - var(--sidebar-width)); + + :root[dir*="rtl"] & { + transform: translateX(50%); + } + + @media only screen and (max-width: 960px) { + width: calc(100vw - 3.5rem); + } +} diff --git a/sass/layout/_grid.scss b/sass/layout/_grid.scss new file mode 100644 index 0000000..6a8b9db --- /dev/null +++ b/sass/layout/_grid.scss @@ -0,0 +1,16 @@ +body { + display: grid; + grid-template-rows: auto auto 1fr auto; + grid-template-columns: var(--sidebar-width) 1fr; + grid-template-areas: + "sidebar header" + "sidebar banner" + "sidebar main " + "sidebar footer"; + justify-items: center; + transition: grid-template-columns var(--transition); + + @media only screen and (max-width: 960px) { + grid-template-columns: 0 1fr; + } +} diff --git a/sass/layout/_header.scss b/sass/layout/_header.scss new file mode 100644 index 0000000..0ecab63 --- /dev/null +++ b/sass/layout/_header.scss @@ -0,0 +1,347 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +#site-header { + display: flex; + position: sticky; + grid-area: header; + // height: 3rem; + justify-content: space-between; + // flex-wrap: nowrap; + align-items: center; + gap: 0.25rem; + z-index: 2; + // height: 3.75rem; + inset-block-start: 0; + // padding: 0.5rem 0.75rem; + padding: 0.5rem; + width: 100%; + min-width: 0; + + &::before, + &::after { + position: absolute; + inset: 0; + @include easing-mask(bottom); + inset-block-end: -100%; + pointer-events: none; + content: ""; + } + + &::before { + z-index: -2; + backdrop-filter: var(--blur); + -webkit-backdrop-filter: var(--blur); + } + + &::after { + z-index: -1; + background-color: var(--bg-color); + } + + .title { + margin-inline: 0.75rem; + min-width: 0; + overflow: hidden; + color: var(--fg-muted-2); + font-size: var(--font-size-small); + line-height: 2rem; + text-overflow: ellipsis; + white-space: nowrap; + } + + .buttons { + display: flex; + flex-wrap: nowrap; + gap: 0.25rem; + margin: 0; + // margin-inline-start: auto; + box-shadow: var(--edge-highlight), var(--shadow-glass); + border-radius: 1.125rem; + background-color: var(--glass-bg-1); + padding: 0.25rem; + list-style: none; + + li { + flex: 0; + margin-block: 0; + padding-inline-start: 0; + } + + & > li { + height: fit-content; + line-height: 0; + } + + button, + details { + box-shadow: none; + background-color: transparent; + line-height: inherit; + + .icon { + margin-inline-end: 0; + box-shadow: none; + background-color: transparent; + padding: 0; + } + } + + button, + summary { + border-radius: 50%; + padding: 0.375rem; + + &:hover { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1); + color: var(--fg-muted-2); + } + + &:active { + transform: var(--active); + } + } + + details { + box-shadow: none; + border-radius: 0; + padding: 0; + } + + button { + font-size: var(--font-size-medium); + } + + summary { + &:hover { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1); + } + + &::before { + display: none; + } + } + + details { + display: block; + position: relative; + margin-block-end: 0; + + &::details-content { + block-size: auto; + } + + &[open] { + &::details-content { + block-size: auto; + } + + summary { + color: var(--fg-muted-1); + + &:hover { + color: var(--fg-muted-2); + } + } + + @supports not (block-size: calc-size(auto, size)) { + summary ~ * { + animation: none; + } + } + } + } + } +} + +.buttons .dropdown { + display: flex; + position: absolute; + flex-direction: column; + gap: 0.25rem; + transform-origin: top right; + z-index: 1; + backdrop-filter: var(--blur); + -webkit-backdrop-filter: var(--blur); + animation: expand-up-right var(--transition-bezier); + inset-block-start: calc(100% + 0.75rem); + inset-inline-end: -0.25rem; + box-shadow: var(--edge-highlight), var(--shadow-glass); + border-radius: calc(var(--rounded-corner) + 0.25rem); + background-color: var(--glass-bg-1); + padding: 0.25rem; + width: max-content; + min-width: 8rem; + list-style: none; + + :root[dir*="rtl"] & { + transform-origin: top left; + animation: expand-up-left var(--transition-bezier); + } + + li { + flex: 1; + } + + a { + box-shadow: none; + border-radius: var(--rounded-corner); + background-color: transparent; + padding: 0.375rem 0.75rem; + width: 100%; + + &:hover { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1) !important; + } + } +} + +#site-sidebar-controls { + display: flex; + flex-shrink: 0; + gap: 0.25rem; + transition: + width var(--transition), + visibility var(--transition); + margin-block: 0; + box-shadow: var(--edge-highlight), var(--shadow-glass); + border-radius: 1.125rem; + background-color: var(--glass-bg-1); + padding: 0.25rem; + height: fit-content; + line-height: 0; + list-style: none; + + @media only screen and (max-width: 960px) { + width: 2.25rem; + + &:has(#expand-sidebar:checked) { + width: 6.25rem; + + #site-sidebar-modes { + transform: none; + visibility: visible; + opacity: 1; + } + } + } + + ul { + padding-inline-start: 0; + list-style: none; + } + + li { + display: inline-block; + margin-block: 0; + padding-inline-start: 0; + + label { + display: inline-block; + transition: + box-shadow var(--transition), + background-color var(--transition), + color var(--transition), + transform var(--transition-bezier); + cursor: pointer; + border-radius: 50%; + padding: 0.375rem; + height: fit-content; + color: var(--fg-muted-1); + line-height: 0; + + &:hover { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1); + color: var(--fg-muted-2); + } + + &:active { + transform: var(--active); + } + } + + input[type="radio"] { + & + label { + position: relative; + z-index: 1; + + &::before { + @include gradient-fill; + position: absolute; + opacity: 0; + z-index: -1; + transition: + opacity var(--transition), + box-shadow var(--transition), + background-color var(--transition); + inset: 0; + border-radius: inherit; + content: ""; + } + } + + &:checked + label { + color: var(--contrast-color); + + &:hover { + box-shadow: none; + background-color: transparent; + } + + &:hover::before { + box-shadow: var(--edge-highlight); + background-color: var(--accent-color); + } + + &::before { + opacity: 1; + } + } + } + + input[type="radio"], + input[type="checkbox"] { + &:focus-visible + label { + animation: focus-in var(--transition); + outline: 0.125rem solid var(--accent-color); + outline-offset: 0.125rem; + } + + @supports not selector(:focus-visible) { + &:focus + label { + animation: focus-in var(--transition); + outline: 0.125rem solid var(--accent-color); + outline-offset: 0.125rem; + } + } + } + } + + #expand-sidebar-li { + display: none; + + @media only screen and (max-width: 960px) { + display: inline-block; + } + } +} + +#site-sidebar-modes { + display: flex; + gap: 0.25rem; + transform-origin: left; + transition: + visibility var(--transition), + opacity var(--transition), + transform var(--transition); + + @media only screen and (max-width: 960px) { + transform: translateX(-2.25rem); + visibility: hidden; + opacity: 0; + } +} diff --git a/sass/layout/_index.scss b/sass/layout/_index.scss new file mode 100644 index 0000000..b6da0b7 --- /dev/null +++ b/sass/layout/_index.scss @@ -0,0 +1,6 @@ +@use "footer"; +@use "full-bleed"; +@use "grid"; +@use "header"; +@use "main-content"; +@use "sidebar"; \ No newline at end of file diff --git a/sass/layout/_main-content.scss b/sass/layout/_main-content.scss new file mode 100644 index 0000000..1c7c3b7 --- /dev/null +++ b/sass/layout/_main-content.scss @@ -0,0 +1,59 @@ +#main-content { + --padding: 1rem; + grid-area: main; + transform-origin: 50% 50vh; + animation: fade-in-zoom var(--transition-bezier-longer); + margin-block-start: 3.25rem; + padding-inline: 1rem; + width: 100%; + min-width: 0; + max-width: calc(var(--content-width) + var(--padding) * 2); + + @supports (transform-origin: 50% 50dvh) { + transform-origin: 50% 50dvh; + } + + & > :first-child { + margin-block-start: 0; + } + + & > :last-child { + margin-block-end: 0; + } + + @keyframes fade-in-zoom { + from { + transform: scale(0.95); + opacity: 0; + } + } +} + +// Don't play animation when jumping to section +:target ~ #main-content { + animation-name: none; +} + +// Loading animation when going up in the sidebar +#↑:target ~ #main-content, +#up:target ~ #main-content { + animation-name: slide-in-up-longer; +} + +// Loading animation when going down in the sidebar +#↓:target ~ #main-content, +#down:target ~ #main-content { + animation-name: slide-in-down-longer; +} + +// Loading animation when going to the previous article +#←:target ~ #main-content, +#left:target ~ #main-content { + animation-name: slide-in-left-longer; +} + +// Loading animation when going to the next article +#→:target ~ #main-content, +#right:target ~ #main-content { + animation-name: slide-in-right-longer; +} diff --git a/sass/layout/_sidebar.scss b/sass/layout/_sidebar.scss new file mode 100644 index 0000000..6f7655e --- /dev/null +++ b/sass/layout/_sidebar.scss @@ -0,0 +1,775 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +#site-sidebar { + display: flex; + position: sticky; + grid-area: sidebar; + flex-direction: column; + z-index: 3; + transition-duration: var(--transition); + transition-property: + visibility, transform, inset-block-start, box-shadow, border-inline-end, + border-start-end-radius, border-end-end-radius, height; + inset-block-start: 0; + inset-inline-start: 0; + border-inline-end: rpx(1) solid var(--bg-muted-2); + width: var(--sidebar-width); + height: 100vh; + + @supports (height: 100dvh) { + height: 100dvh; + } + + @media only screen and (max-width: 960px) { + transform: translateX(-100%); + visibility: hidden; + inset-block-start: 3.25rem; + box-shadow: var(--edge-highlight), var(--shadow); + border-inline-end: 0 solid transparent; + border-start-end-radius: var(--rounded-corner); + border-end-end-radius: var(--rounded-corner); + height: calc(100vh - 3.75rem); + + :root[dir*="rtl"] & { + transform: translateX(100%); + } + + @supports (height: 100dvh) { + height: calc(100dvh - 3.75rem); + } + + #site-header:has(#expand-sidebar:checked) ~ & { + transform: none; + visibility: visible; + backdrop-filter: var(--blur); + -webkit-backdrop-filter: var(--blur); + background-color: var(--glass-bg-1); + } + } + + &-content { + display: flex; + position: relative; + flex: 1; + overflow: hidden; + } +} + +#site-sidebar-header { + display: flex; + align-items: center; + transform-origin: rpx(22) 50%; + transition: + box-shadow var(--transition), + background-color var(--transition), + color var(--transition), + transform var(--transition-bezier); + margin: 1rem; + margin-block-end: 0; + border-radius: 1.375rem; + padding: 0.25rem; + padding-inline-end: 0.75rem; + min-width: 0; + color: var(--fg-muted-1); + text-decoration: none; + + &:hover { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1); + color: var(--fg-muted-2); + + .icon { + background-color: var(--bg-muted-2); + } + } + + &:active { + transform: var(--active); + } + + img { + margin: 0; + } + + img, + .icon { + margin-inline-end: 0.5rem; + border-radius: 50%; + } + + img { + width: 2.25rem; + height: 2.25rem; + } + + .icon { + transition: background-color var(--transition); + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1); + padding: 0.5rem; + font-size: 1.25rem; + } + + div { + display: flex; + flex: 1; + flex-direction: column; + gap: 0.125rem; + min-width: 0; + line-height: 1.25; + + span:nth-of-type(1) { + overflow: hidden; + font-size: var(--font-size-small); + text-overflow: ellipsis; + white-space: nowrap; + } + + span:nth-of-type(2) { + font-weight: var(--font-weight-normal); + font-size: var(--font-size-x-small); + } + } +} + +#sidebar-nav, +#sidebar-meta { + display: flex; + flex: 1; + flex-direction: column; + transition: + visibility var(--transition), + opacity var(--transition), + filter var(--transition), + transform var(--transition-bezier); + padding: 1rem; + width: var(--sidebar-width); + min-width: 100%; + overflow: hidden auto; + scrollbar-width: thin; +} + +#site-header:has(#site-sidebar-modes #mode-meta:checked) + #site-sidebar { + #sidebar-nav { + transform: perspective(var(--content-width)) rotateY(15deg); + opacity: var(--disabled-opacity); + filter: blur(0.25rem); + } + + #sidebar-meta { + transform: perspective(var(--content-width)); + visibility: visible; + opacity: 1; + filter: none; + } +} + +#sidebar-nav { + transform: perspective(var(--content-width)); + transform-origin: left; + + :root[dir*="rtl"] & { + transform-origin: right; + } + + a { + display: flex; + flex: 1; + align-items: center; + transform-origin: rpx(22) 50%; + transition: + box-shadow var(--transition), + background-color var(--transition), + color var(--transition), + transform var(--transition-bezier); + border-radius: 1.375rem; + padding: 0.25rem; + padding-inline-end: 0.75rem; + overflow: hidden; + color: var(--fg-muted-1); + text-decoration: none; + text-overflow: ellipsis; + white-space: nowrap; + + &:hover { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1); + color: var(--fg-muted-2); + + .icon { + background-color: var(--bg-muted-2); + } + } + + &:active { + transform: var(--active); + } + + &[aria-current="page"] { + box-shadow: var(--edge-highlight); + background-color: var(--bg-muted-1); + color: var(--accent-color); + + .icon { + @include gradient-fill; + text-shadow: none; + } + + &:hover { + background-color: var(--bg-muted-2); + + .icon { + @include gradient-fill(hover); + } + } + } + + &.new .icon { + position: relative; + + &::after { + position: absolute; + inset-block-start: rpx(1); + inset-inline-end: rpx(1); + border-radius: 50%; + background-color: var(--accent-color); + width: 0.75rem; + height: 0.75rem; + content: ""; + } + } + } + + .icon { + transition: + background-color var(--transition), + color var(--transition), + box-shadow var(--transition); + margin-inline-end: 0.5rem; + box-shadow: var(--edge-highlight); + border-radius: 50%; + background-color: var(--bg-muted-1); + padding: 0.5rem; + font-size: 1.25rem; + + &.fallback { + width: 1.25rem; + font-weight: var(--font-weight-semi-bold); + font-size: var(--font-size-medium); + line-height: 1.25rem; + font-family: var(--font-monospace); + text-align: center; + + &::before { + content: none; + } + } + } + + details { + flex: 1; + margin-block-end: 0; + box-shadow: none; + border-radius: 0; + background-color: transparent; + padding: 0; + + &[open] { + summary { + transform: none; + opacity: 1; + box-shadow: none; + background-color: transparent; + + &::before { + transform: none; + } + + @supports not (block-size: calc-size(auto, size)) { + & ~ * { + animation: none; + } + } + } + } + + summary { + display: flex; + position: relative; + align-items: center; + border-radius: 0; + padding: 0.75rem 0; + color: var(--fg-muted-1); + font-weight: var(--font-weight-normal); + font-size: var(--font-size-small); + line-height: var(--font-size-small); + white-space: nowrap; + + &:hover { + box-shadow: none; + background-color: transparent; + } + + &:active { + transform: none; + } + + &::before { + position: absolute; + transform: rotate(-90deg); + transition: transform var(--transition-bezier); + margin-inline-end: 0; + inset-block: 0.5rem; + inset-inline-end: 0; + font-size: 1rem; + } + + .line { + flex: 1; + margin-inline: 0.5rem 1.5rem; + background-color: var(--bg-muted-2); + height: rpx(1); + } + } + + ul { + margin-block: 0; + } + } + + ul { + display: flex; + flex-direction: column; + gap: 0.25rem; + margin-block: auto; + padding-inline-start: 0; + list-style: none; + } + + li { + display: flex; + transition: width var(--transition); + margin-block: 0; + padding-inline-start: 0; + width: 100%; + } +} + +#sidebar-meta { + position: absolute; + transform: perspective(var(--content-width)) rotateY(-30deg); + transform-origin: left; + visibility: hidden; + opacity: 0; + filter: blur(0.25rem); + inset: 0; + + :root[dir*="rtl"] & { + transform-origin: right; + } + + &:has(> div:empty) { + .status-page { + display: flex; + } + + & > div { + display: none; + } + } + + & > div { + display: flex; + flex-direction: column; + gap: 1rem; + margin-block: auto; + } + + .card { + background-color: var(--glass-bg-1); + } + + .while-writing { + display: inherit; + position: relative; + flex-direction: inherit; + gap: inherit; + margin: calc(-0.25rem - rpx(1)); + margin-block-start: 1rem; + border: rpx(1) solid var(--bg-muted-2); + border-block-start: none; + border-end-start-radius: calc(var(--rounded-corner) + 0.25rem + #{rpx(1)}); + border-end-end-radius: calc(var(--rounded-corner) + 0.25rem + #{rpx(1)}); + padding: 0.25rem; + + .card-title { + display: flex; + position: absolute; + gap: 0.25rem; + inset-block-start: calc(var(--rounded-corner-small) * -1); + inset-inline: rpx(-1); + height: var(--rounded-corner-small); + + .line { + border: rpx(1) solid var(--bg-muted-2); + border-block-end: none; + } + + .line:nth-of-type(1) { + border-inline-end: none; + border-start-start-radius: var(--rounded-corner); + width: var(--rounded-corner); + } + + .line:nth-of-type(2) { + flex: 1; + border-inline-start: none; + border-start-end-radius: var(--rounded-corner); + } + + .title { + color: var(--fg-muted-2); + font-size: var(--font-size-small); + line-height: 0; + } + } + } + + .weather { + display: flex; + justify-content: space-between; + align-items: center; + gap: 1rem; + border-radius: 1.375rem; + padding: 0.25rem; + padding-inline-end: 1.25rem; + line-height: 1; + + &.cloudy { + animation: perlin 12s linear infinite; + background-image: url("/weather/perlin.png"); + + @media (prefers-color-scheme: dark) { + background-image: url("/weather/perlin-dark.png"); + } + } + + &.showers { + animation: rain 3s linear infinite; + background-image: url("/weather/rain.svg"); + + @media (prefers-color-scheme: dark) { + background-image: url("/weather/rain-dark.svg"); + } + } + + &.snowing { + animation: snow 12s linear infinite; + background-image: url("/weather/snow.svg"); + + @media (prefers-color-scheme: dark) { + background-image: url("/weather/snow-dark.svg"); + } + } + + div { + display: flex; + align-items: center; + gap: 0.5rem; + } + + .title { + color: inherit; + font-size: var(--font-size-medium); + } + + .icon { + box-shadow: var(--edge-highlight); + border-radius: 50%; + background-color: var(--bg-muted-1); + padding: 0.5rem; + font-size: 1.25rem; + } + + .condition { + color: var(--fg-muted-2); + font-size: var(--font-size-small); + } + + @keyframes rain { + to { + background-position: 256px 320px; + } + } + + @keyframes perlin { + to { + background-position: -256px -64px; + } + } + + @keyframes snow { + to { + background-position: 256px 512px; + } + } + } + + .music { + display: flex; + position: relative; + flex-direction: column; + gap: 0.5rem; + border-radius: 1rem; + + &::after { + position: absolute; + z-index: 1; + inset: 0; + box-shadow: var(--edge-highlight); + border-radius: inherit; + max-height: 5rem; + pointer-events: none; + content: ""; + } + + .carousel { + scroll-snap-type: x mandatory; + -webkit-overflow-scrolling: touch; + display: flex; + margin-block: 0; + box-shadow: var(--shadow); + border-radius: inherit; + background-color: var(--glass-bg-1); + padding-inline-start: 0; + overflow-x: auto; + scroll-behavior: smooth; + scrollbar-width: none; + list-style: none; + + li { + display: flex; + position: relative; + flex-shrink: 0; + gap: 1rem; + margin-block: 0; + outline: none; + padding: 0.5rem; + width: 100%; + overflow: hidden; + scroll-snap-align: start; + + &.has-cover { + animation: cover-alternate 60s ease-in-out infinite; + background-image: var(--bg-overlay), var(--blurnail); + background-position: center; + background-size: 2000%; + + @keyframes cover-alternate { + // X 100% + // Y ┌─────────────┐ + // │ 1,9 │ + // │ 8 2 │ + // │ 3,7 │ + // │ 6 4 │ + // │ 5 │ + // 100% └─────────────┘ + + from { + background-position: 50% 0%; + } + + 12.5% { + background-position: 100% 25%; + } + + 25% { + background-position: 50% 50%; + } + + 37.5% { + background-position: 100% 75%; + } + + 50% { + background-position: 50% 100%; + } + + 62.5% { + background-position: 0% 75%; + } + + 75% { + background-position: 50% 50%; + } + + 87.5% { + background-position: 0% 25%; + } + + to { + background-position: 50% 0%; + } + } + + .music-type { + color: var(--fg-muted-neutral-1); + } + + .music-artist { + color: var(--fg-muted-neutral-2); + } + } + + & > div { + display: flex; + flex: 1; + flex-direction: column; + justify-content: space-between; + gap: 0.25rem; + margin-inline-end: 0.5rem; + margin-block: 0.25rem; + min-width: 0; + min-width: 0; + line-height: 1; + } + + a { + position: absolute; + z-index: 1; + inset: 0; + border-radius: inherit; + } + } + } + + .music-type { + color: var(--fg-muted-1); + font-size: var(--font-size-xx-small); + } + + .music-artist { + color: var(--fg-muted-2); + font-size: var(--font-size-small); + } + + .title { + color: var(--fg-color); + font-size: var(--font-size-medium); + } + + img { + margin: 0; + border-radius: var(--rounded-corner-small); + } + + &-paginator { + display: flex; + justify-content: center; + gap: 0.25rem; + margin-inline: 1rem; + margin-block: 0; + padding-inline-start: 0; + list-style: none; + + li { + display: flex; + margin-block: 0; + padding-inline-start: 0; + height: 0.75rem; + } + + a { + transition: + width var(--transition), + height var(--transition), + background-color var(--transition), + transform var(--transition-bezier); + margin-block: auto; + border-radius: 0.375rem; + background-color: var(--fg-muted-1); + width: 1.5rem; + height: 0.375rem; + + &:hover { + background-color: var(--fg-muted-2); + } + + &:active { + transform: var(--active); + } + + &.active { + background-color: var(--fg-color); + width: 0.75rem; + height: 0.75rem; + } + } + } + } + + .toc { + display: flex; + flex-direction: column; + max-height: calc(var(--content-width) / 3); + + @media only screen and (max-width: 720px) { + max-height: calc(var(--content-width) / 2); + } + + .title { + margin-block-end: 1rem; + } + + .overshoot { + flex: 1; + margin: -1rem; + padding: 1rem; + overflow: auto; + scrollbar-width: thin; + + ul { + margin-block: 0; + padding-inline-start: 1rem; + color: var(--fg-muted-1); + font-size: var(--font-size-small); + } + + li { + margin-block: 0; + padding-inline-start: 0; + + &::marker { + color: inherit; + } + } + + a { + color: inherit; + text-decoration: none; + } + } + } + + .status-page { + display: none; + margin-block: auto; + text-align: center; + + .title, + p { + color: var(--fg-muted-2); + } + + .icon { + margin-inline: auto; + color: var(--fg-muted-1); + font-size: 3rem; + } + + p { + margin-block: 0; + } + } +} diff --git a/sass/mods/_compact-sidebar.scss b/sass/mods/_compact-sidebar.scss new file mode 100644 index 0000000..3169af3 --- /dev/null +++ b/sass/mods/_compact-sidebar.scss @@ -0,0 +1,44 @@ +@use "../citrine/abstracts/functions" as *; +@use "../citrine/abstracts/mixins" as *; + +#site-sidebar-header { + transform-origin: rpx(18) 50%; + border-radius: var(--rounded-corner); + + img, + .icon { + border-radius: var(--rounded-corner-small); + } + + img { + width: 2rem; + height: 2rem; + } + + .icon { + padding: 0.375rem; + } + + div { + gap: 0; + line-height: 1.125; + } +} + +#sidebar-nav { + a { + transform-origin: rpx(18) 50%; + border-radius: var(--rounded-corner); + padding: 0.25rem; + + &.new .icon::after { + inset-block-start: rpx(-2); + inset-inline-end: rpx(-2); + } + } + + .icon { + border-radius: var(--rounded-corner-small); + padding: 0.375rem; + } +} diff --git a/sass/mods/_flat.scss b/sass/mods/_flat.scss new file mode 100644 index 0000000..92e04da --- /dev/null +++ b/sass/mods/_flat.scss @@ -0,0 +1,27 @@ +@use "../citrine/abstracts/functions" as *; + +:root { + --color-opacity-1: 0.15; + --color-opacity-2: 0.25; + --edge-highlight: 0 0 0 transparent; + --shadow: 0 0 0 transparent; + --shadow-raised: 0 0 0 transparent; + --shadow-glass: 0 0 0 transparent; + --shadow-glow: 0 0 0 transparent; +} + +.buttons .suggested, +#site-nav a.active .icon { + box-shadow: none; + background-image: none; + background-color: var(--accent-color); + text-shadow: none; +} + +.buttons .suggested:hover { + opacity: var(--dim-opacity); +} + +.crt { + outline: rpx(1) solid var(--bg-muted-2); +} diff --git a/sass/mods/_pill-tags.scss b/sass/mods/_pill-tags.scss new file mode 100644 index 0000000..37985cf --- /dev/null +++ b/sass/mods/_pill-tags.scss @@ -0,0 +1,27 @@ +@use "../citrine/abstracts/mixins" as *; + +.article-details .tags { + gap: 0.25rem; + margin-block-end: 0 !important; + + a { + @include chip; + display: flex; + color: var(--fg-muted-1); + font-weight: var(--font-weight-semi-bold); + line-height: 1.5rem; + + &:hover { + background-color: var(--bg-muted-2); + color: var(--fg-muted-2); + } + + &:active { + transform: var(--active); + } + + &::before { + content: none; + } + } +} diff --git a/sass/mods/_skeuomorphic.scss b/sass/mods/_skeuomorphic.scss new file mode 100644 index 0000000..7b382ea --- /dev/null +++ b/sass/mods/_skeuomorphic.scss @@ -0,0 +1,30 @@ +// UNFINISHED, AND LIKELY WILL NEVER BE +@use "../citrine/abstracts/functions" as *; + +#site-nav { + a, + label, + button, + summary { + outline: rpx(1) solid var(--bg-muted-2); + box-shadow: var(--edge-highlight), var(--shadow); + background-image: linear-gradient( + to bottom, + var(--bg-muted-2), + var(--bg-muted-1) 50%, + transparent 50%, + var(--bg-muted-2) + ); + + .icon { + background-image: conic-gradient( + var(--bg-muted-2), + var(--bg-muted-1) 33%, + transparent, + var(--bg-muted-2) 66%, + transparent, + var(--bg-muted-2) + ); + } + } +} diff --git a/sass/style.scss b/sass/style.scss new file mode 100644 index 0000000..2288edd --- /dev/null +++ b/sass/style.scss @@ -0,0 +1,40 @@ +/* +* Ametrine v0.1.0-alpha (https://ametrine.daudix.one) +* Copyright 2025 David "Daudix" Lapshin and contributors +* Licensed under MIT (https://codeberg.org/daudix/ametrine/raw/branch/main/LICENSE.txt) +*/ + +@use "layout/footer"; +@use "layout/full-bleed"; +@use "layout/grid"; +@use "layout/header"; +@use "layout/main-content"; +@use "layout/sidebar"; + +@use "components/alerts"; +@use "components/article-details"; +@use "components/article-list"; +@use "components/article-nav"; +@use "components/audio"; +@use "components/banner"; +@use "components/comments"; +@use "components/emoji"; +@use "components/footnotes"; +@use "components/giallo"; +@use "components/icon"; +@use "components/iframe"; +@use "components/marquee"; +@use "components/not-found"; +@use "components/pre-container"; +@use "components/search-modal"; +@use "components/skip-to-content"; +@use "components/statements"; +@use "components/tag-chips"; +@use "components/zola-anchor"; +@use "components/zola-error"; + +// Mods +// @use "mods/compact-sidebar"; +// @use "mods/flat"; +// @use "mods/pill-tags"; +// @use "mods/skeuomorphic"; // UNFINISHED, AND LIKELY WILL NEVER BE diff --git a/scripts/audio-button.js b/scripts/audio-button.js new file mode 100644 index 0000000..e76d3fc --- /dev/null +++ b/scripts/audio-button.js @@ -0,0 +1,9 @@ +const audioButtons = document.querySelectorAll(".audio"); + +audioButtons.forEach((button) => { + button.addEventListener("click", (event) => playAudio(button.dataset.audio)); +}); + +function playAudio(url) { + new Audio(url).play(); +} diff --git a/scripts/closable.js b/scripts/closable.js new file mode 100644 index 0000000..a28774c --- /dev/null +++ b/scripts/closable.js @@ -0,0 +1,34 @@ +const closable = document.querySelectorAll("details.closable"); + +closable.forEach((detail) => { + detail.addEventListener("toggle", () => { + if (detail.open) setTargetDetail(detail); + }); +}); + +function setTargetDetail(targetDetail) { + closable.forEach((detail) => { + if (detail !== targetDetail) { + detail.open = false; + } + }); +} + +document.addEventListener("click", function (event) { + const isClickInsideDetail = [...closable].some((detail) => + detail.contains(event.target), + ); + + if (!isClickInsideDetail) { + closable.forEach((detail) => { + detail.open = false; + }); + } +}); + +const expandSidebar = document.getElementById("expand-sidebar"); +const mainContent = document.getElementById("main-content"); + +mainContent.addEventListener("click", () => { + if (expandSidebar.checked) expandSidebar.checked = false; +}); diff --git a/scripts/deanchor.js b/scripts/deanchor.js new file mode 100644 index 0000000..94fbee5 --- /dev/null +++ b/scripts/deanchor.js @@ -0,0 +1,10 @@ +const main = document.querySelector("#main-content"); + +main.addEventListener("animationend", () => { + const hash = decodeURIComponent(location.hash); + if ( + ["#↑", "#↓", "#←", "#→", "#up", "#down", "#left", "#right"].includes(hash) + ) { + history.replaceState(null, "", " "); + } +}); diff --git a/scripts/fuse.js b/scripts/fuse.js new file mode 100644 index 0000000..4357f5a --- /dev/null +++ b/scripts/fuse.js @@ -0,0 +1,9 @@ +/** + * Fuse.js v7.2.0 - Lightweight fuzzy-search (http://fusejs.io) + * + * Copyright (c) 2026 Kiro Risk (http://kiro.me) + * All Rights Reserved. Apache Software License 2.0 + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +var e,t;e=this,t=function(){"use strict";function e(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);t&&(r=r.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,r)}return n}function t(t){for(var n=1;ne.length)&&(t=e.length);for(var n=0,r=new Array(t);n0&&void 0!==arguments[0]?arguments[0]:{},n=t.getFn,i=void 0===n?j.getFn:n,u=t.fieldNormWeight,o=void 0===u?j.fieldNormWeight:u;r(this,e),this.norm=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:3,n=new Map,r=Math.pow(10,t);return{get:function(t){var i=t.match(L).length;if(n.has(i))return n.get(i);var u=1/Math.pow(i,.5*e),o=parseFloat(Math.round(u*r)/r);return n.set(i,o),o},clear:function(){n.clear()}}}(o,3),this.getFn=i,this.isCreated=!1,this.setIndexRecords()}return u(e,[{key:"setSources",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.docs=e}},{key:"setIndexRecords",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.records=e}},{key:"setKeys",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];this.keys=t,this._keysMap={},t.forEach((function(t,n){e._keysMap[t.id]=n}))}},{key:"create",value:function(){var e=this;!this.isCreated&&this.docs.length&&(this.isCreated=!0,A(this.docs[0])?this.docs.forEach((function(t,n){e._addString(t,n)})):this.docs.forEach((function(t,n){e._addObject(t,n)})),this.norm.clear())}},{key:"add",value:function(e){var t=this.size();A(e)?this._addString(e,t):this._addObject(e,t)}},{key:"removeAt",value:function(e){this.records.splice(e,1);for(var t=e,n=this.size();t=0;t-=1)this.records.splice(e[t],1);for(var n=0,r=this.records.length;n2&&void 0!==arguments[2]?arguments[2]:{},r=n.getFn,i=void 0===r?j.getFn:r,u=n.fieldNormWeight,o=void 0===u?j.fieldNormWeight:u,c=new I({getFn:i,fieldNormWeight:o});return c.setKeys(e.map(_)),c.setSources(t),c.create(),c}var $=32;function R(e,t,n){var r=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},i=r.location,u=void 0===i?j.location:i,o=r.distance,c=void 0===o?j.distance:o,s=r.threshold,a=void 0===s?j.threshold:s,h=r.findAllMatches,l=void 0===h?j.findAllMatches:h,f=r.minMatchCharLength,d=void 0===f?j.minMatchCharLength:f,v=r.includeMatches,g=void 0===v?j.includeMatches:v,y=r.ignoreLocation,p=void 0===y?j.ignoreLocation:y;if(t.length>$)throw new Error("Pattern length exceeds max of ".concat($,"."));for(var A,m=t.length,C=e.length,k=Math.max(0,Math.min(u,C)),F=a,b=k,M=function(e,t){var n=e/m;if(p)return n;var r=Math.abs(k-t);return c?n+r/c:r?1:n},E=d>1||g,D=E?Array(C):[];(A=e.indexOf(t,b))>-1;){var B=M(0,A);if(F=Math.min(B,F),b=A+m,E)for(var x=0;x=R;z-=1){var T=z-1,K=n[e[T]];if(E&&(D[T]=+!!K),W[z]=(W[z+1]<<1|1)&K,L&&(W[z]|=(_[z+1]|_[z])<<1|1|_[z+1]),W[z]&O&&(S=M(L,T))<=F){if(F=S,(b=T)<=k)break;R=Math.max(1,2*k-b)}}if(M(L+1,k)>F)break;_=W}var U={isMatch:b>=0,score:Math.max(.001,S)};if(E){var q=function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[],t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:j.minMatchCharLength,n=[],r=-1,i=-1,u=0,o=e.length;u=t&&n.push([r,i]),r=-1)}return e[u-1]&&u-r>=t&&n.push([r,u-1]),n}(D,d);q.length?g&&(U.indices=q):U.isMatch=!1}return U}function P(e){for(var t={},n=0,r=e.length;n1&&void 0!==arguments[1]?arguments[1]:{},u=i.location,o=void 0===u?j.location:u,c=i.threshold,s=void 0===c?j.threshold:c,a=i.distance,h=void 0===a?j.distance:a,l=i.includeMatches,f=void 0===l?j.includeMatches:l,d=i.findAllMatches,v=void 0===d?j.findAllMatches:d,g=i.minMatchCharLength,y=void 0===g?j.minMatchCharLength:g,p=i.isCaseSensitive,A=void 0===p?j.isCaseSensitive:p,m=i.ignoreDiacritics,C=void 0===m?j.ignoreDiacritics:m,k=i.ignoreLocation,F=void 0===k?j.ignoreLocation:k;if(r(this,e),this.options={location:o,threshold:s,distance:h,includeMatches:f,findAllMatches:v,minMatchCharLength:y,isCaseSensitive:A,ignoreDiacritics:C,ignoreLocation:F},t=A?t:t.toLowerCase(),t=C?W(t):t,this.pattern=t,this.chunks=[],this.pattern.length){var b=function(e,t){n.chunks.push({pattern:e,alphabet:P(e),startIndex:t})},M=this.pattern.length;if(M>$){for(var E=0,D=M%$,B=M-D;E1&&void 0!==arguments[1]?arguments[1]:{},o=u.location,c=void 0===o?j.location:o,s=u.threshold,a=void 0===s?j.threshold:s,h=u.distance,l=void 0===h?j.distance:h,f=u.includeMatches,d=void 0===f?j.includeMatches:f,v=u.findAllMatches,g=void 0===v?j.findAllMatches:v,y=u.minMatchCharLength,p=void 0===y?j.minMatchCharLength:y,A=u.isCaseSensitive,m=void 0===A?j.isCaseSensitive:A,C=u.ignoreDiacritics,k=void 0===C?j.ignoreDiacritics:C,F=u.ignoreLocation,b=void 0===F?j.ignoreLocation:F;return r(this,n),(i=t.call(this,e))._bitapSearch=new z(e,{location:c,threshold:a,distance:l,includeMatches:d,findAllMatches:g,minMatchCharLength:p,isCaseSensitive:m,ignoreDiacritics:k,ignoreLocation:b}),i}return u(n,[{key:"search",value:function(e){return this._bitapSearch.searchIn(e)}}],[{key:"type",get:function(){return"fuzzy"}},{key:"multiRegex",get:function(){return/^"(.*)"$/}},{key:"singleRegex",get:function(){return/^(.*)$/}}]),n}(T),X=function(e){c(n,e);var t=l(n);function n(e){return r(this,n),t.call(this,e)}return u(n,[{key:"search",value:function(e){for(var t,n=0,r=[],i=this.pattern.length;(t=e.indexOf(this.pattern,n))>-1;)n=t+i,r.push([t,n-1]);var u=!!r.length;return{isMatch:u,score:u?0:1,indices:r}}}],[{key:"type",get:function(){return"include"}},{key:"multiRegex",get:function(){return/^'"(.*)"$/}},{key:"singleRegex",get:function(){return/^'(.*)$/}}]),n}(T),Y=[U,X,J,Q,G,V,q,H],Z=Y.length,ee=/ +(?=(?:[^\"]*\"[^\"]*\")*[^\"]*$)/,te=new Set([H.type,X.type]),ne=function(){function e(t){var n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},i=n.isCaseSensitive,u=void 0===i?j.isCaseSensitive:i,o=n.ignoreDiacritics,c=void 0===o?j.ignoreDiacritics:o,s=n.includeMatches,a=void 0===s?j.includeMatches:s,h=n.minMatchCharLength,l=void 0===h?j.minMatchCharLength:h,f=n.ignoreLocation,d=void 0===f?j.ignoreLocation:f,v=n.findAllMatches,g=void 0===v?j.findAllMatches:v,y=n.location,p=void 0===y?j.location:y,A=n.threshold,m=void 0===A?j.threshold:A,C=n.distance,k=void 0===C?j.distance:C;r(this,e),this.query=null,this.options={isCaseSensitive:u,ignoreDiacritics:c,includeMatches:a,minMatchCharLength:l,findAllMatches:g,ignoreLocation:d,location:p,threshold:m,distance:k},t=u?t:t.toLowerCase(),t=c?W(t):t,this.pattern=t,this.query=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};return e.split("|").map((function(e){for(var n=e.trim().split(ee).filter((function(e){return e&&!!e.trim()})),r=[],i=0,u=n.length;i2&&void 0!==arguments[2]?arguments[2]:{}).auto,r=void 0===n||n;return he(e)||(e=le(e)),function e(n){var i=Object.keys(n),u=function(e){return!!e[se]}(n);if(!u&&i.length>1&&!he(n))return e(le(n));if(function(e){return!g(e)&&k(e)&&!he(e)}(n)){var o=u?n[se]:i[0],c=u?n[ae]:n[o];if(!A(c))throw new Error(function(e){return"Invalid value for key ".concat(e)}(o));var s={keyId:w(o),pattern:c};return r&&(s.searcher=ue(c,t)),s}var a={children:[],operator:i[0]};return i.forEach((function(t){var r=n[t];g(r)&&r.forEach((function(t){a.children.push(e(t))}))})),a}(e)}function de(e,t){var n=t.ignoreFieldNorm,r=void 0===n?j.ignoreFieldNorm:n,i=1;e.matches.forEach((function(e){var t=e.key,n=e.norm,u=e.score,o=t?t.weight:null;i*=Math.pow(0===u&&o?Number.EPSILON:u,(o||1)*(r?1:n))})),e.score=i}var ve=function(){function e(t){r(this,e),this.limit=t,this.heap=[]}return u(e,[{key:"size",get:function(){return this.heap.length}},{key:"shouldInsert",value:function(e){return this.size0;){var n=e-1>>1;if(t[e].score<=t[n].score)break;var r=t[e];t[e]=t[n],t[n]=r,e=n}}},{key:"_sinkDown",value:function(e){var t=this.heap,n=t.length,r=e;do{var i=2*(e=r)+1,u=2*e+2;if(it[r].score&&(r=i),ut[r].score&&(r=u),r!==e){var o=t[e];t[e]=t[r],t[r]=o}}while(r!==e)}}]),e}();function ge(e,t){var n=e.matches;t.matches=[],F(n)&&n.forEach((function(e){if(F(e.indices)&&e.indices.length){var n={indices:e.indices,value:e.value};e.key&&(n.key=e.key.src),e.idx>-1&&(n.refIndex=e.idx),t.matches.push(n)}}))}function ye(e,t){t.score=e.score}var pe=function(){function e(n){var i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},u=arguments.length>2?arguments[2]:void 0;r(this,e),this.options=t(t({},j),i),this.options.useExtendedSearch,this._keyStore=new x(this.options.keys),this.setCollection(n,u),this._lastQuery=null,this._lastSearcher=null}return u(e,[{key:"_getSearcher",value:function(e){if(this._lastQuery===e)return this._lastSearcher;var t=ue(e,this.options);return this._lastQuery=e,this._lastSearcher=t,t}},{key:"setCollection",value:function(e,t){if(this._docs=e,t&&!(t instanceof I))throw new Error("Incorrect 'index' type");this._myIndex=t||N(this.options.keys,this._docs,{getFn:this.options.getFn,fieldNormWeight:this.options.fieldNormWeight})}},{key:"add",value:function(e){F(e)&&(this._docs.push(e),this._myIndex.add(e))}},{key:"remove",value:function(){for(var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:function(){return!1},t=[],n=[],r=0,i=this._docs.length;r=0;u-=1)this._docs.splice(n[u],1);this._myIndex.removeAll(n)}return t}},{key:"removeAt",value:function(e){this._docs.splice(e,1),this._myIndex.removeAt(e)}},{key:"getIndex",value:function(){return this._myIndex}},{key:"search",value:function(e){var t,n=(arguments.length>1&&void 0!==arguments[1]?arguments[1]:{}).limit,r=void 0===n?-1:n,i=this.options,u=i.includeMatches,o=i.includeScore,c=i.shouldSort,s=i.sortFn,a=i.ignoreFieldNorm;if(m(r)&&r>0&&A(e)){var h=new ve(r);A(this._docs[0])?this._searchStringList(e,{heap:h,ignoreFieldNorm:a}):this._searchObjectList(e,{heap:h,ignoreFieldNorm:a}),t=h.extractSorted(s)}else(function(e,t){var n=t.ignoreFieldNorm,r=void 0===n?j.ignoreFieldNorm:n;e.forEach((function(e){de(e,{ignoreFieldNorm:r})}))})(t=A(e)?A(this._docs[0])?this._searchStringList(e):this._searchObjectList(e):this._searchLogical(e),{ignoreFieldNorm:a}),c&&t.sort(s),m(r)&&r>-1&&(t=t.slice(0,r));return function(e,t){var n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},r=n.includeMatches,i=void 0===r?j.includeMatches:r,u=n.includeScore,o=void 0===u?j.includeScore:u,c=[];return i&&c.push(ge),o&&c.push(ye),e.map((function(e){var n=e.idx,r={item:t[n],refIndex:n};return c.length&&c.forEach((function(t){t(e,r)})),r}))}(t,this._docs,{includeMatches:u,includeScore:o})}},{key:"_searchStringList",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.heap,r=t.ignoreFieldNorm,i=this._getSearcher(e),u=this._myIndex.records,o=n?null:[];return u.forEach((function(e){var t=e.v,u=e.i,c=e.n;if(F(t)){var s=i.searchIn(t),a=s.isMatch,h=s.score,l=s.indices;if(a){var f={item:t,idx:u,matches:[{score:h,value:t,norm:c,indices:l}]};n?(de(f,{ignoreFieldNorm:r}),n.shouldInsert(f.score)&&n.insert(f)):o.push(f)}}})),o}},{key:"_searchLogical",value:function(e){var t=this,n=fe(e,this.options),r=function e(n,r,i){if(!n.children){var u=n.keyId,o=n.searcher,c=t._findMatches({key:t._keyStore.get(u),value:t._myIndex.getValueForItemAtKeyId(r,u),searcher:o});return c&&c.length?[{idx:i,item:r,matches:c}]:[]}for(var s=[],a=0,h=n.children.length;a1&&void 0!==arguments[1]?arguments[1]:{},r=n.heap,i=n.ignoreFieldNorm,u=this._getSearcher(e),o=this._myIndex,c=o.keys,s=o.records,a=r?null:[];return s.forEach((function(e){var n=e.$,o=e.i;if(F(n)){var s=[];if(c.forEach((function(e,r){s.push.apply(s,f(t._findMatches({key:e,value:n[r],searcher:u})))})),s.length){var h={idx:o,item:n,matches:s};r?(de(h,{ignoreFieldNorm:i}),r.shouldInsert(h.score)&&r.insert(h)):a.push(h)}}})),a}},{key:"_findMatches",value:function(e){var t=e.key,n=e.value,r=e.searcher;if(!F(n))return[];var i=[];if(g(n))n.forEach((function(e){var n=e.v,u=e.i,o=e.n;if(F(n)){var c=r.searchIn(n),s=c.isMatch,a=c.score,h=c.indices;s&&i.push({score:a,key:t,value:n,idx:u,norm:o,indices:h})}}));else{var u=n.v,o=n.n,c=r.searchIn(u),s=c.isMatch,a=c.score,h=c.indices;s&&i.push({score:a,key:t,value:u,norm:o,indices:h})}return i}}]),e}();return pe.version="7.2.0",pe.createIndex=N,pe.parseIndex=function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=t.getFn,r=void 0===n?j.getFn:n,i=t.fieldNormWeight,u=void 0===i?j.fieldNormWeight:i,o=e.keys,c=e.records,s=new I({getFn:r,fieldNormWeight:u});return s.setKeys(o),s.setIndexRecords(c),s},pe.config=j,ie(ne),pe.use=function(){for(var e=arguments.length,t=new Array(e),n=0;n { + const text = container.firstElementChild; + if (!text) return; + + const style = getComputedStyle(container); + const paddingLeft = parseFloat(style.paddingLeft); + const paddingRight = parseFloat(style.paddingRight); + + const containerInnerWidth = + container.clientWidth - paddingLeft - paddingRight; + + const textWidth = text.offsetWidth; + + text.style.setProperty("--text-width", `${textWidth}`); + text.style.setProperty("--container-width", `${containerInnerWidth}`); + + container.classList.toggle( + "overshoot-row", + textWidth > containerInnerWidth, + ); + }); +} + +window.addEventListener("load", updateMarquees); +window.addEventListener("resize", updateMarquees); diff --git a/scripts/paginator.js b/scripts/paginator.js new file mode 100644 index 0000000..334f85e --- /dev/null +++ b/scripts/paginator.js @@ -0,0 +1,23 @@ +const carousel = document.querySelector(".music .carousel"); +const items = document.querySelectorAll(".music .carousel > li"); +const links = document.querySelectorAll(".music-paginator li a"); + +const observer = new IntersectionObserver( + (entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + links.forEach((a) => a.classList.remove("active")); + const active = document.querySelector( + `.music-paginator a[href="#${entry.target.id}"]`, + ); + active?.classList.add("active"); + } + }); + }, + { + root: carousel, + threshold: 0.6, + }, +); + +items.forEach((item) => observer.observe(item)); diff --git a/scripts/search-fuse.js b/scripts/search-fuse.js new file mode 100644 index 0000000..df56e7d --- /dev/null +++ b/scripts/search-fuse.js @@ -0,0 +1,178 @@ +// Based on https://codeberg.org/daudix/duckquill/issues/101#issuecomment-2377169 +let searchSetup = false; +let fuse; + +async function initIndex() { + if (searchSetup) return; + + let data; + if (typeof window.searchIndex !== "undefined") { + data = window.searchIndex; + } else { + const response = await fetch( + `/search_index.${document.documentElement.lang}.json`, + ); + if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`); + data = await response.json(); + } + + const options = { + includeScore: false, + includeMatches: true, + ignoreLocation: true, + threshold: 0.15, + keys: [ + { name: "title", weight: 3 }, + { name: "description", weight: 2 }, + { name: "body", weight: 1 }, + ], + }; + + fuse = new Fuse(data, options); + searchSetup = true; + + console.log("Search index initialized successfully"); +} + +function debounce(actual_fn, wait) { + let timeoutId; + + return (...args) => { + clearTimeout(timeoutId); + + timeoutId = setTimeout(() => { + actual_fn(...args); + }, wait); + }; +} + +function initSearch() { + const searchModal = document.getElementById("search-modal"); // Full-screen modal + const searchModalContent = document.getElementById("search-modal-content"); // Actual modal box + const searchInput = document.getElementById("search-input"); // Search input + const searchResults = document.getElementById("search-results"); // Search results + const searchButton = document.getElementById("search"); // Search button + const MAX_ITEMS = 10; + const MAX_RESULTS = 4; + + let currentTerm = ""; + + // Open search modal when clicking the search button + if (searchButton) { + searchButton.addEventListener("click", function () { + searchModal.classList.add("active"); + initIndex(); + searchModal.addEventListener( + "transitionend", + function handler() { + searchInput.focus(); + searchModal.removeEventListener("transitionend", handler); + }, + { once: true }, + ); + }); + } + + // Open search modal on "/" key press + window.addEventListener("keydown", (event) => { + if ( + event.key === "/" && + document.activeElement.tagName !== "INPUT" && + document.activeElement.tagName !== "TEXTAREA" + ) { + event.preventDefault(); + initIndex(); + searchModal.classList.add("active"); + searchModal.addEventListener( + "transitionend", + function handler() { + searchInput.focus(); + searchModal.removeEventListener("transitionend", handler); + }, + { once: true }, + ); + } + }); + + // Close search modal on Escape key + window.addEventListener("keydown", (event) => { + if (event.key === "Escape") { + searchModal.classList.remove("active"); + } + }); + + // Close search modal when clicking outside search-modal-content + searchModal.addEventListener("click", function (e) { + if (!searchModalContent.contains(e.target)) { + searchModal.classList.remove("active"); + } + }); + + // Prevent clicks inside modal content from closing it + searchModalContent.addEventListener("click", function (e) { + e.stopPropagation(); // Stops event from reaching searchModal click handler + }); + + // Search input event + searchInput.addEventListener( + "keyup", + debounce(async function () { + var term = searchInput.value.trim(); + if (term === currentTerm) return; + + searchResults.style.display = term === "" ? "none" : "flex"; + searchResults.innerHTML = ""; // Clear previous results + currentTerm = term; + if (term === "") return; + + await initIndex(); + var results = fuse.search(term, { limit: MAX_ITEMS }); + if (results.length === 0) { + searchResults.style.display = "none"; + return; + } + + // Insert formatted search result items + for (const result of results) { + searchResults.innerHTML += makeTeaser(result, term); + } + }, 150), + ); + + function makeTeaser(result, searchVal) { + const TEASER_SIZE = 20; + let output = `
${result.item.title}`; + + for (const match of result.matches) { + if (match.key === "title") continue; + + const indices = match.indices.sort((a, b) => Math.abs(a[1] - a[0] - searchVal.length) - Math.abs(b[1] - b[0] - searchVal.length)).slice(0, MAX_RESULTS); + const value = match.value; + + for (const ind of indices) { + const start = Math.max(0, ind[0] - TEASER_SIZE); + const end = Math.min(value.length - 1, ind[1] + TEASER_SIZE); + output += "" + + value.substring(start, ind[0]) + + `${value.substring(ind[0], ind[1] + 1)}` + + value.substring(ind[1] + 1, end) + + ""; + } + + if (match.indices.length > MAX_RESULTS) { + const moreMatchesText = document.getElementById("more-matches-text").textContent; + output += `${moreMatchesText}`.replace("$MATCHES", `+${match.indices.length - MAX_RESULTS}`); + } + } + return output + "
"; + } +} + +if ( + document.readyState === "complete" || + (document.readyState !== "loading" && !document.documentElement.doScroll) +) { + initSearch(); +} else { + document.addEventListener("DOMContentLoaded", initSearch); +} diff --git a/styles/ametrine.css b/styles/ametrine.css new file mode 100644 index 0000000..2a20007 --- /dev/null +++ b/styles/ametrine.css @@ -0,0 +1 @@ +:root{--font-sans-serif: "Commissioner";--font-monospace: "Commit Mono"}@font-face{font-style:normal;font-weight:100 900;src:url("/fonts/commissioner.woff2?v=1.012.1") format("woff2");font-family:"Commissioner";font-display:swap;ascent-override:96.3%;descent-override:25%}@font-face{font-style:italic;font-weight:100 900;src:url("/fonts/commissioner.woff2?v=1.012.1") format("woff2");font-family:"Commissioner";font-display:swap;font-variation-settings:"slnt" -12;ascent-override:96.3%;descent-override:25%}@font-face{font-style:normal;font-weight:100 900;src:url("/fonts/commit-mono.woff2?v=1.143") format("woff2");font-family:"Commit Mono";font-display:swap}@font-face{font-style:italic;font-weight:100 900;src:url("/fonts/commit-mono.woff2?v=1.143") format("woff2");font-family:"Commit Mono";font-display:swap;font-variation-settings:"ital" 1}:root{--bg-overlay: linear-gradient( rgba(255, 255, 255, var(--dim-opacity)), rgba(255, 255, 255, var(--dim-opacity)) );--fg-color: #29242a;--fg-contrast: #fcfcfa;--red-fg: hsl(342.0779, 72%, 58%);--red-h: 342.0779;--red-s: 72%;--red-l: 58%;--orange-fg: hsl(15.771428, 74%, 54%);--orange-h: 15.771428;--orange-s: 74%;--orange-l: 54%;--yellow-fg: hsl(34.639175, 91%, 42%);--yellow-h: 34.639175;--yellow-s: 91%;--yellow-l: 42%;--green-fg: hsl(153.7815, 61%, 38%);--green-h: 153.7815;--green-s: 61%;--green-l: 38%;--blue-fg: hsl(192, 71%, 38%);--blue-h: 192;--blue-s: 71%;--blue-l: 38%;--purple-fg: hsl(254.11766, 44%, 55%);--purple-h: 254.11766;--purple-s: 44%;--purple-l: 55%;--red-bg-1: hsla( var(--red-h), var(--red-s), var(--red-l), var(--color-opacity-1) );--red-bg-2: hsla( var(--red-h), var(--red-s), var(--red-l), var(--color-opacity-2) );--orange-bg-1: hsla( var(--orange-h), var(--orange-s), var(--orange-l), var(--color-opacity-1) );--orange-bg-2: hsla( var(--orange-h), var(--orange-s), var(--orange-l), var(--color-opacity-2) );--yellow-bg-1: hsla( var(--yellow-h), var(--yellow-s), var(--yellow-l), var(--color-opacity-1) );--yellow-bg-2: hsla( var(--yellow-h), var(--yellow-s), var(--yellow-l), var(--color-opacity-2) );--green-bg-1: hsla( var(--green-h), var(--green-s), var(--green-l), var(--color-opacity-1) );--green-bg-2: hsla( var(--green-h), var(--green-s), var(--green-l), var(--color-opacity-2) );--blue-bg-1: hsla( var(--blue-h), var(--blue-s), var(--blue-l), var(--color-opacity-1) );--blue-bg-2: hsla( var(--blue-h), var(--blue-s), var(--blue-l), var(--color-opacity-2) );--purple-bg-1: hsla( var(--purple-h), var(--purple-s), var(--purple-l), var(--color-opacity-1) );--purple-bg-2: hsla( var(--purple-h), var(--purple-s), var(--purple-l), var(--color-opacity-2) );--red-fg-muted-1: hsla( var(--red-h), var(--red-s), calc(var(--red-l) - var(--red-l) * 0.85), var(--fg-muted-1-opacity) );--red-fg-muted-2: hsla( var(--red-h), var(--red-s), calc(var(--red-l) - var(--red-l) * 0.85), var(--fg-muted-2-opacity) );--orange-fg-muted-1: hsla( var(--orange-h), var(--orange-s), calc(var(--orange-l) - var(--orange-l) * 0.85), var(--fg-muted-1-opacity) );--orange-fg-muted-2: hsla( var(--orange-h), var(--orange-s), calc(var(--orange-l) - var(--orange-l) * 0.85), var(--fg-muted-2-opacity) );--yellow-fg-muted-1: hsla( var(--yellow-h), var(--yellow-s), calc(var(--yellow-l) - var(--yellow-l) * 0.85), var(--fg-muted-1-opacity) );--yellow-fg-muted-2: hsla( var(--yellow-h), var(--yellow-s), calc(var(--yellow-l) - var(--yellow-l) * 0.85), var(--fg-muted-2-opacity) );--green-fg-muted-1: hsla( var(--green-h), var(--green-s), calc(var(--green-l) - var(--green-l) * 0.85), var(--fg-muted-1-opacity) );--green-fg-muted-2: hsla( var(--green-h), var(--green-s), calc(var(--green-l) - var(--green-l) * 0.85), var(--fg-muted-2-opacity) );--blue-fg-muted-1: hsla( var(--blue-h), var(--blue-s), calc(var(--blue-l) - var(--blue-l) * 0.85), var(--fg-muted-1-opacity) );--blue-fg-muted-2: hsla( var(--blue-h), var(--blue-s), calc(var(--blue-l) - var(--blue-l) * 0.85), var(--fg-muted-2-opacity) );--purple-fg-muted-1: hsla( var(--purple-h), var(--purple-s), calc(var(--purple-l) - var(--purple-l) * 0.85), var(--fg-muted-1-opacity) );--purple-fg-muted-2: hsla( var(--purple-h), var(--purple-s), calc(var(--purple-l) - var(--purple-l) * 0.85), var(--fg-muted-2-opacity) );--shade-color: rgba(0, 0, 0, 0.07);--bg-muted-neutral-1: rgba(0, 0, 0, var(--color-opacity-1));--bg-muted-neutral-2: rgba(0, 0, 0, var(--color-opacity-2));--fg-muted-neutral-1: rgba(0, 0, 0, var(--fg-muted-1-opacity));--fg-muted-neutral-2: rgba(0, 0, 0, var(--fg-muted-2-opacity));--scanlines-opacity: 0.4;--fg-muted-1-opacity: 0.5;--fg-muted-2-opacity: 0.6;--edge-highlight: inset 0 -0.0625rem 0 rgba(0, 0, 0, var(--color-opacity-1));--content-width: 45rem;--sidebar-width: clamp( calc(var(--content-width) / 3), 20vw, calc(var(--content-width) / 2) );--rounded-corner: 0.75rem;--rounded-corner-small: 0.5rem;--blur: blur(0.75rem) saturate(180%);--printed: contrast(2) brightness(0.75) grayscale(1);--drop-shadow: drop-shadow(0 0.08rem 0.1rem rgba(0, 0, 0, 0.35)) drop-shadow(0 0.4rem 0.7rem rgba(0, 0, 0, 0.18));--font-system-ui: system-ui, sans-serif;--font-ui-monospace: ui-monospace, monospace;--font-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-size-xx-small: 0.625rem;--font-size-x-small: 0.75rem;--font-size-small: 0.875rem;--font-size-medium: 1rem;--font-size-large: 1.25rem;--font-size-x-large: 1.5rem;--font-size-xx-large: 2rem;--font-size-xxx-large: 3rem;--font-weight-thin: 100;--font-weight-extra-light: 200;--font-weight-light: 300;--font-weight-normal: 400;--font-weight-medium: 500;--font-weight-semi-bold: 600;--font-weight-bold: 700;--font-weight-extra-bold: 800;--font-weight-black: 900;--color-opacity-1: 0.1;--color-opacity-2: 0.2;--dim-opacity: 0.8;--disabled-opacity: 0.4;--shadow: 0 0 0 0.0625rem rgba(0, 0, 0, 0.03), 0 0.0625rem 0.1875rem 0.0625rem rgba(0, 0, 0, 0.07), 0 0.125rem 0.375rem 0.125rem rgba(0, 0, 0, 0.03);--shadow-inset: inset 0 0 0 0.0625rem rgba(0, 0, 0, 0.03), inset 0 0.0625rem 0.1875rem 0.0625rem rgba(0, 0, 0, 0.07), inset 0 0.125rem 0.375rem 0.125rem rgba(0, 0, 0, 0.03);--shadow-raised: 0 0 0 0.0625rem rgba(0, 0, 0, 0.06), 0 0.125rem 0.375rem 0.125rem rgba(0, 0, 0, 0.14), 0 0.25rem 0.75rem 0.25rem rgba(0, 0, 0, 0.06);--shadow-glass: 0 0.375rem 0.75rem -0.375rem rgba(0, 0, 0, 0.1);--hover: scale(1.1);--active: scale(0.9);--transition: 0.2s;--transition-longer: 0.4s;--transition-long: 0.8s;--transition-bezier: 0.35s cubic-bezier(0.17, 0.89, 0.32, 1.28);--transition-bezier-longer: 0.525s cubic-bezier(0.17, 0.89, 0.32, 1.28);--icon-arrow-square-out: url(data:image/svg+xml,%3Csvg%20role%3D%22img%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2232%22%20height%3D%2232%22%20fill%3D%22currentColor%22%20viewBox%3D%220%200%20256%20256%22%3E%3Cpath%20d%3D%22M228%2C104a12%2C12%2C0%2C0%2C1-24%2C0V69l-59.51%2C59.51a12%2C12%2C0%2C0%2C1-17-17L187%2C52H152a12%2C12%2C0%2C0%2C1%2C0-24h64a12%2C12%2C0%2C0%2C1%2C12%2C12Zm-44%2C24a12%2C12%2C0%2C0%2C0-12%2C12v64H52V84h64a12%2C12%2C0%2C0%2C0%2C0-24H48A20%2C20%2C0%2C0%2C0%2C28%2C80V208a20%2C20%2C0%2C0%2C0%2C20%2C20H176a20%2C20%2C0%2C0%2C0%2C20-20V140A12%2C12%2C0%2C0%2C0%2C184%2C128Z%22/%3E%3C/svg%3E);--icon-caret-down: url(data:image/svg+xml,%3Csvg%20role%3D%22img%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2232%22%20height%3D%2232%22%20fill%3D%22currentColor%22%20viewBox%3D%220%200%20256%20256%22%3E%3Cpath%20d%3D%22M216.49%2C104.49l-80%2C80a12%2C12%2C0%2C0%2C1-17%2C0l-80-80a12%2C12%2C0%2C0%2C1%2C17-17L128%2C159l71.51-71.52a12%2C12%2C0%2C0%2C1%2C17%2C17Z%22/%3E%3C/svg%3E);--icon-check: url(data:image/svg+xml,%3Csvg%20role%3D%22img%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%2232%22%20height%3D%2232%22%20fill%3D%22currentColor%22%20viewBox%3D%220%200%20256%20256%22%3E%3Cpath%20d%3D%22M232.49%2C80.49l-128%2C128a12%2C12%2C0%2C0%2C1-17%2C0l-56-56a12%2C12%2C0%2C1%2C1%2C17-17L96%2C183%2C215.51%2C63.51a12%2C12%2C0%2C0%2C1%2C17%2C17Z%22/%3E%3C/svg%3E);--accent-color: hsl( var(--accent-light-h), var(--accent-light-s), var(--accent-light-l) );--accent-h: var(--accent-light-h);--accent-s: var(--accent-light-s);--accent-l: var(--accent-light-l);--bg-color: hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.8) );--fg-muted-1: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) - var(--accent-l) * 0.85), var(--fg-muted-1-opacity) );--fg-muted-2: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) - var(--accent-l) * 0.85), var(--fg-muted-2-opacity) );--glass-bg-1: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.7), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.9), var(--dim-opacity) );--glass-bg-2: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.8), var(--dim-opacity) );--crt-bg: radial-gradient( hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.9) ), hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.75) ) 80%, hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.65) ) );--accent-highlight: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--disabled-opacity) );--bg-muted-1: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--color-opacity-1) );--bg-muted-2: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--color-opacity-2) );--l: clamp(0, (l / 0.623 - 1) * -infinity, 1);--a: calc(var(--l) + (var(--dim-opacity) * (1 - var(--l))));--contrast-color: oklch(from var(--accent-color) var(--l) 0 h / var(--a));--shadow-glow: 0 0 0 0.0625rem var(--bg-muted-2), 0 0.125rem 0.375rem 0.125rem var(--bg-muted-1), 0 0.25rem 1.5rem 0.25rem var(--bg-muted-2);--shadow-glow-inset: inset 0 0 0 0.0625rem var(--bg-muted-2), inset 0 0.125rem 0.375rem 0.125rem var(--bg-muted-1), inset 0 0.25rem 1.5rem 0.25rem var(--bg-muted-2);--text-shadow-glow: var(--bg-muted-2) 0 0 0.25rem, var(--accent-color) 0 0 0.75rem;scrollbar-color:var(--accent-color) rgba(0,0,0,0);accent-color:var(--accent-color)}@media(prefers-reduced-motion: no-preference){:root{scroll-behavior:smooth}}@media(prefers-color-scheme: dark){:root{--bg-overlay: linear-gradient( rgba(0, 0, 0, var(--dim-opacity)), rgba(0, 0, 0, var(--dim-opacity)) );--fg-color: #fcfcfa;--fg-contrast: #29242a;--red-fg: hsl(345.18988, 100%, 69%);--red-h: 345.18988;--red-s: 100%;--red-l: 69%;--orange-fg: hsl(19.731544, 96%, 70%);--orange-h: 19.731544;--orange-s: 96%;--orange-l: 70%;--yellow-fg: hsl(44.70588, 100%, 70%);--yellow-h: 44.70588;--yellow-s: 100%;--yellow-l: 70%;--green-fg: hsl(90, 59%, 66%);--green-h: 90;--green-s: 59%;--green-l: 66%;--blue-fg: hsl(186.42857, 71%, 69%);--blue-h: 186.42857;--blue-s: 71%;--blue-l: 69%;--purple-fg: hsl(249.88234, 77%, 78%);--purple-h: 249.88234;--purple-s: 77%;--purple-l: 78%;--red-fg-muted-1: hsla( var(--red-h), var(--red-s), calc(var(--red-l) + (100% - var(--red-l)) * 0.85), var(--fg-muted-1-opacity) );--red-fg-muted-2: hsla( var(--red-h), var(--red-s), calc(var(--red-l) + (100% - var(--red-l)) * 0.85), var(--fg-muted-2-opacity) );--orange-fg-muted-1: hsla( var(--orange-h), var(--orange-s), calc(var(--orange-l) + (100% - var(--orange-l)) * 0.85), var(--fg-muted-1-opacity) );--orange-fg-muted-2: hsla( var(--orange-h), var(--orange-s), calc(var(--orange-l) + (100% - var(--orange-l)) * 0.85), var(--fg-muted-2-opacity) );--yellow-fg-muted-1: hsla( var(--yellow-h), var(--yellow-s), calc(var(--yellow-l) + (100% - var(--yellow-l)) * 0.85), var(--fg-muted-1-opacity) );--yellow-fg-muted-2: hsla( var(--yellow-h), var(--yellow-s), calc(var(--yellow-l) + (100% - var(--yellow-l)) * 0.85), var(--fg-muted-2-opacity) );--green-fg-muted-1: hsla( var(--green-h), var(--green-s), calc(var(--green-l) + (100% - var(--green-l)) * 0.85), var(--fg-muted-1-opacity) );--green-fg-muted-2: hsla( var(--green-h), var(--green-s), calc(var(--green-l) + (100% - var(--green-l)) * 0.85), var(--fg-muted-2-opacity) );--blue-fg-muted-1: hsla( var(--blue-h), var(--blue-s), calc(var(--blue-l) + (100% - var(--blue-l)) * 0.85), var(--fg-muted-1-opacity) );--blue-fg-muted-2: hsla( var(--blue-h), var(--blue-s), calc(var(--blue-l) + (100% - var(--blue-l)) * 0.85), var(--fg-muted-2-opacity) );--purple-fg-muted-1: hsla( var(--purple-h), var(--purple-s), calc(var(--purple-l) + (100% - var(--purple-l)) * 0.85), var(--fg-muted-1-opacity) );--purple-fg-muted-2: hsla( var(--purple-h), var(--purple-s), calc(var(--purple-l) + (100% - var(--purple-l)) * 0.85), var(--fg-muted-2-opacity) );--shade-color: rgba(0, 0, 0, 0.25);--bg-muted-neutral-1: rgba(255, 255, 255, var(--color-opacity-1));--bg-muted-neutral-2: rgba(255, 255, 255, var(--color-opacity-2));--fg-muted-neutral-1: rgba(255, 255, 255, var(--fg-muted-1-opacity));--fg-muted-neutral-2: rgba(255, 255, 255, var(--fg-muted-2-opacity));--scanlines-opacity: 1;--fg-muted-1-opacity: 0.6;--fg-muted-2-opacity: 0.7;--edge-highlight: inset 0 0.0625rem 0 rgba(255, 255, 255, var(--color-opacity-1));--accent-color: hsl( var(--accent-dark-h), var(--accent-dark-s), var(--accent-dark-l) );--accent-h: var(--accent-dark-h);--accent-s: var(--accent-dark-s);--accent-l: var(--accent-dark-l);--bg-color: hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.7), calc(var(--accent-l) - var(--accent-l) * 0.9) );--fg-muted-1: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.85), var(--fg-muted-1-opacity) );--fg-muted-2: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.85), var(--fg-muted-2-opacity) );--glass-bg-1: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) - var(--accent-l) * 0.8), var(--dim-opacity) );--glass-bg-2: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.7), var(--dim-opacity) );--crt-bg: radial-gradient( hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.7) ), hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.9) ) 80%, hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.95) ) );color-scheme:dark}}*,:after,:before{vertical-align:baseline;box-sizing:border-box;margin:0;border:none;background:none;padding:0;color:inherit;font:inherit;-webkit-tap-highlight-color:rgba(0,0,0,0)}button,select{appearance:none}body{-webkit-text-size-adjust:none;text-size-adjust:none;width:100%;min-height:100vh;line-height:1.5}a,button,summary,input[type=button],input[type=checkbox],input[type=color],input[type=file],input[type=image],input[type=radio],input[type=range],input[type=reset],input[type=submit],label,select{cursor:pointer}article,aside,audio,canvas,details,figcaption,figure,footer,header,main,nav,rt,ruby,section,summary{display:block}bdi,mark,meter,output,progress,time{display:inline-block}[hidden],datalist,dialog:not([open]),rp{display:none !important}embed,iframe,img,object,picture,svg,video{display:block;max-width:100%;height:auto}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:none}strong{font-weight:700}em{font-style:italic}code,kbd,pre,samp{font-family:monospace}sub{vertical-align:sub}sup{vertical-align:super}small{font-size:small}table{border-collapse:collapse}@keyframes slide-in-up{from{transform:translateY(-1rem);opacity:0}}@keyframes slide-in-left{from{transform:translateX(1rem);opacity:0}}@keyframes slide-in-right{from{transform:translateX(-1rem);opacity:0}}@keyframes slide-in-up-longer{from{transform:translateY(-2rem);opacity:0}}@keyframes slide-in-down-longer{from{transform:translateY(2rem);opacity:0}}@keyframes slide-in-left-longer{from{transform:translateX(-2rem);opacity:0}}@keyframes slide-in-right-longer{from{transform:translateX(2rem);opacity:0}}@keyframes slide-out-down-longer{to{transform:translateY(-2rem);opacity:0}}@keyframes expand-up-left{from{transform:scale(0.5) translate(-1rem, -1rem);opacity:0}}@keyframes expand-up-right{from{transform:scale(0.5) translate(1rem, -1rem);opacity:0}}@keyframes expand-down-left{from{transform:scale(0.5) translate(-1rem, 1rem);opacity:0}}@keyframes expand-down-right{from{transform:scale(0.5) translate(1rem, 1rem);opacity:0}}:target:not(#main-content){scroll-margin-block-start:6.5rem}:focus-visible,:target:not(#main-content){animation:focus-in var(--transition);outline:.125rem solid var(--accent-color);outline-offset:.125rem}@keyframes focus-in{from{outline:.5rem solid rgba(0,0,0,0);outline-offset:.25rem}}::selection{background-color:var(--accent-color);color:var(--contrast-color)}body{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background-color:var(--bg-color);color:var(--fg-color);font-family:var(--font-sans-serif),var(--font-system-ui),var(--font-emoji);overflow-wrap:break-word}@supports(min-height: 100dvh){body{min-height:100dvh}}@media(prefers-reduced-motion){*,*::before,*::after{animation-duration:0s !important;transition-duration:0s !important}}@-moz-document url-prefix(){@media only screen and (max-width: 960px){.firefox-unblur,.firefox-unblur::before,.firefox-unblur::after{backdrop-filter:none !important}}}body>main,body>nav,body>footer{--padding: 1rem;margin-inline:auto;padding-inline:1rem;max-width:calc(var(--content-width) + var(--padding)*2)}body>footer{margin-block-start:4rem}body>nav{margin-block-end:4rem;border-block-end:0.0625rem solid var(--bg-muted-2)}body>nav ul{display:flex;gap:.25rem;margin-block:.625rem;padding-inline-start:0;list-style:none}@media only screen and (max-width: 480px){body>nav ul{flex-direction:column}}body>nav li{margin-block:0;padding-inline-start:0}p,ul,ol,dl,blockquote,pre{margin-block:1rem}ul ul,ul ol,ol ul,ol ol{margin-block:0}p{text-wrap:pretty;overflow-wrap:break-word}:disabled,[aria-disabled=true]{opacity:var(--disabled-opacity);cursor:not-allowed}h1,h2,h3,h4,h5,h6{transition-duration:var(--transition);transition-property:text-shadow,font-weight;margin-block:2rem 1rem;color:var(--accent-color);font-weight:var(--font-weight-bold);line-height:1;font-variation-settings:"FLAR" 100;letter-spacing:-0.05em;text-decoration:underline;text-decoration-color:var(--bg-muted-1);text-decoration-thickness:.125em;text-wrap:balance;overflow-wrap:break-word}h1:target,h2:target,h3:target,h4:target,h5:target,h6:target{animation:none !important;outline:none !important;font-weight:var(--font-weight-light);text-shadow:var(--text-shadow-glow)}h1{font-size:var(--font-size-xxx-large)}h2{font-size:var(--font-size-xx-large)}h3{font-size:var(--font-size-x-large)}h4{font-size:var(--font-size-large)}h5{font-size:var(--font-size-medium)}h6{font-size:var(--font-size-small)}@media only screen and (max-width: 480px){h1{font-size:var(--font-size-xx-large)}h2{font-size:var(--font-size-x-large)}h3{font-size:var(--font-size-large)}h4{font-size:var(--font-size-medium)}h5{font-size:var(--font-size-small)}h6{font-size:var(--font-size-x-small)}}small{color:var(--fg-muted-2);font-size:var(--font-size-small)}abbr[title]{cursor:help;text-decoration:underline;text-decoration-style:dotted;text-decoration-thickness:0.0625rem}figcaption{color:var(--fg-muted-2);font-size:var(--font-size-small);text-align:center}blockquote{margin:1rem 0;border-inline-start:.25rem solid var(--accent-color);border-start-start-radius:.25rem;border-end-start-radius:.25rem;padding-inline-start:.75rem;color:var(--fg-muted-2)}pre,code,kbd,samp{font-family:var(--font-monospace),var(--font-ui-monospace)}pre{margin:1rem 0;box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);padding:.75rem 1rem;overflow-x:auto;scrollbar-width:thin;line-height:1.25}code:not(pre code),mark,samp,q{transition:all var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight);border-radius:var(--rounded-corner-small);background-color:var(--bg-muted-1);padding-inline:.375rem;text-decoration:none}code:not(pre code){background-color:var(--red-bg-1);padding-block:0.1875rem;color:var(--red-fg);font-size:var(--font-size-small)}mark{color:var(--accent-color)}del{color:var(--red-fg);text-decoration:line-through;text-decoration-thickness:0.0625rem}ins{color:var(--green-fg);text-decoration:underline;text-decoration-thickness:0.0625rem}samp{color:var(--fg-muted-2);font-size:var(--font-size-small)}q{color:var(--fg-muted-2);font-style:italic}u{text-decoration:underline;text-decoration-style:wavy;text-decoration-color:var(--red-fg);text-decoration-thickness:0.0625rem}progress{appearance:none;box-shadow:var(--edge-highlight);border:none;border-radius:9999px;background-color:var(--bg-muted-1);padding:.25rem;width:100%;height:1rem;color:var(--accent-color)}progress:indeterminate{background-color:var(--bg-muted-1)}progress:indeterminate::-moz-progress-bar{background-color:rgba(0,0,0,0)}progress::-webkit-progress-bar{background-color:rgba(0,0,0,0)}progress::-moz-progress-bar{border-radius:9999px;background-color:var(--accent-color)}progress::-webkit-progress-value{border-radius:9999px;background-color:var(--accent-color)}kbd{display:inline-block;transition-duration:var(--transition);transition-property:transform,box-shadow,background-color;cursor:pointer;box-shadow:var(--edge-highlight),inset 0 -0.125rem 0 var(--bg-muted-2);border-radius:var(--rounded-corner-small);background-color:var(--bg-muted-1);padding:0 .375rem;font-size:var(--font-size-small)}kbd:active{transform:translateY(0.125rem);box-shadow:inset 0 0.0625rem 0 var(--bg-muted-2);background-color:var(--bg-muted-2)}a{color:var(--accent-color);font-weight:var(--font-weight-semi-bold);text-decoration-color:var(--accent-highlight);text-decoration-thickness:0.0625rem}a:hover{text-decoration-style:wavy}hr{margin:2rem auto;border:.125rem solid var(--bg-muted-2);border-radius:9999px}dt{font-weight:var(--font-weight-semi-bold)}dd{margin-inline-start:1.5rem;margin-block-end:1rem}aside{float:inline-end;margin-inline-start:1rem;margin-block-end:1rem;box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);padding:1rem;width:calc(var(--content-width)/3)}@media only screen and (max-width: 720px){aside{float:none;margin-inline-start:0;width:100%}}aside :first-child{margin-block-start:0}aside :last-child{margin-block-end:0}details{display:flow-root;transition:padding-block-end var(--transition);margin-block-end:1rem;box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);padding:.75rem 1rem}details::details-content{display:block;opacity:0;transition-behavior:allow-discrete;transition-duration:var(--transition);transition-property:opacity,block-size,content-visibility;block-size:0;overflow:hidden}details[open]{padding-block-end:1rem}details[open]::details-content{opacity:1;block-size:auto;block-size:calc-size(auto,size)}details[open] summary{color:var(--accent-color)}details[open] summary::before{transform:scaleY(-1)}@supports not (block-size: calc-size(auto, size)){details[open] summary~*{animation:slide-in-up var(--transition-bezier)}}details summary{transition:var(--transition);color:var(--fg-muted-1);font-weight:var(--font-weight-semi-bold);list-style:none}details summary::-webkit-details-marker{display:none}details summary::before{-webkit-mask-image:var(--icon);-webkit-mask-size:cover;display:block;mask-image:var(--icon);mask-size:cover;background-color:currentColor;width:1em;height:1em;content:"";--icon: var(--icon-caret-down);display:inline-block;vertical-align:-0.125rem;transition:all var(--transition),transform var(--transition-bezier);margin-inline-end:.25rem}details>:last-child,details>:last-child img{margin-block-end:0}figure{margin-inline:0}ol,ul{padding-inline-start:1.5rem}li{margin-block:.25rem;padding-inline-start:.25rem}li::marker{color:var(--accent-color);font-weight:var(--font-weight-semi-bold)}b,strong{font-weight:var(--font-weight-semi-bold)}.buttons{display:flex;flex-wrap:wrap;justify-content:space-between;align-items:center;gap:.25rem;margin-block-start:2rem}.buttons.centered{justify-content:center}.buttons.start{justify-content:flex-start}.buttons.end{justify-content:flex-end}.buttons.big{gap:.5rem}.buttons a,a.button,button{display:inline-block;transition:background-color var(--transition),color var(--transition),box-shadow var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight);border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);padding:.5rem 1rem;color:var(--fg-muted-1);font-weight:var(--font-weight-semi-bold);font-size:var(--font-size-small);line-height:1.5rem;text-decoration:none}.buttons a:not(:disabled,[aria-disabled=true]):hover,a.button:not(:disabled,[aria-disabled=true]):hover,button:not(:disabled,[aria-disabled=true]):hover{background-color:var(--bg-muted-2);color:var(--fg-muted-2)}.buttons a:not(:disabled,[aria-disabled=true]):active,a.button:not(:disabled,[aria-disabled=true]):active,button:not(:disabled,[aria-disabled=true]):active{transform:var(--active)}.buttons a.suggested,a.button.suggested,button.suggested{box-shadow:var(--edge-highlight),var(--shadow-glow);background-image:linear-gradient(to bottom right, var(--accent-color), var(--accent-highlight));color:var(--contrast-color);text-shadow:var(--text-shadow-glow)}.buttons a.suggested:not(:disabled,[aria-disabled=true]):hover,a.button.suggested:not(:disabled,[aria-disabled=true]):hover,button.suggested:not(:disabled,[aria-disabled=true]):hover{box-shadow:var(--edge-highlight);background-color:var(--accent-color);color:var(--contrast-color)}.buttons a.subtle,a.button.subtle,button.subtle{box-shadow:none;background-color:rgba(0,0,0,0)}.buttons a.subtle:not(:disabled,[aria-disabled=true]):hover,a.button.subtle:not(:disabled,[aria-disabled=true]):hover,button.subtle:not(:disabled,[aria-disabled=true]):hover{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1);color:var(--fg-muted-2)}.buttons a[aria-busy=true],a.button[aria-busy=true],button[aria-busy=true]{animation:progress-shimmer var(--transition-long) ease-in-out alternate infinite;background-image:linear-gradient(to right, transparent 50%, var(--bg-muted-1) 75%, transparent 100%);background-size:200%}@keyframes progress-shimmer{to{background-position-x:-200%}}.buttons.big a,.buttons.big button,a.button.big,button.big{border-radius:9999px;padding:.625rem 1.25rem}.card{box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);padding:1rem}.crt,pre:has(code[data-crt],code[data-lang=""]){position:relative;animation:flicker .25s alternate infinite;margin-inline:auto;box-shadow:var(--shadow-glow);background-image:var(--crt-bg);background-color:rgba(0,0,0,0) !important;padding:0;width:fit-content;max-width:100%;overflow:hidden;color:var(--accent-color) !important;font-weight:var(--font-weight-medium);text-shadow:var(--text-shadow-glow)}@keyframes flicker{25%{opacity:.95}50%{opacity:.85}75%{opacity:1}to{opacity:.9}}.crt::before,.crt::after,pre:has(code[data-crt],code[data-lang=""])::before,pre:has(code[data-crt],code[data-lang=""])::after{position:absolute;inset:0;content:""}.crt::before,pre:has(code[data-crt],code[data-lang=""])::before{opacity:var(--scanlines-opacity);z-index:1;animation:scanlines .1s linear infinite;background-image:repeating-linear-gradient(to bottom, rgba(0, 0, 0, 0.25), rgba(0, 0, 0, 0.25) 0.125rem, transparent 0.125rem, transparent 0.25rem);background-position-y:-0.125rem;background-repeat:no-repeat;pointer-events:none}@keyframes scanlines{to{background-position-y:.125rem}}.crt::after,pre:has(code[data-crt],code[data-lang=""])::after{opacity:.05;animation:scanline 5s linear infinite;background-image:linear-gradient(to bottom, transparent, var(--accent-color) 16rem);background-position-y:-16rem;background-size:100% 16rem;pointer-events:none}@keyframes scanline{to{background-position-y:calc(100% + 16rem)}}.crt>code,pre:has(code[data-crt],code[data-lang=""])>code{display:block;border-radius:inherit;padding:.75rem 1rem;overflow-x:auto;scrollbar-width:thin}a.external:not(:has(img,svg,video,picture,figure))::after{-webkit-mask-image:var(--icon);-webkit-mask-size:cover;display:block;mask-image:var(--icon);mask-size:cover;background-color:currentColor;width:1em;height:1em;content:"";--icon: var(--icon-arrow-square-out);display:inline-block;vertical-align:-0.125rem;opacity:var(--dim-opacity);margin-inline-start:.125rem;font-size:max(1rem,1em)}iframe{display:block;margin:1rem auto;box-shadow:var(--edge-highlight),var(--shadow);border:none;border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);width:100%;max-width:100%}iframe:fullscreen{box-shadow:none;border-radius:0}iframe:-webkit-full-screen{box-shadow:none;border-radius:0}input{appearance:none;background-color:var(--bg-muted-1)}input:not([type]),input[type=text],input[type=search],input[type=password],input[type=submit]{box-shadow:var(--edge-highlight);border:none;border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);font-size:inherit;line-height:inherit;font-family:inherit}input:not([type]),input[type=text],input[type=search],input[type=password]{padding:.5rem .75rem;width:100%}input:not([type])::placeholder,input[type=text]::placeholder,input[type=search]::placeholder,input[type=password]::placeholder{opacity:1;color:var(--fg-muted-1)}input[type=submit]{transition:background-color var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight);border-radius:var(--rounded-corner);padding:.375rem .75rem;color:var(--fg-muted-1);font-weight:var(--font-weight-semi-bold);font-size:var(--font-size-small)}input[type=submit]:enabled:hover{background-color:var(--bg-muted-2)}input[type=submit]:enabled:active{transform:var(--active)}input[type=radio],input[type=checkbox],input[type=color]{position:relative;transition:background-color var(--transition);border:.125rem solid var(--bg-muted-2);width:1rem;height:1rem}input[type=radio]:not(:checked):enabled:hover,input[type=checkbox]:not(:checked):enabled:hover,input[type=color]:not(:checked):enabled:hover{background-color:var(--bg-muted-2)}input[type=radio],input[type=checkbox]{vertical-align:-0.125em;margin-inline-end:.25rem}input[type=radio]::before,input[type=checkbox]::before{display:block;position:absolute;transform:scale(0.5);opacity:0;transition:opacity var(--transition),transform var(--transition-bezier);content:""}input[type=radio]:checked,input[type=checkbox]:checked{border:.15rem solid rgba(0,0,0,0);background-color:var(--accent-color)}input[type=radio]:checked::before,input[type=checkbox]:checked::before{transform:scale(1);opacity:1}input[type=radio]{border-radius:50%}input[type=radio]::before{inset-block-start:.125rem;inset-inline-start:.125rem;border-radius:50%;background-color:var(--contrast-color);width:.5rem;height:.5rem}input[type=checkbox]{border-radius:calc(var(--rounded-corner-small)/2)}input[type=checkbox]::before{-webkit-mask-image:var(--icon);-webkit-mask-size:cover;display:block;mask-image:var(--icon);mask-size:cover;background-color:currentColor;width:1em;height:1em;content:"";--icon: var(--icon-check);transform-origin:bottom left;inset-block-start:-0.125rem;inset-inline-start:-0.125rem;color:var(--contrast-color);font-size:1rem}input[type=checkbox].switch{vertical-align:-0.375rem;box-shadow:var(--edge-highlight);border:none;border-radius:9999px;width:2.5rem;height:1.5rem}input[type=checkbox].switch.big{vertical-align:-0.625rem;width:3rem;height:2rem}input[type=checkbox].switch.big::before{width:1.5rem;height:1.5rem}input[type=checkbox].switch::before{transform:none;transform-origin:center;opacity:1;mask-image:none;inset-block-start:.25rem;inset-inline-start:.25rem;box-shadow:var(--shadow);border-radius:50%;background-color:#fff;width:1rem;height:1rem;content:""}input[type=checkbox].switch:checked{background-color:var(--accent-color)}input[type=checkbox].switch:checked::before{transform:translateX(1rem);background-color:var(--contrast-color)}:root[dir*=rtl] input[type=checkbox].switch:checked::before{transform:translateX(-1rem)}input[type=checkbox].switch:disabled::before{box-shadow:none}input[type=color]{vertical-align:-0.375em;box-shadow:var(--edge-highlight);border:none;border-radius:var(--rounded-corner-small);padding:.25rem;width:3rem;height:2rem}input[type=color]::-moz-color-swatch{border:none;border-radius:calc(var(--rounded-corner-small) - .25rem)}input[type=color]::-webkit-color-swatch-wrapper{padding:0}input[type=color]::-webkit-color-swatch{border-radius:calc(var(--rounded-corner-small) - .25rem)}input[type=range]{appearance:none;box-shadow:var(--edge-highlight);border-radius:9999px;background:var(--bg-muted-1);padding:.5rem .25rem;width:100%;height:1rem}input[type=range]::-webkit-slider-runnable-track{cursor:pointer;border-radius:9999px;background:var(--accent-color);width:100%}input[type=range]::-moz-range-track{cursor:pointer;border-radius:9999px;background:var(--accent-color);width:100%;height:.5rem}input[type=range]::-webkit-slider-thumb{appearance:none;cursor:ew-resize;margin-block:-0.5rem;box-shadow:var(--shadow);border:none;border-radius:.25rem;background-color:#fff;width:.5rem;height:1.5rem}input[type=range]::-moz-range-thumb{appearance:none;cursor:ew-resize;margin-block:-0.5rem;box-shadow:var(--shadow);border:none;border-radius:.25rem;background-color:#fff;width:.5rem;height:1.5rem}input[type=range].hollow::-webkit-slider-thumb{margin-block:0;outline:.25rem solid #fff;background-color:rgba(0,0,0,0);height:.5rem}input[type=range].hollow::-moz-range-thumb{margin-block:0;outline:.25rem solid #fff;background-color:rgba(0,0,0,0);height:.5rem}img,video{display:block;margin:1rem auto;box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);max-width:100%;max-height:calc(100vh - 2rem)}img.full,img[src*="#full"],video.full,video[src*="#full"]{width:100%}img.start,img.end,img[src*="#start"],img[src*="#end"],video.start,video.end,video[src*="#start"],video[src*="#end"]{width:calc(var(--content-width)/3)}img.start,img[src*="#start"],video.start,video[src*="#start"]{float:inline-start;transform-origin:left;margin-inline-end:1rem}:root[dir*=rtl] img.start,:root[dir*=rtl] img[src*="#start"],:root[dir*=rtl] video.start,:root[dir*=rtl] video[src*="#start"]{transform-origin:right}img.end,img[src*="#end"],video.end,video[src*="#end"]{float:inline-end;transform-origin:right;margin-inline-start:1rem}:root[dir*=rtl] img.end,:root[dir*=rtl] img[src*="#end"],:root[dir*=rtl] video.end,:root[dir*=rtl] video[src*="#end"]{transform-origin:left}img.pixels,img[src*="#pixels"],video.pixels,video[src*="#pixels"]{image-rendering:pixelated}img.has-alpha,img[src*="#has-alpha"],video.has-alpha,video[src*="#has-alpha"]{box-shadow:none;border-radius:0;background-color:rgba(0,0,0,0)}img.drop-shadow,img[src*="#drop-shadow"],video.drop-shadow,video[src*="#drop-shadow"]{filter:var(--drop-shadow)}img.spoiler,img[src*="#spoiler"],video.spoiler,video[src*="#spoiler"]{opacity:var(--dim-opacity);clip-path:inset(0 0 0 0 round var(--rounded-corner));filter:blur(1.5rem)}img.spoiler:hover,img.spoiler:active,img[src*="#spoiler"]:hover,img[src*="#spoiler"]:active,video.spoiler:hover,video.spoiler:active,video[src*="#spoiler"]:hover,video[src*="#spoiler"]:active{opacity:1;clip-path:inset(-0.75rem -0.75rem -0.75rem -0.75rem round var(--rounded-corner-small));filter:none}img.spoiler.solid,img.spoiler[src*="#solid"],img[src*="#spoiler"].solid,img[src*="#spoiler"][src*="#solid"],video.spoiler.solid,video.spoiler[src*="#solid"],video[src*="#spoiler"].solid,video[src*="#spoiler"][src*="#solid"]{clip-path:none;filter:brightness(0) contrast(0.5);box-shadow:none}img.spoiler.solid:hover,img.spoiler.solid:active,img.spoiler[src*="#solid"]:hover,img.spoiler[src*="#solid"]:active,img[src*="#spoiler"].solid:hover,img[src*="#spoiler"].solid:active,img[src*="#spoiler"][src*="#solid"]:hover,img[src*="#spoiler"][src*="#solid"]:active,video.spoiler.solid:hover,video.spoiler.solid:active,video.spoiler[src*="#solid"]:hover,video.spoiler[src*="#solid"]:active,video[src*="#spoiler"].solid:hover,video[src*="#spoiler"].solid:active,video[src*="#spoiler"][src*="#solid"]:hover,video[src*="#spoiler"][src*="#solid"]:active{filter:none}@media only screen and (max-width: 720px){img.start,img.end,img[src*="#start"],img[src*="#end"],video.start,video.end,video[src*="#start"],video[src*="#end"]{float:none;margin-inline-start:auto;margin-inline-end:auto;width:auto}}img{transition:box-shadow var(--transition),border-radius var(--transition),transform var(--transition-bezier)}img:not(.no-hover,[src*="#no-hover"],.emoji){cursor:zoom-in}img:not(.no-hover,[src*="#no-hover"],.emoji):hover{position:relative;transform:var(--hover);z-index:1;box-shadow:var(--edge-highlight),var(--shadow-raised);border-radius:var(--rounded-corner-small)}img:not(.no-hover,[src*="#no-hover"],.emoji).start,img:not(.no-hover,[src*="#no-hover"],.emoji).end,img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#start"],img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#end"]{transition:transform var(--transition-bezier-longer)}img:not(.no-hover,[src*="#no-hover"],.emoji).start:hover,img:not(.no-hover,[src*="#no-hover"],.emoji).end:hover,img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#start"]:hover,img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#end"]:hover{transform:scale(2)}@media only screen and (max-width: 720px){img:not(.no-hover,[src*="#no-hover"],.emoji).start,img:not(.no-hover,[src*="#no-hover"],.emoji).end,img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#start"],img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#end"]{transform-origin:center;transition:transform var(--transition-bezier)}img:not(.no-hover,[src*="#no-hover"],.emoji).start:hover,img:not(.no-hover,[src*="#no-hover"],.emoji).end:hover,img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#start"]:hover,img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#end"]:hover{transform:var(--hover)}}img:not(.no-hover,[src*="#no-hover"],.emoji).has-alpha:hover,img:not(.no-hover,[src*="#no-hover"],.emoji)[src*="#has-alpha"]:hover{box-shadow:none}a img:not(.no-hover,[src*="#no-hover"]){cursor:pointer}video:fullscreen{box-shadow:none;border-radius:0}video:-webkit-full-screen{box-shadow:none;border-radius:0}picture{display:inline-block}.overflow-auto{overflow:auto}.overflow-auto.overshoot{margin-block:-1rem;padding-block:1rem}.overflow-auto.overshoot-row{margin-inline:-1rem;padding-inline:1rem}.overshoot{--mask: linear-gradient( to bottom, transparent, black 1rem, black calc(100% - 1rem), transparent );-webkit-mask-image:var(--mask);mask-image:var(--mask)}.overshoot-row{--mask: linear-gradient( to right, transparent, black 1rem, black calc(100% - 1rem), transparent );-webkit-mask-image:var(--mask);mask-image:var(--mask)}#skip-to-content{position:absolute;transform:translateX(-50%) translateY(calc(-100% - 1rem));z-index:9999;transition:backdrop-filter var(--transition),box-shadow var(--transition),transform var(--transition-bezier);inset-inline-start:50vw;border-radius:9999px;background-color:var(--glass-bg-1);padding:.375rem .75rem;width:max-content;text-decoration:none}#skip-to-content:focus-visible,#skip-to-content:focus{-webkit-backdrop-filter:var(--blur);transform:translateX(-50%) translateY(1rem);backdrop-filter:var(--blur);box-shadow:var(--edge-highlight),var(--shadow)}span.spoiler{filter:blur(0.25rem);transition:filter var(--transition)}span.spoiler:focus,span.spoiler:hover{filter:none}span.spoiler.solid{filter:none;transition:background-color var(--transition),color var(--transition);border-radius:var(--rounded-corner-small);background-color:var(--fg-muted-1);color:rgba(0,0,0,0)}span.spoiler.solid:focus,span.spoiler.solid:hover{background-color:rgba(0,0,0,0);color:inherit}table{width:100%;overflow:hidden}thead{border-block-start:none;color:var(--fg-muted-2);font-weight:var(--font-weight-semi-bold)}tr:nth-child(even){background-color:var(--bg-muted-1)}tr th,tr td{padding:.5rem 1rem}tbody tr{border-block:0.0625rem solid var(--bg-muted-2)}tbody tr:last-child{border-block-end:none}.overflow-auto table{width:max-content;min-width:100%}textarea{appearance:none;box-shadow:var(--edge-highlight);border:none;border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);padding:1rem;resize:none;font-size:inherit;line-height:inherit;font-family:inherit}textarea::placeholder{opacity:1;color:var(--fg-muted-1)}strong.title{color:var(--accent-color);font-size:var(--font-size-large);line-height:1}@media(pointer: coarse),(hover: none){.tooltip,abbr[title]{display:inline-flex;position:relative;justify-content:center}.tooltip::after,abbr[title]::after{position:absolute;top:calc(100% + .25rem);z-index:9999;backdrop-filter:var(--blur);transition:visibility var(--transition),opacity var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner-small);background-color:var(--glass-bg-1);padding:.125rem .375rem;width:max-content;content:attr(title);-webkit-backdrop-filter:var(--blur);transform:translateY(-0.5rem) scale(0.5);transform-origin:top;visibility:hidden;opacity:0;max-width:calc(var(--content-width)/3);color:var(--fg-muted-2);font-size:var(--font-size-small)}.tooltip:focus::after,.tooltip:hover::after,abbr[title]:focus::after,abbr[title]:hover::after{transform:none;visibility:visible;opacity:1}}.visually-hidden{position:absolute !important;clip-path:inset(50%) !important;margin:-1px !important;border-width:0 !important;padding:0 !important;width:1px !important;height:1px !important;overflow:hidden !important;white-space:nowrap !important}#site-footer{--padding: 1rem;display:flex;grid-area:footer;gap:1rem;margin-block:3.25rem 1rem;border-block-start:0.0625rem solid var(--bg-muted-2);padding:1rem;width:100%;min-width:0;max-width:calc(var(--content-width) + var(--padding)*2)}@media only screen and (max-width: 480px){#site-footer{flex-direction:column-reverse}}#site-footer>div{display:flex;flex:1;flex-direction:column;gap:1rem}#site-footer>div p{margin:0}#site-footer>div:nth-of-type(1){color:var(--fg-muted-2);font-size:var(--font-size-small)}#site-footer>div:nth-of-type(2){justify-content:space-between;align-items:flex-end;text-align:end}@media only screen and (max-width: 480px){#site-footer>div:nth-of-type(2){align-items:normal;text-align:start}}#site-footer a.external::after{margin-inline-start:.25rem}#site-footer .link{transition:all var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight);border-radius:var(--rounded-corner-small);background-color:var(--bg-muted-1);padding-inline:.375rem;text-decoration:none;display:inline-block;line-height:1.5rem}#site-footer .link:hover{background-color:var(--bg-muted-2)}#site-footer .link:active{transform:var(--active)}#site-footer .webrings{display:flex;flex-direction:column;gap:.25rem;margin-block:0;padding-inline-start:0;list-style:none}#site-footer .webring{display:flex;gap:.25rem;margin-block:0;padding-inline-start:0}#site-footer .webring .random,#site-footer .webring .previous,#site-footer .webring .next{display:inline-flex;justify-content:center;align-items:center;border-radius:9999px;padding:.25rem;font-size:1rem}#site-footer .timestamp{color:var(--fg-muted-1);font-size:var(--font-size-xx-small);font-family:var(--font-monospace);text-transform:uppercase}#site-footer .socials{display:flex;justify-content:flex-end;gap:.75rem;margin:0;padding-inline-start:0;list-style:none}@media only screen and (max-width: 480px){#site-footer .socials{justify-content:normal}}#site-footer .socials li{margin-block:0;padding-inline-start:0}#site-footer .socials a{display:flex;transition:color var(--transition),transform var(--transition-bezier);height:fit-content;color:var(--fg-muted-1)}#site-footer .socials a:hover{color:var(--fg-muted-2)}#site-footer .socials a:active{transform:var(--active)}#site-footer .socials svg{fill:currentColor;width:1.5rem;height:1.5rem}.full-bleed{transform:translateX(-50%);margin-inline-start:50%;width:calc(100vw - var(--sidebar-width))}:root[dir*=rtl] .full-bleed{transform:translateX(50%)}@media only screen and (max-width: 960px){.full-bleed{width:calc(100vw - 3.5rem)}}body{display:grid;grid-template-rows:auto auto 1fr auto;grid-template-columns:var(--sidebar-width) 1fr;grid-template-areas:"sidebar header" "sidebar banner" "sidebar main " "sidebar footer";justify-items:center;transition:grid-template-columns var(--transition)}@media only screen and (max-width: 960px){body{grid-template-columns:0 1fr}}#site-header{display:flex;position:sticky;grid-area:header;justify-content:space-between;align-items:center;gap:.25rem;z-index:2;inset-block-start:0;padding:.5rem;width:100%;min-width:0}#site-header::before,#site-header::after{position:absolute;inset:0;--mask-image: linear-gradient( to bottom, hsl(0, 0%, 0%) 0%, hsla(0, 0%, 0%, 0.987) 8.1%, hsla(0, 0%, 0%, 0.951) 15.5%, hsla(0, 0%, 0%, 0.896) 22.5%, hsla(0, 0%, 0%, 0.825) 29%, hsla(0, 0%, 0%, 0.741) 35.3%, hsla(0, 0%, 0%, 0.648) 41.2%, hsla(0, 0%, 0%, 0.55) 47.1%, hsla(0, 0%, 0%, 0.45) 52.9%, hsla(0, 0%, 0%, 0.352) 58.8%, hsla(0, 0%, 0%, 0.259) 64.7%, hsla(0, 0%, 0%, 0.175) 71%, hsla(0, 0%, 0%, 0.104) 77.5%, hsla(0, 0%, 0%, 0.049) 84.5%, hsla(0, 0%, 0%, 0.013) 91.9%, hsla(0, 0%, 0%, 0) 100% );mask-image:var(--mask-image);-webkit-mask-image:var(--mask-image);inset-block-end:-100%;pointer-events:none;content:""}#site-header::before{z-index:-2;backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur)}#site-header::after{z-index:-1;background-color:var(--bg-color)}#site-header .title{margin-inline:.75rem;min-width:0;overflow:hidden;color:var(--fg-muted-2);font-size:var(--font-size-small);line-height:2rem;text-overflow:ellipsis;white-space:nowrap}#site-header .buttons{display:flex;flex-wrap:nowrap;gap:.25rem;margin:0;box-shadow:var(--edge-highlight),var(--shadow-glass);border-radius:1.125rem;background-color:var(--glass-bg-1);padding:.25rem;list-style:none}#site-header .buttons li{flex:0;margin-block:0;padding-inline-start:0}#site-header .buttons>li{height:fit-content;line-height:0}#site-header .buttons button,#site-header .buttons details{box-shadow:none;background-color:rgba(0,0,0,0);line-height:inherit}#site-header .buttons button .icon,#site-header .buttons details .icon{margin-inline-end:0;box-shadow:none;background-color:rgba(0,0,0,0);padding:0}#site-header .buttons button,#site-header .buttons summary{border-radius:50%;padding:.375rem}#site-header .buttons button:hover,#site-header .buttons summary:hover{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1);color:var(--fg-muted-2)}#site-header .buttons button:active,#site-header .buttons summary:active{transform:var(--active)}#site-header .buttons details{box-shadow:none;border-radius:0;padding:0}#site-header .buttons button{font-size:var(--font-size-medium)}#site-header .buttons summary:hover{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1)}#site-header .buttons summary::before{display:none}#site-header .buttons details{display:block;position:relative;margin-block-end:0}#site-header .buttons details::details-content{block-size:auto}#site-header .buttons details[open]::details-content{block-size:auto}#site-header .buttons details[open] summary{color:var(--fg-muted-1)}#site-header .buttons details[open] summary:hover{color:var(--fg-muted-2)}@supports not (block-size: calc-size(auto, size)){#site-header .buttons details[open] summary~*{animation:none}}.buttons .dropdown{display:flex;position:absolute;flex-direction:column;gap:.25rem;transform-origin:top right;z-index:1;backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);animation:expand-up-right var(--transition-bezier);inset-block-start:calc(100% + .75rem);inset-inline-end:-0.25rem;box-shadow:var(--edge-highlight),var(--shadow-glass);border-radius:calc(var(--rounded-corner) + .25rem);background-color:var(--glass-bg-1);padding:.25rem;width:max-content;min-width:8rem;list-style:none}:root[dir*=rtl] .buttons .dropdown{transform-origin:top left;animation:expand-up-left var(--transition-bezier)}.buttons .dropdown li{flex:1}.buttons .dropdown a{box-shadow:none;border-radius:var(--rounded-corner);background-color:rgba(0,0,0,0);padding:.375rem .75rem;width:100%}.buttons .dropdown a:hover{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1) !important}#site-sidebar-controls{display:flex;flex-shrink:0;gap:.25rem;transition:width var(--transition),visibility var(--transition);margin-block:0;box-shadow:var(--edge-highlight),var(--shadow-glass);border-radius:1.125rem;background-color:var(--glass-bg-1);padding:.25rem;height:fit-content;line-height:0;list-style:none}@media only screen and (max-width: 960px){#site-sidebar-controls{width:2.25rem}#site-sidebar-controls:has(#expand-sidebar:checked){width:6.25rem}#site-sidebar-controls:has(#expand-sidebar:checked) #site-sidebar-modes{transform:none;visibility:visible;opacity:1}}#site-sidebar-controls ul{padding-inline-start:0;list-style:none}#site-sidebar-controls li{display:inline-block;margin-block:0;padding-inline-start:0}#site-sidebar-controls li label{display:inline-block;transition:box-shadow var(--transition),background-color var(--transition),color var(--transition),transform var(--transition-bezier);cursor:pointer;border-radius:50%;padding:.375rem;height:fit-content;color:var(--fg-muted-1);line-height:0}#site-sidebar-controls li label:hover{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1);color:var(--fg-muted-2)}#site-sidebar-controls li label:active{transform:var(--active)}#site-sidebar-controls li input[type=radio]+label{position:relative;z-index:1}#site-sidebar-controls li input[type=radio]+label::before{box-shadow:var(--edge-highlight),var(--shadow-glow);background-image:linear-gradient(to bottom right, var(--accent-color), var(--accent-highlight));color:var(--contrast-color);text-shadow:var(--text-shadow-glow);position:absolute;opacity:0;z-index:-1;transition:opacity var(--transition),box-shadow var(--transition),background-color var(--transition);inset:0;border-radius:inherit;content:""}#site-sidebar-controls li input[type=radio]:checked+label{color:var(--contrast-color)}#site-sidebar-controls li input[type=radio]:checked+label:hover{box-shadow:none;background-color:rgba(0,0,0,0)}#site-sidebar-controls li input[type=radio]:checked+label:hover::before{box-shadow:var(--edge-highlight);background-color:var(--accent-color)}#site-sidebar-controls li input[type=radio]:checked+label::before{opacity:1}#site-sidebar-controls li input[type=radio]:focus-visible+label,#site-sidebar-controls li input[type=checkbox]:focus-visible+label{animation:focus-in var(--transition);outline:.125rem solid var(--accent-color);outline-offset:.125rem}@supports not selector(:focus-visible){#site-sidebar-controls li input[type=radio]:focus+label,#site-sidebar-controls li input[type=checkbox]:focus+label{animation:focus-in var(--transition);outline:.125rem solid var(--accent-color);outline-offset:.125rem}}#site-sidebar-controls #expand-sidebar-li{display:none}@media only screen and (max-width: 960px){#site-sidebar-controls #expand-sidebar-li{display:inline-block}}#site-sidebar-modes{display:flex;gap:.25rem;transform-origin:left;transition:visibility var(--transition),opacity var(--transition),transform var(--transition)}@media only screen and (max-width: 960px){#site-sidebar-modes{transform:translateX(-2.25rem);visibility:hidden;opacity:0}}#main-content{--padding: 1rem;grid-area:main;transform-origin:50% 50vh;animation:fade-in-zoom var(--transition-bezier-longer);margin-block-start:3.25rem;padding-inline:1rem;width:100%;min-width:0;max-width:calc(var(--content-width) + var(--padding)*2)}@supports(transform-origin: 50% 50dvh){#main-content{transform-origin:50% 50dvh}}#main-content>:first-child{margin-block-start:0}#main-content>:last-child{margin-block-end:0}@keyframes fade-in-zoom{from{transform:scale(0.95);opacity:0}}:target~#main-content{animation-name:none}#↑:target~#main-content,#up:target~#main-content{animation-name:slide-in-up-longer}#↓:target~#main-content,#down:target~#main-content{animation-name:slide-in-down-longer}#←:target~#main-content,#left:target~#main-content{animation-name:slide-in-left-longer}#→:target~#main-content,#right:target~#main-content{animation-name:slide-in-right-longer}#site-sidebar{display:flex;position:sticky;grid-area:sidebar;flex-direction:column;z-index:3;transition-duration:var(--transition);transition-property:visibility,transform,inset-block-start,box-shadow,border-inline-end,border-start-end-radius,border-end-end-radius,height;inset-block-start:0;inset-inline-start:0;border-inline-end:0.0625rem solid var(--bg-muted-2);width:var(--sidebar-width);height:100vh}@supports(height: 100dvh){#site-sidebar{height:100dvh}}@media only screen and (max-width: 960px){#site-sidebar{transform:translateX(-100%);visibility:hidden;inset-block-start:3.25rem;box-shadow:var(--edge-highlight),var(--shadow);border-inline-end:0 solid rgba(0,0,0,0);border-start-end-radius:var(--rounded-corner);border-end-end-radius:var(--rounded-corner);height:calc(100vh - 3.75rem)}:root[dir*=rtl] #site-sidebar{transform:translateX(100%)}@supports(height: 100dvh){#site-sidebar{height:calc(100dvh - 3.75rem)}}#site-header:has(#expand-sidebar:checked)~#site-sidebar{transform:none;visibility:visible;backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);background-color:var(--glass-bg-1)}}#site-sidebar-content{display:flex;position:relative;flex:1;overflow:hidden}#site-sidebar-header{display:flex;align-items:center;transform-origin:1.375rem 50%;transition:box-shadow var(--transition),background-color var(--transition),color var(--transition),transform var(--transition-bezier);margin:1rem;margin-block-end:0;border-radius:1.375rem;padding:.25rem;padding-inline-end:.75rem;min-width:0;color:var(--fg-muted-1);text-decoration:none}#site-sidebar-header:hover{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1);color:var(--fg-muted-2)}#site-sidebar-header:hover .icon{background-color:var(--bg-muted-2)}#site-sidebar-header:active{transform:var(--active)}#site-sidebar-header img{margin:0}#site-sidebar-header img,#site-sidebar-header .icon{margin-inline-end:.5rem;border-radius:50%}#site-sidebar-header img{width:2.25rem;height:2.25rem}#site-sidebar-header .icon{transition:background-color var(--transition);box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1);padding:.5rem;font-size:1.25rem}#site-sidebar-header div{display:flex;flex:1;flex-direction:column;gap:.125rem;min-width:0;line-height:1.25}#site-sidebar-header div span:nth-of-type(1){overflow:hidden;font-size:var(--font-size-small);text-overflow:ellipsis;white-space:nowrap}#site-sidebar-header div span:nth-of-type(2){font-weight:var(--font-weight-normal);font-size:var(--font-size-x-small)}#sidebar-nav,#sidebar-meta{display:flex;flex:1;flex-direction:column;transition:visibility var(--transition),opacity var(--transition),filter var(--transition),transform var(--transition-bezier);padding:1rem;width:var(--sidebar-width);min-width:100%;overflow:hidden auto;scrollbar-width:thin}#site-header:has(#site-sidebar-modes #mode-meta:checked)+#site-sidebar #sidebar-nav{transform:perspective(var(--content-width)) rotateY(15deg);opacity:var(--disabled-opacity);filter:blur(0.25rem)}#site-header:has(#site-sidebar-modes #mode-meta:checked)+#site-sidebar #sidebar-meta{transform:perspective(var(--content-width));visibility:visible;opacity:1;filter:none}#sidebar-nav{transform:perspective(var(--content-width));transform-origin:left}:root[dir*=rtl] #sidebar-nav{transform-origin:right}#sidebar-nav a{display:flex;flex:1;align-items:center;transform-origin:1.375rem 50%;transition:box-shadow var(--transition),background-color var(--transition),color var(--transition),transform var(--transition-bezier);border-radius:1.375rem;padding:.25rem;padding-inline-end:.75rem;overflow:hidden;color:var(--fg-muted-1);text-decoration:none;text-overflow:ellipsis;white-space:nowrap}#sidebar-nav a:hover{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1);color:var(--fg-muted-2)}#sidebar-nav a:hover .icon{background-color:var(--bg-muted-2)}#sidebar-nav a:active{transform:var(--active)}#sidebar-nav a[aria-current=page]{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-1);color:var(--accent-color)}#sidebar-nav a[aria-current=page] .icon{box-shadow:var(--edge-highlight),var(--shadow-glow);background-image:linear-gradient(to bottom right, var(--accent-color), var(--accent-highlight));color:var(--contrast-color);text-shadow:var(--text-shadow-glow);text-shadow:none}#sidebar-nav a[aria-current=page]:hover{background-color:var(--bg-muted-2)}#sidebar-nav a[aria-current=page]:hover .icon{box-shadow:var(--edge-highlight);background-color:var(--accent-color);color:var(--contrast-color)}#sidebar-nav a.new .icon{position:relative}#sidebar-nav a.new .icon::after{position:absolute;inset-block-start:0.0625rem;inset-inline-end:0.0625rem;border-radius:50%;background-color:var(--accent-color);width:.75rem;height:.75rem;content:""}#sidebar-nav .icon{transition:background-color var(--transition),color var(--transition),box-shadow var(--transition);margin-inline-end:.5rem;box-shadow:var(--edge-highlight);border-radius:50%;background-color:var(--bg-muted-1);padding:.5rem;font-size:1.25rem}#sidebar-nav .icon.fallback{width:1.25rem;font-weight:var(--font-weight-semi-bold);font-size:var(--font-size-medium);line-height:1.25rem;font-family:var(--font-monospace);text-align:center}#sidebar-nav .icon.fallback::before{content:none}#sidebar-nav details{flex:1;margin-block-end:0;box-shadow:none;border-radius:0;background-color:rgba(0,0,0,0);padding:0}#sidebar-nav details[open] summary{transform:none;opacity:1;box-shadow:none;background-color:rgba(0,0,0,0)}#sidebar-nav details[open] summary::before{transform:none}@supports not (block-size: calc-size(auto, size)){#sidebar-nav details[open] summary~*{animation:none}}#sidebar-nav details summary{display:flex;position:relative;align-items:center;border-radius:0;padding:.75rem 0;color:var(--fg-muted-1);font-weight:var(--font-weight-normal);font-size:var(--font-size-small);line-height:var(--font-size-small);white-space:nowrap}#sidebar-nav details summary:hover{box-shadow:none;background-color:rgba(0,0,0,0)}#sidebar-nav details summary:active{transform:none}#sidebar-nav details summary::before{position:absolute;transform:rotate(-90deg);transition:transform var(--transition-bezier);margin-inline-end:0;inset-block:.5rem;inset-inline-end:0;font-size:1rem}#sidebar-nav details summary .line{flex:1;margin-inline:.5rem 1.5rem;background-color:var(--bg-muted-2);height:0.0625rem}#sidebar-nav details ul{margin-block:0}#sidebar-nav ul{display:flex;flex-direction:column;gap:.25rem;margin-block:auto;padding-inline-start:0;list-style:none}#sidebar-nav li{display:flex;transition:width var(--transition);margin-block:0;padding-inline-start:0;width:100%}#sidebar-meta{position:absolute;transform:perspective(var(--content-width)) rotateY(-30deg);transform-origin:left;visibility:hidden;opacity:0;filter:blur(0.25rem);inset:0}:root[dir*=rtl] #sidebar-meta{transform-origin:right}#sidebar-meta:has(>div:empty) .status-page{display:flex}#sidebar-meta:has(>div:empty)>div{display:none}#sidebar-meta>div{display:flex;flex-direction:column;gap:1rem;margin-block:auto}#sidebar-meta .card{background-color:var(--glass-bg-1)}#sidebar-meta .while-writing{display:inherit;position:relative;flex-direction:inherit;gap:inherit;margin:calc(-0.25rem - 0.0625rem);margin-block-start:1rem;border:0.0625rem solid var(--bg-muted-2);border-block-start:none;border-end-start-radius:calc(var(--rounded-corner) + .25rem + 0.0625rem);border-end-end-radius:calc(var(--rounded-corner) + .25rem + 0.0625rem);padding:.25rem}#sidebar-meta .while-writing .card-title{display:flex;position:absolute;gap:.25rem;inset-block-start:calc(var(--rounded-corner-small)*-1);inset-inline:-0.0625rem;height:var(--rounded-corner-small)}#sidebar-meta .while-writing .card-title .line{border:0.0625rem solid var(--bg-muted-2);border-block-end:none}#sidebar-meta .while-writing .card-title .line:nth-of-type(1){border-inline-end:none;border-start-start-radius:var(--rounded-corner);width:var(--rounded-corner)}#sidebar-meta .while-writing .card-title .line:nth-of-type(2){flex:1;border-inline-start:none;border-start-end-radius:var(--rounded-corner)}#sidebar-meta .while-writing .card-title .title{color:var(--fg-muted-2);font-size:var(--font-size-small);line-height:0}#sidebar-meta .weather{display:flex;justify-content:space-between;align-items:center;gap:1rem;border-radius:1.375rem;padding:.25rem;padding-inline-end:1.25rem;line-height:1}#sidebar-meta .weather.cloudy{animation:perlin 12s linear infinite;background-image:url("/weather/perlin.png")}@media(prefers-color-scheme: dark){#sidebar-meta .weather.cloudy{background-image:url("/weather/perlin-dark.png")}}#sidebar-meta .weather.showers{animation:rain 3s linear infinite;background-image:url("/weather/rain.svg")}@media(prefers-color-scheme: dark){#sidebar-meta .weather.showers{background-image:url("/weather/rain-dark.svg")}}#sidebar-meta .weather.snowing{animation:snow 12s linear infinite;background-image:url("/weather/snow.svg")}@media(prefers-color-scheme: dark){#sidebar-meta .weather.snowing{background-image:url("/weather/snow-dark.svg")}}#sidebar-meta .weather div{display:flex;align-items:center;gap:.5rem}#sidebar-meta .weather .title{color:inherit;font-size:var(--font-size-medium)}#sidebar-meta .weather .icon{box-shadow:var(--edge-highlight);border-radius:50%;background-color:var(--bg-muted-1);padding:.5rem;font-size:1.25rem}#sidebar-meta .weather .condition{color:var(--fg-muted-2);font-size:var(--font-size-small)}@keyframes rain{to{background-position:256px 320px}}@keyframes perlin{to{background-position:-256px -64px}}@keyframes snow{to{background-position:256px 512px}}#sidebar-meta .music{display:flex;position:relative;flex-direction:column;gap:.5rem;border-radius:1rem}#sidebar-meta .music::after{position:absolute;z-index:1;inset:0;box-shadow:var(--edge-highlight);border-radius:inherit;max-height:5rem;pointer-events:none;content:""}#sidebar-meta .music .carousel{scroll-snap-type:x mandatory;-webkit-overflow-scrolling:touch;display:flex;margin-block:0;box-shadow:var(--shadow);border-radius:inherit;background-color:var(--glass-bg-1);padding-inline-start:0;overflow-x:auto;scroll-behavior:smooth;scrollbar-width:none;list-style:none}#sidebar-meta .music .carousel li{display:flex;position:relative;flex-shrink:0;gap:1rem;margin-block:0;outline:none;padding:.5rem;width:100%;overflow:hidden;scroll-snap-align:start}#sidebar-meta .music .carousel li.has-cover{animation:cover-alternate 60s ease-in-out infinite;background-image:var(--bg-overlay),var(--blurnail);background-position:center;background-size:2000%}@keyframes cover-alternate{from{background-position:50% 0%}12.5%{background-position:100% 25%}25%{background-position:50% 50%}37.5%{background-position:100% 75%}50%{background-position:50% 100%}62.5%{background-position:0% 75%}75%{background-position:50% 50%}87.5%{background-position:0% 25%}to{background-position:50% 0%}}#sidebar-meta .music .carousel li.has-cover .music-type{color:var(--fg-muted-neutral-1)}#sidebar-meta .music .carousel li.has-cover .music-artist{color:var(--fg-muted-neutral-2)}#sidebar-meta .music .carousel li>div{display:flex;flex:1;flex-direction:column;justify-content:space-between;gap:.25rem;margin-inline-end:.5rem;margin-block:.25rem;min-width:0;min-width:0;line-height:1}#sidebar-meta .music .carousel li a{position:absolute;z-index:1;inset:0;border-radius:inherit}#sidebar-meta .music .music-type{color:var(--fg-muted-1);font-size:var(--font-size-xx-small)}#sidebar-meta .music .music-artist{color:var(--fg-muted-2);font-size:var(--font-size-small)}#sidebar-meta .music .title{color:var(--fg-color);font-size:var(--font-size-medium)}#sidebar-meta .music img{margin:0;border-radius:var(--rounded-corner-small)}#sidebar-meta .music-paginator{display:flex;justify-content:center;gap:.25rem;margin-inline:1rem;margin-block:0;padding-inline-start:0;list-style:none}#sidebar-meta .music-paginator li{display:flex;margin-block:0;padding-inline-start:0;height:.75rem}#sidebar-meta .music-paginator a{transition:width var(--transition),height var(--transition),background-color var(--transition),transform var(--transition-bezier);margin-block:auto;border-radius:.375rem;background-color:var(--fg-muted-1);width:1.5rem;height:.375rem}#sidebar-meta .music-paginator a:hover{background-color:var(--fg-muted-2)}#sidebar-meta .music-paginator a:active{transform:var(--active)}#sidebar-meta .music-paginator a.active{background-color:var(--fg-color);width:.75rem;height:.75rem}#sidebar-meta .toc{display:flex;flex-direction:column;max-height:calc(var(--content-width)/3)}@media only screen and (max-width: 720px){#sidebar-meta .toc{max-height:calc(var(--content-width)/2)}}#sidebar-meta .toc .title{margin-block-end:1rem}#sidebar-meta .toc .overshoot{flex:1;margin:-1rem;padding:1rem;overflow:auto;scrollbar-width:thin}#sidebar-meta .toc .overshoot ul{margin-block:0;padding-inline-start:1rem;color:var(--fg-muted-1);font-size:var(--font-size-small)}#sidebar-meta .toc .overshoot li{margin-block:0;padding-inline-start:0}#sidebar-meta .toc .overshoot li::marker{color:inherit}#sidebar-meta .toc .overshoot a{color:inherit;text-decoration:none}#sidebar-meta .status-page{display:none;margin-block:auto;text-align:center}#sidebar-meta .status-page .title,#sidebar-meta .status-page p{color:var(--fg-muted-2)}#sidebar-meta .status-page .icon{margin-inline:auto;color:var(--fg-muted-1);font-size:3rem}#sidebar-meta .status-page p{margin-block:0}[class^=markdown-alert]{display:flow-root;position:relative}[class^=markdown-alert]::before{margin-inline-start:1.5rem;content:var(--alert-title);color:var(--accent-color);font-weight:var(--font-weight-semi-bold)}[class^=markdown-alert]::after{-webkit-mask-image:var(--icon);-webkit-mask-size:cover;display:block;mask-image:var(--icon);mask-size:cover;background-color:currentColor;width:1em;height:1em;content:"";--icon: var(--alert-icon);position:absolute;margin-inline-end:.25rem;inset-block-start:0.0625rem;inset-inline-start:.75rem;color:var(--accent-color);font-size:1.25rem}[class^=markdown-alert]>:first-child{margin-block-start:.25rem}[class^=markdown-alert]>:last-child{margin-block-end:0}.markdown-alert-note,.markdown-alert-tip,.markdown-alert-important,.markdown-alert-warning,.markdown-alert-caution,.markdown-alert-red,.markdown-alert-orange,.markdown-alert-yellow,.markdown-alert-green,.markdown-alert-blue,.markdown-alert-purple{--accent-color: hsl( var(--accent-light-h), var(--accent-light-s), var(--accent-light-l) );--accent-h: var(--accent-light-h);--accent-s: var(--accent-light-s);--accent-l: var(--accent-light-l);--bg-color: hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.8) );--fg-muted-1: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) - var(--accent-l) * 0.85), var(--fg-muted-1-opacity) );--fg-muted-2: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) - var(--accent-l) * 0.85), var(--fg-muted-2-opacity) );--glass-bg-1: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.7), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.9), var(--dim-opacity) );--glass-bg-2: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.8), var(--dim-opacity) );--crt-bg: radial-gradient( hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.9) ), hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.75) ) 80%, hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.65) ) );--accent-highlight: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--disabled-opacity) );--bg-muted-1: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--color-opacity-1) );--bg-muted-2: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--color-opacity-2) );--l: clamp(0, (l / 0.623 - 1) * -infinity, 1);--a: calc(var(--l) + (var(--dim-opacity) * (1 - var(--l))));--contrast-color: oklch(from var(--accent-color) var(--l) 0 h / var(--a));--shadow-glow: 0 0 0 0.0625rem var(--bg-muted-2), 0 0.125rem 0.375rem 0.125rem var(--bg-muted-1), 0 0.25rem 1.5rem 0.25rem var(--bg-muted-2);--shadow-glow-inset: inset 0 0 0 0.0625rem var(--bg-muted-2), inset 0 0.125rem 0.375rem 0.125rem var(--bg-muted-1), inset 0 0.25rem 1.5rem 0.25rem var(--bg-muted-2);--text-shadow-glow: var(--bg-muted-2) 0 0 0.25rem, var(--accent-color) 0 0 0.75rem}@media(prefers-color-scheme: dark){.markdown-alert-note,.markdown-alert-tip,.markdown-alert-important,.markdown-alert-warning,.markdown-alert-caution,.markdown-alert-red,.markdown-alert-orange,.markdown-alert-yellow,.markdown-alert-green,.markdown-alert-blue,.markdown-alert-purple{--accent-color: hsl( var(--accent-dark-h), var(--accent-dark-s), var(--accent-dark-l) );--accent-h: var(--accent-dark-h);--accent-s: var(--accent-dark-s);--accent-l: var(--accent-dark-l);--bg-color: hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.7), calc(var(--accent-l) - var(--accent-l) * 0.9) );--fg-muted-1: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.85), var(--fg-muted-1-opacity) );--fg-muted-2: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.85), var(--fg-muted-2-opacity) );--glass-bg-1: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) - var(--accent-l) * 0.8), var(--dim-opacity) );--glass-bg-2: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.7), var(--dim-opacity) );--crt-bg: radial-gradient( hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.7) ), hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.9) ) 80%, hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.95) ) )}}.markdown-alert-note::before{content:var(--alert-note-title)}.markdown-alert-note::after{--icon: var(--icon-info)}.markdown-alert-tip::before{content:var(--alert-tip-title)}.markdown-alert-tip::after{--icon: var(--icon-lightbulb)}.markdown-alert-important::before{content:var(--alert-important-title)}.markdown-alert-important::after{--icon: var(--icon-warning-diamond)}.markdown-alert-warning::before{content:var(--alert-warning-title)}.markdown-alert-warning::after{--icon: var(--icon-warning)}.markdown-alert-caution::before{content:var(--alert-caution-title)}.markdown-alert-caution::after{--icon: var(--icon-warning-octagon)}.markdown-alert-caution,.markdown-alert-red{--accent-h: var(--red-h);--accent-s: var(--red-s);--accent-l: var(--red-l);--accent-color: var(--red-fg)}.markdown-alert-orange{--accent-h: var(--orange-h);--accent-s: var(--orange-s);--accent-l: var(--orange-l);--accent-color: var(--orange-fg)}.markdown-alert-warning,.markdown-alert-yellow{--accent-h: var(--yellow-h);--accent-s: var(--yellow-s);--accent-l: var(--yellow-l);--accent-color: var(--yellow-fg)}.markdown-alert-tip,.markdown-alert-green{--accent-h: var(--green-h);--accent-s: var(--green-s);--accent-l: var(--green-l);--accent-color: var(--green-fg)}.markdown-alert-note,.markdown-alert-blue{--accent-h: var(--blue-h);--accent-s: var(--blue-s);--accent-l: var(--blue-l);--accent-color: var(--blue-fg)}.markdown-alert-important,.markdown-alert-purple{--accent-h: var(--purple-h);--accent-s: var(--purple-s);--accent-l: var(--purple-l);--accent-color: var(--purple-fg)}.article-details{display:flex;flex-direction:column;gap:.75rem;margin-block-end:2rem;color:var(--fg-muted-2);font-size:var(--font-size-small)}.article-details .items{display:flex;flex-wrap:wrap;align-items:flex-start;gap:.25rem .5rem}.article-details .item{display:inline-flex;align-items:center;gap:.375rem;border-radius:var(--rounded-corner-small)}.article-details a{color:inherit;font-weight:var(--font-weight-normal);font-size:var(--font-size-small);text-decoration-color:rgba(0,0,0,0)}.article-details a:hover{text-decoration-style:solid;text-decoration-color:var(--fg-muted-1)}.article-details .tooltip{cursor:help}.article-details .icon{box-shadow:var(--edge-highlight);border-radius:var(--rounded-corner-small);background-color:var(--bg-muted-1);padding:.375rem;color:var(--accent-color);font-size:1.25rem}.article-details .tags{display:flex;flex-wrap:wrap;gap:0 .5rem;margin:0;border-block-start:0.0625rem solid var(--bg-muted-2);padding-inline-start:0;padding-block-start:.5rem;width:100%;list-style:none}.article-details .tags li{display:flex;margin-block:0;padding:0}.article-details .tags a::before{content:"#"}.article-list{display:grid;grid-template-columns:repeat(auto-fill, minmax(calc(var(--content-width) / 2.5), 1fr));gap:1rem;margin-block-start:2rem}.article-list.categories article{position:relative}.article-list.categories article:hover .icon:last-child{transform:translateY(-50%) rotate(-10deg) scale(1.5)}.article-list.categories article .icon:last-child{position:absolute;transform:translateY(-50%);z-index:-1;transition:transform var(--transition-bezier);inset-block-start:50%;inset-inline-end:-3rem;color:var(--bg-muted-1);font-size:12rem}.article-list article{display:flex;position:relative;flex-direction:column;justify-content:space-between;box-shadow:var(--edge-highlight),var(--shadow);border-radius:1.25rem;background-color:var(--bg-muted-1);padding:0;overflow:hidden}.article-list article.draft{opacity:var(--disabled-opacity);filter:grayscale(1);transition-duration:var(--transition);transition-property:opacity,filter}.article-list article.draft:hover{opacity:1;filter:none}.article-list article.has-banner.has-accent-color{background-image:var(--bg-overlay),var(--blurnail);background-position:center;background-size:cover}.article-list article.has-accent-color{--accent-color: hsl( var(--accent-light-h), var(--accent-light-s), var(--accent-light-l) );--accent-h: var(--accent-light-h);--accent-s: var(--accent-light-s);--accent-l: var(--accent-light-l);--bg-color: hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.8) );--fg-muted-1: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) - var(--accent-l) * 0.85), var(--fg-muted-1-opacity) );--fg-muted-2: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) - var(--accent-l) * 0.85), var(--fg-muted-2-opacity) );--glass-bg-1: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.7), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.9), var(--dim-opacity) );--glass-bg-2: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.8), var(--dim-opacity) );--crt-bg: radial-gradient( hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.9) ), hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.75) ) 80%, hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.65) ) );--accent-highlight: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--disabled-opacity) );--bg-muted-1: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--color-opacity-1) );--bg-muted-2: hsla( var(--accent-h), var(--accent-s), var(--accent-l), var(--color-opacity-2) );--l: clamp(0, (l / 0.623 - 1) * -infinity, 1);--a: calc(var(--l) + (var(--dim-opacity) * (1 - var(--l))));--contrast-color: oklch(from var(--accent-color) var(--l) 0 h / var(--a));--shadow-glow: 0 0 0 0.0625rem var(--bg-muted-2), 0 0.125rem 0.375rem 0.125rem var(--bg-muted-1), 0 0.25rem 1.5rem 0.25rem var(--bg-muted-2);--shadow-glow-inset: inset 0 0 0 0.0625rem var(--bg-muted-2), inset 0 0.125rem 0.375rem 0.125rem var(--bg-muted-1), inset 0 0.25rem 1.5rem 0.25rem var(--bg-muted-2);--text-shadow-glow: var(--bg-muted-2) 0 0 0.25rem, var(--accent-color) 0 0 0.75rem}@media(prefers-color-scheme: dark){.article-list article.has-accent-color{--accent-color: hsl( var(--accent-dark-h), var(--accent-dark-s), var(--accent-dark-l) );--accent-h: var(--accent-dark-h);--accent-s: var(--accent-dark-s);--accent-l: var(--accent-dark-l);--bg-color: hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.7), calc(var(--accent-l) - var(--accent-l) * 0.9) );--fg-muted-1: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.85), var(--fg-muted-1-opacity) );--fg-muted-2: hsla( var(--accent-h), var(--accent-s), calc(var(--accent-l) + (100% - var(--accent-l)) * 0.85), var(--fg-muted-2-opacity) );--glass-bg-1: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.6), calc(var(--accent-l) - var(--accent-l) * 0.8), var(--dim-opacity) );--glass-bg-2: hsla( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.7), var(--dim-opacity) );--crt-bg: radial-gradient( hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.7) ), hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.9) ) 80%, hsl( var(--accent-h), calc(var(--accent-s) - var(--accent-s) * 0.5), calc(var(--accent-l) - var(--accent-l) * 0.95) ) )}}.article-list article>div{display:flex;flex:1;flex-direction:column;margin:1rem}.article-list article>div>a{text-decoration:none}.article-list article h3{margin:0;border-inline-start:.25rem solid;border-start-start-radius:.25rem;border-end-start-radius:.25rem;padding-inline-start:.5rem;color:var(--accent-color);font-weight:var(--font-weight-semi-bold);text-decoration:none}.article-list article h3:hover::after{opacity:1}.article-list article h3::after{opacity:0;transition:opacity var(--transition);content:" →"}.article-list article .description p{margin-block-end:.75rem}.article-list article .open-post{--mask-image: linear-gradient( to bottom, hsl(0, 0%, 0%) 0%, hsla(0, 0%, 0%, 0.987) 8.1%, hsla(0, 0%, 0%, 0.951) 15.5%, hsla(0, 0%, 0%, 0.896) 22.5%, hsla(0, 0%, 0%, 0.825) 29%, hsla(0, 0%, 0%, 0.741) 35.3%, hsla(0, 0%, 0%, 0.648) 41.2%, hsla(0, 0%, 0%, 0.55) 47.1%, hsla(0, 0%, 0%, 0.45) 52.9%, hsla(0, 0%, 0%, 0.352) 58.8%, hsla(0, 0%, 0%, 0.259) 64.7%, hsla(0, 0%, 0%, 0.175) 71%, hsla(0, 0%, 0%, 0.104) 77.5%, hsla(0, 0%, 0%, 0.049) 84.5%, hsla(0, 0%, 0%, 0.013) 91.9%, hsla(0, 0%, 0%, 0) 100% );mask-image:var(--mask-image);-webkit-mask-image:var(--mask-image);display:flex;position:relative;justify-content:center;align-items:center;margin-block-end:-2rem;box-shadow:var(--edge-highlight);border-radius:0;aspect-ratio:2/1;overflow:hidden;text-decoration:none}.article-list article .open-post:hover::before{background-color:var(--shade-color)}.article-list article .open-post:hover img{transform:var(--hover);filter:blur(0.25rem)}.article-list article .open-post:hover .icon{transform:none;opacity:1}.article-list article .open-post:active img{transform:none}.article-list article .open-post::before{position:absolute;z-index:1;transition:background-color var(--transition);inset:0;border-radius:inherit;content:""}.article-list article .open-post img{position:absolute;transition-duration:var(--transition);transition-property:transform,filter;margin:0;inset:0;width:100%;height:100%;object-fit:cover}.article-list article .open-post .icon{transform:scale(1.5);opacity:0;z-index:1;transition:transform var(--transition-bezier),opacity var(--transition);color:#fff;font-size:1.25rem}.article-list article .article-details{flex:1;justify-content:flex-end;margin-block-end:0}.article-list article .article-details .tags{margin-block-start:auto;margin-block-end:-0.25rem}.paginator{display:flex;justify-content:center;align-items:center;gap:.25rem;margin-block-start:4rem}.paginator:has(a.paginator-item.previous:active) .paginator-counter{border-start-start-radius:1rem;border-end-start-radius:1rem}.paginator:has(a.paginator-item.next:active) .paginator-counter{border-start-end-radius:1rem;border-end-end-radius:1rem}.paginator-item{display:inline-block;transition:border-radius var(--transition),border-start-end-radius var(--transition),border-end-end-radius var(--transition),border-start-start-radius var(--transition),border-end-start-radius var(--transition),background-color var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight);border-radius:1rem;background-color:var(--bg-muted-1);padding:.5rem;color:var(--accent-color);line-height:0;text-decoration:none}.paginator-item:not([aria-disabled=true]):hover{background-color:var(--bg-muted-2)}.paginator-item:not([aria-disabled=true]):active{transform:var(--active);border-radius:1rem}.paginator-item-previous{border-start-end-radius:var(--rounded-corner-small);border-end-end-radius:var(--rounded-corner-small)}.paginator-item-next{border-start-start-radius:var(--rounded-corner-small);border-end-start-radius:var(--rounded-corner-small)}.paginator-counter{box-shadow:var(--edge-highlight),var(--shadow-glow);background-image:linear-gradient(to bottom right, var(--accent-color), var(--accent-highlight));color:var(--contrast-color);text-shadow:var(--text-shadow-glow);display:inline-block;transition:var(--transition-bezier);transition-property:border-start-start-radius,border-end-start-radius,border-start-end-radius,border-end-end-radius;border-radius:var(--rounded-corner-small);background-color:var(--bg-muted-1);padding:.25rem .5rem;font-weight:var(--font-weight-semi-bold);font-variant-numeric:tabular-nums}.article-nav{display:flex;gap:.25rem}@media only screen and (max-width: 720px){.article-nav{flex-direction:column}}.article-nav-item{flex:1;transition:box-shadow var(--transition),background-color var(--transition),transform var(--transition-bezier);border-radius:var(--rounded-corner);padding:.75rem 1rem;min-width:0;text-decoration:none}.article-nav-item:hover{box-shadow:var(--edge-highlight),var(--shadow);background-color:var(--bg-muted-1)}.article-nav-item:hover .article-title{color:var(--accent-color)}.article-nav-item:active{transform:var(--active)}.article-nav-item.prev{text-align:start}.article-nav-item.prev .nav-arrow::before{content:"← "}.article-nav-item.next{text-align:end}.article-nav-item.next .nav-arrow::after{content:" →"}.article-nav .nav-arrow{margin-block-end:.5rem;color:var(--fg-muted-2);font-weight:var(--font-weight-normal);font-size:var(--font-size-small)}.article-nav .article-title{display:block;transition:color var(--transition);max-width:100%;overflow:hidden;color:var(--fg-muted-1);text-overflow:ellipsis;white-space:nowrap}.audio{transition:all var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight);border-radius:var(--rounded-corner-small);background-color:var(--bg-muted-1);padding-inline:.375rem;text-decoration:none;padding-block:0;color:var(--accent-color);font-size:var(--font-size-medium)}.audio:hover{color:var(--accent-color) !important}.audio:hover .icon{animation:speaker var(--transition-long) ease-out infinite}@keyframes speaker{from{--icon: var(--icon-speaker-none)}50%{--icon: var(--icon-speaker-low)}to{--icon: var(--icon-speaker-high)}}.audio:active{transform:var(--active)}.audio .icon{vertical-align:-0.1875rem;margin-inline-start:.25rem}body.has-banner{background-image:var(--bg-overlay),var(--blurnail);background-position:center;background-size:cover;background-attachment:fixed}#banner{--mask: linear-gradient(black calc(100% - 2rem), transparent);-webkit-mask-image:var(--mask);display:flex;grid-area:banner;justify-content:center;mask-image:var(--mask);margin-block-end:-2.5rem;padding-block:3.25rem 2.5rem;width:100%}#banner div{position:relative;margin-inline:1rem;aspect-ratio:2/1;width:100%;max-width:var(--content-width)}#banner img{position:absolute;margin:0;inset:0;border-radius:var(--rounded-corner) var(--rounded-corner) 0 0 !important;aspect-ratio:2/1;width:100%;object-fit:cover}#banner img:nth-of-type(1){opacity:var(--dim-opacity);z-index:-1;filter:blur(1.5rem);animation:banner-glow-load calc(.525s + var(--transition-long));inset-block-start:1rem}@keyframes banner-glow-load{from,52.5%{opacity:0}}#banner img:nth-of-type(2){transform-origin:bottom;animation:banner-load calc(.525s + var(--transition-longer))}@keyframes banner-load{from,52.5%{transform:translateY(2rem) scale(0.95);opacity:0}}#banner+#main-content{margin-block-start:0}#banner+#main-content article>h1:first-of-type,#banner+#main-content h1:first-of-type{margin-inline:-1rem;margin-block:0;border-radius:var(--rounded-corner) var(--rounded-corner) 0 0;padding:1rem;width:unset;-webkit-backdrop-filter:blur(1rem);backdrop-filter:blur(1rem);animation:banner-heading calc(.525s + var(--transition-longer));box-shadow:inset 0 0.0625rem 0 var(--accent-highlight);background-image:linear-gradient(to bottom, var(--bg-muted-1), transparent 50%);background-size:auto 200%;text-decoration:none}@keyframes banner-heading{from,52.5%{box-shadow:none;background-position-y:100%}}#comments #qrcode{float:inline-end;cursor:help;box-sizing:content-box;margin-inline-start:1rem;margin-block-start:3rem;box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner);background-color:#fff;padding:.75rem;width:7.25rem;height:7.25rem}#comments #qrcode svg{pointer-events:none}@media only screen and (max-width: 720px){#comments #qrcode{display:none}}#comments #comments-wrapper{display:flex;flex-direction:column;gap:2rem;margin-block-start:2rem}#comments #comments-wrapper #comments-status{color:var(--fg-muted-1);font-weight:var(--font-weight-semi-bold);font-size:var(--font-size-x-large);text-align:center}#comments #comments-wrapper .comment{display:grid;grid-template-columns:auto 1fr;grid-template-areas:"avatar header " "...... post " "...... media " "...... card " "...... interactions";column-gap:1rem;justify-items:start;animation:slide-in-right var(--transition-bezier)}:root[dir*=rtl] #comments #comments-wrapper .comment{animation:slide-in-left var(--transition-bezier)}@media only screen and (max-width: 720px){#comments #comments-wrapper .comment{grid-template-areas:"avatar header " "post post " "media media " "card card " "interactions interactions";align-items:start}}#comments #comments-wrapper .comment.comment-reply{position:relative;border-inline-start:.25rem solid var(--bg-muted-2);border-start-start-radius:.25rem;border-end-start-radius:.25rem;padding-inline-start:1rem}#comments #comments-wrapper .comment.comment-reply:has(+.comment-reply){border-end-start-radius:0}#comments #comments-wrapper .comment.comment-reply+.comment-reply{margin-block-start:-2rem;border-start-start-radius:0;padding-block-start:2rem}#comments #comments-wrapper .comment .avatar-link{grid-area:avatar;width:4rem;height:4rem}@media only screen and (max-width: 480px){#comments #comments-wrapper .comment .avatar-link{width:3rem;height:3rem}}#comments #comments-wrapper .comment .avatar-link .avatar{transition:transform var(--transition-bezier);margin:0;background-size:cover;width:100%;height:100%}#comments #comments-wrapper .comment .avatar-link .avatar:hover{transform:rotate(10deg) var(--hover);border-radius:var(--rounded-corner)}#comments #comments-wrapper .comment .avatar-link .avatar:active{transform:var(--active)}#comments #comments-wrapper .comment .mention{transition:all var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight);border-radius:var(--rounded-corner-small);background-color:var(--bg-muted-1);padding-inline:.375rem;text-decoration:none;display:inline-block}#comments #comments-wrapper .comment .mention:hover{background-color:var(--bg-muted-2)}#comments #comments-wrapper .comment .mention:active{transform:var(--active)}#comments #comments-wrapper .comment .invisible{display:none}#comments #comments-wrapper .comment .ellipsis::after{content:"…"}#comments #comments-wrapper .comment header{display:flex;grid-area:header;flex-direction:column;width:100%;min-width:0;color:var(--fg-muted-1);line-height:1}#comments #comments-wrapper .comment header .display,#comments #comments-wrapper .comment header .instance{display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}#comments #comments-wrapper .comment header .display{color:var(--fg-color);line-height:1.5rem;text-decoration:none}#comments #comments-wrapper .comment header .display .emoji{cursor:unset}#comments #comments-wrapper .comment header .display .emoji:hover{transform:none}#comments #comments-wrapper .comment header .instance{margin-block-start:.25rem;font-weight:var(--font-weight-semi-bold);font-size:var(--font-size-x-small)}#comments #comments-wrapper .comment header .instance.op{cursor:help}#comments #comments-wrapper .comment header .instance.op::before{-webkit-mask-image:var(--icon);-webkit-mask-size:cover;display:block;mask-image:var(--icon);mask-size:cover;background-color:currentColor;width:1em;height:1em;content:"";--icon: var(--icon-seal-check);display:inline-block;vertical-align:text-top;margin-inline-end:.25rem;font-size:1rem}#comments #comments-wrapper .comment header .timestamp{margin-block-start:.25rem}#comments #comments-wrapper .comment header .timestamp a{color:inherit;font-size:var(--font-size-x-small);text-decoration-color:rgba(0,0,0,0)}#comments #comments-wrapper .comment header .timestamp a:hover{text-decoration-style:solid;text-decoration-color:inherit}#comments #comments-wrapper .comment header .timestamp a::after{vertical-align:top}#comments #comments-wrapper .comment details{border-radius:var(--rounded-corner-small);background-image:linear-gradient(to right, transparent, transparent 0.5rem, var(--bg-muted-1) 0.5rem, var(--bg-muted-1) calc(100% - 0.5rem), transparent calc(100% - 0.5rem), transparent),linear-gradient(to right, transparent, transparent 0.5rem, var(--bg-color) 0.5rem, var(--bg-color) calc(100% - 0.5rem), transparent calc(100% - 0.5rem), transparent),repeating-linear-gradient(45deg, var(--contrast-color), var(--contrast-color) 0.25rem, var(--accent-color) 0.25rem, var(--accent-color) 0.5rem);width:100%}#comments #comments-wrapper .comment details summary{border-radius:0;background-image:none}#comments #comments-wrapper .comment main{grid-area:post;justify-self:stretch;margin-block-start:1rem;padding:0}#comments #comments-wrapper .comment main :first-child{margin-block-start:0}#comments #comments-wrapper .comment main :last-child{margin-block-end:0}#comments #comments-wrapper .comment .attachments{display:grid;grid-template-columns:repeat(auto-fit, minmax(16rem, 1fr));grid-area:media;gap:.5rem;margin-block-start:1rem}#comments #comments-wrapper .comment .attachments img,#comments #comments-wrapper .comment .attachments video{margin:0}#comments #comments-wrapper .comment .meta-card{grid-area:card;transition:transform var(--transition-bezier);margin-block-start:1rem;width:min(var(--content-width)/2,100%);font-weight:var(--font-weight-normal);text-decoration:none}#comments #comments-wrapper .comment .meta-card:hover img{transform:var(--hover);box-shadow:var(--edge-highlight),var(--shadow-raised);border-radius:var(--rounded-corner-small)}#comments #comments-wrapper .comment .meta-card:hover figcaption{border-radius:var(--rounded-corner);background-color:var(--bg-muted-2)}#comments #comments-wrapper .comment .meta-card:active{transform:var(--active)}#comments #comments-wrapper .comment .meta-card figure{display:flex;flex-direction:column;gap:.25rem;margin:0}#comments #comments-wrapper .comment .meta-card figure img{margin:0;border-radius:var(--rounded-corner) var(--rounded-corner) var(--rounded-corner-small) var(--rounded-corner-small);aspect-ratio:16/9;object-fit:cover}#comments #comments-wrapper .comment .meta-card figure img+figcaption{border-radius:var(--rounded-corner-small) var(--rounded-corner-small) var(--rounded-corner) var(--rounded-corner)}#comments #comments-wrapper .comment .meta-card figure figcaption{display:flex;flex-direction:column;gap:.25rem;transition-duration:var(--transition);transition-property:border-radius,background-color;box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);padding:1rem;color:var(--fg-color);font-size:var(--font-size-medium);text-align:start}#comments #comments-wrapper .comment .meta-card figure figcaption p{margin:0;color:var(--fg-muted-2);font-size:var(--font-size-small)}#comments #comments-wrapper .comment footer{display:flex;grid-area:interactions;align-items:center;gap:.25rem;margin-block-start:1rem;width:100%;min-width:0}@media only screen and (max-width: 480px){#comments #comments-wrapper .comment footer{flex-wrap:wrap}}#comments #comments-wrapper .comment footer .boosts,#comments #comments-wrapper .comment footer .faves,#comments #comments-wrapper .comment footer .reaction{padding:.375rem .75rem;font-variant-numeric:tabular-nums;white-space:nowrap}#comments #comments-wrapper .comment footer .boosts,#comments #comments-wrapper .comment footer .faves{transition:box-shadow var(--transition),border-radius var(--transition),transform var(--transition-bezier);border-radius:1.125rem;text-decoration:none}#comments #comments-wrapper .comment footer .boosts .icon,#comments #comments-wrapper .comment footer .faves .icon{vertical-align:-0.125rem;transition:transform var(--transition-bezier);margin-inline-end:.25rem}#comments #comments-wrapper .comment footer .boosts:hover,#comments #comments-wrapper .comment footer .faves:hover{box-shadow:var(--edge-highlight);text-decoration:none}#comments #comments-wrapper .comment footer .boosts:active,#comments #comments-wrapper .comment footer .faves:active{transform:var(--active);border-radius:1.125rem}#comments #comments-wrapper .comment footer .boosts{border-start-end-radius:var(--rounded-corner-small);border-end-end-radius:var(--rounded-corner-small);padding-inline-end:.625rem;color:var(--purple-fg)}#comments #comments-wrapper .comment footer .boosts .icon{--icon: var(--icon-repeat)}#comments #comments-wrapper .comment footer .boosts:hover{background-color:var(--purple-bg-1)}#comments #comments-wrapper .comment footer .boosts:hover .icon{transform:rotate(180deg)}#comments #comments-wrapper .comment footer .faves{border-start-start-radius:var(--rounded-corner-small);border-end-start-radius:var(--rounded-corner-small);padding-inline-start:.625rem;color:var(--yellow-fg)}#comments #comments-wrapper .comment footer .faves .icon{--icon: var(--icon-star)}#comments #comments-wrapper .comment footer .faves:hover{background-color:var(--yellow-bg-1)}#comments #comments-wrapper .comment footer .faves:hover .icon{transform:rotate(72deg)}#comments #comments-wrapper .comment footer .reactions{display:flex;gap:.25rem;margin:-0.5rem -1rem;padding:.5rem 1rem;min-width:0;overflow-x:auto;scrollbar-width:none;color:var(--accent-color);font-weight:var(--font-weight-semi-bold)}#comments #comments-wrapper .comment footer .reactions .reaction{display:flex;flex-shrink:0;align-items:center;gap:.25rem;cursor:zoom-in;border:0.0625rem solid var(--bg-muted-2);border-radius:1.1875rem}#comments #comments-wrapper .comment footer .reactions .reaction:hover .emoji{transform:scale(2)}#comments #comments-wrapper .comment footer .reactions .reaction .emoji{cursor:unset}#comments #comments-wrapper .comment footer .reactions .reaction span{font-variant-numeric:tabular-nums}.emoji{display:inline-block;vertical-align:bottom;transition:transform var(--transition-bezier);cursor:zoom-in;margin:0;box-shadow:none;border-radius:0;background-color:rgba(0,0,0,0);width:1.5rem;height:1.5rem}.emoji:hover{transform:scale(2)}.emoji.big{width:3rem;height:3rem}.footnotes-list p{margin-block:0}.footnote-reference{display:inline-flex;vertical-align:baseline;transition:background-color var(--transition),outline-offset calc(var(--transition-long)*3),transform var(--transition-bezier);margin-inline-start:.25rem;outline-offset:calc(var(--content-width)/2) !important;border-radius:.6875em;background-color:var(--fg-muted-1);line-height:1.375em}.footnote-reference:hover{background-color:var(--accent-color)}.footnote-reference:hover a::after{mask-position:center 1.125rem}.footnote-reference:active{transform:var(--active)}.footnote-reference:target{animation:none !important;outline-offset:-0.1875rem !important;background-color:var(--accent-color)}.footnote-reference a{flex:1;border-radius:inherit;padding-inline:0.5625rem 0.375rem;color:var(--bg-color);font-size:var(--font-size-small);font-variant-numeric:tabular-nums;text-align:center;text-decoration:none}.footnote-reference a::after{-webkit-mask-image:var(--icon);-webkit-mask-size:cover;display:block;mask-image:var(--icon);mask-size:cover;background-color:currentColor;width:1em;height:1em;content:"";--icon: var(--icon-caret-down);display:inline-block;vertical-align:-0.4375rem;mask-position:center;mask-size:1em;transition:mask-position var(--transition);margin-inline-start:.25rem;height:1.375rem}.giallo:has(.giallo-ln){padding-inline:0}.giallo-l{display:inline-block;width:100%}.giallo-ln{display:inline-block;margin-right:1ch;min-width:3ch;user-select:none;text-align:right}.icon{font-style:normal;font-variant:normal;font-weight:normal;line-height:1;user-select:none;text-transform:none;-webkit-user-select:none;display:inline-block;box-sizing:content-box}.icon::before{-webkit-mask-image:var(--icon);-webkit-mask-size:cover;display:block;mask-image:var(--icon);mask-size:cover;background-color:currentColor;width:1em;height:1em;content:""}iframe.vimeo-embed,iframe.youtube-embed{aspect-ratio:16/9}.marquee{margin-inline:-1rem;padding-inline:1rem;overflow:hidden}.marquee.overshoot-row>*{animation:marquee-scroll calc((var(--text-width) + var(--container-width))/40*1s) linear infinite}@keyframes marquee-scroll{0%,10%{transform:translateX(0)}45%,55%{transform:translateX(calc((var(--container-width) - var(--text-width)) * 1px))}90%,100%{transform:translateX(0)}}.marquee>*{display:inline-block;white-space:nowrap}picture#not-found{display:block}picture#not-found img{width:8rem}img#not-found{width:8rem}.pre-container{position:relative;margin:1rem 0 1rem;box-shadow:var(--shadow);border-radius:var(--rounded-corner)}.pre-container::after{position:absolute;inset:0;box-shadow:var(--edge-highlight);border-radius:inherit;pointer-events:none;content:""}.pre-container .header{display:flex;justify-content:space-between;align-items:center;transition:border-block-end-color var(--transition);border-radius:var(--rounded-corner) var(--rounded-corner) 0 0;border-block-end:0.0625rem solid rgba(0,0,0,0);background-image:linear-gradient(to right, transparent 50%, var(--bg-muted-neutral-1) 75%, transparent 100%);background-size:200%;padding:.25rem}.pre-container .header:not(.z-l-code),.pre-container .header:not(.z-d-code){background-color:var(--bg-muted-1);color:var(--accent-color)}.pre-container .header:not(.z-l-code) button:hover,.pre-container .header:not(.z-d-code) button:hover{background-color:var(--bg-muted-1)}.pre-container .header:not(.z-l-code).active,.pre-container .header:not(.z-d-code).active{border-block-end-color:var(--bg-muted-1)}.pre-container .header:not(.z-l-code).active button,.pre-container .header:not(.z-d-code).active button{background-color:var(--bg-muted-1)}.pre-container .header span{margin-inline-start:.75rem;font-weight:var(--font-weight-semi-bold)}.pre-container .header button{position:relative;transition:box-shadow var(--transition),background-color var(--transition),transform var(--transition-bezier);cursor:pointer;box-shadow:none;border-radius:var(--rounded-corner-small);background-color:rgba(0,0,0,0);padding:.5rem;color:inherit;line-height:0}.pre-container .header button:hover{box-shadow:var(--edge-highlight);background-color:var(--bg-muted-neutral-1)}.pre-container .header button:active{transform:var(--active)}.pre-container .header button:disabled{cursor:not-allowed}.pre-container .header button:disabled:active{transform:none}.pre-container .header button .icon{transition:opacity var(--transition-longer),transform var(--transition-bezier-longer);font-size:1rem}.pre-container .header button .icon.copy{--icon: var(--icon-copy);transform-origin:bottom left}.pre-container .header button .icon.check{--icon: var(--icon-check-square-offset);position:absolute;transform:scale(0.5);transform-origin:top right;opacity:0;inset:.5rem}.pre-container .header.active{animation:active-shimmer var(--transition-long);border-block-end-color:var(--bg-muted-neutral-1)}.pre-container .header.active button{opacity:1}.pre-container .header.active button .icon.copy{transform:scale(0.5);opacity:0}.pre-container .header.active button .icon.check{transform:none;opacity:1}@keyframes active-shimmer{to{background-position-x:-200%}}.pre-container pre{margin:0;box-shadow:none;border-radius:0 0 var(--rounded-corner) var(--rounded-corner)}#search-modal{display:flex;position:fixed;flex-direction:column;align-items:center;visibility:hidden;opacity:0;z-index:9999;transition:visibility var(--transition),opacity var(--transition);inset:0;background-color:var(--shade-color);padding-block-start:15vh;width:100%;height:100vh}#search-modal.active{visibility:visible;opacity:1}#search-modal.active #search-modal-content{transform:none}#search-modal-content{display:flex;flex-direction:column;gap:1rem;transform:var(--hover);backdrop-filter:var(--blur);-webkit-backdrop-filter:var(--blur);transition:border-radius var(--transition),padding var(--transition),max-width var(--transition),transform var(--transition-bezier-longer);margin:-0.5rem;box-shadow:var(--edge-highlight),var(--shadow-glass);border-radius:2.25rem 2.25rem 1.75rem 1.75rem;background-color:var(--glass-bg-1);padding:1rem;width:calc(100% - 2rem);max-width:calc(var(--content-width)/1.5);overflow:hidden}#search-modal-content:has(#search-results:empty){border-radius:1.75rem;padding:.5rem;max-width:calc(var(--content-width)/2)}#search-modal-content>div{position:relative}#search-modal #search-input{flex-shrink:0;box-shadow:var(--edge-highlight);border-radius:9999px;padding:0 1rem;width:100%;height:2.5rem}#search-modal #search-input:placeholder-shown+div .icon{transform:none;opacity:var(--disabled-opacity);pointer-events:all}#search-modal #search-input+div .icon{position:absolute;transform:translateX(0.5rem);opacity:0;transition:opacity var(--transition),transform var(--transition-bezier);cursor:help;inset-block-start:.625rem;inset-inline-end:1rem;pointer-events:none;color:var(--fg-muted-2);font-size:1.25rem}#search-modal #search-results{display:none;flex:1;flex-direction:column;gap:.5rem;margin:-1rem;padding:1rem;max-height:50vh;overflow:auto;scrollbar-width:thin}#search-modal #search-results .item{display:inline-flex;flex-direction:column;box-shadow:var(--edge-highlight);border-radius:var(--rounded-corner);background-color:var(--bg-muted-1);padding:.75rem 1rem}#search-modal #search-results .item a{text-decoration:none}#search-modal #search-results .item a::after{content:" →"}#search-modal #search-results .item span{margin-block-start:.25rem;border-block-start:rpx(1) solid var(--bg-muted-2);padding-block-start:.25rem;color:var(--fg-muted-2)}#search-modal #search-results .item span strong{color:var(--fg-color)}#skip-to-content{position:absolute;transform:translateX(-50%) translateY(calc(-100% - 1rem));z-index:9999;transition:backdrop-filter var(--transition),box-shadow var(--transition),transform var(--transition-bezier);inset-inline-start:50vw;border-radius:9999px;background-color:var(--glass-bg-1);padding:.375rem .75rem;width:max-content;text-decoration:none}#skip-to-content:focus-visible,#skip-to-content:focus{-webkit-backdrop-filter:var(--blur);transform:translateX(-50%) translateY(1rem);backdrop-filter:var(--blur);box-shadow:var(--edge-highlight),var(--shadow)}.statement-container{margin:1rem 0;box-shadow:var(--edge-highlight),var(--shadow);border-radius:var(--rounded-corner);padding:1rem}.statement-container :last-child{margin-block-end:0}.statement-container>:nth-child(2){margin-block-start:.5rem}.statement-container .title{font-weight:var(--font-weight-semi-bold)}.statement-container .title .icon{vertical-align:-0.125rem;margin-inline-end:.25rem}.statement-container.archive{--accent-color: var(--purple-fg);--accent-highlight: hsla( var(--purple-h), var(--purple-s), var(--purple-l), var(--disabled-opacity) );background-color:var(--purple-bg-1);color:var(--purple-fg)}.statement-container.disclaimer{--accent-color: var(--red-fg);--accent-highlight: hsla( var(--red-h), var(--red-s), var(--red-l), var(--disabled-opacity) );background-color:var(--red-bg-1);color:var(--red-fg)}.statement-container.trigger{--accent-color: var(--yellow-fg);--accent-highlight: hsla( var(--yellow-h), var(--yellow-s), var(--yellow-l), var(--disabled-opacity) );background-color:var(--yellow-bg-1);color:var(--yellow-fg)}#tag-chips{display:flex;flex-wrap:wrap;gap:.25rem;margin:0;padding-inline-start:0;list-style:none}#tag-chips li{display:flex;margin-block:0;padding-inline-start:0}#tag-chips a{display:flex;transition:background-color var(--transition),color var(--transition),transform var(--transition-bezier);box-shadow:var(--edge-highlight);border-radius:1rem;background-color:var(--bg-muted-1);padding:.25rem;padding-inline-start:.75rem;color:var(--fg-muted-1);font-size:var(--font-size-small);line-height:1.5rem;text-decoration:none}#tag-chips a:hover{background-color:var(--bg-muted-2);color:var(--fg-muted-2)}#tag-chips a:active{transform:var(--active)}#tag-chips a::before{content:"#"}#tag-chips a .counter{transition:inherit;margin-inline-start:.375rem;box-shadow:var(--edge-highlight);border-radius:inherit;background-color:inherit;aspect-ratio:1/1;height:1.5rem;line-height:1.5rem;font-variant-numeric:tabular-nums;text-align:center}h1:hover .zola-anchor,h2:hover .zola-anchor,h3:hover .zola-anchor,h4:hover .zola-anchor,h5:hover .zola-anchor,h6:hover .zola-anchor{transform:none;opacity:1}h1:has(.zola-anchor),h2:has(.zola-anchor),h3:has(.zola-anchor),h4:has(.zola-anchor),h5:has(.zola-anchor),h6:has(.zola-anchor){position:relative}.zola-anchor{position:absolute;transform:translateX(0.25em);opacity:0;transition:color var(--transition),opacity var(--transition),transform var(--transition-bezier);inset-inline-start:-1.25em;padding-inline-end:.25em;color:var(--fg-muted-1);line-height:1}@media only screen and (max-width: 720px){.zola-anchor{display:none}}.zola-anchor:hover{color:var(--fg-muted-2)}.zola-anchor:active{transform:var(--active)}.zola-anchor:focus-visible{opacity:1}:target .zola-anchor{transform:none;opacity:var(--disabled-opacity)}body>div:last-child:has(div:last-child[style]:not([class]):not([id])){z-index:9999 !important;background-color:var(--shade-color) !important}body>div:last-child>div:last-child[style]:not([class]):not([id]){box-shadow:var(--edge-highlight),var(--shadow-glass);border-radius:calc(var(--rounded-corner) + 1rem) !important;background-color:hsla(var(--red-h), calc(var(--red-s) - var(--red-s) * 0.7), calc(var(--red-l) + (100% - var(--red-l)) * 0.9), var(--dim-opacity)) !important;padding:1rem !important;width:fit-content;max-width:calc(100% - 2rem);-webkit-backdrop-filter:var(--blur);backdrop-filter:var(--blur);filter:none !important}@media(prefers-color-scheme: dark){body>div:last-child>div:last-child[style]:not([class]):not([id]){background-color:hsla(var(--red-h), calc(var(--red-s) - var(--red-s) * 0.6), calc(var(--red-l) - var(--red-l) * 0.8), var(--dim-opacity)) !important}}body>div:last-child>div:last-child[style]:not([class]):not([id])>p[style]:first-child{margin-block-end:1rem !important;color:var(--red-fg) !important;font-weight:var(--font-weight-semi-bold) !important;font-size:var(--font-size-large) !important}body>div:last-child>div:last-child[style]:not([class]):not([id])>pre[style]:last-child{border-radius:var(--rounded-corner) !important;background-color:var(--red-bg-1) !important;padding:.75rem 1rem !important;color:var(--red-fg) !important;font-weight:var(--font-weight-normal) !important} diff --git a/styles/vars.css b/styles/vars.css new file mode 100644 index 0000000..82e9b14 --- /dev/null +++ b/styles/vars.css @@ -0,0 +1,20 @@ +:root { + --alert-note-title: "Note"; + --alert-tip-title: "Tip"; + --alert-important-title: "Important"; + --alert-warning-title: "Warning"; + --alert-caution-title: "Caution"; + + --font-sans-serif: "Commissioner"; + --font-system-ui: ui-sans-serif, system-ui, sans-serif; + --font-emoji: "Noto Color Emoji"; + --font-monospace: "Commit Mono"; + + --icon-info: url("https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/info.svg"); + --icon-lightbulb: url("https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/lightbulb.svg"); + --icon-warning-diamond: url("https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/warning-diamond.svg"); + --icon-warning: url("https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/warning.svg"); + --icon-warning-octagon: url("https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/warning-octagon.svg"); + --icon-copy: url("https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/copy.svg"); + --icon-check-square-offset: url("https://unpkg.com/@phosphor-icons/web@2.1.1/src/regular/check-square-offset.svg"); +}