/* Jamie Brown, portfolio site.
   Two voices: serif for anything written as judgment, mono for anything that is
   an artifact or data. Diff structure appears only where the content has a
   change-of-state shape. Everything else is quiet prose on the same gutter. */

@font-face {
  font-family: 'Source Serif 4';
  src: url(/fonts/source-serif-4-latin-opsz-normal.woff2) format('woff2-variations');
  font-weight: 200 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Source Serif 4';
  src: url(/fonts/source-serif-4-latin-opsz-italic.woff2) format('woff2-variations');
  font-weight: 200 900;
  font-style: italic;
  font-display: swap;
}
@font-face { font-family: 'Commit Mono'; src: url(/fonts/commit-mono-latin-400-normal.woff2) format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: 'Commit Mono'; src: url(/fonts/commit-mono-latin-400-italic.woff2) format('woff2'); font-weight: 400; font-style: italic; font-display: swap; }
@font-face { font-family: 'Commit Mono'; src: url(/fonts/commit-mono-latin-500-normal.woff2) format('woff2'); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: 'Commit Mono'; src: url(/fonts/commit-mono-latin-600-normal.woff2) format('woff2'); font-weight: 600; font-style: normal; font-display: swap; }
/* Held in reserve for the type comparison. Not loaded unless data-mono asks for it. */
@font-face { font-family: 'Departure Mono'; src: url(/fonts/DepartureMono-Regular.woff2) format('woff2'); font-weight: 400; font-style: normal; font-display: swap; }

:root {
  --paper: #ffffff;
  --ink: #000000;
  --ink-2: #6b6b6b;
  --rule: #d9d9d9;
  /* Diff hues. Blue and plum by default, which stay apart for colorblind
     readers. The standard palette swaps in red and green, the way the tools
     do it. Markers carry the meaning on their own, so color is never the only
     channel. */
  --rg-add: #177a38; --rg-add-bg: #eaf6ee; --rg-add-ink: #0f5a2a; --rg-del: #b3261e; --rg-del-bg: #fdeeed; --rg-del-ink: #8a1c15;
  --cb-add: #1b4bd1; --cb-add-bg: #eef3ff; --cb-add-ink: #12379e; --cb-del: #8e2c5a; --cb-del-bg: #fbeff5; --cb-del-ink: #6e1f45;
  --add: var(--cb-add); --add-bg: var(--cb-add-bg); --add-ink: var(--cb-add-ink);
  --del: var(--cb-del); --del-bg: var(--cb-del-bg); --del-ink: var(--cb-del-ink);
  /* Receipt hue. Evidence only: dates, counts, repo links. Always mono, never
     inside a tinted block, so it cannot be read as a diff line. It swaps with
     the palette so it stays separable from add and del either way. */
  --rg-cite: #1b4bd1; --cb-cite: #7a5200;
  --cite: var(--cb-cite);
  --serif: 'Source Serif 4', Charter, 'Iowan Old Style', Georgia, serif;
  --mono: 'Commit Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  --measure: 38rem;
  --gutter: 3rem;
  /* The gutter a page reserves can grow (the CV parks its dates in one), but
     the reading measure and the masthead never do. Sizing reads this, padding
     reads --gutter. */
  --gutter-base: 3rem;
  --edge: 1.25rem;
  --body: 1.125rem;
  --lh: 1.55;
  --line: calc(var(--body) * var(--lh));
  color-scheme: light;
}
[data-palette="standard"] {
  --add: var(--rg-add); --add-bg: var(--rg-add-bg); --add-ink: var(--rg-add-ink);
  --del: var(--rg-del); --del-bg: var(--rg-del-bg); --del-ink: var(--rg-del-ink);
  --cite: var(--rg-cite);
}
[data-mono="departure"] { --mono: 'Departure Mono', ui-monospace, monospace; }

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --paper: #000000;
    --ink: #f2f2f2;
    --ink-2: #9a9a9a;
    --rule: #333333;
    --rg-add: #6fd18a; --rg-add-bg: #0d2616; --rg-add-ink: #8fe0a5; --rg-del: #f28b82; --rg-del-bg: #2c1210; --rg-del-ink: #f5a09a;
    --cb-add: #8aa6ff; --cb-add-bg: #0b1533; --cb-add-ink: #a3b8ff; --cb-del: #e393bf; --cb-del-bg: #2a0f1d; --cb-del-ink: #eaa6cc;
    --rg-cite: #8fb0ff; --cb-cite: #e0a83c;
    color-scheme: dark;
  }
}
[data-theme="dark"] {
  --paper: #000000;
  --ink: #f2f2f2;
  --ink-2: #9a9a9a;
  --rule: #333333;
  --rg-add: #6fd18a; --rg-add-bg: #0d2616; --rg-add-ink: #8fe0a5; --rg-del: #f28b82; --rg-del-bg: #2c1210; --rg-del-ink: #f5a09a;
  --cb-add: #8aa6ff; --cb-add-bg: #0b1533; --cb-add-ink: #a3b8ff; --cb-del: #e393bf; --cb-del-bg: #2a0f1d; --cb-del-ink: #eaa6cc;
  --rg-cite: #8fb0ff; --cb-cite: #e0a83c;
  color-scheme: dark;
}

@media (max-width: 40rem) {
  :root { --gutter: 1.75rem; --edge: 1rem; --body: 1.0625rem; }
  .pr { font-size: 0.8125rem; }
  .files { gap: 0.25rem 0.875rem; }
}

/* Base */
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: var(--body);
  line-height: var(--lh);
  font-optical-sizing: auto;
}
.frame {
  /* The gutter is inside this max-width on purpose: it is what keeps a page
     with a wide gutter starting its text on the same pixel as every other
     page. The children below are what stop it from widening the content. */
  max-width: calc(var(--measure) + var(--gutter) + 2 * var(--edge));
  margin: 0 auto;
  padding: 1.5rem var(--edge) 4rem calc(var(--edge) + var(--gutter));
}
.frame > main, .frame > .colophon { max-width: calc(var(--measure) + var(--gutter-base) + 2 * var(--edge)); }
.frame > .pr { max-width: calc(var(--measure) + 2 * var(--gutter-base) + 2 * var(--edge)); }
main { display: block; }
p { margin: 0 0 1em; }
a { color: inherit; text-decoration: underline; text-underline-offset: 0.15em; text-decoration-thickness: 1px; }
a:hover { text-decoration-thickness: 2px; }
:focus-visible { outline: 2px solid var(--ink); outline-offset: 3px; }
b, strong { font-weight: 600; }
code, kbd, .mono { font-family: var(--mono); font-size: 0.9375em; }
hr { border: 0; border-top: 1px solid var(--rule); margin: 2.5rem 0; }

/* PR header. The site is framed as an open pull request: the name is the
   title, the state marker is availability, the nav is the files-changed list
   with real counts from each page. */
.pr { font-family: var(--mono); margin: 0 0 3rem calc(-1 * min(var(--gutter), var(--gutter-base))); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
.pr-title { font-size: 1.125rem; margin: 0 0 0.35rem; display: flex; align-items: baseline; gap: 0.75rem; flex-wrap: wrap; }
.pr-title .name { font-weight: 600; text-decoration: none; }
/* The mark sits where a PR shows the author's avatar. Height is tied to the
   title type so it scales with the masthead and never widens it. Kept inline
   so the title keeps its text baseline and the state marker stays level. */
.pr-title .mark { height: 1.5em; width: auto; vertical-align: -0.4em; margin-right: 0.5rem; }
.pr-title .name:hover { text-decoration: underline; }
.state { font-size: 0.8125rem; font-weight: 600; white-space: nowrap; }
.state::before { content: ''; display: inline-block; width: 0.6em; height: 0.6em; border-radius: 50%; background: currentColor; margin-right: 0.4em; vertical-align: 0.05em; }
.state.open { color: var(--add); }
.state.resolved { color: var(--ink-2); font-weight: 400; font-family: var(--mono); font-size: 0.75rem; margin-left: 0.75em; vertical-align: 0.15em; }
.state.resolved::before { background: var(--ink-2); }
.pr-line { font-size: 0.875rem; color: var(--ink-2); margin: 0 0 1rem; }
.files { display: flex; flex-wrap: wrap; gap: 0.25rem 1.1rem; font-size: 0.8125rem; line-height: 1.6; padding-top: 0.75rem; border-top: 1px solid var(--rule); }
.files a { text-decoration: none; color: var(--ink-2); white-space: nowrap; }
.files a:hover { color: var(--ink); text-decoration: underline; }
.files [aria-current="page"] { color: var(--ink); font-weight: 600; white-space: nowrap; }
.files .stat { font-weight: 400; font-size: 0.8125rem; }
/* The one nav item that is about drawing gets the pixel face. Departure Mono
   runs small, so it needs the size bump to sit on the same line as the rest. */
.files .art { font-family: 'Departure Mono', var(--mono); font-size: 0.9375rem; letter-spacing: 0.04em; }
.files .art .stat { font-family: var(--mono); font-size: 0.8125rem; letter-spacing: 0; }
[data-mono="departure"] .files .art { font-family: 'Commit Mono', ui-monospace, monospace; font-size: 0.8125rem; letter-spacing: 0; }
.stat .plus { color: var(--add); }
.stat .minus { color: var(--del); }

/* Headings. Page titles read like a commit subject line. Section heads stay near body size. */
h1 {
  font-family: var(--mono);
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.4;
  margin: 0 0 1.5rem;
}
h2 {
  font-family: var(--serif);
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.35;
  margin: 3rem 0 1rem;
}
h3 { font-size: 1.0625rem; font-weight: 600; margin: 2rem 0 0.75rem; }

/* Lists in prose */
ol, ul { padding-left: 1.5rem; margin: 0 0 1em; }
li { margin-bottom: 0.5em; }
.model { padding-left: 0; list-style: none; counter-reset: model; }
.model li { counter-increment: model; margin-bottom: 1em; }
.model li::before {
  content: counter(model);
  font-family: var(--mono);
  font-size: 0.875rem;
  color: var(--ink-2);
  position: absolute;
  margin-left: calc(-1 * var(--gutter));
  width: var(--gutter);
  text-align: center;
  line-height: var(--line);
}

/* Diff structure.
   A hunk is a section whose content has before-and-after shape. Blocks inside it
   carry one of four roles: context (unchanged), add, del, or note (a review
   comment, in the margin). Markers live in the gutter and carry the meaning on
   their own. Color is the second channel, never the only one. */
.hunk-head {
  margin: 3.5rem 0 1rem;
}
.hunk-head .range {
  font-family: var(--mono);
  font-weight: 400;
  font-size: 0.8125rem;
  color: var(--ink-2);
  white-space: nowrap;
  margin-right: 0.75em;
}
.ctx, .add, .del, .mod, .note {
  position: relative;
  margin-left: calc(-1 * (var(--gutter) + var(--edge)));
  padding-left: calc(var(--gutter) + var(--edge));
}
.add, .del { padding-top: 0.5rem; padding-bottom: 0.5rem; padding-right: var(--edge); margin-right: calc(-1 * var(--edge)); margin-bottom: 1em; }
.add > :last-child, .del > :last-child, .note > :last-child { margin-bottom: 0; }
.add { background: var(--add-bg); color: var(--add-ink); }
.del { background: var(--del-bg); color: var(--del-ink); }
.add::before, .del::before, .mod::before {
  position: absolute;
  left: var(--edge);
  top: 0.5rem;
  width: var(--gutter);
  text-align: center;
  font-family: var(--mono);
  font-size: 1rem;
  line-height: var(--line);
  font-weight: 600;
}
.add::before { content: '+'; color: var(--add); }
.del::before { content: '-'; color: var(--del); }

/* A modified line. What changed is a word, not the line, so the marker is ~ and
   the strike lands only on the part that went. This is what a word-level diff
   shows, and it is the honest form when the practice stayed and only the
   restriction on it was dropped. */
.mod::before { content: '~'; color: var(--ink-2); font-weight: 400; }
del {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--del);
  color: var(--del-ink);
  background: var(--del-bg);
}
/* An inline addition: a phrase that entered the working model after it was
   first written. Same hue rule as an added line, no underline, since the
   tint already says what it is. */
ins {
  text-decoration: none;
  color: var(--add-ink);
  background: var(--add-bg);
  padding: 0 0.15em;
}
ins b, .model ins { font-weight: inherit; }

/* A removed line is struck, the way a prose diff marks one. The strike lands on
   the thing that was removed and not on the reasoning for removing it, so the
   audit's why column stays readable while its tool name reads as struck. */
.lines > .del:not(.row), .rows .row.del dt {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--del);
}

/* A note is a review comment: the human layer over the artifact. It reads as
   marginalia, with a rule in the gutter rather than a card. */
.note { margin-top: 1.25em; margin-bottom: 1.25em; }
.note::before {
  content: '';
  position: absolute;
  left: calc(var(--edge) + var(--gutter) / 2 - 1.5px);
  top: 0.15em;
  bottom: 0.15em;
  width: 3px;
  background: var(--cite);
}
.note blockquote {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.9375rem;
  line-height: 1.55;
}
.note .who {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--cite);
  margin: 0.5em 0 0;
}

/* Meta text: dates, counts, provenance. Always mono, always quiet. */
.meta { font-family: var(--mono); font-size: 0.8125rem; color: var(--ink-2); }
.meta b, .meta .n { color: var(--cite); font-weight: 600; }

/* Role labels. A case study has diff shape: the context, the human note, the
   result. The label names which one you are about to read, in the machine
   voice, so the paragraph under it can stay plain prose. It replaces the bold
   run-in that made every block open the same way. */
.role {
  font-family: var(--mono);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.6;
  color: var(--ink-2);
  margin: 1.75rem 0 0.35rem;
}
.role::before { content: '// '; color: var(--ink-2); }
.add > .role { color: var(--add); margin-top: 0; }
.del > .role { color: var(--del); margin-top: 0; }
.add > .role::before, .del > .role::before { content: none; }

/* Receipts. Every number on this site traces to something you can go check, so
   the figures get the cite hue and the words around them stay quiet. Mono, and
   never inside a tinted block, so a receipt cannot be misread as a diff line. */
.receipts {
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 1.7;
  color: var(--ink-2);
  list-style: none;
  padding: 0;
  margin: 0 0 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.5rem;
}
.receipts li { margin: 0; }
.receipts .n { color: var(--cite); font-weight: 600; white-space: nowrap; }
.receipts.stack { display: block; max-width: 32rem; }
.receipts.stack li { margin-bottom: 0.15em; }
.hunk-head + .receipts { margin-top: -0.5rem; }

/* Key clause. One phrase a paragraph at most, and only the load-bearing one.
   It is the same ink, heavier, so a reader skimming the page still lands on
   the sentence that carries the point. */
.prose-key { font-weight: 600; }

/* Colophon: the last receipt on every page */
.colophon {
  margin-top: 5rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--ink-2);
}
.colophon p { margin: 0 0 0.5em; max-width: 34rem; }
.colophon .checks { color: var(--ink); }
.colophon .signature { display: block; height: 3rem; width: auto; margin-top: 1.25rem; color: var(--ink); }
/* 404 gets the faint version, the way a watermark sits under a blank page. */
.ghost { display: block; width: min(100%, 22rem); height: auto; margin: 2rem 0 0; color: var(--ink); opacity: 0.09; }

/* Display controls. Fixed in the corner so they are reachable from anywhere
   on a long page. Text labels say what pressing does. */
.controls {
  position: fixed;
  right: max(0.75rem, env(safe-area-inset-right));
  bottom: max(0.75rem, env(safe-area-inset-bottom));
  display: flex;
  gap: 0.5rem;
  z-index: 10;
}
.controls button {
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.4;
  color: var(--ink-2);
  background: var(--paper);
  border: 1px solid var(--rule);
  padding: 0.4em 0.75em;
  cursor: pointer;
}
.controls button:hover { color: var(--ink); border-color: var(--ink); }
.controls button[aria-pressed="true"] { color: var(--paper); background: var(--ink); border-color: var(--ink); }
@media (max-width: 40rem) { .controls .more { display: none; } }

/* Consecutive lines of a hunk form one block, each changed line with its own marker. */
.lines > .add, .lines > .del, .lines > .ctx, .lines > .mod { margin-bottom: 0; padding-top: 0.3rem; padding-bottom: 0.3rem; }
.lines > :first-child { padding-top: 0.6rem; }
.lines > :last-child { padding-bottom: 0.6rem; margin-bottom: 1.5rem; }
.lines > .add::before, .lines > .del::before, .lines > .mod::before { top: 0.3rem; }
.lines > .add:first-child::before, .lines > .del:first-child::before, .lines > .mod:first-child::before { top: 0.6rem; }

@media print {
  :root { --paper: #fff; --ink: #000; --ink-2: #444; --rule: #999; --add-bg: #eef; --del-bg: #fee; --cite: #14349b; }
  .files, .controls, .share button { display: none; }
  .frame { padding: 0 0 0 var(--gutter); max-width: none; }
  .frame > main, .frame > .colophon, .frame > .pr { max-width: none; }
  a { text-decoration: none; }
  * { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}

/* Home: the five-line hunk */
.hero .hunk-head { margin: 0 0 0.5rem; }
.links { font-family: var(--mono); font-size: 0.875rem; line-height: 1.8; margin: 0 0 3rem; }
.links a { margin-right: 1.25rem; white-space: nowrap; }
.catalog { max-width: 30rem; }

/* Audit rows. Each row is one line of the diff. */
.rows { margin: 0 0 2rem; }
.rows .row { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.rows .row::before { top: 0.75rem; }
.rows .row.ctx { position: relative; margin-left: calc(-1 * (var(--gutter) + var(--edge))); padding-left: calc(var(--gutter) + var(--edge)); }
.rows .row.ctx + .row.ctx { border-top: 1px solid var(--rule); }
.rows dt { font-family: var(--mono); font-size: 0.9375rem; font-weight: 600; margin: 0 0 0.25rem; }
.rows dd { margin: 0; }
.rows .when { display: block; font-family: var(--mono); font-size: 0.8125rem; color: var(--ink-2); margin-bottom: 0.25rem; }

/* An unresolved thread: a question the record has not closed. */
.unresolved { position: relative; margin-left: calc(-1 * (var(--gutter) + var(--edge))); padding-left: calc(var(--gutter) + var(--edge)); margin-top: 1.5em; }
.unresolved::before {
  content: 'open';
  position: absolute;
  left: var(--edge);
  top: 0.2em;
  width: var(--gutter);
  text-align: center;
  font-family: var(--mono);
  font-size: 0.75rem;
  line-height: 1.6;
  color: var(--ink-2);
}
.unresolved::after {
  content: '';
  position: absolute;
  left: calc(var(--edge) + var(--gutter) / 2 - 1px);
  top: 1.7em;
  bottom: 0.2em;
  border-left: 2px dotted var(--ink-2);
}

/* Projects: each repo is a file in the change set, with a diff-style file header. */
.file { margin: 0 0 3rem; }
.file-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 1rem; border-top: 1px solid var(--rule); padding-top: 0.75rem; margin: 0 0 0.25rem; }
.file-head h2 { font-family: var(--mono); font-size: 1.125rem; margin: 0; }
.file-head h2.serif { font-family: var(--serif); font-size: 1.25rem; }
.file-head .stat { font-family: var(--mono); font-size: 0.8125rem; color: var(--ink-2); }
.file .meta { margin-bottom: 0.75rem; }
.file .meta a { margin-right: 1rem; }

/* Documentation inside a file: a data-flow diagram built from the same facts
   as the copy. Nodes are mono boxes, arrows are glyphs, rows wrap on a phone. */
.doc-h { font-family: var(--mono); font-size: 0.8125rem; font-weight: 600; color: var(--ink-2); margin: 1.25rem 0 0.5rem; }
.flow { margin: 0 0 1rem; }
.flow ol { list-style: none; padding: 0; margin: 0; display: flex; flex-wrap: wrap; align-items: stretch; gap: 0.5rem 0; }
.flow li { display: flex; align-items: stretch; }
.flow li:not(:last-child)::after { content: '\2192'; font-family: var(--mono); font-size: 0.9375rem; color: var(--cite); padding: 0 0.5rem; display: flex; align-items: center; }
.flow .box { display: block; box-sizing: border-box; height: 100%; border: 1px solid var(--rule); border-left: 2px solid var(--ink); padding: 0.35rem 0.6rem; font-family: var(--mono); font-size: 0.8125rem; line-height: 1.4; max-width: 13rem; }
.flow .box b { font-weight: 600; }
.flow .box small { display: block; font-size: 0.75rem; color: var(--ink-2); }
.verification { font-family: var(--mono); font-size: 0.8125rem; line-height: 1.7; color: var(--ink-2); margin: 0 0 1rem; }
.verification .n { color: var(--cite); font-weight: 600; }

/* CV: experience is a commit log on a rail. Dates sit in a wider gutter on
   desktop and above the subject line on a phone. */
body.cv { --rail: 1rem; }
/* The date gutter only earns its place once the frame can still centre with
   it. Below that the CV would stop centring and inset its content instead,
   which is what made it the one page that did not line up when you click
   through. 68rem is measure + gutter + edges, the width where it just fits. */
@media (min-width: 68rem) { body.cv { --gutter: 12.5rem; } }
@media (max-width: 40rem) { body.cv { --rail: 0.875rem; } }
.cv h2 { margin-top: 3.5rem; }
.cv h3 { font-size: 1.125rem; margin: 2.25rem 0 0.25rem; }
.cv h3 + .meta { margin-bottom: 0.75rem; }
.commits { position: relative; }
.commits::before { content: ''; position: absolute; top: 0.6em; bottom: 0; left: calc(-1 * var(--rail) - 1px); border-left: 2px solid var(--rule); }
.commit { position: relative; margin: 0 0 2.5rem; }
.commit::before { content: ''; position: absolute; left: calc(-1 * var(--rail) - 5px); top: 0.5em; width: 10px; height: 10px; border-radius: 50%; background: var(--ink); }
.commit.head::before { background: var(--add); }
.commit h3 { margin: 0 0 0.15rem; }
.commit .when { font-family: var(--mono); font-size: 0.8125rem; color: var(--cite); margin: 0 0 0.25rem; }
.commit + .commit { border-top: 1px solid var(--rule); padding-top: 1.5rem; }
.commit.head h3 { color: var(--add-ink); }
.commit .meta { margin-bottom: 0.75rem; }
@media (min-width: 68rem) {
  .commit .when { position: absolute; left: calc(-1 * var(--gutter)); width: calc(var(--gutter) - var(--rail) - 1rem); text-align: right; top: 0.25em; margin: 0; }
}
.cv ul { padding-left: 1.25rem; }
.cv li { margin-bottom: 0.4em; }
.cv table { border-collapse: collapse; width: 100%; font-size: 0.9375rem; margin: 0 0 1.5rem; }
.cv th, .cv td { text-align: left; vertical-align: top; padding: 0.4rem 0.75rem 0.4rem 0; border-top: 1px solid var(--rule); }
.cv th { font-family: var(--mono); font-weight: 600; font-size: 0.8125rem; border-top: 0; }
.download { font-family: var(--mono); font-size: 0.875rem; margin: 0 0 2.5rem; }
.download a { border: 1px solid var(--ink); padding: 0.35em 0.75em; text-decoration: none; }
.download a:hover { background: var(--ink); color: var(--paper); }
.contact { font-family: var(--mono); font-size: 0.9375rem; line-height: 1.8; margin: 0 0 1.5rem; list-style: none; padding: 0; }

/* Editor-style folds. A summary line with a fold marker, and the lines under
   it hidden until asked for. The build appends the hidden line count. Printing
   opens every fold. */
details.fold { margin: 0.35rem 0 0.9rem; }
details.fold > summary {
  cursor: pointer;
  list-style: none;
  font-size: 1rem;
  line-height: 1.5;
  padding: 0.15rem 0;
}
details.fold > summary::-webkit-details-marker { display: none; }
details.fold > summary::before {
  content: '\25B8';
  font-family: var(--mono);
  color: var(--cite);
  display: inline-block;
  width: 1.1em;
}
details.fold[open] > summary::before { content: '\25BE'; }
details.fold > summary:hover { text-decoration: underline; text-underline-offset: 0.15em; }
details.fold > summary .fold-n { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-2); margin-left: 0.6em; white-space: nowrap; }
details.fold[open] > summary .fold-n { visibility: hidden; }
details.fold > :not(summary) { margin-left: 1.1em; }
details.fold > ul, details.fold > ol { margin-top: 0.4rem; }
details.fold > p:first-of-type { margin-top: 0.4rem; }

/* Skills as tags, grouped by tier. Same box vocabulary as the data-flow
   diagrams, so a skill reads as a node rather than a keyword in a paragraph. */
.tags { margin: 0 0 1.5rem; }
.tags dt { font-family: var(--mono); font-size: 0.75rem; font-weight: 600; color: var(--ink-2); margin: 1rem 0 0.4rem; }
.tags dt::before { content: '// '; }
.tags dd { margin: 0; }
.tags .tier { font-size: 0.9375rem; color: var(--ink-2); margin: 0 0 0.5rem; }
.tag {
  display: inline-block;
  border: 1px solid var(--rule);
  border-left: 2px solid var(--ink);
  padding: 0.12rem 0.5rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  line-height: 1.5;
  margin: 0 0.35rem 0.4rem 0;
}
.tag.deep { border-left-color: var(--cite); font-weight: 600; }
.tag small { color: var(--ink-2); font-size: 0.75rem; margin-left: 0.35em; }
.cv .personal h3::after { content: 'personal work'; font-family: var(--mono); font-size: 0.75rem; font-weight: 400; color: var(--cite); margin-left: 0.75em; vertical-align: 0.15em; }

/* Author line under the PR title. */
.pr-author { font-size: 0.8125rem; color: var(--ink-2); margin: 0 0 1rem; }
.pr-author a { text-decoration: none; color: var(--ink-2); margin-right: 1rem; white-space: nowrap; }
.pr-author a:hover { color: var(--ink); text-decoration: underline; }

/* Blame on the working model: which items changed since the model was
   written, and when. In the right margin on a wide screen, on its own line
   otherwise. */
.model li { position: relative; }
.model li[data-blame]::after { content: attr(data-blame); font-family: var(--mono); font-size: 0.75rem; color: var(--cite); }
@media (min-width: 64rem) { .model li[data-blame]::after { position: absolute; left: calc(100% + 1.5rem); top: 0.35em; white-space: nowrap; } }
@media (max-width: 63.99rem) { .model li[data-blame]::after { display: block; margin-top: 0.25rem; } }

/* A view toggle on a hunk: unified or split. Desktop only. */
.view-toggle { font-family: var(--mono); font-size: 0.75rem; margin-left: 1rem; }
.view-toggle button { font: inherit; color: var(--ink-2); background: none; border: 1px solid var(--rule); padding: 0.1em 0.5em; cursor: pointer; }
.view-toggle button:hover { color: var(--ink); border-color: var(--ink); }
.view-toggle button[aria-pressed="true"] { color: var(--paper); background: var(--ink); border-color: var(--ink); }

/* Split view: old on the left, new on the right, rows aligned. It breaks out
   of the reading column because two columns need the width. */
.lines.split { position: relative; left: 50%; transform: translateX(-50%); width: min(96vw, 64rem); margin: 0 0 1.5rem; font-size: 1rem; }
.lines.split .row { display: grid; grid-template-columns: 1fr 1fr; gap: 0 2px; }
.lines.split .row + .row { margin-top: 2px; }
/* Cells reset the unified line rules (gutter margins, marker box) that the
   shared role classes carry, so each cell stays inside its grid column. */
.lines.split .cell { position: relative; margin: 0; padding: 0.3rem 0.75rem 0.3rem 1.75rem; min-height: 1.5em; min-width: 0; overflow-wrap: anywhere; }
.lines.split .cell::before { position: absolute; left: 0.6rem; top: 0.3rem; width: auto; text-align: left; line-height: inherit; font-family: var(--mono); font-size: 1rem; font-weight: 600; }
.lines.split .cell.add { background: var(--add-bg); color: var(--add-ink); }
.lines.split .cell.add::before { content: '+'; color: var(--add); }
.lines.split .cell.del { background: var(--del-bg); color: var(--del-ink); text-decoration: line-through; text-decoration-thickness: 1px; text-decoration-color: var(--del); }
.lines.split .cell.del::before { content: '-'; color: var(--del); }
.lines.split .cell.del.mod { text-decoration: none; }
.lines.split .cell.empty { background: repeating-linear-gradient(45deg, transparent 0 6px, var(--rule) 6px 7px); }
@media (max-width: 64rem) {
  .view-toggle { display: none; }
  .lines.split { display: none !important; }
  .lines.unified { display: block !important; }
}

/* CV year ruler: one bar per commit, each a link to its entry. Bars are
   positioned by date as a percentage of 2009 to now. */
.ruler { margin: 0 0 2.5rem; font-family: var(--mono); font-size: 0.75rem; }
.ruler ol { list-style: none; margin: 0; padding: 0; }
.ruler-rows li { position: relative; height: 1.45rem; margin: 0; }
.ruler-rows a { position: absolute; left: var(--from); width: calc(var(--to) - var(--from)); min-width: 3px; top: 0.2rem; height: 1rem; background: var(--add-bg); border-left: 2px solid var(--ink); text-decoration: none; color: var(--ink); }
.ruler-rows a span { position: absolute; left: 100%; top: 0; line-height: 1rem; padding-left: 0.4rem; white-space: nowrap; }
.ruler-rows a.late span { left: auto; right: 100%; padding-left: 0; padding-right: 0.4rem; }
.ruler-rows a:hover { background: var(--add); }
.ruler-rows a:hover span { text-decoration: underline; }
.ruler-axis { position: relative; height: 1.5rem; border-top: 1px solid var(--rule); margin-top: 0.35rem; }
.ruler-axis li { position: absolute; left: var(--at); top: 0.3rem; transform: translateX(-50%); color: var(--ink-2); margin: 0; }
.ruler-axis li::before { content: ''; position: absolute; left: 50%; top: -0.55rem; height: 0.4rem; border-left: 1px solid var(--rule); }
.ruler-axis li.now { color: var(--add); transform: translateX(-100%); }
.ruler-axis li.now::before { left: 100%; border-color: var(--add); }
/* The last year before "now" runs into it on a phone. The bars carry the
   dates anyway, and every entry below states its own. */
@media (max-width: 40rem) { .ruler-axis li.tight { display: none; } }
.commit { scroll-margin-top: 1rem; }

/* An artifact: a screenshot of the running thing, captioned in the machine voice. */
.artifact { margin: 1rem 0 1.5rem; }
.artifact img { display: block; max-width: 100%; height: auto; border: 1px solid var(--rule); }
.artifact figcaption { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-2); margin-top: 0.4rem; }

/* The share block under a comic: the date it went up, then the permalink and
   the button. The permalink is the feature: a page of its own whose card is
   the drawing, so pasting it anywhere that unfurls a URL shows the comic and
   not the masthead. The button is revealed by the script and opens the share
   sheet, or copies the link where there is none. A blank line sits between
   the caption and the date, so the machine voice keeps the line to itself. */
.share { display: block; margin-top: 2em; }
.share time { display: block; }
.share a { text-decoration: none; }
.share a:hover { text-decoration: underline; }
.share button { font: inherit; color: var(--ink-2); background: none; border: 1px solid var(--rule); padding: 0.1em 0.5em; margin-left: 0.5rem; cursor: pointer; }
.share button:hover { color: var(--ink); border-color: var(--ink); }
.share-nav { font-family: var(--mono); font-size: 0.75rem; color: var(--ink-2); display: flex; gap: 1.5rem; margin: 0 0 1.5rem; }
