/* ==========================================================================
   Font Face Declarations
   ========================================================================== */

@font-face {
  font-family: "Monument Grotesk";
  src: url("../fonts/MonumentGrotesk-Regular.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}

/* Subset to what tags and the symbol's rhyme use: letters, digits, space and
   - . / : [ ] – (OFL, see fonts/GeistMono-OFL.txt). Re-subset from v1.7.2 if
   other characters are needed. */
@font-face {
  font-family: "Geist Mono";
  src: url("../fonts/GeistMono-Regular-subset.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0020, U+002D-002F, U+0030-003A, U+0041-005B, U+005D, U+0061-007A, U+2013;
}

/* ==========================================================================
   Design Tokens
   ========================================================================== */

:root {
  /* Color Primitives */
  --color-white: #FFFFFF;
  --color-black: #000000;
  --color-gray-950: #080C04;
  --color-gray-900: #151812;
  --color-gray-800: #20241A;
  --color-gray-700: #3E403A;
  --color-gray-600: #646660;
  --color-gray-500: #8E908A;
  --color-gray-400: #AEB0AA;
  --color-gray-300: #CED0CA;
  --color-gray-200: #E8EAE5;
  --color-gray-100: #F2F4EF;
  --color-gray-50: #F8FAF5;
  --color-irwin: #D22D04;
  --color-ruud: #FD3E21;
  --color-zalgiris: #529735;
  --color-sabonis: #E6BB0F;
  --color-dardai: #3E81E0;

  /* Color Semantic - Light Theme (default in :root) */
  --color-background: var(--color-gray-50);
  --color-background-subtle: var(--color-gray-100);
  --color-background-hover: var(--color-gray-100);
  --color-background-hover-rgb: 242, 244, 239;
  --color-body: var(--color-gray-800);
  --color-body-subtle: var(--color-gray-600);
  --color-border: var(--color-gray-200);
  --color-border-hover: var(--color-gray-300);
  --color-annotation: var(--color-gray-400);
  --color-accent: var(--color-irwin);
  --color-shadow: rgba(0, 0, 0, 0.04);

  /* Fonts */
  --font-family-sans: "Monument Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  --font-family-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Typography */
  --type-body-font-family: var(--font-family-sans);
  --type-body-font-size: 16px;
  --type-body-xs-font-size: 12px;
  --type-body-sm-font-size: 14px;
  --type-body-line-height: 20px;
  --type-body-sm-line-height: 18px;
  --type-body-xs-line-height: 16px;
  --type-body-font-weight: 400;
  --type-body-letter-spacing: 0;
  --type-display-letter-spacing: -0.01em;
  --type-heading-font-family: var(--font-family-sans);
  --type-heading-xl-font-size: 52px;
  --type-heading-lg-font-size: 44px;
  --type-heading-md-font-size: 28px;
  --type-heading-sm-font-size: 19px;
  --type-heading-xs-font-size: 16px;
  --type-heading-xl-line-height: 56px;
  --type-heading-lg-line-height: 48px;
  --type-heading-md-line-height: 32px;
  --type-heading-sm-line-height: 24px;
  --type-heading-xs-line-height: 20px;
  --type-heading-font-weight: 400;
  --type-mono-font-size: 11px;

  /* Breakpoints */
  --breakpoint-sm: 375px;
  --breakpoint-md: 768px;
  --breakpoint-lg: 1024px;
  --breakpoint-xl: 1360px;
  --breakpoint-max: 1600px;

  /* Grid */
  --grid-margin: 16px;
  --grid-gap: 16px;
  --grid-row: 16px;
  --grid-readable-width: 548px;
  --grid-content-width: 1600px;

  /* Spacing */
  --spacing-0: 0;
  --spacing-4: 4px;
  --spacing-8: 8px;
  --spacing-12: 12px;
  --spacing-16: 16px;
  --spacing-20: 20px;
  --spacing-24: 24px;
  --spacing-32: 32px;
  --spacing-40: 40px;
  --spacing-48: 48px;
  --spacing-56: 56px;
  --spacing-64: 64px;
  --spacing-72: 72px;
  --spacing-96: 96px;
  --spacing-144: 144px;
  --spacing-192: 192px;
  --spacing-320: 320px;
  --spacing-section-sm: var(--spacing-96);
  --spacing-section-lg: var(--spacing-144);

  /* Layout */
  --nav-height: 48px;

  /* Border Radius */
  --radius-xs: 4px;
  --radius-sm: 8px;
  --radius-md: 16px;
  --radius-lg: 48px;

  /* Easing */
  --ease-out: cubic-bezier(0.23, 1, 0.32, 1);
}

/* Color Semantic - Dark theme */
body.theme-dark {
  --color-background: var(--color-gray-950);
  --color-background-subtle: var(--color-gray-900);
  --color-background-hover: var(--color-gray-900);
  --color-background-hover-rgb: 21, 24, 18;
  --color-body: var(--color-gray-100);
  --color-body-subtle: var(--color-gray-500);
  --color-border: var(--color-gray-800);
  --color-border-hover: var(--color-gray-700);
  --color-annotation: var(--color-gray-700);
  --color-accent: var(--color-ruud);
  --color-shadow: rgba(255, 255, 255, 0.04);
}

/* ==========================================================================
   Base Styles
   ========================================================================== */

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

*:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* Stops iOS enlarging text when the phone is turned to landscape */
html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

body {
  font-family: var(--type-body-font-family);
  font-size: var(--type-body-font-size);
  line-height: var(--type-body-line-height);
  font-weight: var(--type-body-font-weight);
  color: var(--color-body);
  background-color: var(--color-background);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--color-annotation);
  text-decoration-thickness: .06em;
  text-underline-offset: 2px;
  transition: color 150ms ease, text-decoration-color 150ms ease;
  -webkit-tap-highlight-color: transparent;
}

@media (hover: hover) and (pointer: fine) {
  a:hover {
    color: var(--color-accent);
    text-decoration-color: oklch(from var(--color-accent) l c h / 0.45);
  }
}

/* Touch has no hover, so a press shows the hover colour at once */
a:active,
.hero-contact a:active,
.note a:active {
  color: var(--color-accent);
  text-decoration-color: oklch(from var(--color-accent) l c h / 0.45);
  transition-duration: 0ms;
}

h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: var(--type-heading-font-weight);
}

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

::selection {
  background-color: var(--color-background-subtle);
  color: var(--color-body);
}

/* Notes and the 404 are for reading, so selected text needs to stand out */
.page-post ::selection,
.page-404 ::selection {
  background-color: oklch(from var(--color-accent) l c h / 0.2);
}

/* ==========================================================================
   Utility Classes
   ========================================================================== */

.text-strikethrough {
  text-decoration: line-through;
}

.text-nowrap {
  white-space: nowrap;
}

.text-body {
  font-family: var(--type-body-font-family);
  font-size: var(--type-body-font-size);
  line-height: var(--type-body-line-height);
  font-weight: var(--type-body-font-weight);
  color: var(--color-body);  
}
   
.text-subtle {
  color: var(--color-body-subtle);  
}

.text-mono {
  align-self: flex-start;
  color: var(--color-body);
  font-family: var(--font-family-mono);
  font-size: var(--type-mono-font-size);
  line-height: var(--type-body-xs-line-height);
  font-feature-settings: "lnum", "ordn";
  text-transform: uppercase;
  white-space: nowrap;
}

.text-mono::before,
.text-mono::after {
  color: var(--color-annotation);
}

.text-mono::before {
  content: "[" / "";
}

.text-mono::after {
  content: "]" / "";
}

/* A CSS mask rather than an <img>, so the arrow's colour comes from a token */
.icon-external {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: middle;
  position: relative;
  top: -0.05em;
  opacity: 0.5;
  background-color: var(--color-body-subtle);
  -webkit-mask: url("../images/arrow-up-right.svg") center / contain no-repeat;
  mask: url("../images/arrow-up-right.svg") center / contain no-repeat;
  -webkit-user-select: none;
  user-select: none;
}

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

/* Phones set the homepage a size down, apart from On Repeat */
.page-index main,
.page-index .text-body,
.page-index .footer-links a,
.page-index .footer-year {
  font-size: var(--type-body-sm-font-size);
  line-height: var(--type-body-sm-line-height);
}

/* ==========================================================================
   Preloader
   ========================================================================== */

.preloader {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: var(--color-background);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
  opacity: 1;
  transition: opacity 260ms var(--ease-out);
}

.preloader video {
  width: 256px;
  height: auto;
  aspect-ratio: 2 / 3;
}

.preloader.fade-out {
  opacity: 0;
  pointer-events: none;
}

body.loading main,
body.loading .footer {
  opacity: 0;
}

body.loaded main,
body.loaded .footer {
  opacity: 1;
  transition-property: opacity;
  transition-duration: 260ms;
  transition-timing-function: var(--ease-out);
}

body.loaded main {
  transition-delay: 50ms;
}

body.loaded .footer {
  transition-delay: 100ms;
}

/* The hero and the projects arrive together, the projects 80ms behind: from
   768px the split puts them side by side, and below it the first project
   shows under the bio on most phones. Only the first two projects blur; the
   column is too tall to blur whole. */
body.loading .hero {
  opacity: 0;
  transform: translateY(6px);
  filter: blur(4px);
}

body.loaded .hero {
  opacity: 1;
  transform: translateY(0);
  filter: none;
  transition: 800ms var(--ease-out) 50ms;
  transition-property: opacity, transform, filter;
}

body.loading .projects {
  opacity: 0;
  transform: translateY(6px);
}

body.loaded .projects {
  opacity: 1;
  transform: translateY(0);
  transition: 800ms var(--ease-out) 130ms;
  transition-property: opacity, transform;
}

body.loading .project:nth-of-type(-n+2) {
  filter: blur(4px);
}

body.loaded .project:nth-of-type(-n+2) {
  filter: none;
  transition: filter 800ms var(--ease-out) 130ms;
}

/* ==========================================================================
   Navigation
   ========================================================================== */

.navigation {
  position: relative;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  column-gap: var(--grid-gap);
  align-items: center;
  height: var(--nav-height);
  padding: var(--spacing-12) var(--grid-margin);
  margin-inline: calc(var(--grid-margin) * -1);
}

/* W. and K. take 2 columns each on phones, 1 each from 768px */
.nav-names {
  grid-column: 1 / 7;
  display: grid;
  grid-template-columns: subgrid;
  align-items: center;
}

.nav-name {
  position: relative;
  grid-column: span 2;
  min-width: 0;
  font-size: var(--type-body-sm-font-size);
  line-height: var(--type-body-sm-line-height);
  font-weight: var(--type-body-font-weight);
  text-transform: uppercase;
  white-space: nowrap;
}

.nav-name a {
  display: inline-grid;
  text-decoration: none;
  transition: opacity 150ms ease;
}

/* Hit area fills the column and the nav's height and meets its neighbour
   halfway across the gap, so moving between W. and K. stays hovered */
.nav-name a::before {
  content: "";
  position: absolute;
  inset: calc((var(--nav-height) - 100%) / -2) calc(var(--grid-gap) / -2);
}

.nav-name-short,
.nav-name-full {
  grid-area: 1 / 1;
  transition: opacity 150ms var(--ease-out), filter 150ms var(--ease-out);
}

.nav-name-full {
  opacity: 0;
  filter: blur(2px);
}

.nav-name a:active {
  color: inherit;
  opacity: 0.6;
  transition-duration: 0ms;
}

.nav-names:has(a:focus-visible) .nav-name-short {
  opacity: 0;
  transition: none;
}

.nav-names:has(a:focus-visible) .nav-name-full {
  opacity: 1;
  filter: none;
  transition: none;
}

@media (hover: hover) and (pointer: fine) {
  .nav-name a:hover {
    color: inherit;
  }

  .nav-names:has(a:hover) .nav-name-short {
    opacity: 0;
    filter: blur(2px);
  }

  .nav-names:has(a:hover) .nav-name-full {
    opacity: 1;
    filter: none;
  }

  /* K. follows W. on the way in; both return together */
  .nav-names:has(a:hover) .nav-name:last-child .nav-name-short,
  .nav-names:has(a:hover) .nav-name:last-child .nav-name-full {
    transition-delay: 40ms;
  }
}

.nav-links {
  grid-column: 7 / 13;
  display: flex;
  gap: var(--spacing-8);
  font-size: var(--type-body-sm-font-size);
  line-height: var(--type-body-sm-line-height);
}

@keyframes nav-scroll-fade {
  to {
    opacity: 0;
    filter: blur(var(--handover-blur));
    visibility: hidden;
  }
}

/* Takes over from the nav once the projects reach the top (pinned split only) */
.projects-label {
  display: none;
}

@keyframes projects-label-fade {
  from {
    opacity: 0;
    filter: blur(var(--handover-blur));
    visibility: hidden;
  }
}

/* The names and links make way while the full-width pill is open (phones) */
.nav-names,
.nav-links {
  transition: opacity 150ms var(--ease-out);
}

.navigation:has(.nav-symbol.is-open) .nav-names,
.navigation:has(.nav-symbol.is-open) .nav-links {
  opacity: 0;
}

/* On phones the pill spans the nav between the margins, so it reads as the
   nav making way for the rhyme rather than covering the links */
.nav-symbol {
  --island-width: calc(100vw - var(--grid-margin) * 2);
  --island-inset: 13px;
  display: none;
  position: absolute;
  top: calc(50% + 0.5px);
  right: var(--grid-margin);
  width: 12px;
  height: 12px;
  translate: 0 -50%;
  -webkit-user-select: none;
  user-select: none;
}

.symbol-button {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  fill: var(--color-accent);
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  transition: transform 250ms var(--ease-out);
}

.symbol-button::before {
  content: "";
  position: absolute;
  inset: -16px;
}

.symbol-button:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 4px;
}

/* A quarter turn per line of the rhyme, set by main.js */
.symbol-button svg {
  display: block;
  width: 100%;
  height: 100%;
  rotate: var(--symbol-turn, 0deg);
  transition: scale 100ms var(--ease-out), rotate 600ms var(--ease-out);
}

.symbol-button:active svg {
  scale: 0.9;
}

.nav-symbol.is-open .symbol-button {
  transform: translateX(calc(var(--island-inset) * -1));
  transition-duration: 300ms;
}

@property --pulse-depth {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

/* A colour per tile, from "SVG Symbol Multicolor" in Paper */
.nav-symbol path {
  --tile: var(--color-accent);
  fill: var(--tile);
  animation: nav-symbol-think 1200ms ease-in-out -300ms infinite paused;
  transition: --pulse-depth 300ms var(--ease-out);
}

.nav-symbol path:nth-child(1) {
  --tile: var(--color-sabonis);
}

.nav-symbol path:nth-child(2) {
  --tile: var(--color-dardai);
  animation-delay: 0ms;
}

.nav-symbol path:nth-child(3) {
  --tile: var(--color-ruud);
  animation-delay: 300ms;
}

.nav-symbol path:nth-child(4) {
  --tile: var(--color-gray-100);
  animation-delay: 600ms;
}

.nav-symbol.is-open path {
  --pulse-depth: 0.5;
  animation-play-state: running;
}

@keyframes nav-symbol-think {
  0%,
  100% {
    fill: var(--tile);
  }

  50% {
    fill: oklch(from var(--tile) calc(l * var(--pulse-depth)) calc(c * var(--pulse-depth)) h);
  }
}

@media (hover: hover) and (pointer: fine) {
  .nav-symbol:hover path {
    --pulse-depth: 0.5;
    animation-play-state: running;
  }
}

/* The turn replaces the pulse while the rhyme plays */
@media (prefers-reduced-motion: no-preference) {
  .nav-symbol.is-open path,
  .nav-symbol.is-open:hover path {
    --pulse-depth: 1;
    animation-play-state: paused;
  }
}

.island {
  position: absolute;
  top: 50%;
  right: 0;
  width: var(--island-width);
  height: 32px;
  background: var(--color-background-subtle);
  translate: 0 -50%;
  clip-path: inset(0 0 0 calc(var(--island-width) - 32px) round var(--radius-md));
  opacity: 0;
  pointer-events: none;
  transition: clip-path 250ms var(--ease-out), opacity 150ms var(--ease-out) 100ms;
}

.nav-symbol.is-open .island {
  clip-path: inset(0 0 round var(--radius-md));
  opacity: 1;
  pointer-events: auto;
  transition: clip-path 300ms var(--ease-out), opacity 100ms var(--ease-out);
}

.island-text {
  position: absolute;
  top: var(--spacing-8);
  left: var(--island-inset);
  color: var(--color-body);
  font-family: var(--font-family-mono);
  font-size: var(--type-mono-font-size);
  line-height: var(--type-body-xs-line-height);
  white-space: pre;
}

.island-text.is-leaving {
  opacity: 0;
  filter: blur(2px);
  transition: opacity 150ms var(--ease-out), filter 150ms var(--ease-out);
}

.island-chunk {
  transition: opacity 200ms var(--ease-out);
}

@starting-style {
  .island-chunk {
    opacity: 0;
  }
}

/* Post pages and the 404: a single link back to the homepage */
.navigation-post {
  height: var(--nav-height);
  margin-inline: calc(var(--grid-margin) * -1);
}

.navigation-post-link {
  display: inline-flex;
  padding: var(--spacing-16) var(--grid-margin);
  text-decoration: none;
}

/* Underline "Back" only, not its brackets */
.navigation-post-link .text-mono::before,
.navigation-post-link .text-mono::after {
  display: inline-block;
}

@media (hover: hover) and (pointer: fine) {
  .navigation-post-link:hover .text-mono {
    text-decoration-line: underline;
    text-decoration-color: var(--color-annotation);
    text-decoration-thickness: .06em;
    text-underline-offset: 2px;
  }
}

/* ==========================================================================
   Split Layout
   ========================================================================== */

.split {
  --split-height: 100vh;
  --projects-end: var(--spacing-56);
  padding-inline: var(--grid-margin);
}

@supports (height: 100svh) {
  .split {
    --split-height: 100svh;
  }
}

/* ==========================================================================
   Hero Section
   ========================================================================== */

.hero {
  padding-top: var(--spacing-72);
}

/* Bio and project descriptions take 11 of the 12 columns on large phones
   (480-767px); smaller phones keep the full width */
@media (min-width: 480px) {
  .hero-title,
  .project-description {
    max-width: calc((100% + var(--grid-gap)) * 11 / 12 - var(--grid-gap));
  }
}

.hero-title > * + * {
  margin-top: var(--spacing-12);
}

.hero-contact {
  margin-top: var(--spacing-12);
}

.hero-contact a {
  color: var(--color-body);
}

@media (hover: hover) and (pointer: fine) {
  .hero-contact a:hover {
    color: var(--color-accent);
    text-decoration-color: oklch(from var(--color-accent) l c h / 0.45);
  }
}

/* ==========================================================================
   On Repeat
   ========================================================================== */

.on-repeat {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-16);
  padding: 0 var(--grid-margin) var(--spacing-48);
  font-size: var(--type-body-xs-font-size);
  line-height: var(--type-body-xs-line-height);
}

.on-repeat-title {
  color: var(--color-body-subtle);
  font-size: inherit;
  font-weight: var(--type-body-font-weight);
}

.tracks {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-12);
  list-style: none;
}

.track {
  display: grid;
  grid-template-columns: 32px auto;
  justify-content: start;
  grid-template-rows: auto auto;
  column-gap: var(--spacing-8);
}

/* Cover and title share one link (one tab stop per track); the artist sits
   outside it on the second row */
.track-link {
  position: relative;
  transition: opacity 150ms ease;
  grid-column: 1 / -1;
  grid-row: 1 / -1;
  display: grid;
  grid-template-columns: subgrid;
  grid-template-rows: subgrid;
}

/* Hit area spans half the gap either side, so neighbouring rows meet */
.track-link::before {
  content: "";
  position: absolute;
  inset: calc(var(--spacing-12) / -2) 0;
}

.track picture {
  display: contents;
}

.track source {
  display: none;
}

.track-cover {
  grid-column: 1;
  grid-row: 1 / 3;
  width: 32px;
  height: 32px;
  object-fit: cover;
  border-radius: var(--radius-xs);
  background-color: var(--color-background-subtle);
}

.track-title,
.track-artist {
  grid-column: 2;
}

.track-title {
  grid-row: 1;
}

.track-artist {
  grid-row: 2;
}

.track-link:active {
  opacity: 0.6;
  transition-duration: 0ms;
}

/* Blurred copy of the cover, added by main.js */
.track-glow {
  grid-column: 1;
  grid-row: 1 / 3;
  z-index: -1;
  width: 32px;
  height: 32px;
  object-fit: cover;
  border-radius: var(--radius-xs);
  filter: blur(10px) saturate(1.6);
  opacity: 0;
  transform: scale(0.8);
  will-change: transform;
  pointer-events: none;
}

@media (hover: hover) and (pointer: fine) {
  .track-link {
    isolation: isolate;
  }

  .track-link:hover {
    text-decoration-color: var(--color-annotation);
  }

  .track-glow {
    transition: opacity 150ms var(--ease-out), transform 150ms var(--ease-out);
  }

  .track-link:hover .track-glow,
  .track-link:focus-visible .track-glow {
    opacity: 0.72;
    transform: scale(1.25);
    transition-duration: 200ms;
  }
}

@media (prefers-reduced-motion: reduce) {
  .track-glow {
    transform: scale(1.25);
  }
}

.track-title {
  color: var(--color-body);
}

.track-artist {
  color: var(--color-body-subtle);
  pointer-events: none;
}

/* ==========================================================================
   Projects Section
   ========================================================================== */

.projects {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-32);
  padding: var(--spacing-72) 0 var(--projects-end);
}

.project {
  padding-top: var(--spacing-16);
  border-top: 1px solid var(--color-border);
}

.project-header {
  display: flex;
  align-items: center;
  gap: var(--spacing-4);
}

.project-description {
  margin-bottom: var(--spacing-24);
}

.project-images {
  display: flex;
  flex-direction: column;
  gap: var(--grid-row);
}

.project-media {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--spacing-24);
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: var(--radius-xs);
  background-color: var(--color-background-subtle);
}

.project-media picture {
  display: contents;
}

.project-media source {
  display: none;
}

.project-image {
  height: auto;
}

.project-image--wide {
  width: 80%;
}

.project-image--tall {
  width: 60%;
}

.project-image--phone {
  width: 25%;
}

/* The load-in fade is a WAAPI animation fired on the image's load event (see
   main.js). Nothing is pre-hidden in CSS on purpose - opacity would take the
   placeholder background down with it, leaving a hole for the whole fetch. */

/* ==========================================================================
   Notes Section
   ========================================================================== */

.notes {
  padding: 0 var(--grid-margin) var(--spacing-56);
}

.notes-list {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-12);
  list-style: none;
}

.note a {
  position: relative;
  color: var(--color-body);
}

/* Hit area spans half the gap either side, as on the On Repeat rows */
.note a::before {
  content: "";
  position: absolute;
  inset: calc(var(--spacing-12) / -2) 0;
}

@media (hover: hover) and (pointer: fine) {
  .note a:hover {
    color: var(--color-accent);
    text-decoration-color: oklch(from var(--color-accent) l c h / 0.45);
  }
}

.note-meta {
  display: block;
}

/* ==========================================================================
   About Section
   ========================================================================== */

.about {
  padding: 0 var(--grid-margin) var(--spacing-section-sm);
}

.section-two-up {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-12);
  padding-top: var(--spacing-16);
  border-top: 1px solid var(--color-border);
}

.section-two-up-body {
  grid-column: auto;
}

.section-two-up-body p {
  margin-bottom: var(--spacing-12);
}

.section-two-up-body p:last-child {
  margin-bottom: 0;
}

.claude-logo {
  vertical-align: -0.15em;
}

/* ==========================================================================
   Footer
   ========================================================================== */

.footer {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--spacing-16);
  padding: 0 var(--grid-margin) var(--spacing-16);
}

.footer-contact {
  display: none;
  font-size: var(--type-body-font-size);
  color: var(--color-body);
}

.footer-content {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0;
}

.footer-links {
  display: flex;
  order: -1;
  gap: var(--spacing-8);
}

.nav-links a:not(:last-child)::after,
.footer-links a:not(:last-child)::after {
  content: "," / "";
  position: absolute;
  left: 100%;
  pointer-events: none;
  color: var(--color-body);
}

.nav-links a,
.footer-links a {
  position: relative;
  text-decoration-line: none;
}

/* Hit areas about 44px tall, meeting their neighbours across the gap */
.nav-links a::before,
.footer-links a::before {
  content: "";
  position: absolute;
  inset: calc(var(--spacing-12) * -1) calc(var(--spacing-8) / -2);
}

.footer-links a {
  font-size: var(--type-body-font-size);
}

.nav-links a:active,
.footer-links a:active {
  color: inherit;
  text-decoration-line: underline;
  text-decoration-color: var(--color-annotation);
}

@media (hover: hover) and (pointer: fine) {
  .nav-links a:hover,
  .footer-links a:hover {
    color: inherit;
    text-decoration-line: underline;
    text-decoration-color: var(--color-annotation);
  }
}

.footer-year {
  color: var(--color-body-subtle);
  font-size: var(--type-body-font-size);
}

/* ==========================================================================
   Not Found (404.html)
   ========================================================================== */

.page-404 {
  display: flex;
  flex-direction: column;
  min-height: 100svh;
}

.not-found {
  display: flex;
  flex: 1;
  flex-direction: column;
  padding-inline: var(--grid-margin);
}

.not-found-body {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-16);
  margin-block: auto;
  padding-block: var(--spacing-16);
}

.not-found-text {
  max-width: 512px;
  padding-bottom: var(--spacing-16);
  font-size: var(--type-body-sm-font-size);
  line-height: var(--type-body-sm-line-height);
}

.not-found-title {
  margin-bottom: var(--spacing-8);
}

.not-found-link {
  margin-top: var(--spacing-8);
}

.not-found-figure {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-4);
}

.not-found-image {
  width: 100%;
  height: auto;
  background-color: var(--color-background-subtle);
  aspect-ratio: 459 / 288;
  object-fit: cover;
}

.not-found-caption {
  font-size: var(--type-mono-font-size);
  line-height: var(--type-body-xs-line-height);
  color: var(--color-body-subtle);
}

/* ==========================================================================
   Responsive - Tablet (--breakpoint-md, 768px and up)
   ========================================================================== */

@media (min-width: 768px) {
  .preloader video {
    width: 320px;
  }

  .nav-symbol {
    --island-width: 184px;
    display: block;
    width: 14px;
    height: 14px;
  }

  .symbol-button::before {
    inset: -15px;
  }

  .navigation:has(.nav-symbol.is-open) .nav-names,
  .navigation:has(.nav-symbol.is-open) .nav-links {
    opacity: 1;
  }

  .nav-name {
    grid-column: span 1;
  }

  .hero-title,
  .project-description {
    max-width: none;
  }

  .nav-name,
  .nav-links {
    font-size: var(--type-body-font-size);
    line-height: var(--type-body-line-height);
  }

  .page-index main,
  .page-index .text-body,
  .page-index .footer-links a,
  .page-index .footer-year {
    font-size: var(--type-body-font-size);
    line-height: var(--type-body-line-height);
  }

  .split {
    --projects-end: var(--spacing-64);
  }

  .hero-title > * + * {
    margin-top: var(--spacing-16);
  }

  .notes {
    padding-bottom: var(--spacing-64);
  }

  .notes-list,
  .section-two-up {
    gap: var(--spacing-16);
  }

  .section-two-up-body p {
    margin-bottom: var(--spacing-16);
  }

  .not-found-text {
    line-height: var(--type-body-line-height);
  }

  /* Hero and nav stay pinned while the projects scroll; both release together
     when the projects end. The nav's track stops one hero-height short of the
     split's bottom, which is the same moment the hero's sticky range runs out. */
  /* The bio starts on the halfway line, rising (to 96px at most) when the rest
     of the hero wouldn't fit below it. --hero-rest is the rest of the hero at
     its tallest for the width - bio, gap, link and bottom padding. The
     projects start 16px higher so the first title lines up with the bio. */
  .split {
    --hero-rest: 383px;
    --split-top: clamp(var(--spacing-96), calc(var(--split-height) - var(--hero-rest)), calc(var(--split-height) / 2));
    position: relative;
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    column-gap: var(--grid-gap);
    align-items: start;
  }

  .nav-track {
    position: absolute;
    top: 0;
    right: 0;
    bottom: calc(var(--split-height) - var(--nav-height));
    left: 0;
    z-index: 10;
    padding-inline: var(--grid-margin);
    pointer-events: none;
  }

  .navigation {
    pointer-events: auto;
  }

  .hero {
    grid-column: 1 / 7;
    display: grid;
    grid-template-columns: subgrid;
    grid-template-rows: auto 1fr;
    row-gap: var(--spacing-32);
    padding: var(--split-top) 0 var(--spacing-16);
  }

  .hero-title {
    grid-column: 1 / -1;
  }

  /* Bottom of the hero row, so on a window too short to pin it simply
     follows the bio */
  .hero-contact {
    grid-column: 1 / -1;
    align-self: end;
    margin-top: 0;
  }

  .projects {
    grid-column: 7 / 13;
    display: grid;
    grid-template-columns: subgrid;
    gap: var(--spacing-32) normal;
    padding-top: calc(var(--split-top) - var(--spacing-16));
  }

  .project {
    grid-column: 1 / -1;
    display: grid;
    grid-template-columns: subgrid;
  }

  .project:first-of-type {
    border-top: 0;
  }

  .project-header,
  .project-description,
  .project-images {
    grid-column: 1 / -1;
  }

  .section-two-up {
    grid-template-columns: repeat(12, 1fr);
  }

  .section-two-up-title {
    grid-column: 1 / 7;
    margin-bottom: 0;
  }

  .section-two-up-body {
    grid-column: span 6;
  }

  /* On Repeat lines up with the About body column */
  .on-repeat {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    gap: var(--spacing-16) var(--grid-gap);
  }

  .on-repeat > * {
    grid-column: 7 / 13;
  }

  .footer {
    grid-template-columns: 1fr 1fr;
  }

  .footer-contact {
    display: block;
  }

  .footer-content {
    flex-direction: row;
    justify-content: space-between;
    align-items: center;
  }

  .footer-links {
    order: 0;
  }

  .not-found-body {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    align-items: start;
    gap: var(--grid-gap);
  }

  .not-found-text {
    grid-column: 1 / 7;
  }

  .not-found-figure {
    grid-column: 7 / 13;
  }
}

/* ==========================================================================
   Split pinning (--breakpoint-md and at least 600px tall)
   The hero needs up to 456px of height at its tallest (768px wide).
   Shorter windows, such as phones in landscape, keep the split layout but
   scroll normally so the end of the bio is never cut off. Raise the
   threshold if the bio gets longer.
   ========================================================================== */

@media (min-width: 768px) and (min-height: 600px) {
  /* Release the pin early enough that the contact link lands level with the
     last project image: the projects end --projects-end below that image, the
     hero only 16px below the link. The nav's track ends by the same amount so the two
     still leave together. */
  /* The nav hands over to the label by the time Wired Women lines up with it */
  .split {
    --split-release: calc(var(--projects-end) - var(--spacing-16));
    --handover-start: calc((var(--split-top) - var(--spacing-16) - var(--nav-height)) / 2);
    --handover-end: calc(var(--split-top) - var(--spacing-12));
    --handover-mid: calc((var(--handover-start) + var(--handover-end)) / 2);
    --handover-blur: 2px;
  }

  .nav-track {
    bottom: calc(var(--split-height) - var(--nav-height) + var(--split-release));
  }

  .navigation {
    position: sticky;
    top: 0;
  }

  .projects-label {
    position: sticky;
    top: 0;
    display: flex;
    align-items: center;
    height: var(--nav-height);
    margin-top: calc(var(--nav-height) * -1);
    color: var(--color-body);
    opacity: 0;
  }

  @supports (animation-timeline: scroll()) {
    .navigation {
      animation: nav-scroll-fade linear both;
      animation-timeline: scroll(root);
      animation-range: var(--handover-start) calc(var(--handover-mid) + var(--spacing-8));
    }

    .projects-label {
      opacity: 1;
      animation: projects-label-fade linear both;
      animation-timeline: scroll(root);
      animation-range: calc(var(--handover-mid) - var(--spacing-8)) var(--handover-end);
    }
  }

  .hero {
    position: sticky;
    top: 0;
    height: var(--split-height);
    margin-bottom: var(--split-release);
  }
}

/* ==========================================================================
   Responsive - Desktop (--breakpoint-lg, 1024px and up)
   ========================================================================== */

@media (min-width: 1024px) {
  .split {
    --hero-rest: 323px;
    --projects-end: var(--spacing-96);
  }

  .notes {
    padding-bottom: var(--spacing-96);
  }

  .hero-title,
  .project-description {
    grid-column: 1 / 6;
  }

  .projects {
    row-gap: var(--spacing-64);
  }

  .project-header {
    justify-content: space-between;
  }

  .project-media {
    gap: var(--spacing-32);
  }

  .on-repeat {
    padding-bottom: var(--spacing-64);
  }

  .section-two-up-body {
    grid-column: 7 / 12;
  }

  .not-found-text {
    grid-column: 1 / 6;
  }

  .not-found-figure {
    grid-column: 7 / 12;
  }
}

/* ==========================================================================
   Responsive - Large Desktop (--breakpoint-xl, 1360px and up)
   ========================================================================== */

@media (min-width: 1360px) {
  .hero-title,
  .project-description {
    grid-column: 1 / 5;
  }

  .section-two-up-body {
    grid-column: span 4;
  }

  .not-found-text {
    grid-column: 1 / 5;
  }

  .not-found-figure {
    grid-column: 7 / 11;
  }
}

/* ==========================================================================
   Responsive - Max (--breakpoint-max, 1600px and up)
   The page stops at --grid-content-width and centres. From 2400px the sides
   take the subtle background; narrower, they would read as thin strips.
   The body's background fills the whole window, so the sides follow the
   page's theme.
   ========================================================================== */

@media (min-width: 1600px) {
  body {
    max-width: var(--grid-content-width);
    margin-inline: auto;
  }
}

@media (min-width: 2400px) {
  body {
    background: linear-gradient(to right,
      var(--color-background-subtle) calc(50% - var(--grid-content-width) / 2),
      var(--color-background) 0 calc(50% + var(--grid-content-width) / 2),
      var(--color-background-subtle) 0);
  }
}

/* ==========================================================================
   Reduced Motion
   Keep opacity and colour changes; drop movement and scaling.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .split {
    --handover-blur: 0px;
  }

  .nav-name-full,
  .nav-names:has(a:hover) .nav-name-short {
    filter: none;
  }

  .symbol-button,
  .island-chunk {
    transition: none;
  }

  .island,
  .nav-symbol.is-open .island {
    clip-path: inset(0 0 round var(--radius-md));
    transition: opacity 150ms ease;
  }

  .island-text.is-leaving {
    filter: none;
  }

  body.loading .hero,
  body.loaded .hero,
  body.loading .projects,
  body.loaded .projects,
  body.loading .project:nth-of-type(-n+2),
  body.loaded .project:nth-of-type(-n+2) {
    opacity: 1;
    transform: none;
    filter: none;
    transition: none;
  }

  .preloader video {
    display: none;
  }
}
