/* ==========================================================================
   stephenvosloo.com × Bureau of Moon Rabbits — shared styles
   Tokens, base, header, buttons, closing CTA, moonset, footer.

   Rules of the house:
   - White first. Black arrives twice: the night scene and the footer.
   - Everything is visible and laid out without JavaScript. The scripted
     layouts only apply under `html.motion`, a class a head script adds when
     JS is running and the reader hasn't asked for reduced motion.
   - No rounded rectangles. Circles, crescents and plain rectangles.
   ========================================================================== */

@font-face {
  font-family: "Bureau Mono";
  src: url("/assets/fonts/bureau-mono-regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Bureau Mono";
  src: url("/assets/fonts/bureau-mono-light.woff2") format("woff2");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}

/* Freight arrives from Adobe with font-display: swap. Until it does, text sets in
   local fonts scaled to Freight's width (measured in the browser), so lines
   break in the same places and nothing jumps when the real fonts land. */
@font-face {
  font-family: "Freight Display Fallback";
  src: local("Georgia");
  size-adjust: 82.5%;
}
@font-face {
  font-family: "Freight Display Fallback";
  src: local("Georgia Italic"), local("Georgia-Italic");
  font-style: italic;
  size-adjust: 74.4%;
}
@font-face {
  font-family: "Freight Sans Fallback";
  src: local("Arial"), local("Helvetica Neue");
  size-adjust: 88.8%;
}

:root {
  /* colour: a monochrome brand; the work brings the colour */
  --white: #ffffff;
  --black: #0c0c0d;
  --ink: #111112;
  --rule: color-mix(in srgb, var(--fg) 18%, transparent);
  --soft: color-mix(in srgb, var(--fg) 62%, transparent);

  /* the page's current ground and ink; the night scene and case themes move these */
  --bg: var(--white);
  --fg: var(--ink);
  --accent: var(--fg);

  /* type */
  --font-display: "freight-display-pro", "Freight Display Fallback", Georgia, "Times New Roman", serif;
  --font-sans: "freight-sans-pro", "Freight Sans Fallback", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "Bureau Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* labels and nav: mono caps read small, so they get a little more than you'd think */
  --step--1: clamp(0.8125rem, 0.76rem + 0.2vw, 0.9375rem);
  --step-0: clamp(1.0625rem, 1.02rem + 0.2vw, 1.1875rem);
  --step-1: clamp(1.375rem, 1.24rem + 0.6vw, 1.875rem);
  --step-2: clamp(1.75rem, 1.4rem + 1.55vw, 3rem);
  --step-3: clamp(2.5rem, 1.7rem + 3.5vw, 5.25rem);
  --step-4: clamp(3.25rem, 0.9rem + 9.6vw, 10.5rem);
  /* the one very big thing a page is allowed: the closing ask */
  --step-5: clamp(3.75rem, 0.6rem + 12.6vw, 13.5rem);

  /* space */
  --gutter: clamp(1.25rem, 4.2vw, 4.5rem);
  --section: clamp(6rem, 14vw, 13rem);

  /* motion */
  /* the same curves and UI timings as the Hope Miners site */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-in-out: cubic-bezier(0.77, 0, 0.175, 1);
  --dur-press: 160ms;
  --dur-ui: 240ms;
}

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

html {
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-sans);
  font-size: var(--step-0);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: clip;
}

h1, h2, h3, p, figure, blockquote, dl, dd, ul, ol {
  margin: 0;
}
ul, ol {
  padding: 0;
  list-style: none;
}
img, svg {
  display: block;
  max-width: 100%;
}
img {
  height: auto;
}
a {
  color: inherit;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}
:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 4px;
}

.skip-link {
  position: absolute;
  left: var(--gutter);
  top: -4rem;
  z-index: 200;
  padding: 0.75rem 1rem;
  background: var(--ink);
  color: var(--white);
  font-family: var(--font-mono);
  font-size: var(--step--1);
}
.skip-link:focus {
  top: 1rem;
}

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

/* ---------- type roles ---------- */

/* Freight Display is the voice */
.display {
  font-family: var(--font-display);
  font-weight: 300;
  line-height: 0.98;
  letter-spacing: -0.012em;
  text-wrap: balance;
}
.display em {
  font-style: italic;
}

/* the mono is an accent: labels, nav, the bubble. Never paragraphs. */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  font-weight: 400;
  line-height: 1.5;
  letter-spacing: 0.09em;
  text-transform: uppercase;
}

.link-arrow {
  display: inline-flex;
  gap: 0.7em;
  padding-bottom: 0.3em;
  /* the rule is a background, so it can be drawn: on hover it leaves to the
     right and comes back in from the left */
  border: 0;
  background: linear-gradient(currentColor, currentColor) 0 100% / 100% 1px no-repeat;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
}
.link-arrow::after {
  content: "→";
  transition: transform 0.5s var(--ease-out);
}
@keyframes rule-redraw {
  0% {
    background-size: 100% 1px;
    background-position: 100% 100%;
  }
  45% {
    background-size: 0% 1px;
    background-position: 100% 100%;
  }
  45.1% {
    background-size: 0% 1px;
    background-position: 0 100%;
  }
  100% {
    background-size: 100% 1px;
    background-position: 0 100%;
  }
}
@media (hover: hover) and (pointer: fine) {
  a:hover .link-arrow::after,
  .link-arrow:hover::after {
    transform: translateX(0.4em);
  }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  a:hover .link-arrow,
  .link-arrow:hover {
    animation: rule-redraw 0.9s var(--ease-in-out);
  }
}

/* plain text links in running copy and lists: a hairline that draws in from the left */
.site-footer__cols a,
.case-hero__crumbs a {
  background: linear-gradient(currentColor, currentColor) 0 100% / 0% 1px no-repeat;
  padding-bottom: 0.12em;
  transition: background-size 0.5s var(--ease-out);
}
.site-footer__cols a:hover,
.site-footer__cols a:focus-visible,
.case-hero__crumbs a:hover,
.case-hero__crumbs a:focus-visible {
  background-size: 100% 1px;
  text-decoration: none;
}

/* hairlines that draw in from the left (lib.js, drawRules): the border makes
   way for a background line whose width can be eased */
.motion .rule-draw {
  border-top-color: transparent;
  border-bottom-color: transparent;
  background-image: linear-gradient(var(--rule), var(--rule)), linear-gradient(var(--rule), var(--rule));
  background-repeat: no-repeat;
  background-origin: border-box;
  background-position: 0 0, 0 100%;
  background-size: 0% var(--rule-top, 0px), 0% var(--rule-bottom, 0px);
  transition: background-size 1.6s var(--ease-out);
}
.motion .rule-draw.is-drawn {
  background-size: 100% var(--rule-top, 0px), 100% var(--rule-bottom, 0px);
}

/* headline lines rise out of a mask (the script splits them, then puts the text back) */
.line {
  display: block;
  overflow: hidden;
  /* room for descenders and italic overhang inside the mask */
  padding: 0.08em 0.16em 0.16em;
  margin: -0.08em -0.16em -0.16em;
}
.line__inner {
  display: block;
  will-change: transform;
}
/* Opening headlines are never hidden in CSS: they paint with the first frame
   (under the intro or the arriving curtain when there is one), and only rise
   line by line when something was covering them a moment ago. */

/* ---------- header ---------- */

.site-header {
  position: absolute;
  inset: 0 0 auto 0;
  z-index: 100;
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  padding: clamp(1rem, 2vw, 1.6rem) var(--gutter);
  color: var(--fg);
  transition: transform 0.36s var(--ease-out), color var(--dur-ui) linear;
}
.js .site-header {
  position: fixed;
}
/* scrolling down tucks it away; scrolling up brings it back on the page's own ground */
.site-header.is-tucked {
  transform: translateY(-110%);
}
.site-header.is-floating {
  background: var(--bg);
}

.site-header__mark {
  display: block;
  width: clamp(4.75rem, 6.6vw, 6.25rem);
  transform-origin: 0 0;
  transition: width 0.3s var(--ease-out);
}
.site-header.is-floating .site-header__mark {
  width: clamp(3.5rem, 4.4vw, 4.25rem);
}
.tile-logo {
  width: 100%;
  height: auto;
  overflow: visible;
}

.site-nav ul {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.6vw, 2.75rem);
}
.site-nav a {
  display: block;
  padding: 0.55rem 0;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  text-decoration: none;
}
.site-nav a {
  white-space: nowrap;
}
/* on a narrow phone the three links share a row with the logo: they keep the smaller size */
@media (max-width: 479px) {
  .site-nav a {
    font-size: 0.75rem;
  }
}
/* a hairline draws in under the link, from the left */
.site-nav a {
  position: relative;
}
.site-nav a::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.25rem;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--dur-ui) var(--ease-out);
}
.site-nav__cta::before {
  right: 1.3em;
}
@media (hover: hover) and (pointer: fine) {
  .site-nav a:hover::before {
    transform: scaleX(1);
  }
}
.site-nav a:focus-visible::before,
.site-nav a[aria-current]::before {
  transform: scaleX(1);
}
/* When art runs behind the header (the hare's ears on the home page), each
   link it touches gets a patch of the page's ground behind it. main.js turns
   it on per link, only while something dark is actually under it.
   The patch hugs the type: a hair past each side (padding the link and
   giving it back as margin, so nothing moves) and just the cap height tall. */
.site-nav a {
  --hug: 0.3em;
  margin-inline: calc(var(--hug) * -1);
  padding-inline: var(--hug);
  background: linear-gradient(var(--bg), var(--bg)) center / 100% calc(100% - 1.1rem + 0.5em) no-repeat;
  background-color: transparent;
  background-image: none;
}
.site-nav a.is-over-art {
  background-image: linear-gradient(var(--bg), var(--bg));
}
.site-nav a::before {
  left: var(--hug);
  right: var(--hug);
}
.site-nav__cta::before {
  right: calc(1.3em + var(--hug));
}

/* the one persistent ask, marked with a small full moon */
.site-nav__cta::after {
  content: "";
  display: inline-block;
  width: 0.6em;
  height: 0.6em;
  margin-left: 0.7em;
  border-radius: 50%;
  background: currentColor;
}

/* ---------- intro: night, and the tile logo filling in, small, in the middle ----------
   Once per session. The page is already rendered underneath; when the last
   tile lands, the night lifts off it with the moon's curved edge. */

.intro {
  display: none;
  position: fixed;
  inset: 0;
  z-index: 90;
  place-items: center;
  color: var(--white);
}
/* the sheet is bigger than the screen so its curved lower edge stays off-screen until it lifts */
.intro__sheet {
  position: absolute;
  inset: -2vh -20vw -14vh;
  border-radius: 0 0 50% 50% / 0 0 14vh 14vh;
  background: var(--black);
}
.intro__mark {
  position: relative;
  width: clamp(7.5rem, 13vw, 11rem);
}
.is-intro .intro {
  display: grid;
  /* if the script never arrives, get out of the way */
  animation: intro-failsafe 0.01s 3.5s forwards;
}
.is-intro .site-header__mark,
.is-intro .site-nav {
  opacity: 0;
}
@keyframes intro-failsafe {
  to {
    visibility: hidden;
  }
}

/* ---------- round button (magnetic on a fine pointer) ---------- */

.round-btn {
  --size: clamp(9rem, 13vw, 12rem);
  display: inline-grid;
  place-items: center;
  width: var(--size);
  height: var(--size);
  padding: 1.25rem;
  border-radius: 50%;
  background: var(--fg);
  color: var(--bg);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.09em;
  line-height: 1.4;
  text-align: center;
  text-decoration: none;
  text-transform: uppercase;
  position: relative;
  overflow: hidden;
  isolation: isolate;
  box-shadow: inset 0 0 0 1px var(--fg);
  transition: scale var(--dur-press) var(--ease-out), color var(--dur-ui) linear;
}
/* hover: the page's own ground rises inside the disc, new moon to full */
.round-btn::before {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: -1;
  border-radius: 50%;
  background: var(--bg);
  transform: translateY(103%);
  transition: transform 0.5s var(--ease-out);
}
.round-btn span {
  display: block;
  pointer-events: none;
}
@media (hover: hover) and (pointer: fine) {
  .round-btn:hover {
    color: var(--fg);
  }
  .round-btn:hover::before {
    transform: none;
  }
}
.round-btn:active {
  scale: 0.97;
}

/* a small moon that follows the cursor over a piece of work and says where a click goes */
.cursor-label {
  display: none;
}
.motion .cursor-label {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  width: 6rem;
  height: 6rem;
  margin: -3rem 0 0 -3rem;
  border-radius: 50%;
  background: var(--ink);
  color: var(--white);
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: 0.09em;
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  scale: 0.6;
  transition: opacity var(--dur-ui) linear, scale 0.4s var(--ease-out);
}
.motion .cursor-label.is-on {
  opacity: 1;
  scale: 1;
}

/* ---------- closing ask: the last thing on white before the moon sets ---------- */

.closing {
  --bg: var(--white);
  --fg: var(--ink);
  /* derived tones are worked out where --fg is set, so they are set again here */
  --rule: color-mix(in srgb, var(--fg) 18%, transparent);
  --soft: color-mix(in srgb, var(--fg) 62%, transparent);
  position: relative;
  display: grid;
  gap: clamp(2rem, 4vw, 3.5rem);
  justify-items: start;
  padding: var(--section) var(--gutter) clamp(5rem, 10vw, 9rem);
  background: var(--bg);
  color: var(--fg);
}
.closing__title {
  font-size: var(--step-5);
  line-height: 0.92;
  max-width: 10ch;
}
/* The one loud moment: Freight Big Black, used here and nowhere else. */
.loud {
  font-family: "freight-big-pro", var(--font-display);
  font-weight: 900;
  letter-spacing: -0.018em;
  white-space: nowrap;
}
.closing__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: clamp(1.5rem, 4vw, 4rem);
}
.closing__text {
  max-width: 22ch;
  font-size: var(--step-1);
  line-height: 1.3;
}
.closing__secondary {
  margin-top: 0.5rem;
}

/* ---------- moonset: the page's lower edge is the moon's limb ---------- */

.moonset {
  position: relative;
  z-index: 1;
  height: clamp(5rem, 15vw, 15rem);
  margin-top: -1px;
  overflow: hidden;
  pointer-events: none;
}
.moonset__limb {
  position: absolute;
  left: 50%;
  bottom: 0;
  width: 190vw;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--white);
  transform: translateX(-50%);
}

/* ---------- footer: night, always the same ---------- */

/* main is opaque and sits above the footer, which waits underneath it */
main {
  position: relative;
  z-index: 1;
  background: var(--bg);
}

.site-footer {
  --bg: var(--black);
  --fg: var(--white);
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: clamp(2.5rem, 5vw, 4rem);
  /* the moonset band overlaps the top of the footer */
  margin-top: calc(-1 * clamp(5rem, 15vw, 15rem));
  padding: calc(clamp(5rem, 15vw, 15rem) + 2rem) var(--gutter) clamp(1.25rem, 2vw, 1.75rem);
  background: var(--bg);
  color: var(--fg);
}
.site-footer__logo {
  width: 100%;
  max-width: 62rem;
  color: var(--white);
}
.site-footer__side {
  display: grid;
  gap: clamp(2rem, 4vw, 3rem);
  align-content: end;
}
.site-footer__line {
  font-size: var(--step-2);
  line-height: 1.05;
  max-width: 12ch;
}
.site-footer__cols {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 2rem 1.5rem;
}
.site-footer__cols h2 {
  margin-bottom: 0.9rem;
  color: color-mix(in srgb, var(--white) 55%, transparent);
}
.site-footer__cols li + li {
  margin-top: 0.3rem;
}
.site-footer__cols a {
  text-decoration: none;
}
.site-footer__time {
  font-family: var(--font-mono);
  font-size: var(--step-0);
  font-weight: 300;
}
.site-footer__base {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid color-mix(in srgb, var(--white) 22%, transparent);
  color: color-mix(in srgb, var(--white) 62%, transparent);
}
.site-footer__seal {
  width: 1.9rem;
}

@media (min-width: 900px) {
  .site-footer {
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    grid-template-rows: 1fr auto;
    column-gap: clamp(3rem, 6vw, 7rem);
    min-height: 100vh;
    min-height: 100svh;
    padding-top: clamp(4rem, 10vh, 7rem);
  }
  .site-footer__logo {
    align-self: end;
    /* three stacked lines of lettering: size it by the height it may take */
    width: min(100%, calc((100svh - 17rem) * 1.397));
  }
  .site-footer__base {
    grid-column: 1 / -1;
  }
}
/* where it fits, the footer waits under the page and is uncovered by the setting moon */
@media (min-width: 900px) and (min-height: 640px) {
  .site-footer {
    position: sticky;
    bottom: 0;
    z-index: 0;
  }
}

/* ---------- small phase marker (case studies): the moon waxes as you read ---------- */

.phase {
  display: none;
}
.motion .phase {
  --size: clamp(0.8rem, 1.5vw, 1.4rem);
  position: fixed;
  /* it lives in the page margin, clear of the text */
  left: calc((var(--gutter) - var(--size)) / 2);
  bottom: clamp(1rem, 2vw, 1.6rem);
  z-index: 50;
  display: block;
  width: var(--size);
  height: var(--size);
  border-radius: 50%;
  background: var(--fg);
  overflow: hidden;
  transition: opacity 0.4s linear;
}
.phase__shadow {
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  background: var(--bg);
  transform: translateX(-12%);
}

/* ---------- page-to-page: night rises over the page, then lifts off the next one ----------
   A sheet with a curved leading and trailing edge (the moon's limb again) and
   the name of where you are going. The script covers the page, then lets the
   browser navigate normally; the next page starts covered and uncovers itself.
   Real page loads, real URLs, no router. */

.curtain {
  position: fixed;
  inset: 0;
  z-index: 150;
  display: grid;
  place-items: center;
  visibility: hidden;
  pointer-events: none;
  color: var(--white);
}
.curtain__sheet {
  position: absolute;
  inset: -14vh -20vw;
  border-radius: 50% / 14vh;
  background: var(--black);
  transform: translateY(112%);
}
/* a small moon over the name: it waxes while the page changes, and is full on arrival */
.curtain {
  align-content: center;
  justify-items: center;
  gap: clamp(1.25rem, 3vh, 2rem);
}
.curtain__moon {
  position: relative;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 50%;
  background: var(--white);
  overflow: hidden;
  opacity: 0;
}
.curtain__moon i {
  position: absolute;
  inset: -1px;
  border-radius: 50%;
  background: var(--black);
  transform: translateX(-104%);
}
.is-arriving .curtain__moon {
  opacity: 1;
}
.curtain__label {
  position: relative;
  padding: 0 var(--gutter);
  font-family: var(--font-display);
  font-weight: 300;
  font-size: var(--step-3);
  line-height: 1;
  letter-spacing: -0.012em;
  text-align: center;
}
/* arriving: covered from the very first paint (a head script sets the class) */
.is-arriving .curtain {
  visibility: visible;
  /* if the script never arrives, get out of the way */
  animation: intro-failsafe 0.01s 3s forwards;
}
.is-arriving .curtain__sheet {
  transform: none;
}

/* ---------- pause: one rabbit, one line, a whole screen of quiet ----------
   A breath between busy sections. It takes the opposite colour of the section
   before it (Stephen): white with the black seal, or .pause--night. The seal waxes in as it scrolls into view. */

.pause {
  display: grid;
  place-items: center;
  align-content: center;
  gap: clamp(1rem, 2vw, 1.6rem);
  min-height: 100vh;
  min-height: 100svh;
  padding: clamp(4rem, 10vh, 6rem) var(--gutter);
  background: var(--bg);
  color: var(--fg);
  text-align: center;
}
.pause--night {
  --bg: var(--black);
  --fg: var(--white);
  /* derived tones are worked out where --fg is set, so they are set again here */
  --rule: color-mix(in srgb, var(--fg) 18%, transparent);
  --soft: color-mix(in srgb, var(--fg) 62%, transparent);
}
.pause__seal {
  width: clamp(5.5rem, 9vw, 8.5rem);
  margin-bottom: clamp(1rem, 4vh, 2.5rem);
}
.pause--night .pause__seal {
  filter: invert(1);
}
.pause__kicker {
  color: var(--soft);
}
.pause__line {
  font-size: var(--step-3);
  max-width: 13ch;
}
.pause .link-arrow {
  margin-top: 0.75rem;
}
/* the moon's shadow slides off the seal as it arrives (the script moves --shadow) */
.motion .pause__seal {
  --shadow: 50%;
  -webkit-mask-image: radial-gradient(circle at var(--shadow) 46%, transparent 49%, #000 50%);
  mask-image: radial-gradient(circle at var(--shadow) 46%, transparent 49%, #000 50%);
}

/* ---------- the book: a cover as an object you can pick up ----------
   The cover picture is the front board of a real volume: spine, page block,
   a black cloth back with the seal stamped on it. Everything is driven by
   two numbers, --ry and --rx (degrees): the faces turn, and the light on
   each face is worked out from the same angle, so a script only ever has to
   move those two. Where container units or CSS trigonometry are missing it
   is simply the picture with a shadow. */

@property --ry {
  syntax: "<number>";
  inherits: true;
  initial-value: 26;
}
@property --rx {
  syntax: "<number>";
  inherits: true;
  initial-value: -4;
}

.book {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: var(--ar, 2 / 3);
  box-shadow: 0 1.2rem 2.4rem -1rem rgba(0, 0, 0, 0.4);
}
.book__stage,
.book__body,
.book__front {
  display: block;
  height: 100%;
}
.book__front img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.book__back,
.book__spine,
.book__fore,
.book__top,
.book__bottom,
.book__shadow,
.book__sheen {
  display: none;
}

@supports (width: 1cqw) and (width: calc(sin(1) * 1px)) {
  .book {
    /* thickness, and how far the boards stand proud of the pages */
    --d: 13cqw;
    --lip: 1.3cqw;
    container-type: inline-size;
    box-shadow: none;
    transition: --ry 0.9s var(--ease-out), --rx 0.9s var(--ease-out);
  }
  /* a wide picture book: the same boards, so a thinner block for its width */
  .book.book--wide {
    --d: 6cqw;
    --lip: 0.8cqw;
  }
  /* a script is steering it frame by frame: no easing on top */
  .book.is-live {
    transition: none;
  }
  .book__stage {
    position: absolute;
    inset: 0;
    perspective: 420cqw;
  }
  .book__body {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
    transform: rotateX(calc(var(--rx) * 1deg)) rotateY(calc(var(--ry) * 1deg));
  }
  .book__front,
  .book__back,
  .book__spine,
  .book__fore,
  .book__top,
  .book__bottom {
    position: absolute;
    display: block;
    overflow: hidden;
    backface-visibility: hidden;
  }
  .book__front,
  .book__back {
    inset: 0;
    height: auto;
  }
  .book__front {
    transform: translateZ(calc(var(--d) / 2));
    background: #1a1a1b;
  }
  /* the hinge, pressed into the board a little way in from the spine */
  .book__front::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(90deg, rgba(0, 0, 0, 0.28), rgba(255, 255, 255, 0.14) 1.4%, rgba(0, 0, 0, 0.16) 3.2%, transparent 5.6%);
  }
  /* the board falls into shade as it turns away from the light */
  .book__front::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 2;
    background: #000;
    opacity: clamp(0, 0.5 * (1 - cos(var(--ry) * 1deg)), 0.6);
  }
  /* a band of light that slides across the cover as it turns */
  .book__sheen {
    position: absolute;
    inset: 0;
    z-index: 3;
    display: block;
    background: linear-gradient(100deg, transparent 32%, rgba(255, 255, 255, 0.34) 49%, transparent 64%) no-repeat;
    background-size: 260% 100%;
    background-position: calc(50% + var(--ry) * 2.4%) 0;
    mix-blend-mode: soft-light;
  }
  /* the back board takes the cover's own ground (--back, sampled when the page
     is built), with the seal stamped small in the middle: black, or a lighter
     tone of the same colour where black would vanish (--back-seal) */
  .book__back {
    transform: rotateY(180deg) translateZ(calc(var(--d) / 2));
    background: var(--back, var(--black));
  }
  .book__back::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--back-seal, var(--white));
    -webkit-mask: url("/assets/brand/seal-black.svg") 50% 47% / 8.5% auto no-repeat;
    mask: url("/assets/brand/seal-black.svg") 50% 47% / 8.5% auto no-repeat;
  }
  .book__back::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(100deg, rgba(255, 255, 255, 0.12), transparent 45%, rgba(0, 0, 0, 0.08));
  }
  /* the jacket wraps: the spine carries the colour of the cover's first sliver,
     out of focus so no cut-off lettering shows, and in shade */
  .book__spine {
    left: calc(var(--d) / -2);
    top: 0;
    width: var(--d);
    height: 100%;
    transform: rotateY(-90deg);
    background: #1a1a1b;
  }
  /* printed solid in the colour down the cover's left edge (build: spine_of) */
  .book__spine::before {
    content: "";
    position: absolute;
    inset: 0;
    background: var(--spine, #1a1a1b);
  }
  .book__spine::after {
    content: "";
    position: absolute;
    inset: 0;
    background:
      linear-gradient(90deg, rgba(0, 0, 0, 0.42), transparent 22%, transparent 78%, rgba(0, 0, 0, 0.3)),
      rgba(0, 0, 0, clamp(0.08, 0.5 - 0.42 * sin(var(--ry) * 1deg), 0.6));
  }
  /* the page block: paper, with the grain of its leaves */
  .book__fore,
  .book__top,
  .book__bottom {
    background: #efede8;
  }
  .book__fore {
    right: calc((var(--d) - var(--lip)) / -2);
    top: var(--lip);
    bottom: var(--lip);
    width: calc(var(--d) - var(--lip));
    transform: rotateY(90deg) translateZ(calc(var(--lip) * -1.2));
    background-image:
      linear-gradient(90deg, rgba(0, 0, 0, 0.3), transparent 14%, transparent 86%, rgba(0, 0, 0, 0.3)),
      repeating-linear-gradient(90deg, transparent 0 0.45cqw, rgba(0, 0, 0, 0.11) 0.45cqw 0.6cqw);
  }
  .book__top,
  .book__bottom {
    left: 0;
    right: calc(var(--lip) * 1.2);
    height: calc(var(--d) - var(--lip));
    background-image:
      linear-gradient(rgba(0, 0, 0, 0.3), transparent 14%, transparent 86%, rgba(0, 0, 0, 0.3)),
      repeating-linear-gradient(transparent 0 0.45cqw, rgba(0, 0, 0, 0.11) 0.45cqw 0.6cqw);
  }
  .book__top {
    top: calc((var(--d) - var(--lip)) / -2);
    transform: rotateX(90deg) translateZ(calc(var(--lip) * -1));
  }
  .book__bottom {
    bottom: calc((var(--d) - var(--lip)) / -2);
    transform: rotateX(-90deg) translateZ(calc(var(--lip) * -1));
  }
  /* where it stands: a soft contact shadow that leans away from the light */
  .book__shadow {
    position: absolute;
    /* under the volume, never over it: turned toward the reader, the near
       corner of the book comes down past its own box, and a shadow painted on
       top made it look cut off */
    z-index: -1;
    left: -4%;
    right: -4%;
    top: 97%;
    display: block;
    height: 13cqw;
    border-radius: 50%;
    background: radial-gradient(closest-side, rgba(0, 0, 0, 0.36), rgba(0, 0, 0, 0.12) 55%, transparent);
    filter: blur(1.6cqw);
    transform: translateX(calc(sin(var(--ry) * 1deg) * 7cqw)) scaleX(calc(0.62 + 0.38 * cos(var(--ry) * 1deg) * cos(var(--ry) * 1deg)));
    pointer-events: none;
  }
}

/* ---------- the sleeve: the "Yellow" record, whole ----------
   The square jacket with the yellow disc part-way out of it. --out is how far
   the disc has slid (0 = tucked in, 0.5 = half out); a script or a hover moves
   it, and the disc turns a little as it comes. Nothing is cropped. */

@property --out {
  syntax: "<number>";
  inherits: true;
  initial-value: 0.4;
}
.sleeve {
  --out: 0.4;
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1.52 / 1;
  transition: --out 0.9s var(--ease-out);
}
.sleeve__jacket {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
  height: 100%;
  aspect-ratio: var(--jacket, 2560 / 2482);
  box-shadow: 0.4rem 0.6rem 1.6rem -0.6rem rgba(0, 0, 0, 0.45);
}
.sleeve__jacket img,
.sleeve__disc img {
  display: block;
  width: 100%;
  height: 100%;
}
.sleeve__disc {
  position: absolute;
  left: 0;
  top: 1.5%;
  height: 97%;
  aspect-ratio: 1;
  translate: calc(var(--out) * 100%) 0;
  rotate: calc(var(--out) * 110deg);
  filter: drop-shadow(0.3rem 0.5rem 0.7rem rgba(0, 0, 0, 0.28));
}
/* the disc is photographed on a dark ground: cut it round */
.sleeve__disc img {
  object-fit: cover;
  clip-path: var(--disc-clip, circle(47.2% at 50% 49.9%));
}
@media (hover: hover) and (pointer: fine) {
  a:hover .sleeve {
    --out: 0.5;
  }
}

/* ---------- the pause, with the crescent and its hare ----------
   The scene that used to be the 404: a hare sitting in the bowl of a huge
   crescent. The line sits in the dark of the bowl. */

.pause--crescent {
  position: relative;
  place-items: start;
  align-content: start;
  overflow: hidden;
  isolation: isolate;
  text-align: left;
  padding-top: clamp(6rem, 14vh, 9rem);
}
.pause__sky {
  position: absolute;
  inset: -6% 0;
  z-index: -1;
}
.pause__sky img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* keep the hare in frame on a tall screen: he sits about a fifth of the way in */
  object-position: 10% 100%;
}
.pause__copy {
  display: grid;
  gap: clamp(1rem, 2vw, 1.6rem);
  justify-items: start;
}
@media (min-width: 760px) and (min-aspect-ratio: 1 / 1) {
  .pause--crescent {
    align-content: center;
    padding-left: 30vw;
    padding-bottom: 20vh;
  }
  .pause__sky img {
    object-position: 50% 50%;
  }
}
