/* ScrollTrigger measures layout by jumping the scroll position to 0 and back;
   CSS smooth scrolling animates that jump, so every refresh mismeasures by the
   current scroll offset. Anchor links on this page scroll smoothly via JS instead. */
html {
  scroll-behavior: auto;
}

.project-page {
  min-height: 100svh;
}

.project-back {
  display: flex;
  align-items: center;
  gap: clamp(0.5rem, 0.8vw, 0.75rem);
  width: fit-content;
}

.project-back__symbol {
  display: grid;
  flex: 0 0 0.72rem;
  place-items: center;
  width: 0.72rem;
  height: 0.72rem;
  background: currentColor;
}

.project-back__symbol.has-logo {
  background: transparent;
}

.project-back__symbol-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.project-page.is-project-locked {
  overflow: hidden;
}

.project-password-gate {
  position: fixed;
  inset: var(--main-nav-height) 0 0;
  z-index: var(--z-project-gate);
  display: grid;
  place-items: center;
  padding: var(--page-gutter);
  background: var(--color-bg);
}

.project-password-gate__inner {
  width: min(36rem, 100%);
}

.project-password-gate__eyebrow,
.project-password-gate__label,
.project-password-gate__error {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--type-label);
  font-weight: 400;
}

.project-password-gate__eyebrow,
.project-password-gate__label {
  color: var(--color-text-secondary);
  text-transform: uppercase;
}

.project-password-gate__title {
  margin: 0.75rem 0 clamp(2.5rem, 6vw, 5rem);
  overflow: visible;
  font-family: var(--font-display);
  font-size: var(--type-project-heading);
  font-weight: 400;
  letter-spacing: -0.052em;
  line-height: 0.95;
  transform: translateY(var(--rinter-optical-offset));
}

.project-password-gate__form {
  display: grid;
  gap: 0.5rem;
}

.project-password-gate__controls {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: 1rem;
  border-bottom: 1px solid var(--color-line);
}

.project-password-gate__input,
.project-password-gate__submit {
  min-height: 3.5rem;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--color-text);
  font: inherit;
}

.project-password-gate__input {
  min-width: 0;
  outline: 0;
}

.project-password-gate__submit {
  text-transform: uppercase;
  cursor: pointer;
  transition: color 180ms ease;
}

.project-password-gate__submit:hover,
.project-password-gate__submit:focus-visible {
  color: var(--color-accent);
}

.project-password-gate__submit:focus-visible {
  outline: 1px solid var(--color-accent);
  outline-offset: 0.2rem;
}

.project-password-gate__error {
  color: var(--color-text-secondary);
}

.project-password-gate__error[hidden] {
  display: none;
}

.project-main {
  /* Shared with .project-story so its own trailing padding (facet, overview) can be pinned to
     the exact same rhythm .project-content uses for its gap — otherwise the two containers each
     round their own clamp() independently and the boundary between them reads uneven. */
  --section-rhythm: clamp(4rem, 8vw, 8rem);

  padding-inline: var(--page-gutter);
}

.project-main[hidden] {
  display: none;
}

.project-section[hidden],
.project-pagination__link[hidden],
.project-client[hidden],
.project-hero[hidden],
.project-metadata-strip[hidden],
.project-contribution-summary[hidden],
.project-metadata__row[hidden] {
  display: none;
}

.project-intro {
  --project-accent: transparent;

  position: relative;
  isolation: isolate;
  padding-block: clamp(5rem, 9vw, 8rem) clamp(2.75rem, 5vw, 5rem);
}

.project-intro > :not(.project-intro__light) {
  position: relative;
  z-index: 1;
}

.project-intro__heading {
  --project-client-optical-inset: clamp(0.35rem, 1vw, 1.75rem);

  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: clamp(1.25rem, 2vw, 2.5rem);
}

.project-client {
  margin: 0 0 0 var(--project-client-optical-inset);
  color: var(--color-text);
  font-family: var(--font-display);
  font-size: calc(var(--type-project-title) * 0.3);
  font-weight: 400;
  letter-spacing: -0.045em;
  line-height: 0.95;
  text-transform: none;
  transform: translateY(var(--rinter-optical-offset));
}

.project-intro.has-project-accent .project-client {
  color: var(--project-accent);
}

.project-intro__light {
  --project-light-bleed: clamp(18rem, 30vw, 32rem);

  position: absolute;
  top: calc(var(--main-nav-height) * -1);
  bottom: calc(0px - var(--project-light-bleed));
  left: 50%;
  z-index: 0;
  width: 100vw;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
  transform: translateX(-50%);
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 52%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 52%, transparent 100%);
}

.project-intro.has-project-accent .project-intro__light {
  opacity: 1;
}

.project-intro__glow {
  position: absolute;
  display: block;
  border-radius: 50%;
  background: radial-gradient(
    ellipse at 64% 28%,
    color-mix(in srgb, var(--project-accent) 35%, transparent) 0%,
    color-mix(in srgb, var(--project-accent) 19%, transparent) 32%,
    color-mix(in srgb, var(--project-accent) 6%, transparent) 60%,
    transparent 82%
  );
  filter: blur(clamp(1rem, 3vw, 3rem));
  pointer-events: none;
  transform: translate3d(0, 0, 0) scale(1);
  will-change: transform, opacity;
}

.project-intro__glow--one {
  top: -75vh;
  right: -20vw;
  width: max(120vw, 72rem);
  height: max(160vh, 68rem);
  opacity: 0.76;
  animation: project-header-glow-one 36s ease-in-out infinite alternate;
}

@keyframes project-header-glow-one {
  from { transform: translate3d(-3%, -2%, 0) scale(0.98); }
  to { transform: translate3d(5%, 4%, 0) scale(1.06); }
}

.project-title {
  --project-title-glyph-buffer: 0.12em;

  margin: calc(var(--project-title-glyph-buffer) * -1) 0;
  padding-block: var(--project-title-glyph-buffer);
  overflow: visible;
  font-family: var(--font-display);
  font-size: var(--type-project-title);
  font-weight: 400;
  letter-spacing: -0.062em;
  line-height: 0.92;
  transform: translateY(var(--rinter-optical-offset));
}

.project-hero {
  margin-inline: calc(var(--page-gutter) * -1);
  padding-bottom: clamp(2.5rem, 5vw, 5rem);
}

.project-hero .project-media-block--contained {
  margin-inline: var(--page-gutter);
  overflow: hidden;
  border-radius: var(--media-radius);
}

.project-hero .project-media-block--full {
  border-radius: 0;
}

.project-hero video {
  width: 100%;
  height: auto;
  background: var(--color-black);
}

.project-hero__video-frame {
  position: relative;
  overflow: hidden;
  aspect-ratio: var(--hero-aspect-ratio, 16 / 9);
  background: var(--color-black);
}

.project-video-player {
  position: relative;
  overflow: hidden;
  background: var(--color-black);
}

.project-video-player--content video {
  display: block;
  width: 100%;
  height: auto;
}

.project-hero__video-frame video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  max-width: none;
  object-fit: cover;
  transform: scale(1.003);
  transform-origin: center;
}

.project-hero__play {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  width: clamp(3rem, 5vw, 4.5rem);
  aspect-ratio: 1;
  padding: 0;
  border: 0;
  color: var(--color-white);
  background: transparent;
  cursor: pointer;
  opacity: 1;
  transform: translate(-50%, -50%) scale(0.9);
  transition:
    transform 320ms cubic-bezier(0.55, 0.055, 0.675, 0.19),
    opacity 220ms ease;
}

.project-hero__play svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.project-hero__play circle {
  fill: rgb(0 0 0 / 0.18);
  stroke: currentColor;
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.project-hero__play path {
  fill: currentColor;
}

.project-hero__play:hover,
.project-hero__play:focus-visible {
  transform: translate(-50%, -50%) scale(1);
  transition-duration: 420ms;
  transition-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}

.project-hero__video-frame.is-playing .project-hero__play {
  pointer-events: none;
  opacity: 0;
}

.project-video-controls {
  position: absolute;
  right: clamp(0.65rem, 1.25vw, 1rem);
  bottom: clamp(0.65rem, 1.25vw, 1rem);
  z-index: 3;
  display: flex;
  align-items: center;
  gap: 0.15rem;
  min-height: 2.5rem;
  padding: 0.3rem 0.5rem;
  border: 1px solid rgb(255 255 255 / 0.16);
  border-radius: var(--media-radius);
  color: var(--color-white);
  background: rgb(0 0 0 / 0.48);
  box-shadow: 0 0.35rem 1.2rem rgb(0 0 0 / 0.14);
  opacity: 0;
  pointer-events: none;
  transform: translateY(0.25rem);
  transition:
    opacity 220ms ease,
    transform 320ms cubic-bezier(0.16, 1, 0.3, 1);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

.project-video-player:hover .project-video-controls,
.project-video-player:focus-within .project-video-controls {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.project-video-controls__button {
  display: grid;
  width: 1.8rem;
  height: 1.8rem;
  padding: 0;
  border: 0;
  place-items: center;
  color: inherit;
  background: transparent;
  cursor: pointer;
  opacity: 0.78;
  transition: opacity 180ms ease, transform 260ms cubic-bezier(0.16, 1, 0.3, 1);
}

.project-video-controls__button:hover,
.project-video-controls__button:focus-visible {
  opacity: 1;
  transform: scale(1.06);
}

.project-video-controls__button svg {
  width: 1.05rem;
  height: 1.05rem;
  overflow: visible;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.45;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.project-video-controls__button .is-fill {
  fill: currentColor;
  stroke: none;
}

.project-video-controls__volume {
  --video-volume: 0%;

  width: clamp(3.75rem, 6vw, 5.5rem);
  height: 1.8rem;
  margin: 0 0.1rem 0 0.25rem;
  padding: 0;
  background: transparent;
  cursor: pointer;
  appearance: none;
  -webkit-appearance: none;
}

.project-video-controls__volume::-webkit-slider-runnable-track {
  height: 1px;
  background: linear-gradient(to right, currentColor 0 var(--video-volume), rgb(255 255 255 / 0.32) var(--video-volume) 100%);
}

.project-video-controls__volume::-webkit-slider-thumb {
  width: 0.5rem;
  height: 0.5rem;
  margin-top: -0.21875rem;
  border: 0;
  border-radius: 50%;
  background: currentColor;
  appearance: none;
  -webkit-appearance: none;
}

.project-video-controls__volume::-moz-range-track {
  height: 1px;
  background: rgb(255 255 255 / 0.32);
}

.project-video-controls__volume::-moz-range-progress {
  height: 1px;
  background: currentColor;
}

.project-video-controls__volume::-moz-range-thumb {
  width: 0.5rem;
  height: 0.5rem;
  border: 0;
  border-radius: 50%;
  background: currentColor;
}

.project-metadata-strip {
  padding-block: clamp(1.5rem, 2.5vw, 2.5rem);
  border-top: 1px solid var(--color-line);
  border-bottom: 1px solid var(--color-line);
}

.project-metadata {
  --project-info-column-gap: clamp(1rem, 2.25vw, 3rem);
}

.project-metadata__row {
  display: grid;
  column-gap: var(--project-info-column-gap, clamp(1rem, 2.25vw, 3rem));
  row-gap: clamp(1.5rem, 2.5vw, 2.5rem);
}

.project-metadata__row--primary,
.project-metadata__row--creative {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.project-metadata__row:not(:has(> :nth-child(3))) {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.project-metadata__row > :only-child {
  grid-column: 1 / -1;
}

.project-meta {
  min-width: 0;
}

.project-meta__label,
.project-section h2,
.project-story__label,
.project-pagination__label {
  display: block;
  margin: 0 0 0.7rem;
  color: var(--color-text-secondary);
  font-family: var(--font-body);
  font-size: var(--type-label);
  font-weight: 400;
  text-transform: uppercase;
}

.project-meta__label {
  margin-bottom: 0.45rem;
}

.project-meta__value,
.project-meta__list,
.project-story p,
.project-process__item p,
.project-recognition p {
  margin: 0;
}

.project-meta__value,
.project-meta__list {
  color: color-mix(in srgb, var(--color-text) 76%, var(--color-bg));
  font-size: clamp(0.875rem, 0.82rem + 0.18vw, 1rem);
  line-height: 1.35;
}

.project-meta__value--mono {
  font-family: var(--font-mono);
  font-size: var(--type-mono);
  font-variant-numeric: tabular-nums;
}

.project-meta__list {
  padding: 0;
  list-style: none;
}

.project-partners {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 0.65rem;
  margin: 0;
  padding: 0;
  color: color-mix(in srgb, var(--color-text) 76%, var(--color-bg));
  font-size: clamp(0.875rem, 0.82rem + 0.18vw, 1rem);
  line-height: 1.35;
  list-style: none;
}

.project-partners__item {
  display: inline-flex;
  min-width: 0;
  gap: 0.3em;
  overflow-wrap: anywhere;
}

.project-partners__item + .project-partners__item::before {
  content: "—";
  margin-right: 0.35rem;
}

.project-contribution-summary {
  --project-info-column-gap: clamp(2rem, 5vw, 6rem);

  padding-block: clamp(4rem, 8vw, 8rem);
  border-top: 1px solid var(--color-line);
}

.project-media {
  display: grid;
  gap: clamp(0.75rem, 1.5vw, 1.5rem);
  margin-inline: calc(var(--page-gutter) * -1);
  padding-block: clamp(0.75rem, 1.5vw, 1.5rem);
}

.project-media:empty {
  display: none;
}

.project-media-block {
  margin: 0;
  background: transparent;
}

.project-media-block img,
.project-media-block video {
  display: block;
  width: 100%;
  height: auto;
}

.project-video-placeholder {
  position: relative;
  background: var(--color-black);
}

.project-video-embed {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  background: var(--color-black);
}

.project-video-embed iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.project-video-placeholder__label {
  position: absolute;
  right: var(--page-gutter);
  bottom: clamp(1rem, 2vw, 2rem);
  color: var(--color-white);
  font-family: var(--font-mono);
  font-size: var(--type-mono);
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
}

/* Module width. --module-width-setting is written inline by the renderer, but it is
   read into --module-width here rather than used directly, so the mobile breakpoint
   below can override it — a media query cannot beat an inline custom property. */
.project-media-block--pair,
.project-media-block--row {
  --module-width: var(--module-width-setting, 100);
  --module-scale: calc(var(--module-width) / 100);

  display: flex;
  /* Never wrap: the module is a single row that always fills its width exactly. */
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: calc(var(--space-md) * var(--module-scale));
  background: transparent;
}

/* 100% of the content container — the width every other block occupies — not of the
   viewport. .project-media itself spans full-bleed, so the gutters are subtracted here. */
.project-media > .project-media-block--pair,
.project-media > .project-media-block--row {
  /* .project-media is a grid; a grid item's default min-width: auto resolves to
     min-content and would beat max-width, leaving the module unconstrained and
     margin-inline: auto with no free space to centre. */
  min-width: 0;
  max-width: calc((100% - 2 * var(--page-gutter)) * var(--module-scale));
  margin-inline: auto;
}

/* Width drives, height follows. Each item grows in proportion to its own aspect ratio
   (--item-aspect, written by the renderer once the image reports its natural size) from
   a zero basis, so the items divide the module's width exactly and every one resolves to
   the same height — width_i / aspect_i is identical across items. The image then takes
   its height from that width, which is what makes a narrower module a smaller module. */
.project-media-block--pair > .project-media-block__item,
.project-media-block--row > .project-media-block__item {
  flex: var(--item-aspect, 1) 1 0;
  min-width: 0;
}

.project-media-block--pair img,
.project-media-block--pair video,
.project-media-block--row img,
.project-media-block--row video {
  display: block;
  width: 100%;
  height: auto;
  transform-origin: center;
}

/* Text panels inside pair / gallery ---------------------------------------- */

.project-media-text {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
  /* Copy that began exactly where an image begins reads as a layout accident. One rem is
     enough to make the inset deliberate without pushing the text away from its corner. */
  padding: 1rem;
}

/* Nine-point placement, read off the two halves of the value: the first word positions the
   column along the block axis, the second along the inline axis. */
.project-media-text[data-align^="top"] { justify-content: flex-start; }
.project-media-text[data-align^="center"] { justify-content: center; }
.project-media-text[data-align^="bottom"] { justify-content: flex-end; }

.project-media-text[data-align$="left"] { align-items: flex-start; text-align: left; }
.project-media-text[data-align$="right"] { align-items: flex-end; text-align: right; }
.project-media-text[data-align$="center"],
.project-media-text[data-align="center"] { align-items: center; text-align: center; }
.project-media-text[data-align="center-left"] { align-items: flex-start; text-align: left; }
.project-media-text[data-align="center-right"] { align-items: flex-end; text-align: right; }

.project-media-text__lead {
  max-width: 100%;
  margin: 0;
  font-size: var(--type-editorial-copy);
  line-height: 1.12;
  letter-spacing: -0.025em;
}

.project-media-text__paragraphs {
  display: grid;
  gap: var(--space-md);
  /* Flush with the lead above it, on the same rule as every other body copy on the page. */
  max-width: min(100%, var(--body-measure, 100%));
}

.project-media-text__paragraphs p {
  margin: 0;
  font-size: var(--type-paragraph);
  line-height: 1.45;
}

/* Only when a lead is actually present — an absent lead leaves the paragraph at the top of
   the frame with no compensating margin to strip. */
.project-media-text__lead + .project-media-text__paragraphs {
  margin-top: var(--space-lg);
}

/* A row carrying a text panel stretches its items to a common height instead of letting each
   sit at its own. The media item keeps its natural ratio as a *preferred* height through
   aspect-ratio, so short copy changes nothing; longer copy makes the row taller and the
   absolutely positioned image covers the extra height by zooming rather than distorting. */
.project-media-block--with-text {
  align-items: stretch;
}

.project-media-block--with-text > .project-media-block__item--media {
  position: relative;
  aspect-ratio: var(--item-natural-aspect, 1.5);
}

.project-media-block--with-text > .project-media-block__item--media :is(img, video) {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Ratio lock opts an item out of the stretch entirely rather than switching it to `contain`.
   Containing inside a stretched frame would keep the artwork intact but leave it floating in
   dead space; opting out keeps it at its natural size and lets the row grow around it, which
   is what artwork with alpha — a packshot, a logotype — actually needs. */
.project-media-block--with-text > .project-media-block__item--locked {
  align-self: flex-start;
  aspect-ratio: auto;
}

.project-media-block--with-text > .project-media-block__item--locked :is(img, video) {
  position: static;
  height: auto;
  object-fit: contain;
}

/* In the grid the cell size is fixed, so the artwork is fitted inside it instead. */
.project-media-block--gallery > .project-media-block__item--locked :is(img, video) {
  object-fit: contain;
}

.project-media-block--gallery {
  --gallery-effective-columns: var(--gallery-columns, 5);
  /* Kept as its own variable rather than var(--space-md) directly: grid-auto-rows below
     must use the exact same value to keep cells square, so the two can't drift apart. */
  --gallery-gap: 0.5rem;

  container-type: inline-size;
  display: grid;
  grid-template-columns: repeat(var(--gallery-effective-columns), 1fr);
  grid-auto-rows: minmax(calc((100cqw - (var(--gallery-effective-columns) - 1) * var(--gallery-gap)) / var(--gallery-effective-columns)), auto);
  grid-auto-flow: dense;
  gap: var(--gallery-gap);
  background: transparent;
}

.project-media > .project-media-block--gallery {
  margin-inline: var(--page-gutter);
}

.project-media-block--gallery > .project-media-block__item {
  grid-column: span min(var(--item-col-span, 1), var(--gallery-effective-columns));
  grid-row: span var(--item-row-span, 1);
}

.project-media-block--gallery img,
.project-media-block--gallery video {
  height: 100%;
  object-fit: cover;
  transform-origin: center;
}

.project-media-block--gallery-natural {
  grid-auto-rows: auto;
  align-items: start;
}

.project-media-block--gallery-natural > .project-media-block__item {
  grid-row: auto;
  align-self: start;
}

.project-media-block--gallery-natural img,
.project-media-block--gallery-natural video {
  height: auto;
  object-fit: initial;
}

.project-media > .project-media-block--contained {
  margin-inline: var(--page-gutter);
  overflow: hidden;
  border-radius: var(--media-radius);
}

.project-media-block--full {
  overflow: hidden;
}

.project-media-block--parallax {
  position: relative;
  aspect-ratio: 16 / 9;
}

.project-media-block--parallax img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 130%;
  object-fit: cover;
}

.project-media-block--comparison {
  overflow: hidden;
  border-radius: var(--media-radius);
}

.project-media > .project-media-block--comparison {
  margin-inline: var(--page-gutter);
}

/* Registered so the handback can be animated: a bare custom property is not interpolable,
   so transitioning --comparison-position would otherwise jump. */
@property --comparison-position {
  syntax: "<percentage>";
  inherits: false;
  initial-value: 50%;
}

.project-comparison {
  --comparison-position: 50%;

  position: relative;
  overflow: hidden;
  background: var(--color-surface);
  /* No longer ew-resize: the divider follows the cursor on its own, and a drag affordance
     would promise a grab that is neither needed nor handled. */
  cursor: default;
}

/* Applied only while the divider eases from where the pointer left it back to the
   scroll-driven position — tracking itself stays untransitioned so it feels attached to
   the cursor rather than trailing it. */
.project-comparison.is-realigning {
  transition: --comparison-position 650ms cubic-bezier(0.22, 1, 0.36, 1);
}

.project-media-block .project-comparison__after {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  clip-path: inset(0 calc(100% - var(--comparison-position)) 0 0);
}

.project-comparison__divider {
  position: absolute;
  top: 0;
  bottom: 0;
  left: var(--comparison-position);
  z-index: 1;
  width: 1px;
  background: var(--color-white);
  pointer-events: none;
  transform: translateX(-0.5px);
}

.project-comparison__range {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}

.project-media-block__item {
  margin: 0;
  overflow: hidden;
  background: transparent;
  border-radius: var(--media-radius);
}

/* Horizontal Gallery is native overflow by default. Desktop enhancement only changes the
   section into a tall scroll chapter; its viewport remains sticky while one ScrollTrigger
   translates the semantic, normally laid-out track by its measured overflow. */
.project-media > .gallery-horizontal {
  min-width: 0;
  margin-inline: 0;
}

.gallery-horizontal__sticky {
  width: 100%;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-width: thin;
  -webkit-overflow-scrolling: touch;
}

.gallery-horizontal__sticky:focus-visible {
  outline: 1px solid var(--color-accent);
  outline-offset: -1px;
}

.gallery-horizontal__track {
  display: flex;
  width: max-content;
  padding-inline: var(--page-gutter);
  gap: clamp(0.75rem, 1.5vw, 1.5rem);
}

.gallery-horizontal__item {
  flex: 0 0 clamp(18rem, 82vw, 48rem);
  width: clamp(18rem, 82vw, 48rem);
  max-height: 72svh;
}

.gallery-horizontal__item > :is(img, video) {
  width: 100%;
  height: auto;
  max-height: 72svh;
  object-fit: contain;
  transform-origin: center;
}

.gallery-horizontal__item > .project-media-text {
  min-height: min(72svh, 42rem);
}

@media (min-width: 901px) and (hover: hover) and (pointer: fine) {
  .gallery-horizontal.is-scroll-driven .gallery-horizontal__sticky {
    position: sticky;
    top: var(--main-nav-height);
    display: flex;
    align-items: center;
    height: calc(100svh - var(--main-nav-height));
    overflow: hidden;
  }

  .gallery-horizontal__item {
    flex-basis: var(--horizontal-item-width, clamp(28rem, 64vw, 72rem));
    width: var(--horizontal-item-width, clamp(28rem, 64vw, 72rem));
  }
}

.project-process__media > .project-media-block--full,
.project-media-grid,
.project-embedded-media,
.project-embed {
  overflow: hidden;
  border-radius: var(--media-radius);
}

.project-media figcaption,
.project-process__media figcaption {
  display: none;
}

.project-section {
  border-top: 1px solid var(--color-line);
}

/* One rhythm for every section, whatever it holds. Media blocks carried 24px of padding and
   text sections 128px, so the gap between two sections depended on which two they were — 48px
   between two galleries, 152px across a boundary, 256px between two text blocks. The spacing
   moves to a single gap on the container so it cannot double, and the blocks stop setting it. */
.project-content {
  display: grid;
  gap: var(--section-rhythm);
}

.project-content > .project-media,
.project-content > .project-story__section {
  padding-block: 0;
}

/* Whatever top-level section follows — Process, Recognition, Credits, Contribution — draws its
   own divider unconditionally (`.project-contribution-summary` etc. carry their own border-top
   and padding-top regardless of what precedes them). Before this, .project-content contributed
   nothing on its side of that line, so the line sat flush against the last paragraph with all
   the clearance dumped on the other side — the same asymmetry already fixed once for dividers
   inside .project-content, recurring here because this boundary sits outside that container.
   Matches the section rhythm so the line reads as centred, the same as every other divider. */
.project-content {
  margin-bottom: var(--section-rhythm);
}

.project-content:empty {
  display: none;
}

/* Where two text blocks meet, a divider is drawn on top of the container's own rhythm gap, so
   without its own inset the heading would sit on the line. That inset shares --section-rhythm
   rather than a value of its own — a separate ad hoc clamp(2rem,4vw,3rem) was tried first and
   read as noticeably smaller than the gap below (48px capped at 1200px wide, against the gap's
   128px), which put unequal air above and below the same line. Sharing the variable makes the
   line optically centred: the same rhythm both above and below it. */
.project-content__text + .project-content__text,
.project-content__process + .project-content__text,
.project-content__text + .project-content__process {
  border-top: 1px solid var(--color-line);
  padding-top: var(--section-rhythm);
}

.project-content__text h3 {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--type-project-heading);
  font-weight: 400;
  letter-spacing: -0.052em;
  line-height: 0.95;
  transform: translateY(var(--rinter-optical-offset));
}

/* One control for every text section. The copy column is a share of the space it was given,
   which is what puts a right inset there at all — the grid's content column otherwise runs to
   the page gutter and nothing else, leaving copy flush against the edge while the label column
   held a wide margin on the left. Percent rather than a measure so a single number governs
   every module of this kind, and Studio exposes exactly that number. */
.project-content__text-copy {
  min-width: 0;
  max-width: var(--text-column-width, 80%);
}

.project-content__text--without-label .project-content__text-copy {
  grid-column: 2;
}

.project-content__text h3 + .project-content__lead {
  margin-top: 1rem;
}

.project-content__text h3 + .project-content__paragraphs {
  margin-top: var(--space-lg);
}

.project-content__lead + .project-content__paragraphs {
  margin-top: var(--space-xl);
}

/* One measure for the whole stack. These caps were set independently over time — 54rem here,
   64ch there, 48ch and 78ch for the variants — in two different unit systems, which is what
   produced a pyramid of three widths with no logic joining them. Every copy element now fills
   the column the author sized, and only the body guard below bites, and only at extremes. */
.project-content__lead {
  max-width: 100%;
  margin: 0;
  font-size: var(--type-editorial-copy);
  line-height: 1.12;
  letter-spacing: -0.025em;
}

.project-content__paragraphs {
  display: grid;
  gap: var(--space-md);
  width: 100%;
  /* Flush by default. A 90ch guard was tried here and removed: because the body scale is fluid,
     90ch resolves against a small font on narrow viewports and was clipping the paragraph to
     799px inside a 1077px column — firing at ordinary widths, which is exactly what a guard
     must not do. The measure stays a variable so the narrow and wide variants can set one. */
  max-width: min(100%, var(--body-measure, 100%));
}

.project-content__paragraphs--narrow {
  --body-measure: 52ch;
}

.project-content__paragraphs--wide {
  --body-measure: 100ch;
}

.project-content__paragraphs p {
  margin: 0;
  font-size: var(--type-paragraph);
  line-height: 1.58;
  letter-spacing: -0.01em;
}

.project-story:empty {
  display: none;
}

/* Keep line and paragraph breaks entered in the Studio story fields. */
.project-story__overview p,
.project-story__facet p {
  white-space: pre-line;
}

.project-story__overview {
  display: grid;
  grid-template-columns: minmax(8rem, 0.7fr) minmax(0, 2.3fr);
  gap: clamp(2rem, 6vw, 8rem);
  padding-block: clamp(4rem, 8vw, 8rem);
}

/* Brought under the same rule as every other text section. Its old 32ch measure was a separate
   mechanism reaching the same goal, which is exactly the drift this change exists to remove. */
/* Scoped through .project-story so it outranks the blanket `.project-story p` rule further
   down, which has the same specificity and would otherwise win on source order alone. */
.project-story .project-story__overview p {
  max-width: var(--text-column-width, 80%);
  margin: 0;
  color: var(--color-text);
  font-size: clamp(1.75rem, 2.5vw, 3rem);
  line-height: 1.1;
  letter-spacing: -0.025em;
}

.project-story__challenge-approach {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: stretch;
  gap: 0;
  border-top: 1px solid var(--color-line);
}

.project-story__challenge-approach.is-single {
  grid-template-columns: minmax(0, 1fr);
}

.project-story__facet {
  display: grid;
  align-content: start;
  gap: clamp(1.5rem, 2.5vw, 2.5rem);
  min-width: 0;
  /* Matches --section-rhythm rather than its own clamp, so the boundary into .project-content
     lands on the same rhythm as every other section gap instead of capping ~25% short of it. */
  padding-block: var(--section-rhythm);
  padding-left: clamp(2rem, 5vw, 6rem);
  padding-right: clamp(2rem, 5vw, 6rem);
}

/* The second column used to zero its right padding, so Challenge sat evenly inset while
   Approach ran to the page edge. Nothing depended on it — the divider is the border below. */
.project-story__facet + .project-story__facet {
  border-left: 1px solid var(--color-line);
}

.project-story .project-story__facet p {
  max-width: min(100%, var(--body-measure, 100%));
  margin: 0;
  font-size: var(--type-paragraph);
  line-height: 1.58;
  letter-spacing: -0.01em;
}

.project-story__section,
.project-section {
  display: grid;
  grid-template-columns: minmax(8rem, 0.7fr) minmax(0, 2.3fr);
  gap: clamp(2rem, 6vw, 8rem);
  padding-block: clamp(4rem, 8vw, 8rem);
}

.project-story__section + .project-story__section {
  border-top: 1px solid var(--color-line);
}

.project-story__label,
.project-section h2 {
  margin: 0;
}

.project-story p,
.project-process__item p {
  max-width: 100%;
  font-size: var(--type-editorial-copy);
  line-height: 1.12;
  letter-spacing: -0.025em;
}

.project-process__items {
  display: grid;
  gap: clamp(4rem, 7vw, 7rem);
}

.project-process__item h3 {
  margin: 0 0 1rem;
  font-family: var(--font-display);
  font-size: var(--type-project-heading);
  font-weight: 400;
  letter-spacing: -0.052em;
  line-height: 0.95;
  transform: translateY(var(--rinter-optical-offset));
}

.project-process__media {
  display: grid;
  gap: clamp(0.75rem, 1.5vw, 1.5rem);
  margin-top: clamp(2rem, 4vw, 4rem);
}

.project-recognition__items {
  display: grid;
  gap: 1rem;
}

.project-recognition__award {
  display: grid;
  grid-template-columns: minmax(12rem, 0.8fr) minmax(0, 1.6fr);
  gap: 2rem;
  padding-block: 1rem;
  border-top: 1px solid var(--color-line);
}

.project-recognition__award:first-child {
  padding-top: 0;
  border-top: 0;
}

.project-recognition__name {
  display: flex;
  /* Stacked rather than side by side: at the scaled-up mark size there is no room left for a
     name beside it, and the festival mark reads better with its name sitting under it. */
  flex-direction: column;
  align-items: flex-start;
  gap: 0.75rem;
}

.project-recognition__name p {
  margin: 0;
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 1.6vw, 1.5rem);
  letter-spacing: -0.03em;
  line-height: 1.1;
}

/* The awards block is the most important thing on some of these pages and was reading as an
   afterthought at 2rem. Scaled up and given the same currentColor mask treatment.
   The box takes the full column width with only its height capped, rather than being a fixed
   square: festival marks range from wide wordmarks to tall figures, and `contain` inside a
   square let a wide one use barely half the space it had. Width-driven with a height ceiling
   sizes both to the same visual weight. */
.project-recognition__mark {
  width: 100%;
  height: clamp(5rem, 7vw, 6.5rem);
  background-color: currentColor;
  -webkit-mask: var(--award-mark) left center / contain no-repeat;
  mask: var(--award-mark) left center / contain no-repeat;
}

/* Grid on the list with subgrid rows: the level, title and year columns have to line up down
   the whole list, which per-entry grids cannot do since each would size its columns alone. */
.project-recognition__entries {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  margin: 0;
  padding: 0;
  list-style: none;
}

.project-recognition__entry {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  gap: 1.5rem;
  align-items: baseline;
  padding-bottom: 0.75rem;
}

.project-recognition__level {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  white-space: nowrap;
}

/* The dot slot is rendered for every entry, coloured only for a recognised tier. `level` is a
   free-text field and some projects store a craft category in it, which keeps its label and an
   uncoloured dot rather than borrowing a medal colour it did not win. */
.project-recognition__dot {
  flex: 0 0 auto;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: var(--recognition-dot, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, currentColor 18%, transparent);
}

.project-recognition__entry[data-level="grand-prix"] { --recognition-dot: #0f9d6b; }
.project-recognition__entry[data-level="gold"] { --recognition-dot: #cfa029; }
.project-recognition__entry[data-level="silver"] { --recognition-dot: #b4b8bd; }
.project-recognition__entry[data-level="bronze"] { --recognition-dot: #ac6f3c; }
/* Deliberately far from silver so a shortlist never reads as a medal. */
.project-recognition__entry[data-level="shortlist"] { --recognition-dot: #4c4f55; }

[data-theme="dark"] .project-recognition__entry[data-level="shortlist"] { --recognition-dot: #74787f; }

.project-recognition__year {
  font-family: var(--font-mono);
  font-size: var(--type-mono);
  font-variant-numeric: tabular-nums;
}

.project-credits__list {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 1.5rem;
  margin: 0;
}

.project-credit {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.35rem;
}

.project-credit dt {
  color: var(--color-text-secondary);
  text-transform: uppercase;
}

.project-credit dd {
  margin: 0;
}

.project-pagination {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border-top: 1px solid var(--color-line);
}

.project-pagination__link {
  min-width: 0;
  padding-block: clamp(4rem, 8vw, 8rem);
}

.project-pagination__link--next {
  text-align: right;
}

.project-pagination__link:only-child.project-pagination__link--next {
  grid-column: 2;
}

.project-pagination__title {
  display: block;
  overflow: visible;
  font-family: var(--font-display);
  font-size: var(--type-project-heading);
  letter-spacing: -0.052em;
  line-height: 0.95;
  transform: translateY(var(--rinter-optical-offset));
  transition: color 180ms ease;
}

.project-pagination__link:hover .project-pagination__title,
.project-pagination__link:focus-visible .project-pagination__title {
  color: var(--color-accent);
}

@media (max-width: 900px) {
  .project-credits__list {
    grid-template-columns: 1fr;
  }

  .project-media-block--gallery {
    --gallery-effective-columns: 3;
  }
}

@media (max-width: 760px) {
  /* Narrow screens have no room to give up to side margins, so a module always
     spans the full content width here regardless of its configured percentage. */
  .project-media-block--pair,
  .project-media-block--row {
    --module-width: 100;
  }

  /* Side by side leaves the image a thumbnail at this width — a pair carrying copy stacks
     instead, image first, so the picture introduces the text rather than sitting beside it. */
  .project-media-block--with-text {
    flex-direction: column;
  }

  .project-media-block--with-text > .project-media-block__item {
    flex: 0 0 auto;
    width: 100%;
  }

  .project-media-block--with-text > .project-media-block__item--media {
    order: -1;
    aspect-ratio: auto;
  }

  .project-media-block--with-text > .project-media-block__item--media :is(img, video) {
    position: static;
    height: auto;
    object-fit: contain;
  }

  .project-media-text {
    padding-inline: 0;
  }

  :root {
    --type-project-title: clamp(3.5rem, 18vw, 7rem);
  }

  .project-intro {
    padding-top: clamp(4.5rem, 18vw, 6.5rem);
    padding-bottom: 2.5rem;
  }

  .project-title {
    line-height: 0.94;
  }

  .project-metadata,
  .project-contribution-summary {
    --project-info-column-gap: 1rem;
  }

  .project-metadata__row--primary {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .project-metadata__row--primary:not(:has(> :nth-child(3))) {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .project-metadata__row--creative {
    grid-template-columns: 1fr;
    gap: 2.5rem;
  }

  .project-partners {
    display: grid;
    gap: 0.45rem;
  }

  .project-partners__item {
    display: flex;
    flex-wrap: wrap;
  }

  .project-partners__item + .project-partners__item::before {
    display: none;
  }

  .project-hero {
    padding-bottom: 2.5rem;
  }

  .project-media-block--gallery {
    --gallery-effective-columns: 2;
  }

  .project-comparison__range {
    pointer-events: none;
  }

  .project-comparison {
    cursor: default;
  }

  .project-story__section,
  .project-section {
    grid-template-columns: 1fr;
    gap: 2rem;
  }

  /* Nothing to give back at this width — the inset comes from the page gutter alone. */
  .project-content__text-copy,
  .project-story .project-story__overview p {
    max-width: 100%;
  }

  .project-story__overview {
    grid-template-columns: 1fr;
    gap: 1.5rem;
    padding-block: 3.5rem;
  }

  .project-story__overview p {
    font-size: clamp(1.75rem, 8vw, 2.5rem);
  }

  .project-story__challenge-approach {
    grid-template-columns: 1fr;
  }

  .project-story__facet {
    padding: 2.5rem 0;
  }

  .project-story__facet + .project-story__facet {
    padding: 2.5rem 0;
    border-top: 1px solid var(--color-line);
    border-left: 0;
  }

  .project-content__text--without-label .project-content__text-copy {
    grid-column: 1;
  }

  .project-recognition__award {
    grid-template-columns: 1fr;
    gap: 1rem;
  }

  /* Below the breakpoint the year drops onto its own line rather than squeezing the title. */
  .project-recognition__entries {
    grid-template-columns: auto minmax(0, 1fr);
  }

  .project-recognition__entry {
    gap: 0.25rem 1rem;
  }

  .project-recognition__year {
    grid-column: 2;
  }

  .project-credit {
    grid-template-columns: minmax(6rem, 0.7fr) minmax(0, 1.3fr);
  }

  .project-pagination {
    grid-template-columns: 1fr;
  }

  .project-pagination__link--next,
  .project-pagination__link:only-child.project-pagination__link--next {
    grid-column: 1;
    order: -1;
    text-align: left;
  }

  .project-pagination__link + .project-pagination__link {
    border-top: 0;
  }

  .project-pagination__link--previous:not(:only-child) {
    border-top: 1px solid var(--color-line);
  }
}

@media (prefers-reduced-motion: reduce) {
  .gallery-horizontal__sticky {
    position: static !important;
    height: auto !important;
    overflow-x: auto !important;
  }

  .gallery-horizontal__track {
    transform: none !important;
  }

  .project-intro__glow {
    animation: none !important;
  }

  .project-hero__play {
    transition: none;
  }

  .project-video-controls,
  .project-video-controls__button {
    transition: none;
  }
}

@media (hover: none) {
  .project-video-controls {
    opacity: 1;
    pointer-events: auto;
    transform: none;
  }
}

/* PRO projects — the dark page treatment, switched on per project by theme.mode = "dark"
   and applied as data-theme on <html> (baked into the static document at build time, so
   there is no flash of the light theme before the script runs).

   Overrides design tokens rather than components: every block type — hero, metadata, text,
   image, image-pair, gallery, comparison, video — follows without per-component work. */
[data-theme="dark"] {
  /* Not pure black. A trace of warmth keeps photography from sitting in a void. */
  --color-bg: #0c0c0d;
  --color-surface: #151517;

  /* Reuses the off-white the light theme already ships rather than pure white, which
     glares against a dark field. */
  --color-text: #f2f1ef;
  --color-text-secondary: #8e8e93;
  --color-line: #2b2b2e;
  --color-grey: #6e6e73;

  /* The floating nav panel that docks on scroll reads white until this is set: it uses
     --color-nav-panel, not the --color-nav-light/dark pair. */
  --color-nav-panel: rgb(24 24 27 / 96%);
  --color-nav-submenu: rgb(38 38 41 / 96%);
  --color-nav-light: rgb(18 18 20 / 72%);
  --color-nav-dark: rgb(0 0 0 / 72%);

  /* A drop shadow is invisible against a dark page, so a hairline defines the panel and
     the shadow is kept only for depth. */
  --nav-panel-shadow:
    0 0 0 1px rgb(255 255 255 / 10%),
    0 0.6rem 1.8rem rgb(0 0 0 / 55%);
}

/* --color-accent is deliberately not overridden: it gains punch against dark rather than
   needing help, and per-project theme.accentColor keeps working unchanged. */
[data-theme="dark"] body {
  background: var(--color-bg);
  color: var(--color-text);
}

/* Sits on --color-surface, so without this the slider keeps a white bed behind its images. */
[data-theme="dark"] .project-comparison {
  background: var(--color-surface);
}

/* Dark imagery bleeds into a dark page with no edge to stop it. A hairline gives each media
   block a boundary without introducing a visible frame. */
[data-theme="dark"] .project-media-block__item,
[data-theme="dark"] .project-media-block--contained,
[data-theme="dark"] .project-media-block--full img,
[data-theme="dark"] .project-comparison {
  box-shadow: 0 0 0 1px rgb(255 255 255 / 7%);
}

/* Spotify / Apple Music player. The provider owns everything inside the frame — its colours,
   type and radius are not ours to restyle — so the block only controls the space around it
   and matches the width other contained blocks use. */
.project-media-block--audio {
  background: transparent;
}

.project-media > .project-media-block--audio {
  margin-inline: var(--page-gutter);
}

.project-audio {
  display: block;
  width: 100%;
  border: 0;
  /* Spotify's own embed ships this radius; matching it stops the frame corners showing
     through as square edges against the page. */
  border-radius: 12px;
}

/* Audio Story — transcript-led playback. Every segment remains one semantic list item in
   the document; the focus window changes only its visual emphasis. */
.audio-story {
  --audio-story-step: 3.8rem;
  width: min(100% - (2 * var(--page-gutter)), 76rem);
  margin: clamp(5rem, 10vw, 10rem) auto;
  padding-block: clamp(2rem, 5vw, 4rem);
  border-block: 1px solid var(--color-line);
}

.audio-story__header {
  display: grid;
  grid-template-columns: minmax(8rem, 0.32fr) 1fr;
  gap: var(--space-md);
  align-items: start;
  margin-bottom: clamp(2rem, 6vw, 5rem);
}

.audio-story__label,
.audio-story__controls,
.audio-story__full {
  font-family: "Google Sans Code", monospace;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.audio-story__label {
  margin: 0;
  color: var(--color-text-secondary);
}

.audio-story__header h2 {
  margin: 0;
  font-size: clamp(2rem, 5vw, 5rem);
  font-weight: 400;
  line-height: 0.95;
}

.audio-story__focus {
  position: relative;
  height: calc(var(--audio-story-step) * 5);
  overflow: hidden;
  mask-image: linear-gradient(to bottom, transparent, #000 13%, #000 87%, transparent);
}

.audio-story__transcript {
  position: relative;
  height: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

.audio-story__segment {
  position: absolute;
  top: 50%;
  left: 0;
  width: 100%;
  margin: 0;
  opacity: 0;
  transition: transform 0.8s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.8s ease;
}

.audio-story__segment[data-distance="0"] { opacity: 1; }
.audio-story__segment[data-distance="1"] { opacity: 0.7; }
.audio-story__segment[data-distance="2"] { opacity: 0.4; }
.audio-story__segment[data-distance="3"] { pointer-events: none; }

.audio-story__segment button {
  display: block;
  width: 100%;
  min-height: var(--audio-story-step);
  padding: 0.6rem 0;
  border: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: clamp(1.15rem, 2.2vw, 2rem);
  line-height: 1.25;
  text-align: left;
  cursor: pointer;
}

.audio-story__segment.is-active button { font-weight: 500; }

.audio-story__controls {
  display: grid;
  grid-template-columns: auto auto minmax(8rem, 1fr) auto;
  gap: 0.8rem;
  align-items: center;
  margin-top: var(--space-lg);
}

.audio-story__play,
.audio-story__full {
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  cursor: pointer;
}

.audio-story__play { min-width: 3.8rem; text-align: left; }
.audio-story__controls input { width: 100%; accent-color: var(--color-accent); }
.audio-story__full { margin-top: var(--space-md); color: var(--color-text-secondary); }
.audio-story__error { color: var(--color-accent); }

.audio-story.is-transcript-expanded .audio-story__focus {
  height: auto;
  overflow: visible;
  mask-image: none;
}

.audio-story.is-transcript-expanded .audio-story__transcript { height: auto; }
.audio-story.is-transcript-expanded .audio-story__segment {
  position: static;
  width: auto;
  opacity: 1;
  transform: none !important;
}

.audio-story[data-state="error"] > audio { display: block; width: 100%; margin-block: var(--space-md); }

@media (max-width: 700px) {
  .audio-story { --audio-story-step: 4.2rem; }
  .audio-story__header { grid-template-columns: 1fr; }
  .audio-story__controls { grid-template-columns: auto 1fr auto; }
  .audio-story__controls input { grid-column: 1 / -1; grid-row: 2; }
}

@media (prefers-reduced-motion: reduce) {
  .audio-story__segment { transition: none; }
}
