/* ----------------------------------------------------------------- fonts */

/* Maple Mono, latin subset, self-hosted so every reader sees the same thing.
   CJK is deliberately not included — the posts contain a little Japanese and
   Chinese, which falls back to the system font rather than pulling a
   multi-megabyte CJK face for a handful of glyphs. */

@font-face {
  font-family: "Maple Mono";
  src: url("/assets/fonts/maple-400.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Maple Mono";
  src: url("/assets/fonts/maple-700.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Maple Mono";
  src: url("/assets/fonts/maple-400i.woff2") format("woff2");
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Maple Mono";
  src: url("/assets/fonts/maple-700i.woff2") format("woff2");
  font-weight: 700;
  font-style: italic;
  font-display: swap;
}

/* ---------------------------------------------------------------- tokens */

:root {
  /* Light: paper-white with a darkened periwinkle. The accent is used for
     link text, so it is the dark sibling of the dark-mode colour rather than
     the colour itself, which sits at 1.9:1 on white. */
  --bg: #ffffff;
  --fg: #000000;
  --dim: #666666;
  --rule: #dddddd;
  --code-bg: #f7f7f7;
  --accent: #4a52c8;   /* directories, links, the host in the prompt */
  --file: #cc0000;     /* regular files, the way ls colours executables red-ish */
  --path: #0e6f7d;     /* the cwd in the prompt — teal, to stay distinct */
  --green: #008000;
  --magenta: #aa00aa;
  --sel: rgba(74, 82, 200, 0.22);

  --mono: "Maple Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --measure: 90ch;
  --hover: 0.3s;
  --leading: 1.55;
}

/* Dark is the actual terminal. Guarded so an explicit light choice wins. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --bg: #0c0c0c;
    --fg: #d4d4d4;
    --dim: #7a7a7a;
    --rule: #2a2a2a;
    --code-bg: #161616;
    --accent: #b1b9f9;
    --file: #e05561;
    --path: #56b6c2;
    --green: #4caf50;
    --magenta: #c678dd;
    --sel: rgba(177, 185, 249, 0.3);
  }
}

:root[data-theme="dark"] {
  --bg: #0c0c0c;
  --fg: #d4d4d4;
  --dim: #7a7a7a;
  --rule: #2a2a2a;
  --code-bg: #161616;
  --accent: #b1b9f9;
  --file: #e05561;
  --path: #56b6c2;
  --green: #4caf50;
  --magenta: #c678dd;
  --sel: rgba(177, 185, 249, 0.3);
}

/* ------------------------------------------------------------------ base */

* { box-sizing: border-box; text-rendering: geometricPrecision; }

html { -webkit-text-size-adjust: 100%; }

/* Maple's contextual alternates are left on, which means `x` between digits
   becomes a multiplication sign: 0x1B3F renders as 0×1B3F. Known and
   accepted — context makes an address obvious, and the arrow ligatures are
   worth more. The rule sits inside `feature calt` with no stylistic-set gate,
   so this is all-or-nothing; `font-feature-settings: "calt" 0` here would
   reverse it. */
body {
  margin: 0 auto;
  /* Full width with a gutter on a phone, clamped on a desktop. */
  padding: 2rem 1rem 4rem;
  max-width: var(--measure);
  background: var(--bg);
  color: var(--fg);
  font-family: var(--mono);
  font-size: 1rem;
  line-height: var(--leading);
}

/* The UA stylesheet sets `font-family: monospace` on these, which overrides
   inheritance from `body`. Without this, every code span and code block
   renders in the browser's default monospace rather than ours — and loses the
   ligatures with it, since the fallback has none. */
code,
pre,
kbd,
samp {
  font-family: var(--mono);
}

::selection { background: var(--sel); }

/* Each kind of link sets `--link` rather than `color` directly, so the single
   hover rule below can use it. Setting `color` per class meant `a.file`, being
   declared later at equal specificity, won on hover and left red text sitting
   on an accent background. */
a {
  --link: var(--accent);
  color: var(--link);
  text-decoration: none;
  transition: background-color var(--hover) ease, color var(--hover) ease;
}
a:hover {
  background: var(--link);
  color: var(--bg);
}

hr { border: none; border-top: 1px solid var(--rule); margin: 2rem 0; }

/* -------------------------------------------------------------- terminal */

.term { color: var(--fg); }
.term .line { white-space: nowrap; overflow-x: auto; scrollbar-width: none; }
.term .line::-webkit-scrollbar { display: none; }

.prompt .host { font-weight: 700; }
.prompt .sep { color: var(--dim); }
.prompt .path { color: var(--path); }
.prompt .sigil { color: var(--dim); padding-left: 0.5ch; }
.prompt .sigil::after { content: ""; }
.cmd { font-weight: 700; }

/* Packed like real `ls` output rather than stretched across the width. */
.ls-grid {
  display: flex;
  flex-wrap: wrap;
  gap: 0 3ch;
  margin: 0.25rem 0 1rem;
}

/* LS_COLORS, roughly: directories accent, files red, executables green. */
a.dir { --link: var(--accent); }
a.file { --link: var(--file); }
a.exe { --link: var(--green); }
a.symlink { --link: var(--magenta); }

a.dir::after { content: "/"; color: var(--dim); transition: color var(--hover) ease; }
a.symlink::after { content: "@"; color: var(--dim); transition: color var(--hover) ease; }
a.dir:hover::after,
a.symlink:hover::after { color: var(--bg); }

.cursor {
  display: inline-block;
  width: 1ch;
  height: 1.05em;
  vertical-align: text-bottom;
  background: var(--fg);
  animation: blink 1.1s step-end infinite;
}
@keyframes blink { 50% { background: transparent; } }
@media (prefers-reduced-motion: reduce) {
  .cursor { animation: none; }
  /* A half-second colour fade is motion too, for anyone who asked for less. */
  a,
  a.dir::after,
  a.symlink::after,
  .pref { transition: none; }
}

header.term { border-bottom: 1px solid var(--rule); padding-bottom: 0.75rem; margin-bottom: 2rem; }
footer.term { border-top: 1px solid var(--rule); padding-top: 0.75rem; margin-top: 3rem; }

.prefs { float: right; display: flex; gap: 2ch; }
.pref {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--dim);
  cursor: pointer;
  white-space: nowrap;
}
.pref { transition: background-color var(--hover) ease, color var(--hover) ease; }
.pref:hover { background: var(--accent); color: var(--bg); }
.pref:hover .pref-value { color: inherit; }
.pref-value { color: var(--accent); }

/* -------------------------------------------------------------- listings */

.ls-la { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.md .ls-la td, .md .ls-la th { border: none; padding-left: 0; }
.ls-la caption { text-align: left; color: var(--dim); padding-bottom: 0.5rem; }
.ls-la td { padding: 0.15rem 1ch 0.15rem 0; vertical-align: top; }
/* The mode column stays uniformly dim, including for directories and locked
   posts. The permission string already says which is which — `drwxr-xr-x`
   against `-rw-------` — so colouring it only repeats the glyphs, and it
   competes with the titles for the eye. Real `ls --color` tints the name, not
   the mode. */
.ls-la .mode, .ls-la .size, .ls-la .date { color: var(--dim); white-space: nowrap; }
.ls-la .size { text-align: right; }
.ls-la .name { width: 100%; }
.ls-la .blurb { color: var(--dim); white-space: normal; padding-bottom: 0.4rem; }
.badge { color: var(--dim); }
.tag { --link: var(--magenta); }

/* ----------------------------------------------------------------- prose */

.content { margin-top: 1rem; }

.md h1, .md h2, .md h3, .md h4, .md h5, .md h6 {
  line-height: 1.25;
  margin: 2.2rem 0 0.8rem;
  font-weight: 700;
}
.md h1 { font-size: 1.5rem; }
.md h2 { font-size: 1.25rem; }
.md h3 { font-size: 1.1rem; }
.md h4, .md h5, .md h6 { font-size: 1rem; }

.md .anchor { --link: var(--dim); padding-right: 0.5ch; }
.md .anchor::before { content: "#"; }
.md .anchor:hover { background: none; color: var(--accent); }

.md p { margin: 0 0 1.1rem; }
.md ul, .md ol { padding-left: 3ch; margin: 0 0 1.1rem; }
.md li { margin-bottom: 0.2rem; }

.md blockquote {
  margin: 0 0 1.1rem;
  padding-left: 2ch;
  border-left: 1px solid var(--rule);
  color: var(--dim);
}

.md table { border-collapse: collapse; margin-bottom: 1.1rem; }
.md th, .md td { border: 1px solid var(--rule); padding: 0.2rem 1ch; text-align: left; }
.md th { font-weight: 700; }

.md img { max-width: 100%; height: auto; }

.post-title { margin-top: 0.5rem; }
.post-meta { color: var(--dim); margin-bottom: 2rem; }
.post-meta .sep { padding: 0 0.5ch; }

/* ------------------------------------------------------------------ code */

.md code {
  background: var(--code-bg);
  padding: 0 0.3ch;
  border-radius: 2px;
}
.md pre {
  background: var(--code-bg);
  border: 1px solid var(--rule);
  padding: 0.8rem 1ch;
  overflow-x: auto;
  margin: 0 0 1.1rem;
  position: relative;
}
.md pre code { background: none; padding: 0; }
.md pre[data-lang]::before {
  content: attr(data-lang);
  position: absolute;
  top: 0;
  right: 0.5ch;
  color: var(--dim);
  font-size: 0.8em;
}

/* syntect emits `hl-`-prefixed scope classes; keep the palette here. */
.hl-comment, .hl-punctuation.hl-definition.hl-comment { color: var(--dim); font-style: italic; }
.hl-string, .hl-constant.hl-character { color: var(--green); }
.hl-constant.hl-numeric, .hl-constant.hl-language { color: var(--magenta); }
.hl-keyword, .hl-storage { color: var(--accent); }
.hl-entity.hl-name.hl-function { color: var(--path); }
.hl-entity.hl-name.hl-type, .hl-support.hl-type { color: var(--file); }
.hl-variable.hl-parameter { color: var(--fg); }
.hl-invalid { color: var(--bg); background: var(--file); }

/* ------------------------------------------------------------------ math */

.math-inline { display: inline-block; }
/* Block math sits in a paragraph of its own, so its margin and the
   paragraph's would otherwise stack. */
.md p:has(> .math-display:only-child) { margin: 0; }

/* Images written on their own source line were `{{< image position="center" >}}`
   shortcodes in Hugo, which rendered block-level and centred. Keeping them
   inline makes the following line of prose run alongside the picture. */
.md p img { display: block; margin: 1.5rem auto; }

.math-display {
  display: block;   /* a span, so this is what makes it a block */
  margin: 1.4rem auto;
  text-align: center;
  overflow-x: auto;
}
.math-raw { color: var(--magenta); }

/* ------------------------------------------------- markdown source marks */

/* Everything here is monospace, so inline code has no typographic contrast of
   its own to fall back on — the backticks and the tint are the only thing
   marking an identifier as an identifier. Bold and italic already look bold
   and italic, so their asterisks would say it twice; they live in the `raw`
   layer instead. */
.md :not(pre) > code::before,
.md :not(pre) > code::after { content: "`"; color: var(--dim); }

/* The left rule and dim colour already say "quote". Marking the opening
   paragraph places the construct; marking every one repeats it. */
.md blockquote > p:first-child::before { content: "> "; color: var(--dim); }

/* ------------------------------------------------------------- footnotes */

/* pulldown-cmark emits a flat run of `.footnote-definition` blocks with no
   wrapper, so the separator hangs off the first one. */
.md .footnote-definition {
  color: var(--dim);
  font-size: 0.92em;
  margin-bottom: 0.4rem;
}
/* `:first-of-type` would match a display-math div instead, so the separator
   goes on every definition and is then suppressed on consecutive ones. */
.md .footnote-definition { margin-top: 3rem; }
.md .footnote-definition::before {
  content: "* * *";
  display: block;
  color: var(--dim);
  margin-bottom: 1rem;
  font-size: 1rem;
}
.md .footnote-definition + .footnote-definition { margin-top: 0; }
.md .footnote-definition + .footnote-definition::before { content: none; }
.md .footnote-definition p { display: inline; }
.md .footnote-definition-label { color: var(--accent); padding-right: 1ch; }
.md .footnote-reference a { --link: var(--accent); }

/* ---------------------------------------------------------------- errors */

pre.error { color: var(--file); background: none; border: none; padding: 0; }

/* --------------------------------------------------------------- narrow */

@media (max-width: 40rem) {
  body { padding: 1rem 1rem 3rem; font-size: 0.95rem; }
  .ls-la .mode, .ls-la .size { display: none; }
}

/* ------------------------------------------------------------ locked posts */

.lock { margin: 2rem 0; }
.lock-teaser { color: var(--dim); margin-bottom: 1.5rem; }
.lock-form { display: flex; flex-wrap: wrap; gap: 1ch; align-items: baseline; }
.lock-label { color: var(--dim); }
.lock-label::after { content: ":"; }
.lock input {
  font: inherit;
  color: var(--fg);
  background: var(--code-bg);
  border: 1px solid var(--rule);
  padding: 0.15rem 1ch;
  min-width: 24ch;
}
.lock input:focus { outline: 1px solid var(--accent); }
.lock button {
  font: inherit;
  color: var(--accent);
  background: none;
  border: 1px solid var(--accent);
  padding: 0.15rem 1.5ch;
  cursor: pointer;
}
.lock button:hover { background: var(--accent); color: var(--bg); }
.lock-status { color: var(--dim); min-height: 1.5em; margin: 0.5rem 0 0; }
.lock-status.error { color: var(--file); }

/* ---------------------------------------------------------------- series */

.series-nav {
  margin-top: 3rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}
.series-nav .badge { display: inline-block; min-width: 5ch; }

/* ----------------------------------------------------------- wiki + search */

.md a.wiki { border-bottom: 1px dotted var(--accent); }
.md .wiki.broken {
  color: var(--file);
  border-bottom: 1px dotted var(--file);
  cursor: help;
}

.backlinks {
  margin-top: 3rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}

.search { display: flex; gap: 1ch; align-items: baseline; margin-bottom: 1rem; }
.search-label { color: var(--dim); }
.search-label::after { content: ":"; }
.search input {
  font: inherit;
  flex: 1;
  color: var(--fg);
  background: var(--code-bg);
  border: 1px solid var(--rule);
  padding: 0.15rem 1ch;
}
.search input:focus { outline: 1px solid var(--accent); }
.search-status { color: var(--dim); }
.search-hit { margin-bottom: 1rem; }

/* A `soon` post is listed but has no page, so its title is not a link. */
.ls-la .pending { color: var(--dim); }

.series-intro { margin: 2rem 0 3rem; }

/* -------------------------------------------------------------- sidenotes */

/* Opt in per post with `sidenotes = true`. The marker is a <label> and the
   note a sibling <span>, with a hidden checkbox between them, so the narrow
   layout can toggle a note open with no JavaScript at all. */
.sidenote-toggle {
  cursor: pointer;
  color: var(--accent);
  vertical-align: super;
  font-size: 0.75em;
  padding: 0 0.2ch;
}
.sidenote-switch { display: none; }
.sidenote-number {
  color: var(--accent);
  vertical-align: super;
  font-size: 0.75em;
  padding-right: 0.6ch;
}

/* Only a post that uses sidenotes gets the asymmetric layout; widening every
   page for a gutter most of them never use would just look lopsided. */
@media (min-width: 78rem) {
  body.has-sidenotes {
    max-width: calc(var(--measure) + 28ch);
    padding-right: 28ch;
  }
}

/* Wide enough for a margin: the note floats out into the right gutter,
   beside the line that referenced it. */
@media (min-width: 78rem) {
  .sidenote {
    float: right;
    clear: right;
    width: 22ch;
    margin-right: -26ch;
    margin-bottom: 1rem;
    font-size: 0.85em;
    line-height: 1.45;
    color: var(--dim);
    text-align: left;
  }
}

/* Too narrow for a gutter: the note stays collapsed until its marker is
   clicked, then appears as an indented block. */
@media (max-width: 77.99rem) {
  .sidenote { display: none; }
  .sidenote-switch:checked + .sidenote {
    display: block;
    margin: 0.6rem 0 0.9rem 2ch;
    padding-left: 1.5ch;
    border-left: 1px solid var(--rule);
    font-size: 0.9em;
    color: var(--dim);
  }
  .sidenote-toggle { border-bottom: 1px dotted var(--accent); }
}

.prompt .user { color: var(--green); }
.prompt .at { color: var(--dim); }
