@charset "UTF-8";
/* ==========================================================================
   site.css
   One stylesheet for the whole site. Order: tokens, base, layout,
   components, print. Change the look from the tokens block first.
   ========================================================================== */

/* 1. Tokens ---------------------------------------------------------------- */

/* Black and white, with terminal blue (ANSI blue) as the only colour. Light is black ink
   on white paper, dark is white ink on black, blueprint is white ink on blueprint blue.
   The accent marks what is highlighted: § numbers, decision IDs, the current page,
   link hovers, focus and selection. */

:root {
  color-scheme: light;

  --paper: #ffffff;
  --ink: #111111;
  --ink-2: #3d3d3d;
  --ink-3: #666666;
  --rule: #dddddd;
  --tint: #f0f0f0;
  --accent: #0000ee;
  --grid: rgba(0, 0, 0, 0.05);
  --contour: #000000;
  --contour-opacity: 0.22;
  /* The profile drawing stays black on white in every theme, a cut-out on the sheet. */
  --drawing-ink: #111111;
  --drawing-paper: #ffffff;

  --serif: "Iowan Old Style", "Charter", "Bitstream Charter", "Sitka Text", Cambria, Georgia, serif;
  --mono: ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Mono", Menlo, Consolas, monospace;

  --fs-meta: 0.8125rem;
  --fs-small: 0.9375rem;
  --fs-body: 1.0625rem;
  --fs-lede: 1.25rem;
  --fs-h2: 1.5rem;
  --fs-h1: clamp(2.25rem, 1.6rem + 2.4vw, 3.25rem);

  --rail: 10rem;
  --gutter: 2.5rem;
  --measure: 36rem;
  --side: 14rem;
  --pad: 1.25rem;
  --grid-size: 24px;
  --content: calc(var(--rail) + var(--gutter) + var(--measure));
  --page: calc(var(--content) + var(--gutter) + var(--side));
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    color-scheme: dark;
    --paper: #0b0b0b;
    --ink: #f2f2f2;
    --ink-2: #c4c4c4;
    --ink-3: #8e8e8e;
    --rule: #2a2a2a;
    --tint: #1a1a1a;
    --accent: #5c5cff;
    --grid: rgba(255, 255, 255, 0.06);
    --contour: #ffffff;
    --contour-opacity: 0.25;
  }
}

:root[data-theme="dark"] {
  color-scheme: dark;
  --paper: #0b0b0b;
  --ink: #f2f2f2;
  --ink-2: #c4c4c4;
  --ink-3: #8e8e8e;
  --rule: #2a2a2a;
  --tint: #1a1a1a;
  --accent: #5c5cff;
  --grid: rgba(255, 255, 255, 0.06);
  --contour: #ffffff;
  --contour-opacity: 0.25;
}

/* Blueprint: white ink on blueprint blue. A blue accent would vanish here, so it uses pale cyan. */
:root[data-theme="blueprint"] {
  color-scheme: dark;
  --paper: #123e6e;
  --ink: #f2f6ff;
  --ink-2: #d0dcef;
  --ink-3: #a3b8d8;
  --rule: #3a6394;
  --tint: #1b4a80;
  --accent: #9fd8ff;
  --grid: rgba(255, 255, 255, 0.12);
  --contour: #ffffff;
  --contour-opacity: 0.25;
}

/* 2. Base ------------------------------------------------------------------ */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  scroll-padding-top: 1.5rem;
  /* Reserve the scrollbar's width on every page, so short and long pages line up. */
  scrollbar-gutter: stable;
}

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

body {
  margin: 0;
  background-color: var(--paper);
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px);
  background-size: var(--grid-size) var(--grid-size);
  color: var(--ink);
  font: var(--fs-body)/1.6 var(--serif);
  font-kerning: normal;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, h4 {
  margin: 0;
  font-weight: 600;
  line-height: 1.25;
  text-wrap: balance;
}

p { margin: 0 0 1em; text-wrap: pretty; }
ul, ol { margin: 0 0 1em; padding-left: 1.25em; }
li + li { margin-top: 0.25em; }
img, svg { max-width: 100%; height: auto; }

a {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule);
  text-underline-offset: 0.2em;
  transition: text-decoration-color 0.15s ease, color 0.15s ease;
}
a:hover { text-decoration-color: var(--accent); }

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

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

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

code {
  font-size: 0.86em;
  background: var(--tint);
  padding: 0.08em 0.3em;
  border-radius: 2px;
}

kbd {
  font-size: 0.85em;
  border: 1px solid var(--rule);
  border-bottom-width: 2px;
  border-radius: 3px;
  padding: 0 0.35em;
}

pre {
  margin: 1.75rem 0;
  padding: 0.25rem 0 0.25rem 1.1rem;
  border-left: 2px solid var(--ink);
  font-size: 0.8125rem;
  line-height: 1.6;
  overflow-x: auto;
  tab-size: 2;
}
pre code { background: none; padding: 0; font-size: inherit; }

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

abbr[title] {
  text-decoration: none;
  border-bottom: 1px dotted var(--ink-3);
  cursor: help;
}

em, i { font-style: italic; }
strong, b { font-weight: 600; }

.skip {
  position: absolute;
  left: var(--pad);
  top: -3rem;
  padding: 0.4rem 0.75rem;
  background: var(--ink);
  color: var(--paper);
  font: var(--fs-meta)/1.5 var(--mono);
  z-index: 10;
}
.skip:focus { top: 0.75rem; }


/* 3. Layout ---------------------------------------------------------------- */

.wrap {
  max-width: calc(var(--content) + 2 * var(--pad));
  margin-inline: auto;
  padding-inline: var(--pad);
}

@media (min-width: 76rem) {
  .wrap { max-width: calc(var(--page) + 2 * var(--pad)); }
}

/* Short pages: main grows so the footer sits at the bottom of the viewport, not mid-screen. */
#app {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  min-height: 100dvh;
}
#app > main { flex: 1 0 auto; }

/* Two columns: a narrow rail for labels and metadata, a reading column. */
.split {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: 1rem;
  max-width: var(--content);
}

@media (min-width: 48rem) {
  .split {
    grid-template-columns: var(--rail) minmax(0, var(--measure));
    column-gap: var(--gutter);
  }
  .split > .rail { grid-column: 1; grid-row: 1; }
  .split > .main { grid-column: 2; grid-row: 1; }
}

/* Masthead */

.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem 2rem;
  padding-block: 1.25rem 0.9rem;
  border-bottom: 1px solid var(--ink);
}

.brand {
  font-weight: 600;
  font-size: 1.0625rem;
  line-height: 1.3;
  text-decoration: none;
}

.nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: 0.2rem 1.1rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font: var(--fs-meta)/1.6 var(--mono);
}
.nav li + li { margin-top: 0; }
.nav a { color: var(--ink-3); text-decoration-color: transparent; }
.nav a:hover { color: var(--ink); text-decoration-color: var(--rule); }
.nav a[aria-current="page"] {
  color: var(--ink);
  text-decoration-color: var(--accent);
  text-decoration-thickness: 2px;
}

/* Footer. The double rule closes the page, like the totals line on a statement. */

.colophon {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem 2rem;
  margin-top: 4.5rem;
  padding-block: 1.1rem 3rem;
  border-top: 3px double var(--ink);
  font: var(--fs-meta)/1.7 var(--mono);
  color: var(--ink-3);
}
.colophon p { margin: 0; }
.colophon-tools { display: flex; flex-wrap: wrap; gap: 0.25rem 1.25rem; }
.colophon a { text-decoration-color: var(--rule); }

/* 4. Components ------------------------------------------------------------ */

/* Home introduction */

/* Same top padding as .page-head, so the photo sits in the same place on home and about. */
.intro { padding-block: 3.5rem 3rem; }

.display {
  font-size: var(--fs-h1);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.02;
  margin-bottom: 1.25rem;
}

.lede {
  font-size: var(--fs-lede);
  line-height: 1.5;
  color: var(--ink-2);
  max-width: 33rem;
  margin-bottom: 0;
}

/* Inner page header */

.page-head { padding-block: 3.5rem 2.5rem; }

.page-head h1 {
  font-size: var(--fs-h1);
  font-weight: 400;
  letter-spacing: -0.02em;
  line-height: 1.05;
  margin-bottom: 1rem;
}

@media (min-width: 48rem) {
  .intro .rail, .page-head .rail { padding-top: 0.85rem; }
}

/* Contour map in the right margin of each page header. Each page sets
   --map-at to show a different part of the same map. Decorative only. */
.intro, .page-head { position: relative; }

@media (min-width: 76rem) {
  .intro::after, .page-head::after {
    content: "";
    position: absolute;
    top: 1.5rem;
    bottom: -1rem;
    left: calc(var(--content) + var(--gutter));
    width: var(--side);
    background-color: var(--contour);
    opacity: var(--contour-opacity);
    /* Second layer fades the crop edges so it reads as a map fragment. */
    -webkit-mask:
      url("../contours.svg") var(--map-at, 30% 20%) / 42rem auto no-repeat,
      radial-gradient(closest-side, #000 55%, transparent);
    -webkit-mask-composite: source-in;
    mask:
      url("../contours.svg") var(--map-at, 30% 20%) / 42rem auto no-repeat,
      radial-gradient(closest-side, #000 55%, transparent);
    mask-composite: intersect;
    pointer-events: none;
  }
  .intro::after { top: 1rem; bottom: 0; }
}

/* Profile photo in the rail, above the metadata. On phones it moves above the heading. */

.portrait { margin: 0; width: 8rem; }
.portrait img,
.portrait-empty {
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
  height: auto;
  object-fit: cover;
  border: 1px solid var(--rule);
  background-color: var(--paper);
}
.portrait.bw img { filter: grayscale(1) contrast(1.05); }

/* Line drawing: the fill mask paints white under the figure, the lines mask paints the ink.
   The background around the figure stays transparent, so the grid runs up to it. */
.drawing {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 4 / 5;
}
.drawing::before,
.drawing::after {
  content: "";
  position: absolute;
  inset: 0;
  -webkit-mask: var(--m) center / 100% 100% no-repeat;
  mask: var(--m) center / 100% 100% no-repeat;
}
.drawing::before { --m: var(--fill); background-color: var(--drawing-paper); }
.drawing::after { --m: var(--lines); background-color: var(--drawing-ink); }

/* Placeholder: a drawing-sheet "image goes here" frame with crossed diagonals. */
.portrait-empty {
  position: relative;
  display: grid;
  place-items: center;
  border-color: var(--ink-3);
  background-image:
    linear-gradient(to top right, transparent calc(50% - 0.5px), var(--rule) calc(50% - 0.5px), var(--rule) calc(50% + 0.5px), transparent calc(50% + 0.5px)),
    linear-gradient(to bottom right, transparent calc(50% - 0.5px), var(--rule) calc(50% - 0.5px), var(--rule) calc(50% + 0.5px), transparent calc(50% + 0.5px));
}
.portrait-empty span {
  padding: 0 0.4rem;
  background: var(--paper);
  font: var(--fs-meta)/1.6 var(--mono);
  color: var(--ink-3);
}

.has-photo > .portrait { order: -1; width: 6rem; }

/* With a photo, the metadata leaves the rail and runs under the lede as a title-block strip,
   so the two columns stay close in height. */
.has-photo > .rail.meta {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr));
  gap: 0.9rem 1.5rem;
  margin-top: 1.75rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}
.has-photo > .rail.meta > div { display: block; }
.has-photo > .rail.meta > div + div dt { margin-top: 0; }

@media (min-width: 48rem) {
  .has-photo > .portrait { grid-column: 1; grid-row: 1 / span 2; order: 0; width: var(--rail); padding-top: 0.85rem; }
  .has-photo > .main { grid-column: 2; grid-row: 1; }
  .has-photo > .rail.meta { grid-column: 2; grid-row: 2; padding-top: 1rem; }
}

/* Metadata block: terse mono labels, like a man page or a doc header. */

.meta {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-content: start;
  gap: 0.1rem 1rem;
  margin: 0;
  font: var(--fs-meta)/1.6 var(--mono);
}
.meta dt { color: var(--ink-3); }
.meta dd { margin: 0; color: var(--ink-2); }
.meta > div { display: contents; }

/* In the narrow rail, labels sit above their values. */
@media (min-width: 48rem) {
  .rail.meta { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .rail.meta > div + div dt { margin-top: 0.6rem; }
}

/* Numbered sections. Numbers come from CSS counters, so authors never renumber. */

main { counter-reset: sec; }

.sec {
  padding-block: 2rem 1.25rem;
  border-top: 1px solid var(--rule);
}

.sec-h {
  counter-increment: sec;
  font: 500 var(--fs-meta)/1.5 var(--mono);
  color: var(--ink-2);
}
.sec-h::before {
  content: "§" counter(sec);
  margin-right: 0.8ch;
  color: var(--accent);
}

@media (min-width: 48rem) {
  .sec-h { padding-top: 0.3rem; }
}

.sec .main > :last-child { margin-bottom: 0; }

.more {
  font: var(--fs-meta)/1.6 var(--mono);
  margin-top: 0.5rem;
}

/* Leader rows: title, dotted leader, status or date. Used for every list. */

.toc {
  margin: 0 0 1.25rem;
  padding: 0;
  list-style: none;
}
.toc > li { margin: 0 0 1.1rem; }
.toc > li:last-child { margin-bottom: 0; }

.toc-line {
  display: flex;
  align-items: baseline;
  align-items: last baseline;
  gap: 0.6ch;
  margin: 0;
  font-size: var(--fs-body);
  font-weight: 600;
  line-height: 1.4;
}
.toc-line::after {
  content: "";
  order: 1;
  flex: 1 1 1.5rem;
  min-width: 1.5rem;
  border-bottom: 1px dotted var(--ink-3);
  opacity: 0.55;
  transform: translateY(-0.3em);
  transition: opacity 0.15s ease;
}
.toc > li:hover .toc-line::after,
.record:hover .toc-line::after { opacity: 1; }

.toc-line > a,
.toc-line > span:first-child { text-decoration-color: transparent; }
.toc-line > a:hover { text-decoration-color: var(--accent); }

.toc-meta {
  order: 2;
  flex: none;
  font: 400 var(--fs-meta)/1.4 var(--mono);
  color: var(--ink-3);
}

.toc-desc {
  margin: 0.2rem 0 0;
  color: var(--ink-2);
}

.toc-line .draft { color: var(--ink-3); font-weight: 400; }

/* Project records: a heading row plus a field list. */

.record { margin-bottom: 2.25rem; }
.record:last-child { margin-bottom: 0; }
.record > p { margin: 0.35rem 0 0; color: var(--ink-2); }

.fields {
  display: grid;
  grid-template-columns: 4.75rem minmax(0, 1fr);
  gap: 0.25rem 1rem;
  margin: 0.8rem 0 0;
  font-size: var(--fs-small);
  line-height: 1.55;
}
.fields dt {
  font: var(--fs-meta)/1.85 var(--mono);
  color: var(--ink-3);
}
.fields dd { margin: 0; }

/* Changelog */

.log {
  margin: 0;
  padding: 0;
  list-style: none;
}
.log > li {
  display: grid;
  grid-template-columns: 5.5rem minmax(0, 1fr);
  gap: 0 1rem;
  margin: 0;
  padding-block: 0.7rem;
  border-bottom: 1px solid var(--rule);
}
.log > li:first-child { padding-top: 0; }
.log time {
  font: var(--fs-meta)/1.95 var(--mono);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.log p { margin: 0; }
.tag {
  font: var(--fs-meta)/1 var(--mono);
  color: var(--accent);
  margin-right: 0.6ch;
}

/* Plain lists inside sections */

.plain {
  padding: 0;
  list-style: none;
}
.plain > li + li { margin-top: 0.6rem; }

.principles { counter-reset: p; padding: 0; list-style: none; }
.principles > li {
  counter-increment: p;
  position: relative;
  padding-left: 2rem;
}
.principles > li + li { margin-top: 0.75rem; }
.principles > li::before {
  content: counter(p, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: 0.3em;
  font: var(--fs-meta)/1.4 var(--mono);
  color: var(--ink-3);
}

/* Article: case notes and blog posts */

.article-body { padding-top: 0.5rem; }

.prose { counter-reset: h2 fig; }
.prose > :first-child { margin-top: 0; }
.prose h2 {
  counter-increment: h2;
  position: relative;
  margin: 2.75rem 0 0.9rem;
  font-size: var(--fs-h2);
  letter-spacing: -0.01em;
}
.prose h2::before {
  content: "§" counter(h2);
  margin-right: 0.6rem;
  font: 500 var(--fs-meta)/1 var(--mono);
  color: var(--accent);
  vertical-align: 0.3em;
}
@media (min-width: 48rem) {
  .prose h2::before {
    position: absolute;
    right: calc(100% + 0.9rem);
    top: 0.62em;
    margin: 0;
  }
}
.prose h3 { margin: 2rem 0 0.6rem; font-size: 1.125rem; }
.prose ul, .prose ol { padding-left: 1.2em; }

.anchor {
  margin-left: 0.4ch;
  color: var(--ink-3);
  font: 400 var(--fs-meta)/1 var(--mono);
  text-decoration: none;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.prose h2:hover .anchor,
.anchor:focus-visible { opacity: 1; }

/* "On this page", built by enhance.js from the article's h2s */

.toc-mini { display: none; }

@media (min-width: 48rem) {
  .toc-mini:not([hidden]) {
    display: block;
    position: sticky;
    top: 1.5rem;
    align-self: start;
    font: var(--fs-meta)/1.5 var(--mono);
  }
}
.toc-mini p { margin: 0 0 0.6rem; color: var(--ink-3); }
.toc-mini ol { margin: 0; padding: 0; list-style: none; counter-reset: t; }
.toc-mini li { counter-increment: t; margin: 0 0 0.35rem; }
.toc-mini a {
  display: flex;
  gap: 0.8ch;
  color: var(--ink-3);
  text-decoration: none;
}
.toc-mini a::before { content: "§" counter(t); flex: none; }
.toc-mini a:hover,
.toc-mini a[aria-current="true"] { color: var(--ink); }
.toc-mini a[aria-current="true"]::before { color: var(--accent); }

/* Sidenotes: in the right margin on wide screens, inline elsewhere. */

.note {
  margin: 1.25rem 0;
  padding-left: 1rem;
  border-left: 1px solid var(--rule);
  font-size: var(--fs-small);
  line-height: 1.5;
  color: var(--ink-2);
}
.note p { margin: 0; }
.note-label {
  display: block;
  font: var(--fs-meta)/1.5 var(--mono);
  color: var(--ink-3);
}

@media (min-width: 76rem) {
  .note {
    float: right;
    clear: right;
    width: var(--side);
    margin: 0.3rem calc(-1 * (var(--side) + var(--gutter))) 1rem 0;
    padding-left: 0;
    border-left: 0;
  }
}

/* Figures and diagrams */

figure { margin: 2rem 0; }

.diagram pre {
  margin: 0;
  padding: 0.25rem 0;
  border-left: 0;
  font-size: 0.75rem;
  line-height: 1.35;
  color: var(--ink-2);
}

figcaption {
  margin-top: 0.6rem;
  font: var(--fs-meta)/1.5 var(--mono);
  color: var(--ink-3);
}
.prose figcaption::before {
  counter-increment: fig;
  content: "fig. " counter(fig) "  ";
  color: var(--ink-2);
  white-space: pre;
}

/* Decision records inside case notes */

.decision {
  margin: 1.75rem 0;
  padding-left: 1.1rem;
  border-left: 2px solid var(--accent);
}
.decision h3 { margin: 0 0 0.5rem; font-size: var(--fs-body); }
.decision p:last-child { margin-bottom: 0; }
.decision-id {
  margin-right: 0.8ch;
  font: 500 var(--fs-meta)/1 var(--mono);
  color: var(--accent);
}

/* Tables */

.table-scroll { margin: 1.5rem 0; overflow-x: auto; }

table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-small);
  font-variant-numeric: tabular-nums;
}
th {
  padding: 0.4rem 1rem 0.4rem 0;
  border-bottom: 1px solid var(--ink);
  font: 500 var(--fs-meta)/1.4 var(--mono);
  color: var(--ink-3);
  text-align: left;
}
td {
  padding: 0.55rem 1rem 0.55rem 0;
  border-bottom: 1px solid var(--rule);
  vertical-align: top;
}
td code { white-space: nowrap; }

/* Contact */

.big-link {
  font-size: var(--fs-h2);
  line-height: 1.3;
  overflow-wrap: anywhere;
}

.btn {
  padding: 0.15rem 0.55rem;
  border: 1px solid var(--rule);
  border-radius: 2px;
  background: none;
  color: var(--ink-2);
  font: var(--fs-meta)/1.6 var(--mono);
  cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.btn:hover { border-color: var(--ink-3); color: var(--ink); }

.linklike {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-decoration: underline;
  text-decoration-color: var(--rule);
  text-underline-offset: 0.2em;
  cursor: pointer;
}
.linklike:hover { text-decoration-color: var(--accent); }

.muted { color: var(--ink-3); }

/* 404 */

.status-code {
  font: 500 var(--fs-meta)/1.5 var(--mono);
  color: var(--accent);
  margin-bottom: 0.5rem;
}

/* 5. Motion and print ------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; }
}

@media print {
  :root {
    --paper: #fff; --ink: #000; --ink-2: #222; --ink-3: #555;
    --rule: #bbb; --tint: #eee; --accent: #000;
  }
  body { font-size: 11pt; background-image: none; }
  .intro::after, .page-head::after { display: none; }
  .nav, .skip, .toc-mini, .colophon, [data-theme-toggle], [data-copy] { display: none !important; }
  .wrap { max-width: none; padding: 0; }
  a { text-decoration: none; }
  .note { float: none; width: auto; margin: 1rem 0; }
  pre, figure, .decision, .record { break-inside: avoid; }
}
