/* joncphillips.com
   Set like a printed sheet, after preflight.sh and cronwatch.dev: soleil for
   everything a person reads, Auger Mono for labels and anything a machine
   printed, and DIN Condensed, black and tight, for the big titles. Neutral
   greys, with the site's yellow as the one accent (the hover on a title, the
   underline under a link, the bar beside a quote). Dark by default; the
   footer's toggle turns the sheet over to light. The faces come from the
   Adobe Fonts kit nst1rrf. */

/* Until soleil arrives, Arial (Liberation Sans on Linux, the same widths)
   set to soleil's width and height, so the text keeps its lines and nothing
   below it moves when soleil swaps in. */
@font-face { font-family: "soleil-fallback"; font-weight: 100 599; src: local("Arial"), local("ArialMT"), local("Liberation Sans"), local("Arimo"); size-adjust: 102.8%; ascent-override: 105%; descent-override: 32%; line-gap-override: 0%; }
@font-face { font-family: "soleil-fallback"; font-weight: 600 900; src: local("Arial Bold"), local("Arial-BoldMT"), local("Liberation Sans Bold"), local("Arimo Bold"); size-adjust: 99.9%; ascent-override: 113%; descent-override: 32%; line-gap-override: 0%; }

/* The yellow is too pale to read on white, so where it has to be seen on
   its own (a number, a focus ring) the light sheet uses a deeper amber. */
:root {
  --paper: #f4f4f5;
  --sheet: #ffffff;
  --sunk: #fafafa;
  --rule: #e4e4e7;
  --rule-2: #d4d4d8;
  --tick: #909098;
  --faint: #71717a;
  --ink: #000000;
  --body: #18181b;
  --muted: #71717a;
  --accent: #fbbf24;
  --accent-deep: #f59e0b;
  --accent-ink: #000000;
  --accent-strong: #b45309;
  --danger: #dc2626;
  --success: #15803d;
  --code-tag: #c2410c;
  --code-number: #a16207;
  --code-name: #7c3aed;
  --code-keyword: #be185d;
  --code-string: #15803d;
  --radius-s: 4px;
  --radius: 6px;
  --radius-l: 10px;
  --sans: "soleil", "soleil-fallback", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: "auger-mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --cond: "ff-din-paneuropean-cond", "Arial Narrow", "Helvetica Neue", sans-serif;
  --gutter: clamp(20px, 4vw, 56px);
  color-scheme: light;
}
:root[data-theme="dark"] {
  --paper: #09090b;
  --sheet: #111113;
  --sunk: #18181b;
  --rule: #27272a;
  --rule-2: #3f3f46;
  --tick: #66666f;
  --faint: #85858f;
  --ink: #ffffff;
  --body: #e4e4e7;
  --muted: #a1a1aa;
  --accent: #fbbf24;
  --accent-deep: #fde047;
  --accent-strong: #fbbf24;
  --danger: #f87171;
  --success: #4ade80;
  --code-tag: #f0997b;
  --code-number: #fbbf24;
  --code-name: #c4b5fd;
  --code-keyword: #f9a8d4;
  --code-string: #86efac;
  color-scheme: dark;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
/* hidden first, for a browser that doesn't know clip yet. */
html, body { overflow-x: hidden; overflow-x: clip; }
body { margin: 0; background: var(--paper); color: var(--ink); font: 400 1.0625rem/1.62 var(--sans); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
a { color: var(--ink); text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .16em; text-decoration-color: var(--rule-2); }
a:hover { text-decoration-color: var(--accent); }
:focus-visible { outline: 2px solid var(--accent-strong); outline-offset: 2px; }
::selection { background: var(--accent); color: var(--accent-ink); }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
code, pre, kbd { font-family: var(--mono); }
h1, h2, h3, h4, h5 { margin: 0; font-weight: 600; }
p, figure, blockquote { margin: 0; }
/* No word left alone on the last line of a paragraph. */
p, li, figcaption, blockquote { text-wrap: pretty; }
img { max-width: 100%; height: auto; }
.icon { width: 1em; height: 1em; display: inline-block; vertical-align: -.125em; flex: none; }
.skip { position: absolute; left: -999px; top: 8px; background: var(--ink); color: var(--sheet); padding: 8px 12px; }
.skip:focus { left: 8px; z-index: 30; }
.vh { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
.label, .foot-head { font: 500 .71875rem/1.5 var(--mono); letter-spacing: .08625rem; text-transform: uppercase; color: var(--muted); }

/* The sheet. */
.page { max-width: 1180px; min-height: 100vh; margin: 0 auto; background: var(--sheet); border-left: 1px solid var(--rule); border-right: 1px solid var(--rule); padding: 0 var(--gutter); display: flex; flex-direction: column; }
.page > main { flex: 1 0 auto; }

/* Top: links left, the name right, the way a masthead sits. */
.top { display: flex; align-items: center; justify-content: space-between; gap: 20px; padding: 24px 0 0; }
.links { display: flex; flex-wrap: wrap; align-items: center; gap: 22px; font-size: 1rem; }
/* The yellow line grows out from the middle of a link under the pointer,
   and stays under the page you're on. */
.links a { position: relative; color: var(--ink); text-decoration: none; }
.links a::after { content: ""; position: absolute; left: 50%; bottom: -6px; width: 0; height: 2px; background: var(--accent); transform: translateX(-50%); opacity: 0; transition: width .22s cubic-bezier(.4, 0, .2, 1), opacity .22s cubic-bezier(.4, 0, .2, 1); }
.links a:hover::after, .links a.here::after { width: 100%; opacity: 1; }
.brand { font: 700 .875rem/1 var(--sans); letter-spacing: .21875rem; text-transform: uppercase; color: var(--ink); text-decoration: none; white-space: nowrap; }
.brand:hover { color: var(--muted); }
.menu { display: none; }

/* The yellow button does the thing. */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 9px; font: 500 .875rem/1 var(--mono); border-radius: var(--radius); padding: 12px 16px; text-decoration: none; white-space: nowrap; color: var(--accent-ink); background: var(--accent); border: 1px solid var(--accent); }
.btn:hover { background: var(--accent-deep); border-color: var(--accent-deep); }

/* The opening of every page: the title set big in DIN, the intro, and the
   ways to follow along. */
.opening { padding: 64px 0 56px; }
.title { font: 900 clamp(3rem, 10vw, 7rem)/.85 var(--cond); letter-spacing: clamp(-0.175rem, -0.25vw, -0.075rem); color: var(--ink); margin: 18px 0 34px clamp(-0.28rem, -0.4vw, -0.12rem); text-wrap: balance; }
.title.article { font-size: clamp(2.8rem, 9vw, 6.6rem); letter-spacing: clamp(-0.165rem, -0.225vw, -0.07rem); margin-left: clamp(-0.264rem, -0.36vw, -0.112rem); }
/* The home page opens the way preflight.sh does: one statement, set large,
   with the name in bold, and a paragraph under it. */
.opening.home { padding-top: clamp(48px, 7vw, 84px); }
.opening .say { font: 400 clamp(1.75rem, 3.8vw, 2.9rem)/1.2 var(--sans); letter-spacing: clamp(-0.087rem, -0.114vw, -0.0525rem); color: var(--ink); max-width: clamp(35rem, 76vw, 58rem); text-wrap: pretty; }
.opening .say b { font-weight: 800; }
.opening .then { margin-top: 30px; font-size: 1.125rem; line-height: 1.75; color: var(--ink); max-width: 68ch; text-wrap: pretty; }
.opening .then a { text-decoration-color: var(--accent); text-decoration-thickness: 2px; text-underline-offset: .2025rem; }
.opening .then a:hover { text-decoration-color: var(--ink); }
.subhead { font-size: clamp(1.4rem, 3vw, 2.2rem); line-height: 1.3; font-weight: 300; color: var(--muted); margin: -14px 0 32px; text-wrap: pretty; }
.intro { font-size: clamp(1.1rem, 2vw, 1.55rem); line-height: 1.55; font-weight: 400; color: var(--ink); max-width: 58.75rem; text-wrap: pretty; }
/* An article's intro runs the full width of the sheet. */
.opening.article .intro { max-width: none; }
.intro p + p { margin-top: clamp(1.1rem, 2vw, 1.55rem); }
.intro a, .rich-text a, .figure figcaption a { text-decoration-color: var(--accent); text-decoration-thickness: 2px; }
.intro a { text-underline-offset: clamp(.198rem, .36vw, .279rem); }
.rich-text a { text-underline-offset: clamp(.189rem, .288vw, .216rem); }
.figure figcaption a { text-underline-offset: .140625rem; }
.intro a:hover, .rich-text a:hover, .figure figcaption a:hover { text-decoration-color: var(--ink); }
.intro a { font-weight: 400; }
.social { display: flex; gap: 20px; margin-top: 28px; font-size: 1.125rem; }
.social a { color: var(--muted); text-decoration: none; }
.social a:hover { color: var(--ink); }
.byline { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; margin-top: 36px; font: 400 .75rem/1.4 var(--mono); color: var(--muted); }
.byline img { width: 26px; height: 26px; border-radius: 50%; }
.byline a.name { font: 600 .71875rem/1 var(--sans); letter-spacing: .14375rem; text-transform: uppercase; color: var(--ink); text-decoration: none; }
.byline a.name:hover { color: var(--muted); }

/* A list that runs the full width of the sheet, edge to edge: the articles
   on the home page, the projects on the work page. Each line goes yellow
   under the pointer. */
.bleed { margin: 0 calc(-1 * var(--gutter)); border-top: 1px solid var(--rule); }
.bleed > * { border-bottom: 1px solid var(--rule); }
.list-head { display: flex; justify-content: space-between; align-items: baseline; gap: 16px; padding: 14px var(--gutter); }
/* The articles page's year headings (and the work page's groups) sit on a
   slightly different shade, and stay at the top of the window while their
   list scrolls under them. */
.list-head.year { position: sticky; top: 0; z-index: 3; background: var(--sunk); }
.list-head.year h2.label { color: var(--ink); }
a.more { display: inline-flex; align-items: center; gap: 8px; color: var(--ink); text-decoration: none; }
a.more:hover { color: var(--muted); }
.entry { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 36px; padding: 26px var(--gutter); color: var(--ink); text-decoration: none; }
/* The date, set like a calendar leaf: the month in small mono over the day in DIN. */
.leaf { display: flex; flex-direction: column; align-items: center; line-height: 1; }
.leaf .month { font: 500 .8125rem/1 var(--mono); letter-spacing: .075rem; text-transform: uppercase; color: var(--ink); }
.leaf .day { font: 800 1.75rem/1 var(--cond); margin-top: 5px; }
.entry .leaf { width: 4.75rem; padding: 4px 1.5rem 4px 0; border-right: 1px solid var(--rule-2); }
.entry:hover .leaf, .entry:focus-visible .leaf { border-right-color: rgb(0 0 0 / .25); }
.entry:hover .leaf .month, .entry:focus-visible .leaf .month { color: var(--accent-ink); }
.entry h2 { font: 400 clamp(1.2rem, 2.8vw, 1.95rem)/1.2 var(--sans); letter-spacing: clamp(-0.0195rem, -0.028vw, -0.012rem); text-wrap: pretty; }
.entry .go { font-size: 1.375rem; opacity: 0; transition: opacity .15s; }
.entry:hover, .entry:focus-visible { background: var(--accent); color: var(--accent-ink); outline: none; }
.entry:hover .go, .entry:focus-visible .go { opacity: 1; }

/* An article's picture runs the full width of the sheet, edge to edge. */
.hero { margin: 0 calc(-1 * var(--gutter)); }
.hero img { display: block; width: 100%; }
.hero + .row { border-top: 0; padding-top: clamp(40px, 5vw, 64px); }
/* The about page's photo sits to the right of the intro, cropped to a
   circle. */
.with-portrait { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
.portrait { grid-column: 2; grid-row: 1; width: clamp(14rem, 24vw, 19rem); aspect-ratio: 1; margin: 1.5rem calc(-1 * var(--gutter) - 3rem) 0 0; }
.portrait img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: 48% 30%; border-radius: 50%; }

/* Rows: a small label down the left, the thing itself on the right. */
.row { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 40px; align-items: start; padding: clamp(48px, 7vw, 88px) 0; border-top: 1px solid var(--rule); }
.row > .label { padding-top: 8px; }
/* An article's share bar sits at the right of its column, close to the
   text, the icons centred under the label. */
.row > .side { position: sticky; top: 24px; justify-self: end; display: flex; flex-direction: column; align-items: center; padding-top: 5px; }
.share { display: flex; flex-direction: column; align-items: center; gap: 4px; margin-top: 14px; }
.share a { display: inline-flex; padding: 6px 0; color: var(--muted); font-size: 1.0625rem; }
.share a:hover { color: var(--ink); }
.row .body { min-width: 0; }
.row .body.form-body { max-width: 44rem; }

/* The article itself. */
.rich-text { max-width: 40rem; }
.article-content > * + * { margin-top: 0; }
.rich-text p { font-size: clamp(1.05rem, 1.6vw, 1.2rem); line-height: 1.6; font-weight: 400; color: var(--body); padding-bottom: clamp(1.47rem, 2.24vw, 1.68rem); text-wrap: pretty; }
.rich-text strong { font-weight: 600; color: var(--ink); }
.rich-text em { font-style: italic; }
.rich-text a { font-weight: 500; color: var(--ink); }
.rich-text ul, .rich-text ol { margin: 0 0 clamp(1.47rem, 2.24vw, 1.68rem); padding-left: clamp(1.47rem, 2.24vw, 1.68rem); font-size: clamp(1.05rem, 1.6vw, 1.2rem); line-height: 1.6; font-weight: 400; color: var(--body); }
.rich-text li { padding: 0 0 clamp(.735rem, 1.12vw, .84rem) clamp(.315rem, .48vw, .36rem); }
.rich-text li::marker { color: var(--muted); }
.rich-text h2 { font: 800 clamp(1.65rem, 3.4vw, 2.6rem)/1.15 var(--cond); letter-spacing: clamp(.02475rem, .051vw, .039rem); text-transform: uppercase; color: var(--ink); margin: clamp(.7425rem, 1.53vw, 1.17rem) 0 clamp(.9075rem, 1.87vw, 1.43rem); }
.rich-text h3 { font: 900 clamp(1.4rem, 2.6vw, 1.9rem)/1.2 var(--cond); letter-spacing: clamp(.021rem, .039vw, .0285rem); text-transform: uppercase; color: var(--ink); margin: clamp(1.26rem, 2.34vw, 1.71rem) 0 clamp(.84rem, 1.56vw, 1.14rem); }
.rich-text h4 { font: 500 1.5rem/1.4 var(--sans); letter-spacing: -0.015rem; color: var(--ink); margin: 1.8rem 0 .6rem; }
.rich-text h5 { font: 500 1.2rem/1.5 var(--sans); color: var(--ink); margin: 1.2rem 0 .36rem; }
.rich-text > :first-child { margin-top: 0; }
/* A page that opens on a heading lines its capitals up with the label beside it. */
.article-content > .rich-text:first-child > h2:first-child { margin-top: clamp(-0.312rem, -0.408vw, -0.198rem); }
.article-content > .rich-text:first-child > h3:first-child { margin-top: clamp(-0.228rem, -0.312vw, -0.168rem); }
/* A heading linked to by its #id stops a little below the top of the window,
   and its lines are balanced so no word sits alone on the last one. */
.rich-text :is(h2, h3, h4, h5) { scroll-margin-top: 24px; text-wrap: balance; }
.anchor-link { margin-left: .3em; font-size: .5em; vertical-align: .28em; color: var(--muted); opacity: 0; text-decoration: none; }
h2:hover .anchor-link, h3:hover .anchor-link, h4:hover .anchor-link, h5:hover .anchor-link, .anchor-link:focus-visible { opacity: 1; }
.anchor-link .icon { vertical-align: 0; }
.rich-text code { font-size: .86em; background: var(--sunk); border: 1px solid var(--rule); border-radius: var(--radius-s); padding: .05em .35em; }

/* Paragraphs in a row that each open with a bold lead (lib/markdown.php,
   pointRuns) are set as numbered points: indented, a small yellow number
   beside each, and the lead on its own line. */
.points { counter-reset: point; margin: .25rem 0 clamp(3.75rem, 6.5vw, 4.75rem); border-left: 1px solid var(--rule-2); }
.rich-text p.point { position: relative; padding: 0 0 clamp(1.5rem, 2.4vw, 1.85rem) clamp(2.5rem, 4vw, 3.25rem); }
.rich-text p.point:last-child { padding-bottom: 0; }
.rich-text p.point::before { counter-increment: point; content: counter(point, decimal-leading-zero); position: absolute; left: -1px; top: .4rem; padding: 2px 0; width: clamp(1.75rem, 3vw, 2.25rem); border-left: 2px solid var(--accent); padding-left: clamp(.6rem, 1vw, .8rem); font: 500 .75rem/1.4 var(--mono); color: var(--accent-strong); }
.rich-text p.point > strong:first-child { display: block; margin-bottom: .2rem; color: var(--ink); }

/* Quotes: a plain one has the yellow bar; a pulled one sits out to the side
   on a wide screen. */
.quote { max-width: 40rem; margin: clamp(.69rem, 1.2vw, .84rem) 0 clamp(2.53rem, 4.4vw, 3.08rem); padding: 4px 0 4px 24px; border-left: 3px solid var(--accent); font-size: clamp(1.15rem, 2vw, 1.4rem); line-height: 1.55; font-weight: 400; font-style: italic; color: var(--ink); }
.quote p + p { margin-top: clamp(.92rem, 1.6vw, 1.12rem); }
.pullquote { width: 100%; margin: clamp(.75rem, 1.44vw, .96rem) 0 clamp(2.75rem, 5.28vw, 3.52rem); padding: 6px 0 6px 24px; border-left: 6px solid var(--accent); font-size: clamp(1.25rem, 2.4vw, 1.6rem); line-height: 1.45; font-weight: 200; font-style: italic; color: var(--ink); }
@media (min-width: 1180px) {
  .pullquote { width: 19rem; }
  .pullquote.right { float: right; margin: .64rem 0 2.56rem 2.4rem; }
  .pullquote.left { float: left; margin: .64rem 2.4rem 2.56rem 0; }
}

/* Pictures in the text. */
.figure { margin: .425rem 0 2.55rem; clear: both; }
.figure img { display: block; width: 100%; border-radius: var(--radius); border: 1px solid var(--rule); }
.figure figcaption { margin-top: 10px; font: 400 .78125rem/1.5 var(--mono); color: var(--muted); }

/* Tables: ruled, the header in mono capitals. */
.table { max-width: 40rem; overflow-x: auto; margin: .425rem 0 2.55rem; border: 1px solid var(--rule); border-radius: var(--radius); }
.table table { border-collapse: collapse; width: 100%; font-size: .9375rem; }
.table th { font: 500 .71875rem/1.5 var(--mono); letter-spacing: .071875rem; text-transform: uppercase; color: var(--muted); text-align: left; padding: 12px 14px; background: var(--sunk); border-bottom: 1px solid var(--rule); }
.table td { padding: 12px 14px; border-top: 1px solid var(--rule); font-weight: 400; color: var(--body); vertical-align: top; }
.table tr:first-child td { border-top: 0; }
/* A table's credit line (lib/markdown.php, tableSources) tucks in under it,
   small and set right: the table gives up its bottom margin to the line. */
.table:has(+ .table-source) { margin-bottom: .45rem; }
.table-source { max-width: 40rem; margin: 0 0 2.4rem; text-align: right; font: 400 .78125rem/1.5 var(--mono); color: var(--muted); }
.table-source a { color: var(--muted); text-decoration-color: var(--rule-2); }
.table-source a:hover { color: var(--ink); text-decoration-color: var(--accent); }

/* Code: a card with the language and a copy button on top. */
.code { margin: .425rem 0 2.55rem; clear: both; }
.code-caption { font: 400 .75rem/1.5 var(--mono); color: var(--muted); margin-bottom: 8px; }
.code-box { background: var(--sunk); border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden; }
.code-head { display: flex; align-items: center; justify-content: space-between; padding: 6px 8px 6px 14px; border-bottom: 1px solid var(--rule); font: 500 .6875rem/1.5 var(--mono); letter-spacing: .06875rem; text-transform: uppercase; color: var(--muted); }
.copy { display: none; font: 500 .6875rem/1 var(--mono); letter-spacing: .048125rem; text-transform: uppercase; color: var(--muted); border: 1px solid var(--rule-2); border-radius: var(--radius-s); padding: 6px 8px; }
.copy:hover { color: var(--ink); border-color: var(--muted); }
@media (scripting: enabled) { .copy { display: inline-block; } }
.code pre { margin: 0; padding: 16px 18px; overflow-x: auto; font: 400 .84375rem/1.7 var(--mono); color: var(--body); tab-size: 4; }
.code code { font: inherit; background: none; border: 0; padding: 0; }
.code pre.line-numbers { position: relative; padding-left: 3.0375rem; counter-reset: linenumber; }
.code pre.line-numbers > code { position: relative; white-space: inherit; }
.code .line-numbers-rows { position: absolute; pointer-events: none; top: 0; left: -3.0375rem; width: 2.3625rem; user-select: none; border-right: 1px solid var(--rule); }
.code .line-numbers-rows > span { display: block; counter-increment: linenumber; }
.code .line-numbers-rows > span::before { content: counter(linenumber); color: var(--tick); display: block; padding-right: .590625rem; text-align: right; }
/* Prism's tokens, coloured from the --code- tokens at the top. */
.token.comment, .token.prolog, .token.doctype, .token.cdata { color: var(--faint); font-style: italic; }
.token.punctuation { color: var(--muted); }
.token.tag, .token.attr-name, .token.namespace, .token.deleted { color: var(--code-tag); }
.token.boolean, .token.number, .token.function { color: var(--code-number); }
.token.property, .token.class-name, .token.constant, .token.symbol { color: var(--code-name); }
.token.selector, .token.important, .token.atrule, .token.keyword, .token.builtin { color: var(--code-keyword); }
.token.string, .token.char, .token.attr-value, .token.regex, .token.variable { color: var(--code-string); }
.token.operator, .token.entity, .token.url { color: var(--body); }
.token.important, .token.bold { font-weight: 600; }
.token.italic { font-style: italic; }

/* Forms: labels in mono capitals, fields drawn as thin boxes. The contact
   form keeps its fui- classes, which its scripts look for. */
.fui-form { display: grid; gap: 20px; }
.fui-form-container, .fui-page-container { display: grid; gap: 20px; }
.fui-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 20px; }
.fui-label { display: block; font: 500 .71875rem/1.5 var(--mono); letter-spacing: .08625rem; text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
.fui-input { display: block; width: 100%; font: 400 1rem/1.5 var(--sans); color: var(--ink); background: var(--sheet); border: 1px solid var(--rule-2); border-radius: var(--radius); padding: 10px 12px; margin: 0; }
textarea.fui-input { min-height: 11rem; resize: vertical; }
.fui-input:hover { border-color: var(--tick); }
.fui-input:focus-visible { border-color: var(--accent-strong); outline: 1px solid var(--accent-strong); outline-offset: 0; }
.fui-input.fui-error { border-color: var(--danger); }
.fui-error-message { margin-top: 6px; font-size: .875rem; color: var(--danger); }
.fui-alert { border-left: 3px solid var(--rule-2); padding: 4px 0 4px 16px; font-size: 1rem; color: var(--ink); }
.fui-alert p { margin: 0; }
.fui-alert-success { border-left-color: var(--success); }
.fui-alert-error { border-left-color: var(--danger); }
.fui-btn-wrapper { margin-top: 24px; }
.fui-btn { display: inline-flex; align-items: center; gap: 10px; font: 500 .875rem/1 var(--mono); color: var(--accent-ink); background: var(--accent); border: 1px solid var(--accent); border-radius: var(--radius); padding: 13px 18px; }
.fui-btn:hover { background: var(--accent-deep); border-color: var(--accent-deep); }
/* formie-custom.js puts a spinner after the button's words while it sends. */
.fui-spinner { display: inline-flex; }
.fui-spinner svg { width: .875rem; height: .875rem; animation: spin 1s linear infinite; }
.fui-spinner .opacity-25 { opacity: .25; }
.fui-spinner .opacity-75 { opacity: .75; }
@keyframes spin { to { transform: rotate(360deg); } }

/* The error page's line of mono. */
.error-code { margin-top: 30px; font: 400 .875rem/1.5 var(--mono); color: var(--muted); border-left: 3px solid var(--danger); padding: 4px 0 4px 16px; }
.opening .actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 36px; }

/* A draft or scheduled article, seen locally. */
.preview-badge { position: fixed; right: 16px; bottom: 16px; z-index: 60; padding: 8px 12px; border-radius: var(--radius); background: var(--accent); color: var(--accent-ink); font: 500 .75rem/1.2 var(--mono); }

/* The footer: who this is, the ways around the site, and the switch from
   dark to light. */
footer { padding: 48px 0 40px; margin-top: 40px; font-size: .9375rem; color: var(--muted); }
footer a { color: var(--muted); text-decoration: none; }
footer a:hover { color: var(--ink); }
footer .foot-top { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 2.2fr); gap: 40px 48px; }
footer .foot-about p { margin-top: 16px; max-width: 30ch; line-height: 1.5; }
footer .foot-about .brand { color: var(--ink); }
footer .foot-cols { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 32px 24px; }
footer .foot-head { margin: 0 0 12px; }
footer ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; }
footer ul .icon { margin-right: 8px; font-size: .8125rem; }
footer .foot-end { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 44px; padding-top: 18px; border-top: 1px solid var(--rule); }
/* The copyright line, as joncphillips.com has always set it: the years in
   small mono, the name in spaced capitals that go yellow under the pointer. */
footer .colophon { display: flex; flex-wrap: wrap; align-items: center; color: var(--ink); }
footer .colophon .years { font: 400 .7rem/1 var(--mono); text-transform: lowercase; }
footer .colophon .years .icon { vertical-align: -.1rem; }
footer .colophon a { font: 600 .65rem/1 var(--sans); letter-spacing: .13rem; text-transform: uppercase; color: var(--ink); margin-left: .5rem; }
footer .colophon a:hover { color: var(--accent); }
.theme { display: inline-flex; align-items: center; justify-content: center; width: 36px; height: 36px; color: var(--muted); }
.theme:hover { color: var(--ink); }
.theme .icon { width: 20px; height: 20px; }
.theme .sun { display: none; }
:root[data-theme="dark"] .theme .sun { display: inline-block; }
:root[data-theme="dark"] .theme .moon { display: none; }
@media (max-width: 860px) { footer .foot-top { grid-template-columns: minmax(0, 1fr); } }
@media (max-width: 560px) { footer .foot-cols { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

@media (max-width: 900px) {
  .row { grid-template-columns: minmax(0, 1fr); gap: 16px; }
  .row > .label { padding-top: 0; }
  .row > .side { position: static; justify-self: start; align-items: flex-start; }
  .share { flex-direction: row; gap: 16px; margin-top: 8px; }
}
@media (max-width: 760px) {
  body { font-size: 1.03125rem; }
  .page { border-left: 0; border-right: 0; }
  .links { display: none; }
  .menu { display: block; position: relative; }
  .menu summary { list-style: none; cursor: pointer; font-size: 1rem; color: var(--ink); }
  .menu summary::-webkit-details-marker { display: none; }
  .menu ol { position: absolute; left: 0; top: calc(100% + 10px); min-width: 190px; background: var(--sheet); border: 1px solid var(--rule); border-radius: var(--radius); padding: 10px 12px; list-style: none; margin: 0; z-index: 5; display: grid; gap: 8px; }
  .menu a { color: var(--ink); }
  .top { padding-top: 20px; }
  .opening { padding: 40px 0 40px; }
  .entry { padding: 20px var(--gutter); gap: 20px; grid-template-columns: auto minmax(0, 1fr); }
  .entry .leaf { width: 3.75rem; padding-right: 1rem; }
  .leaf .day { font-size: 1.5rem; }
  .entry .go { display: none; }
  .with-portrait { display: block; }
  .portrait { float: right; width: 7rem; margin: 18px calc(-1 * var(--gutter) - 1rem) .75rem 1.25rem; shape-outside: circle(50%); shape-margin: .75rem; }
  footer .colophon a { font-size: .6rem; letter-spacing: .12rem; }
}
@media (prefers-reduced-motion: reduce) { .entry .go, .links a::after { transition: none; } }

/* The newsletter box on the home page: one line, like an install command,
   the field and the button inside the same box. */
.signup-line { margin-top: 36px; scroll-margin-top: 40vh; }
.signup-row { display: flex; flex-wrap: wrap; align-items: center; gap: 12px 18px; }
.signup-row .box { flex: 0 1 40rem; min-width: 0; }
.signup-line .box { display: flex; align-items: center; gap: 10px; background: var(--sunk); border: 1px solid var(--rule-2); border-radius: var(--radius-l); padding: 7px 7px 7px 18px; }
/* The field itself draws no outline; the box takes the focus for it. */
.signup-line .box:focus-within { border-color: var(--accent-strong); }
.signup-line input { flex: 1 1 auto; width: 0; min-width: 0; font: 400 1.0625rem/1.4 var(--mono); color: var(--ink); background: none; border: 0; padding: 8px 0; outline: none; }
.signup-line input::placeholder { color: var(--faint); }
.signup-line .btn { padding: 13px 18px; font-size: .9375rem; }
.signup-line .note { margin-top: 10px; font-size: .875rem; color: var(--muted); }
.signup-line .formkit-alert-error:empty { display: none; }
.signup-line .formkit-alert-error { margin: 0 0 10px; padding: 4px 0 4px 16px; list-style: none; border-left: 3px solid var(--danger); font-size: .9375rem; }
/* Kit puts its "Thank you for signing up." in the empty slot beside the
   field, in green, and leaves the field where it is. */
.kit-slot:empty { display: none; }
.formkit-alert-success { margin: 0; font-size: .9375rem; font-weight: 500; color: var(--success); }

/* The home page's book: what it is and where to get it, with the cover on
   the right hanging past the sheet's edge, like the about page's photo. The
   section is set on a slightly different shade, edge to edge, with its name
   on a tab down the left edge, the words turned on their side. */
.book { display: grid; grid-template-columns: auto minmax(0, 1fr); margin: 0 calc(-1 * var(--gutter)); background: var(--sunk); }
.book .body { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: clamp(24px, 4vw, 56px); align-items: center; padding: clamp(40px, 6vw, 72px) var(--gutter) clamp(40px, 6vw, 72px) clamp(32px, 6vw, 88px); }
.book .tab { display: flex; align-items: center; justify-content: center; margin: 0; padding: 0 12px; writing-mode: vertical-rl; transform: rotate(180deg); background: var(--accent); color: var(--accent-ink); font: 500 .71875rem/1 var(--mono); letter-spacing: .25rem; text-transform: uppercase; white-space: nowrap; }
.book .about { max-width: 48rem; }
.book .cover { display: block; width: clamp(14rem, 28vw, 22rem); margin-right: calc(-1 * var(--gutter) - 3rem); transform: rotate(3deg); transition: transform .3s cubic-bezier(.2, .8, .2, 1); }
.book .cover img { display: block; width: 100%; height: auto; border-radius: var(--radius); box-shadow: 0 24px 50px -20px rgb(0 0 0 / .6); }
/* The button and the cover both link to the book, and pointing at either
   lifts the cover and nudges the button's arrow. */
.book .body:has(.btn:hover, .cover:hover) .cover { transform: rotate(1deg) translateY(-6px); }
.book .body:has(.cover:hover) .btn { background: var(--accent-deep); border-color: var(--accent-deep); }
.book .btn .icon { transition: transform .2s cubic-bezier(.2, .8, .2, 1); }
.book .body:has(.btn:hover, .cover:hover) .btn .icon { transform: translateX(4px); }
@media (prefers-reduced-motion: reduce) { .book .btn .icon, .book .cover { transition: none; } }
.book h2 { font: 900 clamp(2rem, 4vw, 3rem)/.95 var(--cond); }
.book .pitch { margin-top: 18px; font-size: clamp(1.3rem, 2.3vw, 1.65rem); line-height: 1.3; text-wrap: balance; color: var(--ink); }
.book .about > p:not(.pitch):not(.actions) { margin-top: 16px; max-width: 40rem; font-size: clamp(1.05rem, 1.5vw, 1.15rem); line-height: 1.6; color: var(--body); }
.book .actions { margin-top: 30px; }
/* On a phone the cover sits beside the title and the line under it. */
@media (max-width: 760px) {
  .book .body { grid-template-columns: minmax(0, 1fr) auto; gap: 0 1.25rem; align-items: start; padding: 2rem 1.25rem; }
  .book .about { display: contents; }
  .book .about > * { grid-column: 1 / -1; }
  .book .about > h2, .book .about > .pitch { grid-column: 1; }
  .book .cover { grid-column: 2; grid-row: 1 / span 2; width: 7.5rem; margin-right: 4px; }
  .book .tab { padding: 0 8px; font-size: .65625rem; letter-spacing: .2rem; }
}

/* Projects as a ruled sheet of tiles, three across (the home page's
   "Building right now" and the work page), each one going yellow under the
   pointer like the article lines. Every tile draws its own right and bottom
   rule, and the sheet hides the ones along its outer edges. */
.tiles-sheet { overflow: clip; }
.tiles { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); margin: 0 -1px -1px 0; }
.tile { display: flex; flex-direction: column; gap: 10px; min-height: 15rem; padding: 40px var(--gutter) 34px; color: var(--ink); text-decoration: none; border-right: 1px solid var(--rule); border-bottom: 1px solid var(--rule); transition: background .15s; }
.tile-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px; margin-bottom: 14px; }
.tile img { width: 48px; height: 48px; border-radius: 50%; background: #fff; transition: transform .25s cubic-bezier(.2, .8, .2, 1); }
.tile .go { font-size: .9375rem; opacity: 0; transition: opacity .15s; }
.tile .type { font: 500 .6875rem/1 var(--mono); letter-spacing: .075rem; text-transform: uppercase; color: var(--muted); border: 1px solid var(--rule-2); border-radius: var(--radius-s); padding: 5px 7px; }
.tile .name { font: 800 1.45rem/1.1 var(--cond); letter-spacing: .14rem; text-transform: uppercase; }
.tile .what { color: var(--body); line-height: 1.5; text-wrap: pretty; }
.tile .where { margin-top: auto; padding-top: 12px; font: 400 .75rem/1.4 var(--mono); color: var(--muted); }
a.tile:hover, a.tile:focus-visible { background: var(--accent); color: var(--accent-ink); outline: none; }
a.tile:hover :is(.what, .where, .type), a.tile:focus-visible :is(.what, .where, .type) { color: var(--accent-ink); border-color: rgb(0 0 0 / .3); }
a.tile:hover .go, a.tile:focus-visible .go { opacity: 1; }
a.tile:hover img, a.tile:focus-visible img { transform: rotate(-8deg) scale(1.06); }
@media (max-width: 900px) { .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) {
  .tiles { grid-template-columns: minmax(0, 1fr); }
  .tile { min-height: 0; }
  .tile .go { display: none; }
}
@media (prefers-reduced-motion: reduce) { .tile, .tile img, .tile .go { transition: none; } }

/* The invitation at the end of every article. */
.newsletter-end { padding: 0; margin-top: -3rem; }
.newsletter-end .body { position: relative; padding: clamp(28px, 6vw, 68px); background: var(--sunk); border: 1px solid var(--rule); border-radius: var(--radius-l); }
.newsletter-end .signup-line .box { background: var(--sheet); }
.newsletter-end h2 { font: 500 clamp(1.3rem, 2.4vw, 1.75rem)/1.25 var(--sans); letter-spacing: clamp(-0.0175rem, -0.024vw, -0.013rem); max-width: 40rem; text-wrap: balance; }
.newsletter-end h2 + p { margin-top: 14px; max-width: 40rem; color: var(--body); text-wrap: pretty; }
.newsletter-end .signup-line { margin-top: 26px; }
.newsletter-end .signup-line .note { font-size: .78125rem; }
/* A paper plane takes off from the right of the box and out past its edge. */
.newsletter-end .plane { position: absolute; right: -3rem; top: -5rem; width: clamp(18rem, 32vw, 26rem); height: auto; overflow: visible; pointer-events: none; }
/* With scripts on, it flies in from the bottom left the first time the box
   scrolls into view (site.js adds .is-in). */
@media (scripting: enabled) and (prefers-reduced-motion: no-preference) {
  .newsletter-end .plane { opacity: 0; transform: translate(-14rem, 9rem) rotate(-8deg) scale(.7); transition: transform 1.1s cubic-bezier(.2, .8, .2, 1), opacity .5s ease-out; }
  .newsletter-end .plane.is-in { opacity: 1; transform: none; }
  /* Once it's in place it hovers, rising and dipping a little. */
  .newsletter-end .plane.is-in .hover { animation: plane-hover 4.4s ease-in-out 1.1s infinite; }
}
@keyframes plane-hover { 0%, 100% { transform: translate(0, 0) rotate(0); } 50% { transform: translate(2px, -5px) rotate(-.8deg); } }
.newsletter-end .plane .hover { transform-box: view-box; transform-origin: 70% 40%; }
.newsletter-end .plane path { stroke: var(--ink); stroke-width: 2.5; stroke-linejoin: round; stroke-linecap: round; }
.newsletter-end .plane .trail { fill: none; stroke: var(--accent); stroke-dasharray: 2 9; stroke-width: 3; }
.newsletter-end .plane .wing { fill: var(--sheet); }
.newsletter-end .plane .fold { fill: var(--accent); }
.newsletter-end .plane .tuck { fill: var(--rule-2); }
/* Until the box is wide enough for the big plane to clear the words, it's
   smaller and flies over the top-right corner of the box, with the heading
   kept clear of it. */
@media (max-width: 1100px) {
  .newsletter-end .plane { width: 9rem; right: -.5rem; top: -2rem; }
  .newsletter-end h2 { padding-right: 6.5rem; }
}
@media (max-width: 900px) {
  .newsletter-end h2 + p { font-size: .95rem; }
  .newsletter-end .signup-line input { font-size: .9375rem; }
  .newsletter-end .signup-line .box { padding-left: 14px; }
}
