/* EB Garamond. Three static instances, no variable face — deliberately.
   =====================================================================
   Safari won't apply text-decoration-skip-ink to any family that contains
   a variable face: underlines cut straight through descenders. It's the
   presence of the variable face in the family that does it, not which face
   renders, so mixing a static 400 with a variable 500-800 under one name
   fails too. Static instances are fine, which is why the old site never
   hit it. 400 for text, 600 for headings, 400 italic for the masthead. */
@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-display: swap;
  font-weight: 400;
  src: url('../font/EBGaramond-Regular.woff2') format('woff2');
}

@font-face {
  font-family: 'EB Garamond';
  font-style: italic;
  font-display: swap;
  font-weight: 400;
  src: url('../font/EBGaramond-Italic.woff2') format('woff2');
}

@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-display: swap;
  font-weight: 600;
  src: url('../font/EBGaramond-SemiBold.woff2') format('woff2');
}

:root {
  --bg: #fbf3db;
  --fg: #53676d;
  --muted: #909995;
  --border: #d5cdb6;
  --surface: #ece3cc;
  --sel: #d5cdb6;
  --accent: #0072d4;
  --red: #d2212d;
  --green: #489100;
  --yellow: #ad8900;
  --magenta: #ca4898;
  --cyan: #009c8f;
  --add-bg: #4891001f;
  --del-bg: #d2212d1f;
  /* Recessed window chrome: the fill behind inactive tabs and the lights. It
     has to be darker than --bg in both schemes, because the active tab is
     painted --bg to merge with the pane and so must be the lighter of the two.
     --surface satisfies that in light mode; in dark mode it's lighter than
     --bg, so that scheme needs its own value. */
  --term-chrome: var(--surface);
  --mono: ui-monospace, 'SF Mono', 'Menlo', 'Cascadia Mono', monospace;
  /* Absolute, not a rem multiple: 1rem stays 16px for the rest of the scale.
     Named so anything that needs to return to reading size can say so. */
  --body-size: 19px;
  /* Old-style serifs throughout the fallbacks, so the voice survives when the
     webfont hasn't loaded (or is blocked). */
  --serif: 'EB Garamond', Garamond, 'Iowan Old Style', 'Hoefler Text',
    'Palatino Linotype', Palatino, 'Book Antiqua', Georgia, 'Times New Roman',
    serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #103c48;
    --fg: #adbcbc;
    --muted: #72898f;
    --border: #2d5b69;
    --surface: #184956;
    --sel: #2d5b69;
    --accent: #4695f7;
    --red: #fa5750;
    --green: #75b938;
    --yellow: #dbb32d;
    --magenta: #f275be;
    --cyan: #41c7b9;
    --add-bg: #75b9381f;
    --del-bg: #fa57501f;
    /* Selenized dark has nothing below bg_0, so this is #103c48 stepped down
       to sit under it while holding the same teal. */
    --term-chrome: #0c2f38;
  }
}

* {
  box-sizing: border-box;
}

html {
  color-scheme: light dark;
}

/* Native smooth scrolling for in-page links (footnote references and their
   back-links), honoring the OS-level reduced-motion preference. */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--serif);
  /* Garamond's small x-height: 19px matches the apparent size of 16px sans. */
  font-size: var(--body-size);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* kern is present in all three faces, so this is doing real work.
     No font-variant-numeric here: magritte asks for oldstyle-nums, but these
     EB Garamond files carry no `onum` feature and their default figures are
     already lining (all ten sit at ~0.63em against a 0.653em cap, with no
     descenders), so the declaration was inert. Tables use `tnum`, which the
     faces do have. Nothing to set until the fonts can deliver text figures. */
  font-kerning: normal;
}

/* 35rem leaves 520px of text. EB Garamond averages 0.379em per character over
   these posts, so at 19px that's ~72 characters, or 2.4 lowercase alphabet
   lengths — the wide end of Bringhurst's range, which suits justified setting:
   more words per line gives justif more places to break.
   (Magritte's 46rem runs to ~97 characters; this face is narrow.) */
.container {
  max-width: 35rem;
  margin: 0 auto;
  padding: 0 1.25rem;
}

/* Links keep the body color and carry a quiet underline, rather than magritte's
   colored-until-hover treatment. Reaching for the accent is the hover state.

   skip-ink is the initial value and this would behave the same without it —
   it's stated because it matters here and is easy to break: the underline sits
   1.9px to 4.3px below the baseline and Garamond's descenders reach 5.5px, so
   anything that defeats skipping cuts straight through g, y, p, q and j. See
   the note on the fonts above. */
a {
  color: var(--fg);
  text-decoration-skip-ink: auto;
  text-decoration-color: var(--muted);
  text-decoration-thickness: 0.125em;
  text-underline-offset: 0.1em;
}

a:hover {
  color: var(--accent);
  text-decoration-color: var(--accent);
}

/* Keyboard-only escape hatch past the masthead. */
.skip-link {
  position: absolute;
  left: -9999px;
}

.skip-link:focus {
  left: 1.25rem;
  top: 0.5rem;
  z-index: 1;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 0.4rem 0.8rem;
}

/* Site header. Magritte sets a bold wordmark flush left, with a nav opposite
   and a rule underneath; here it's the masthead on its own — centered, italic,
   and left to sit in open space rather than boxed in by a border. */
.site-header .container {
  padding-top: 1.25rem;
  padding-bottom: 1.25rem;
  text-align: center;
}

/* Nothing about the underline: the wordmark takes the same treatment as any
   other link, which at this size means a substantial rule under the title. */
.wordmark {
  font-size: 3rem;
  font-style: italic;
  font-weight: 400;
}

main {
  padding: 1.5rem 0 4rem;
}

/* Type */
h1,
h2,
h3 {
  line-height: 1.25;
  font-weight: 600;
  /* Wrapped headings (mostly mobile) get evened-out lines, not a straggler. */
  text-wrap: balance;
  /* Never justified. Headings inherit the article's justification otherwise,
     which stretches the spaces on every line but the last and works against
     the balancing above. */
  text-align: left;
}

h1 {
  font-size: 2rem;
  margin: 0 0 0.5rem;
}

h2 {
  font-size: 1.4rem;
  margin: 2.6rem 0 0.75rem;
}

h3 {
  font-size: 1.15rem;
  margin: 1.8rem 0 0.5rem;
}

/* Magritte stops at h3. Posts here can go deeper, and without this they'd
   fall through to the UA's bold Times. */
h4,
h5,
h6 {
  line-height: 1.25;
  font-weight: 600;
  margin: 1.4rem 0 0.4rem;
  text-wrap: balance;
  text-align: left;
}

h4 {
  font-size: 1.05rem;
}

h5,
h6 {
  font-size: 1rem;
}

h6 {
  color: var(--muted);
}

p,
ul,
ol {
  margin: 0.75rem 0;
}

code,
kbd,
pre {
  font-family: var(--mono);
}

code {
  font-size: 0.74em;
  background: var(--surface);
  border-radius: 4px;
  padding: 0.1em 0.2em;
}

kbd {
  font-size: 0.8rem;
  background: var(--surface);
  border: 1px solid var(--muted);
  border-bottom-width: 2px;
  border-radius: 4px;
  padding: 0.05em 0.4em;
  white-space: nowrap;
}

pre {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.75rem 1rem;
  overflow-x: auto;
  font-size: 0.85rem;
  line-height: 1.5;
}

pre code {
  background: none;
  padding: 0;
  font-size: 1em;
}

/* Giallo's generated stylesheets paint their own block background (Solarized's
   #FDF6E3 / #002B36). Keep our surface color in both modes for cohesion and
   take only the token foregrounds from Solarized — the same trick magritte
   plays on Shiki's output. */
pre.z-code {
  background: var(--surface);
}

/* Shell blocks read as something you'd type. The prompt isn't part of the
   command, so it's muted and can't be selected into a copy. */
pre code[data-lang='shellscript']::before {
  content: '$ ';
  color: var(--muted);
  user-select: none;
}

/* Tables */
table {
  border-collapse: collapse;
  width: 100%;
  margin: 1rem 0;
  font-size: 1rem;
  /* tabular, not lining: these faces have no `lnum` (and are lining by
     default anyway, so it changed nothing) but they do have `tnum`, which is
     what a column of figures actually wants — equal advance widths so the
     benchmark numbers line up. */
  font-variant-numeric: tabular-nums;
}

th,
td {
  text-align: left;
  padding: 0.35rem 0.75rem 0.35rem 0;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

th {
  font-weight: 600;
}

blockquote {
  margin: 1rem 0;
  padding: 0 0 0 1rem;
  border-left: 3px solid var(--border);
  color: var(--muted);
}

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

/* Prose. justif re-sets these paragraphs with Knuth–Plass line breaking; what
   follows is the initial rendering and the no-JS fallback. */
article {
  text-align: justify;
  text-wrap: pretty;
  /* Hyphenate, but not short words, tiny fragments, or more than two lines in
     a row (the -webkit- forms are Safari's equivalents). */
  hyphens: auto;
  hyphenate-limit-chars: 7 4 3;
  -webkit-hyphenate-limit-before: 4;
  -webkit-hyphenate-limit-after: 3;
  -webkit-hyphenate-limit-lines: 2;
  hanging-punctuation: first last;
}

li {
  text-align: left;
}

article ul > li::marker {
  content: '⁃ ';
}

/* A macOS window drawn in CSS, holding the listing. Magritte frames its
   screenshots the same way — same radius, border and shadow — but there the
   dots are painted over an image that already contains a title bar, so they
   scale in cqw. Here the chrome is real, so the metrics are fixed px and
   taken from the actual system: a 28pt bar, 12pt lights, 8pt apart, the first
   one centred 20pt in. Titlebar on --surface, content on --bg, which is how
   a Selenized terminal actually sits against its own window frame. */
.term {
  margin: 0.5rem 0 0;
  border: 1px solid var(--border);
  /* 14px, measured off the reference screenshot. overflow:hidden is what
     rounds the tab bar's outer corners with it, so the tabs never need a
     top radius of their own. */
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 16px 40px -12px rgb(0 0 0 / 0.28);
}

/* iTerm puts the tabs in the title bar, so the lights live inside the active
   tab rather than on a strip of their own. The bar's background is the
   inactive tab colour; the active tab is painted the content colour.

   No rule under the bar, deliberately. The active tab has to run straight
   into the pane it belongs to with nothing between them — that continuity is
   what makes it read as selected. The only edge along the bottom is where the
   inactive tab's darker fill stops, and that's a colour step, not a border. */
.term-bar {
  display: flex;
  height: 38px;
  background: var(--term-chrome);
}

/* The lights get their own segment ahead of the tabs — they are not inside
   the active one. Measured across the bar: 0-74px is the dark fill behind the
   lights, a 1px rule at 74, the active tab light from 75 to 450, another rule,
   then the inactive tab dark to the end. So this segment behaves like an
   inactive tab: same fill, same rule beneath it, same divider beside it.

   8px / 14px / 9px / 14px / 9px / 14px / 6px = the measured 74. */
.term-lights {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 6px 0 8px;
  border-bottom: 1px solid var(--border);
  border-right: 1px solid var(--border);
}

/* The rule along the bottom belongs to the segments, not the bar. Inactive
   ones are closed off by it; the active tab is not, so it runs straight into
   the pane below. The screenshot bears this out — under the active tab
   #f9f2dc meets #faf3dd with nothing between them, while under everything
   else there's a hard 1px line. */
.term-tab {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  padding: 0 8px;
  border-bottom: 1px solid var(--border);
}

.term-tab + .term-tab {
  border-left: 1px solid var(--border);
}

.term-tab--on {
  background: var(--bg);
  border-bottom-color: transparent;
}

/* The about tab is a link. --sel is Selenized's own selection colour, which
   is darker than the chrome in light mode and lighter in dark — so the hover
   reads as a press in one scheme and a highlight in the other without needing
   two rules. It was declared in the palette and unused until now. */
.term-tab-link {
  text-decoration: none;
  transition: background-color 0.12s ease;
}

.term-tab-link:hover {
  background: var(--sel);
}

/* Chrome doesn't take the accent on hover the way body links do. */
.term-tab-link:hover .term-tab-name {
  color: var(--fg);
}

.term-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
}

/* Fixed hues in both schemes, as on the real thing. */
.term-close { background: #ec6a5e; }
.term-min { background: #f4bf4f; }
.term-zoom { background: #61c554; }

/* The system UI face, because that is what the real chrome uses — this is
   the one place on the site that isn't Garamond or mono, and it's a window
   frame rather than content. Centred in the space the lights leave over,
   which is where iTerm puts it, not on the tab's full width. */
.term-tab-name {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-family: system-ui, -apple-system, sans-serif;
  font-size: 13px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.term-tab--on .term-tab-name {
  color: var(--fg);
}

/* A segment of its own rather than an overlay, so the about tab's hover fill
   stops short of it. No divider on its left — the screenshot shows the chrome
   running unbroken from the inactive tab out to the window edge — but it does
   carry the same bottom rule as everything that isn't the active tab. */
.term-new {
  display: flex;
  align-items: center;
  padding: 0 11px;
  font-family: system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
}

.term .tree {
  margin: 0;
  padding: 0.7rem 1rem 0.85rem;
}

/* Index, set like tree(1) output. The connectors are drawn with borders rather
   than box-drawing characters: EB Garamond has no glyphs in U+2500-257F, so
   ├ └ │ would each fall back to some other font and stop lining up. Borders
   also take a palette color and stay crisp at any size.

   Everything here is chrome except the post titles: the command, root, years,
   tally and connectors are monospace at 0.85em, and the titles are Garamond at
   reading size. The rows staying mono is what lets the geometry below use ch,
   which is one character cell only in a monospace face. Sizing at this level
   rather than per row also stops the nested year/post levels compounding it. */
.tree {
  text-align: left;
  margin: 0.5rem 0 0;
  font-family: var(--mono);
  font-size: 0.85em;

  /* A row's vertical padding, and its line box. The line box is a length, not
     a ratio, so the 16px mono strut and the 19px serif title resolve to the
     same value — otherwise the row height depends on which font is taller and
     the connector offsets below stop landing on the title's midline.

     Tighter than the 1.5 body leading, because a listing wants to read as a
     block. 1.35 is near the floor: EB Garamond's content area is 1.305em, so
     below roughly 1.31 consecutive rows begin to collide. */
  --row-pad: 0.05em;
  --row-line: calc(var(--body-size) * 1.35);

  line-height: var(--row-line);
}

/* The invocation, sitting above its own output. The prompt is dimmed and
   unselectable so copying the line yields the command alone — the same
   treatment the ```sh blocks in posts get. */
/* --row-line is sized for the rows, which hold 19px serif titles. These two
   lines are 16px mono, so that same box leads them at about 1.6 of their own
   size and they drift apart. Lead them at their own size instead. */
.tree-cmd,
.tree-root {
  margin: 0;
  padding: var(--row-pad) 0;
  color: var(--muted);
  line-height: 1.35;
}

.tree-cmd::before {
  content: '$ ';
  color: var(--muted);
  user-select: none;
}


/* The titles are the only content in the listing, so they come back to the
   body face and size — Garamond's small x-height is the reason 19px is the
   reading size in the first place, and 0.85em of it would read as fine print.
   No underline: it reads as terminal output, and the accent on hover is
   enough to mark the rows as links. */
.tree a {
  font-family: var(--serif);
  font-size: var(--body-size);
  text-decoration: none;
}

/* Put the chip back. The global `a code` rule strips it because a code span
   inside running prose is already marked by the link color, but here the whole
   row is the link — so the title should read exactly as it does in the <h1> on
   the post page. */
.tree a code {
  background: var(--surface);
  padding: 0.1em 0.2em;
}

.tree ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Applies at both levels. A year's row nests its posts inside itself, so the
   posts' spines start at the year's content edge — which is the indent — and
   the year's own spine runs the full height of the row, carrying │ down past
   its children exactly as tree(1) does.

   Indented in ch, one character cell in a monospace face, which reproduces
   tree(1)'s real geometry: the glyph sits in column 0, "──" runs through
   columns 1-2, column 3 is the gap, and the name starts at column 4. */
.tree li {
  position: relative;
  margin: 0;
  padding: var(--row-pad) 0 var(--row-pad) 4ch;
}

/* Years are scaffolding, like the root and the tally; the posts are content. */
.tree-year {
  color: var(--muted);
}

/* One spine segment per row. They stack edge to edge — the row spacing is
   padding, not margin — so the segments read as a single unbroken line. */
/* 0.5ch is the center of column 0, where the period's ink sits — it is exactly
   centered in its cell in SF Mono. The extra half pixel back centers the 1px
   border on that coordinate rather than starting it there, which is what put
   the spine a hair right of the "." above it. */
.tree li::before {
  content: '';
  position: absolute;
  left: calc(0.5ch - 0.5px);
  top: 0;
  bottom: 0;
  border-left: 1px solid var(--muted);
}

/* The last row stops its spine at the elbow, turning ├ into └. */
.tree li:last-child::before {
  bottom: auto;
  height: calc(var(--row-pad) + var(--row-line) / 2);
}

/* The elbow, running from the spine through column 2 and stopping a column
   short of the name — the "──" in "├── name". Its height is the row's
   padding-top plus half a line box, which puts it on the title's midline. */
.tree li::after {
  content: '';
  position: absolute;
  left: calc(0.5ch - 0.5px);
  top: calc(var(--row-pad) + var(--row-line) / 2);
  width: calc(2.5ch + 0.5px);
  border-top: 1px solid var(--muted);
}

/* tree(1) closes with a tally. */
.tree-count {
  margin: 0.8rem 0 0;
  color: var(--muted);
}

.post-date {
  margin-top: 2.5rem;
  color: var(--muted);
  font-size: 1rem;
}

/* Footnotes. Zola emits <div class="footnotes"><ol class="footnotes-list">
   with no rule of its own, so draw one. */
.footnotes {
  font-size: 0.85em;
}

.footnotes::before {
  content: '';
  display: block;
  height: 1px;
  margin: 2.5rem 0 1.5rem;
  background: var(--border);
}

.footnote-definition-label,
.footnotes-list {
  color: inherit;
}

/* Content images. Magritte only ever frames screenshots, so this is new.
   The picture shortcode leaves its <picture> inline inside the paragraph;
   block it out so the image isn't laid out as a very tall line of text. */
article picture {
  display: block;
}

article img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 1.5rem auto 0;
  border-radius: 6px;
}

/* Captions: the posts write them as an <em> immediately after the image. */
article img + em,
article picture + em,
article picture + p > em {
  display: block;
  margin-top: 0.6rem;
  text-align: center;
  font-style: italic;
  color: var(--muted);
  font-size: 0.95rem;
}

.callout {
  background: var(--surface);
  border-left: 3px solid var(--accent);
  border-radius: 0 6px 6px 0;
  padding: 0.6rem 1rem;
  margin: 1.25rem 0;
}

/* An aside rather than a call to attention: no fill, and it recedes. */
.smallout {
  border-left: 3px solid var(--border);
  padding: 0.1rem 1rem;
  margin: 1.25rem 0;
  color: var(--muted);
  font-size: 0.85em;
}

.callout > :first-child,
.smallout > :first-child {
  margin-top: 0;
}

.callout > :last-child,
.smallout > :last-child {
  margin-bottom: 0;
}

.callout code,
.callout kbd {
  background: var(--border);
}

/* A code span inside a link is already marked by the link color. */
.callout a code,
a code {
  background: unset;
  padding: 0;
}

@media (max-width: 40rem) {
  /* Wide tables beat a phone's measure; scroll them in place rather than
     widening the page. */
  table {
    display: block;
    overflow-x: auto;
  }

  .wordmark {
    font-size: 2.25rem;
  }
}
