@font-face {
  font-family: 'Inter';
  src: url('/fonts/InterVariable.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  color-scheme: light;
  --ink: #000000;
  --ground: #ffffff;
  --paper: #f5f3f1;
  --paper-deep: #ebe8e4;
  --line: #eeeeee;
  --ink-hover: #44403b;
  --ease: cubic-bezier(.4, 0, .2, 1);
}

html, body {
  background: var(--ground);
  color: var(--ink);
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Arial, sans-serif;
  font-weight: 400;
  margin: 0;
  padding: 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

* { box-sizing: border-box; }

.wrap {
  max-width: 680px;
  margin: 0 auto;
  padding-inline: clamp(16px, 5vw, 32px);
}

a {
  color: var(--ink);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color 150ms var(--ease);
}
a:hover { color: var(--ink-hover); }
a:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: 4px;
}

p {
  font-size: 17px;
  line-height: 1.65;
  margin: 0 0 20px;
}
p:last-child { margin-bottom: 0; }

h1, h2, h3 { font-weight: 400; margin: 0; }
h1 { text-wrap: balance; }
/* A page title is the one h1 on a page that has no hero and no post heading.
   It is sized exactly like a section h2, so the two rules stay together. */
h2,
h1.page-title {
  font-size: clamp(26px, 4vw, 32px);
  letter-spacing: -0.015em;
  line-height: 1.2;
  margin-bottom: 28px;
}
h1.page-title { text-wrap: wrap; }

.hairline {
  border: 0;
  border-top: 1px solid var(--line);
  margin: 0;
}

.micro {
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
}

/* Top bar. Thin and pinned on every page at every width (18 Aug 2026, Fred's
   go): a plain quiet pin, no hide-and-reveal here. The library layers its own
   approved hide-on-the-way-down transform on top of this base. The hairline
   that follows the bar in the markup is hidden because the bar carries its own
   bottom rule while pinned. */
.topbar {
  padding: 12px 0;
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--ground);
  border-bottom: 1px solid var(--line);
}
.topbar + .hairline { display: none; }
.topbar .wrap {
  max-width: 1040px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 16px;
}
.topbar .name { font-size: 16px; letter-spacing: -0.01em; }
a.name { text-decoration: none; }

/* Site navigation */
.sitenav {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
}
.sitenav a {
  font-size: 14px;
  color: var(--ink);
  text-decoration: none;
}
.sitenav a:hover,
.sitenav a.is-current {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}

/* Views. The scroll margin keeps a linked view at the top of the page. */
.view { display: none; scroll-margin-top: 100vh; }
.view.is-active { display: block; }

/* Hero */
.hero { padding: clamp(72px, 12vw, 128px) 0 clamp(64px, 10vw, 104px); }
.hero h1 {
  font-size: clamp(34px, 6.5vw, 58px);
  line-height: 1.08;
  letter-spacing: -0.03em;
  margin-bottom: 36px;
}
.hero p { font-size: clamp(17px, 2.2vw, 19px); }

section { padding: clamp(64px, 10vw, 112px) 0; }

/* Big three words */
.words { padding-top: clamp(48px, 8vw, 80px); }
.words .stack {
  margin: 40px 0 44px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.words .stack span {
  font-size: clamp(36px, 7vw, 64px);
  line-height: 1.06;
  letter-spacing: -0.03em;
}

/* Timeline */
.timeline {
  display: flex;
  flex-direction: column;
}
.t-entry {
  display: grid;
  grid-template-columns: 104px 25px 1fr;
  gap: 0 18px;
  position: relative;
  padding: 26px 0;
}
.t-entry .year {
  text-align: right;
  padding-top: 4px;
  white-space: nowrap;
}
.t-entry .rail { position: relative; }
.t-entry .rail::before {
  content: '';
  position: absolute;
  left: 11px;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--line);
}
.t-entry:first-child .rail::before { top: 12px; }
.t-entry:last-child .rail::before { bottom: auto; height: 12px; }
.t-entry .rail::after {
  content: '';
  position: absolute;
  left: 11px;
  top: 12px;
  width: 2px;
  height: 2px;
  background: var(--ink);
  border-radius: 50%;
  box-shadow: 0 0 0 3px var(--paper-deep);
  transform: translate(-0.5px, -1px);
}
.t-entry h3 {
  font-size: 19px;
  letter-spacing: -0.01em;
  margin-bottom: 6px;
}
.t-entry p { font-size: 16px; margin-bottom: 0; }

/* Beliefs */
.belief { padding: 26px 0; }
.belief + .belief { border-top: 1px solid var(--line); }
.belief h3 {
  font-size: 21px;
  letter-spacing: -0.015em;
  margin-bottom: 8px;
}
.belief p { font-size: 16px; margin-bottom: 0; }

/* Making cards */
.cards {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.card {
  background: var(--paper);
  border-radius: 20px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
  padding: 24px 26px;
}
.card .head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  margin-bottom: 8px;
}
.card h3 { font-size: 19px; letter-spacing: -0.01em; }
.card p { font-size: 16px; margin: 0; }

/* Claude band */
.claude-band .panel {
  background: var(--paper);
  border-radius: 24px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
  padding: clamp(32px, 6vw, 56px);
}
.claude-band .panel > h2 { margin-bottom: 20px; }
.observation {
  position: relative;
  padding-left: 26px;
  margin-top: 24px;
}
.observation::before {
  content: '';
  position: absolute;
  left: 8px;
  top: 11px;
  width: 2px;
  height: 2px;
  background: var(--ink);
  border-radius: 50%;
  box-shadow: 0 0 0 3px var(--paper-deep);
}
.observation p { font-size: 16px; margin: 0; }

/* Text filters */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 28px;
}
.filters button,
.card .more {
  font-family: inherit;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--ink);
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  transition: color 150ms var(--ease);
}
.filters button:hover,
.card .more:hover { color: var(--ink-hover); }
.filters button:hover,
.filters button.is-current,
.card .more:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.filters button:focus-visible,
.card .more:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Credit line inside a card */
.card .credit { margin-top: 12px; font-size: 14px; }
.card .more { display: block; margin-top: 12px; }
.card .detail { margin-top: 12px; }

/* Library mosaic */
.mosaic {
  columns: 2;
  column-gap: 12px;
}
.mosaic .card {
  -webkit-column-break-inside: avoid;
  break-inside: avoid;
  margin-bottom: 12px;
  padding: 0;
}
.card .body { padding: 24px 26px; }
.mosaic .card .head { flex-direction: column; gap: 4px; }
.card .figure {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 20px 20px 0 0;
}

/* Work placeholders. An empty box wearing the papers this site already uses,
   sitting where a real image will sit. Decorative, so the markup marks each one
   aria-hidden. The heights vary by position so the masonry still reads while
   every box is empty. */
.placeholder {
  border-radius: 20px 20px 0 0;
  background: linear-gradient(155deg, var(--paper), var(--paper-deep));
}
.mosaic .card:nth-child(1) .placeholder { height: 240px; }
.mosaic .card:nth-child(2) .placeholder { height: 340px; }
.mosaic .card:nth-child(3) .placeholder { height: 260px; }
.mosaic .card:nth-child(4) .placeholder { height: 400px; }
.mosaic .card:nth-child(5) .placeholder { height: 420px; }

/* The floating category button and its panel. Both are built by library.js, so
   the page stays a plain list of cards without the script. The button rides at
   every width. The tabs scroll away with the rest of the page, so this is how a
   reader changes category once they are down among the cards, and it brings the
   categories to them rather than sending them back to the top. */
.jump {
  position: fixed;
  right: 16px;
  bottom: calc(16px + env(safe-area-inset-bottom));
  z-index: 30;
  min-height: 44px;
  padding: 12px 18px;
  font-family: inherit;
  font-size: 14px;
  font-weight: 400;
  line-height: 1.4;
  color: var(--ink);
  background: var(--paper-deep);
  border: 0;
  border-radius: 20px;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
  cursor: pointer;
}
.jump:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

/* A clear sheet over the page catches the tap that closes the panel, so the
   tap cannot open a card by accident. */
.scrim {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 29;
  visibility: hidden;
}
.scrim.is-open { visibility: visible; }

.sheet {
  position: fixed;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 31;
  max-height: 78vh;
  overflow-y: auto;
  padding: 8px 20px calc(20px + env(safe-area-inset-bottom));
  background: var(--paper);
  border-radius: 24px 24px 0 0;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04);
  visibility: hidden;
  transform: translateY(100%);
  /* On the way out the panel stays visible until the slide finishes. On the way
     in it turns visible at once, so the first category can take the focus. */
  transition: transform 240ms var(--ease), visibility 0s linear 240ms;
}
.sheet.is-open {
  visibility: visible;
  transform: translateY(0);
  transition: transform 240ms var(--ease), visibility 0s linear 0s;
}
.sheet-item {
  display: block;
  width: 100%;
  min-height: 48px;
  padding: 13px 4px;
  font-family: inherit;
  font-size: 16px;
  font-weight: 400;
  line-height: 1.4;
  text-align: left;
  color: var(--ink);
  background: none;
  border: 0;
  cursor: pointer;
}
.sheet-item + .sheet-item { border-top: 1px solid var(--line); }
.sheet-item.is-current {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.sheet-item:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: -2px;
  border-radius: 4px;
}

/* A sheet the full width of the screen is right on a phone and wrong on a
   desktop, where it would be a 1280px band holding nine short labels. From
   620px up the panel stands on the button instead, in the bottom right corner,
   at a width that suits the longest label with room to spare. It rises a little
   as it arrives rather than sliding a whole screen, and its height is capped so
   it always stops short of the menu at the top. */
@media (min-width: 620px) {
  .sheet {
    left: auto;
    right: 16px;
    bottom: calc(72px + env(safe-area-inset-bottom));
    width: 300px;
    max-width: calc(100vw - 32px);
    max-height: calc(100vh - var(--topbar-h, 64px) - 88px - env(safe-area-inset-bottom));
    padding: 8px 20px;
    border-radius: 24px;
    transform: translateY(8px);
    opacity: 0;
    transition: transform 180ms var(--ease), opacity 180ms var(--ease), visibility 0s linear 180ms;
  }
  .sheet.is-open {
    transform: none;
    opacity: 1;
    transition: transform 180ms var(--ease), opacity 180ms var(--ease), visibility 0s linear 0s;
  }
}

/* The open state carries its own transition, so it is named here too. Without
   it the panel still slides in for a reader who has asked for no motion. */
@media (prefers-reduced-motion: reduce) {
  .sheet,
  .sheet.is-open { transition: none; }
}

/* Plain lists */
.list { list-style: none; margin: 0; padding: 0; }
.list li {
  position: relative;
  padding-left: 26px;
  margin-top: 20px;
  font-size: 16px;
  line-height: 1.65;
}
.list li::before {
  content: '';
  position: absolute;
  left: 8px;
  top: 11px;
  width: 2px;
  height: 2px;
  background: var(--ink);
  border-radius: 50%;
  box-shadow: 0 0 0 3px var(--paper-deep);
}

/* Sentence that follows a block */
.follow { margin-top: 28px; }

.is-hidden { display: none; }

/* Blog list */
.post-entry { padding: 26px 0; }
.post-entry + .post-entry { border-top: 1px solid var(--line); }
.post-entry h3 {
  font-size: 21px;
  letter-spacing: -0.015em;
  margin-bottom: 8px;
}
.post-entry h3 a { text-decoration: none; }
.post-entry h3 a:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.post-entry .micro { display: block; }

/* Blog post */
.post h1 {
  font-size: clamp(30px, 5vw, 42px);
  line-height: 1.15;
  letter-spacing: -0.025em;
  margin-bottom: 12px;
}
.post .micro { display: block; margin-bottom: 32px; }

/* Footer */
footer { padding: 40px 0 72px; }
footer .wrap {
  max-width: 1040px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  flex-wrap: wrap;
  font-size: 14px;
}

@media (max-width: 620px) {
  /* One row on phones: the location line leaves the pinned bar; the footer
     still carries it. */
  .topbar .micro { display: none; }
  .mosaic { columns: 1; }
  .t-entry {
    grid-template-columns: 1fr;
    gap: 6px;
    padding: 22px 0;
  }
  .t-entry .year { text-align: left; padding-top: 0; }
  .t-entry .rail { display: none; }
  .t-entry + .t-entry { border-top: 1px solid var(--line); }
  .card .head { flex-direction: column; gap: 4px; }
}

/* The tabs still scroll away: nine labels need two rows on any screen this
   site is likely to meet, and a permanent 97px of tabs over a page of
   photographs is not worth the height. The floating category button takes over
   at every width. Anchor jumps land clear of the pinned bar wherever
   library.js has measured it. */
html { scroll-padding-top: var(--topbar-h, 0px); }

/* The menu gets out of the way while the reader is going down the library, and
   comes back the moment they turn around. library.js adds these classes; with
   the script gone none of this applies and the page keeps the behaviour above.
   The menu moves by a transform and is never hidden, so it stays in the page
   for a screen reader and stays in the tab order. */
body.page-library .topbar {
  transition: transform 240ms var(--ease);
}
/* The extra pixel covers a menu that measures a fraction short, so the rule
   under it clears the top of the screen with nothing showing above. */
body.page-library.nav-live.nav-hidden .topbar {
  transform: translateY(calc((var(--topbar-h, 64px) + 1px) * -1));
}
/* Tabbing into a menu that is out of sight brings it straight back. */
body.page-library.nav-live.nav-hidden .topbar:focus-within { transform: none; }
/* Engaging and letting go both happen while the menu is off the screen, so the
   slide is turned off for that frame and nothing flickers. */
body.page-library.nav-instant .topbar { transition: none; }

/* The bar is pinned site-wide now (see Top bar), so the library's old
   pin-only-once-scrolling rules are gone; its hide-on-the-way-down transform
   above still applies on top of the pinned base. */

@media (prefers-reduced-motion: reduce) {
  body.page-library .topbar { transition: none; }
}

/* Library goes wider and to three columns on large screens.
   Only the library view widens; every other view keeps its measure. */
@media (min-width: 1080px) {
  #library .wrap { max-width: 1020px; }
  #library .wrap > h1,
  #library .wrap > h2,
  #library .wrap > p { max-width: 616px; }
  #library .mosaic { columns: 3; }
}
@media (min-width: 1440px) {
  #library .wrap { max-width: 1240px; }
}

/* The veil, ported from the Rational Magic site 14 August 2026: a progressive
   blur over the bottom of the screen. Seven full-size layers, blur doubling 1
   to 64px, each masked to an OVERLAPPING trapezoid band so that adjacent layers
   cross-fade into each other. The overlap is the whole trick: it is what keeps
   the blur smooth, and hard-edged strips are what band. Masks are applied after
   filters in every browser, so masking a backdrop-filter is safe. One thing
   must never go inside this container: a child carrying mix-blend-mode. It
   isolates the group and the blur loses the backdrop it is reading.

   Three things differ from the original. It sits at z-index 10, above the page
   and below the pinned menu (20), the scrim (29), the floating category button
   (30) and the category panel (31), so none of those is ever blurred or
   covered. Its fallback fades to this site's own ground, white, rather than the
   warm paper the original faded to. And the taper that dissolves it lives in a
   shared /veil.js instead of a script written into one page.

   The dissolve fades each layer's OWN opacity through --veil-o, never a
   wrapper's: fading the wrapper would fade the seven layers as one group and
   take the cross-fade with it. With the script gone --veil-o falls back to 1
   and the veil simply holds at full strength. */
.veil {
  position: fixed; left: 0; right: 0; bottom: 0; height: 34vh;
  pointer-events: none; z-index: 10;
}
.veil i { position: absolute; inset: 0; display: block; opacity: var(--veil-o, 1); }
.veil i:nth-child(1) { backdrop-filter: blur(1px); -webkit-backdrop-filter: blur(1px);
  mask-image: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,1) 10%, rgba(0,0,0,1) 30%, rgba(0,0,0,0) 40%);
  -webkit-mask-image: linear-gradient(rgba(0,0,0,0), rgba(0,0,0,1) 10%, rgba(0,0,0,1) 30%, rgba(0,0,0,0) 40%); }
.veil i:nth-child(2) { backdrop-filter: blur(2px); -webkit-backdrop-filter: blur(2px);
  mask-image: linear-gradient(rgba(0,0,0,0) 10%, rgba(0,0,0,1) 20%, rgba(0,0,0,1) 40%, rgba(0,0,0,0) 50%);
  -webkit-mask-image: linear-gradient(rgba(0,0,0,0) 10%, rgba(0,0,0,1) 20%, rgba(0,0,0,1) 40%, rgba(0,0,0,0) 50%); }
.veil i:nth-child(3) { backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
  mask-image: linear-gradient(rgba(0,0,0,0) 15%, rgba(0,0,0,1) 30%, rgba(0,0,0,1) 50%, rgba(0,0,0,0) 60%);
  -webkit-mask-image: linear-gradient(rgba(0,0,0,0) 15%, rgba(0,0,0,1) 30%, rgba(0,0,0,1) 50%, rgba(0,0,0,0) 60%); }
.veil i:nth-child(4) { backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  mask-image: linear-gradient(rgba(0,0,0,0) 20%, rgba(0,0,0,1) 40%, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 70%);
  -webkit-mask-image: linear-gradient(rgba(0,0,0,0) 20%, rgba(0,0,0,1) 40%, rgba(0,0,0,1) 60%, rgba(0,0,0,0) 70%); }
.veil i:nth-child(5) { backdrop-filter: blur(16px); -webkit-backdrop-filter: blur(16px);
  mask-image: linear-gradient(rgba(0,0,0,0) 40%, rgba(0,0,0,1) 60%, rgba(0,0,0,1) 80%, rgba(0,0,0,0) 90%);
  -webkit-mask-image: linear-gradient(rgba(0,0,0,0) 40%, rgba(0,0,0,1) 60%, rgba(0,0,0,1) 80%, rgba(0,0,0,0) 90%); }
.veil i:nth-child(6) { backdrop-filter: blur(32px); -webkit-backdrop-filter: blur(32px);
  mask-image: linear-gradient(rgba(0,0,0,0) 60%, rgba(0,0,0,1) 80%);
  -webkit-mask-image: linear-gradient(rgba(0,0,0,0) 60%, rgba(0,0,0,1) 80%); }
.veil i:nth-child(7) { backdrop-filter: blur(64px); -webkit-backdrop-filter: blur(64px);
  mask-image: linear-gradient(rgba(0,0,0,0) 70%, rgba(0,0,0,1) 100%);
  -webkit-mask-image: linear-gradient(rgba(0,0,0,0) 70%, rgba(0,0,0,1) 100%); }
/* Without backdrop-filter there is nothing to blur, so the last layer fades to
   the page's own ground, white, and the bottom of the screen simply goes quiet.
   The value is --ground at 92%, written out because a gradient cannot take a
   plain hex and an alpha separately. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .veil i:nth-child(7) { background: linear-gradient(to bottom, transparent, rgba(255, 255, 255, 0.92)); mask-image: none; -webkit-mask-image: none; }
}

/* The people named in a post: expandable notes at the article's foot.
   Native details/summary, no script. The plus rotates to a cross on open. */
.people {
  margin-top: 44px;
  border-top: 1px solid var(--line);
  padding-top: 28px;
}
.people h2 { margin-bottom: 8px; }
.people details { border-bottom: 1px solid var(--line); }
.people summary {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 16px 0;
  cursor: pointer;
  list-style: none;
}
.people summary::-webkit-details-marker { display: none; }
.people summary .name { flex: 1; }
.people .micro { display: inline; margin: 0; }
.people summary .mark { transition: transform 200ms var(--ease); }
.people details[open] summary .mark { transform: rotate(45deg); }
.people details p { margin: 0; padding: 0 0 20px; }
.people + .micro { margin-top: 28px; }

/* Reading progress: the bar lives inside the pinned top bar as its bottom
   edge, so the hairline under the menu is the track and fills black as the
   page is read. Post pages only. Driven by progress.js; scaleX avoids layout
   work. */
.progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;
  height: 2px;
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: 0 0;
  pointer-events: none;
}

/* Breadcrumb bar. A single library piece has no menu; it has a path back to
   where it was found. The links are the size of the menu links, and the 8px
   either side of a slash adds up to the 16px the menu holds between its own
   links, so the two bars read at the same rhythm. The slashes are page text,
   the same black as everything else. */
.crumbs {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  font-size: 14px;
  color: var(--ink);
}
.crumbs a {
  font-size: 14px;
  color: var(--ink);
  text-decoration: none;
}
.crumbs a:hover {
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
/* On a bar that carries a breadcrumb, the path reads as one line with the
   name: everything sits left, and the location line alone keeps the right
   edge. Without this, the bar's even spacing floats the path in the middle. */
.topbar .wrap:has(.crumbs) { justify-content: flex-start; }
.topbar .wrap:has(.crumbs) .micro { margin-left: auto; }

/* A single library piece on its own page. The picture is the reason the page
   exists, so it is allowed more width than the words: the wrap opens to 964px,
   which leaves 900px of room inside the site's own side padding. The image
   stops at 900px; its markup carries its true pixel size, so the browser
   reserves its shape while it loads and never blows it up past those pixels.
   The words underneath hold the 616px measure every other page reads at. */
.piece .wrap { max-width: 964px; }
/* The image sets the page's composition, so the copy is anchored to it: the
   inner block shrinks to the image's width and everything inside starts at
   the image's left edge and ends at its right, capped at the reading measure
   for any image wider than it (Fred's call, 19 Aug 2026). */
.piece .inner {
  width: fit-content;
  margin-inline: auto;
}
.piece .figure {
  display: block;
  max-width: min(100%, 900px);
  height: auto;
  margin: 0 0 32px;
  border-radius: 20px;
}
/* The zero-width trick: the text contributes nothing to the inner block's
   size, then stretches to it, so copy takes exactly the image's width and
   both edges align. That width runs up to 900px on this library; the short
   metadata lines wear it fine at any width, and the prose paragraphs carry
   their own measure cap below. */
.piece h1,
.piece h2,
.piece p {
  width: 0;
  min-width: 100%;
  margin-inline: 0;
}
/* Sized to match .post h1 exactly. Written out here rather than folded into
   that rule so the piece pages can be added without reaching back into the
   blog styles above. */
.piece h1 {
  font-size: clamp(30px, 5vw, 42px);
  line-height: 1.15;
  letter-spacing: -0.025em;
  margin-bottom: 12px;
}
/* Credits are the piece's paperwork, not its prose, so they hold the card's
   quiet 14px rather than swelling to body size off the card. */
.piece .credit { font-size: 14px; }

/* Researched copy on a piece page (19 Aug 2026). The about paragraphs are
   sentences, so they stop at the 616px measure every other page reads at while
   staying pinned to the image's left edge; under an image narrower than the
   measure they still hold both its edges. The maker's name is an h2 one quiet
   size up from body text, because this site carries a single weight. Every
   block here is optional: a page with no researched copy renders exactly as it
   did before these rules existed. */
.piece .about { min-width: min(100%, 616px); }
.piece h2 {
  font-size: 20px;
  margin: 40px 0 12px;
}

/* The note at the foot of every piece page: housekeeping in the credit's
   quiet 14px, set a step apart from the paperwork above it. */
.piece .note {
  font-size: 14px;
  margin-top: 28px;
}

/* Share, on a library card. It sits at the far right of the See more line. The
   foot of a card is not a row, it is two stacked blocks, so the button is
   floated right and the See more line runs alongside it rather than the markup
   being rebuilt around a new wrapper. 13px of padding around an 18px icon
   makes the 44px tap target; the negative margins hand that padding back to the
   layout, so the line keeps the height it always had, the icon lands level with
   the See more text, and the detail underneath is not pushed down. The negative
   right margin sets the icon flush with the card's own right edge.

   Do not scope this to .card. The button does nothing without share.js, so the
   library hides it in a <noscript> block with a plain .share rule, and that
   rule can only win on source order if the display it is overruling is set at
   the same one-class weight. .card .share would outrank it and leave a dead
   button on the page for anyone with scripting off. Same reason .filters is
   written as one class. */
.share {
  float: right;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 -13px -24px 0;
  padding: 13px;
  font-family: inherit;
  color: var(--ink);
  background: none;
  border: 0;
  cursor: pointer;
  transition: color 150ms var(--ease);
}
.share svg {
  display: block;
  width: 18px;
  height: 18px;
}
.share:hover { color: var(--ink-hover); }
/* The outline is pulled inside the padding so it draws around the icon rather
   than around the invisible tap target. */
.share:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: -7px;
  border-radius: 4px;
}

/* The word the button says after it has copied an address, on browsers with no
   share sheet. Micro size, and it reads left of the icon. */
.share-note {
  font-size: 13px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1;
  white-space: nowrap;
}

/* The card image is a link to the piece's own page. Nothing about it should
   look different, but the anchor has to be a block or it inherits a text line's
   descender space under the picture and the focus ring draws around a phantom
   line box instead of the image. */
.card > a {
  display: block;
  line-height: 0;
  text-decoration: none;
}
