/* Palette: paper, ink, rule, muted, seal. Dark mode overrides below. */
:root {
  --paper: #faf8f2;
  --ink: #24211b;
  --rule: #ded8ca;
  --muted: #6f6857;
  --seal: #a33b26;
  --link-underline: rgba(36, 33, 27, 0.28);
  --selection: #e7e0cf;
  --mono: 'IBM Plex Mono', ui-monospace, Menlo, monospace;
  /* Column geometry, shared by `main` and the margin rule on its right edge.
     The column sits off-centre: its middle lands about a third of the way
     across. The offset is capped so the column stops drifting on very wide
     screens, and falls to zero on narrow ones, where the column is flush
     left rather than re-centred -- one axis at every width. */
  --column: 480px;
  --gutter: 24px;
  --column-offset: clamp(0px, 33.33vw - var(--column) / 2, 320px);
  /* Fine-grain speckle texture (washi paper). camelCase SVG attributes
     must stay camelCase -- lowercasing them silently drops the filter. */
  --speckle: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.14'/%3E%3C/svg%3E");
}

@media (prefers-color-scheme: dark) {
  :root {
    --paper: #16150f;
    --ink: #e7e2d5;
    --rule: #322f26;
    --muted: #948d7e;
    --seal: #c9563c;
    --link-underline: rgba(231, 226, 213, 0.3);
    --selection: #34312a;
  }
}

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

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: 'IBM Plex Sans', 'Helvetica Neue', Arial, sans-serif;
  font-size: 15px;
  line-height: 1.62;
}

body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background-image: var(--speckle);
  background-size: 180px 180px;
  opacity: 0.3;
  mix-blend-mode: multiply;
}

@media (prefers-color-scheme: dark) {
  body::before {
    mix-blend-mode: screen;
    opacity: 0.12;
  }
}

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

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  text-decoration-color: var(--link-underline);
  transition: color .15s, text-decoration-color .15s;
}

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

/* Off-centre to the left, so the open field falls to the right, where the
   portrait, the leaders and the seal already lean. */
main {
  max-width: var(--column);
  margin: 0 auto 0 var(--column-offset);
  padding: 96px var(--gutter) 128px;
  text-wrap: pretty;
}

/* The margin rule: a hairline down the outside of the column, running the
   full height of the window. It sits a gutter clear of the text, the way a
   printed margin rule sits outside the text block -- close enough to bound
   the right-hand field, far enough that the dates and notes don't collide
   with it. Kept faint, matching the leaders: at full strength it stops
   reading as a margin and starts reading as a page split in two. Below
   900px there is no field left to bound, so it goes away. */
@media (min-width: 900px) {
  body::after {
    content: "";
    position: fixed;
    top: 0;
    bottom: 0;
    left: calc(var(--column-offset) + var(--column) + var(--gutter) * 2);
    width: 1px;
    background: var(--rule);
    opacity: 0.45;
    pointer-events: none;
  }
}

hr {
  border: 0;
  border-top: 1px solid var(--rule);
  width: 72px;
  margin: 0 0 44px;
  clear: both;
}

/* Homepage bio copy, between the motto and the hr. */
.bio p {
  margin: 0 0 24px;
}

.bio p:last-child {
  margin-bottom: 40px;
}

/* Portrait frame, homepage-only. */
.portrait {
  float: right;
  width: 210px;
  margin: 3px 0 24px 34px;
  padding: 7px;
  border: 1px solid var(--rule);
}

.portrait img {
  display: block;
  width: 100%;
  height: 252px;
  object-fit: cover;
}

@media (max-width: 560px) {
  .portrait {
    float: none;
    width: 200px;
    margin: 0 0 28px;
  }
}

h1 {
  font-size: 1em;
  font-weight: 400;
  margin: 0 0 40px;
  letter-spacing: 0.01em;
}

.motto {
  display: block;
  margin: 0 0 40px;
  padding: 0;
  border: 0;
  color: var(--muted);
}

section {
  margin: 0 0 40px;
}

.section-label {
  font-size: 0.85em;
  font-weight: 400;
  margin: 0 0 14px;
  color: var(--muted);
  letter-spacing: 0.14em;
}

/* Rows of link left, hairline leader, muted note right -- the homepage
   sections, the archive, the topics index, and each topic's post list. */
.link-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 7px;
}

.link-list li {
  display: flex;
  gap: 20px;
  /* Align to the title's closing line, so a title that wraps keeps its note
     beside that last line instead of beside the first. Plain `baseline` is
     the fallback where `last` is unsupported -- that only restores the older
     first-line alignment, so the row degrades rather than breaking. */
  align-items: baseline;
  align-items: last baseline;
}

.link-list a {
  /* Grows to fill the row, pushing the note flush right. */
  flex: 1 1 auto;
  /* Post titles keep their real capitalization in the markup and the feed;
     these lists render them lowercase to match the page's voice. */
  text-transform: lowercase;
  /* Same value as the leader below, so the underline and the leader read as
     one continuous line rather than two strokes at different weights. */
  text-decoration-color: var(--rule);
  /* Trims the leader's deliberate overrun. The bottom bleed spares the
     descenders and the underline, which both sit below the content box. */
  clip-path: inset(0 0 -0.4em 0);
}

/* The leader carries the eye from title to note. It rides the inline flow
   rather than sitting in its own flex box, so on a title that wraps it
   starts after the last word -- a flex box would be squeezed to nothing,
   because a wrapped link is as wide as its longest line. Width 100% with an
   equal negative margin lets it overrun the row without claiming space; the
   clip on the link cuts the overrun off before the note. The 3px matches
   text-underline-offset, so leader and underline sit on one line.

   It starts 20px after the last word, the same gap the row leaves between
   the link and the note, so the leader breathes on both sides. That 20px
   comes back out of the width, keeping the margin box zero-wide -- a leader
   wide enough to measure could wrap onto a line of its own. */
.link-list a::after {
  content: "";
  display: inline-block;
  margin-left: 20px;
  width: calc(100% - 20px);
  margin-right: -100%;
  border-bottom: 1px solid var(--rule);
  transform: translateY(3px);
  opacity: 0.45;
}

.link-list .note {
  flex: 0 0 auto;
  color: var(--muted);
}

.link-list .note--date {
  font-variant-numeric: tabular-nums;
}

/* Hovering anywhere on a row lights the whole row, not just the link. */
.link-list li:hover a,
.link-list li:hover .note {
  color: var(--seal);
}

.link-list li:hover a {
  text-decoration-color: var(--seal);
}

.link-list li:hover a::after {
  border-bottom-color: var(--seal);
  opacity: 1;
}

.more-link {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
}

.more-link a {
  display: inline-flex;
  align-items: baseline;
  gap: 9px;
  font-size: 0.85em;
  letter-spacing: 0.1em;
  color: var(--muted);
  text-decoration: none;
}

.more-link a:hover {
  color: var(--seal);
}

.more-link .arrow {
  letter-spacing: 0;
}

footer {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 32px;
  margin-top: 88px;
}

.cities {
  margin: 0;
  max-width: 30em;
  font-size: 0.8em;
  line-height: 2;
  letter-spacing: 0.04em;
  color: var(--muted);
}

.seal {
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  border: 1.5px solid var(--seal);
  border-radius: 50%;
  color: var(--seal);
  font-family: 'Songti SC', 'Source Han Serif SC', 'Noto Serif CJK SC', STSong, SimSun, serif;
  font-size: 18px;
  line-height: 1;
  user-select: none;
  flex: 0 0 auto;
}

/* Interior nav: one quiet line on every page except the homepage. */
.site-nav {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  margin: 0 0 56px;
  color: var(--muted);
  font-size: 0.85em;
}

.site-nav a {
  color: var(--muted);
}

.site-nav a:hover {
  color: var(--seal);
}

/* Post body. */
.post-title {
  font-size: 1.3em;
  font-weight: 500;
  margin: 0 0 8px;
}

/* The date and topics ride under the title, so a reader sees how old a
   piece is before reading it. */
.post-meta {
  margin: 0 0 32px;
  color: var(--muted);
  font-size: 0.85em;
}

main h2 {
  font-size: 1.1em;
  font-weight: 500;
  margin: 40px 0 16px;
}

main h3 {
  font-size: 1em;
  font-weight: 500;
  margin: 32px 0 12px;
}

main img {
  max-width: 100%;
}

main blockquote {
  margin: 0 0 24px;
  padding: 0 0 0 16px;
  border-left: 1px solid var(--rule);
  color: var(--muted);
}

main pre {
  margin: 0 0 24px;
  padding: 16px;
  overflow-x: auto;
  border: 1px solid var(--rule);
}

/* Monospace runs large next to Plex Sans at the same nominal size, so both
   inline and block code step down to 0.8em. Naming a real family also
   sidesteps the browsers' monospace default-size quirk, which rendered the
   untagged blocks -- no `language-` class, so Prism never touches them --
   smaller than the highlighted ones.

   Prism.js copies the `language-` class from the `code` up onto its `pre` at
   runtime, and its theme styles both at `[class*="language-"]` weight. A bare
   `main pre` loses to that, so every selector Prism claims has to be named
   here or the code blocks keep Prism's Consolas at full body size. */
main pre,
main pre[class*="language-"],
main pre code[class*="language-"] {
  font-family: var(--mono);
}

main pre,
main pre[class*="language-"] {
  font-size: 0.8em;
}

main :not(pre) > code {
  font-family: var(--mono);
  font-size: 0.8em;
}
