From a4e35942fee745e64db5d0099ba2582e0fb4417e Mon Sep 17 00:00:00 2001
From: Dennis Fink
Date: Thu, 18 Jun 2026 17:57:51 +0200
Subject: Build the first usable version of the blog
This adds the Lektor project structure, models, templates, styling,
feeds, categories, tags, archives, custom Markdown heading handling, and
the initial content needed to make the blog usable.
---
assets/static/css/.djlintrc | 14 +
assets/static/css/blog.css | 10 +
assets/static/css/categories.css | 61 ++
assets/static/css/category.css | 17 +
assets/static/css/libre_baskerville.css | 73 ++
assets/static/css/modern_normalize.css | 194 ++++++
assets/static/css/style.css | 753 +++++++++++++++++++++
...libre-baskerville-v24-latin_latin-ext-500.woff2 | Bin 0 -> 36692 bytes
...baskerville-v24-latin_latin-ext-500italic.woff2 | Bin 0 -> 38664 bytes
...libre-baskerville-v24-latin_latin-ext-600.woff2 | Bin 0 -> 36916 bytes
...baskerville-v24-latin_latin-ext-600italic.woff2 | Bin 0 -> 38864 bytes
...libre-baskerville-v24-latin_latin-ext-700.woff2 | Bin 0 -> 35944 bytes
...baskerville-v24-latin_latin-ext-700italic.woff2 | Bin 0 -> 38192 bytes
...re-baskerville-v24-latin_latin-ext-italic.woff2 | Bin 0 -> 37936 bytes
...e-baskerville-v24-latin_latin-ext-regular.woff2 | Bin 0 -> 35232 bytes
assets/static/images/auto.svg | 3 +
assets/static/images/dark.svg | 3 +
assets/static/images/email.svg | 8 +
assets/static/images/favicon.png | Bin 0 -> 1132 bytes
assets/static/images/light.svg | 3 +
assets/static/images/logo.svg | 21 +
assets/static/images/mastodon.svg | 8 +
assets/static/images/opengraph.png | Bin 0 -> 27513 bytes
assets/static/images/rss.svg | 4 +
assets/static/images/xmpp.svg | 8 +
25 files changed, 1180 insertions(+)
create mode 100644 assets/static/css/.djlintrc
create mode 100644 assets/static/css/blog.css
create mode 100644 assets/static/css/categories.css
create mode 100644 assets/static/css/category.css
create mode 100644 assets/static/css/libre_baskerville.css
create mode 100644 assets/static/css/modern_normalize.css
create mode 100644 assets/static/css/style.css
create mode 100644 assets/static/fonts/libre-baskerville-v24-latin_latin-ext-500.woff2
create mode 100644 assets/static/fonts/libre-baskerville-v24-latin_latin-ext-500italic.woff2
create mode 100644 assets/static/fonts/libre-baskerville-v24-latin_latin-ext-600.woff2
create mode 100644 assets/static/fonts/libre-baskerville-v24-latin_latin-ext-600italic.woff2
create mode 100644 assets/static/fonts/libre-baskerville-v24-latin_latin-ext-700.woff2
create mode 100644 assets/static/fonts/libre-baskerville-v24-latin_latin-ext-700italic.woff2
create mode 100644 assets/static/fonts/libre-baskerville-v24-latin_latin-ext-italic.woff2
create mode 100644 assets/static/fonts/libre-baskerville-v24-latin_latin-ext-regular.woff2
create mode 100644 assets/static/images/auto.svg
create mode 100644 assets/static/images/dark.svg
create mode 100644 assets/static/images/email.svg
create mode 100644 assets/static/images/favicon.png
create mode 100644 assets/static/images/light.svg
create mode 100644 assets/static/images/logo.svg
create mode 100644 assets/static/images/mastodon.svg
create mode 100644 assets/static/images/opengraph.png
create mode 100644 assets/static/images/rss.svg
create mode 100644 assets/static/images/xmpp.svg
(limited to 'assets')
diff --git a/assets/static/css/.djlintrc b/assets/static/css/.djlintrc
new file mode 100644
index 0000000..5882faa
--- /dev/null
+++ b/assets/static/css/.djlintrc
@@ -0,0 +1,14 @@
+{
+"blank_line_before_tag": "macro",
+"blank_line_after_tag": "endmacro,extends,import,from",
+"profile": "jinja",
+"format_css": true,
+"css": {
+"indent_size": 2
+},
+"format_js": true,
+"js": {
+"indent_size": 2
+},
+"indent": "2"
+}
diff --git a/assets/static/css/blog.css b/assets/static/css/blog.css
new file mode 100644
index 0000000..48a716b
--- /dev/null
+++ b/assets/static/css/blog.css
@@ -0,0 +1,10 @@
+@charset "UTF-8";
+
+article > p:first-of-type:first-letter {
+ color: var(--blue);
+ float: left;
+ font-weight: 700;
+ font-size: 3.8rem;
+ line-height: 3.8rem;
+ margin: 0.2em 0.1em 0 0;
+}
diff --git a/assets/static/css/categories.css b/assets/static/css/categories.css
new file mode 100644
index 0000000..5e26021
--- /dev/null
+++ b/assets/static/css/categories.css
@@ -0,0 +1,61 @@
+@charset "UTF-8";
+
+article > ul {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 0.5rem;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+
+ @media (max-width: 500px) {
+ grid-template-columns: 1fr;
+ }
+
+ li {
+ max-inline-size: unset;
+ }
+
+ a {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ padding: 1rem;
+ border: 1px solid var(--foreground);
+ border-radius: 0.5rem;
+ height: 100%;
+
+ &:hover,
+ &:hover > dl {
+ border-color: var(--blue);
+ /*background-color: var(--blue);*/
+ color: var(--blue);
+ }
+
+ h3 {
+ margin: 0;
+ }
+
+ dl {
+ display: flex;
+ flex-direction: column;
+ color: var(--dim-foreground);
+ gap: 0.25rem;
+ font-size: 1.2rem;
+ font-style: italic;
+ line-height: initial;
+ margin: 0;
+
+ div {
+ display: flex;
+ flex-direction: row;
+ gap: 0.25rem;
+ }
+
+ dd {
+ margin: 0;
+ max-inline-size: unset;
+ }
+ }
+ }
+}
diff --git a/assets/static/css/category.css b/assets/static/css/category.css
new file mode 100644
index 0000000..e3567d8
--- /dev/null
+++ b/assets/static/css/category.css
@@ -0,0 +1,17 @@
+@charset "UTF-8";
+
+article > table {
+ border-collapse: collapse;
+
+ td {
+ vertical-align: top;
+ padding-bottom: 0.25rem;
+ }
+
+ td:first-child {
+ color: var(--dim-foreground);
+ font-style: italic;
+ padding-right: 2rem;
+ white-space: nowrap;
+ }
+}
diff --git a/assets/static/css/libre_baskerville.css b/assets/static/css/libre_baskerville.css
new file mode 100644
index 0000000..fa61e73
--- /dev/null
+++ b/assets/static/css/libre_baskerville.css
@@ -0,0 +1,73 @@
+@charset "UTF-8";
+
+@font-face {
+ font-display: swap;
+ font-family: "Libre Baskerville";
+ font-style: normal;
+ font-weight: 400;
+ src: url("/static/fonts/libre-baskerville-v24-latin_latin-ext-regular.woff2")
+ format("woff2");
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Libre Baskerville";
+ font-style: italic;
+ font-weight: 400;
+ src: url("/static/fonts/libre-baskerville-v24-latin_latin-ext-italic.woff2")
+ format("woff2");
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Libre Baskerville";
+ font-style: normal;
+ font-weight: 500;
+ src: url("/static/fonts/libre-baskerville-v24-latin_latin-ext-500.woff2")
+ format("woff2");
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Libre Baskerville";
+ font-style: italic;
+ font-weight: 500;
+ src: url("/static/fonts/libre-baskerville-v24-latin_latin-ext-500italic.woff2")
+ format("woff2");
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Libre Baskerville";
+ font-style: normal;
+ font-weight: 600;
+ src: url("/static/fonts/libre-baskerville-v24-latin_latin-ext-600.woff2")
+ format("woff2");
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Libre Baskerville";
+ font-style: italic;
+ font-weight: 600;
+ src: url("/static/fonts/libre-baskerville-v24-latin_latin-ext-600italic.woff2")
+ format("woff2");
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Libre Baskerville";
+ font-style: normal;
+ font-weight: 700;
+ src: url("/static/fonts/libre-baskerville-v24-latin_latin-ext-700.woff2")
+ format("woff2");
+}
+
+@font-face {
+ font-display: swap;
+ font-family: "Libre Baskerville";
+ font-style: italic;
+ font-weight: 700;
+ src: url("/static/fonts/libre-baskerville-v24-latin_latin-ext-700italic.woff2")
+ format("woff2");
+}
diff --git a/assets/static/css/modern_normalize.css b/assets/static/css/modern_normalize.css
new file mode 100644
index 0000000..708b6a2
--- /dev/null
+++ b/assets/static/css/modern_normalize.css
@@ -0,0 +1,194 @@
+/*! modern-normalize v3.0.1 | MIT License | https://github.com/sindresorhus/modern-normalize */
+/*
+Document
+========
+*/
+/**
+Use a better box model (opinionated).
+*/
+*,
+::before,
+::after {
+ box-sizing: border-box;
+}
+
+html {
+ /* Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3) */
+ font-family:
+ system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif,
+ "Apple Color Emoji", "Segoe UI Emoji";
+ line-height: 1.15;
+ /* 1. Correct the line height in all browsers. */
+ -webkit-text-size-adjust: 100%;
+ /* 2. Prevent adjustments of font size after orientation changes in iOS. */
+ tab-size: 4;
+ /* 3. Use a more readable tab size (opinionated). */
+}
+
+/*
+Sections
+========
+*/
+body {
+ margin: 0;
+ /* Remove the margin in all browsers. */
+}
+
+/*
+Text-level semantics
+====================
+*/
+/**
+Add the correct font weight in Chrome and Safari.
+*/
+b,
+strong {
+ font-weight: bolder;
+}
+
+/**
+1. Improve consistency of default fonts in all browsers. (https://github.com/sindresorhus/modern-normalize/issues/3)
+2. Correct the odd 'em' font sizing in all browsers.
+*/
+code,
+kbd,
+samp,
+pre {
+ font-family:
+ ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
+ /* 1 */
+ font-size: 1em;
+ /* 2 */
+}
+
+/**
+Add the correct font size in all browsers.
+*/
+small {
+ font-size: 80%;
+}
+
+/**
+Prevent 'sub' and 'sup' elements from affecting the line height in all browsers.
+*/
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+sup {
+ top: -0.5em;
+}
+
+/*
+Tabular data
+============
+*/
+/**
+Correct table border color inheritance in Chrome and Safari. (https://issues.chromium.org/issues/40615503, https://bugs.webkit.org/show_bug.cgi?id=195016)
+*/
+table {
+ border-color: currentcolor;
+}
+
+/*
+Forms
+=====
+*/
+/**
+1. Change the font styles in all browsers.
+2. Remove the margin in Firefox and Safari.
+*/
+button,
+input,
+optgroup,
+select,
+textarea {
+ font-family: inherit;
+ /* 1 */
+ font-size: 100%;
+ /* 1 */
+ line-height: 1.15;
+ /* 1 */
+ margin: 0;
+ /* 2 */
+}
+
+/**
+Correct the inability to style clickable types in iOS and Safari.
+*/
+button,
+[type="button"],
+[type="reset"],
+[type="submit"] {
+ -webkit-appearance: button;
+}
+
+/**
+Remove the padding so developers are not caught out when they zero out 'fieldset' elements in all browsers.
+*/
+legend {
+ padding: 0;
+}
+
+/**
+Add the correct vertical alignment in Chrome and Firefox.
+*/
+progress {
+ vertical-align: baseline;
+}
+
+/**
+Correct the cursor style of increment and decrement buttons in Safari.
+*/
+::-webkit-inner-spin-button,
+::-webkit-outer-spin-button {
+ height: auto;
+}
+
+/**
+1. Correct the odd appearance in Chrome and Safari.
+2. Correct the outline style in Safari.
+*/
+[type="search"] {
+ -webkit-appearance: textfield;
+ /* 1 */
+ outline-offset: -2px;
+ /* 2 */
+}
+
+/**
+Remove the inner padding in Chrome and Safari on macOS.
+*/
+::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/**
+1. Correct the inability to style clickable types in iOS and Safari.
+2. Change font properties to 'inherit' in Safari.
+*/
+::-webkit-file-upload-button {
+ -webkit-appearance: button;
+ /* 1 */
+ font: inherit;
+ /* 2 */
+}
+
+/*
+Interactive
+===========
+*/
+/*
+Add the correct display in Chrome and Safari.
+*/
+summary {
+ display: list-item;
+}
diff --git a/assets/static/css/style.css b/assets/static/css/style.css
new file mode 100644
index 0000000..ca7443a
--- /dev/null
+++ b/assets/static/css/style.css
@@ -0,0 +1,753 @@
+@charset "UTF-8";
+
+/*
+=============================================================================
+CUSTOM PROPERTIES
+=============================================================================
+*/
+:root {
+ /* Named palette */
+ --black: light-dark(#575279, #393b44);
+ --red: light-dark(#b4637a, #c94f6d);
+ --green: light-dark(#618774, #81b29a);
+ --yellow: light-dark(#ea9d34, #dbc074);
+ --blue: light-dark(#286983, #719cd6);
+ --magenta: light-dark(#907aa9, #9d79d6);
+ --cyan: light-dark(#56949f, #63cdcf);
+ --white: light-dark(#e5e9f0, #dfdfe0);
+ --orange: light-dark(#d7827e, #f4a261);
+ --pink: light-dark(#d685af, #d67ad2);
+ /* Dim palette */
+ --dim-black: light-dark(#504c6b, #30323a);
+ --dim-red: light-dark(#a5576d, #ab435d);
+ --dim-green: light-dark(#597668, #6e9783);
+ --dim-yellow: light-dark(#dd9024, #baa363);
+ --dim-blue: light-dark(#295e73, #6085b6);
+ --dim-magenta: light-dark(#816b9a, #8567b7);
+ --dim-cyan: light-dark(#50848c, #54aeb0);
+ --dim-white: light-dark(#c8cfde, #bebebe);
+ --dim-foreground: light-dark(#4c4769, #aeafb0);
+ /* Bright palette */
+ --bright-black: light-dark(#5f5695, #575860);
+ --bright-red: light-dark(#c26d85, #d16983);
+ --bright-green: light-dark(#629f81, #8ebaa4);
+ --bright-yellow: light-dark(#eea846, #e0c989);
+ --bright-blue: light-dark(#2d81a3, #86abdc);
+ --bright-magenta: light-dark(#9a80b9, #baa1e2);
+ --bright-cyan: light-dark(#5ca7b4, #7ad5d6);
+ --bright-white: light-dark(#e6ebf3, #e4e4e5);
+ --bright-foreground: light-dark(#625c87, #d6d6d7);
+ /* Semantic */
+ --foreground: light-dark(#575279, #cdcecf);
+ --background: light-dark(#faf4ed, #192330);
+ /* Layout */
+ --measure: 60ch;
+ --column-min-width: calc(1/3 * 100vw);
+ --column-max-width: 100%;
+ /* Gradient border — reused in header, footer */
+ --gradient-border: linear-gradient(
+ to right,
+ var(--foreground),
+ var(--green),
+ var(--foreground)
+ );
+
+ /* Responsive layout */
+ @media (min-width: 500px) {
+ --column-max-width: var(--measure);
+ }
+
+ &:has(input[name="theme-switcher"][value="light dark"]:checked) {
+ color-scheme: light dark;
+ }
+
+ &:has(input[name="theme-switcher"][value="light"]:checked) {
+ color-scheme: light;
+ }
+
+ &:has(input[name="theme-switcher"][value="dark"]:checked) {
+ color-scheme: dark;
+ }
+}
+
+/*
+=============================================================================
+BASE
+=============================================================================
+ */
+html {
+ font-size: 14px;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+ min-height: 100vh;
+ min-width: var(--column-min-width);
+ max-width: var(--column-max-width);
+ margin: 0 auto;
+ background-color: var(--background);
+ color: var(--foreground);
+ font-family: "Libre Baskerville", Georgia, serif;
+ font-size: clamp(1rem, 1.5vw, 1.4rem);
+ line-height: 1.6;
+
+ @media (max-width: 500px) {
+ padding: 0 1.5rem;
+ }
+
+ & > footer {
+ border-image-slice: 1;
+ border-image-source: var(--gradient-border);
+ border-image-width: 1px 0 0 0;
+ border-top: 1px solid;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ margin-bottom: 2rem;
+ margin-top: auto;
+ padding-top: 0.5rem;
+
+ @media (max-width: 500px) {
+ flex-direction: column;
+ gap: 2rem;
+ }
+
+ h2 {
+ font-size: clamp(1.2rem, 2.5vw, 1.7rem);
+ font-style: italic;
+ font-weight: 400;
+ line-height: 1;
+ }
+
+ section div {
+ display: flex;
+ flex-direction: row;
+ gap: 0.25rem;
+ }
+
+ & > section[aria-label="Follow me"] a {
+ background-color: var(--green);
+ display: inline-block;
+ height: 3rem;
+ mask-size: cover;
+ width: 3rem;
+
+ &:hover {
+ background-color: var(--blue);
+ }
+
+ atom {
+ mask-image: url("/static/images/rss.svg");
+ }
+
+ mastodon {
+ mask-image: url("/static/images/mastodon.svg");
+ }
+
+ mpp {
+ mask-image: url("/static/images/xmpp.svg");
+ }
+
+ email {
+ mask-image: url("/static/images/email.svg");
+ }
+ }
+
+ ul {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+ }
+}
+
+/*
+=============================================================================
+TYPOGRAPHY
+=============================================================================
+*/
+
+/* --- Headings --- */
+h1 {
+ font-size: clamp(2rem, 4vw, 3.2rem);
+ font-weight: 700;
+ line-height: 1;
+ margin: 0 0 0.5rem 0;
+}
+
+h2 {
+ font-size: clamp(1.5rem, 3vw, 2.2rem);
+ font-style: italic;
+ font-weight: 500;
+ line-height: 1;
+ margin: 1rem 0;
+}
+
+h3 {
+ font-size: clamp(1.2rem, 2.5vw, 1.7rem);
+ font-style: italic;
+ font-weight: 400;
+ line-height: 1;
+ margin-bottom: 1rem;
+}
+
+/* --- Body text --- */
+p {
+ font-size: clamp(1rem, 1.5vw, 1.4rem);
+ line-height: 1.5em;
+ margin: 0;
+ text-align: justify;
+ text-wrap: pretty;
+ word-spacing: 0.16em;
+
+ &.center {
+ text-align: center;
+ }
+}
+
+blockquote {
+ font-style: italic;
+ border-left: 2px solid var(--blue);
+ margin: 1em 0 1em 0.5em;
+ padding-left: 0.5em;
+}
+
+/* --- Lists --- */
+ul,
+ol,
+dl {
+ font-size: clamp(1rem, 1.5vw, 1.4rem);
+ line-height: 2rem;
+}
+
+li,
+dd {
+ max-inline-size: var(--measure);
+}
+
+/* --- Rules --- */
+hr {
+ border: 0;
+ border-top: 2px solid var(--blue);
+ margin: 1em 0;
+}
+
+/* --- Links --- */
+a {
+ color: var(--green);
+ text-decoration: none;
+ text-decoration-thickness: 0.05em;
+ text-underline-offset: 0.1em;
+
+ &:hover {
+ color: var(--blue);
+ }
+}
+
+/*
+=============================================================================
+SKIP LINK (accessibility)
+=============================================================================
+ */
+.skip-link {
+ background: #fff;
+ color: #000;
+ left: 0;
+ padding: 0.5rem 1rem;
+ position: absolute;
+ text-decoration: none;
+ top: 0;
+ transform: translateY(-120%);
+ transition: transform 0.2s ease;
+ z-index: 1000;
+
+ &:focus,
+ &:focus-visible {
+ transform: translateY(0);
+ }
+}
+
+.visually-hidden {
+ clip: rect(0 0 0 0);
+ clip-path: inset(50%);
+ height: 1px;
+ overflow: hidden;
+ position: absolute;
+ white-space: nowrap;
+ width: 1px;
+}
+
+/*
+=============================================================================
+SITE HEADER
+=============================================================================
+*/
+body > header {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+ justify-content: space-evenly;
+ margin-top: 4rem;
+ padding-bottom: 1rem;
+ border-bottom: 1px solid;
+ border-image-slice: 1;
+ border-image-source: var(--gradient-border);
+ border-image-width: 0 0 1px 0;
+
+ & > div {
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ gap: 0.25rem;
+ }
+
+ svg {
+ max-width: 4em;
+ max-height: 4em;
+ fill: var(--green);
+
+ @media (max-width: 500px) {
+ display: none;
+ }
+ }
+
+ p {
+ font-size: clamp(1.1rem, 2vw, 1.75rem);
+ font-style: italic;
+ max-width: unset;
+ }
+
+ /* --- Navigation --- */
+ nav {
+ ul {
+ display: flex;
+ flex-direction: row;
+ gap: 1rem;
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ }
+
+ a[aria-current="page"] {
+ color: var(--blue);
+
+ &:hover {
+ color: var(--bright-blue);
+ }
+ }
+ }
+
+ /* --- Theme switcher --- */
+ fieldset {
+ align-items: center;
+ border: 0;
+ display: flex;
+ flex-direction: row;
+ gap: 0.25rem;
+ justify-content: flex-start;
+ margin: 0;
+ padding: 0;
+
+ input[type="radio"] {
+ display: none;
+
+ &:checked + span {
+ background-color: var(--yellow);
+ }
+ }
+
+ label span {
+ background-color: var(--foreground);
+ cursor: pointer;
+ display: block;
+ height: 1.5rem;
+ mask-size: cover;
+ width: 1.5rem;
+ }
+ }
+}
+
+#lightmode + span,
+#automode + span,
+#darkmode + span {
+ mask-repeat: no-repeat;
+ mask-position: 50% 50%;
+}
+
+#lightmode + span {
+ mask-image: url("/static/images/light.svg");
+}
+
+#automode + span {
+ mask-image: url("/static/images/auto.svg");
+}
+
+#darkmode + span {
+ mask-image: url("/static/images/dark.svg");
+}
+
+/*
+=============================================================================
+MAIN
+=============================================================================
+*/
+main {
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 2rem;
+ margin-top: 2rem;
+}
+
+/*
+=============================================================================
+ARTICLES
+=============================================================================
+*/
+article {
+ max-width: var(--column-max-width);
+ min-width: var(--column-min-width);
+
+ & > header {
+ display: flex;
+ flex-direction: row;
+ align-items: start;
+ justify-content: space-between;
+
+ &:not(:has(nav)) {
+ margin-bottom: 2rem;
+ }
+
+ & > div {
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ max-width: calc(2 / 3 * 100%);
+
+ /* --- Article metadata --- */
+ & > dl {
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ font-size: 1rem;
+ gap: calc(1 / 3 * 1rem);
+ margin: 0;
+
+ div {
+ align-items: center;
+ border: 1px solid var(--foreground);
+ border-radius: 0.5rem;
+ display: flex;
+ flex-direction: row;
+ }
+
+ dt {
+ color: var(--bright-blue);
+ padding-left: calc(1 / 3 * 1rem);
+ padding-right: calc(1 / 3 * 1rem);
+ }
+
+ dd {
+ margin: 0;
+ padding-right: calc(1 / 3 * 1rem);
+
+ a {
+ color: var(--green);
+ }
+ }
+
+ @media (max-width: 700px) {
+ margin-top: 2rem;
+ }
+
+ @media (max-width: 500px) {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+ }
+ }
+
+ h2 {
+ margin-bottom: 0.25rem;
+ margin-top: 0;
+ }
+
+ /* --- TOC --- */
+ & > nav {
+ border: 1px solid var(--foreground);
+ border-radius: 0.5rem;
+ font-size: 1rem;
+ line-height: 1.4;
+ padding: 0.75rem;
+ max-width: calc(1 / 3 * 100%);
+
+ & > p {
+ color: var(--blue);
+ font-size: 1em;
+ font-weight: 700;
+ line-height: 1.2;
+ margin: 0 0 0.5rem 0;
+ }
+
+ ol {
+ font-size: 1em;
+ line-height: 1.4;
+ margin: 0;
+ padding-left: 1.5em;
+
+ ol {
+ margin-top: 0.25rem;
+ padding-left: 1.25em;
+ }
+ }
+
+ li {
+ max-inline-size: none;
+
+ & + li {
+ margin-top: 0.25rem;
+ }
+ }
+
+ a {
+ overflow-wrap: anywhere;
+ }
+
+ @media (max-width: 700px) {
+ margin-top: 2rem;
+ }
+ }
+ }
+
+ /* --- Article body --- */
+ p {
+ max-inline-size: var(--measure);
+
+ & + p {
+ margin-top: 2rem;
+ }
+ }
+
+ & > h3:not(:first-of-type) {
+ margin-top: 2rem;
+ }
+
+ & > footer {
+ color: var(--dim-foreground);
+ font-style: italic;
+ margin-top: 1.5rem;
+
+ p {
+ display: inline;
+ font-size: 1.2rem;
+ line-height: 1.5;
+ margin: 0;
+ padding: 0;
+ }
+
+ ul {
+ font-size: 1.2rem;
+ line-height: 1.5;
+ margin: 0;
+ padding: 0;
+ display: inline-flex;
+ flex-wrap: wrap;
+ list-style: none;
+ }
+
+ li {
+ display: flex;
+ max-inline-size: none;
+ }
+ }
+
+ /* --- Article separator --- */
+ & + hr {
+ align-items: center;
+ background: none;
+ border: 0;
+ display: flex;
+ height: 1em;
+ justify-content: center;
+ margin: 4em 0;
+ position: relative;
+ width: 100%;
+
+ &::before {
+ background-image: var(--gradient-border);
+ content: "";
+ height: 1px;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 50%;
+ }
+
+ &::after {
+ background-color: var(--background);
+ color: var(--blue);
+ content: "§";
+ padding: 0 1rem;
+ position: relative;
+ transform: rotate(90deg);
+ z-index: 1;
+ }
+ }
+}
+
+/*
+=============================================================================
+PAGINATION
+=============================================================================
+ */
+nav:is(
+ [aria-label="Page navigation"],
+ [aria-label="Year archive navigation"],
+ [aria-label="Monthly archive navigation"]
+) {
+ display: flex;
+ flex-direction: row;
+ justify-content: center;
+
+ ol {
+ align-items: center;
+ border: 1px solid var(--foreground);
+ border-radius: 0.5rem;
+ display: flex;
+ flex-direction: row;
+ gap: 0.25rem;
+ list-style: none;
+ margin: 2rem 0;
+ padding: 0.25rem;
+ }
+
+ a,
+ span {
+ align-items: center;
+ display: flex;
+ height: 2rem;
+ justify-content: center;
+ min-width: 2rem;
+ padding-inline: 0.5rem;
+ }
+
+ & a {
+ border-radius: 0.5rem;
+
+ &:hover,
+ &[aria-current="page"]:hover {
+ background-color: var(--blue);
+ color: var(--foreground);
+ }
+
+ &[aria-current="page"] {
+ color: var(--blue);
+ }
+ }
+
+ span[aria-disabled="true"] {
+ color: var(--dim-foreground);
+ }
+}
+
+/*
+=============================================================================
+IMAGES
+=============================================================================
+*/
+img {
+ height: auto;
+ max-width: 100%;
+
+ &.center {
+ display: block;
+ margin-inline: auto;
+ }
+
+ &.round {
+ border-radius: 100%;
+ }
+
+ &.w25 {
+ width: 25%;
+ }
+
+ &.w33 {
+ width: calc(1 / 3 * 100%);
+ }
+
+ &.w50 {
+ width: 50%;
+ }
+
+ &.w66 {
+ width: calc(2 / 3 * 100%);
+ }
+
+ &.w75 {
+ width: 75%;
+ }
+
+ &.breakout-xs {
+ width: calc(100% + 16rem);
+ transform: translateX(-8rem);
+ }
+
+ &.breakout-sm {
+ width: calc(100% + 24rem);
+ transform: translateX(-12rem);
+ }
+
+ &.breakout-md {
+ width: calc(100% + 32rem);
+ transform: translateX(-16rem);
+ }
+
+ &.breakout-lg {
+ width: calc(100% + 48rem);
+ transform: translateX(-24rem);
+ }
+
+ &.breakout-xl {
+ width: calc(100% + 64rem);
+ transform: translateX(-32rem);
+ }
+
+ &.breakout-full {
+ width: 100vw;
+ margin-left: calc(50% - 50vw);
+ }
+
+ &.breakout-xs,
+ &.breakout-sm,
+ &.breakout-md,
+ &.breakout-lg,
+ &.breakout-xl,
+ &.breakout-full {
+ max-width: 100vw;
+ }
+
+ @media (max-width: 500px) {
+ &.breakout-xs,
+ &.breakout-sm,
+ &.breakout-md,
+ &.breakout-lg,
+ &.breakout-xl {
+ width: 100vw;
+ transform: none;
+ margin-left: calc(50% - 50vw);
+ }
+ }
+}
+
+.missing-publication-date-warning {
+ background: var(--red);
+ color: var(--foreground);
+ font-weight: 700;
+ font-size: 1.1rem;
+}
+
+.missing-publication-date-warning > dt {
+ color: var(--foreground);
+}
diff --git a/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-500.woff2 b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-500.woff2
new file mode 100644
index 0000000..4c373fe
Binary files /dev/null and b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-500.woff2 differ
diff --git a/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-500italic.woff2 b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-500italic.woff2
new file mode 100644
index 0000000..7e780a9
Binary files /dev/null and b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-500italic.woff2 differ
diff --git a/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-600.woff2 b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-600.woff2
new file mode 100644
index 0000000..f94f429
Binary files /dev/null and b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-600.woff2 differ
diff --git a/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-600italic.woff2 b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-600italic.woff2
new file mode 100644
index 0000000..067b7fb
Binary files /dev/null and b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-600italic.woff2 differ
diff --git a/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-700.woff2 b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-700.woff2
new file mode 100644
index 0000000..4e0f4e7
Binary files /dev/null and b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-700.woff2 differ
diff --git a/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-700italic.woff2 b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-700italic.woff2
new file mode 100644
index 0000000..5351437
Binary files /dev/null and b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-700italic.woff2 differ
diff --git a/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-italic.woff2 b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-italic.woff2
new file mode 100644
index 0000000..733d0dd
Binary files /dev/null and b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-italic.woff2 differ
diff --git a/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-regular.woff2 b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-regular.woff2
new file mode 100644
index 0000000..3c2170e
Binary files /dev/null and b/assets/static/fonts/libre-baskerville-v24-latin_latin-ext-regular.woff2 differ
diff --git a/assets/static/images/auto.svg b/assets/static/images/auto.svg
new file mode 100644
index 0000000..5032980
--- /dev/null
+++ b/assets/static/images/auto.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/static/images/dark.svg b/assets/static/images/dark.svg
new file mode 100644
index 0000000..ccb82f5
--- /dev/null
+++ b/assets/static/images/dark.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/static/images/email.svg b/assets/static/images/email.svg
new file mode 100644
index 0000000..2c197d4
--- /dev/null
+++ b/assets/static/images/email.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/static/images/favicon.png b/assets/static/images/favicon.png
new file mode 100644
index 0000000..cb4a2f8
Binary files /dev/null and b/assets/static/images/favicon.png differ
diff --git a/assets/static/images/light.svg b/assets/static/images/light.svg
new file mode 100644
index 0000000..56e1084
--- /dev/null
+++ b/assets/static/images/light.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/static/images/logo.svg b/assets/static/images/logo.svg
new file mode 100644
index 0000000..a3090ad
--- /dev/null
+++ b/assets/static/images/logo.svg
@@ -0,0 +1,21 @@
+
+
+
+
diff --git a/assets/static/images/mastodon.svg b/assets/static/images/mastodon.svg
new file mode 100644
index 0000000..f6b8203
--- /dev/null
+++ b/assets/static/images/mastodon.svg
@@ -0,0 +1,8 @@
+
diff --git a/assets/static/images/opengraph.png b/assets/static/images/opengraph.png
new file mode 100644
index 0000000..c44994f
Binary files /dev/null and b/assets/static/images/opengraph.png differ
diff --git a/assets/static/images/rss.svg b/assets/static/images/rss.svg
new file mode 100644
index 0000000..0a6bed9
--- /dev/null
+++ b/assets/static/images/rss.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/static/images/xmpp.svg b/assets/static/images/xmpp.svg
new file mode 100644
index 0000000..9d33519
--- /dev/null
+++ b/assets/static/images/xmpp.svg
@@ -0,0 +1,8 @@
+
--
cgit v1.3.1