/* ffuffix site styles, built on Halcyon. index.html loads the vendored
   assets/halcyon/halcyon-<version>.css first; this file names its public --h-*
   tokens and never a --_h-* primitive. tests/halcyon.test.mjs checks both, and
   that every token named here exists in the vendored copy.

   The theme is Halcyon's dark theme seeded on pure black, because the mascot
   art, the splash and the film are all drawn on black. The step is wide enough
   to lift --h-bg-panel to #0c0c0c. Nothing fits below black, so every floating
   and sunken surface shares the page's fill and is read by its ring or its
   hairline instead.

   Halcyon has no vocabulary for three things this site does, so they stay as
   plain values here:

   - display type: the post, access, contact, 404, film and projects titles,
     and the names and years on the projects page, are sized past
     --h-text-title-1 and scale with the viewport. The years are set lighter
     than any Halcyon weight. The film and contact titles share a serif,
     --font-display, which shows nothing while it loads (font-display: block)
     so neither title is ever seen in the fallback face first;
   - art-directed motion: the splash, the mascot, the film, the footer bunny and
     the toast's slow dissolve run longer than any Halcyon step, and the
     stagger delays have no token. Entrances that fit a step use
     --h-transition-enter; opacity-only fades run under reduced motion too,
     because Halcyon removes movement there, not time;
   - art colours (--art-*): the film title is painted to match rendered
     footage, and the contact scene's still backdrop to match its shader, not
     the theme. That backdrop is shown only where no shader will be drawn. The
     shader picks a palette for each visit, so any colour under the canvas
     while it arrives is a colour the page then has to cross over from.

   The header blur stacks five backdrop-filter layers, each masked to fade
   downward only.

   The projects timeline has one rail behind the whole list and a node on each
   entry, drawn from ::after. --node-y sets a node level with the middle of its
   entry's name, and the year beside it is centred on the same line. Space
   between entries is padding inside an entry (--entry-gap), and a year label
   keeps the same distance clear beneath it, which is what stops it short of
   the next year's label when it is held at the top of the window.

   The timeline's scroll motion is three animations, each written once. Every
   one is paused and placed by a custom property (--rise, --recede) that rests
   where nothing has moved, so a page that nothing drives is fully shown. Where
   the browser has scroll timelines, an @supports block hands the same
   animations to them. Elsewhere assets/site/timeline-motion.mjs writes the
   properties, and reads --timeline-band and --intro-recede to do it, which is
   why both are registered as lengths: a registered length reads back in
   pixels. tests/timeline-motion.test.mjs holds the two sides together.

   Everything that arrives does so through a band at the foot of the window,
   --timeline-band tall. The playhead waits at the top of that band, lower by
   --node-y, so a node finishes lighting as it reaches it. The playhead is a
   sticky mark with a lit tail one window tall, and the rail clips the tail, so
   the rail is lit above the playhead by layout alone. An entry's links are
   shown at once while one of them has keyboard focus, because focus can land
   inside the band. */

@font-face {
  font-family: Inter;
  font-style: normal;
  font-weight: 300 800;
  font-display: swap;
  src: url("/assets/fonts/inter-latin.woff2") format("woff2");
}

/* Arial reshaped to Inter's measurements, so text keeps its size and line
   breaks when Inter arrives and replaces it. Liberation Sans is Arial's
   metric twin on Linux. */
@font-face {
  font-family: "Inter fallback";
  src: local("Arial"), local("Liberation Sans"), local("Helvetica");
  ascent-override: 90.44%;
  descent-override: 22.52%;
  line-gap-override: 0%;
  size-adjust: 107.12%;
}

@font-face {
  font-family: "Noto Serif Display";
  font-style: normal;
  font-weight: 500;
  font-display: block;
  src: url("/assets/fonts/noto-serif-display-medium-latin.woff2") format("woff2");
}

@layer halcyon.seed {
  [data-theme="ffuffix"] {
    color-scheme: dark;
    --h-seed-bg: oklch(0 0 0);
    --h-seed-fg: oklch(0.97 0 0);
    --h-seed-accent: oklch(0.97 0 0);
    --h-step: 0.154;
    --h-contrast: 1;
  }
}

:root {
  --h-font-sans: Inter, "Inter fallback", system-ui, sans-serif;
  --font-display: "Noto Serif Display", Georgia, serif;

  --art-film-text: #f7eed9;
  --art-film-glow: rgb(255 179 115 / 32%);
  --art-film-soften: 0.6px;
  --art-contact-ember: #3b230b;
  --art-contact-ash: #100b08;

  --space-content: var(--h-space-5);
  --width-content: 72rem;
  --height-header: 4rem;

  --header-fade: var(--h-bg-fade);
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
  font-size: var(--h-text-large);
  overflow-x: clip;
}

::selection {
  background: var(--h-accent-bg);
  color: var(--h-fg-on-accent);
}

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

/* Toasts slide in at the top centre and dissolve with a blur. They sit over
   the page, so one appearing never moves anything, and pause while hovered.
   The account pages in worker/accountPages.ts draw the same toast. */
.toast-region {
  position: fixed;
  inset-block-start: var(--h-space-4);
  inset-inline: 0;
  z-index: var(--h-z-toast);
  width: max-content;
  max-width: calc(100vw - 2 * var(--h-space-4));
  margin-inline: auto;
  pointer-events: none;
}

.toast {
  padding: var(--h-space-3) var(--h-space-4);
  border-radius: var(--h-radius-full);
  background: var(--h-bg-raised);
  color: var(--h-fg-primary);
  font-size: var(--h-text-base);
  line-height: var(--h-leading-snug);
  box-shadow: var(--h-shadow-medium);
  pointer-events: auto;
  animation:
    toast-in var(--h-transition-enter) both,
    toast-out 700ms var(--h-ease-in-out) 3.6s forwards;
}

.toast--bad {
  background: var(--h-danger-bg-subtle);
  color: var(--h-danger-fg);
}

.toast:hover {
  animation-play-state: paused;
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(calc(-1 * var(--h-travel-2))) scale(0.97);
    filter: blur(var(--h-blur-3));
  }
}

@keyframes toast-out {
  to {
    opacity: 0;
    transform: scale(0.98);
    filter: blur(var(--h-blur-3));
    visibility: hidden;
  }
}

@media (prefers-reduced-motion: reduce) {
  .toast {
    animation-name: toast-fade-in, toast-fade-out;
  }

  @keyframes toast-fade-in {
    from {
      opacity: 0;
    }
  }

  @keyframes toast-fade-out {
    to {
      opacity: 0;
      visibility: hidden;
    }
  }
}

.skip-link {
  position: fixed;
  inset-block-start: var(--h-space-2);
  inset-inline-start: var(--h-space-2);
  z-index: var(--h-z-overlay);
  padding: var(--h-space-3) var(--h-space-4);
  border-radius: var(--h-radius-lg);
  background: var(--h-accent-bg);
  color: var(--h-fg-on-accent);
  font-size: var(--h-text-base);
  font-weight: var(--h-weight-medium);
  text-decoration: none;
  transform: translateY(calc(-100% - var(--h-space-4)));
  transition: transform var(--h-transition-overlay);
}

.skip-link:focus {
  transform: translateY(0);
}

/* It slides by its own height, which no travel token covers, so Halcyon's
   reduced motion cannot reach it. */
@media (prefers-reduced-motion: reduce) {
  .skip-link {
    transition: none;
  }
}

.site-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--h-z-sticky);
  height: var(--height-header);
}

.site-header__blur {
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  height: calc(var(--height-header) * 1.25);
  overflow-x: clip;
  pointer-events: none;
}

.site-header__blur > span {
  position: absolute;
  inset: 0;
  backdrop-filter: blur(var(--blur));
  -webkit-backdrop-filter: blur(var(--blur));
  mask-image: var(--band);
  -webkit-mask-image: var(--band);
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
}

.site-header__blur > span:nth-child(1) {
  --blur: var(--h-blur-1);
  --band: linear-gradient(to bottom, black 0%, black 74%, transparent 100%);
}

.site-header__blur > span:nth-child(2) {
  --blur: var(--h-blur-1);
  --band: linear-gradient(to bottom, black 0%, black 59%, transparent 85%);
}

.site-header__blur > span:nth-child(3) {
  --blur: var(--h-blur-2);
  --band: linear-gradient(to bottom, black 0%, black 44%, transparent 70%);
}

.site-header__blur > span:nth-child(4) {
  --blur: var(--h-blur-3);
  --band: linear-gradient(to bottom, black 0%, black 29%, transparent 55%);
}

.site-header__blur > span:nth-child(5) {
  --blur: var(--h-blur-4);
  --band: linear-gradient(to bottom, black 0%, black 14%, transparent 40%);
}

/* The tint goes over the ramp, inset past the header and blurred so its own
   edges fall outside it rather than banding inside. */
.site-header__blur::after {
  content: "";
  position: absolute;
  inset: calc(-1 * var(--h-space-5));
  background: var(--header-fade);
  filter: blur(var(--h-blur-3));
}

body:has(.contact-scene) {
  --header-fade: linear-gradient(
    to bottom,
    color-mix(in oklab, var(--h-bg-app) 55%, transparent),
    color-mix(in oklab, var(--h-bg-app) 25%, transparent) 55%,
    transparent
  );
}

.site-header__bar {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--h-space-4);
  height: 100%;
  padding-inline: var(--space-content);
}

.site-header__wordmark {
  color: var(--h-fg-primary);
  font-size: var(--h-text-large);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-tight);
  text-decoration: none;
}

.site-nav {
  display: flex;
  align-items: center;
  gap: var(--h-space-5);
}

.account {
  display: inline-flex;
  width: var(--h-control-md);
  height: var(--h-control-md);
  padding: 0;
  border: 0;
  border-radius: var(--h-radius-full);
  overflow: hidden;
  background: var(--h-accent-bg);
}

button.account {
  cursor: pointer;
}

.account--pending {
  visibility: hidden;
}

.account__photo,
.account__blank {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.site-nav__link {
  color: var(--h-fg-secondary);
  font-size: var(--h-text-base);
  text-decoration: none;
  transition: color var(--h-transition-hover);
}

.site-nav__link:hover,
.site-nav__link:focus-visible,
.site-nav__link[aria-current="page"] {
  color: var(--h-fg-primary);
}

.site-main {
  flex: 1 0 auto;
  width: 100%;
  max-width: var(--width-content);
  margin-inline: auto;
  padding: var(--h-space-7) var(--space-content);
}

.site-main:focus {
  outline: none;
}

.post-featured {
  display: grid;
  gap: var(--h-space-5);
  margin-block-start: var(--h-space-6);
  color: inherit;
  text-decoration: none;
}

.post-featured:has(.post-featured__figure) {
  grid-template-columns: minmax(0, 3fr) minmax(0, 2fr);
  align-items: center;
  gap: var(--h-space-7);
}

.post-featured__figure {
  margin: 0;
  border-radius: var(--h-radius-xl);
  overflow: hidden;
}

.post-featured__body {
  display: grid;
  gap: var(--h-space-3);
}

.post-featured__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--h-space-3);
  margin: 0;
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-base);
  font-variant-numeric: tabular-nums;
}

.post-featured__category,
.post-link__category {
  font-size: var(--h-text-micro);
  font-weight: var(--h-weight-medium);
  letter-spacing: var(--h-tracking-wide);
  text-transform: uppercase;
}

.post-featured__title {
  margin: 0;
  font-size: clamp(1.75rem, 4vw, 2.5rem);
  font-weight: var(--h-weight-semibold);
  line-height: 1.1;
  letter-spacing: -0.03em;
  text-wrap: balance;
  text-decoration: underline transparent;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: text-decoration-color var(--h-transition-hover);
}

.post-featured:hover .post-featured__title,
.post-featured:focus-visible .post-featured__title {
  text-decoration-color: currentColor;
}

.post-featured__lede {
  margin: 0;
  color: var(--h-fg-secondary);
  font-size: var(--h-text-large);
  line-height: var(--h-leading-normal);
  text-wrap: pretty;
}

.post-list {
  margin: var(--h-space-8) 0 0;
  padding: 0;
  list-style: none;
  border-block-start: 1px solid var(--h-border-subtle);
}

.post-list > li {
  border-block-end: 1px solid var(--h-border-subtle);
}

.post-link {
  display: grid;
  grid-template-columns: 7.5rem 11rem minmax(0, 1fr);
  align-items: baseline;
  column-gap: var(--h-space-5);
  row-gap: var(--h-space-1);
  margin-inline: calc(-1 * var(--h-space-3));
  padding: var(--h-space-5) var(--h-space-3);
  color: inherit;
  text-decoration: none;
  transition: background var(--h-transition-hover);
}

.post-link:hover {
  background: var(--h-bg-hover);
}

.post-link__date {
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-base);
  font-variant-numeric: tabular-nums;
}

.post-link__category {
  color: var(--h-fg-tertiary);
}

.post-link__title {
  font-size: var(--h-text-large);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-tight);
}

.post-link__description {
  grid-column: 3;
  color: var(--h-fg-secondary);
  font-size: var(--h-text-large);
  line-height: var(--h-leading-normal);
}

@media (max-width: 48rem) {
  .post-featured:has(.post-featured__figure) {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--h-space-5);
  }

  .post-link {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .post-link__title {
    grid-column: 1 / -1;
  }

  .post-link__description {
    display: none;
  }
}

@property --timeline-band {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

@property --intro-recede {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

.projects-intro {
  --intro-recede: min(60vh, 30rem);

  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: end;
  gap: var(--h-space-6) var(--h-space-7);
  padding-block: clamp(var(--h-space-5), 7vh, var(--h-space-8)) var(--h-space-6);
  border-block-end: 1px solid var(--h-border-subtle);
}

.projects-intro__title {
  grid-column: 1 / -1;
  margin: 0;
  font-size: clamp(3.5rem, 14vw, 11rem);
  font-weight: var(--h-weight-semibold);
  letter-spacing: -0.05em;
  line-height: 0.9;
  transform-origin: 0 100%;
}

.projects-intro__lede {
  max-width: 26rem;
  margin: 0;
  color: var(--h-fg-secondary);
  font-size: var(--h-text-title-3);
  letter-spacing: var(--h-tracking-tight);
  line-height: var(--h-leading-snug);
}

.projects-intro__span {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--h-space-2) var(--h-space-6);
  margin: 0;
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-mini);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--h-tracking-wide);
  text-transform: uppercase;
}

.projects-intro__years {
  display: inline-flex;
  align-items: center;
  gap: var(--h-space-3);
}

.projects-intro__rule {
  width: var(--h-space-7);
  height: 1px;
  background: currentColor;
}

.timeline {
  --timeline-band: min(30vh, 16rem);
  --name-size: clamp(2rem, 1.25rem + 3vw, 3.5rem);
  --name-leading: 1.1;
  --node: 0.6875rem;
  --node-y: calc(var(--name-size) * var(--name-leading) / 2);
  --year-size: clamp(4.5rem, 9.5vw, 8.5rem);
  --year-column: clamp(13rem, 25vw, 21rem);
  --rail-gap: var(--h-space-8);
  --rail-width: var(--h-space-6);
  --rail-x: calc(var(--year-column) + var(--rail-gap) / 2);
  --entry-gap: clamp(var(--h-space-8), 16vh, 9rem);

  position: relative;
  margin-block-start: calc(var(--h-space-8) + var(--h-space-6));
}

.timeline__rail {
  position: absolute;
  inset-block: var(--node-y) 0;
  inset-inline-start: calc(var(--rail-x) - var(--rail-width) / 2);
  width: var(--rail-width);
  overflow: clip;
  background: linear-gradient(var(--h-border-default), var(--h-border-default)) center / 1px 100%
    no-repeat;
  mask-image: linear-gradient(black calc(100% - 2 * var(--h-space-8)), transparent);
  pointer-events: none;
}

.timeline__playhead {
  display: none;
}

.projects,
.project-year__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.project-year {
  display: grid;
  grid-template-columns: var(--year-column) minmax(0, 1fr);
  column-gap: var(--rail-gap);
}

.project-year__label {
  position: sticky;
  inset-block-start: calc(var(--height-header) + var(--h-space-5));
  align-self: start;
  margin: calc(var(--node-y) - var(--year-size) / 2) 0 var(--entry-gap);
  color: var(--h-fg-primary);
  font-size: var(--year-size);
  font-weight: 300;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.05em;
  line-height: 1;
  text-align: end;
}

.project-year__list {
  grid-column: 2;
  min-width: 0;
}

.project {
  position: relative;
  padding-block-end: var(--entry-gap);
  scroll-margin-block-start: calc(var(--height-header) + var(--h-space-6));
}

.project::after {
  content: "";
  position: absolute;
  inset-block-start: calc(var(--node-y) - var(--node) / 2);
  inset-inline-start: calc(var(--rail-gap) / -2 - var(--node) / 2);
  width: var(--node);
  height: var(--node);
  border: 1px solid var(--h-fg-primary);
  border-radius: var(--h-radius-full);
  background: var(--h-fg-primary);
  transition: box-shadow var(--h-transition-layout);
}

.project--deprecated::after {
  border-color: var(--h-fg-tertiary);
  background: var(--h-bg-app);
}

.project:target::after {
  box-shadow: 0 0 0 var(--h-space-2) var(--h-border-default);
}

.project__body {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--h-space-4);
  min-width: 0;
}

.project__name {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--h-space-2) var(--h-space-4);
  margin: 0;
  font-size: var(--name-size);
  font-weight: var(--h-weight-semibold);
  letter-spacing: -0.035em;
  line-height: var(--name-leading);
}

.project--deprecated .project__title {
  color: var(--h-fg-tertiary);
  text-decoration: line-through;
  text-decoration-color: color-mix(in oklab, currentColor 45%, transparent);
  text-decoration-thickness: 1px;
}

.project__status {
  padding: var(--h-space-1) var(--h-space-2);
  border: 1px solid var(--h-border-default);
  border-radius: var(--h-radius-sm);
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-micro);
  font-weight: var(--h-weight-medium);
  letter-spacing: var(--h-tracking-wide);
  line-height: var(--h-leading-tight);
  text-transform: uppercase;
}

.project__role {
  margin: 0;
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-mini);
  letter-spacing: var(--h-tracking-wide);
  text-transform: uppercase;
}

.project__description {
  max-width: 38rem;
  margin: var(--h-space-2) 0 0;
  color: var(--h-fg-secondary);
  font-size: var(--h-text-large);
  line-height: var(--h-leading-normal);
}

.project__stack {
  display: flex;
  flex-wrap: wrap;
  gap: var(--h-space-2);
  margin: var(--h-space-1) 0 0;
  padding: 0;
  list-style: none;
}

.project__stack li {
  padding: var(--h-space-1) var(--h-space-3);
  border: 1px solid var(--h-border-subtle);
  border-radius: var(--h-radius-full);
  background: var(--h-bg-panel);
  color: var(--h-fg-secondary);
  font-size: var(--h-text-small);
}

.project__link {
  display: inline-flex;
  align-items: center;
  gap: var(--h-space-1);
  margin-block-start: var(--h-space-1);
  color: var(--h-fg-primary);
  font-size: var(--h-text-large);
  font-weight: var(--h-weight-medium);
  text-decoration: none;
}

.project__link-arrow {
  transition: transform var(--h-transition-hover);
}

.project__link:hover .project__link-arrow,
.project__link:focus-visible .project__link-arrow {
  transform: translate(2px, -2px);
}

@media (max-width: 48rem) {
  .projects-intro {
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
  }

  .timeline {
    --year-size: clamp(3.5rem, 19vw, 5rem);
    --rail-gap: var(--h-space-7);
    --rail-x: calc(var(--node) / 2);
  }

  .timeline__rail {
    inset-block-start: 0;
  }

  .project-year {
    display: block;
    padding-inline-start: calc(var(--rail-gap) / 2 + var(--node) / 2);
  }

  .project-year__label {
    position: static;
    margin: 0 0 var(--h-space-6);
    text-align: start;
  }

  .project::after {
    inset-inline-start: calc(var(--rail-gap) / -2);
  }
}

@keyframes timeline-rise {
  from {
    opacity: 0;
    translate: 0 calc(2 * var(--h-travel-3));
  }
}

@keyframes timeline-ignite {
  from,
  75% {
    opacity: 0.3;
    scale: 0.5;
  }
}

@keyframes intro-recede {
  to {
    opacity: 0;
    scale: 0.92;
  }
}

@media screen and (prefers-reduced-motion: no-preference) {
  .timeline__playhead {
    display: block;
    position: sticky;
    inset-block-start: calc(100vh - var(--timeline-band) + var(--node-y));
    height: 0;
  }

  .timeline__playhead::before,
  .timeline__playhead::after {
    content: "";
    position: absolute;
  }

  .timeline__playhead::before {
    inset-block-end: 0;
    inset-inline-start: calc(50% - 0.5px);
    width: 1px;
    height: 100vh;
    background: linear-gradient(to top, var(--h-fg-primary), var(--h-fg-tertiary) var(--timeline-band));
  }

  .timeline__playhead::after {
    inset-block-start: calc(var(--h-space-1) / -2);
    inset-inline-start: calc(50% - var(--h-space-1) / 2);
    width: var(--h-space-1);
    height: var(--h-space-1);
    border-radius: var(--h-radius-full);
    background: var(--h-fg-primary);
    box-shadow: 0 0 var(--h-space-3) var(--h-space-1) color-mix(in oklab, var(--h-fg-primary) 45%, transparent);
  }

  .project__body > *,
  .project-year__label {
    animation: timeline-rise 1s linear both paused;
    animation-delay: calc(var(--rise, 1) * -1s);
  }

  .project::after {
    animation: timeline-ignite 1s linear both paused;
    animation-delay: calc(var(--rise, 1) * -1s);
  }

  .projects-intro__title {
    animation: intro-recede 1s linear both paused;
    animation-delay: calc(var(--recede, 0) * -1s);
  }

  .project__links:has(:focus-visible) {
    animation: none;
  }

  @supports ((animation-timeline: view()) and (animation-range: entry)) {
    .project {
      view-timeline: --project block;
    }

    .project::after,
    .project__body > *,
    .project-year__label,
    .projects-intro__title {
      animation-duration: auto;
      animation-delay: 0s;
      animation-play-state: running;
    }

    .project__body > *,
    .project-year__label {
      animation-timeline: view();
      animation-range: cover 0% cover var(--timeline-band);
    }

    .project::after {
      animation-timeline: --project;
      animation-range: cover 0% cover var(--timeline-band);
    }

    .projects-intro__title {
      animation-timeline: scroll(root block);
      animation-range: 0px var(--intro-recede);
    }
  }
}

.post > * {
  max-width: 40rem;
  margin-inline: auto;
}

.post__contents {
  position: fixed;
  inset-block-start: 50%;
  inset-inline-start: var(--space-content);
  z-index: var(--h-z-sticky);
  max-width: none;
  margin: 0;
  translate: 0 -50%;
}

.post-contents__list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.post-contents__link {
  position: relative;
  display: flex;
  align-items: center;
  width: 3.5rem;
  height: 1.75rem;
  text-decoration: none;
}

.post-contents__tick {
  display: block;
  width: 1rem;
  height: 2px;
  border-radius: var(--h-radius-full);
  background: var(--h-border-strong);
  transition:
    background var(--h-transition-hover),
    opacity var(--h-transition-overlay);
}

@media (prefers-reduced-motion: no-preference) {
  .post-contents__tick {
    transition:
      background var(--h-transition-hover),
      opacity var(--h-transition-overlay),
      width var(--h-duration-3) var(--h-ease-out);
  }
}

.post-contents__link[aria-current="location"] .post-contents__tick {
  width: 1.5rem;
  background: var(--h-fg-primary);
}

.post-contents__link:hover .post-contents__tick,
.post-contents__link:focus-visible .post-contents__tick {
  width: 1.75rem;
  background: var(--h-fg-primary);
}

@media (hover: hover) {
  .post-contents__list:has(.post-contents__link:hover)
    .post-contents__link:not(:hover)
    .post-contents__tick {
    opacity: 0.4;
  }
}

.post-contents__label {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: calc(1.75rem + var(--h-space-3));
  translate: calc(-1 * var(--h-travel-2)) -50%;
  max-width: 16rem;
  padding: var(--h-space-1) var(--h-space-3);
  overflow: hidden;
  border-radius: var(--h-radius-full);
  background: var(--h-accent-bg);
  color: var(--h-fg-on-accent);
  font-size: var(--h-text-base);
  line-height: var(--h-leading-snug);
  text-overflow: ellipsis;
  white-space: nowrap;
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity var(--h-transition-overlay),
    visibility var(--h-transition-overlay),
    translate var(--h-duration-3) var(--h-ease-out);
}

.post-contents__link:hover .post-contents__label,
.post-contents__link:focus-visible .post-contents__label {
  translate: 0 -50%;
  opacity: 1;
  visibility: visible;
}

@media (max-width: 56rem) {
  .post__contents {
    display: none;
  }
}

.post__header {
  text-align: center;
  padding-block-start: var(--h-space-5);
}

.post__tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--h-space-5);
  margin-block-end: var(--h-space-5);
}

.post__tag {
  font-size: var(--h-text-small);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-tight);
}

.post__title {
  margin: 0;
  font-size: clamp(2.25rem, 5.5vw, 3.25rem);
  font-weight: var(--h-weight-semibold);
  line-height: 1.1;
  letter-spacing: -0.03em;
  text-wrap: balance;
}

.post__date {
  display: block;
  margin-block-start: var(--h-space-5);
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-base);
  font-variant-numeric: tabular-nums;
}

.post__figure {
  max-width: 56rem;
  margin: var(--h-space-7) auto 0;
  border-radius: var(--h-radius-xl);
  overflow: hidden;
}

.post__image {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  transition:
    opacity var(--h-transition-enter),
    filter var(--h-transition-enter),
    transform var(--h-transition-enter);
}

.js .post__image {
  opacity: 0;
  filter: blur(var(--h-blur-4));
  transform: scale(1.04);
}

.js .post__image--loaded {
  opacity: 1;
  filter: none;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .post__image {
    transition: opacity var(--h-transition-overlay);
  }

  .js .post__image {
    filter: none;
    transform: none;
  }
}

.post__footer {
  margin-block-start: var(--h-space-8);
  padding-block-start: var(--h-space-6);
  border-block-start: 1px solid var(--h-border-subtle);
}

.post-pager {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--h-space-5);
  margin-block-end: var(--h-space-6);
}

.post-pager__link {
  display: grid;
  align-content: start;
  gap: var(--h-space-1);
  color: inherit;
  text-decoration: none;
}

.post-pager__link--next {
  grid-column: 2;
  text-align: end;
}

.post-pager__label {
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-micro);
  font-weight: var(--h-weight-medium);
  letter-spacing: var(--h-tracking-wide);
  text-transform: uppercase;
}

.post-pager__title {
  font-size: var(--h-text-large);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-tight);
  text-decoration: underline transparent;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: text-decoration-color var(--h-transition-hover);
}

.post-pager__link:hover .post-pager__title,
.post-pager__link:focus-visible .post-pager__title {
  text-decoration-color: currentColor;
}

@media (max-width: 34rem) {
  .post-pager {
    grid-template-columns: minmax(0, 1fr);
  }

  .post-pager__link--next {
    grid-column: 1;
    text-align: start;
  }
}

.post__back {
  color: var(--h-fg-secondary);
  font-size: var(--h-text-base);
  text-decoration: none;
  transition: color var(--h-transition-hover);
}

.post__back:hover,
.post__back:focus-visible {
  color: var(--h-fg-primary);
}

.post__lede {
  margin-block: var(--h-space-6) 0;
  color: var(--h-fg-secondary);
  font-size: var(--h-text-title-3);
  line-height: var(--h-leading-normal);
  text-wrap: pretty;
}

.prose {
  margin-block-start: var(--h-space-6);
  font-size: 1.0625rem;
  line-height: 1.7;
}

.prose > * {
  margin: 0 0 var(--h-space-5);
}

.prose > :first-child {
  margin-block-start: 0;
}

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

.prose h2 {
  margin: var(--h-space-8) 0 var(--h-space-4);
  font-size: clamp(1.5rem, 1.1rem + 1.6vw, 1.75rem);
  font-weight: var(--h-weight-semibold);
  line-height: var(--h-leading-tight);
  letter-spacing: -0.025em;
  text-wrap: balance;
}

.prose h3 {
  margin: var(--h-space-6) 0 var(--h-space-3);
  font-size: var(--h-text-title-3);
  font-weight: var(--h-weight-semibold);
  line-height: var(--h-leading-snug);
  letter-spacing: var(--h-tracking-tight);
}

.prose :is(h2, h3) {
  scroll-margin-block-start: calc(var(--height-header) + var(--h-space-5));
}

.prose img {
  display: block;
  max-width: 100%;
  height: auto;
  border-radius: var(--h-radius-lg);
}

.prose figure {
  margin-block: var(--h-space-7);
}

.prose figcaption {
  margin-block-start: var(--h-space-3);
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-base);
  line-height: var(--h-leading-snug);
  text-wrap: pretty;
}

.prose a {
  color: var(--h-fg-primary);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--h-fg-tertiary);
}

.prose a:hover {
  text-decoration-color: var(--h-fg-primary);
}

.prose code {
  padding: 0.15em 0.4em;
  border-radius: var(--h-radius-sm);
  background: var(--h-bg-panel);
  font-size: 0.875em;
}

.prose pre {
  overflow-x: auto;
  padding: var(--h-space-4);
  border: 1px solid var(--h-border-subtle);
  border-radius: var(--h-radius-lg);
  background: var(--h-bg-sunken);
}

.prose pre code {
  padding: 0;
  background: none;
  font-size: var(--h-text-base);
}

.prose blockquote {
  padding-inline-start: var(--h-space-4);
  border-inline-start: 2px solid var(--h-border-subtle);
  color: var(--h-fg-secondary);
}

.prose ul,
.prose ol {
  padding-inline-start: var(--h-space-5);
}

.prose li {
  margin-block-end: var(--h-space-2);
}

.prose table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--h-text-base);
}

.prose th,
.prose td {
  padding: var(--h-space-2) var(--h-space-3);
  border: 1px solid var(--h-border-subtle);
  text-align: start;
}

.prose .stages {
  --stages-width: min(64rem, 100vw - 2 * var(--space-content));

  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 0.85fr);
  column-gap: clamp(var(--h-space-6), 6vw, calc(2 * var(--h-space-8)));
  width: var(--stages-width);
  margin: var(--h-space-7) calc((100% - var(--stages-width)) / 2);
}

.stages__figure {
  position: sticky;
  inset-block-start: var(--height-header);
  display: flex;
  flex-direction: column;
  align-items: center;
  align-self: start;
  justify-content: center;
  gap: var(--h-space-5);
  height: calc(100svh - var(--height-header));
  margin: 0;
}

.prose .stages__art {
  flex: 0 1 auto;
  width: 100%;
  min-height: 0;
  border-radius: 0;
}

.stages:has(.figure-pause__toggle:checked) {
  --figure-play: paused;
}

.js .stages:not(.stages--live) .stages__art {
  animation: page-fade var(--h-transition-enter) 3s both;
}

.prose .stages__steps {
  margin: 0;
  padding: 0 0 20svh;
  list-style: none;
}

.prose .stages__step {
  display: flex;
  flex-direction: column;
  justify-content: center;
  min-height: 52svh;
  margin: 0;
}

.prose .stages__step :is(h3, p) {
  margin: 0;
  transition: color var(--h-transition-layout);
}

.prose .stages__step h3 {
  margin-block-end: var(--h-space-3);
}

.stages__step p {
  color: var(--h-fg-secondary);
}

.stages--live .stages__step:not([aria-current]) :is(h3, p) {
  color: var(--h-fg-tertiary);
}

.figure-pause {
  visibility: hidden;
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-micro);
  font-weight: var(--h-weight-medium);
  letter-spacing: var(--h-tracking-wide);
  line-height: var(--h-leading-tight);
  text-transform: uppercase;
  cursor: pointer;
  transition: color var(--h-transition-hover);
}

.figure-pause:hover {
  color: var(--h-fg-primary);
}

.figure-pause__toggle:focus-visible + .figure-pause {
  outline: 1px solid var(--h-border-focus);
  outline-offset: 2px;
}

.figure-pause__play,
.figure-pause__toggle:checked + .figure-pause .figure-pause__halt {
  display: none;
}

.figure-pause__toggle:checked + .figure-pause .figure-pause__play {
  display: inline;
}

.prose .replay {
  margin-block: var(--h-space-7);
}

.replay:hover,
.replay:has(.figure-pause__toggle:checked) {
  --figure-play: paused;
}

.replay__window {
  margin-block-end: var(--h-space-3);
  border: 1px solid var(--h-border-subtle);
  border-radius: var(--h-radius-xl);
  background: var(--h-bg-panel);
  font-family: var(--h-font-mono);
  font-size: var(--h-text-small);
  line-height: var(--h-leading-normal);
}

.prose .replay__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin: 0;
  padding: var(--h-space-3) var(--h-space-5);
  border-block-end: 1px solid var(--h-border-subtle);
}

.replay__bar,
.ask__parts dt,
.prose .ask__verdict {
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-micro);
  font-weight: var(--h-weight-medium);
  letter-spacing: var(--h-tracking-wide);
  text-transform: uppercase;
}

.replay__state {
  display: grid;
  justify-items: end;
}

.replay__busy,
.replay__rest {
  display: flex;
  grid-area: 1 / 1;
  align-items: center;
  gap: var(--h-space-2);
}

.replay__busy {
  color: var(--h-fg-primary);
  opacity: 0;
}

.replay__busy::before {
  content: "";
  width: 0.75rem;
  height: 0.75rem;
  background: currentColor;
  clip-path: polygon(50% 0, 58% 42%, 100% 50%, 58% 58%, 50% 100%, 42% 58%, 0 50%, 42% 42%);
}

.prose .replay__log {
  margin: 0;
  padding: var(--h-space-4) var(--h-space-5) var(--h-space-5);
  list-style: none;
}

.prose .replay__line {
  margin: 0;
  padding-block: var(--h-space-2);
}

.prose .replay__call {
  display: flex;
  gap: var(--h-space-3);
  margin: 0;
  color: var(--h-fg-secondary);
}

.replay__call::before {
  content: "";
  flex: none;
  width: 0.375rem;
  height: 0.375rem;
  margin-block-start: 0.6em;
  border-radius: var(--h-radius-full);
  background: var(--h-fg-tertiary);
}

.replay__call b,
.prose :is(.replay__line--you, .replay__line--done) .replay__call {
  color: var(--h-fg-primary);
  font-weight: var(--h-weight-medium);
}

.replay__line--you .replay__call::before {
  content: ">";
  width: 0.375rem;
  height: auto;
  margin: 0;
  background: none;
  color: var(--h-fg-tertiary);
}

.replay__line--done .replay__call::before {
  background: var(--h-fg-primary);
}

.prose .replay__out {
  overflow: visible;
  margin: var(--h-space-2) 0 0 calc(0.1875rem - 0.5px);
  padding: 0 0 0 calc(0.1875rem + var(--h-space-3));
  border: 0;
  border-inline-start: 1px solid var(--h-border-default);
  border-radius: 0;
  background: none;
  color: var(--h-fg-tertiary);
  font: inherit;
  white-space: pre-wrap;
}

.replay__out :is(del, ins) {
  display: block;
  text-decoration: none;
}

.replay__out del::before {
  content: "- ";
}

.replay__out ins::before {
  content: "+ ";
}

.replay__bad,
.replay__out del,
.prose .ask__way--wrong .ask__verdict {
  color: var(--h-danger-fg);
}

.replay__good,
.replay__out ins,
.prose .ask__way--right .ask__verdict {
  color: var(--h-success-fg);
}

@keyframes replay-loop {
  0%, 3% { opacity: 0; }
  4%, 94% { opacity: 1; }
  96%, 100% { opacity: 0; }
}

@keyframes replay-busy {
  0%, 66% { opacity: 1; }
  66.1%, 100% { opacity: 0; }
}

@keyframes replay-rest {
  0%, 66% { opacity: 0; }
  66.1%, 100% { opacity: 1; }
}

@keyframes replay-pulse {
  from { scale: 0.55; }
  to { scale: 1; }
}

@keyframes replay-line-1 {
  0%, 4% { clip-path: inset(0 100% 0 0); }
  8%, 100% { clip-path: inset(0); }
}

@keyframes replay-line-2 {
  0%, 13% { clip-path: inset(0 100% 0 0); }
  17%, 100% { clip-path: inset(0); }
}

@keyframes replay-line-3 {
  0%, 21% { clip-path: inset(0 100% 0 0); }
  25%, 100% { clip-path: inset(0); }
}

@keyframes replay-line-4 {
  0%, 33% { clip-path: inset(0 100% 0 0); }
  37%, 100% { clip-path: inset(0); }
}

@keyframes replay-line-5 {
  0%, 41% { clip-path: inset(0 100% 0 0); }
  45%, 100% { clip-path: inset(0); }
}

@keyframes replay-line-6 {
  0%, 53% { clip-path: inset(0 100% 0 0); }
  57%, 100% { clip-path: inset(0); }
}

@keyframes replay-line-7 {
  0%, 62% { clip-path: inset(0 100% 0 0); }
  66%, 100% { clip-path: inset(0); }
}

@keyframes replay-line-8 {
  0%, 72% { clip-path: inset(0 100% 0 0); }
  76%, 100% { clip-path: inset(0); }
}

@media (prefers-reduced-motion: no-preference) {
  .replay .figure-pause,
  .stages--live .figure-pause {
    visibility: visible;
  }

  .replay__log,
  .replay__busy,
  .replay__rest,
  .replay__line > * {
    animation: 20s linear infinite both;
    animation-play-state: var(--figure-play, running);
  }

  .replay__log { animation-name: replay-loop; }
  .replay__busy { animation-name: replay-busy; }
  .replay__rest { animation-name: replay-rest; }

  .replay__busy::before {
    animation: replay-pulse 700ms var(--h-ease-in-out) infinite alternate;
    animation-play-state: var(--figure-play, running);
  }

  .replay__line:nth-child(1) > * { animation-name: replay-line-1; }
  .replay__line:nth-child(2) > * { animation-name: replay-line-2; }
  .replay__line:nth-child(3) > * { animation-name: replay-line-3; }
  .replay__line:nth-child(4) > * { animation-name: replay-line-4; }
  .replay__line:nth-child(5) > * { animation-name: replay-line-5; }
  .replay__line:nth-child(6) > * { animation-name: replay-line-6; }
  .replay__line:nth-child(7) > * { animation-name: replay-line-7; }
  .replay__line:nth-child(8) > * { animation-name: replay-line-8; }

  .replay__line > .replay__out {
    animation-delay: 500ms;
  }
}

.prose .ask {
  --ask-width: min(56rem, 100vw - 2 * var(--space-content));

  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--h-space-5);
  width: var(--ask-width);
  margin: var(--h-space-7) calc((100% - var(--ask-width)) / 2);
}

.prose .ask__way {
  margin: 0;
  padding: var(--h-space-6);
  border: 1px solid var(--h-border-subtle);
  border-radius: var(--h-radius-xl);
  background: var(--h-bg-panel);
}

.prose .ask__verdict {
  margin: 0 0 var(--h-space-5);
  text-align: start;
}

.prose .ask__prompt {
  margin: 0;
  padding: 0;
  border: 0;
  color: var(--h-fg-primary);
  font-size: var(--h-text-large);
  font-style: normal;
}

.prose .ask__way--wrong .ask__prompt {
  color: var(--h-fg-secondary);
}

.ask__parts {
  display: grid;
  gap: var(--h-space-4);
}

.ask__parts dt {
  margin-block-end: var(--h-space-1);
}

.ask__parts dd {
  margin: 0;
}

.prose .ask__why {
  margin: var(--h-space-5) 0 0;
  padding-block-start: var(--h-space-5);
  border-block-start: 1px solid var(--h-border-subtle);
  color: var(--h-fg-secondary);
  font-size: var(--h-text-base);
}

@media (max-width: 48rem) {
  .prose .stages {
    grid-template-columns: minmax(0, 1fr);
  }

  .stages__figure {
    z-index: var(--h-z-base);
    height: auto;
    padding-block: var(--h-space-5);
    border-block-end: 1px solid var(--h-border-subtle);
    background: var(--h-bg-app);
  }

  .prose .stages__step {
    min-height: 46svh;
  }

  .prose .ask {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* The changelog. A release is a row of two columns: the label (version and
   date) on the left and the entries on the right. The label is sticky and a
   direct child of its row, so it can travel only as far as its own release and
   the next version's label takes over where the row ends. The offset clears the
   header and the band its blur covers. Nothing here moves once the page has
   drawn, and none of it needs script. */
.changelog__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--h-space-1) var(--h-space-5);
  margin: var(--h-space-3) 0 0;
  font-size: var(--h-text-base);
}

.changelog__link {
  color: var(--h-fg-secondary);
  text-decoration: none;
  transition: color var(--h-transition-hover);
}

.changelog__link:hover,
.changelog__link:focus-visible {
  color: var(--h-fg-primary);
}

.changelog {
  --release-padding: var(--h-space-7);
  --release-offset: calc(var(--height-header) + var(--h-space-5));
  margin-block-start: var(--h-space-7);
}

.release {
  display: grid;
  grid-template-columns: minmax(7rem, 11rem) minmax(0, 40rem);
  gap: var(--h-space-4) var(--h-space-7);
  padding-block: var(--release-padding);
  border-block-start: 1px solid var(--h-border-subtle);
}

.release__label {
  position: sticky;
  inset-block-start: var(--release-offset);
  display: grid;
  align-self: start;
  gap: var(--h-space-1);
}

.release__version {
  margin: 0;
  font-size: var(--h-text-title-3);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-tight);
}

.release__date {
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-small);
}

.release__entries {
  font-size: var(--h-text-large);
  line-height: var(--h-leading-normal);
}

.release__entries h3 {
  margin: var(--h-space-6) 0 var(--h-space-3);
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-small);
  font-weight: var(--h-weight-medium);
  letter-spacing: var(--h-tracking-wide);
  text-transform: uppercase;
}

.release__entries > h3:first-child {
  margin-block-start: 0;
}

.release__entries ul {
  margin: 0;
  padding-inline-start: var(--h-space-5);
}

.release__entries li {
  margin-block-end: var(--h-space-2);
}

.release__entries li:last-child {
  margin-block-end: 0;
}

.release__entries a {
  color: var(--h-fg-primary);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--h-fg-tertiary);
}

.release__entries code {
  padding: 0.15em 0.4em;
  border-radius: var(--h-radius-sm);
  background: var(--h-bg-panel);
  font-size: 0.875em;
}

@media (max-width: 48rem) {
  .changelog {
    --release-padding: var(--h-space-6);
  }

  .release {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--h-space-3);
  }

  .release__label {
    position: static;
    grid-auto-flow: column;
    justify-content: start;
    align-items: baseline;
    gap: var(--h-space-3);
  }
}

.project__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--h-space-1) var(--h-space-5);
}

.project__link--internal:hover .project__link-arrow,
.project__link--internal:focus-visible .project__link-arrow {
  transform: translateX(2px);
}

.site-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--h-space-4);
  padding: var(--h-space-4) var(--space-content) calc(var(--h-space-8) + var(--h-space-6));
  text-align: center;
}

.site-footer__line {
  margin: 0;
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-small);
}

.site-footer__nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  margin-block: calc(-1 * var(--h-space-3));
}

.site-footer__link {
  padding: var(--h-space-3) var(--h-space-4);
  color: var(--h-fg-secondary);
  font-size: var(--h-text-base);
  text-decoration: none;
  transition: color var(--h-transition-hover);
}

.site-footer__link:hover,
.site-footer__link:focus-visible,
.site-footer__link[aria-current="page"] {
  color: var(--h-fg-primary);
}

@media (hover: hover) {
  .site-footer__nav:has(.site-footer__link:hover)
    .site-footer__link:not(:hover) {
    color: var(--h-fg-disabled);
  }
}

.site-end {
  display: flex;
  justify-content: center;
  padding-block: calc(var(--h-space-8) + var(--h-space-6)) var(--h-space-4);
}

.bunny {
  display: block;
  width: 4.125rem;
  height: auto;
  overflow: visible;
  color: var(--h-fg-primary);
  fill: currentColor;
}

/* The pivots below are in the art's own units (282 by 340), which the SVG
   scales with its viewBox. The ear and tail twitches are hand-written loops of
   their own lengths, so the bunny never settles into a visible cycle. The hop is
   baked by tools/bunny-motion/bake.py: the figure crouches, stretches and
   squashes, and the ear, tail and paw swing on springs. The paw does nothing
   else, because the other paw (the toe) is still. It is a sliver that follows
   the curve of the chest at a steady gap, so it turns about that curve's centre
   and glides along the chest. Past about 10 degrees it would reach the body, and
   tests/bunny.test.mjs holds it under that. */
@keyframes bunny-breathe {
  from { scale: 1 1; }
  to { scale: 1.015 1.03; }
}

@keyframes bunny-ear {
  0%, 20%, 36%, 100% { rotate: 0deg; }
  24% { rotate: 9deg; }
  28% { rotate: -3deg; }
  32% { rotate: 2deg; }
}

@keyframes bunny-tail {
  0%, 30%, 38%, 100% { translate: 0 0; scale: 1; }
  32% { translate: 0 -12px; scale: 1.12; }
  34% { translate: 0 4px; scale: 0.97; }
  36% { translate: 0 -2px; scale: 1.02; }
}

/* bunny-motion:begin: written by tools/bunny-motion/bake.py, do not edit by hand.
   One 1.45s hop starting 7s into a 11s loop. */
@keyframes bunny-hop {
  0%, 63.64% { transform: translate(0, 0.0px) scale(1.000, 1.000) skewX(0.00deg); }
  64.24% { transform: translate(0, 0.0px) scale(1.007, 0.988) skewX(0.08deg); }
  64.45% { transform: translate(0, 0.0px) scale(1.016, 0.973) skewX(0.18deg); }
  64.96% { transform: translate(0, 0.0px) scale(1.051, 0.915) skewX(0.57deg); }
  65.62% { transform: translate(0, 0.0px) scale(1.060, 0.900) skewX(0.67deg); }
  65.83% { transform: translate(0, 0.0px) scale(1.008, 0.999) skewX(-0.11deg); }
  66.02% { transform: translate(0, 0.0px) scale(0.979, 1.053) skewX(-0.55deg); }
  66.25% { transform: translate(0, 0.0px) scale(0.964, 1.083) skewX(-0.79deg); }
  66.53% { transform: translate(0, 0.0px) scale(0.960, 1.090) skewX(-0.84deg); }
  67.05% { transform: translate(0, -20.9px) scale(0.964, 1.077) skewX(-0.69deg); }
  67.50% { transform: translate(0, -34.5px) scale(0.969, 1.065) skewX(-0.55deg); }
  67.97% { transform: translate(0, -43.1px) scale(0.975, 1.053) skewX(-0.40deg); }
  68.45% { transform: translate(0, -46.0px) scale(0.980, 1.040) skewX(-0.26deg); }
  68.92% { transform: translate(0, -43.3px) scale(0.975, 1.052) skewX(-0.11deg); }
  69.39% { transform: translate(0, -34.9px) scale(0.969, 1.065) skewX(0.04deg); }
  69.87% { transform: translate(0, -20.8px) scale(0.964, 1.077) skewX(0.18deg); }
  70.36% { transform: translate(0, -0.2px) scale(0.958, 1.090) skewX(0.34deg); }
  70.61% { transform: translate(0, 0.0px) scale(1.021, 0.982) skewX(0.18deg); }
  70.87% { transform: translate(0, 0.0px) scale(1.061, 0.911) skewX(0.07deg); }
  71.16% { transform: translate(0, 0.0px) scale(1.083, 0.873) skewX(0.02deg); }
  71.52% { transform: translate(0, 0.0px) scale(1.090, 0.860) skewX(0.00deg); }
  71.63% { transform: translate(0, 0.0px) scale(1.090, 0.860) skewX(0.00deg); }
  71.65% { transform: translate(0, 0.0px) scale(1.078, 0.861) skewX(0.00deg); }
  71.80% { transform: translate(0, 0.0px) scale(1.068, 0.877) skewX(0.00deg); }
  72.75% { transform: translate(0, 0.0px) scale(0.996, 1.008) skewX(0.00deg); }
  73.26% { transform: translate(0, 0.0px) scale(0.980, 1.041) skewX(0.00deg); }
  73.73% { transform: translate(0, 0.0px) scale(0.981, 1.039) skewX(0.00deg); }
  74.72% { transform: translate(0, 0.0px) scale(1.000, 1.001) skewX(0.00deg); }
  75.23% { transform: translate(0, 0.0px) scale(1.005, 0.989) skewX(0.00deg); }
  76.82%, 100% { transform: translate(0, 0.0px) scale(1.000, 1.000) skewX(0.00deg); }
}

@keyframes bunny-hop-ear {
  0%, 63.64% { transform: rotate(0.0deg); }
  64.92% { transform: rotate(-3.0deg); }
  65.61% { transform: rotate(3.5deg); }
  65.81% { transform: rotate(11.9deg); }
  65.97% { transform: rotate(14.0deg); }
  66.17% { transform: rotate(11.0deg); }
  66.57% { transform: rotate(-2.1deg); }
  67.10% { transform: rotate(-6.5deg); }
  68.18% { transform: rotate(-0.6deg); }
  68.79% { transform: rotate(1.0deg); }
  69.81% { transform: rotate(-3.1deg); }
  70.32% { transform: rotate(-3.5deg); }
  70.53% { transform: rotate(-7.0deg); }
  70.66% { transform: rotate(-7.4deg); }
  70.87% { transform: rotate(-4.6deg); }
  71.44% { transform: rotate(9.1deg); }
  71.86% { transform: rotate(12.9deg); }
  72.18% { transform: rotate(10.4deg); }
  72.92% { transform: rotate(-5.4deg); }
  73.30% { transform: rotate(-9.3deg); }
  73.64% { transform: rotate(-8.0deg); }
  74.38% { transform: rotate(1.5deg); }
  74.75% { transform: rotate(4.1deg); }
  75.11% { transform: rotate(3.8deg); }
  75.97% { transform: rotate(-0.5deg); }
  76.82%, 100% { transform: rotate(0.0deg); }
}

@keyframes bunny-hop-tail {
  0%, 63.64% { transform: translate(0, 0.0px) scale(1.000); }
  64.87% { transform: translate(0, -2.6px) scale(1.013); }
  65.61% { transform: translate(0, 3.7px) scale(0.981); }
  65.80% { transform: translate(0, 10.4px) scale(0.948); }
  65.93% { transform: translate(0, 12.0px) scale(0.940); }
  66.14% { transform: translate(0, 8.5px) scale(0.958); }
  66.53% { transform: translate(0, -5.6px) scale(1.028); }
  66.74% { transform: translate(0, -7.3px) scale(1.037); }
  67.92% { transform: translate(0, 1.7px) scale(0.992); }
  69.07% { transform: translate(0, -1.8px) scale(1.009); }
  70.30% { transform: translate(0, -1.2px) scale(1.006); }
  70.66% { transform: translate(0, -5.4px) scale(1.027); }
  71.36% { transform: translate(0, 7.5px) scale(0.963); }
  71.72% { transform: translate(0, 9.3px) scale(0.953); }
  71.99% { transform: translate(0, 7.7px) scale(0.961); }
  72.65% { transform: translate(0, -3.4px) scale(1.017); }
  73.01% { transform: translate(0, -6.2px) scale(1.031); }
  74.28% { transform: translate(0, 1.9px) scale(0.991); }
  76.82%, 100% { transform: translate(0, 0.0px) scale(1.000); }
}

@keyframes bunny-hop-paw {
  0%, 63.64% { transform: rotate(0.0deg); }
  64.79% { transform: rotate(-1.3deg); }
  65.61% { transform: rotate(1.7deg); }
  65.87% { transform: rotate(6.0deg); }
  66.53% { transform: rotate(-4.9deg); }
  67.05% { transform: rotate(0.0deg); }
  67.33% { transform: rotate(1.2deg); }
  68.22% { transform: rotate(-1.3deg); }
  68.94% { transform: rotate(0.1deg); }
  70.30% { transform: rotate(-0.8deg); }
  70.61% { transform: rotate(-3.2deg); }
  71.16% { transform: rotate(3.2deg); }
  71.38% { transform: rotate(4.4deg); }
  72.77% { transform: rotate(-1.4deg); }
  76.82%, 100% { transform: rotate(0.0deg); }
}
/* bunny-motion:end */

@media (prefers-reduced-motion: no-preference) {
  .bunny__figure {
    transform-origin: 160px 340px;
    animation: bunny-hop 11s linear infinite;
  }

  .bunny__breath {
    transform-origin: 141px 340px;
    animation: bunny-breathe 4.5s var(--h-ease-in-out) infinite alternate;
  }

  .bunny__ear {
    transform-origin: 109px 90px;
    animation:
      bunny-ear 7s var(--h-ease-in-out) infinite,
      bunny-hop-ear 11s linear infinite;
  }

  .bunny__tail {
    transform-origin: 23.5px 261.5px;
    animation:
      bunny-tail 9s var(--h-ease-in-out) infinite,
      bunny-hop-tail 11s linear infinite;
  }

  .bunny__paw {
    transform-origin: 182.1px 242.4px;
    animation: bunny-hop-paw 11s linear infinite;
  }
}

.page-title {
  margin: 0;
  font-size: var(--h-text-title-1);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-tighter);
}

.page-body {
  margin: var(--h-space-3) 0 0;
  color: var(--h-fg-secondary);
}

.site-main:has(.access-state) {
  display: grid;
  place-items: center;
  max-width: none;
  min-height: calc(100svh - var(--height-header));
  padding: var(--h-space-5);
}

.site-main:has(.access-state) ~ :is(.site-footer, .site-end) {
  display: none;
}

.access-state {
  width: min(100%, 28rem);
  text-align: center;
}

.access-state__mark {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  margin: 0 auto var(--h-space-6);
  border: 1px solid var(--h-border-strong);
  border-radius: var(--h-radius-2xl);
  background: var(--h-bg-panel);
}

.access-state__mark svg {
  width: 1.4rem;
  height: 1.4rem;
}

.access-state__eyebrow {
  margin: 0 0 var(--h-space-4);
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-micro);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-wide);
}

.access-state__title {
  margin: 0;
  font-size: clamp(2.5rem, 7vw, 3.5rem);
  font-weight: var(--h-weight-semibold);
  letter-spacing: -0.06em;
  line-height: 1.05;
}

.access-state__body {
  margin: var(--h-space-5) 0 0;
  color: var(--h-fg-secondary);
  line-height: var(--h-leading-normal);
}

.access-state__action {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  margin-block-start: var(--h-space-6);
  padding: var(--h-space-3) var(--h-space-4);
  border: 1px solid var(--h-accent-bg);
  border-radius: var(--h-radius-md);
  background: var(--h-accent-bg);
  color: var(--h-fg-on-accent);
  font: inherit;
  font-weight: var(--h-weight-semibold);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
  transition:
    background var(--h-transition-hover),
    color var(--h-transition-hover);
}

.access-state__action:hover {
  background: transparent;
  color: var(--h-fg-primary);
}

.access-state__back {
  display: inline-flex;
  gap: var(--h-space-2);
  margin-block-start: var(--h-space-6);
  color: var(--h-fg-secondary);
  font-size: var(--h-text-base);
  text-decoration: none;
  transition: color var(--h-transition-hover);
}

.access-state__back:hover {
  color: var(--h-fg-primary);
}

@media (prefers-reduced-motion: no-preference) {
  .access-state {
    animation: image-clarify var(--h-transition-enter) both;
  }
}

.site-main:has(.contact-scene) {
  max-width: none;
  padding: 0;
}

.site-main:has(.contact-scene) ~ :is(.site-footer, .site-end) {
  display: none;
}

.contact-scene {
  position: relative;
  display: grid;
  align-items: center;
  min-height: 100svh;
  margin-block-start: calc(-1 * var(--height-header));
  padding-block-start: var(--height-header);
  overflow: hidden;
}

.contact-scene::before,
.contact-scene::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.contact-scene::before {
  background: radial-gradient(
    ellipse at 65% 40%,
    var(--art-contact-ember),
    var(--art-contact-ash) 65%,
    var(--h-bg-app)
  );
  opacity: 0;
  transition: opacity 1600ms var(--h-ease-out);
}

.contact-scene--still::before {
  opacity: 1;
}

@media (scripting: none) {
  .contact-scene::before {
    opacity: 1;
  }
}

.contact-scene::after {
  background: radial-gradient(
    ellipse 70% 70% at 50% 55%,
    color-mix(in oklab, var(--h-bg-app) 50%, transparent),
    color-mix(in oklab, var(--h-bg-app) 24%, transparent)
  );
}

.contact-scene__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  filter: blur(var(--h-blur-5));
  transform: scale(1.04);
  transition:
    opacity 1600ms var(--h-ease-out),
    filter 1600ms var(--h-ease-out),
    transform 1600ms var(--h-ease-out);
}

.contact-scene--ready .contact-scene__canvas {
  opacity: 1;
  filter: none;
  transform: none;
}

.contact-scene__content {
  position: relative;
  z-index: 1;
  width: min(100%, 36rem + 2 * var(--space-content));
  margin-inline: auto;
  padding: var(--h-space-7) var(--space-content);
  color: var(--h-fg-primary);
  text-shadow: 0 1px 16px color-mix(in oklab, var(--h-bg-app) 65%, transparent);
}

.contact-scene__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 4.6vw, 4.25rem);
  font-weight: var(--h-weight-medium);
  line-height: 1.1;
  letter-spacing: var(--h-tracking-tight);
  text-align: center;
}

.contact-scene__lede {
  max-width: 24rem;
  margin: var(--h-space-4) auto 0;
  font-size: var(--h-text-large);
  line-height: var(--h-leading-normal);
  color: color-mix(in oklab, var(--h-fg-primary) 82%, transparent);
  text-align: center;
  text-wrap: balance;
}

.contact-scene__groups {
  margin-block-start: var(--h-space-8);
  border-block-end: 1px solid var(--h-border-default);
}

.contact-group {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  align-items: baseline;
  border-block-start: 1px solid var(--h-border-default);
}

.contact-group__label {
  margin: 0;
  font-size: var(--h-text-micro);
  font-weight: var(--h-weight-medium);
  letter-spacing: var(--h-tracking-wide);
  text-transform: uppercase;
  opacity: 0.7;
}

.contact-group__channels {
  grid-column: 2;
  margin: 0;
  padding: 0;
  list-style: none;
}

.contact-channel + .contact-channel {
  border-block-start: 1px solid var(--h-border-subtle);
}

.contact-channel__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: baseline;
  column-gap: var(--h-space-4);
  margin-inline: calc(-1 * var(--h-space-3));
  padding: var(--h-space-4) var(--h-space-3);
  color: inherit;
  text-decoration: none;
  transition: background var(--h-transition-hover);
}

.contact-channel__link:hover {
  background: var(--h-bg-hover);
}

.contact-channel__name {
  font-size: var(--h-text-large);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-tight);
}

.contact-channel__detail {
  font-size: var(--h-text-base);
  opacity: 0.75;
}

.contact-channel__arrow {
  transition: transform var(--h-transition-hover);
}

.contact-channel__link:hover .contact-channel__arrow,
.contact-channel__link:focus-visible .contact-channel__arrow {
  transform: translate(2px, -2px);
}

.contact-scene__title,
.contact-scene__lede,
.contact-scene__groups {
  animation: page-fade 900ms var(--h-ease-out) both;
}

.contact-scene__lede {
  animation-delay: 100ms;
}

.contact-scene__groups {
  animation-delay: 200ms;
}

.site-main:has(.not-found) {
  max-width: none;
  padding: 0;
}

.site-main:has(.not-found) ~ :is(.site-footer, .site-end) {
  display: none;
}

.not-found {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: 2fr auto 3fr auto;
  column-gap: var(--h-space-4);
  min-height: calc(100svh - var(--height-header));
  padding: var(--h-space-7) var(--space-content);
}

.not-found__copy {
  grid-area: 2 / 1 / 3 / -1;
  width: fit-content;
  max-width: min(100%, 48rem);
  margin-inline: auto;
}

.not-found__title {
  margin: 0;
  font-size: clamp(6rem, 11vw, 12rem);
  font-weight: 800;
  line-height: 0.85;
  letter-spacing: -0.04em;
  text-align: center;
}

.not-found__message,
.not-found__suggestion {
  margin: 0;
  font-size: var(--h-text-title-3);
  font-weight: var(--h-weight-medium);
  line-height: var(--h-leading-snug);
  letter-spacing: var(--h-tracking-tight);
}

.not-found__message {
  margin-block-start: clamp(var(--h-space-5), 3vh, var(--h-space-6));
}

.not-found__path {
  color: var(--h-fg-tertiary);
  font-family: inherit;
  font-size: inherit;
  font-weight: var(--h-weight-normal);
  overflow-wrap: anywhere;
}

.not-found__suggestions {
  margin-block-start: clamp(var(--h-space-7), 9vh, calc(var(--h-space-8) + var(--h-space-5)));
}

.not-found__suggestion {
  max-width: 34rem;
}

.not-found__links {
  display: grid;
  gap: var(--h-space-1);
  margin: clamp(var(--h-space-5), 4vh, var(--h-space-6)) 0 0;
  padding: 0;
  list-style: none;
}

.not-found__link {
  color: var(--h-fg-primary);
  font-size: var(--h-text-title-2);
  line-height: var(--h-leading-snug);
  letter-spacing: var(--h-tracking-tighter);
  text-decoration: underline 1px transparent;
  text-underline-offset: 0.18em;
  overflow-wrap: anywhere;
  transition: text-decoration-color var(--h-transition-hover);
}

.not-found__link:hover,
.not-found__link:focus-visible {
  text-decoration-color: currentColor;
}

.not-found__mascot {
  grid-area: 4 / 2;
  display: block;
  align-self: end;
  justify-self: end;
  width: clamp(9rem, 16vw, 20rem);
  height: auto;
  margin-block-start: var(--h-space-7);
  user-select: none;
}

.not-found__scenario {
  grid-area: 4 / 1;
  align-self: end;
  margin: 0;
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-large);
  font-weight: var(--h-weight-normal);
  letter-spacing: var(--h-tracking-wide);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

@media (min-width: 64rem) {
  .not-found {
    grid-template-columns: 1fr fit-content(48rem) 1fr;
    column-gap: var(--h-space-6);
    padding: var(--h-space-6) clamp(var(--space-content), 2vw, var(--h-space-6));
  }

  .not-found__copy {
    grid-area: 2 / 2;
    width: auto;
    max-width: none;
    margin: 0;
  }

  .not-found__mascot {
    grid-area: 1 / 3 / -1;
    margin: 0;
  }
}

/* An opacity fade is not motion, so it runs under reduced motion too; only
   the mascot's blur waits for no-preference. */
.site-main > .page-title,
.site-main > .page-body,
.post-featured,
.post-list,
.projects-intro,
.timeline,
.post__header,
.post__figure,
.not-found__title,
.not-found__message,
.not-found__suggestions,
.not-found__mascot,
.not-found__scenario {
  animation: page-fade var(--h-transition-enter) both;
}

.site-main > .page-body,
.post-featured,
.timeline,
.post__figure,
.not-found__message {
  animation-delay: 100ms;
}

.post-list,
.not-found__suggestions,
.not-found__mascot,
.not-found__scenario {
  animation-delay: 140ms;
}

@media (prefers-reduced-motion: no-preference) {
  .not-found__mascot {
    animation-name: image-clarify;
  }
}

@keyframes page-fade {
  from {
    opacity: 0;
  }

  to {
    opacity: 1;
  }
}

@keyframes image-clarify {
  from {
    opacity: 0;
    filter: blur(var(--h-blur-2));
  }

  to {
    opacity: 1;
    filter: none;
  }
}

.splash {
  position: fixed;
  inset: 0;
  z-index: var(--h-z-modal);
  display: grid;
  place-items: center;
  background: var(--h-bg-app);
  transition: opacity 700ms var(--h-ease-in-out);
}

.ready .splash {
  opacity: 0;
  pointer-events: none;
}

.splash__mark {
  width: clamp(7rem, 20vw, 12rem);
  height: auto;
}

.site-main:has(.hero) {
  max-width: none;
  padding: 0 0 var(--h-space-7);
}

.hero {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: calc(100svh - var(--height-header));
  padding: var(--h-space-6) var(--space-content) calc(2 * var(--h-space-8));
}

.hero__credit {
  position: absolute;
  inset-block-end: calc(var(--h-space-8) + var(--h-space-7));
  inset-inline: 0;
  width: max-content;
  margin-inline: auto;
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-micro);
  letter-spacing: var(--h-tracking-wide);
  text-decoration: none;
  white-space: nowrap;
  opacity: var(--credit-opacity, 1);
  transition: color var(--h-transition-hover);
}

.hero__credit:hover,
.hero__credit:focus-visible {
  color: var(--h-fg-primary);
}

.hero__mascot {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: min(36rem, 64vh);
  object-fit: contain;
  user-select: none;
}

/* The mascot's src is given by script. Without JavaScript it never gets one,
   and the image in the noscript takes its place in the hero. With JavaScript a
   noscript holds its markup as text, which only the browser's own display: none
   keeps off the page, so the rule that shows it applies without script only. */
.hero__mascot:not([src]) {
  display: none;
}

@media (scripting: none) {
  .hero__fallback {
    display: contents;
  }
}

@keyframes mascot-reveal {
  from {
    opacity: 0;
    filter: blur(var(--h-blur-4));
  }

  to {
    opacity: 1;
    filter: blur(0);
  }
}

.js .hero__mascot {
  opacity: 0;
  filter: blur(var(--h-blur-4));
}

.js.ready .hero__mascot {
  animation: mascot-reveal 1400ms var(--h-ease-out) 100ms both;
}

.js .hero__credit span {
  opacity: 0;
}

.js.ready .hero__credit span {
  animation: mascot-fade 700ms var(--h-ease-in-out) 1000ms both;
}

.js.ready .hero__mascot[data-mascot-loading] {
  animation: none;
  opacity: 0;
  filter: blur(var(--h-blur-4));
}

.showcase {
  width: 100%;
  height: 118svh;
  margin-block-start: calc(-1 * (var(--h-space-8) + var(--h-space-4)));
}

/* The radius here is only the resting shape before JavaScript, and under
   reduced motion. assets/site/card-motion.mjs drives it from 64px to square
   as the card grows. */
.showcase__card {
  display: flex;
  align-items: center;
  width: min(100% - 2 * var(--h-space-5), 64rem);
  height: 100%;
  position: relative;
  isolation: isolate;
  margin-inline: auto;
  overflow: hidden;
  border-radius: 2rem;
  background: radial-gradient(
    130% 95% at 50% -10%,
    var(--h-bg-selected),
    var(--h-bg-hover) 55%,
    transparent 100%
  );
}

.showcase[data-card-ready] .showcase__card {
  width: 100%;
  height: calc(100% + var(--h-space-5));
  margin: 0;
  transform: translateY(var(--card-rise)) scale(var(--card-scale));
  transform-origin: 50% 0;
  border-radius: var(--card-radius);
  box-shadow: 0 2px 12px color-mix(in oklab, var(--h-bg-app) 45%, transparent);
}

.showcase[data-card-ready] .showcase__content {
  opacity: var(--card-content-opacity);
  transform: translateY(var(--card-content-y));
}

.showcase__content {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  height: 100svh;
  margin-inline: auto;
  padding: var(--h-space-8) clamp(var(--h-space-5), 5vw, var(--h-space-8));
  text-align: center;
}

.showcase__film,
.showcase__hold {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.showcase__hold {
  opacity: 0;
}

.showcase[data-holding] .showcase__hold {
  opacity: 1;
}

.showcase__film {
  background: url("/assets/showcase/final-landscape.webp") 50% 50% / cover no-repeat;
}

@media (orientation: portrait) {
  .showcase__film {
    background-image: url("/assets/showcase/final-portrait.webp");
  }
}

@media (prefers-reduced-motion: no-preference) {
  .js .showcase__film {
    background-image: none;
  }
}

.showcase[data-cinematic] .showcase__copy {
  opacity: 0;
}

.showcase[data-cinematic="done"] .showcase__copy {
  animation: showcase-copy 1.2s var(--h-ease-out) both;
}

.showcase__copy {
  max-width: 40rem;
}

.showcase__title {
  margin: 0;
  color: var(--art-film-text);
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 4.6vw, 4.25rem);
  font-weight: var(--h-weight-medium);
  line-height: 1.1;
  letter-spacing: var(--h-tracking-tight);
  text-wrap: balance;
  text-shadow: 0 0 0.45em var(--art-film-glow);
  filter: blur(var(--art-film-soften));
}

@keyframes showcase-copy {
  from { opacity: 0; filter: blur(var(--h-blur-3)); }
  to { opacity: 1; filter: blur(var(--art-film-soften)); }
}

.wall {
  --wall-width: 34rem;
  --note-avatar: 1.375rem;

  margin-block-start: calc(var(--h-space-8) + var(--h-space-4));
  scroll-margin-block-start: calc(var(--height-header) + var(--h-space-6));
}

.wall__column {
  width: min(100%, calc(var(--wall-width) + 2 * var(--space-content)));
  margin-inline: auto;
  padding-inline: var(--space-content);
}

.wall__title {
  margin: 0 0 var(--h-space-6);
  font-size: var(--h-text-title-1);
  font-weight: var(--h-weight-semibold);
  letter-spacing: var(--h-tracking-tighter);
  text-align: center;
  text-wrap: balance;
}

.wall__composer {
  border: 1px solid var(--h-border-subtle);
  border-radius: var(--h-radius-2xl);
  background: var(--h-bg-panel);
  transition: border-color var(--h-transition-hover);
}

.wall__composer:hover {
  border-color: var(--h-border-default);
}

.wall__composer:has(:is(.wall__message, .wall__name):focus-visible) {
  border-color: var(--h-border-focus);
}

.wall__field {
  display: block;
}

.wall__message {
  display: block;
  width: 100%;
  min-height: calc(3lh + 2 * var(--h-space-4));
  max-height: calc(12lh + 2 * var(--h-space-4));
  padding: var(--h-space-4);
  border: 0;
  background: transparent;
  color: var(--h-fg-primary);
  font: inherit;
  font-size: var(--h-text-large);
  line-height: var(--h-leading-normal);
  resize: vertical;
}

@supports (field-sizing: content) {
  .wall__message {
    field-sizing: content;
    resize: none;
  }
}

.wall__message:focus-visible,
.wall__name:focus-visible {
  outline: none;
}

.wall__message::placeholder,
.wall__name::placeholder {
  color: var(--h-fg-tertiary);
  transition: color var(--h-transition-hover);
}

.wall__message:focus::placeholder,
.wall__name:focus::placeholder {
  color: var(--h-fg-disabled);
}

.wall__honeypot {
  position: absolute;
  inset-inline-start: -9999px;
  width: 1px;
  height: 1px;
  opacity: 0;
}

.wall__bar {
  display: flex;
  align-items: center;
  gap: var(--h-space-3);
  min-height: 3.5rem;
  padding: var(--h-space-2);
  border-block-start: 1px solid var(--h-border-subtle);
}

.wall__field--name {
  flex: 1;
  min-width: 0;
}

.wall__name {
  width: 100%;
  padding: var(--h-space-2) var(--h-space-3);
  border: 0;
  border-radius: var(--h-radius-lg);
  background: transparent;
  color: var(--h-fg-primary);
  font: inherit;
  font-size: var(--h-text-large);
  transition: background-color var(--h-transition-hover);
}

.wall__name:hover,
.wall__name:focus-visible {
  background: var(--h-bg-hover);
}

.wall__identity {
  display: flex;
  flex: 1;
  align-items: center;
  gap: var(--h-space-2);
  min-width: 0;
  margin: 0;
  padding-inline-start: var(--h-space-3);
  color: var(--h-fg-secondary);
  font-size: var(--h-text-base);
}

.wall__identity-photo {
  flex: none;
  width: var(--note-avatar);
  height: var(--note-avatar);
  border-radius: var(--h-radius-full);
  object-fit: cover;
}

.wall__identity-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.wall__identity strong {
  color: var(--h-fg-primary);
  font-weight: var(--h-weight-medium);
}

.wall__count {
  flex: none;
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-small);
  font-variant-numeric: tabular-nums;
  transition: color var(--h-transition-hover);
}

.wall__count--low {
  color: var(--h-danger-fg);
}

.wall__submit {
  flex: none;
  min-height: var(--h-control-xl);
  margin-inline-start: auto;
  padding-inline: var(--h-space-4);
  border: 1px solid var(--h-accent-bg);
  border-radius: var(--h-radius-lg);
  background: var(--h-accent-bg);
  color: var(--h-fg-on-accent);
  font: inherit;
  font-size: var(--h-text-base);
  font-weight: var(--h-weight-semibold);
  cursor: pointer;
  transition:
    border-color var(--h-transition-hover),
    background-color var(--h-transition-hover),
    color var(--h-transition-hover),
    opacity var(--h-transition-hover),
    transform var(--h-transition-hover);
}

.wall__composer:has(.wall__message:placeholder-shown) .wall__submit {
  border-color: var(--h-border-default);
  background: transparent;
  color: var(--h-fg-tertiary);
}

.wall__submit:active:not(:disabled) {
  transform: scale(0.97);
}

.wall__submit:disabled {
  opacity: 0.6;
  cursor: progress;
}

.wall__aside {
  margin: var(--h-space-4) 0;
  color: var(--h-fg-secondary);
  font-size: var(--h-text-base);
  line-height: var(--h-leading-normal);
  text-align: center;
  text-wrap: balance;
}

.wall__link {
  color: var(--h-fg-secondary);
  text-decoration: underline;
  text-decoration-color: color-mix(in oklab, currentColor 30%, transparent);
  text-underline-offset: 0.22em;
  transition:
    color var(--h-transition-hover),
    text-decoration-color var(--h-transition-hover);
}

.wall__signout {
  display: inline;
}

.wall__link--pending {
  visibility: hidden;
}

button.wall__link {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

.wall__link:hover,
.wall__link:focus-visible {
  color: var(--h-fg-primary);
  text-decoration-color: currentColor;
}

.wall__link-arrow {
  display: inline-block;
  margin-inline-start: var(--h-space-1);
  transition: transform var(--h-transition-hover);
}

.wall__link:hover .wall__link-arrow {
  transform: translateX(2px);
}

.wall__said {
  margin-block-start: calc(var(--h-space-8) + var(--h-space-4));
  text-align: center;
}

.wall__said-title {
  margin: 0 0 var(--h-space-5);
  padding-inline: var(--space-content);
  color: var(--h-fg-secondary);
  font-size: var(--h-text-large);
  font-weight: var(--h-weight-medium);
  letter-spacing: var(--h-tracking-tight);
}

/* The edges fade with a mask rather than overlays painted in the page colour,
   so the fade needs no colour of its own and nothing has to be layered above
   the moving track. */
.wall__strip {
  --strip-card: 17.5rem;
  --strip-fade: clamp(2rem, 12vw, 10rem);

  position: relative;
  overflow: hidden;
  text-align: start;
  mask-image: linear-gradient(
    to right,
    transparent,
    black var(--strip-fade),
    black calc(100% - var(--strip-fade)),
    transparent
  );
}

.wall__track {
  display: flex;
  width: max-content;
}

.wall__reel {
  display: flex;
  margin: 0;
  padding: 0;
  border-block: 1px solid var(--h-border-subtle);
  list-style: none;
}

.wall__reel > li {
  flex: none;
  width: var(--strip-card);
  border-inline-start: 1px solid var(--h-border-subtle);
}

@media (prefers-reduced-motion: no-preference) {
  .wall__track {
    animation: strip-drift calc(var(--strip-cards) * 7s) linear infinite;
  }

  .wall__strip:hover .wall__track,
  .wall__strip:focus-within .wall__track,
  .wall__strip--paused .wall__track {
    animation-play-state: paused;
  }
}

@media (prefers-reduced-motion: reduce) {
  .wall__strip {
    overflow-x: auto;
    padding-inline: var(--space-content);
    scroll-snap-type: x proximity;
    scroll-padding-inline: var(--space-content);
    mask-image: none;
  }

  .wall__reel + .wall__reel,
  .wall__reel > li[aria-hidden] {
    display: none;
  }

  .wall__reel {
    width: max-content;
    border-inline-end: 1px solid var(--h-border-subtle);
  }

  .wall__reel > li {
    scroll-snap-align: start;
  }
}

@keyframes strip-drift {
  to {
    transform: translateX(-50%);
  }
}

.wall__said-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--h-space-5);
  margin-block-start: var(--h-space-5);
  padding-inline: var(--space-content);
}

.wall__pause {
  display: grid;
  place-items: center;
  width: var(--h-control-xl);
  height: var(--h-control-xl);
  padding: 0;
  border: 1px solid var(--h-border-default);
  border-radius: var(--h-radius-full);
  background: transparent;
  color: var(--h-fg-tertiary);
  cursor: pointer;
  transition:
    border-color var(--h-transition-hover),
    color var(--h-transition-hover);
}

.wall__pause svg {
  width: 0.75rem;
  height: 0.75rem;
  fill: currentColor;
}

.wall__pause:hover {
  border-color: var(--h-border-strong);
  color: var(--h-fg-primary);
}

@media (prefers-reduced-motion: reduce) {
  .wall__pause {
    display: none;
  }
}

.wall__all {
  display: inline-flex;
  align-items: center;
  color: var(--h-fg-secondary);
  font-size: var(--h-text-base);
  text-decoration: none;
  transition: color var(--h-transition-hover);
}

.wall__all:hover,
.wall__all:focus-visible {
  color: var(--h-fg-primary);
}

.wall__all:hover .wall__link-arrow {
  transform: translateX(2px);
}

.snapshot {
  display: flex;
  flex-direction: column;
  gap: var(--h-space-4);
  height: 100%;
  margin: 0;
  padding: var(--h-space-5);
  background: var(--h-bg-panel);
}

.snapshot__body {
  flex: 1;
  margin: 0;
}

.snapshot__body p {
  display: -webkit-box;
  margin: 0;
  overflow: hidden;
  font-size: var(--h-text-large);
  line-height: var(--h-leading-normal);
  overflow-wrap: anywhere;
  white-space: pre-line;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 4;
  line-clamp: 4;
}

.snapshot__meta,
.note__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--h-space-1) var(--h-space-3);
  margin: 0;
  font-size: var(--h-text-small);
}

.snapshot__date,
.note__date {
  margin-inline-start: auto;
  color: var(--h-fg-tertiary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.byline {
  display: inline-flex;
  align-items: center;
  gap: var(--h-space-2);
  min-width: 0;
}

.byline__avatar {
  flex: none;
  width: var(--note-avatar);
  height: var(--note-avatar);
  border-radius: var(--h-radius-full);
  object-fit: cover;
}

.byline__name {
  overflow: hidden;
  font-weight: var(--h-weight-medium);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.byline__badge {
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-mini);
}

.site-main:has(.notes) {
  --note-avatar: 1.375rem;

  max-width: calc(44rem + 2 * var(--space-content));
}

.notes__leave {
  color: var(--h-fg-secondary);
  text-decoration: underline;
  text-decoration-color: color-mix(in oklab, currentColor 30%, transparent);
  text-underline-offset: 0.22em;
  transition:
    color var(--h-transition-hover),
    text-decoration-color var(--h-transition-hover);
}

.notes__leave:hover,
.notes__leave:focus-visible {
  color: var(--h-fg-primary);
  text-decoration-color: currentColor;
}

.notes__status {
  margin: var(--h-space-7) 0 0;
  color: var(--h-fg-secondary);
}

.notes__list {
  margin: var(--h-space-6) 0 0;
  padding: 0;
  border-block-start: 1px solid var(--h-border-subtle);
  list-style: none;
}

.note {
  padding: var(--h-space-4) var(--h-space-1);
  border-block-end: 1px solid var(--h-border-subtle);
}

.note__body {
  margin: var(--h-space-2) 0 0;
  font-size: var(--h-text-large);
  line-height: var(--h-leading-normal);
  overflow-wrap: anywhere;
  white-space: pre-line;
}

.notes__more {
  display: block;
  min-height: var(--h-control-xl);
  margin: var(--h-space-6) auto 0;
  padding-inline: var(--h-space-5);
  border: 1px solid var(--h-border-default);
  border-radius: var(--h-radius-lg);
  background: transparent;
  color: var(--h-fg-secondary);
  font: inherit;
  font-size: var(--h-text-base);
  cursor: pointer;
  transition:
    border-color var(--h-transition-hover),
    color var(--h-transition-hover);
}

.notes__more:hover:not(:disabled) {
  border-color: var(--h-border-strong);
  color: var(--h-fg-primary);
}

.notes__more:disabled {
  opacity: 0.6;
  cursor: progress;
}

.wall__said,
.notes__list {
  animation: page-fade var(--h-transition-enter) both;
}

.queue {
  margin: var(--h-space-6) 0 0;
  padding: 0;
  list-style: none;
  border-block-start: 1px solid var(--h-border-subtle);
}

.queue__item {
  padding: var(--h-space-5) 0;
  border-block-end: 1px solid var(--h-border-subtle);
}

.queue__item--pending {
  border-inline-start: 2px solid var(--h-border-strong);
  padding-inline-start: var(--h-space-4);
}

.queue__body {
  margin: 0;
  overflow-wrap: anywhere;
}

.queue__meta {
  margin: var(--h-space-2) 0 var(--h-space-3);
  color: var(--h-fg-tertiary);
  font-size: var(--h-text-small);
}

.queue__actions {
  display: flex;
  gap: var(--h-space-2);
}

.queue__actions button {
  padding: var(--h-space-1) var(--h-space-3);
  border: 1px solid var(--h-border-subtle);
  border-radius: var(--h-radius-md);
  background: var(--h-bg-panel);
  color: var(--h-fg-primary);
  font: inherit;
  font-size: var(--h-text-base);
  cursor: pointer;
}

.queue__actions button:hover {
  border-color: var(--h-border-strong);
}

.queue__delete:hover {
  border-color: var(--h-danger-border);
  color: var(--h-danger-fg);
}

@media (prefers-reduced-motion: reduce) {
  .js.ready .hero__mascot {
    animation-duration: 250ms;
    animation-name: mascot-fade;
  }

  .js.ready .hero__credit span {
    animation-duration: 250ms;
    animation-delay: 0ms;
  }

  .js.ready .hero__mascot[data-mascot-loading] {
    filter: none;
  }

  .showcase {
    height: auto;
    min-height: 36rem;
    margin-block-start: 0;
    padding: var(--h-space-7) var(--h-space-5);
  }

  .showcase__card {
    min-height: 30rem;
  }

  .showcase__content {
    height: auto;
    min-height: 30rem;
  }

  .contact-scene__canvas {
    filter: none;
    transform: none;
    transition: opacity 250ms var(--h-ease-out);
  }
}

@keyframes mascot-fade {
  from { opacity: 0; filter: none; }
  to { opacity: 1; filter: none; }
}

@media (max-width: 48rem) {
  .hero {
    padding: var(--h-space-6) var(--space-content) calc(2 * var(--h-space-7));
  }

  .hero__mascot {
    max-height: min(24rem, 48vh);
  }
}

@media (max-width: 34rem) {
  .showcase__title {
    font-size: clamp(2.3rem, 9vw, 3.5rem);
  }

  .showcase__content {
    padding-inline: var(--h-space-5);
  }

  .site-nav {
    gap: var(--h-space-4);
  }

  .site-nav__link {
    font-size: var(--h-text-small);
  }

  .site-footer__link {
    padding-inline: var(--h-space-2);
  }
}
