/* creature.blog — one stylesheet, no build step.
   Type: Crimson Text (self-hosted, latin + latin-ext), Georgia metric-matched
   fallback. One theme only: warm cream paper.
   Layout: 160px nav column + 80px gap + reading column, stacking under 720px. */

/* ---------------------------------------------------------------- fonts */

@font-face {
  font-family: 'Crimson Text';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/crimson-400.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Crimson Text';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/crimson-400-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Crimson Text';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/crimson-400i.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Crimson Text';
  font-style: italic;
  font-weight: 400;
  font-display: swap;
  src: url('/fonts/crimson-400i-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Crimson Text';
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/crimson-600.woff2') format('woff2');
}
@font-face {
  font-family: 'Crimson Text';
  font-style: italic;
  font-weight: 600;
  font-display: swap;
  src: url('/fonts/crimson-600i.woff2') format('woff2');
}
@font-face {
  font-family: 'Crimson Text';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/crimson-700.woff2') format('woff2');
}
@font-face {
  font-family: 'Crimson Text';
  font-style: italic;
  font-weight: 700;
  font-display: swap;
  src: url('/fonts/crimson-700i.woff2') format('woff2');
}

/* Georgia, scaled and re-boxed to sit on Crimson Text's metrics so the swap
   doesn't reflow. Measured in the browser with the real font in place:
   CT x-height .4238 / asc .949 / desc .351 em; Georgia x-height .4814.
   .4238/.4814 = .880, advance ratio .923 -> 90% is the honest compromise
   (matching x-height exactly would overshoot the line lengths). */
@font-face {
  font-family: 'Crimson Fallback';
  src: local('Georgia'), local('Times New Roman');
  size-adjust: 90%;
  ascent-override: 105.4%;
  descent-override: 39%;
  line-gap-override: 0%;
}

/* ---------------------------------------------------------------- tokens */

:root {
  /* One theme: cream paper, no dark variant, so the ground never changes
     under the photographs. (Parchment #f3eada was live briefly on 2026-11-05;
     the owner went back to cream as easier on phones.) */
  color-scheme: only light;

  --paper: #faf7f0;        /* the cream; og.png's ground is a hair lighter */
  --paper-sunk: #f2ece1;   /* code blocks */
  --ink: #1a1a1a;
  --ink-muted: #6b625a;    /* warmed off neutral grey so it sits on cream */
  --rule: #ded6c6;
  --accent: #c1512e;       /* the brand orange: wordmark, marks, ornaments */
  --accent-text: #b94a28;  /* a shade deeper, so link-size text clears AA */
  --accent-hover: #9e3f20;
  --accent-wash: #dfb4a2;  /* underlines and the blockquote rule */
  --mark: rgba(193, 81, 46, 0.16);

  --serif: 'Crimson Text', 'Crimson Fallback', Georgia, serif;
  --mono: ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas,
    'Liberation Mono', monospace;

  /* 20px. Crimson's x-height is small (.424em against Georgia's .481), so
     it sets a size smaller than the number suggests, but 22px read as loud
     for a quiet site; 20px on the same column gives ~66 characters a line. */
  --body-size: 1.25rem;
  /* The type scale. Every piece of text takes one of these sizes, by role:
       laptop  phone
       28px    24px   page titles: an article, the 404, a short contents list
       24px    22px   headings: ## in a piece, contents entries, a creature's name
       24px    24px   the wordmark
       20px    18px   body: prose, ### (bold italic), year labels, notices, nav
       17px    17px   secondary: descriptions, notes, captions, tables, quote
                      attributions, "another", back links
       14px    14px   dates; code blocks (mono runs large)
       13px    13px   photo credits
     Inline code (0.76em) and footnote markers (0.64em) scale with their line. */
  /* In rem, not em: it is applied to elements at several font sizes (prose,
     footnotes, headings) and they all have to share one right-hand edge. */
  --measure: 38rem;        /* 608px: ~66 characters of 20px Crimson */
  --nav-width: 160px;
  --gutter: 80px;
}

/* ---------------------------------------------------------------- base */

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

html {
  -webkit-text-size-adjust: 100%;
  /* A phone lays a grey box over whatever is tapped. Every press here is already
     answered, by a beat at the edge of the window, and the box only gets in its way. */
  -webkit-tap-highlight-color: transparent;
  background: var(--paper);
}

body {
  font-family: var(--serif);
  font-size: var(--body-size);
  line-height: 1.52;
  color: var(--ink);
  background: var(--paper);
  font-variant-numeric: oldstyle-nums proportional-nums;
  hanging-punctuation: first last;
  /* Inherited by everything: a word or an address too long for its line breaks
     where it must, in a title, a list, a note or a caption as much as in a
     paragraph, instead of pushing a phone's page sideways. */
  overflow-wrap: break-word;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

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

/* Plain by default: colour carries the link and the underline arrives on
   hover. Running prose turns it on permanently. */
a {
  color: var(--accent-text);
  text-decoration: none;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.16em;
  text-decoration-color: var(--accent-wash);
  transition: color 0.2s;
}

/* Every hover rule in this file sits inside (hover: hover). A touch screen has
   no hover, and treats the last thing tapped as hovered until something else
   is: `another` stayed dark and underlined after each press. */
@media (hover: hover) {
  a:hover {
    color: var(--accent-hover);
    text-decoration-line: underline;
    text-decoration-color: currentColor;
  }
}

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

/* The router moves focus to the new view's heading so a screen reader hears
   where it landed. That is a programmatic focus, not something the reader
   aimed at, so it gets no ring — a box drawn round the title of every page
   you navigate to reads as a fault. Real focus targets keep theirs. */
.main[tabindex='-1']:focus,
.main[tabindex='-1']:focus-visible,
.view h1[tabindex='-1']:focus,
.view h1[tabindex='-1']:focus-visible {
  outline: none;
}

img {
  max-width: 100%;
  height: auto;
}

/* Digits in dates, footnote numbers and code want to stay lining. */
code,
kbd,
pre,
samp {
  font-variant-numeric: normal;
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 10;
  background: var(--paper);
  color: var(--accent-text);
  font-size: 1rem;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--rule);
}

.skip-link:focus {
  left: 0.5rem;
  top: 0.5rem;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ---------------------------------------------------------------- layout */

/* The text is the page, so the reading column — not the nav-plus-column block
   — is what sits in the centre of the window. The nav hangs in the left margin
   beside it, 160px wide and 80px away. The side tracks
   share the leftover space equally, but the left one can never be narrower
   than the nav and its gutter: as the window narrows the right margin gives
   way first and the column slides left, with no breakpoint and nothing
   jumping. The column is the measure, not 680px — 680px of 22px Crimson is
   83 characters to a line. */
.layout {
  display: grid;
  grid-template-columns:
    minmax(calc(var(--nav-width) + var(--gutter)), 1fr)
    minmax(0, var(--measure))
    minmax(0, 1fr);
  padding: 3.75rem 2.5rem 6rem;
}

.masthead {
  align-self: start;
  justify-self: end;
  width: var(--nav-width);
  margin-right: var(--gutter);
  /* Sticky so the three sections stay reachable inside a long essay. It adds
     nothing to look at; it only stops the nav scrolling away. */
  position: sticky;
  top: 3.75rem;
  /* Measured with the real font, per view: the first line of the column wants
     the wordmark 1.9px (contents) to 3.5px (about) lower than its own box top.
     2.4px splits the difference, so the cap-heights line up on every view to
     within about a pixel. */
  padding-top: 0.15rem;
}

/* As wide as the word and no wider, like the nav labels below it, so the
   focus ring fits it instead of boxing the column's empty remainder. */
.wordmark {
  display: block;
  width: fit-content;
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--accent);
}

@media (hover: hover) {
  .wordmark:hover {
    color: var(--accent-hover);
    text-decoration: none;
  }
}

/* The nav is one list, so it is leaded like one: body-size type on a 1.7 line
   (34px on a laptop), no gaps between items — close enough to read as a group,
   open enough to point at. The size and leading live on the <ul>, not the links: an
   inline-block link inside an <li> that still carried the body's 22px/1.55
   strut was being spaced by a 34px line it never used. The wordmark is the
   list's heading, so it stands off by about one and a half lines. */
.nav {
  margin-top: 1.25rem;
}

.nav ul {
  list-style: none;
  display: flex;
  flex-direction: column;
  font-size: var(--body-size);
  line-height: 1.7;
}

/* inline-block, not block: a block would stretch to the full 160px column and
   the focus ring would draw a box round the empty half of it. */
.nav a {
  display: inline-block;
  position: relative;
  letter-spacing: 0.02em;
  color: var(--ink);
}

/* The deeper orange, not the wordmark's: the label is body-size text and
   #c1512e on cream is 4.38:1, just under AA. */
.nav a[aria-current] {
  color: var(--accent-text);
  text-decoration: none;
}

@media (hover: hover) {
  .nav a:hover {
    color: var(--accent-text);
    text-decoration: none;
  }
}

/* The current section is marked with a small caret. It is
   drawn, not typed: Crimson has no U+25B8, so each system's fallback font set
   it at a different size and height. It hangs outside the label on every
   layout, so the labels never move and keep one left edge with the wordmark. */
.nav a::before {
  content: '';
  position: absolute;
  right: calc(100% + 0.36em);
  top: calc(50% - 0.23em);
  border-left: 0.3em solid var(--accent);
  border-top: 0.23em solid transparent;
  border-bottom: 0.23em solid transparent;
  visibility: hidden;
}

.nav a[aria-current]::before {
  visibility: visible;
}

.main {
  min-width: 0;
}

/* ---------------------------------------------------------------- states */

.notice {
  color: var(--ink-muted);
  font-style: italic;
}

.notice a {
  font-style: normal;
}

/* ------------------------------------------------------------ contents */

.index-list {
  list-style: none;
}

.index-item + .index-item {
  margin-top: 1.75rem;
}

/* The whole entry is one link, so the description and date are part of the
   target rather than decoration beside it. */
.index-item > a {
  display: block;
  color: inherit;
}

.index-title {
  display: block;
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.24;
  color: var(--ink);
  text-wrap: balance;
  transition: color 0.2s;
}

/* Only the title answers the hover. Underlining the whole entry — title,
   description and date together — would be a lot of rules for one link. */
.index-item > a:focus-visible .index-title {
  color: var(--accent-text);
}

@media (hover: hover) {
  .index-item > a:hover {
    text-decoration: none;
  }

  .index-item > a:hover .index-title {
    color: var(--accent-text);
  }
}

.index-desc {
  display: block;
  max-width: 26em;
  margin-top: 0.3rem;
  font-size: 1.0625rem;
  line-height: 1.45;
  color: var(--ink-muted);
  text-wrap: pretty;
}

.index-date {
  display: block;
  margin-top: 0.45rem;
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  color: var(--ink-muted);
}

/* Few pieces: let each one take more room, so three entries read as a
   composed contents page rather than a stub feed. */
.index-list[data-density='sparse'] .index-item + .index-item {
  margin-top: 2.5rem;
}

.index-list[data-density='sparse'] .index-title {
  font-size: 1.75rem;
  line-height: 1.16;
  letter-spacing: -0.006em;
}

.index-list[data-density='sparse'] .index-date {
  margin-top: 0.6rem;
}

.index-year {
  margin: 3.75rem 0 1.4rem;
  font-size: var(--body-size);
  font-weight: 400;
  color: var(--ink-muted);
  letter-spacing: 0.06em;
}

/* Not :first-child — the view opens with a visually-hidden <h1>, so the first
   year heading is never the first child and used to inherit the 3.75rem. */
.index-year:first-of-type {
  margin-top: 0;
}

/* ------------------------------------------------------------- article */

.article-header {
  margin-bottom: 2.1rem;
}

.article-title {
  font-size: 1.75rem;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.006em;
  text-wrap: balance;
}

.article-date {
  display: block;
  margin-top: 0.7rem;
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  color: var(--ink-muted);
}

/* ---- prose -------------------------------------------------------- */

.prose > * {
  max-width: var(--measure);
}

.prose p {
  margin-bottom: 1.1em;
  text-wrap: pretty;
  /* No automatic hyphens at this measure — they only add noise, and they
     look wrong in the larger settings. Narrow screens turn them on below. */
  hyphens: manual;
  overflow-wrap: break-word;
}

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

.prose h2 {
  max-width: 22em;
  margin: 2.2em 0 0.6em;
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.22;
  letter-spacing: -0.004em;
  text-wrap: balance;
}

/* Set apart by italic rather than by size, so it can sit at reading size
   and still read as subordinate to an h2. */
.prose h3 {
  max-width: 22em;
  margin: 1.9em 0 0.45em;
  font-size: 1.25rem;
  font-weight: 600;
  font-style: italic;
  line-height: 1.3;
  text-wrap: balance;
}

.prose h2 + h3 {
  margin-top: 1.1em;
}

/* Inside an essay a link has to be visible mid-sentence, so in running text
   the underline is permanent. The back-links in the notes are navigation, so
   they are left plain. (The notes are a section of their own, beside the
   prose and not inside it, so each rule they share with it names them.) */
.prose a,
.footnotes li a:not(.fn-back) {
  text-decoration-line: underline;
}

.prose strong,
.footnotes strong {
  font-weight: 600;
}

.prose ul,
.prose ol {
  margin: 0 0 1.1em;
  padding-left: 1.5em;
}

/* Wider than the line gap, or the items read as one paragraph. */
.prose li {
  margin-bottom: 0.45em;
  text-wrap: pretty;
}

.prose li > ul,
.prose li > ol {
  margin: 0.45em 0 0.2em;
}

.prose ul {
  list-style-type: '\2013\2003';
}

.prose ul ul {
  list-style-type: '\2022\2003';
}

.prose ol {
  list-style-type: decimal;
}

.prose ol ol {
  list-style-type: lower-alpha;
}

.prose li::marker {
  color: var(--ink-muted);
  font-variant-numeric: oldstyle-nums;
}

.prose blockquote {
  margin: 1.8em 0;
  padding-left: 1.3em;
  border-left: 2px solid var(--accent-wash);
}

.prose blockquote p {
  margin-bottom: 0.75em;
}

.prose blockquote p:last-of-type {
  margin-bottom: 0;
}

.prose blockquote footer {
  margin-top: 0.65em;
  font-size: 1.0625rem;
  color: var(--ink-muted);
}

.prose code,
.footnotes code {
  font-family: var(--mono);
  font-size: 0.76em;
  background: var(--paper-sunk);
  padding: 0.1em 0.32em;
  border-radius: 2px;
  overflow-wrap: break-word;
}

/* Size and leading belong on the <pre>: left on the inner <code> they lose
   to the block's own 22px strut and the lines drift apart. */
.prose pre {
  max-width: var(--measure);
  margin: 1.8em 0;
  padding: 1rem 1.15rem;
  font-size: 0.875rem;
  line-height: 1.65;
  background: var(--paper-sunk);
  border: 1px solid var(--rule);
  border-radius: 3px;
  overflow-x: auto;
}

.prose pre code {
  font-size: 1em;
  background: none;
  padding: 0;
  border-radius: 0;
  white-space: pre;
}

.prose figure {
  max-width: var(--measure);
  margin: 2.2em 0;
}

.prose figure img {
  display: block;
  width: 100%;
  border-radius: 2px;
}

.prose figcaption {
  max-width: 30em;
  margin-top: 0.6rem;
  font-size: 1.0625rem;
  line-height: 1.45;
  color: var(--ink-muted);
  text-wrap: pretty;
}

/* ---- tables ------------------------------------------------------- */

/* A table is set at the secondary size: a firmer rule under the heads, a
   hairline under each row, and nothing upright. It keeps the text's left
   edge. Figures are asked to be lining and of one width; Crimson's are lining
   already and all but even (only 0, 1 and 3 differ), and the request is for
   the fallback. */
.prose .table-scroll {
  overflow-x: auto;
}

/* As wide as its contents and no wider, up to the column it sits in, where
   its cells begin to wrap. */
.prose table {
  border-collapse: collapse;
  font-size: 1.0625rem;
  line-height: 1.4;
  font-variant-numeric: lining-nums tabular-nums;
}

/* That suits a table of up to four columns on a phone too. One of five or
   more keeps the shape it has on a wide screen (the 38rem measure, written
   out because a phone redefines it) and the phone scrolls it sideways in its
   own box: fitted to a phone, that many columns break every cell at every
   space, and scrolling reads better than that. */
.prose table:has(th:nth-child(5)) {
  width: max-content;
  max-width: max(100%, 38rem);
}

.prose th,
.prose td {
  padding: 0.5rem 1.75rem 0.5rem 0;
  vertical-align: top;
  border-bottom: 1px solid var(--rule);
}

.prose th:last-child,
.prose td:last-child {
  padding-right: 0;
}

.prose th {
  padding-top: 0;
  font-weight: 600;
  vertical-align: bottom;
  border-bottom-color: var(--ink-muted);
}

/* A browser centres a column's head unless told otherwise. Only a column
   that asks for an alignment (the align attribute the renderer writes) has
   one; the rest read from the left, heads included. */
.prose th:not([align]) {
  text-align: left;
}

/* A column set to the right or the centre is a column of figures or marks,
   and "118 words" is no use on two lines. Its head may still wrap. */
.prose td[align='right'],
.prose td[align='center'] {
  white-space: nowrap;
}

/* Section break: three small squares, the favicon's mark, spaced out. */
.prose .break {
  max-width: var(--measure);
  margin: 2.5em auto;
  border: 0;
  height: 4px;
  width: 42px;
  background-image: radial-gradient(
    circle at center,
    var(--accent) 0 1.6px,
    transparent 1.7px
  );
  background-size: 14px 4px;
  background-repeat: repeat-x;
  background-position: center;
  opacity: 0.75;
}

/* End mark, set inline after the last paragraph. */
.endmark {
  display: inline-block;
  width: 0.38em;
  height: 0.38em;
  margin-left: 0.35em;
  border-radius: 1.5px;
  background: var(--accent);
  vertical-align: 0.02em;
}

/* ---- footnotes ---------------------------------------------------- */

.prose sup a,
.footnotes li a.fn-back {
  font-variant-numeric: lining-nums;
}

.prose sup {
  font-size: 0.64em;
  line-height: 0;
  vertical-align: super;
}

.prose sup a {
  padding: 0 0.1em;
}

.footnotes {
  max-width: var(--measure);
  margin-top: 3rem;
  padding-top: 1.3rem;
  border-top: 1px solid var(--rule);
}

.footnotes h2 {
  max-width: none;
  margin: 0 0 0.8rem;
  font-size: 1.0625rem;
  font-weight: 600;
  font-style: italic;
  color: var(--ink-muted);
}

.footnotes ol {
  margin: 0;
  padding-left: 1.6em;
  font-size: 1.0625rem;
  line-height: 1.5;
}

.footnotes li {
  margin-bottom: 0.65em;
  scroll-margin-top: 2rem;
}

.fn-back {
  margin-left: 0.3em;
}

.prose sup[id] {
  scroll-margin-top: 4rem;
}

/* A note's number is a few pixels wide and its way back not much more: a
   target for a mouse, not a thumb. Under a finger each one answers for the
   space around it as well. Nothing is drawn, and nothing moves. */
@media (pointer: coarse) {
  .prose sup a,
  .fn-back {
    position: relative;
  }

  .prose sup a::after,
  .fn-back::after {
    content: '';
    position: absolute;
    inset: -0.8rem -0.7rem;
  }
}

/* ---- article footer ----------------------------------------------- */

/* One way back, and nothing else. The end mark has already closed the piece,
   so there is no rule and no next/previous apparatus under it. */
.article-footer {
  max-width: var(--measure);
  margin-top: 3.25rem;
  font-size: 1.0625rem;
}

/* The lone way home on the not-found and error views. */
.back-link {
  margin-top: 2.5rem;
  font-size: 1.0625rem;
}

/* -------------------------------------------------------------- about */

/* A few short sentences are set plainly: reading
   size, one sentence group per line, no paragraph gaps. Anything longer falls
   back to ordinary prose. */
.colophon[data-brief] p {
  line-height: 1.5;
  margin-bottom: 0;
  hyphens: manual;
}

/* The contact line is one more sentence, spaced like the lines above it. Its
   address reads like the current nav item: accent colour, no underline until
   hover or keyboard focus. */
.colophon .contact a {
  color: var(--accent-text);
  text-decoration-line: none;
}

.colophon .contact a:focus-visible {
  color: var(--accent-text);
  text-decoration-line: underline;
  text-decoration-thickness: 1px;
  text-decoration-color: currentColor;
  text-underline-offset: 3px;
}

@media (hover: hover) {
  .colophon .contact a:hover {
    color: var(--accent-text);
    text-decoration-line: underline;
    text-decoration-thickness: 1px;
    text-decoration-color: currentColor;
    text-underline-offset: 3px;
  }
}

/* ------------------------------------------------------------ creature */

/* Nothing on this page should move when the creature changes. The stage is
   sized from the column, never from the photograph. Its 7:5 sits between the
   two commonest camera shapes (4:3 and 3:2), and photographs near it — about
   two in three — are cropped a few percent to fill it exactly (.is-fill), so
   for most swaps the picture box is identical. Portraits, squares and
   panoramas keep their shape, hung from the top left. Phones differ: see the
   phone rules below. */
.creature-stage {
  display: flex;
  /* Hung from the top so every photograph starts level with the wordmark. */
  align-items: flex-start;
  justify-content: flex-start;
  aspect-ratio: 7 / 5;
  margin-bottom: 1.4rem;
}

.creature-stage img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  object-fit: contain;
  border-radius: 2px;
  opacity: 1;
  transition: opacity 0.22s ease;
}

.creature-stage img.is-fill {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* One photograph gives way to the next by a cross-fade. For as long as that takes
   (is-changing, from app.js) the outgoing one is held where it is, out of the flow and
   against the stage, and the new one is laid over it: both positioned, so the later is
   drawn on top. Only for that long, because a photograph positioned at rest is given a
   layer of its own by the browser, under the edge, for nothing. */
.creature-stage.is-changing,
.creature-stage.is-changing img {
  position: relative;
}

.creature-stage img.is-loading,
.creature-stage img.is-leaving {
  opacity: 0;
}

/* An outgoing photograph that the new one does not cover goes slowly at first and
   quickly at the end, while the new one arrives the other way about, so that very
   little paper shows through the pair of them on the way. */
.creature-stage img.is-leaving {
  transition-timing-function: ease-in;
}

/* Until the first photograph is on the stage there is nothing for `another` to
   change and no name to show, so neither is there. */
.creature-view.is-waiting .creature-head {
  display: none;
}

.creature-stage img.no-fade {
  transition: none;
}

/* Name on the left, `another` on the right of the same line: both sit at a
   fixed height under the fixed stage, so the control never moves. */
.creature-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1.5rem;
  max-width: var(--measure);
}

.creature-title {
  min-width: 0;
}

.creature-name {
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: -0.004em;
  text-wrap: balance;
}

.creature-desc {
  max-width: var(--measure);
  margin-top: 0.85rem;
  text-wrap: pretty;
}

/* The licences want the maker named; this is the quietest honest way. */
.creature-credit {
  max-width: var(--measure);
  margin-top: 0.7rem;
  font-size: 0.8125rem;
  line-height: 1.5;
  letter-spacing: 0.01em;
  color: var(--ink-muted);
}

.creature-credit:empty {
  display: none;
}

.creature-credit a {
  color: inherit;
  text-decoration: none;
}

@media (hover: hover) {
  .creature-credit a:hover {
    color: var(--accent-text);
  }
}

.creature-actions {
  flex: none;
  margin: 0;
  font-size: 1.0625rem;
}

/* A button that has to pass for one of the site's links. */
.another {
  font-family: inherit;
  font-size: inherit;
  color: var(--accent-text);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  letter-spacing: 0.02em;
  text-decoration: none;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.16em;
  transition: color 0.2s;
}

@media (hover: hover) {
  .another:hover {
    color: var(--accent-hover);
    text-decoration: underline;
  }
}

/* ---------------------------------------------------------- responsive */

@media (max-width: 1100px) {
  .layout {
    --gutter: 56px;
    padding-left: 2rem;
    padding-right: 2rem;
  }
}

@media (max-width: 720px) {
  :root {
    --body-size: 1.125rem;   /* 18px: ~43 characters a line on a phone */
    --measure: 100%;
  }

  body {
    line-height: 1.56;
  }

  .layout {
    grid-template-columns: minmax(0, 1fr);
    gap: 1.5rem;
    padding: 1.75rem 1.375rem 3.5rem;
  }

  /* Wordmark on its own line, sections in a row beneath it — the same
     arrangement as the desktop column, just turned. */
  .masthead {
    position: static;
    justify-self: stretch;
    width: auto;
    margin-right: 0;
    padding-top: 0;
  }

  .nav {
    margin-top: 0.375rem;
  }

  .nav ul {
    flex-direction: row;
    flex-wrap: wrap;
    gap: 0 1.5rem;
  }

  /* 44px of height to press, without 44px of visible gap. */
  .nav a {
    padding: 0.7rem 0;
  }

  .article-title {
    font-size: 1.5rem;
  }

  .index-list[data-density='sparse'] .index-title {
    font-size: 1.5rem;
  }

  .index-title {
    font-size: 1.375rem;
  }

  .prose h2 {
    font-size: 1.375rem;
  }

  .prose h3 {
    font-size: 1.125rem;
  }

  .creature-name {
    font-size: 1.375rem;
  }


  .another {
    display: inline-block;
    padding: 0.7rem 0;
  }

  /* On a phone the name and `another` come first, above the photograph, so the
     control still never moves, and the photograph can keep its own shape: as
     wide as the column, or a tall one at most 58% of the window's height, rather
     than shrunk into a landscape frame. */
  .creature-view {
    display: flex;
    flex-direction: column;
  }

  .creature-head {
    order: -1;
    margin-bottom: 0.5rem;
  }

  .creature-stage {
    aspect-ratio: auto;
    margin-bottom: 0.2rem;
  }

  /* The stage takes the height of the photograph in it, so an outgoing one that was
     taller is cut off at the new one's foot while it fades, not laid over the words. */
  .creature-stage.is-changing {
    overflow: hidden;
  }

  .creature-stage img,
  .creature-stage img.is-fill {
    width: auto;
    height: auto;
    max-height: 58vh;
    object-fit: contain;
  }

  /* The measure is short enough here that hyphenation earns its keep, but
     only in a word long enough to leave four letters on each line: left to
     itself the browser breaks be-cause, ev-ery and peo-ple, a hyphen on
     nearly one line in five. (The second pair of lines says the same thing
     to Safari.) */
  .prose p,
  .prose li {
    hyphens: auto;
    hyphenate-limit-chars: 8 4 4;
    -webkit-hyphenate-limit-before: 4;
    -webkit-hyphenate-limit-after: 4;
  }

  .prose blockquote {
    padding-left: 1.1em;
  }

  /* Less air between a table's columns, so that more of them fit. */
  .prose th,
  .prose td {
    padding-right: 1.1rem;
  }
}

/* The photograph is as wide as the text on anything from a small laptop up. A
   full-width 434px photo leaves the name on screen in any window 560px tall or
   more; only a shorter one (a phone on its side) shrinks it to keep the name
   and `another` in view. */
@media (max-height: 559px) {
  .creature-stage {
    max-height: 56vh;
  }
}

/* ------------------------------------------------------------- motion */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* -------------------------------------------------------------- print */

@page {
  margin: 18mm;
}

@media print {
  :root {
    --paper: #fff;
    --paper-sunk: #fff;
    --ink: #000;
    --ink-muted: #333;
    --rule: #bbb;
    --accent: #000;
    --accent-text: #000;
    --accent-wash: #bbb;
    /* In rem the measure is pinned to a 16px root and would run to ~97
       characters at 11.5pt. Restated in mm it stays a book measure. */
    --measure: 112mm;
    /* The sizes set in rem (notes, captions, tables, code, the title) keep
       the proportion to the text that they have on screen, where the text is
       1.25rem: 11.5pt / 1.25. Left at the browser's 12pt root, the notes and
       captions came out larger than the text they annotate. */
    font-size: 9.2pt;
  }

  body {
    font-size: 11.5pt;
    line-height: 1.45;
    background: #fff;
    color: #000;
  }

  .layout {
    display: block;
    max-width: none;
    padding: 0;
  }

  .masthead,
  .skip-link,
  .article-footer,
  .creature-actions {
    display: none;
  }

  /* The measure is kept on paper too — it fits inside A4 margins and the
     text stays readable instead of running the full width of the sheet. */

  .prose a {
    color: #000;
  }

  /* Spell out destinations that won't survive the paper. */
  .prose a[href^='http']::after {
    content: ' <' attr(href) '>';
    font-size: 0.85em;
    color: #555;
    word-break: break-all;
  }

  .prose h2,
  .prose h3,
  .article-title {
    break-after: avoid;
  }

  .prose blockquote,
  .prose figure,
  .prose pre {
    break-inside: avoid;
  }

  /* Paper cannot scroll, so a wide table is fitted to the page like any other. */
  .prose .table-scroll {
    overflow: visible;
  }

  .prose table:has(th:nth-child(5)) {
    max-width: 100%;
  }
}

/* Moving between pages is a short cross-fade (the browser's own is a quarter of a
   second). A press during it is not lost either, though not through anything here:
   the page under a fade is out of reach whatever pointer-events says, so app.js ends
   the fade at the press and passes it on to what it was over. */
::view-transition-group(root),
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 0.15s;
}

::view-transition {
  pointer-events: none;
}

/* What a press sets off (the hard beat at the edge, the new vessels) takes no part in
   that cross-fade. Left in the picture, its first and hardest moment was spent blended
   with the outgoing page, which had none of it: the beat arrived at under half its
   strength, and the vessels faded in when they should shoot in. For as long as two
   pages are changing it is a picture of its own, shown as it is from the first frame
   and with nothing of the old one. It is named only for those 150ms (app.js marks the
   root), so a page at rest is drawn exactly as it always was. */
html.changing .press {
  view-transition-name: press;
}

::view-transition-group(press),
::view-transition-new(press) {
  animation: none;
}

::view-transition-old(press) {
  display: none;
}

::view-transition-new(press) {
  mix-blend-mode: normal;
}

/* The window is the inside of a creature, looking out. It stays in the outermost
   margin and must never compete with the reading. Everything is in the wordmark's rust
   (--accent, written out because a shadow needs it with transparency). It is drawn over
   the page, not under it, and lets every touch through: drawn under, it would make the
   browser keep a second copy of everything it covers. It is built to be cheap to show,
   since it never stops moving: only opacity changes, so nothing is repainted while it
   beats, the animations use plain numbers rather than variables so every browser can
   hand them to the graphics chip, and there are as few layers as it can have.
     the rim      the thin strong edge and the soft glow inside it; it beats, lub-dub,
                  then rests
     the vessels  branch in from the edges, fading as they go (the fade is in the
                  stroke's own colour, not a mask), and flush on their own clocks;
                  app.js grows them, and after a flush or two each group moves
   All of it quietens further on an essay. */
body {
  background: transparent;
}

body::before,
.vessels {
  position: fixed;
  inset: 0;
  z-index: 5;   /* over the page, under the skip link */
  pointer-events: none;
}

body::before {
  content: '';
  will-change: opacity;
  /* --rim-x and --rim-y come from app.js: the edge sits slightly unevenly, differently
     each load. The last shadow is the glow, reaching in from just beyond the window. */
  box-shadow:
    inset 0 0 4px 1px rgb(193, 81, 46),
    inset var(--rim-x, 0px) var(--rim-y, 0px) 15px 2px rgba(193, 81, 46, 0.76),
    inset 0 0 60px rgba(193, 81, 46, 0.14),
    inset 0 0 70px -24px rgba(193, 81, 46, 0.18);
  opacity: 0.86;
  animation: heart-rim 30.54s linear var(--rim-delay, 0s) infinite;
}

.vessels {
  overflow: hidden;
}

/* Each edge's vessels come in groups on their own clocks (set in app.js). A group fades
   out completely at the end of its cycle, which is when app.js may move it, unseen. */
.vessels svg {
  position: absolute;
  overflow: hidden;
  opacity: 0.14;
  will-change: opacity;
  animation: vessel-flush 4s linear infinite;
}

.vessels .top { top: 0; left: 0; }
.vessels .bottom { bottom: 0; left: 0; }
.vessels .left { top: 0; left: 0; }
.vessels .right { top: 0; right: 0; }

.vessels path {
  fill: none;
  stroke-linecap: round;
}

/* Pressing a link or button: the rim gives one hard beat and a few new vessels shoot in
   from the nearest edges, then fade. app.js runs both, in a second layer over the
   vessels (.press); they rest invisible here. */
.jolt {
  position: absolute;
  inset: 0;
  opacity: 0;
  /* Kept close to the edge: it is the rim beating harder, not a second glow reaching
     into the page. (It reached 62px in on a wide window, against 22px on a phone, and
     tinted a fifth of a laptop's window at its height.) */
  box-shadow:
    inset 0 0 5px 2px rgb(193, 81, 46),
    inset 0 0 18px 3px rgba(193, 81, 46, 0.55),
    inset 0 0 48px rgba(193, 81, 46, 0.16);
}

.vessels svg.sprout {
  opacity: 0;
  animation: none;
  will-change: auto;
}

body:has(.prose)::before {
  opacity: 0.93;
  animation-name: heart-rim-quiet;
}

body:has(.prose) .vessels svg:not(.sprout) {
  opacity: 0.1;
  animation-name: vessel-flush-quiet;
}

/* While an essay is being read the edge is quiet (app.js). The rim is stopped between
   two beats, where it already sits at the opacity it is given above, so taking the
   animation away changes nothing that can be seen. The vessels are not held where they
   are, because a line that is faint and also still looks like a mark on the glass: each
   group is let run to the end of its cycle, where it has faded out, and is kept out
   from there until the page changes. */
html.still body::before {
  animation: none;
}

.vessels svg.is-gone {
  display: none;
}

/* Four unequal beats, lub-dub, in a 6s pattern, played five times at different paces
   (1.04, 0.82, 1.21, 0.9 and 1.12 times as long) so no two cycles of beats are the
   same length: 30.54s in all, entered at a random point on each load (--rim-delay, set
   by app.js). The pace is in the keyframes rather than changed from script, because
   changing an animation's pace makes Safari tend it on every frame from then on. The
   quiet one, for essays, is the same beat from a higher floor. */
@keyframes heart-rim {
  0%, 2.86%, 5.52%, 7.97%, 10.22%, 13.08%, 16.35%, 17.78%, 20.43%, 22.69%, 24.78%, 26.72%, 28.49%, 30.74%, 33.32%, 34.45%, 36.54%, 39.87%, 42.96%, 45.81%, 48.43%, 51.76%, 55.56%, 57.22%, 60.31%, 62.79%, 65.09%, 67.21%, 69.16%, 71.63%, 74.46%, 75.7%, 78%, 81.08%, 83.94%, 86.58%, 89%, 92.08%, 95.6%, 97.14%, 100% { opacity: 0.86; }
  0.41%, 20.75%, 37.02%, 60.67%, 78.44% { opacity: 1; }
  0.92%, 6.64%, 21.16%, 25.67%, 37.61%, 44.27%, 61.11%, 66.06%, 78.99%, 85.15% { opacity: 0.902; }
  1.43%, 21.56%, 38.21%, 61.55%, 79.54% { opacity: 0.951; }
  5.82%, 25.02%, 43.32%, 65.35%, 84.27% { opacity: 0.93; }
  6.23%, 25.35%, 43.79%, 65.71%, 84.71% { opacity: 0.881; }
  10.62%, 28.81%, 48.9%, 69.51%, 89.44% { opacity: 0.986; }
  11.03%, 29.13%, 49.38%, 69.86%, 89.88% { opacity: 0.895; }
  11.54%, 29.53%, 49.97%, 70.3%, 90.43% { opacity: 0.923; }
  16.65%, 33.56%, 55.92%, 74.72%, 95.93% { opacity: 0.916; }
}

@keyframes heart-rim-quiet {
  0%, 2.86%, 5.52%, 7.97%, 10.22%, 13.08%, 16.35%, 17.78%, 20.43%, 22.69%, 24.78%, 26.72%, 28.49%, 30.74%, 33.32%, 34.45%, 36.54%, 39.87%, 42.96%, 45.81%, 48.43%, 51.76%, 55.56%, 57.22%, 60.31%, 62.79%, 65.09%, 67.21%, 69.16%, 71.63%, 74.46%, 75.7%, 78%, 81.08%, 83.94%, 86.58%, 89%, 92.08%, 95.6%, 97.14%, 100% { opacity: 0.93; }
  0.41%, 20.75%, 37.02%, 60.67%, 78.44% { opacity: 1; }
  0.92%, 6.64%, 21.16%, 25.67%, 37.61%, 44.27%, 61.11%, 66.06%, 78.99%, 85.15% { opacity: 0.951; }
  1.43%, 21.56%, 38.21%, 61.55%, 79.54% { opacity: 0.976; }
  5.82%, 25.02%, 43.32%, 65.35%, 84.27% { opacity: 0.965; }
  6.23%, 25.35%, 43.79%, 65.71%, 84.71% { opacity: 0.941; }
  10.62%, 28.81%, 48.9%, 69.51%, 89.44% { opacity: 0.993; }
  11.03%, 29.13%, 49.38%, 69.86%, 89.88% { opacity: 0.948; }
  11.54%, 29.53%, 49.97%, 70.3%, 90.43% { opacity: 0.962; }
  16.65%, 33.56%, 55.92%, 74.72%, 95.93% { opacity: 0.958; }
}

@keyframes vessel-flush {
  0%, 100% { opacity: 0; }
  6% { opacity: 0.44; }
  14% { opacity: 0.23; }
  22% { opacity: 0.335; }
  38%, 82% { opacity: 0.14; }
}

@keyframes vessel-flush-quiet {
  0%, 100% { opacity: 0; }
  6% { opacity: 0.24; }
  14% { opacity: 0.142; }
  22% { opacity: 0.191; }
  38%, 82% { opacity: 0.1; }
}

@media (max-width: 720px) {
  body::before {
    box-shadow:
      inset 0 0 3px 1px rgb(193, 81, 46),
      inset var(--rim-x, 0px) var(--rim-y, 0px) 8px 1px rgba(193, 81, 46, 0.6),
      inset 0 0 24px rgba(193, 81, 46, 0.09),
      inset 0 0 30px -12px rgba(193, 81, 46, 0.1);
  }

  /* A phone's margin is narrow, so its vessels are small; a little stronger to be seen. */
  .vessels svg {
    opacity: 0.18;
  }

  body:has(.prose) .vessels svg:not(.sprout) {
    opacity: 0.13;
  }

  @keyframes vessel-flush {
    0%, 100% { opacity: 0; }
    6% { opacity: 0.5; }
    14% { opacity: 0.276; }
    22% { opacity: 0.388; }
    38%, 82% { opacity: 0.18; }
  }

  @keyframes vessel-flush-quiet {
    0%, 100% { opacity: 0; }
    6% { opacity: 0.28; }
    14% { opacity: 0.175; }
    22% { opacity: 0.228; }
    38%, 82% { opacity: 0.13; }
  }

  .jolt {
    box-shadow:
      inset 0 0 4px 1px rgb(193, 81, 46),
      inset 0 0 14px 2px rgba(193, 81, 46, 0.6),
      inset 0 0 36px rgba(193, 81, 46, 0.14);
  }
}

/* With reduced motion set nothing at the edge may move, and lines that cannot move are
   left out, for the reason they are kept out of a settled essay: faint and still, they
   look like marks on the glass. The rim stays. */
@media (prefers-reduced-motion: reduce) {
  .vessels:not(.press) {
    display: none;
  }
}

@media print {
  body::before,
  .jolt,
  .vessels {
    display: none;
    animation: none;
  }
}
