/* Route announcer */
	.astro-route-announcer {
		position: absolute;
		left: 0;
		top: 0;
		clip: rect(0 0 0 0);
		clip-path: inset(50%);
		overflow: hidden;
		white-space: nowrap;
		width: 1px;
		height: 1px;
	}:root {
  --color-text: rgba(0, 0, 0, 0.75);
  --color-nav: rgba(0, 0, 0, 0.72);
  --color-meta: rgba(0, 0, 0, 0.45);
  --color-title: rgba(0, 0, 0, 0.85);
  --color-bg: #fff;
  --color-border: rgba(0, 0, 0, 0.08);
  --color-rule: #000;
  --color-accent-info: #002FA7;
  --color-accent-works: #8B0000;
  --color-accent-scoring: #006400;
  --color-accent-log: #4C2A86;
  --color-surface: #f5f5f5;
  --font: 'Instrument Sans', -apple-system, 'Helvetica Neue', sans-serif;
  --nav-width: 18%;
}

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

/* Skip link — hidden until focused via keyboard */
.skip-link {
  position: absolute;
  top: -100%;
  left: 1rem;
  z-index: 999;
  padding: 0.5rem 1rem;
  background: var(--color-title);
  color: var(--color-bg);
  font-size: 0.85rem;
  opacity: 1;
}
.skip-link:focus { top: 0.5rem; }

/* Visually hidden, available to screen readers */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

html {
  /* 13px at the default 16px root, but as a percentage it scales with the
     visitor's browser font-size preference (a fixed px root would ignore it). */
  font-size: 81.25%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* no scroll-behavior: smooth — conflicts with scroll gallery */
}

body {
  font-family: var(--font);
  font-size: 1rem;
  font-weight: 400;
  line-height: 1.45;
  color: var(--color-text);
  background: var(--color-bg);
  min-height: 100vh;
  overflow-x: clip;
}

::selection { background: rgba(0, 0, 0, 0.08); }
img, video { max-width: 100%; height: auto; display: block; }
ul, ol { list-style: none; }

a {
  color: inherit;
  text-decoration: none;
  transition: opacity 0.15s ease;
}
a:hover { opacity: 0.6; }

/* Focus ring — only visible for keyboard navigation */
:focus-visible {
  outline: 2px solid var(--color-title);
  outline-offset: 2px;
}
a:focus { opacity: 1; }

/* -- Layout -- */

.site-wrapper { display: flex; min-height: 100vh; }

.site-nav {
  position: fixed;
  top: 0; left: 0;
  width: var(--nav-width);
  height: 100vh;
  padding: 2rem;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  color: var(--color-nav);
  z-index: 100;
}

.site-main {
  margin-left: var(--nav-width);
  flex: 1;
  /* Shrinkable below content min-width, so wide unbreakable content (long code lines)
     scrolls inside its own overflow container instead of widening the page. */
  min-width: 0;
  padding: 2rem 3rem 3rem 3rem;
}

/* -- Nav -- */

.nav-name {
  font-size: 1.4rem;
  font-weight: 500;
  letter-spacing: 0.06em;
  margin-bottom: 2rem;
  text-transform: uppercase;
}

.nav-links {
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.nav-links a {
  font-size: 1.15rem;
  line-height: 1.3;
  color: var(--color-nav);
}
.nav-links a[aria-current="page"][href*="info"] {
  color: var(--color-accent-info);
  font-weight: 500;
}
.nav-links a[aria-current="page"][href*="scoring"] {
  color: var(--color-accent-scoring);
  font-weight: 500;
}
.nav-links a[aria-current="page"]:not([href*="info"]):not([href*="scoring"]) {
  color: var(--color-accent-works);
  font-weight: 500;
}

/* Works hover dropdown */
.nav-works-hover { position: relative; }
.nav-works-list {
  position: relative;
  padding-top: 0;
  max-height: 0;
  overflow: hidden;
  opacity: 0;
  transition: max-height 0.45s ease, opacity 0.3s ease, padding-top 0.3s ease;
}
.nav-works-list::before {
  content: '';
  position: absolute;
  left: 4px;
  top: 0;
  width: 0.5px;
  height: 0;
  background: currentColor;
  transition: height 0.45s ease;
}
.nav-works-hover:hover .nav-works-list,
.nav-works-hover:focus-within .nav-works-list {
  max-height: 40rem;
  opacity: 1;
  padding-top: 0.4rem;
  transition: max-height 0.75s ease, opacity 0.45s ease, padding-top 0.3s ease;
}
.nav-works-hover:hover .nav-works-list::before,
.nav-works-hover:focus-within .nav-works-list::before {
  height: 100%;
  transition: height 0.75s ease;
}
.nav-works-list a {
  display: block;
  padding-left: 0.75rem;
  font-size: 0.8rem;
  line-height: 1.6;
  color: var(--color-text);
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 0.3s ease, transform 0.3s ease, color 0.15s ease;
  transition-delay: calc(var(--i, 0) * 0.03s + 0.03s);
}
.nav-works-hover:hover .nav-works-list a,
.nav-works-hover:focus-within .nav-works-list a { opacity: 1; transform: translateY(0); }
.nav-works-list a:hover { color: var(--color-accent-works); font-weight: 500; opacity: 1; }


/* -- Mobile bar -- */

.nav-mobile-bar {
  display: none;
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 200;
  padding: 16px 20px;
  background: var(--color-bg);
  align-items: center;
  border-bottom: 1px solid var(--color-border);
}
.mobile-name {
  font-size: 1.4rem;
  font-weight: 400;
  color: var(--color-title);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  /* Yield first if the bar runs out of room so the nav links never clip. */
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mobile-links { display: flex; gap: 24px; margin-left: auto; flex-shrink: 0; }
.mobile-links a {
  font-size: 1.15rem;
  color: var(--color-nav);
}
.mobile-links a[aria-current="page"][href*="info"] {
  color: var(--color-accent-info);
  font-weight: 700;
}
.mobile-links a[aria-current="page"][href*="scoring"] {
  color: var(--color-accent-scoring);
  font-weight: 700;
}
.mobile-links a[aria-current="page"]:not([href*="info"]):not([href*="scoring"]) {
  color: var(--color-accent-works);
  font-weight: 700;
}

/* -- Typography -- */

h1 {
  color: var(--color-title);
  margin-bottom: 0.5rem;
}

h2 {
  font-size: 2rem;
  color: var(--color-title);
  margin-top: 3rem;
  margin-bottom: 1.5rem;
}

p { margin-bottom: 1.5rem; }
p:last-child { margin-bottom: 0; }

blockquote {
  border-left: 1px solid var(--color-border);
  padding-left: 1.5rem;
  font-style: italic;
  margin: 2rem 0;
}

hr {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: 4rem 0;
}

/* -- Works page -- */

.works-page { padding-top: 0; }
.work-list { display: flex; flex-direction: column; gap: 48px; }

/* -- Work card -- */

.work-card { display: block; }
.work-card:hover { opacity: 1; }

.work-card-media {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--color-surface);
  margin-bottom: 12px;
}

.work-card-media img {
  width: 100%; height: 100%;
  object-fit: cover;
}

.work-card-media video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  /* Settles 6px into place as it fades in — the same gesture as the /log hover previews. */
  transform: translateY(6px);
  transition: opacity 0.8s ease, transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.work-card.video-active .work-card-media video,
.work-card.playing .work-card-media video { opacity: 1; transform: translateY(0); }

.work-card-sound-toggle {
  position: absolute; bottom: 12px; right: 12px;
  background: rgba(0, 0, 0, 0.6); color: white; border: none;
  width: 28px; height: 28px; cursor: pointer;
  opacity: 0; transition: opacity 0.2s ease;
  display: flex; align-items: center; justify-content: center;
  border-radius: 4px;
}
.work-card.video-active .work-card-sound-toggle,
.work-card.playing .work-card-sound-toggle { opacity: 1; }
.work-card-sound-toggle .icon-unmuted { display: none; }
.work-card-sound-toggle[data-muted="false"] .icon-muted { display: none; }
.work-card-sound-toggle[data-muted="false"] .icon-unmuted { display: block; }

.work-card-text { display: flex; flex-direction: column; gap: 4px; }

/* Work card titles */
.work-card-title {
  font-size: 1.3rem;
  color: var(--color-title);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.work-card-meta {
  color: var(--color-meta);
  letter-spacing: 0.03em;
}

/* -- Work detail: two-column layout -- */

.work-detail {
  padding-top: 0;
  margin-top: -3rem;
  position: relative;
}

/* Left sidebar: title, medium, year, counter — fixed to bottom-left */
.work-detail-sidebar {
  position: fixed;
  bottom: 2rem;
  left: 2rem;
  width: calc(var(--nav-width) - 2rem);
  z-index: 5;
}

.work-detail-title {
  font-size: 1.6rem;
  font-weight: 500;
  color: var(--color-title);
  margin-bottom: 0.3rem;
  line-height: 1.15;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.work-detail-medium {
  color: var(--color-text);
  margin-bottom: 0.15rem;
}

.work-detail-year {
  color: var(--color-meta);
}

.gallery-counter {
  color: var(--color-meta);
  font-size: 0.85rem;
  margin-top: 0.8rem;
}

.work-score-link {
  display: inline-block;
  color: var(--color-meta);
  font-size: 0.9rem;
  text-align: left;
  text-decoration: underline;
  text-decoration-thickness: 0.5px;
  text-underline-offset: 3px;
}

/* Right content area */
.work-detail-content {
  margin-left: 0;
}

/* Scroll gallery */
.work-scroll-wrap {
  position: relative;
}
.work-scroll-wrap[data-image-count="1"] .work-scroll-gallery {
  justify-content: flex-end;
}
.work-scroll-wrap[data-image-count="1"] .work-scroll-track {
  width: 100%;
}
.work-scroll-wrap[data-image-count="1"] .work-scroll-track img {
  height: auto;
  width: auto;
  max-width: 100%;
  max-height: 92vh;
  margin-left: auto;
}
/* Gallery is a native horizontal scroller by default — the universal baseline for
   Firefox, mobile/touch, and any browser without scroll-driven animation support. */
.work-scroll-gallery {
  position: relative;
  width: 100%;
  max-width: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  display: flex;
  align-items: center;
}
.work-scroll-track {
  display: flex;
  gap: 1rem;
  flex-shrink: 0;
  align-items: center;
}

/* Progressive enhancement: where scroll-driven animation is supported (Chrome/Edge/Safari),
   desktop upgrades to a pinned, vertical-scroll-driven horizontal pan — pure CSS on the
   compositor. A small JS shim sets --gallery-overflow so vertical scroll distance equals
   horizontal travel. (Breakpoint kept in sync with the @media max-width:768px block below.) */
@supports (animation-timeline: scroll()) {
  @media (min-width: 769px) {
    .work-scroll-wrap {
      height: calc(100vh + var(--gallery-overflow, 0px));
      view-timeline: --gallery block;
    }
    .work-scroll-gallery {
      position: sticky;
      top: 0;
      height: 100vh;
      overflow: hidden;
      scroll-snap-type: none;
    }
    .work-scroll-track {
      animation: gallery-pan linear both;
      animation-timeline: --gallery;
      animation-range: contain;
      will-change: transform;
    }
    @keyframes gallery-pan {
      to { transform: translateX(calc(-1 * var(--gallery-overflow, 0px))); }
    }
  }
}
/* Video embed — responsive 16:9 */
.work-video {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  margin-top: 4vh;
  margin-bottom: 1rem;
}
.work-video iframe,
.work-video video {
  position: absolute;
  top: 0; left: 0;
  width: 100%; height: 100%;
  border: 0;
  display: block;
}
.work-video { background: #000; }

.video-facade {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  padding: 0;
  margin: 0;
  background-color: #000;
  background-size: cover;
  background-position: center;
  cursor: pointer;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
}
.video-play {
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0.85;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.video-facade:hover .video-play,
.video-facade:focus-visible .video-play {
  opacity: 1;
  transform: scale(1.06);
}

.work-scroll-track img {
  flex: 0 0 auto;
  height: 92vh;
  width: auto;
  object-fit: contain;
  scroll-snap-align: start;
}

/* Lower section: description + bandcamp + exhibitions */
.work-detail-lower {
  max-width: 100%;
  padding-top: 4rem;
}

.work-detail-body {
  max-width: 50%;
  margin: 0 auto 4rem;
  line-height: 1.55;
  text-align: justify;
}

.work-videos-below {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin-bottom: 2rem;
}

.bandcamp-wrap { margin: 5rem 0; display: block; }
.bandcamp-note {
  max-width: 50%;
  margin: 0 auto 3rem;
  line-height: 1.55;
  text-align: justify;
}
.bandcamp-embed { margin: 0 auto; width: 100%; max-width: 470px; display: block; }
.bandcamp-wrap--horizontal .bandcamp-embed { width: 100%; max-width: 100%; margin: 0; }
.bandcamp-embed iframe { border: 0; width: 100%; display: block; }

/* Exhibition block — rule + text, pushed to bottom */
.work-exhibition-block {
  margin-top: auto;
  padding-top: 2rem;
}
.work-exhibition-rule {
  width: 100%;
  height: 0;
  border-top: 1px solid var(--color-rule);
  margin-bottom: 0.8rem;
}
.work-exhibition-inline {
  font-weight: 400;
  color: var(--color-text);
  font-size: 0.9rem;
}
.work-exhibition-description {
  color: var(--color-meta);
  font-size: 0.85rem;
  line-height: 1.55;
  margin-top: 1rem;
}
.audio-note {
  font-size: 0.8rem;
  margin-top: 0.5rem;
}
.work-detail-content > .audio-player[data-src],
.work-detail-content > .audio-note {
  max-width: 50%;
  margin-left: auto;
  margin-right: auto;
}
.work-detail-content > .audio-note {
  text-align: center;
}
.audio-files-block {
  width: 50%;
  margin: 2rem auto 0;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.placeholder {
  background: var(--color-surface);
  border: 1px dashed var(--color-border);
  padding: 2rem;
  color: var(--color-meta);
  font-style: italic;
  text-align: center;
}

/* -- Info page -- */

.info-page {
  padding-top: 0;
  padding-right: var(--nav-width);
  font-size: 0.95rem;
  line-height: 1.5;
}
.info-page > .info-section {
  margin-bottom: 0;
  padding: 1.5rem 0 2.5rem 0;
}

/* Sticky section headings — h2s are direct children of .info-page
   so sticky works across the whole page (not trapped per-section).
   Negative top hides border-top + padding-top when stuck. */
.info-page > h2 {
  position: sticky;
  top: -1px;
  z-index: 10;
  background: var(--color-bg);
  font-size: 1.4rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--color-title);
  margin: 0;
  padding: 2rem 0;
  border-top: 1px solid var(--color-rule);
  border-bottom: 1px solid var(--color-rule);
}
/* BIO: no top border, align with nav name */
.info-page > h2:first-of-type {
  border-top: none;
  top: 0;
  margin-top: -2rem;
  padding-top: 2rem;
}

/* Bio: two-column — text left, photo right */
.info-bio-section {
  display: flex;
  gap: 3rem;
  align-items: flex-start;
}
.info-bio {
  flex: 1;
  min-width: 0;
}
.info-bio p + p { margin-top: 1.2rem; }
.info-photo {
  flex: 0 0 250px;
  overflow: hidden;
}
.info-photo img {
  width: 100%;
  height: auto;
  display: block;
}

/* CV — three-column aligned entries */
.cv-list {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 0.2rem 1.5rem;
  align-items: baseline;
  width: 100%;
  line-height: 1.7;
}

.cv-entry-dots {
  display: contents;
}
.cv-entry-dots strong {
  font-weight: 500;
}
.cv-entry-dots .cv-dots { display: none; }
.cv-entry-dots .cv-venue {
  font-style: italic;
  color: var(--color-text);
  text-align: right;
}
.cv-entry-dots a, .press-entry a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.15s ease;
}
.cv-entry-dots a:hover, .press-entry a:hover {
  border-bottom-color: var(--color-text);
  opacity: 1;
}

/* Press */
.press-list { display: flex; flex-direction: column; gap: 1rem; }
.press-publication { color: var(--color-meta); margin-top: 2px; font-size: 0.85rem; }

/* Contact — bottom of nav sidebar */
.nav-contact {
  margin-top: auto;
  font-size: 0.92rem;
}
.nav-contact a {
  color: var(--color-nav);
  padding: 0.1rem 0.25rem;
  margin: -0.1rem -0.25rem;
  border-radius: 2px;
  transition: background 0.4s ease;
}
.nav-contact a:hover { opacity: 1; }
.nav-contact > a { display: inline-block; margin-bottom: 0.3rem; }
.nav-contact-links {
  display: flex;
  gap: 0.3rem;
}
.nav-contact-links a { display: inline; }
.nav-contact-links span { color: var(--color-meta); }

/* Reveal class is a no-op — elements visible immediately */

/* -- Footer -- */

.site-footer {
  margin-top: 4rem;
  padding-top: 1rem;
  text-align: right;
  color: var(--color-meta);
  font-size: 0.8rem;
  font-style: italic;
}

/* Info contact section — mobile only */
.info-contact-mobile { display: none; }
.info-contact-mobile a { color: var(--color-nav); }
.info-contact-mobile > a { display: block; margin-bottom: 0.3rem; }
.info-contact-mobile-links { display: flex; gap: 0.3rem; }
.info-contact-mobile-links span { color: var(--color-meta); }

/* -- Scoring page -- */

.scoring-page {
  /* Match the info page's horizontal sizing so the header band, credit
     dividers, and footer rule all span the full content width (the column
     stops short of the right edge by the nav width, mirroring .info-page). */
  padding-right: var(--nav-width);
}

/* Single editorial column: a header band (title + standfirst) above the
   credits list. Its top edge lines up with the "Noah Berrie" wordmark because
   the nav and .site-main share the same 2rem top padding and nothing here adds
   more — the header's first line is the first thing at that 2rem origin, and
   the title inherits the body line-height just as .nav-name does. */
.scoring-layout {
  padding-bottom: 3rem;
}
.scoring-header {
  padding-bottom: 3rem;
  border-bottom: 1px solid var(--color-rule);
}
/* Match the info page's section heading (e.g. "BIO") exactly — same size,
   weight, tracking, and uppercase treatment — so the page header reads as
   consistent with the other pages. */
.scoring-header-title {
  font-size: 1.4rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-title);
  margin: 0 0 0.6rem;
}
.scoring-standfirst {
  margin: 0;
  color: var(--color-text);
  line-height: 1.5;
  text-wrap: pretty;
}
.scoring-standfirst + .scoring-standfirst { margin-top: 0.15rem; }

.scoring-credits {
  display: flex;
  flex-direction: column;
}

/* Divider rhythm: the ::before rule sits at the top of each credit, with 3rem
   of margin above it and 3rem of padding below — centered in a 6rem gap. */
.scoring-credit {
  position: relative;
  margin: 0;
  padding-top: 3rem;
  /* Two columns: credit text on the left, media on the right (mirrors the
     bio's text/photo split). Collapses to a single column on mobile. */
  display: flex;
  gap: 3rem;
  align-items: flex-start;
}
.scoring-credit + .scoring-credit { margin-top: 3rem; }
.scoring-credit:not(:first-child)::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--color-rule);
  pointer-events: none;
}
.scoring-credit-header {
  /* The text carries the most content, so give it real room — it grows with
     the media column rather than being pinned to a cramped 18rem. */
  flex: 1 1 21rem;
  min-width: 0;
}
/* Media column: posters, video, and audio stack on the right. Given a real
   basis so it shrinks alongside the text instead of collapsing when space is
   tight just above the mobile breakpoint. */
.scoring-media {
  flex: 1 1 22rem;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}

.scoring-video {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
}
.scoring-video video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

.scoring-audio {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.scoring-audio .audio-note {
  margin: 0.25rem 0 0;
  color: var(--color-meta);
  font-size: 0.85rem;
  line-height: 1.55;
}

.scoring-platform {
  display: block;
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 500;
  color: var(--color-title);
  margin-bottom: 0.35rem;
}
.scoring-year {
  display: block;
  color: var(--color-meta);
  font-size: 0.85rem;
  line-height: 1.5;
  margin-bottom: 0.5rem;
}
.scoring-title {
  font-size: 1.25rem;
  font-weight: 500;
  color: var(--color-title);
  line-height: 1.25;
  margin: 0 0 0.6rem;
}
.scoring-meta {
  margin: 0 0 0.85rem;
  color: var(--color-text);
  line-height: 1.5;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.scoring-role {
  margin: 0 0 0.85rem;
  color: var(--color-text);
  font-size: 0.9rem;
  line-height: 1.55;
}
.scoring-festival {
  margin: 0;
  color: var(--color-text);
  font-size: 0.9rem;
  line-height: 1.55;
}
.scoring-festival em {
  font-style: italic;
}
.scoring-festival a {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 0.15s ease;
}
.scoring-festival a:hover { border-bottom-color: var(--color-text); }

.scoring-poster img {
  display: block;
  width: 100%;
  height: auto;
}

.scoring-shorts {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}
.scoring-short { margin: 0; }
.scoring-short figcaption {
  margin-top: 0.4rem;
  color: var(--color-meta);
  font-size: 0.85rem;
}

.scoring-placeholder {
  color: var(--color-meta);
  font-style: italic;
  margin: 0;
}

.scoring-footer {
  position: relative;
  margin-top: 4rem;
  padding-top: 1.5rem;
  font-size: 0.9rem;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.scoring-footer::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 1px;
  background: var(--color-rule);
  pointer-events: none;
}
.scoring-updated {
  color: var(--color-meta);
  font-style: italic;
}
.scoring-footer .email-link {
  color: var(--color-text);
  border-bottom: 1px solid transparent;
  transition: border-color 0.15s ease;
}
.scoring-footer .email-link:hover { border-bottom-color: var(--color-text); }

/* -- Responsive -- */

@media (max-width: 768px) {
  .site-nav { display: none; }
  .nav-mobile-bar { display: flex; }
  .site-main { margin-left: 0; padding: 56px 20px 80px; }
  .work-card-media { aspect-ratio: 4 / 3; }
  .work-card-media video,
  .work-card-sound-toggle { display: none; }
  .work-detail-sidebar {
    position: sticky;
    top: 50px;
    bottom: auto;
    left: auto;
    width: calc(100% + 40px);
    margin-left: -20px;
    padding: 1rem 0 0.8rem 20px;
    background: var(--color-bg);
    z-index: 20;
    border-bottom: 1px solid var(--color-rule);
  }
  .work-detail-title { font-size: 1.4rem; }
  .work-detail-body,
  .bandcamp-note { max-width: none; }
  .work-detail-content > .audio-player[data-src],
  .work-detail-content > .audio-note {
    max-width: none;
  }
  .work-detail-content > .audio-player[data-src] {
    margin-top: 2rem;
  }
  .gallery-counter { position: absolute; top: 0.5rem; right: 20px; margin-top: 0; }
  .work-detail { margin-top: 0; }
  .info-page > h2 { top: calc(50px - 1px); padding: 16px 20px; margin-left: -20px; margin-right: -20px; }
  .info-page > h2:first-of-type { top: 50px; margin-top: -16px; padding-top: 16px; }
  .info-page { font-size: 1rem; padding-right: 0; }
  .info-bio-section { flex-direction: column-reverse; gap: 1.5rem; padding-top: 0.5rem; }
  .info-page > .info-section { padding: 1.8rem 0 2.2rem 0; }
  .info-photo { flex: none; width: 100%; }
  /* Mobile: collapse grid to stacked */
  .cv-list { display: block; }
  .cv-entry-dots { display: block; margin-bottom: 0.5rem; line-height: 1.5; }
  .cv-entry-dots .cv-year::after { content: ' — '; }
  .cv-entry-dots .cv-title::after { content: ' | '; color: var(--color-meta); font-style: normal; }
  .cv-entry-dots .cv-venue { text-align: left; }
  .info-contact-mobile { display: block; }
  /* Mobile: native horizontal scroll comes from the base rule; just edge-bleed + inset. */
  .work-scroll-wrap { margin-left: -20px; margin-right: -20px; }
  .work-scroll-gallery {
    padding-left: 20px;
    scroll-padding-left: 20px;
  }
  .work-scroll-track img {
    height: 75vh;
  }
  /* Scroll hint: small arrow indicator */
  .work-scroll-gallery::after {
    content: '→';
    position: absolute;
    right: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: rgba(0, 0, 0, 0.25);
    font-size: 1.5rem;
    pointer-events: none;
    animation: hint-fade 2s ease 1s forwards;
  }
  @keyframes hint-fade { to { opacity: 0; } }
  /* Scoring page mobile — full-width single column. Reset align-items back
     to stretch (the desktop row uses flex-start) so the stacked media boxes
     fill the width — without it, the aspect-ratio video boxes collapse to
     zero width. flex:none drops the desktop basis so heights stay natural. */
  .scoring-page { padding-right: 0; }
  .scoring-credit { flex-direction: column; gap: 1.5rem; align-items: stretch; }
  .scoring-credit-header,
  .scoring-media { flex: none; }
  .scoring-credit:first-child { padding-top: 2rem; }
}

@media (max-width: 480px) {
  .site-main { padding-left: 16px; padding-right: 16px; }
  .info-page > h2 { margin-left: -16px; margin-right: -16px; padding-left: 16px; padding-right: 16px; }
  .nav-mobile-bar { padding: 14px 16px; }
  .mobile-name { font-size: 1.25rem; }
  .mobile-links { gap: 16px; }
  .mobile-links a { font-size: 1.05rem; }
}

@media print {
  .site-nav, .nav-mobile-bar, .work-card-sound-toggle { display: none; }
  .site-main { margin-left: 0; padding: 0; }
  body { color: #000; background: #fff; }
}

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

/* ============================================================
   /log — engineering portfolio. Additive; reuses existing tokens
   and the scoring page's vertical rhythm. /log is intentionally not
   in nav, so there are no nav-accent selectors — the accent token is
   for in-page links, tags, and footer only.
   ============================================================ */

/* Listing */
.log-header {
  position: relative;
  padding: 0 1.25rem 1rem;
  margin-bottom: 0;
}
.log-header-title {
  font-size: 1.4rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--color-title);
  margin: 0 0 0.75rem;
}
.log-standfirst { max-width: 38rem; color: var(--color-text); line-height: 1.55; }

/* Index: a two-pane master–detail. The left column is a menu of studies separated
   by the same dark rule as the rest of the page furniture; the right column is the
   viewer — a hover-preview image by default, or the full study once one is clicked.
   A dark vertical divider runs between them, and each column scrolls on its own so
   the menu stays in place while the article scrolls. */
/* Page wrapper — sizing lives on .log-layout; this groups the layout (or the
   empty-state placeholder) and spans the content width. */
/* --sqh (≈ box height) drives --sqw, a hair wider so the boxes read as square. Defined here (not
   on .log-layout) so the LOG aside can size itself to match a study box. 3 rows of studies fill
   100vh − 4rem (the site-main 2rem top padding + a matching 2rem bottom). */
.log-page {
  width: 100%;
  --sqh: max(7rem, calc((100vh - 4rem) / 3));
  --sqw: calc(var(--sqh) * 1.05);
  /* Pen-draw intro timing. Each box starts at its own --start (evenly spaced + shuffled per load in
     index.astro); within a box the four sides step by --side-gap and each is drawn over --draw-dur,
     so the square closes (3 * --side-gap + --draw-dur) after the box's --start — short enough that
     the next box has usually begun, i.e. the boxes dovetail. After a side is drawn its red tip dries
     to black over --dry-dur. The animations use the plain `ease` curve. */
  --side-gap: 0.06s;   /* delay between a box's four sides */
  --draw-dur: 0.16s;   /* how long each side's line takes to draw out */
  --dry-dur: 0.13s;    /* how long the red tip takes to dry to black once a side is drawn */
  --text-dur: 0.35s;   /* how long a box's text takes to fade in */
  --tip-len: 16px;     /* length of the red "wet ink" tip trailing the pen */
  --ink: var(--color-rule);        /* the dried line colour (black) */
  --tip: hsl(0 100% 42%);          /* default pen-tip red (LOG box); studies override per row */
}
.log-layout {
  position: relative;
  display: grid;
  grid-template-columns: calc(var(--sqw) * 2) minmax(0, 1fr);
  height: calc(100vh - 4rem);
}
/* /log gets a bottom cushion equal to its 2rem top one, so the grid is inset symmetrically. */
.site-main:has(.log-page) { padding-bottom: 2rem; }
.log-main {
  min-width: 0;
  height: 100%;
  overflow-y: auto;
  scrollbar-width: none;            /* Firefox */
  /* No right padding: the separator rules run the full width and meet the divider.
     Text is held off the divider by the inner padding on the header + each row. */
}
/* The menu frame is drawn on an inner wrapper that's only as tall as its content, so the
   left edge line and the divider stop at the last box: a short, filtered list no longer
   trails the lines down an empty viewport, and a long list scrolls them with its rows.
   Hairlines scaled to a true 1 device pixel; the row rules run into them so every study is
   boxed. */
.log-main-inner { position: relative; }
.log-main-inner::before,
.log-main-inner::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  background: var(--color-rule);
  transform: scaleX(0.5);
  pointer-events: none;
  z-index: 1;
}
.log-main-inner::before { left: 0; transform-origin: left center; }    /* left edge of the menu */
.log-main-inner::after  { right: 0; transform-origin: right center; }  /* divider on the column boundary */
.log-detail-col {
  position: relative;
  min-width: 0;
  height: 100%;
  overflow-y: auto;
  padding: 0 0.5rem 3rem 2.5rem;
  scrollbar-width: none;            /* Firefox */
}
/* WebKit/Blink: hide the bars too — both panes still scroll (wheel/trackpad/keys),
   the bars just don't clutter the boxed menu or the article viewer. */
.log-main::-webkit-scrollbar,
.log-detail-col::-webkit-scrollbar { display: none; }

/* Hover stage — overlays the viewer and holds the crossfading preview images;
   hidden the instant a study is opened. No shadow: the image sits flat. */
.log-stage {
  position: absolute;
  inset: 0;
  pointer-events: none;
  transition: opacity 0.45s ease;
}
/* Fade (don't snap) the hover image out as the clicked study's text drops in over it. */
.log-detail-col.has-open .log-stage { opacity: 0; }
.log-stage-img {
  position: absolute;
  top: 50%;
  left: 50%;
  max-width: 88%;
  max-height: 70vh;
  width: auto;
  height: auto;
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  opacity: 0;
  /* Rests 6px low while hidden so the fade-in reads as a print settling onto the desk
     (and the outgoing image lifts away as it loses .is-active). */
  transform: translate(-50%, calc(-50% + 6px)) rotate(var(--rot, 0deg));
  transition: opacity 0.4s ease, transform 0.5s cubic-bezier(0.22, 0.61, 0.36, 1);
  pointer-events: none;
}
.log-stage-img.is-active { opacity: 1; transform: translate(-50%, -50%) rotate(var(--rot, 0deg)); }

/* Inline study panels — pre-rendered, one shown at a time (desktop only). A study fades IN on open
   exactly the way it fades OUT on close: one opacity transition, no per-block movement. */
.log-panel { display: none; opacity: 0; pointer-events: none; transition: opacity 0.6s ease; }
.log-panel.is-open { display: block; }              /* in the DOM, still transparent */
.log-panel.is-shown { opacity: 1; pointer-events: auto; }   /* faded in */
.log-panel:focus { outline: none; }
@media (prefers-reduced-motion: reduce) {
  .log-panel { transition: none; }
}
/* Inline, the study fills the viewer column left-aligned (not the centered 74rem reading
   cap the standalone page uses): the body text uses the full width and, with a fixed left
   edge, the header/footer rules line up with the divider. --log-wide:100% drives the
   article max-width, the running-text measure, and the media width all at once. */
.log-detail.log-panel {
  --log-wide: 100%;
  margin-inline: 0;
}
.log-panel-close {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font: inherit;
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-meta);
  transition: color 0.15s ease;
}
.log-panel-close:hover { color: var(--color-accent-log); }

@media (max-width: 900px), (hover: none) {
  /* Single column of boxes. Tapping a box opens the standalone /log/<slug>/ page — the inline
     two-pane viewer is desktop-only. A 1px gap over a rule-coloured backdrop draws the box
     separators, same trick as the desktop grid. */
  .log-layout { display: block; height: auto; }
  .log-main { height: auto; overflow: visible; }
  .log-detail-col { display: none; }
  .log-main-inner::before, .log-main-inner::after { display: none; }  /* no two-pane frame lines */
  .log-list {
    display: grid;
    grid-template-columns: 1fr;
    gap: 1px;
    background: var(--color-rule);
    border: 1px solid var(--color-rule);
  }
  .log-card { background: var(--color-bg); }
  .log-card::after { display: none; }
  .log-cell-empty { display: none; }   /* no blank cell in the single-column list */
}

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

/* LOG label box. Default (mobile): a plain block sitting above the studies grid. */
.log-aside .log-title-box { padding: 1.5rem 1.25rem; }

/* On /log the LOG box lives in the nav column, where the Works hover-accordion would open right on
   top of it — disable that dropdown on this page (the Works link itself still navigates). */
.site-wrapper:has(.log-page) .nav-works-list { display: none; }

/* Pen-draw intro (desktop). Each box is traced as one continuous square — top (L→R), right (T→B),
   bottom (R→L), left (B→T) — and its text fades in the instant that square closes. The boxes draw in
   a sequence shuffled per load (each box's --start is dealt out by a script in index.astro) and they
   dovetail. Each side is one 1px segment whose colour is a moving gradient: a red "wet ink" tip at
   the leading edge (--tip, the box's row red), dried to black (--ink) just behind it, transparent
   ahead (not drawn yet). --draw (0→1, animated) is the pen position; once the side is fully drawn,
   --tipc fades the tip from red to black ("the ink dries"). Timing/easing tokens live on .log-page. */
@keyframes log-fade-in { from { opacity: 0; } to { opacity: 1; } }
/* --draw is the normalised pen position along a side; --tipc is the live tip colour. Both are
   registered so they interpolate smoothly inside the gradient (plain custom props can't animate). */
@property --draw { syntax: "<number>"; inherits: false; initial-value: 0; }
@property --tipc { syntax: "<color>"; inherits: false; initial-value: #000; }
/* 1px line segments; each side's top/left/width/height is set in the markup to its box edge. Hidden
   on mobile (the single-column list draws its own separators); shown + drawn on desktop. The gradient
   runs along the side in its draw direction (--dir): black behind the tip, red AT the tip, then
   transparent. var(--draw,1) so a browser without @property support just shows the finished line. */
.log-draw { display: none; position: absolute; inset: 0; overflow: visible; pointer-events: none; }
.ld-h, .ld-v {
  position: absolute;
  background: linear-gradient(var(--dir, to right),
    var(--ink) 0,
    var(--ink) calc(var(--draw, 1) * 100% - var(--tip-len)),
    var(--tipc, var(--ink)) calc(var(--draw, 1) * 100%),
    transparent calc(var(--draw, 1) * 100%));
  background-repeat: no-repeat;
}
.ld-h { height: 1px; }
.ld-v { width: 1px; }
.ld-t { --dir: to right; }    /* top edge → draws left to right */
.ld-r { --dir: to bottom; }   /* right edge → top to bottom */
.ld-b { --dir: to left; }     /* bottom edge → right to left */
.ld-l { --dir: to top; }      /* left edge → bottom to top */
@keyframes ld-draw { from { --draw: 0; } to { --draw: 1; } }
@keyframes ld-dry  { from { --tipc: var(--tip); } to { --tipc: var(--ink); } }

@media (hover: hover) and (min-width: 901px) {
  /* LOG label: fixed in the nav column. Its LEFT edge sits at the nav's text margin (2rem) so
     "Noah Berrie / Info / Scoring / Works" and the LOG box share one left line; it runs right to
     meet the grid's left edge, flush against Suspension. Same height as a study box; --aside-row
     (Suspension's row, set in index.astro) keeps it level. */
  .log-aside {
    position: fixed;
    top: calc(2rem + var(--aside-row, 1) * var(--sqh));
    left: 2rem;
    width: calc(var(--nav-width) + 1rem);
    height: var(--sqh);
    /* Above the nav (z 100) so the LOG box can take the replay click — the full-height
       nav otherwise swallows pointer events over this strip. Safe on /log: the box never
       overlaps the nav's links and the works dropdown is disabled here (see
       .site-wrapper:has(.log-page) .nav-works-list). */
    z-index: 101;
  }
  .log-aside .log-title-box {
    height: 100%;
    box-sizing: border-box;
    padding: 1rem 1.1rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    text-align: center;
    /* Clicking the box replays the pen-draw (see index.astro); keep a double-click
       from selecting the label instead. */
    user-select: none;
    -webkit-user-select: none;
  }
  .log-aside .log-header-title { margin: 0 0 0.5rem; }
  .log-aside .log-standfirst { max-width: none; font-size: 0.92rem; line-height: 1.5; }

  .log-draw { display: block; }
  /* Each side draws then dries. Both run off the same per-side start = the box's --start plus the
     side's place within the box (--side * --side-gap): ld-draw moves the pen (--draw 0→1) with the
     stroke easing, then ld-dry (delayed by --draw-dur, so it begins as the side finishes) fades the
     tip from red to black. The red tip travels with the leading edge, so behind it the line is
     already black — only the very tip is wet, and it dries once the pen moves on / the side ends. */
  .ld-h, .ld-v {
    animation: ld-draw var(--draw-dur) ease both,
               ld-dry  var(--dry-dur)  ease both;
    animation-delay: calc(var(--start, 0s) + var(--side, 0) * var(--side-gap)),
                     calc(var(--start, 0s) + var(--side, 0) * var(--side-gap) + var(--draw-dur));
  }
  /* Each box's text fades in the instant its square closes — i.e. after its 4th side is drawn. Both
     the LOG label and the study cards inherit --start from their box, so they share one formula. */
  .log-aside .log-title-box,
  .log-card-link { opacity: 0; animation: log-fade-in var(--text-dur) ease both;
    animation-delay: calc(var(--start, 0s) + 3 * var(--side-gap) + var(--draw-dur)); }
}
@media (prefers-reduced-motion: reduce) {
  /* No drawing/drying: show the finished black grid and text outright. */
  .ld-h, .ld-v { background: var(--color-rule) !important; }
  .ld-h, .ld-v, .log-aside .log-title-box, .log-card-link {
    animation: none !important;
    opacity: 1 !important;
  }
}
/* Row + header separators: full-width hairlines that run to the divider. A 1px bar
   scaled to 0.5 renders a true 1-device-pixel line on hi-DPR (a plain 0.5px border
   gets rounded back up to 1px by Chrome). */
.log-card { position: relative; background-color: var(--color-bg); }
.log-card-link { display: block; padding: 1.5rem 1.25rem; text-decoration: none; color: inherit; }
/* Uniform square boxes (desktop two-pane only): each card is a 1:1 box the width of the menu
   column, its content vertically centred. On touch / single-column the cards keep natural height. */
@media (hover: hover) and (min-width: 901px) {
  /* Two-column grid of boxes: the LOG title is the first box, then the 5 studies — 2 columns ×
     3 rows that fill the menu height (1fr rows ⇒ flush to the bottom). The 1px gap over a
     rule-coloured backdrop draws the grid; boxes are a hair wider than tall so they read square. */
  .log-main-inner { height: 100%; }
  .log-main-inner::before, .log-main-inner::after { display: none; }  /* the grid lines replace the frame lines */
  /* Grid of boxes filling the menu height. Rows are an explicit --sqh (not 1fr) so the fixed LOG
     box, sized the same, lines up with them exactly. The lines come from the pen-draw overlay. */
  .log-list {
    height: 100%;
    box-sizing: border-box;
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    grid-auto-rows: var(--sqh);
    background: var(--color-bg);
  }
  .log-card { background: var(--color-bg); box-sizing: border-box; }
  .log-card::after { display: none; }
  /* Boxes stay bare — the pen-draw overlay (.log-draw) supplies all the grid lines. */
  .log-card-link {
    height: 100%;
    padding: 1rem 1.1rem;
    display: flex;
    flex-direction: column;
    justify-content: center;
    text-align: center;
    overflow: hidden;
  }
}
/* Date line — a clear "Month YYYY" under each title. */
.log-card-date {
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: #000;
  margin: 0.3rem 0 0;
}
.log-card-title {
  font-size: 1.25rem;
  font-weight: 500;
  color: #000;
  margin: 0;
}
/* Tools used — a middot-separated line of the build stack under each title. */
.log-card-tools {
  margin-top: 0.5rem;
  font-size: 0.72rem;
  line-height: 1.55;
  letter-spacing: 0.02em;
  color: #000;
}

/* Desktop only. Each box's hover fill is a fixed shade of the works red (--hover-accent,
   set inline per row in index.astro) so the menu reads top-to-bottom as one light→dark red
   gradient. The fill snaps ON instantly but FADES OUT on leave — so a swept cursor leaves a
   fading red trail behind it. The frame lines (above) clip the fill. */
@media (hover: hover) and (min-width: 901px) {
  /* Asymmetric transition for the trail: the fade lives on the resting state, while :hover
     sets transition:none so entering a box is instant. (Global prefers-reduced-motion above
     zeroes the duration.) */
  .log-card { transition: background-color 0.45s ease; }
  .log-card-title,
  .log-card-tools,
  .log-card-date { transition: color 0.45s ease; }
  .log-card:hover,
  .log-card:hover .log-card-title,
  .log-card:hover .log-card-tools,
  .log-card:hover .log-card-date { transition: none; }
  /* Selected box: stays filled in the shade it was opened with, white text — same as hover. */
  .log-card.is-active { background-color: var(--active-accent, var(--color-accent-works)); }
  .log-card.is-active .log-card-title,
  .log-card.is-active .log-card-tools,
  .log-card.is-active .log-card-date { color: #fff; }
  /* Hover: fill the box with crisp white text. Listed last so white wins over the frozen
     colour while hovering; opacity:1 overrides the global a:hover dimming. */
  .log-card:hover { background-color: var(--hover-accent, var(--color-accent-works)); }
  .log-card-link:hover { opacity: 1; }
  .log-card:hover .log-card-title,
  .log-card:hover .log-card-tools,
  .log-card:hover .log-card-date { color: #fff; }
}

/* Chips: article tags + footer stack, shared look (the menu uses .log-card-tools) */
.log-detail-tags, .log-stack {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  font-size: 0.72rem;
  letter-spacing: 0.03em;
  color: #000;
}
.log-detail-tags li, .log-stack li {
  border: 1px solid #000;
  padding: 0.15rem 0.5rem;
  border-radius: 2px;
}

/* Detail */
/* Fluid editorial layout: a wide, centered article where media (hero, figures, video)
   breaks out to the full width while running text is held to a readable, centered measure.
   --log-measure = reading column · --log-wide = full media/article width. Both adapt to vw. */
.log-detail {
  --log-measure: var(--log-wide);  /* text fills the full width, level with the widest image */
  --log-wide: 74rem;
  max-width: var(--log-wide);
  margin-inline: auto;
}
/* Header shares the reading column so the title + rule align with the body text */
.log-detail-header {
  position: relative;
  max-width: var(--log-measure);
  margin: 0 auto 2.5rem;
  padding-bottom: 1.5rem;
}
/* Rule under the header — black scaled hairline, matching the footer rule and the menu
   boxes; in the inline viewer it bleeds left to meet the divider and the box grid. */
.log-detail-header::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--color-rule);
  transform: scaleY(0.5);
  transform-origin: bottom;
  pointer-events: none;
}
.log-panel .log-detail-header::after { left: -2.5rem; }
.log-detail-eyebrow { margin: 0 0 1.25rem; font-size: 0.8rem; letter-spacing: 0.04em; text-transform: uppercase; }
.log-detail-eyebrow a { color: var(--color-meta); text-decoration: none; }
.log-detail-eyebrow a:hover { color: var(--color-accent-log); }
.log-detail-title { font-size: 2rem; font-weight: 500; line-height: 1.15; color: var(--color-title); margin: 0; }
.log-detail-subtitle { margin: 0.55rem 0 0; font-size: 1.12rem; line-height: 1.3; color: #000; font-weight: 400; max-width: 40rem; }
.log-detail-demo { margin: 0.55rem 0 0; font-size: 0.95rem; }
.log-detail-demo a { color: var(--color-accent-log); text-decoration: none; border-bottom: 1px solid var(--color-border); }
.log-detail-demo a:hover { border-bottom-color: var(--color-accent-log); }
.log-detail-updated { color: #000; font-size: 0.82rem; margin: 0.5rem 0 0; }
.log-detail-tags { margin-top: 1rem; }

/* Hero — full article width, centered; cover + height cap keeps tall heroes banner-like */
.log-hero { margin: 0 auto 2.5rem; }
.log-hero img { width: 100%; max-height: 78vh; object-fit: cover; display: block; border: 1px solid var(--color-border); }
/* UI-screenshot heroes (heroFit: "contain"): size to the image's own aspect, capped by
   the same max-height and centered, so the whole frame shows instead of a cover-cropped band. */
.log-hero.log-hero--contain img { width: auto; max-width: 100%; object-fit: contain; margin-inline: auto; }

/* Body spans the full article; running text below is re-constrained to the reading measure */
.log-detail-body { font-size: 1.08rem; line-height: 1.62; color: var(--color-text); }
.log-detail-body > :first-child { margin-top: 0; }
.log-detail-body p { margin: 0 0 1.4rem; }
.log-detail-body :is(h2, h3) { color: var(--color-title); margin: 2.75rem 0 1rem; }
.log-detail-body h2 { font-size: 1.4rem; }
.log-detail-body h3 { font-size: 1.1rem; }
.log-detail-body a { color: var(--color-accent-log); border-bottom: 1px solid var(--color-border); text-decoration: none; }
.log-detail-body a:hover { border-bottom-color: var(--color-accent-log); }
.log-detail-body ul, .log-detail-body ol { margin: 0 0 1.4rem 1.25rem; }
.log-detail-body li { margin: 0.3rem 0; }
.log-detail-body strong { color: var(--color-title); }

/* Inline + block code (Shiki colors fenced blocks; this is the chrome only) */
.log-detail-body :is(pre, code, kbd, samp) {
  font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace;
  font-size: 0.85em;
}
.log-detail-body code { background: var(--color-surface); padding: 0.1em 0.35em; border-radius: 2px; }
.log-detail-body pre {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  padding: 1rem 1.25rem;
  overflow-x: auto;
  line-height: 1.5;
  margin: 1.75rem 0;
  border-radius: 2px;
}
.log-detail-body pre code { background: none; padding: 0; }

/* Figures — break out past the text column and center. Top-level figures shrink-wrap their
   image, which fills the article width but never upscales past native size; portrait/tall
   shots are clamped by height so they don't dominate. Captions track the image width. */
.log-detail-body figure { margin: 2.5rem auto; }
.log-detail-body figure img { width: 100%; height: auto; display: block; border: 1px solid var(--color-border); }
.log-detail-body figcaption { margin-top: 0.5rem; color: var(--color-meta); font-size: 0.82rem; line-height: 1.5; }

/* Phone-screenshot pairs — portrait app captures shown side by side, centered at device scale */
.log-detail-body .phone-row { display: flex; gap: 1rem; flex-wrap: wrap; justify-content: center; max-width: 46rem; margin: 2.5rem auto; }
.log-detail-body .phone-row figure { flex: 1 1 220px; max-width: 22rem; margin: 0; }

/* Contact-sheet grid — a set of small related photos in roughly one photo's footprint */
.log-detail-body .log-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: 0.75rem;
  margin: 2.5rem auto;
  align-items: start;
}
.log-detail-body .log-grid figure { margin: 0; width: 100%; }
.log-detail-body .log-grid img { aspect-ratio: 1 / 1; object-fit: cover; }
.log-detail-body .log-grid figcaption { margin-top: 0.4rem; font-size: 0.72rem; }

/* Five-up contact sheet as two-over-three; both rows span the full width */
.log-detail-body .log-grid-2-3 { grid-template-columns: repeat(6, 1fr); }
.log-detail-body .log-grid-2-3 > figure:nth-child(-n+2) { grid-column: span 3; }
.log-detail-body .log-grid-2-3 > figure:nth-child(n+3) { grid-column: span 2; }

/* Phone-screenshot showcase — a tidy wall of app screens at device scale */
.log-detail-body .log-gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem 1.25rem;
  max-width: 56rem;
  margin: 2.5rem auto;
}
.log-detail-body .log-gallery figure { margin: 0; }
.log-detail-body .log-gallery figcaption { font-size: 0.72rem; }

/* Two-up row — equal columns so a comparison pair shares its edges (stays aligned) */
.log-detail-body .log-duo {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.25rem;
  margin: 2.5rem auto;
  align-items: start;
}
.log-detail-body .log-duo figure { margin: 0; width: 100%; }

/* A single figure held narrower than full width, centered (e.g. a lone near-square plot) */
.log-detail-body .log-narrow { max-width: 40rem; margin-inline: auto; }

/* Every article image is click-to-zoom (see the lightbox script in the route) */
.log-detail-body figure img, .log-hero img { cursor: zoom-in; }

/* Faux browser-window framing — opt-in via the study's `browserFrame` flag.
   The real browser chrome (tabs, URL, bookmarks) was cropped out of the source
   screenshots; this is the clean stand-in, drawn in CSS so it stays crisp at any
   size and theme-aware. Used by the TutorDash study so every app shot — and the
   auto-scrolling landing-page hero — reads as a live product window. */
.log-hero--browser .browser {
  border: 1px solid var(--color-border);
  border-radius: 12px;
  overflow: hidden;
  background: var(--color-surface);
  box-shadow: 0 20px 55px rgba(0, 0, 0, 0.16);
}
.log-hero--browser .browser-bar {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  height: 40px;
  padding: 0 14px;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}
.browser-dots {
  flex: none;
  width: 46px;
  height: 12px;
  background:
    radial-gradient(circle at 6px 6px, #ff5f57 5px, transparent 5.5px),
    radial-gradient(circle at 23px 6px, #febc2e 5px, transparent 5.5px),
    radial-gradient(circle at 40px 6px, #28c840 5px, transparent 5.5px);
}
.log-hero--browser .browser-url {
  font-size: 0.8rem;
  color: var(--color-meta);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: 6px;
  padding: 0.16rem 0.7rem;
  line-height: 1.5;
  max-width: 60%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.log-hero--browser .browser-shot {
  display: block;
  width: 100%;
  height: auto;
  background: #0b0b0c;
}
video.browser-shot { aspect-ratio: 336 / 185; }

/* Every screenshot in a framed study gets its own little window. The chrome bar
   sits in the figure's top padding (::before) with traffic-light dots (::after);
   the image keeps its border but rounds only its bottom corners, so bar + image
   read as one window. The figcaption stays outside, below the frame. */
.log-detail-body--framed figure { position: relative; padding-top: 30px; }
.log-detail-body--framed figure::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 30px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-bottom: none;
  border-radius: 8px 8px 0 0;
}
.log-detail-body--framed figure::after {
  content: "";
  position: absolute;
  top: 11px;
  left: 12px;
  width: 38px;
  height: 8px;
  background:
    radial-gradient(circle at 4px 4px, #ff5f57 3.5px, transparent 4px),
    radial-gradient(circle at 19px 4px, #febc2e 3.5px, transparent 4px),
    radial-gradient(circle at 34px 4px, #28c840 3.5px, transparent 4px);
}
.log-detail-body--framed figure img {
  border: 1px solid var(--color-border);
  border-radius: 0 0 8px 8px;
}

/* Enlarged images use a native modal with keyboard and touch dismissal. */
.log-lightbox {
  margin: 0;
  border: 0;
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  overflow: auto;
  overscroll-behavior: contain;
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: none;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.9rem;
  padding: 4vmin;
  background: rgba(12, 12, 14, 0.85);
  cursor: zoom-out;
  opacity: 0;
  transition: opacity 0.18s ease;
}
.log-lightbox[open] { display: flex; }
.log-lightbox::backdrop { background: transparent; }
.log-lightbox.is-open { opacity: 1; }
/* A small visible cross inside a comfortable, transparent touch target. */
.log-lightbox-close {
  position: fixed;
  top: max(12px, env(safe-area-inset-top));
  right: max(12px, env(safe-area-inset-right));
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 14px;
  border: 0;
  background: transparent;
  color: var(--sidebar-ink, #fff);
  opacity: .65;
  cursor: pointer;
}
.log-lightbox-close:hover, .log-lightbox-close:focus-visible { opacity: 1; }
.log-lightbox-close:focus-visible { outline: 1px solid currentColor; outline-offset: 1px; }
.log-lightbox-close svg { display: block; width: 16px; height: 16px; }
[data-lightbox-trigger] { cursor: zoom-in; }
.log-lightbox img {
  max-width: min(100%, 72rem);
  max-height: 88vh;
  width: auto;
  height: auto;
  border: 1px solid var(--color-border);
  box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.55);
}
/* Framed studies enlarge the whole browser window (chrome bar + shot). */
.log-lightbox .lightbox-browser {
  display: inline-flex;
  flex-direction: column;
  max-width: min(96vw, 80rem);
  max-height: 90vh;
  border-radius: 12px;
  overflow: hidden;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  box-shadow: 0 1.5rem 4rem rgba(0, 0, 0, 0.55);
}
.log-lightbox .lightbox-browser-bar {
  flex: none;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  height: 42px;
  padding: 0 16px;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}
.log-lightbox .lightbox-browser img {
  display: block;
  max-width: min(96vw, 80rem);
  max-height: calc(90vh - 42px);
  width: auto;
  height: auto;
  border: none;
  box-shadow: none;
}
.log-lightbox-cap {
  margin: 0;
  max-width: 60ch;
  color: rgba(255, 255, 255, 0.82);
  font-size: 0.82rem;
  line-height: 1.5;
  text-align: center;
}
@media (prefers-reduced-motion: reduce) { .log-lightbox { transition: none; } }

/* Placeholder slot for photos Noah will supply */
.log-detail-body .photo-slot {
  max-width: var(--log-measure);
  border: 1px dashed var(--color-border);
  background: var(--color-surface);
  padding: 2.5rem 1.5rem;
  text-align: center;
  color: var(--color-meta);
  font-size: 0.85rem;
  margin: 2rem auto 0.5rem;
}

/* Hold running text to the reading measure, centered in the wide body. Placed after the
   per-element margin rules above so the inline centering wins on source order. Media
   (figures, .phone-row, .log-split) is deliberately excluded so it can break out wider. */
.log-detail-body > :is(p, h2, h3, h4, ul, ol, blockquote, pre) {
  max-width: var(--log-measure);
  margin-inline: auto;
}

/* Scoring-style two-column split for artifact-heavy sections (raw HTML in markdown) */
.log-split { display: flex; gap: 3rem; align-items: flex-start; margin: 2.5rem 0; }
.log-split-text { flex: 1 1 21rem; min-width: 0; }
.log-split-media { flex: 1 1 22rem; min-width: 0; display: flex; flex-direction: column; gap: 1.25rem; }
.log-split-media figure { margin: 0; }

/* Frontmatter media blocks below the body */
.log-audio-block { margin: 2.5rem auto 0; display: flex; flex-direction: column; gap: 1rem; max-width: var(--log-measure); }
.log-audio-entry { display: flex; flex-direction: column; gap: 0.5rem; }  /* player above its optional note */
.log-videos-below { margin: 1.5rem auto 0; display: flex; flex-direction: column; gap: 1.5rem; max-width: var(--log-wide); }

/* Footer */
.log-detail-footer {
  position: relative;
  max-width: var(--log-measure);
  margin: 3.5rem auto 0;
  padding-top: 1.75rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
/* The rule above the footer: a scaled hairline matching the menu's box rules (a plain
   1px border renders 2 device-px on hi-DPR — twice as thick). */
.log-detail-footer::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--color-rule);
  transform: scaleY(0.5);
  transform-origin: top;
  pointer-events: none;
}
/* Inline viewer: the article is inset from the divider by the column's left padding
   (2.5rem). Pull the rule back so it meets the divider and intersects the menu boxes. */
.log-panel .log-detail-footer::before { left: -2.5rem; }
.log-footer-section { display: flex; flex-direction: column; gap: 0.6rem; }
.log-footer-label { font-size: 0.72rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--color-meta); }
.log-footer-links { display: flex; gap: 1.5rem; }
.log-footer-links a { color: var(--color-accent-log); text-decoration: none; font-size: 0.9rem; }
.log-footer-links a:hover { text-decoration: underline; }

@media (max-width: 768px) {
  .log-split { flex-direction: column; gap: 1.5rem; }
  .log-duo { grid-template-columns: 1fr; }
  .log-gallery { grid-template-columns: repeat(2, 1fr); }
  .log-detail-title { font-size: 1.6rem; }
  .log-detail-body figure { margin: 1.75rem auto; }
  .log-hero img { max-height: 62vh; }
}

/* -- Mini player -- */
/* A small square, bottom right, that carries the playing piece when its inline player
   isn't on screen (scrolled away, or on another page — audio survives navigation).
   Ink only: a 1px black line around the box (same weight as the site's other lines),
   ✕ to dismiss top right, play/pause centered, the title (a link back to the piece's
   page) left-aligned above the bar waveform, duration in the bottom-right corner. */
.mini-player {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 150;
  width: 11rem;
  aspect-ratio: 1 / 1;
  padding: 0.85rem 0.95rem;
  background: var(--color-bg);
  border: 1px solid var(--color-rule);
  display: flex;
  flex-direction: column;
  opacity: 0;
  transform: translateY(6px);
  pointer-events: none;
  transition: opacity 0.35s ease, transform 0.45s cubic-bezier(0.22, 0.61, 0.36, 1);
}
.mini-player.is-shown { opacity: 1; transform: translateY(0); pointer-events: auto; }
.mini-player-close {
  position: absolute;
  top: 0.3rem;
  right: 0.45rem;
  background: none;
  border: none;
  padding: 0.25rem;
  cursor: pointer;
  font-size: 0.7rem;
  line-height: 1;
  color: #000;
  transition: opacity 0.15s ease;
}
.mini-player-close:hover { opacity: 0.6; }
.mini-player-toggle {
  margin: auto;
  flex-shrink: 0;
  width: 30px;
  height: 30px;
  border: 1px solid rgba(0, 0, 0, 0.15);
  border-radius: 50%;
  background: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  color: #000;
  padding: 0;
  transition: border-color 0.15s ease;
}
.mini-player-toggle:hover { border-color: rgba(0, 0, 0, 0.4); }
.mini-player .icon-pause { display: none; }
.mini-player.is-playing .icon-play { display: none; }
.mini-player.is-playing .icon-pause { display: block; }
.mini-player-wave {
  display: block;
  width: 100%;
  height: 30px;
  cursor: pointer;
  margin-top: 0.9rem;   /* top of the box, clearing the ✕ */
}
.mpw-axis { stroke: var(--color-rule); stroke-width: 1; vector-effect: non-scaling-stroke; }
/* 1px screen-width bars, the axis line's exact weight (56 bars over ~150px ≈ 1.6px
   gaps). Gray fills to black as the piece plays — the one sanctioned gray. */
.mpw-bars { fill: none; stroke: rgba(0, 0, 0, 0.28); stroke-width: 1; vector-effect: non-scaling-stroke; }
.mpw-fill { fill: none; stroke: var(--color-rule); stroke-width: 1; vector-effect: non-scaling-stroke; }
.mini-player-foot {
  /* Bottom row; the toggle's auto margins center it in the space between the
     top-right waveform and this row. */
  display: flex;
  align-items: baseline;
  gap: 0.6rem;
}
.mini-player-title {
  flex: 1;
  min-width: 0;
  font-size: 0.78rem;
  line-height: 1.3;
  color: var(--color-title);
  text-align: left;
  text-decoration: none;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mini-player-time {
  flex-shrink: 0;
  font-size: 11px;
  color: #000;
  letter-spacing: 0.03em;
  font-variant-numeric: tabular-nums;
  text-align: right; /* duration keeps the bottom-right corner */
}
@media (max-width: 768px) {
  .mini-player { right: 12px; bottom: 12px; width: 10rem; }
}

/* View transitions honor reduced motion: no cross-fade. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-image-pair(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
/* Shared native controls across portfolio studies and the artist site. */
html body input[type=range] { appearance:none; -webkit-appearance:none; box-sizing:border-box; height:28px; min-height:28px; padding:0; border:0; border-radius:0; background:transparent; cursor:ew-resize; accent-color:var(--ink,#242721); }
html body input[type=range]::-webkit-slider-runnable-track { height:3px; border-radius:0; background:linear-gradient(to right,var(--ink,#242721) var(--fill,0%),var(--line,#cfcec4) var(--fill,0%)); }
html body input[type=range]::-moz-range-track { height:3px; border-radius:0; background:var(--line,#cfcec4); }
html body input[type=range]::-moz-range-progress { height:3px; background:var(--ink,#242721); }
html body input[type=range]::-webkit-slider-thumb { appearance:none; -webkit-appearance:none; width:13px; height:13px; margin-top:-5px; border:1px solid var(--ink,#242721); border-radius:0; background:var(--paper,#f5f3ed); }
html body input[type=range]::-moz-range-thumb { width:11px; height:11px; border:1px solid var(--ink,#242721); border-radius:0; background:var(--paper,#f5f3ed); }
html body input[type=range]:disabled { opacity:.38; cursor:default; }
html body input[type=range]:focus-visible { outline:2px solid var(--ink,#242721); outline-offset:3px; }
.study-check { display:inline-flex; align-items:center; gap:9px; position:relative; cursor:pointer; }
.study-check input { position:absolute; opacity:0; width:1px; height:1px; }
.study-check>span:first-of-type { display:inline-grid; place-items:center; width:15px; height:15px; border:1px solid var(--muted,#686866); flex-shrink:0; }
.study-check>span:first-of-type::after { content:''; width:7px; height:4px; border-left:1.5px solid var(--paper,#f5f3ed); border-bottom:1.5px solid var(--paper,#f5f3ed); transform:translateY(-1px) rotate(-45deg); opacity:0; }
.study-check input:checked+span { background:var(--ink,#242721); border-color:var(--ink,#242721); }
.study-check input:checked+span::after { opacity:1; }
.study-check input:focus-visible+span { outline:2px solid var(--ink,#242721); outline-offset:4px; }
