:root {
  /* width of content */
  --measure: min(850px, 100% - 2 * clamp(0.75rem, 3vw, 1rem));

  color-scheme: light dark; /* follow system; UA chrome (scrollbars, controls) matches */

  --space-md: 1rem;
  --space-lg: 2rem;

  /* Palette — Flexoki (stephango.com/flexoki). Semantic names so values
     swap per mode. Dark is the default; light lives in the media query below. */
  --dark-bg: #100f0f; /* Flexoki black — warm near-black, not void #000 */
  --dark-fg: #deddd3; /* text */
  --dark-muted: #878580; /* metadata / secondary text */
  --dark-rule: #403e3c; /* hairlines & borders */
  --dark-accent: #d0a215; /* soft amber — one accent, used sparingly */
  --dark-info: #879a39; /* muted green for informational callouts */
  --dark-code-bg: #1c1b1a; /* code panel: one step lighter than the page */

  --light-bg: #ffffff; /* barely-warm white — crisp, not tea-stained */
  --light-fg: #000000; /* ink */
  --light-muted: #6f6e69; /* metadata / secondary text */
  --light-rule: #dad9d0; /* hairlines & borders */
  --light-accent: #af3029; /* deep red — amber is too pale on light */
  --light-info: #536907; /* muted green for informational callouts */
  --light-code-bg: #f6f5f1; /* code panel: near-white, a breath off the page */

  --bg: var(--dark-bg);
  --fg: var(--dark-fg);
  --muted: var(--dark-muted);
  --rule: var(--dark-rule);
  --accent: var(--dark-accent);
  --info: var(--dark-info);
  --code-bg: var(--dark-code-bg);
}

.theme-toggle {
  appearance: none;
  -webkit-appearance: none;
  position: relative;
  border: 0;
  padding: 0;
  margin: 0;
  font: inherit; /* anchor the em sizing below to nav text */
  cursor: pointer;
  color: inherit;
  background-color: transparent;

  inline-size: 2rem;
  block-size: 2rem;
  align-self: center;
}

/* Mask the icon separately so the invisible hit area can extend beyond it. */
.theme-toggle::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: currentColor;
  -webkit-mask: var(--icon-sun) center / 0.95em 0.95em no-repeat;
  mask: var(--icon-sun) center / 0.95em 0.95em no-repeat;
}

.theme-toggle::after {
  content: "";
  position: absolute;
  inset: -0.75rem;
}

.theme-toggle:hover,
.theme-toggle:focus-visible {
  color: var(--accent);
}

/* Icon mirrors the effective theme, same cascade as the palette:
   dark -> sun (click for light); light -> moon (click for dark). */
@media (prefers-color-scheme: light) {
  .theme-toggle::before {
    -webkit-mask-image: var(--icon-moon);
    mask-image: var(--icon-moon);
  }
}
:root[data-theme="dark"] .theme-toggle::before {
  -webkit-mask-image: var(--icon-sun);
  mask-image: var(--icon-sun);
}
:root[data-theme="light"] .theme-toggle::before {
  -webkit-mask-image: var(--icon-moon);
  mask-image: var(--icon-moon);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;
    --bg: var(--light-bg);
    --fg: var(--light-fg);
    --muted: var(--light-muted);
    --rule: var(--light-rule);
    --accent: var(--light-accent);
    --info: var(--light-info);
    --code-bg: var(--light-code-bg);
  }
}

/* Manual override from the nav toggle: it sets data-theme on <html>, which
   beats the media query above (attribute selector = higher specificity). */
:root[data-theme="dark"] {
  color-scheme: dark;
}
:root[data-theme="light"] {
  color-scheme: light;
  --bg: var(--light-bg);
  --fg: var(--light-fg);
  --muted: var(--light-muted);
  --rule: var(--light-rule);
  --accent: var(--light-accent);
  --info: var(--light-info);
  --code-bg: var(--light-code-bg);
}

/* Reset from https://www.joshwcomeau.com/css/custom-css-reset/ */

/* 1. Use a more-intuitive box-sizing model */
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* 2. Remove default margin */
*:not(dialog) {
  margin: 0;
}

/* 3. Enable keyword animations */
@media (prefers-reduced-motion: no-preference) {
  html {
    interpolate-size: allow-keywords;
  }
}

html {
  scrollbar-gutter: stable;
  background: var(--bg);
  color: var(--fg);
}

body {
  /* 4. Increase line-height */
  line-height: 1.5;
}

/* 6. Improve media defaults */
img,
picture,
video,
canvas,
svg {
  display: block;
  max-width: 100%;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* 7. Inherit fonts for form controls */
input,
button,
textarea,
select {
  font: inherit;
}

/* 8. Avoid text overflows */
p,
h1,
h2,
h3,
h4,
h5,
h6 {
  overflow-wrap: break-word;
}

/* 9. Improve line wrapping */
p {
  /* `pretty` avoids last-line orphans but does so by shortening an earlier
     line, which at narrow widths leaves a glaring gap. Greedy `auto` fill
     reads better for body copy. */
  text-wrap: auto;
}
h1,
h2,
h3,
h4,
h5,
h6 {
  /* `balance` evens the lines but breaks before a word that would fit (it pushed
     "IPv6" to line 2). Greedy `auto` fills each line first — matches the p rule. */
  text-wrap: auto;
}

/*
  10. Create a root stacking context
*/
#root,
#__next {
  isolation: isolate;
}

/* === my styles === */

body {
  container: body / inline-size;
  overflow-x: clip;

  font-family: "Source Serif 4", serif;
  font-optical-sizing: auto;
  font-weight: 400;
  font-style: normal;
  font-size: 1.0625rem;
  line-height: 1.7;

  color: var(--fg);
  background: var(--bg);

  /* sneakily push the <footer> to the bottom by having the <main>
     consume up to 100% of one viewport, so on short pages it's footer
     at the bottom and on longer ones it does nothing. */
  display: flex;
  flex-direction: column;
  min-height: 100vh; /* fallback for browsers without svh, which is 2022 */
}

@supports (min-height: 100svh) {
  body {
    min-height: 100svh; /* stable on mobile, with no URL-bar jump */
  }
}

body > main {
  flex: 1 0 auto;
} /* fill all the space above the footer */

main,
body > header,
body > footer {
  max-width: var(--measure);
  /* no padding-inline: --measure already reserves the edge gutters on
     narrow screens; on wide screens the auto margins are the gutter */
  margin-inline: auto;
  width: 100%;
}

body > header {
  padding-block-start: 0.5rem;
}

.tilted-letter {
  display: inline-block;
  transform: rotate(-20deg);
  padding-right: 0.05em;
}

/* A small, uneven wobble, with the original e as the strongest tilt. */
.tilted-letter:nth-child(2) {
  transform: rotate(6deg);
}

.tilted-letter:nth-child(3) {
  transform: rotate(-8deg);
}

.tilted-letter:nth-child(4) {
  transform: rotate(4deg);
}

.tilted-letter:nth-child(5) {
  transform: rotate(-5deg);
}

.tilted-letter:nth-child(6) {
  transform: rotate(8deg);
}

.tld .tilted-letter:first-child {
  transform: rotate(-5deg);
}

body > header nav {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.75em;
  line-height: 1.3; /* chrome, not prose */
  font-size: 0.8rem;
  font-weight: 400;
  color: var(--muted);
  column-gap: 1.1rem;
}

.nav-location {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25em 0.65em;
}

.domain,
.nav-section {
  font-size: 1rem;
  font-weight: 700;
}

.nav-section {
  display: inline-flex;
  align-items: baseline;
  gap: 0.45em;
  color: var(--fg);
}

.nav-separator {
  /* Keep the larger glyph from changing the header's shared text baseline. */
  align-self: center;
  font-size: 1.25em;
  font-weight: 400;
  line-height: 1;
  color: var(--accent);
}

body > header .theme-toggle {
  margin-inline-start: auto;
  font-size: 0.9rem;
  /* the default 2rem box would set the line height; keep it text-sized
     so the nav row stays as tight as the words */
  block-size: 1.3em;
  inline-size: 1.2em;
}

body > header a {
  color: inherit;
  text-decoration-line: none;
}

body > header a:hover {
  color: var(--accent);
  text-decoration-line: none;
}

body > header a.active {
  text-decoration-line: underline;
  text-decoration-color: var(--accent);
  text-underline-offset: 0.15em;
}

.domain {
  position: relative;
  isolation: isolate; /* keeps the underline's z-index: -1 inside the logo */
  color: var(--fg);
  transition: color 120ms;
}

/* Draw across the tilted inline-block letters as well as the rest of the logo. */
body > header a.domain.active {
  text-decoration-line: none;
}

body > header a.domain.active::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  /* Source Serif: descent 0.335em, half-leading at line-height 1.3 is
     -0.036em, so the box bottom is 0.30em below the baseline. 0.1em up from
     there puts the line 0.2em below the baseline, through the descender
     of the g (which reaches 0.24em); painted behind the text, the g
     interrupts it like an ink-skipping underline would */
  inset-block-end: 0.1em;
  border-block-end: 1px solid var(--accent);
  z-index: -1;
  pointer-events: none;
}

/* compact `date · tags` caption under the title: tiny mono, centered
   with the title block */
div.metadata {
  /* biome-ignore lint/suspicious/noDuplicateFontNames: Deliberate workaround for browser bug - https://github.com/necolas/normalize.css/issues/519 */
  font-family: "JetBrains Mono", monospace, monospace;
  font-size: 0.8rem;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75em;
}

div.metadata a {
  color: inherit;
  text-decoration-line: none;
}

div.metadata a:hover,
div.metadata a:focus-visible {
  color: var(--accent);
}

/* === Code blocks === */

/* inline code: the mono face is signal enough — a chip background
   mid-sentence breaks the reading line. Panels are for blocks. */
code:not(pre code) {
  /* biome-ignore lint/suspicious/noDuplicateFontNames: Deliberate workaround for browser bug - https://github.com/necolas/normalize.css/issues/519 */
  font-family: "JetBrains Mono", monospace, monospace;
  font-size: 0.9em;
}

article pre {
  overflow-x: auto;
  padding: 0;
  white-space: pre;
}

pre.giallo {
  position: relative; /* anchors the copy overlay */
  overflow: hidden;
  padding: 0;
  border-radius: 4px;
  /* our own faint panel — the one marker code blocks get; the syntax
     themes' code backgrounds are not carried over (see below) */
  background-color: var(--code-bg);
}

pre.giallo > code[data-lang] {
  display: block;
  overflow-x: auto;
  padding: 0.6em 0.9em;
  /* biome-ignore lint/suspicious/noDuplicateFontNames: Deliberate workaround for browser bug - https://github.com/necolas/normalize.css/issues/519 */
  font-family: "JetBrains Mono", monospace, monospace;
  font-size: 85%;
  line-height: 1.45;
}

pre.giallo > code[data-lang]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
}

/* === Syntax highlighting === */
/* Zola (giallo) gives every token a light class (z-l-*) and a dark class
   (z-d-*) and generates one stylesheet per theme. Neither is served: the
   values are folded in below by `mise run syntax:fold`, each class only
   recording its colour in a custom property, and the rules here pick the
   light or dark one on the same cascade as the palette above. So the switch
   is pure CSS, nothing runs before first paint, and there are no extra
   stylesheet requests. The themes' own code backgrounds are left out: the
   panel is --code-bg (pre.giallo above). Inside a block every classed
   element is a giallo token or line number. tests/test_asset_urls.py fails
   the build if the values drift from what Zola generates. */

pre.giallo,
pre.giallo code [class] {
  color: var(--zd, currentcolor);
}

pre.giallo code [class] {
  background-color: var(--zd-bg, transparent);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) :is(pre.giallo, pre.giallo code [class]) {
    color: var(--zl, currentcolor);
  }

  :root:not([data-theme]) pre.giallo code [class] {
    background-color: var(--zl-bg, transparent);
  }
}

:root[data-theme="light"] :is(pre.giallo, pre.giallo code [class]) {
  color: var(--zl, currentcolor);
}

:root[data-theme="light"] pre.giallo code [class] {
  background-color: var(--zl-bg, transparent);
}

/* === Syntax theme values: generated by scripts/fold-syntax-themes, do not edit === */
/* theme "github-light-high-contrast" */
.z-l-code {
  --zl: #0e1116;
}
.z-l-hl {
  --zl-bg: #e7ecf0;
}
.giallo-ln {
  --zl: #88929d;
}
.z-l-1 {
  --zl: #023b95;
}
.z-l-2 {
  --zl: #024c1a;
}
.z-l-3 {
  --zl: #032563;
}
.z-l-4 {
  --zl: #4b535d;
}
.z-l-5 {
  --zl: #622cbc;
}
.z-l-6 {
  --zl: #66707b;
}
.z-l-7 {
  --zl: #6e011a;
}
.z-l-8 {
  --zl: #702c00;
}
.z-l-9 {
  --zl: #a0111f;
}
.z-l-10 {
  --zl: #e7ecf0;
}
.z-l-11 {
  --zl: #ffffff;
}
.z-l-bg1 {
  --zl-bg: #023b95;
}
.z-l-bg2 {
  --zl-bg: #a0111f;
}
.z-l-bg3 {
  --zl-bg: #d2fedb;
}
.z-l-bg4 {
  --zl-bg: #ffc67b;
}
.z-l-bg5 {
  --zl-bg: #fff0ee;
}
.z-l-b {
  font-weight: bold;
}
.z-l-i {
  font-style: italic;
}
.z-l-u {
  text-decoration: underline;
}
.z-l-s {
  text-decoration: line-through;
}
.z-l-us {
  text-decoration: underline line-through;
}

/* theme "gruvbox-dark-medium" */
.z-d-code {
  --zd: #ebdbb2;
}
.z-d-hl {
  --zd-bg: #3c383660;
}
.giallo-ln {
  --zd: #665c54;
}
.z-d-1 {
  --zd: #458588;
}
.z-d-2 {
  --zd: #689d6a;
}
.z-d-3 {
  --zd: #83a598;
}
.z-d-4 {
  --zd: #8ec07c;
}
.z-d-5 {
  --zd: #928374;
}
.z-d-6 {
  --zd: #98971a;
}
.z-d-7 {
  --zd: #a89984;
}
.z-d-8 {
  --zd: #b16286;
}
.z-d-9 {
  --zd: #b8bb26;
}
.z-d-10 {
  --zd: #bdae93;
}
.z-d-11 {
  --zd: #cc241d;
}
.z-d-12 {
  --zd: #d3869b;
}
.z-d-13 {
  --zd: #d5c4a1;
}
.z-d-14 {
  --zd: #d65d0e;
}
.z-d-15 {
  --zd: #d79921;
}
.z-d-16 {
  --zd: #fabd2f;
}
.z-d-17 {
  --zd: #fb4934;
}
.z-d-18 {
  --zd: #fe8019;
}
.z-d-b {
  font-weight: bold;
}
.z-d-i {
  font-style: italic;
}
.z-d-u {
  text-decoration: underline;
}
.z-d-s {
  text-decoration: line-through;
}
.z-d-us {
  text-decoration: underline line-through;
}
/* === end syntax theme values === */

/* Build-time D2 output is linked as an ordinary image. The SVG owns its
   adaptive internal palette; this panel ties its outer edge to the site. */
article img[src$=".generated.svg"] {
  height: auto;
  margin-inline: auto;
  padding: 0.5rem;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background-color: var(--code-bg);
}

.article-figure {
  margin: 2rem 0;
}

.article-figure img {
  height: auto;
  margin-inline: auto;
}

.article-figure figcaption {
  margin-top: 0.75rem;
  color: var(--muted);
  font-size: 0.875rem;
  line-height: 1.5;
  text-align: center;
}

/* Click-to-expand for article images (wired up by image-zoom.js). The
   wrapper button is invisible chrome; the image inside keeps its look. */
.image-zoom-trigger {
  display: block;
  width: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}

/* The dialog lives under <body>, outside the article-scoped panel rule
   above, so it supplies the panel frame itself. Its native fit-content
   width sizes it to the image, capped to the viewport. */
.image-zoom {
  max-width: min(95vw, 90rem);
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 4px;
  background-color: var(--code-bg);
}

.image-zoom img {
  margin-inline: auto;
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: calc(95vh - 4rem);
}

/* Rendered D2 SVGs have a viewBox but no intrinsic size, so fit-content
   would collapse them; force the dialog wide and stretch to fill. */
.image-zoom-wide {
  width: min(95vw, 90rem);
}

.image-zoom-wide img {
  width: 100%;
}

.image-zoom::backdrop {
  background: rgb(0 0 0 / 60%);
}

/* just the copy affordance, pinned to the block's top-right; the WHOLE
   toolbar fades (not just the button) — its opaque backing would
   otherwise sit over the code's top-right corner while invisible */
.code-toolbar {
  position: absolute;
  top: 0;
  right: 0;
  display: flex;
  gap: 1em;
  margin: 0;
  padding: 0.3em 0.5em;
  color: var(--muted);
  background: inherit; /* matches the code panel; covers code scrolling beneath */
  font-size: 0.75rem;
  line-height: 1.2;
  opacity: 0;
  pointer-events: none; /* don't block selecting the text underneath */
  transition: opacity 120ms;
}

pre.giallo:hover .code-toolbar,
.code-toolbar:focus-within {
  opacity: 1;
  pointer-events: auto;
}

@media (hover: none) {
  .code-toolbar {
    opacity: 1;
    pointer-events: auto;
  }
}

.code-copy {
  appearance: none;
  border: 0;
  padding: 0;
  margin: 0;
  color: inherit;
  background: transparent;
  font: inherit;
  cursor: pointer;
}

.code-copy:hover,
.code-copy:focus-visible {
  color: var(--accent);
}

.copy-fallback-buffer {
  position: fixed;
  left: -9999px;
}

/* Don't show, it's for accessibility */
.skip-to-content {
  position: absolute;
  top: -40px;
  left: -100%;
  background: var(--fg);
  color: var(--bg);
  padding: 8px;
  text-decoration: none;
  z-index: 100;
}

.skip-to-content:focus {
  top: 0;
  left: 0;
}

h1.title,
main > h1 {
  margin-block-start: clamp(1rem, 4vw, 1rem);
  margin-block-end: 0.15em;
  /* font-weight: 1000; */
  /* fluid: full 2.25em-equivalent on desktop without swallowing a phone screen */
  /* font-size: clamp(1.5rem, 1rem + 3vw, 2.4rem); */
  line-height: 1.1;
  text-align: left;
}

/* the deck: clearly subordinate to the title — smaller and muted, so the
   hierarchy reads title → deck → metadata */
h2.subtitle {
  padding: 0;
  margin: 0 0 0.25em;
  line-height: 1.3;
  font-size: 1rem;
  font-weight: 400;
  font-style: normal;
  color: var(--muted);
  text-align: center;
}

article {
  margin-top: 0.5rem;
}

/* owl to set broad spacing */
article > * + * {
  margin-block-start: 1em;
}

.page-header + :not(:is(h2, h3, h4, h5, h6)) {
  margin-block-start: 0.5rem;
}

.page-header > h1 {
  text-align: center;
}

/* section headings: tight leading, roomy above, snug below */
article :is(h2, h3, h4, h5, h6) {
  line-height: 1.1;
  border-bottom: 1px solid transparent;
  /* Lift the full-width rule through the descenders on the last line.
     A crisp page-coloured halo leaves a little space around the ink. */
  background:
    linear-gradient(var(--rule), var(--rule)) left bottom 0.08em / 100% 1px
    no-repeat;
  text-shadow:
    -0.05em -0.05em var(--bg),
    0.05em -0.05em var(--bg),
    -0.05em 0.05em var(--bg),
    0.05em 0.05em var(--bg);
}

article h2.subtitle {
  border-bottom: none;
  background: none;
  text-shadow: none;
  font-style: italic;
}

article h2 {
  font-size: 1.35em;
}

article h3 {
  font-size: 1.1em;
}

article > :is(h2, h3, h4, h5, h6) {
  margin-block-start: 1.6em;
}

article > :is(h2, h3, h4, h5, h6) + * {
  margin-block-start: 0.5em;
}

/* heading anchor links (templates/anchor-link.html): invisible until the
   heading is hovered */
a.anchor {
  margin-inline-start: 0.4em;
  color: var(--muted);
  font-size: 0.8em;
  font-weight: 400;
  text-decoration-line: none;
  opacity: 0;
  transition: opacity 120ms;
}

:is(h1, h2, h3, h4, h5, h6):hover > a.anchor,
a.anchor:focus-visible {
  opacity: 1;
}

a.anchor:hover {
  color: var(--accent);
  text-decoration-line: none;
}

@media (hover: none) {
  a.anchor {
    opacity: 1;
  }
}

/* but a bit tighter in prose */
article > p + p {
  margin-block-start: 0.75em;
}

article > p + :is(ul, ol) {
  margin-block-start: 0.4em;
}

/* === Table of contents === */
/* a quiet, compact block — muted links, no markers, hover shows accent.
   Narrow screens: a collapsed <details> zippie in the flow.
   Wide screens (same breakpoint as sidenotes): an always-visible <nav>
   floated out into the right-hand gutter. */
.toc {
  font-size: 0.8rem;
  line-height: 1.55;
}

.toc-wide {
  display: none;
}

.toc-contents {
  position: relative;
  border-inline-start: 1px solid var(--rule);
  padding-inline-start: 0.8rem;
}

.toc-label,
.toc-narrow > summary {
  font-weight: 600;
  color: var(--fg);
}

.toc-label {
  margin-block-end: 0.35rem;
}

.toc-top {
  margin-block-start: 0.5rem;
  font-size: 0.75rem;
}

.toc-wide > .toc-top {
  margin-inline-start: calc(0.8rem + 1px);
}

.toc-narrow > summary {
  cursor: pointer;
  text-decoration-line: none;
}

.toc-narrow > summary:hover {
  color: var(--accent);
}

.toc ul {
  margin: 0.4em 0 0;
  list-style: none;
  padding: 0;
}

.toc ul ul {
  padding-inline-start: 1em;
  margin-block-start: 0;
}

.toc-contents > ul {
  margin-block-start: 0;
}

.toc a {
  /* Keep full click targets even with the smaller type. */
  display: block;
  min-block-size: 24px;
  padding-block: 2px;
  color: var(--muted);
  text-decoration-line: none;
}

.toc a:hover,
.toc a:focus-visible,
.toc-wide li a[aria-current="location"] {
  color: var(--accent);
  text-decoration-line: none;
}

.toc-wide li a[aria-current="location"]::before {
  content: "";
  /* The contents block positions the marker on the same rule at every depth;
     its automatic block position follows the active link's first line. */
  position: absolute;
  inset-inline-start: -2px;
  margin-block-start: 0.25em;
  inline-size: 3px;
  block-size: 1em;
  border-radius: 1px;
  background: var(--accent);
}

@media (min-width: 1425px) {
  .toc-narrow {
    display: none;
  }

  .toc-wide {
    display: block;
    --toc-width: min(16rem, calc((100vw - 800px) / 2 - 2rem));

    float: right;
    clear: right;
    width: var(--toc-width);
    margin-right: calc(-1 * (var(--toc-width) + 1.5rem));
    margin-block-start: 0.25rem;
    /* pin to the top while scrolling through the article; sticky is
       bounded by the containing block, so it lets go at the article end */
    position: sticky;
    top: 1rem;
  }
}

footer {
  margin-top: 1rem;
  padding-block: 0.5rem 0.75rem;
  color: var(--muted);
  font-size: 0.8rem;
  text-align: center;
}

footer > hr {
  margin: 0 0 0.25em;
  border: 0;
  border-block-start: 1px solid var(--rule);
}

/* === List Formatting === */

ul {
  list-style-type: "→ ";
  padding-inline-start: 1.25em;
}

ul > li::marker {
  font-weight: 900;
  /* biome-ignore lint/suspicious/noDuplicateFontNames: Deliberate workaround for browser bug - https://github.com/necolas/normalize.css/issues/519 */
  font-family: "JetBrains Mono", monospace, monospace;
}

/* ===  Error Pages === */
.error-content {
  text-align: center;
  margin: var(--space-lg) 0;
}

.error-content p {
  font-size: 1.1em;
  margin-bottom: var(--space-md);
}

.error-art {
  border: 0;
}

.error-message {
  text-align: center;
}

/* === Links === */
/* Ordinary prose links stay ink-coloured and use the accent as an underline.
   Quiet zones such as the header and metadata opt out of the underline. */
a {
  color: var(--fg);
  text-decoration-line: underline;
  text-decoration-color: var(--accent);
  text-underline-offset: 0.15em;
}
a:hover {
  color: var(--accent);
}

::selection {
  background: var(--accent);
  color: var(--bg);
}

/* dev-only: drafts never render in `zola build` */
article.draft::before {
  content: "DRAFT";
  position: fixed;
  top: 1rem;
  left: 1rem;
  z-index: 10;
  padding: 0.15em 0.6em;
  /* biome-ignore lint/suspicious/noDuplicateFontNames: Deliberate workaround for browser bug - https://github.com/necolas/normalize.css/issues/519 */
  font-family: "JetBrains Mono", monospace, monospace;
  font-size: 0.8rem;
  color: var(--fg);
  background-color: pink;
  border: 1px solid red;
  pointer-events: none; /* never intercepts clicks */
}

@media (max-width: 60rem) {
  /* no left gutter to live in */
  article.draft::before {
    top: auto;
    bottom: 1rem;
  }
}

/* === Blocks === */

blockquote {
  margin-block: 1.4em;
  margin-inline: clamp(0rem, 3vw, 1.5rem);
  padding-inline-start: 1rem;
  border-inline-start: 2px solid var(--rule);
  background-color: transparent;
  color: var(--fg);
  font-style: italic;
  font-weight: 400;
  line-height: 1.65;
}

blockquote > p {
  text-wrap: pretty;
}

blockquote > p + p {
  margin-block-start: 0.75em;
}

/* === Post lists === */

ul.posts {
  padding-left: 0;
}

ul.posts > li {
  display: flex;
  align-items: baseline; /* title + date share a baseline */
  gap: 0.9em;
  text-align: left;
}

@media (max-width: 480px) {
  ul.posts > li + li {
    margin-block-start: 0.3rem;
  }
}

/* Quiet underlines make titles recognisable as links before interaction. */
ul.posts > li > a {
  flex: 0 1 auto;
  min-width: 0;
  color: inherit;
  text-decoration-line: underline;
  text-decoration-color: color-mix(in srgb, var(--accent) 50%, transparent);
  text-decoration-thickness: 1px;
}

ul.posts > li > a:visited {
  text-decoration-color: color-mix(in srgb, var(--muted) 50%, transparent);
}

ul.posts > li > a:hover,
ul.posts > li > a:focus-visible {
  color: var(--accent);
  text-decoration-color: var(--accent);
}

ul.posts > li > time {
  flex: 0 0 5.25rem;
  color: var(--muted);
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  font-size: 0.8em;
}

/* Year headings leave only day and month in the date column. */
h2.year + ul.posts > li > time {
  flex-basis: 3.25rem;
}

/* Draft rows include metadata without requiring a publication date. */
ul.draft-list {
  margin-block-start: 1.25rem;
  padding: 0;
  list-style: none;
}

ul.draft-list > li {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: baseline;
  gap: 0.35rem 1.5rem;
  padding-block: 0.85rem;
  border-block-end: 1px solid var(--rule);
}

.draft-title {
  min-width: 0;
  font-size: 1.05rem;
  font-weight: 600;
  overflow-wrap: anywhere;
  text-decoration-line: none;
}

.draft-meta,
ul.draft-tags {
  color: var(--muted);
  /* biome-ignore lint/suspicious/noDuplicateFontNames: Deliberate workaround for browser bug - https://github.com/necolas/normalize.css/issues/519 */
  font-family: "JetBrains Mono", monospace, monospace;
  font-size: 0.75rem;
}

.draft-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 0.5rem;
  font-variant-numeric: tabular-nums;
}

.draft-date,
.draft-word-count {
  white-space: nowrap;
}

ul.draft-tags {
  display: flex;
  flex-wrap: wrap;
  grid-column: 1 / -1;
  gap: 0.2rem 0.8rem;
  padding: 0;
  list-style: none;
}

.draft-tags a {
  color: inherit;
  overflow-wrap: anywhere;
  text-decoration-line: none;
}

.draft-title:hover,
.draft-title:focus-visible,
.draft-tags a:hover,
.draft-tags a:focus-visible {
  color: var(--accent);
  text-decoration-line: underline;
}

@media (max-width: 480px) {
  ul.draft-list > li {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 0.3rem;
  }

  .draft-meta {
    justify-content: flex-start;
  }
}

/* The taxonomy has many single-use tags. Keep recurring subjects easy to
   scan, while leaving the complete alphabetical index one disclosure away. */
.tag-index-label,
.one-off-tags > summary {
  color: var(--muted);
  /* biome-ignore lint/suspicious/noDuplicateFontNames: Deliberate workaround for browser bug - https://github.com/necolas/normalize.css/issues/519 */
  font-family: "JetBrains Mono", monospace, monospace;
  font-size: 0.8rem;
}

.tag-index-label {
  margin-block: 1.25rem 0.5rem;
}

ul.tag-index {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
  gap: 0.25rem 1.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

ul.tag-index > li {
  display: flex;
  min-width: 0;
  align-items: baseline;
  gap: 0.35em;
}

ul.tag-index a {
  min-width: 0;
  color: inherit;
  overflow-wrap: anywhere;
  text-decoration-line: none;
}

ul.tag-index a:hover,
ul.tag-index a:focus-visible {
  color: var(--accent);
  text-decoration-line: none;
}

ul.tag-index a > span {
  color: var(--accent);
}

.tag-count {
  flex: 0 0 auto;
  color: var(--muted);
  /* biome-ignore lint/suspicious/noDuplicateFontNames: Deliberate workaround for browser bug - https://github.com/necolas/normalize.css/issues/519 */
  font-family: "JetBrains Mono", monospace, monospace;
  font-size: 0.7rem;
  font-variant-numeric: tabular-nums;
}

.one-off-tags {
  margin-block-start: 1.75rem;
  padding-block-start: 0.75rem;
}

.one-off-tags > summary {
  width: fit-content;
  cursor: pointer;
}

.one-off-tags[open] > summary {
  margin-block-end: 0.75rem;
}

@media (max-width: 480px) {
  ul.tag-index {
    grid-template-columns: minmax(0, 1fr);
    row-gap: 0.2rem;
  }
}

/* plain muted marker for the odd non-til entry in mixed lists — no pill */
span.post-type {
  flex: 0 0 auto;
  color: var(--muted);
  font-size: 0.7rem;
  letter-spacing: 0.03em;
}

/* === Asides === */

aside.note {
  --border: var(--rule);
  --background: var(--code-bg);
  --label-fg: var(--muted);
  --note-fg: var(--fg);
  --note-border-width: 2px;

  display: block;
  margin-block: 1em;
  border: 0;
  border-inline-start: var(--note-border-width) solid var(--border);
  border-radius: 2px;
  padding: 0.75rem 1rem;
  background-color: var(--background);
  color: var(--note-fg);
}

aside.note-info {
  --border: var(--info);
  --label-fg: var(--info);
  --note-border-width: 3px;
}

aside.note-warning {
  --border: orange;
  --background: #ffd580;
  --label-fg: #704300;
  --note-fg: #282726;
}

aside.note-alert {
  --border: red;
  --background: #ffd5d5;
  --label-fg: #8d1717;
  --note-fg: #282726;
}

aside.note::before {
  content: attr(data-title);
  display: block;
  margin-block-end: 0.4rem;
  color: var(--label-fg);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  line-height: 1.2;
  text-transform: uppercase;
}

aside.note p {
  margin: 0;
  padding: 0;
}

aside.note p + p {
  margin-block-start: 0.5em;
}

/* === home page tweaks === */

.home-content {
  margin-top: 1rem;
}

.home-content > ul + p {
  margin-block-start: var(--space-lg);
}

.home-recent {
  margin-top: 0.5rem;
}

/* Compact headings for post lists. */
.home-recent > h2,
h2.year {
  margin-block-start: 1.5rem;
  margin-block-end: 0.25rem;
  font-size: 1rem;
  font-weight: 700;
}

.home-recent > h2 {
  font-size: 0.9rem;
  font-weight: 600;
}

.home-post-links {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem 1.5rem;
  margin-top: 0.75rem;
  font-size: 0.9rem;
}

/* =============================================================================
   FOOTNOTES
   ============================================================================= */
.footnotes {
  margin-block-start: 2em;
  font-size: 0.9em;
}

.footnotes::before {
  display: block;
  inline-size: 3rem;
  margin-block-end: 0.75em;
  border-block-start: 1px solid var(--rule);
  content: "";
}

.footnotes-list {
  padding-inline-start: 1.75em;
  list-style-type: decimal;
}

.footnotes-list > li + li {
  margin-block-start: 0.5em;
}

.footnotes-list p {
  margin: 0;
  text-align: left;
}

.footnotes-list a[href*="#fr-"] {
  margin-inline-start: 0.25em;
  font-size: 0.85em;
  text-decoration-line: none;
}

sup.footnote-reference {
  font-size: 0.8em;
  vertical-align: super;
}

/* =============================================================================
   SIDENOTES
   ============================================================================= */
.sidenote {
  display: none;
}

@media (min-width: 1425px) {
  .sidenote {
    --sidenote-width: min(16rem, calc((100vw - 800px) / 2 - 2rem));

    display: block;
    float: right;
    clear: right;
    width: var(--sidenote-width);
    margin-top: 0.25rem;
    margin-right: calc(-1 * (var(--sidenote-width) + 1.5rem));
    margin-bottom: 0.75rem;
    color: var(--muted);
    font-size: 0.85rem;
    line-height: 1.35;
    text-align: left;
  }

  .sidenote-number {
    margin-inline-end: 0.35em;
    color: var(--accent);
    font-size: 0.8em;
    vertical-align: baseline;
  }

  .sidenote a[href*="#fr-"] {
    display: none;
  }

  .sidenote:target {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
  }
}
