/* Palette — Catppuccin Mocha (committed site theme).
   Every color on the site derives from these tokens; change them here to
   re-theme the whole site. Mocha is a dark theme, so the site commits to a
   single dark palette (no light-mode media query). */
:root {
  color-scheme: dark;

  --bg: #1e1e2e;         /* base */
  --rail-bg: #181825;    /* mantle */
  --card-bg: #313244;    /* surface0 */
  --ink: #cdd6f4;        /* text */
  --accent: #89b4fa;     /* blue */
  --accent-2: #fab387;   /* peach */
  --accent-3: #a6e3a1;   /* green */

  /* Tag palette. Each tag name hashes to one of these, so a tag keeps the same
     color everywhere it appears and gets its color the first time it is used
     without anyone assigning one. Eight hues from Mocha, chosen to stay
     distinguishable from each other against --bg. Adding a hue here changes the
     assignment of existing tags, since the hash is taken modulo the count. */
  --tag-1: #89b4fa;      /* blue */
  --tag-2: #fab387;      /* peach */
  --tag-3: #a6e3a1;      /* green */
  --tag-4: #cba6f7;      /* mauve */
  --tag-5: #f38ba8;      /* red */
  --tag-6: #94e2d5;      /* teal */
  --tag-7: #f9e2af;      /* yellow */
  --tag-8: #f5c2e7;      /* pink */
  --rule: #45475a;       /* surface1 */
  /* Horizontal rules and section dividers, tinted toward --accent so breaks in
     the page read as part of the palette rather than as neutral grey. */
  --rule-accent: #4a5578; /* surface1 blended toward blue */
  --muted: #a6adc8;      /* subtext0 */
  --focus: #89b4fa;      /* blue */

  /* Diagram surface: a shade darker than --bg so inlined D2 diagrams read as a
     distinct panel. */
  --diagram-bg: #11111b; /* crust */
  /* Dimming layer behind the enlarge-diagram modal. */
  --overlay-bg: rgba(17, 17, 27, 0.76);
}
*, *::before, *::after {
  box-sizing: border-box;
}

html, body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--ink);
  min-height: 100vh;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

h1, h2, h3, h4, h5, h6, p, figure, dl, dd, ol, ul {
  margin: 0;
  padding: 0;
}

img, svg {
  max-width: 100%;
}

html {
  scroll-behavior: smooth;
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
@font-face {
  font-family: "Public Sans";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/fonts/public-sans-latin-wght-normal.woff2") format("woff2-variations");
}

:root {
  --font-body: "Public Sans", ui-sans-serif, "Segoe UI", Arial, system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}

body {
  font-family: var(--font-body);
  font-size: 18px;
  line-height: 1.65;
  font-weight: 400;
}

h1, h2, h3, h4, h5, h6 {
  font-family: var(--font-body);
  line-height: 1.2;
  font-weight: 700;
  color: var(--ink);
  margin-bottom: 0.7em;
}

/* Headings set their own top margin in em, so the space scales with the
   heading's own size: a h2 opens a bigger gap than a h4, which keeps the
   hierarchy readable without hand-tuning each level. Note that a top-level
   block is its own grid row, so these margins are what separates sections —
   there is no row gap to fall back on. */
h2 { margin-top: 2.4em; }
h3 { margin-top: 2em; }
h4, h5, h6 { margin-top: 1.9em; }

/* Headings below the title carry accent color, in two tones, so nesting depth
   reads at a glance rather than from font size alone. The page title (h1) and
   all body text stay --ink. */
h2 { color: var(--accent); }
h3, h4, h5, h6 { color: var(--accent-2); }

h1 { font-size: 2.15rem; font-weight: 800; letter-spacing: -0.015em; }
h2 { font-size: 1.55rem; }
h3 { font-size: 1.25rem; }
h4 { font-size: 1.05rem; }
h5, h6 { font-size: 0.95rem; }

p {
  margin: 0 0 1.1em;
}

/* base.css zeroes padding on ul/ol as part of the reset, which also removes the
   indent the marker lives in — without this, bullets hang outside the text
   column, to the left of the headings. Restore it for prose lists. Components
   that lay out their own lists (the TOC, tag rows, the essay index) set their
   own padding and are unaffected. */
.content-cell ul,
.content-cell ol {
  margin: 0 0 1.1em;
  padding-left: 1.4em;
}

.content-cell li {
  margin-bottom: 0.35em;
}

.content-cell li:last-child {
  margin-bottom: 0;
}

/* A nested list belongs to its parent item, not to the block after it. */
.content-cell li > ul,
.content-cell li > ol {
  margin: 0.35em 0 0;
}

.content-cell li::marker {
  color: var(--muted);
}

.content-cell > *:first-child {
  margin-top: 0;
}

a {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 0.14em;
  text-decoration-thickness: 0.06em;
}

a:hover {
  text-decoration-thickness: 0.12em;
}

a:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 2px;
}

strong { font-weight: 700; }
em { font-style: italic; }

blockquote {
  margin: 1.4em 0;
  padding: 0.2em 0 0.2em 1.1em;
  border-left: 3px solid var(--rule);
  color: var(--muted);
  font-style: italic;
}

hr {
  border: 0;
  border-top: 1px solid var(--rule-accent);
  margin: 2.4em 0;
}
.page {
  max-width: 1040px;
  margin: 0 auto;
  padding: 2rem 1.75rem 4rem;
}

.article-grid {
  display: grid;
  grid-template-columns: minmax(0,1fr) 240px;
  column-gap: 1.75rem;
  align-items: start;
}
.content-cell { max-width: 620px; }
@media (max-width: 620px) {
  .article-grid { grid-template-columns: 1fr; }
}

/* The right column: a quiet rail holding margin items. */
.margin-cell {
  font-size: 0.85rem;
  line-height: 1.5;
  color: var(--muted);
  padding-left: 0.9rem;
  border-left: 1px solid var(--rule);
}
.margin-cell:empty {
  border-left: 0;
}

/* When collapsed to one column, margin items stack under their content and
   lose the rail treatment so they read inline. */
@media (max-width: 620px) {
  .margin-cell {
    padding-left: 0;
    border-left: 0;
    margin: 0.4rem 0 1.4rem;
  }
}

/* Article header: title and subtitle on the left, the .post-meta box in the
   right-hand rail, level with them.

   The columns are declared identically to .article-grid below so the box lines
   up exactly over the margin rail that the article's marginalia use. Keeping
   the box here rather than inside .article-grid matters: every top-level
   markdown block is its own grid row, and with `align-items: start` a row is as
   tall as its tallest cell, so a box in the first row's margin cell stretched
   that row and opened a gap between a leading heading and the text below it.

   `align-items: start` here likewise keeps the box at its natural height rather
   than stretching it to match the title block. */
.article-header {
  display: grid;
  grid-template-columns: minmax(0,1fr) 240px;
  column-gap: 1.75rem;
  align-items: start;
  margin-bottom: 2rem;
}
.article-heading {
  max-width: 620px;
}
.article-header .subtitle {
  color: var(--muted);
  font-size: 1.2rem;
  line-height: 1.4;
  margin-top: 0.4rem;
}

/* Below the breakpoint the article grid collapses to one column; the header
   follows so the box stacks under the title instead of squeezing beside it. */
@media (max-width: 620px) {
  .article-header {
    grid-template-columns: 1fr;
    row-gap: 1.25rem;
  }
}

/* "Related on this site" block. */
.related {
  max-width: 620px;
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule-accent);
}
.related h2 {
  font-size: 1.1rem;
  margin-top: 0;
}
.related .related-list {
  display: grid;
  gap: 0.75rem;
  list-style: none;
}

/* Homepage "about me" blurb (homepage only). */
.home-intro {
  max-width: 620px;
  margin: 0 0 2.5rem;
  padding-bottom: 1.75rem;
  border-bottom: 1px solid var(--rule-accent);
}
.home-intro p {
  margin: 0;
  font-size: 1.12rem;
  line-height: 1.6;
  color: var(--ink);
}

/* ---- Homepage / tag-page essay list ---- */
.essay-list {
  list-style: none;
  max-width: 620px;
  display: grid;
  gap: 1.4rem;
}
.essay-item {
  padding-bottom: 1.4rem;
  border-bottom: 1px solid var(--rule-accent);
}
.essay-item:last-child {
  border-bottom: 0;
}
.essay-link {
  text-decoration: none;
  display: block;
}
.essay-link:hover .essay-title {
  color: var(--accent);
  text-decoration: underline;
}
.essay-title {
  display: block;
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--ink);
}
.essay-subtitle {
  display: block;
  color: var(--muted);
  font-size: 0.95rem;
  margin-top: 0.15rem;
}
.essay-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  margin-top: 0.5rem;
  font-size: 0.8rem;
  color: var(--muted);
}
.essay-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
/* .tag pills are styled in components/tag.css, shared with the margin card. */

/* ---- Timeline (used inside content) ---- */
.timeline {
  list-style: none;
  margin: 1.6em 0;
  padding-left: 1.25rem;
  border-left: 2px solid var(--rule);
}
.timeline li {
  position: relative;
  padding: 0 0 1.3em 0.75rem;
}
.timeline li::before {
  content: "";
  position: absolute;
  left: -1.7rem;
  top: 0.45em;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
}
.timeline .tl-date {
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--muted);
  font-weight: 600;
}
.timeline .tl-title {
  font-weight: 700;
  color: var(--ink);
}
.timeline .tl-desc {
  margin: 0.2em 0 0;
  color: var(--ink);
}

/* ---- Dialogue (used inside content) ---- */
.dialogue {
  margin: 1.6em 0;
  display: grid;
  gap: 0.9rem;
}
.dialogue .line {
  padding: 0.7rem 0.9rem;
  border-radius: 10px;
  background: var(--rail-bg);
  border: 1px solid var(--rule);
}
.dialogue .line .speaker {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  margin-bottom: 0.25rem;
}
.dialogue .line p {
  margin: 0;
  color: var(--ink);
}
.dialogue .speaker-a {
  border-left: 3px solid var(--accent);
}
.dialogue .speaker-a .speaker {
  color: var(--accent);
}
.dialogue .speaker-b {
  border-left: 3px solid var(--accent-3);
  background: var(--card-bg);
}
.dialogue .speaker-b .speaker {
  color: var(--accent-3);
}
.site-header {
  max-width: 1040px;
  margin: 0 auto;
  padding: 1.25rem 1.75rem;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  border-bottom: 1px solid var(--rule);
}
.site-header .brand {
  font-weight: 800;
  font-size: 1.05rem;
  color: var(--ink);
  text-decoration: none;
  letter-spacing: -0.01em;
}
.site-header .brand:hover {
  color: var(--accent);
}
.site-nav {
  display: flex;
  gap: 1.1rem;
  font-size: 0.92rem;
}
.site-nav a {
  color: var(--muted);
  text-decoration: none;
}
.site-nav a:hover {
  color: var(--accent);
  text-decoration: underline;
}

.site-footer {
  max-width: 1040px;
  margin: 3rem auto 0;
  padding: 1.5rem 1.75rem 2.5rem;
  border-top: 1px solid var(--rule);
  color: var(--muted);
  font-size: 0.85rem;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.site-footer a {
  color: var(--muted);
}
.site-footer a:hover {
  color: var(--accent);
}
/* Post metadata box: breadcrumb, author, date, reading time and tags.

   It is placed by the page template in .article-header, which is a two-column
   grid whose columns match .article-grid — so the box sits in the right-hand
   rail, level with the title, heading the marginalia column that the article's
   margin notes go on to use.

   It deliberately lives OUTSIDE .article-grid. Every top-level markdown block
   becomes its own row of that grid, and because the grid uses
   `align-items: start`, a row is as tall as its tallest cell. A box placed in
   the first row's margin cell therefore stretched that row to its own height,
   and a post opening on a heading showed a gap before its first paragraph.

   Colors come entirely from tokens.css so the palette stays swappable. */

.post-meta {
  background: var(--card-bg);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: 10px;
  /* Extra padding on the right so the text does not crowd the box's edge. */
  padding: 0.9rem 1.25rem 0.95rem 0.95rem;
  color: var(--ink);
}

/* ---- Breadcrumb -------------------------------------------------------- */

.post-meta .crumb {
  font-size: 0.76rem;
  line-height: 1.5;
  color: var(--muted);
}

.post-meta .crumb a {
  color: var(--muted);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.post-meta .crumb a:hover,
.post-meta .crumb a:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
  outline: none;
}

.post-meta .crumb a:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 2px;
}

.post-meta .crumb .sep {
  color: var(--rule);
  margin: 0 0.15rem;
}

/* ---- Author / date / reading time -------------------------------------- */

/* In the 240px rail these stack; the flex wrap handles both that and the wide
   single-column layout below the breakpoint. */
.post-meta .facts {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.1rem 0.5rem;
  margin-top: 0.7rem;
  padding-top: 0.65rem;
  border-top: 1px solid var(--rule);
  font-size: 0.78rem;
  color: var(--muted);
}

.post-meta .facts .author {
  color: var(--ink);
  font-weight: 500;
  /* The author owns the first line; date and reading time sit together under
     it, which reads better in a narrow rail than three wrapped fragments. */
  flex-basis: 100%;
}

.post-meta .facts .fact + .fact:not(.author)::before {
  content: "·";
  color: var(--rule);
  margin-right: 0.5rem;
}

/* The author is on its own line, so the fact after it needs no separator. */
.post-meta .facts .author + .fact::before {
  content: none;
  margin-right: 0;
}

/* ---- Tags -------------------------------------------------------------- */

.post-meta .tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  margin-top: 0.7rem;
}

/* Tag pill colors live in components/tag.css, shared with the homepage. */

/* ---- Single column ----------------------------------------------------- */

/* The header grid collapses at this breakpoint, so the box is full width under
   the title; the facts then read better on one line. */
@media (max-width: 620px) {
  .post-meta {
    max-width: 620px;
    padding: 0.85rem 1.25rem 0.9rem 1rem;
  }

  .post-meta .facts .author {
    flex-basis: auto;
  }

  .post-meta .facts .author + .fact::before {
    content: "·";
    margin-right: 0.5rem;
  }
}
/* Tag pills. Every tag carries a palette class (tag-c1 .. tag-c8) derived from
   a hash of its own name in internal/render/tagcolor.go, so a tag looks the
   same everywhere it appears — the margin card, the homepage, a tag page — and
   keeps that color across rebuilds without anyone assigning one.

   Each pill sets a single --tag-color and the shared rule below derives its
   text, background and border from that one value with color-mix, so a hue is
   named in exactly one place. Colors come from tokens.css. */

.tag {
  display: inline-block;
  font-size: 0.72rem;
  line-height: 1;
  padding: 0.32em 0.6em;
  border-radius: 999px;
  /* Falls back to the neutral rail treatment if a pill is ever rendered
     without a palette class. */
  --tag-color: var(--muted);
  color: var(--tag-color);
  background: color-mix(in srgb, var(--tag-color) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--tag-color) 35%, transparent);
}

.tag-c1 { --tag-color: var(--tag-1); }
.tag-c2 { --tag-color: var(--tag-2); }
.tag-c3 { --tag-color: var(--tag-3); }
.tag-c4 { --tag-color: var(--tag-4); }
.tag-c5 { --tag-color: var(--tag-5); }
.tag-c6 { --tag-color: var(--tag-6); }
.tag-c7 { --tag-color: var(--tag-7); }
.tag-c8 { --tag-color: var(--tag-8); }

/* A tag inside a link should not pick up the link underline. */
a .tag {
  text-decoration: none;
}
.margin-note {
  border-left: 3px solid var(--accent-3);
  padding-left: 0.75rem;
  margin-bottom: 1.4rem;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
}

.margin-note .kind {
  display: block;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 700;
  color: var(--muted);
  margin-bottom: 0.25rem;
}
.margin-citation {
  border-left: 3px solid var(--accent-2);
  padding-left: 0.75rem;
  margin-bottom: 1.4rem;
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
}

.margin-citation .kind {
  display: block;
  font-size: 0.68rem;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 700;
  /* accent-2 only passes AA-large; this label is bold decorative text.
     Kept on muted for guaranteed body-size contrast, matching the note. */
  color: var(--muted);
  margin-bottom: 0.25rem;
}

.margin-citation cite {
  display: block;
  font-style: normal;
  font-weight: 700;
  color: var(--ink);
  font-size: 0.85rem;
}

.margin-citation .src {
  color: var(--muted);
  font-style: italic;
}

.margin-citation a {
  color: var(--accent);
  word-break: break-word;
}
.margin-chip {
  display: block;
  text-decoration: none;
  background: var(--card-bg);
  border: 1px solid var(--rule);
  border-radius: 10px;
  padding: 0.7rem 0.8rem;
  margin-bottom: 1.4rem;
  color: var(--ink);
  line-height: 1.4;
}
.margin-chip:hover {
  border-color: var(--accent);
}

/* The chip is a link, so the global a/a:hover rules in type.css would draw an
   underline through it — and a decoration set on an ancestor paints across all
   its descendants, so the title and description got the line too. The chip
   signals its own hover with a border color instead, so suppress the
   decoration in every state rather than only the default one. */
.margin-chip,
.margin-chip:hover,
.margin-chip:focus,
.margin-chip:focus-visible,
.margin-chip:active,
.margin-chip:visited {
  text-decoration: none;
}

/* Belt and braces: a descendant cannot opt out of an ancestor's decoration,
   but it can if the ancestor's is suppressed, so these stay explicit for any
   future chip variant that does underline its title. */
.margin-chip .chip-title,
.margin-chip .chip-desc,
.margin-chip .domain {
  text-decoration: none;
}
.margin-chip:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
}

.margin-chip .domain {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.72rem;
  color: var(--muted);
  text-transform: lowercase;
  margin-bottom: 0.35rem;
}

.margin-chip .dot {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 50%;
  background: var(--accent);
  color: var(--bg);
  font-size: 0.68rem;
  font-weight: 700;
  text-transform: uppercase;
  line-height: 1;
}

.margin-chip .chip-title {
  display: block;
  font-weight: 700;
  color: var(--ink);
  font-size: 0.86rem;
  margin-bottom: 0.15rem;
}

.margin-chip .chip-desc {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  color: var(--muted);
  font-size: 0.8rem;
}

/* Internal variant: same-site link, tinted with accent-3. */
.margin-chip.internal .dot {
  background: var(--accent-3);
}
.margin-chip.internal:hover {
  border-color: var(--accent-3);
}
/* Diagrams (inlined D2 SVGs) and generic figures.
   Colors come entirely from tokens.css so the palette stays swappable. */

figure {
  margin: 1.8em 0;
  text-align: center;
}

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

figure figcaption {
  margin-top: 0.6rem;
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.45;
  text-align: center;
}

/* ---- D2 diagram thumbnail --------------------------------------------- */

.diagram {
  margin: 1.8em 0;
}

/* The clickable thumbnail: a bounded panel with the darker diagram surface. */
.diagram-thumb {
  display: inline-block;
  max-width: 380px;
  width: 100%;
  padding: 0.9rem 1rem;
  background: var(--diagram-bg);
  border: 1px solid var(--rule);
  border-radius: 10px;
  cursor: zoom-in;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

.diagram-thumb:hover,
.diagram-thumb:focus-visible {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
  outline: none;
}

.diagram-thumb svg {
  width: 100%;
  height: auto;
  max-width: 100%;
  color: var(--ink); /* diagram strokes/text that use currentColor follow theme */
}

/* Override the white background rectangle D2 bakes into the SVG (class fill-N7)
   so it matches our darker diagram surface instead of a bright-white cutout. */
.diagram svg .fill-N7,
.diagram-modal svg .fill-N7 {
  fill: var(--diagram-bg);
}

.diagram-hint {
  margin-top: 0.5rem;
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---- CSS-only modal via a hidden checkbox (no JavaScript) -------------- */
/* A checkbox toggle is used instead of :target so opening/closing the modal
   never changes the URL fragment and therefore never moves the reader's
   scroll position. */

/* The hidden toggle is fixed to the top-left of the viewport rather than
   absolutely positioned within the article. When a label is clicked, focus
   moves to this checkbox and the browser scrolls it into view; a fixed element
   already in the viewport makes that scroll a no-op, so the background never
   jumps. */
.diagram-toggle {
  position: fixed;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

/* Lock background scroll while any diagram modal is open, so the page behind
   the overlay can't scroll. Zero JavaScript. */
html:has(.diagram-toggle:checked) {
  overflow: hidden;
}

.diagram-modal {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 100;
  padding: clamp(1rem, 5vw, 3rem);
  align-items: center;
  justify-content: center;
  overflow: auto; /* the modal itself scrolls if the enlarged diagram is tall */
}

.diagram-toggle:checked ~ .diagram-modal {
  display: flex;
}

/* Show a visible focus ring on the thumbnail when the (hidden) checkbox is
   focused via keyboard, so the control remains keyboard-operable. */
.diagram-toggle:focus-visible ~ .diagram-thumb {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent);
}

.diagram-modal-backdrop {
  position: absolute;
  inset: 0;
  background: var(--overlay-bg);
  cursor: zoom-out;
}

.diagram-modal-body {
  position: relative;
  z-index: 1;
  max-width: min(1100px, 92vw);
  max-height: 88vh;
  overflow: auto;
  padding: 1.5rem;
  background: var(--diagram-bg);
  border: 1px solid var(--rule);
  border-radius: 12px;
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.4);
}

.diagram-modal-body svg {
  width: 100%;
  height: auto;
  max-width: 100%;
  color: var(--ink);
}

.diagram-modal-close {
  position: absolute;
  top: 0.4rem;
  right: 0.7rem;
  font-size: 1.6rem;
  line-height: 1;
  text-decoration: none;
  color: var(--muted);
  border-radius: 6px;
  padding: 0.1rem 0.4rem;
  cursor: pointer;
}

.diagram-modal-close:hover,
.diagram-modal-close:focus-visible {
  color: var(--ink);
  outline: 2px solid var(--focus);
}

@media (prefers-reduced-motion: reduce) {
  .diagram-thumb {
    transition: none;
  }
}
pre, pre.chroma {
  background: var(--rail-bg);
  border: 1px solid var(--rule);
  border-radius: 8px;
  padding: 0.9rem 1rem;
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--ink);
  margin: 1.5em 0;
}

pre code {
  font-family: inherit;
  font-size: inherit;
  background: none;
  padding: 0;
  color: inherit;
}

/* Inline code (not inside a pre). */
code {
  font-family: var(--font-mono);
  font-size: 0.88em;
  background: var(--rail-bg);
  border: 1px solid var(--rule);
  border-radius: 4px;
  padding: 0.08em 0.35em;
  color: var(--ink);
}

/* ---- Chroma token colors ---- */

/* Keywords (k-family) except types -> accent */
.chroma .k,
.chroma .kd,
.chroma .kn,
.chroma .kp,
.chroma .kr,
.chroma .kc {
  color: var(--accent);
}

/* Types -> accent-3 */
.chroma .kt {
  color: var(--accent-3);
}

/* String literals (s-family) -> accent-2 */
.chroma .s,
.chroma .s1,
.chroma .s2,
.chroma .sb,
.chroma .sd {
  color: var(--accent-2);
}

/* Comments (c-family) -> muted italic */
.chroma .c,
.chroma .c1,
.chroma .cm,
.chroma .cs {
  color: var(--muted);
  font-style: italic;
}

/* Numbers (m-family) -> accent-2 */
.chroma .m,
.chroma .mi,
.chroma .mf {
  color: var(--accent-2);
}

/* Function / identifier names */
.chroma .nf {
  color: var(--ink);
  font-weight: 700;
}
.chroma .n,
.chroma .na,
.chroma .nb {
  color: var(--ink);
}
/* Markdown tables (GFM). Colors come entirely from tokens.css so the palette
   stays swappable.

   A wide table in a 620px column has to go somewhere, so the table scrolls
   horizontally inside its own wrapper rather than forcing the whole page to
   scroll sideways. */

.content-cell table {
  width: 100%;
  border-collapse: collapse;
  margin: 1.8em 0;
  font-size: 0.86rem;
  line-height: 1.45;
  /* Let a long table scroll itself instead of widening the page. */
  display: block;
  overflow-x: auto;
  /* Keep the scroll affordance from clipping focus rings on links inside. */
  overscroll-behavior-x: contain;
}

.content-cell table thead {
  /* A long reading list is easier to scan when the header stays put. */
  position: sticky;
  top: 0;
  z-index: 1;
}

.content-cell table th {
  text-align: left;
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--muted);
  background: var(--rail-bg);
  padding: 0.55rem 0.7rem;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}

.content-cell table td {
  padding: 0.5rem 0.7rem;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}

/* Zebra striping makes a long row-dense table far easier to track across. */
.content-cell table tbody tr:nth-child(even) {
  background: color-mix(in srgb, var(--card-bg) 45%, transparent);
}

.content-cell table tbody tr:hover {
  background: color-mix(in srgb, var(--accent) 10%, transparent);
}

.content-cell table td:first-child {
  color: var(--ink);
  font-weight: 500;
}

.content-cell table a {
  color: var(--accent);
}

/* On narrow screens shrink the type a little more rather than letting the
   horizontal scroll do all the work. */
@media (max-width: 620px) {
  .content-cell table {
    font-size: 0.8rem;
  }

  .content-cell table th,
  .content-cell table td {
    padding: 0.4rem 0.5rem;
  }
}
/* Author-placed images from ```img blocks, and the optional hero image above a
   post. Colors come entirely from tokens.css so the palette stays swappable. */

.post-img {
  margin: 1.8em 0;
}

.post-img img {
  max-width: 100%;
  height: auto;
  border-radius: 8px;
  border: 1px solid var(--rule);
}

.post-img figcaption {
  margin-top: 0.6rem;
  font-size: 0.85rem;
  color: var(--muted);
  line-height: 1.45;
}

/* ---- Alignment -------------------------------------------------------- */
/* Floats are applied only above the single-column breakpoint. Below it the
   content column is too narrow for text to wrap around an image legibly, so
   every alignment collapses to full width (see the media query below). */

.post-img.align-center {
  text-align: center;
}

.post-img.align-center figcaption {
  text-align: center;
}

@media (min-width: 621px) {
  .post-img.align-left {
    float: left;
    max-width: 46%;
    margin: 0.4em 1.4em 1em 0;
  }

  .post-img.align-right {
    float: right;
    max-width: 46%;
    margin: 0.4em 0 1em 1.4em;
  }
}

/* Below the breakpoint, floated images become ordinary full-width blocks. */
@media (max-width: 620px) {
  .post-img.align-left,
  .post-img.align-right {
    float: none;
    max-width: 100%;
    margin: 1.4em 0;
  }
}

/* A floated image must not overhang the block that follows it. */
.post-img.align-left + *,
.post-img.align-right + * {
  overflow-wrap: break-word;
}

/* ---- Hero image ------------------------------------------------------- */

.post-hero {
  margin: 0 0 2rem;
}

.post-hero img {
  width: 100%;
  height: auto;
  border-radius: 10px;
  border: 1px solid var(--rule);
}
/* List posts: the auto-generated table of contents and the numbered items it
   links to. Colors come entirely from tokens.css so the palette stays
   swappable.

   The TOC is plain anchor links — no JavaScript, and no :target rules — so
   nothing here can move the reader's scroll position on its own. Following a
   link is the reader's own click. */

.list-toc {
  max-width: 620px;
  margin: 0 0 2.5rem;
  padding: 1rem 1.25rem 1.1rem;
  background: var(--rail-bg);
  border: 1px solid var(--rule);
  border-radius: 10px;
}

.list-toc-heading {
  margin: 0 0 0.6rem;
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  font-weight: 600;
}

.list-toc-items {
  margin: 0;
  padding-left: 1.4rem;
  line-height: 1.7;
}

.list-toc-items li {
  margin: 0.1rem 0;
}

.list-toc-items a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}

.list-toc-items a:hover,
.list-toc-items a:focus-visible {
  color: var(--accent);
  border-bottom-color: var(--accent);
  outline: none;
}

.list-toc-items a:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 2px;
}

/* ---- Numbered item headings ------------------------------------------- */

.list-item-heading {
  /* scroll-margin keeps a heading clear of the viewport's top edge when the
     reader follows a TOC anchor to it. */
  scroll-margin-top: 1.5rem;
}

.list-item-number {
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  margin-right: 0.35rem;
}
@media print {
  .site-header,
  .site-nav,
  .site-footer {
    display: none !important;
  }

  body {
    background: #fff;
    color: #000;
  }

  /* Headings carry accent color on screen; on paper they must be black, since
     a light blue or peach heading is barely legible in print. */
  h1, h2, h3, h4, h5, h6 {
    color: #000;
  }

  hr {
    border-top-color: #ccc;
  }

  .page {
    max-width: none;
    padding: 0;
  }

  /* Single column: drop the margin rail. */
  .article-grid {
    display: block;
  }
  .content-cell {
    max-width: none;
  }
  .margin-cell {
    border-left: 0;
    padding-left: 0;
    color: #333;
  }

  a {
    color: #000;
    text-decoration: underline;
  }

  /* Show the destination of external links. */
  a[href^="http"]::after {
    content: " (" attr(href) ")";
    font-size: 0.85em;
    word-break: break-all;
  }

  pre, pre.chroma, code {
    background: #f4f4f4;
    border: 1px solid #ccc;
    color: #000;
  }

  .post-meta,
  .margin-chip {
    border: 1px solid #ccc;
    background: #fff;
  }
}
