/* ---------- Base ---------- */

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

:root {
  --paper: #f7f5f1;
  --paper-2: #efece6;
  --ink: #15171a;
  --muted: #6b6f76;
  --line: rgba(21, 23, 26, 0.1);
  --accent: #d62828; /* the red stroke from the logo */
}

/* no native smooth scrolling: Lenis (vendor/lenis.min.js) drives the scroll */
html {
  scroll-behavior: auto;
}

html.lenis,
html.lenis body {
  height: auto;
}

.lenis.lenis-smooth {
  scroll-behavior: auto !important;
}

.lenis.lenis-stopped {
  overflow: hidden;
}

body {
  font-family: "Jost", system-ui, sans-serif;
  font-weight: 300;
  color: var(--ink);
  background: var(--paper);
  overflow-x: hidden;
}

img {
  user-select: none;
}

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

/* ---------- Navigation ---------- */

.nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: 3rem;
  padding: 1.5rem clamp(1.5rem, 4vw, 3rem);
  --nav-solid: 0;
  /* white type over the photo, ink type once the bar has its own ground */
  color: #fff;
  background: rgba(247, 245, 241, calc(var(--nav-solid) * 0.92));
  backdrop-filter: blur(calc(var(--nav-solid) * 12px));
  -webkit-backdrop-filter: blur(calc(var(--nav-solid) * 12px));
  border-bottom: 1px solid rgba(21, 23, 26, calc(var(--nav-solid) * 0.08));
  padding-top: calc(1.5rem - var(--nav-solid) * 0.5rem);
  padding-bottom: calc(1.5rem - var(--nav-solid) * 0.5rem);
  transition: color 300ms ease;
}

.nav.is-solid {
  color: var(--ink);
}

/* Soft shade behind the header so the wordmark reads on a bright facade */
.nav::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 200px;
  background: linear-gradient(
    to bottom,
    rgba(10, 12, 15, 0.55) 0%,
    rgba(10, 12, 15, 0.3) 45%,
    rgba(10, 12, 15, 0) 100%
  );
  pointer-events: none;
  z-index: -1;
  opacity: calc(1 - var(--nav-solid));
}

.nav a {
  color: inherit;
  text-decoration: none;
}

.nav__logo {
  justify-self: start;
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.98rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.32em;
  line-height: 1;
  white-space: nowrap;
}

.nav__logo .mark {
  width: 26px;
  height: 26px;
  display: block;
}

.nav__logo .mark .a {
  stroke: currentColor;
}

.nav__logo .mark .r {
  stroke: var(--accent);
}

.nav__links {
  display: flex;
  gap: 2.25rem;
}

.lang {
  justify-self: end;
  display: inline-flex;
  gap: 0.45rem;
  align-items: center;
}

.lang a {
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  opacity: 0.45;
  transition: opacity 200ms ease;
}

.lang a:hover,
.lang a.is-current {
  opacity: 1;
}

.lang > span {
  opacity: 0.3;
  font-size: 0.72rem;
}

.nav__links a {
  position: relative;
  font-size: 0.78rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  opacity: 0.72;
  transition: opacity 200ms ease;
}

.nav__links a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -5px;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 280ms cubic-bezier(0.16, 1, 0.3, 1);
}

.nav__links a:hover,
.nav__links a.is-current {
  opacity: 1;
}

.nav__links a:hover::after,
.nav__links a.is-current::after {
  transform: scaleX(1);
}

/* Header entrance: wordmark fades, the menu drops in, the language switch
   comes in from the right edge */
@keyframes nav-fade-in {
  from { opacity: 0; }
}

@keyframes menu-drop {
  from { opacity: 0; transform: translateY(-40px); }
}

@keyframes pair-right {
  from { opacity: 0; transform: translateX(100vw); }
}

.nav__logo {
  animation: nav-fade-in 650ms cubic-bezier(0.16, 1, 0.3, 1) 150ms both;
}

/* each item drops in a beat after the one before it */
.nav__links a {
  animation: menu-drop 750ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

.nav__links a:nth-child(1) { animation-delay: 500ms; }
.nav__links a:nth-child(2) { animation-delay: 620ms; }
.nav__links a:nth-child(3) { animation-delay: 740ms; }

.lang {
  animation: pair-right 750ms cubic-bezier(0.16, 1, 0.3, 1) 640ms both;
}

@media (prefers-reduced-motion: reduce) {
  .nav__logo,
  .nav__links a,
  .lang {
    animation: none !important;
  }
}

@media (min-width: 821px) and (max-width: 1180px) {
  .nav {
    column-gap: 1.5rem;
  }

  .nav__logo {
    font-size: 0.86rem;
    letter-spacing: 0.24em;
  }

  .nav__links {
    gap: 1.4rem;
  }

  .nav__links a {
    font-size: 0.7rem;
  }
}

/* the call to action and the language switch inside the menu exist for
   the phone sheet only */
.nav .nav__cta,
.nav .lang--menu {
  display: none;
}

/* Burger: two lines that become a cross. Hidden on wide screens. */
.nav__burger {
  display: none;
  position: relative;
  z-index: 2;
  width: 44px;
  height: 44px;
  margin: -10px -10px -10px 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
}

.nav__burger span {
  position: absolute;
  left: 11px;
  right: 11px;
  top: 50%;
  height: 1.5px;
  background: currentColor;
  transition: transform 320ms cubic-bezier(0.16, 1, 0.3, 1);
}

.nav__burger span:nth-child(1) { transform: translateY(-4px); }
.nav__burger span:nth-child(2) { transform: translateY(4px); }

.nav.is-open .nav__burger span:nth-child(1) { transform: rotate(45deg); }
.nav.is-open .nav__burger span:nth-child(2) { transform: rotate(-45deg); }

@media (max-width: 820px) {
  .nav {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 0.9rem 1.1rem;
  }

  .nav__logo {
    position: relative;
    z-index: 2;
    font-size: 0.82rem;
    letter-spacing: 0.26em;
  }

  .nav__burger {
    display: block;
  }

  /* the menu is a sheet over the whole screen, links stacked in the middle,
     the language switch at the foot; the bar with the logo and the cross
     stays on top of it */
  /* The menu is a card that slides in from the right, under the bar: the
     items are set like the headings (thin, large, optically spaced by
     script.js) and their letters arrive one by one each time it opens. The
     current page is in the red of the logo. */
  /* joined to the top and right edges of the screen; the cross in the bar
     sits inside its top corner, so the card carries its own close */
  .nav__links {
    position: fixed;
    top: 0;
    right: 0;
    left: auto;
    z-index: 1;
    width: min(88vw, 380px);
    max-height: 100svh;
    overflow-y: auto;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    gap: 1.9rem;
    min-height: min(78svh, 640px);
    padding: 5.4rem 1.6rem 3.2rem;
    border-radius: 0 0 0 3px;
    background: var(--paper);
    color: var(--ink);
    /* a plain shadow below and to the left; the light neumorphic edge
       reads as a glow on a photograph */
    box-shadow: -16px 24px 60px rgba(21, 23, 26, 0.28);
    transform: translateX(calc(100% + 2rem));
    visibility: hidden;
    transition: transform 520ms cubic-bezier(0.7, 0, 0.3, 1), visibility 0s linear 520ms;
  }

  .nav__links > a:not(.nav__cta) {
    font-size: clamp(2.4rem, 12vw, 3.2rem);
    font-weight: 200;
    letter-spacing: 0;
    line-height: 1.1;
    text-align: center;
    opacity: 1;
    animation: none;
    transition: color 200ms ease;
  }

  .nav__links > a.is-current {
    color: var(--accent);
  }

  .nav__links > a::after {
    display: none;
  }

  .nav__links a .k-char {
    opacity: 0;
  }

  .nav.is-open .nav__links a .k-char {
    animation-duration: 900ms;
    animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
    animation-fill-mode: both;
  }

  .nav.is-open .nav__links a .k-char.v-fade  { animation-name: cut-fade; animation-duration: 1200ms; }
  .nav.is-open .nav__links a .k-char.v-drop  { animation-name: cut-drop; }
  .nav.is-open .nav__links a .k-char.v-rise  { animation-name: cut-rise; }
  .nav.is-open .nav__links a .k-char.v-left  { animation-name: cut-left; }
  .nav.is-open .nav__links a .k-char.v-right { animation-name: cut-right; }
  .nav.is-open .nav__links a .k-char.v-zoom  { animation-name: cut-zoom; }

  /* the call to action in the middle of the card, after the items */
  .nav .nav__cta {
    display: inline-block;
    margin-top: 1.8rem;
    font-size: 0.76rem;
    letter-spacing: 0.1em;
    opacity: 0;
    transform: translateY(10px);
    animation: none;
    transition: opacity 500ms ease, transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  .nav.is-open .nav__cta {
    opacity: 1;
    transform: none;
    transition-delay: 1150ms;
  }

  .nav__cta::after {
    display: none;
  }

  /* the language switch lives in the card on phones; the one in the bar is hidden */
  .nav > .lang {
    display: none;
  }

  .nav .lang--menu {
    display: inline-flex;
    gap: 0.45rem;
    margin-top: 2rem;
    opacity: 0;
    animation: none;
    transition: opacity 400ms ease;
  }

  .nav.is-open .lang--menu {
    opacity: 1;
    transition-delay: 1300ms;
  }

  .lang--menu a {
    font-size: 0.78rem;
    font-weight: 400;
    letter-spacing: 0.1em;
    opacity: 0.45;
    animation: none;
  }

  .lang--menu a.is-current {
    opacity: 1;
    color: var(--ink);
  }

  .lang--menu a::after {
    display: none;
  }

  /* only the cross turns dark: it sits on the card. The wordmark would
     run under the card's left edge, so it steps aside while the menu is open. */
  .nav.is-open .nav__burger {
    color: var(--ink);
  }

  .nav__logo {
    transition: opacity 250ms ease;
  }

  .nav.is-open .nav__logo {
    opacity: 0;
    pointer-events: none;
  }

  .nav.is-open .nav__links {
    transform: none;
    visibility: visible;
    transition: transform 520ms cubic-bezier(0.16, 1, 0.3, 1), visibility 0s;
  }

  @media (prefers-reduced-motion: reduce) {
    .nav__links a .k-char,
    .nav.is-open .nav__links a .k-char {
      animation: none;
      opacity: 1;
    }
  }

  /* backdrop blur repaints on every frame of the scroll and stutters on
     phones; the bar is nearly opaque anyway. The padding stays fixed too,
     so the bar does not relayout while the hero scrolls away. */
  .nav {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    /* the ground switches on at one point with a short fade, instead of
       following every scroll event */
    background: rgba(247, 245, 241, 0);
    border-bottom-color: transparent;
    padding-top: 0.9rem;
    padding-bottom: 0.9rem;
    transition: color 300ms ease, background-color 300ms ease;
  }

  .nav.is-solid {
    background: rgba(247, 245, 241, 0.96);
    border-bottom-color: rgba(21, 23, 26, 0.08);
  }

  /* no scroll-linked motion on phones: the photo and the pinned sections
     stay put, only the stacking remains */
  .stage {
    transform: none;
    opacity: 1;
    will-change: auto;
  }

  .stage__img {
    animation: none;
  }

  .block--pin > * {
    transform: none;
    opacity: 1;
  }
}

/* ---------- Hero ---------- */

.stage {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  /* the largest viewport: the photo must reach under the mobile toolbar
     when it collapses, without the hero changing height as it does */
  height: 100vh;
  height: 100lvh;
  overflow: hidden;
  user-select: none;
  /* the page colour, not dark: whatever shows before the photograph has
     arrived should look like the page, not like a black frame */
  background: var(--paper);
  /* Parallax: as the content scrolls up over it, the photograph moves up
     too, at about a fifth of the speed, and fades. --scroll runs 0 -> 1
     across the height of the hero, written by script.js on every scroll. */
  --scroll: 0;
  transform: translate3d(0, calc(var(--scroll) * -22vh), 0);
  opacity: calc(1 - var(--scroll) * 0.7);
  will-change: transform, opacity;
}

.stage__img {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  animation: hero-drift 26s ease-in-out infinite alternate;
}

@keyframes hero-drift {
  from { transform: scale(1.06) translate3d(0, 0, 0); }
  to   { transform: scale(1.13) translate3d(-1.2%, -1%, 0); }
}

/* The facade is almost white, so the veil is heavier at the bottom where
   the title sits, and light at the top so the house keeps its brightness */
.scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom, rgba(10, 12, 15, 0.28) 0%, rgba(10, 12, 15, 0) 35%),
    /* the title sits lower left, so the left half carries most of the veil */
    linear-gradient(to right, rgba(10, 12, 15, 0.72) 0%, rgba(10, 12, 15, 0.42) 38%, rgba(10, 12, 15, 0) 68%),
    linear-gradient(to top, rgba(10, 12, 15, 0.7) 0%, rgba(10, 12, 15, 0.32) 40%, rgba(10, 12, 15, 0) 65%);
  pointer-events: none;
}

.section {
  position: absolute;
  inset: 0;
  display: flex;
  padding: 8rem clamp(2rem, 5vw, 4.5rem) clamp(7.5rem, 16vh, 12rem);
  pointer-events: none;
  color: #fff;
}

/* Home: title in the lower left, over the lawn and the darkest part of the veil */
.section--home {
  align-items: flex-end;
  justify-content: flex-start;
  text-align: left;
}

.section--band {
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 7rem clamp(2rem, 5vw, 4.5rem) 3rem;
}

.section--band .content {
  max-width: 760px;
}

.section--band h1 {
  font-size: clamp(2.2rem, 5vw, 3.8rem);
}

.section--band p {
  margin-bottom: 0;
  max-width: 560px;
  margin-left: auto;
  margin-right: auto;
}

.content {
  max-width: 720px;
  pointer-events: auto;
}

.h1-line {
  display: block;
}

.h1-word {
  display: inline-block;
  white-space: nowrap;
}

/* The title letters arrive the way the intro letters do: each one is dealt
   an entrance and a delay by script.js, so the line assembles from all
   directions. The keyframes are shared with the intro (cut-*). */
.k-word {
  display: inline-block;
  white-space: nowrap;
}

.k-char {
  display: inline-block;
}

.h1-char {
  opacity: 0;
}

.is-visible .h1-char {
  animation-duration: 1000ms;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  animation-fill-mode: both;
}

.is-visible .h1-char.v-fade  { animation-name: cut-fade; animation-duration: 1400ms; }
.is-visible .h1-char.v-drop  { animation-name: cut-drop; }
.is-visible .h1-char.v-rise  { animation-name: cut-rise; }
.is-visible .h1-char.v-left  { animation-name: cut-left; }
.is-visible .h1-char.v-right { animation-name: cut-right; }
.is-visible .h1-char.v-zoom  { animation-name: cut-zoom; }

.section .content p {
  opacity: 0;
}

.section.is-visible .content p {
  opacity: 1;
}

/* on the inner pages the line under the title rises from below, starting
   about halfway through the title assembling itself */
@keyframes sub-rise {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: none; }
}

/* an animation, not a transition: the band is already in its final state
   when the page loads, so there is no change for a transition to follow */
.section--band.is-visible .content p {
  opacity: 1;
  animation: sub-rise 900ms cubic-bezier(0.16, 1, 0.3, 1) 900ms both;
}

@media (prefers-reduced-motion: reduce) {
  .section--band.is-visible .content p {
    animation: none;
  }
}

/* the button simply fades in once the paragraph has been typed */
.section .btn {
  opacity: 0;
}

.section .btn.is-shown {
  opacity: 1;
  transition:
    opacity 900ms ease,
    color 400ms ease;
}

@media (prefers-reduced-motion: reduce) {
  .h1-char,
  .is-visible .h1-char,
  .section .btn,
  .content .eyebrow {
    animation: none !important;
    transition: none !important;
    transform: none !important;
    opacity: 1 !important;
  }

  .stage__img {
    animation: none;
  }
}

/* The line above the title waits off the left edge of the screen and slides
   in, no fade, once the last letter of the title has landed */
.content .eyebrow {
  color: #fff;
  opacity: 0.9;
  transform: translateX(-100vw);
}

.content .eyebrow.is-shown {
  transform: none;
  transition: transform 1000ms cubic-bezier(0.16, 1, 0.3, 1);
}

.content h1 {
  font-weight: 300;
  font-size: clamp(2.4rem, 6.2vw, 5.4rem);
  text-transform: uppercase;
  letter-spacing: 0; /* script.js spaces the letters by their shapes */
  line-height: 1.02;
  margin-bottom: 1.2rem;
  text-shadow: 0 2px 6px rgba(10, 12, 15, 0.45), 0 8px 40px rgba(10, 12, 15, 0.55);
}

.content p {
  font-size: 1.04rem;
  font-weight: 400;
  line-height: 1.75;
  letter-spacing: 0.01em;
  margin-bottom: 1.75rem;
  min-height: 1.75em;
  max-width: 520px;
  text-shadow: 0 1px 4px rgba(10, 12, 15, 0.5), 0 4px 24px rgba(10, 12, 15, 0.55);
}

/* Soft button, after the Uiverse pattern by xueyuantan: the ground colour
   with a light shadow top left and a dark one bottom right, so it looks
   pressed out of the page. On hover a dark fill wipes in from the left and
   the label turns light. */
.btn {
  position: relative;
  z-index: 1;
  overflow: hidden;
  display: inline-block;
  padding: 0.95rem 2.3rem;
  border: 0;
  border-radius: 3px;
  background: var(--paper);
  color: var(--ink);
  text-decoration: none;
  font-size: 0.76rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  text-shadow: none;
  box-shadow:
    6px 6px 12px rgba(21, 23, 26, 0.14),
    -6px -6px 12px #ffffff;
  transition: color 400ms ease;
}

/* The fill is a circle that grows out of the middle of the button until
   it has flooded it. Its height is set through padding, in the button's
   own width, so it stays a circle at any size. */
/* on the darker sections the button takes that ground, so before its
   shadows grow it is invisible there too */
.block--tint .btn {
  background: var(--paper-2);
}

.btn::before {
  content: "";
  position: absolute;
  /* script.js writes --x / --y where the pointer entered; centre otherwise */
  top: var(--y, 50%);
  left: var(--x, 50%);
  width: 0;
  padding-bottom: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: radial-gradient(circle, #2e3238 0%, var(--ink) 70%);
  transition: width 550ms cubic-bezier(0.16, 1, 0.3, 1), padding-bottom 550ms cubic-bezier(0.16, 1, 0.3, 1);
  z-index: -1;
}

.btn:hover {
  color: var(--paper);
}

.btn:hover::before {
  /* large enough to cover the far corner from wherever it started */
  width: 260%;
  padding-bottom: 260%;
}

/* On the photograph there is no flat ground for the two shadows to sit on,
   so the hero button is a pane of frosted glass with a light edge instead.
   The wipe is white here, and the label turns dark. */
.section .btn {
  background: rgba(255, 255, 255, 0.09);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: #fff;
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.55),
    inset 0 0 0 1px rgba(255, 255, 255, 0.28),
    0 10px 24px rgba(10, 12, 15, 0.3);
}

.section .btn::before {
  background: radial-gradient(circle, #ffffff 0%, var(--paper) 70%);
}

.section .btn:hover {
  color: var(--ink);
}

.scroll-cue {
  position: absolute;
  right: clamp(2rem, 5vw, 4.5rem);
  bottom: 2rem;
  color: #fff;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.24em;
  opacity: 0;
  animation: cue-in 800ms ease 2400ms forwards;
}

.scroll-cue::after {
  content: "";
  display: block;
  width: 1px;
  height: 42px;
  margin: 0.7rem auto 0;
  background: linear-gradient(to bottom, rgba(255, 255, 255, 0.85), rgba(255, 255, 255, 0));
  animation: cue-pulse 2.4s ease-in-out infinite;
}

@keyframes cue-in {
  to { opacity: 0.75; }
}

@keyframes cue-pulse {
  0%, 100% { transform: scaleY(1); opacity: 1; }
  50% { transform: scaleY(0.55); opacity: 0.4; }
}

@media (prefers-reduced-motion: reduce) {
  .scroll-cue,
  .scroll-cue::after {
    animation: none;
    opacity: 0.7;
  }
}

/* ---------- Intro: the name cut out of a white sheet ----------

   A white layer covers the photograph and the letters are holes in it.
   `mix-blend-mode: screen` keeps white as white and lets the backdrop
   through wherever the layer is black, so black type on a white sheet
   turns into the photograph underneath. Real HTML text, same Jost as the
   wordmark, so the two words can later fly onto the header letter for
   letter. Ported from the studio-autori intro. */

.cutout {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: #fff;
  mix-blend-mode: screen;
  pointer-events: none;
  overflow: hidden;
  padding: 0 clamp(0.5rem, 2vw, 2rem);
}

.cutout__stack {
  display: block;
}

/* Same weight and the same 0.32em tracking as the header wordmark, so a
   uniform scale carries a line exactly onto its counterpart in the header.
   The tracking makes the word wide, so the size is capped by the width
   first; the vertical stretch from script.js fills the screen height. */
.cutout__type {
  /* the same weight and tracking as the wordmark, and no stretching, so
     the letters keep exactly the shape they have in the logo */
  font-weight: 400;
  color: #000;
  text-transform: uppercase;
  font-size: min(15vw, 38vh);
  line-height: 1.12;
  /* tighter than the wordmark so the name can be bigger; the flight scales
     the whole line, so the letters still land on their pairs in the header */
  letter-spacing: 0.14em;
  text-align: center;
  text-indent: 0.14em; /* balance the trailing letter-space */
  display: block;
  white-space: nowrap;
}

.cut-char {
  display: inline-block;
  opacity: 0;
}

.cutout.is-ready .cut-char {
  animation-duration: 1000ms;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  animation-fill-mode: both;
}

.cutout.is-ready .cut-char.v-fade  { animation-name: cut-fade; animation-duration: 1400ms; }
.cutout.is-ready .cut-char.v-drop  { animation-name: cut-drop; }
.cutout.is-ready .cut-char.v-rise  { animation-name: cut-rise; }
.cutout.is-ready .cut-char.v-left  { animation-name: cut-left; }
.cutout.is-ready .cut-char.v-right { animation-name: cut-right; }
.cutout.is-ready .cut-char.v-zoom  { animation-name: cut-zoom; }

@keyframes cut-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes cut-drop {
  from { opacity: 1; transform: translateY(-115vh); }
  to   { opacity: 1; transform: none; }
}

@keyframes cut-rise {
  from { opacity: 1; transform: translateY(115vh); }
  to   { opacity: 1; transform: none; }
}

@keyframes cut-left {
  from { opacity: 1; transform: translateX(-110vw); }
  to   { opacity: 1; transform: none; }
}

@keyframes cut-right {
  from { opacity: 1; transform: translateX(110vw); }
  to   { opacity: 1; transform: none; }
}

@keyframes cut-zoom {
  from { opacity: 0; transform: scale(0.2); }
  to   { opacity: 1; transform: none; }
}

/* Handover: the white ground turns transparent while the letters turn from
   black to white, so the name survives the sheet and flies to the header */
.cutout {
  transition: background-color 1000ms ease;
}

.cutout.is-gone {
  background-color: transparent;
}

.cutout__type {
  transition:
    transform 1250ms cubic-bezier(0.5, 0, 0.2, 1),
    color 1000ms ease;
}

.cutout.is-gone .cutout__type {
  color: #fff;
}

.cutout.is-done {
  display: none;
}

/* Skipped intro: the inline script drops has-intro before the first paint,
   and this rule keeps the sheet from ever being drawn. It must not wait for
   script.js at the end of the page, because the browser may paint before
   that script runs. */
body:not(.has-intro) .cutout {
  display: none;
}

.logo-word {
  display: inline-block;
}

/* While the sequence runs, everything belonging to the hero is held back */
.scrim {
  transition: opacity 950ms ease;
}

body.has-intro .scrim {
  opacity: 0;
}

body.has-intro.intro-out .scrim {
  opacity: 1;
}

/* The facade is nearly white, so the letters cut out of a white sheet would
   barely show. A dark veil sits under the sheet while the name assembles,
   then lifts over the length of the flight, so the photograph brightens as
   the letters travel to the header. */
.intro-veil {
  position: absolute;
  inset: 0;
  background: rgba(10, 12, 15, 0.58);
  opacity: 0;
  pointer-events: none;
  transition: opacity 1250ms ease;
}

body.has-intro .intro-veil {
  opacity: 1;
}

body.has-intro.intro-out .intro-veil {
  opacity: 0;
}

body.has-intro .nav::before {
  opacity: 0;
}

body.has-intro .nav__logo,
body.has-intro .nav__links a,
body.has-intro .lang,
body.has-intro .scroll-cue {
  animation-play-state: paused;
}

@media (prefers-reduced-motion: reduce) {
  .cut-char,
  .cutout.is-ready .cut-char {
    animation: none;
    opacity: 1;
    transform: none;
  }
}

/* ---------- Content scrolling over the fixed hero ---------- */

.pages {
  position: relative;
  z-index: 5;
  /* the smallest viewport, so the content does not jump when the mobile
     toolbar collapses; it overlaps the foot of the photo instead */
  margin-top: 100vh;
  margin-top: 100svh;
  background: var(--paper);
}

.stage--band {
  height: 58vh;
  height: 58svh;
}

.stage--band .scrim {
  background:
    linear-gradient(to bottom, rgba(10, 12, 15, 0.62) 0%, rgba(10, 12, 15, 0.3) 45%, rgba(10, 12, 15, 0.55) 100%),
    radial-gradient(ellipse 70% 60% at 50% 45%, rgba(10, 12, 15, 0.42) 0%, rgba(10, 12, 15, 0) 100%);
}

.pages--band {
  margin-top: 58vh;
  margin-top: 58svh;
}

.block {
  padding: 6.5rem clamp(1.5rem, 5vw, 4.5rem);
  /* every section has its own opaque ground: the pinned section stays
     underneath the ones that slide over it, and must not show through */
  background: var(--paper);
}

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

.block--tint {
  background: var(--paper-2);
}

/* ---------- Pinned sections ----------
   Every section sticks to the top of the window while the next one slides
   up over it, the way the content slides over the hero. Later sections
   paint over earlier ones by document order, each on its own opaque
   ground with a soft shadow along its top edge. --cover runs 0 -> 1 as the
   next section climbs the window (script.js), and the pinned content
   drifts up and fades under it, as the hero does. */
.block--pin {
  position: sticky;
  top: 0;
  --cover: 0;
  box-shadow: 0 -24px 60px rgba(21, 23, 26, 0.08);
}

.block--pin > * {
  width: 100%;
  transform: translate3d(0, calc(var(--cover) * -12vh), 0);
  opacity: calc(1 - var(--cover) * 0.7);
}

/* the intro fills the window, with its content centred */
.block--tall {
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
}

.block__inner {
  max-width: 680px;
  margin: 0 auto;
  text-align: center;
}

.block__inner--wide {
  max-width: 1280px;
}

.block__inner--left {
  text-align: left;
  margin: 0 auto;
}

.block h2 {
  font-weight: 200;
  font-size: clamp(1.9rem, 3.8vw, 2.9rem);
  text-transform: uppercase;
  letter-spacing: 0; /* script.js spaces the letters by their shapes */
  line-height: 1.1;
  margin-bottom: 1.25rem;
}

.block p {
  font-size: 1.04rem;
  line-height: 1.8;
  letter-spacing: 0.01em;
  color: #3b3f45;
}

.block p + p {
  margin-top: 1.1rem;
}

.block .lede {
  font-size: 1.25rem;
  line-height: 1.6;
  color: var(--ink);
  margin-bottom: 1.4rem;
}

.block .btn {
  margin-top: 2rem;
}

.eyebrow {
  display: block;
  font-size: 0.66rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.26em;
  color: var(--muted);
  margin-bottom: 1.2rem;
}

/* a short red rule, the logo's stroke, used as the section marker */
.eyebrow::before {
  content: "";
  display: inline-block;
  width: 22px;
  height: 1px;
  background: var(--accent);
  vertical-align: middle;
  margin-right: 0.7rem;
}

.block__inner:not(.block__inner--left) .eyebrow::before {
  display: none;
}

/* Reveal on scroll */
.reveal {
  opacity: 0;
  transform: translateY(26px);
}

.reveal.is-in {
  opacity: 1;
  transform: none;
  transition:
    opacity 620ms ease,
    transform 720ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--d, 0) * 80ms);
}

/* Cards hinge up from their bottom edge, as on the reference site */
.card.reveal {
  opacity: 1;
  transform: none;
}

.card.reveal .card__media {
  transform-origin: 50% 100%;
  transform: perspective(900px) rotateX(90deg);
}

.card.reveal.is-in .card__media {
  transform: perspective(900px) rotateX(0deg);
  transition: transform 1150ms cubic-bezier(0.22, 1.32, 0.36, 1);
  transition-delay: calc(var(--d, 0) * 110ms);
}

.card.reveal .card__body {
  opacity: 0;
  transform: translateY(16px);
}

.card.reveal.is-in .card__body {
  opacity: 1;
  transform: none;
  transition:
    opacity 500ms ease,
    transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--d, 0) * 110ms + 260ms);
}

@media (prefers-reduced-motion: reduce) {
  .reveal,
  .card.reveal,
  .card.reveal .card__media,
  .card.reveal .card__body {
    opacity: 1;
    transform: none;
  }
}

/* ---------- Home: intro in two columns ---------- */

.intro {
  display: grid;
  grid-template-columns: 1fr 1.4fr;
  gap: clamp(2rem, 6vw, 6rem);
  align-items: start;
  max-width: 1280px;
  margin: 0 auto;
  text-align: left;
}

.intro h2 {
  margin-bottom: 0;
  font-size: clamp(2.2rem, 4.6vw, 3.6rem);
}

/* ---------- Sequenced column ----------
   A .seq block runs its children in order once it scrolls into view: the
   words of the lead paragraph surface one after another, the second
   paragraph rises as a whole, then the button comes up out of the page (it
   starts flat, the colour of the ground, and its shadows grow) and its
   label appears last. The block itself neither fades nor moves. */
.seq.reveal,
.seq.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: none;
}

.seq .w {
  display: inline-block;
  opacity: 0;
  transform: translateY(0.55em);
}

.seq.is-in .w {
  opacity: 1;
  transform: none;
  transition:
    opacity 500ms ease,
    transform 700ms cubic-bezier(0.16, 1, 0.3, 1);
  /* the heading letters land first, then the words start */
  transition-delay: calc(800ms + var(--i, 0) * 32ms);
}

.seq .rise {
  opacity: 0;
  transform: translateY(18px);
}

.seq.is-in .rise {
  opacity: 1;
  transform: none;
  transition:
    opacity 700ms ease,
    transform 800ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: var(--t, 1600ms);
}

/* a list whose items surface one after another, like the words above */
.seq .rise-items li {
  opacity: 0;
  transform: translateY(0.55em);
}

.seq.is-in .rise-items li {
  opacity: 1;
  transform: none;
  transition:
    opacity 500ms ease,
    transform 700ms cubic-bezier(0.16, 1, 0.3, 1);
}

.seq.is-in .rise-items li:nth-child(1) { transition-delay: var(--t, 1600ms); }
.seq.is-in .rise-items li:nth-child(2) { transition-delay: calc(var(--t, 1600ms) + 140ms); }
.seq.is-in .rise-items li:nth-child(3) { transition-delay: calc(var(--t, 1600ms) + 280ms); }
.seq.is-in .rise-items li:nth-child(4) { transition-delay: calc(var(--t, 1600ms) + 420ms); }

/* a line above the heading slides in from the left, after the letters */
.seq .slide-in {
  opacity: 0;
  transform: translateX(-60px);
}

.seq.is-in .slide-in {
  opacity: 1;
  transform: none;
  transition:
    opacity 500ms ease,
    transform 800ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: 900ms;
}

.seq .btn--emerge {
  box-shadow:
    0 0 0 rgba(21, 23, 26, 0),
    0 0 0 rgba(255, 255, 255, 0);
}

.seq.is-in .btn--emerge {
  box-shadow:
    6px 6px 12px rgba(21, 23, 26, 0.14),
    -6px -6px 12px #ffffff;
  /* --e is when the button starts to come up; the label follows 400ms later */
  transition:
    box-shadow 1000ms cubic-bezier(0.16, 1, 0.3, 1) var(--e, 2200ms),
    color 400ms ease;
}

.seq .btn__label {
  opacity: 0;
}

.seq.is-in .btn__label {
  opacity: 1;
  transition: opacity 600ms ease calc(var(--e, 2200ms) + 400ms);
}

@media (prefers-reduced-motion: reduce) {
  .seq .w,
  .seq .rise,
  .seq .rise-items li,
  .seq .slide-in,
  .seq .btn__label {
    opacity: 1;
    transform: none;
  }

  .seq .btn--emerge {
    box-shadow:
      6px 6px 12px rgba(21, 23, 26, 0.14),
      -6px -6px 12px #ffffff;
  }
}

/* Headings marked .h-fly assemble letter by letter when they scroll into
   view, with the same random entrances as the hero title. The heading
   itself does not fade or move; only its letters do. */
.h-fly.reveal,
.h-fly.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: none;
}

/* until script.js has split the heading into letters it is plain text, and
   must not be seen: the first time it appears is letter by letter */
.h-fly:not(.is-split),
#hero h1:not(.is-split) {
  visibility: hidden;
}

.h-fly .k-char {
  opacity: 0;
}

.h-fly.is-in .k-char {
  animation-duration: 1000ms;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
  animation-fill-mode: both;
}

.h-fly.is-in .k-char.v-fade  { animation-name: cut-fade; animation-duration: 1400ms; }
.h-fly.is-in .k-char.v-drop  { animation-name: cut-drop; }
.h-fly.is-in .k-char.v-rise  { animation-name: cut-rise; }
.h-fly.is-in .k-char.v-left  { animation-name: cut-left; }
.h-fly.is-in .k-char.v-right { animation-name: cut-right; }
.h-fly.is-in .k-char.v-zoom  { animation-name: cut-zoom; }

@media (prefers-reduced-motion: reduce) {
  .h-fly .k-char,
  .h-fly.is-in .k-char {
    animation: none;
    opacity: 1;
  }
}

@media (max-width: 860px) {
  .intro {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }
}

/* ---------- Services ---------- */

/* The four services as the volumes of the Zemun house: flat blocks of
   different heights, set off against each other, one white plaster cube
   carrying the composition, a box pushed out above it like the covered
   terrace, a dark one like the big window, a low one at the right. Twelve
   columns, two rows, each block placed by hand. */
.services {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-template-rows: auto auto;
  gap: 1.25rem;
  margin-top: 3.5rem;
  text-align: left;
  align-items: start;
}

.service {
  padding: 2.4rem 2rem 2.6rem;
  border-radius: 3px;
  background: var(--paper-2);
  box-shadow:
    10px 10px 24px rgba(21, 23, 26, 0.12),
    -10px -10px 24px rgba(255, 255, 255, 0.95);
  transition: box-shadow 300ms ease;
}

/* on hover the card settles halfway into the page, as the buttons do */
.service:hover {
  box-shadow:
    5px 5px 12px rgba(21, 23, 26, 0.1),
    -5px -5px 12px rgba(255, 255, 255, 0.9);
}

/* Across the whole width, like the skyline of the house: a low block, the
   tall main cube, a box pushed out above it, and a low block at the right */

/* 01: low volume at the left, resting on the ground line */
.service:nth-child(1) {
  grid-column: 1 / 4;
  grid-row: 2;
  align-self: end;
}

/* 02: the main cube, tallest, spans both rows */
.service:nth-child(2) {
  grid-column: 4 / 8;
  grid-row: 1 / 3;
  align-self: stretch;
  padding-top: 3rem;
  padding-bottom: 4rem;
}

/* 03: the box pushed out over the top right, lifted above the row so it
   overhangs the one below */
.service:nth-child(3) {
  grid-column: 8 / 11;
  grid-row: 1;
  margin-top: -1.75rem;
}

/* 04: low volume at the right edge, tucked under the overhang */
.service:nth-child(4) {
  grid-column: 10 / 13;
  grid-row: 2;
  align-self: end;
  margin-top: 1.5rem;
}

/* Each card comes up out of the page one after another, the way the
   buttons do: it starts flat and the colour of the ground, its shadows
   grow, then its content appears. The scroll watcher sets --d in reading
   order. Animations, not transitions, so the hover keeps its own speed
   once a card is up. */
.service.reveal {
  opacity: 1;
  transform: none;
  transition: box-shadow 300ms ease;
}

.service.reveal:not(.is-in) {
  box-shadow: none;
}

.service.reveal > * {
  opacity: 0;
}

@keyframes card-up {
  from {
    box-shadow:
      0 0 0 rgba(21, 23, 26, 0),
      0 0 0 rgba(255, 255, 255, 0);
  }
}

@keyframes card-content {
  from { opacity: 0; }
}

.service.reveal.is-in {
  animation: card-up 1000ms cubic-bezier(0.16, 1, 0.3, 1) backwards;
  animation-delay: calc(var(--d, 0) * 350ms);
}

.service.reveal.is-in > * {
  opacity: 1;
  animation: card-content 600ms ease backwards;
  animation-delay: calc(var(--d, 0) * 350ms + 400ms);
}

@media (prefers-reduced-motion: reduce) {
  .service.reveal:not(.is-in) {
    box-shadow:
      10px 10px 24px rgba(21, 23, 26, 0.12),
      -10px -10px 24px rgba(255, 255, 255, 0.95);
  }

  .service.reveal > *,
  .service.reveal.is-in,
  .service.reveal.is-in > * {
    opacity: 1;
    animation: none;
  }
}

.service__num {
  display: block;
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  color: var(--accent);
  margin-bottom: 2.6rem;
}

.service h3 {
  font-weight: 400;
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  line-height: 1.35;
  margin-bottom: 0.8rem;
}

.service p {
  font-size: 0.94rem;
  line-height: 1.7;
}

/* narrower: two columns, the composition loosens but keeps its offsets */
@media (max-width: 960px) {
  .services {
    grid-template-columns: 1fr 1fr;
    grid-template-rows: auto;
  }

  .service:nth-child(1) { grid-column: 1; grid-row: 1; align-self: end; margin-top: 2rem; }
  .service:nth-child(2) { grid-column: 2; grid-row: 1 / 3; }
  .service:nth-child(3) { grid-column: 1; grid-row: 2; margin-top: 0; }
  .service:nth-child(4) { grid-column: 1 / 3; grid-row: 3; margin-top: 0; align-self: auto; }
}

@media (max-width: 520px) {
  .services {
    grid-template-columns: 1fr;
  }

  .service:nth-child(n) {
    grid-column: 1;
    grid-row: auto;
    margin-top: 0;
    align-self: auto;
  }
}


/* ---------- Featured project ---------- */

.feature {
  display: grid;
  grid-template-columns: 1.25fr 1fr;
  gap: clamp(1.5rem, 4vw, 4rem);
  align-items: center;
  max-width: 1280px;
  margin: 0 auto;
  text-align: left;
}

.feature__media {
  display: block;
  overflow: hidden;
  aspect-ratio: 3 / 2;
  background: var(--paper-2);
}

.feature__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 1100ms cubic-bezier(0.16, 1, 0.3, 1);
}

.feature__media:hover img {
  transform: scale(1.04);
}

/* The photographs open from a horizontal line through their middle,
   stretching up and down at once until the whole frame is uncovered */
.feature__media.reveal {
  opacity: 1;
  transform: none;
  clip-path: inset(50% 0 50% 0);
}

.feature__media.reveal.is-in {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1000ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--d, 0) * 140ms);
}

@media (prefers-reduced-motion: reduce) {
  .feature__media.reveal {
    clip-path: none;
  }
}

.feature__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  margin-top: 1rem;
}

.feature__pair .feature__media {
  aspect-ratio: 4 / 3;
}

.feature__text h2 {
  margin-bottom: 1rem;
}

.feature__meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.6rem;
  margin: 1.4rem 0 0;
  padding-top: 1.2rem;
  border-top: 1px solid var(--line);
  list-style: none;
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}

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

/* ---------- Process ---------- */

.steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  margin-top: 3rem;
  text-align: left;
  counter-reset: step;
}

.step {
  position: relative;
  padding-top: 1.2rem;
}

/* the rule above each step is drawn from left to right when the step comes
   into view, one step after another, then the number, title and text rise */
.step::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--ink);
  transform-origin: left;
}

.step.reveal {
  opacity: 1;
  transform: none;
}

.step.reveal::after {
  transform: scaleX(0);
}

.step.reveal.is-in::after {
  transform: scaleX(1);
  transition: transform 900ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: 400ms; /* all three rules at once */
}

.step.reveal > * {
  opacity: 0;
  transform: translateY(14px);
}

.step.reveal.is-in > * {
  opacity: 1;
  transform: none;
  transition:
    opacity 600ms ease,
    transform 700ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* the number is the ::before counter, so it is timed separately */
.step.reveal::before {
  opacity: 0;
}

/* one step at a time: --s is the step's own place in the row, not its
   place in the batch of everything coming into view together */
.step:nth-child(1) { --s: 0; }
.step:nth-child(2) { --s: 1; }
.step:nth-child(3) { --s: 2; }

.step.reveal.is-in::before {
  opacity: 1;
  transition: opacity 600ms ease;
  transition-delay: calc(700ms + var(--s, 0) * 600ms);
}

.step.reveal.is-in > :nth-child(1) { transition-delay: calc(860ms + var(--s, 0) * 600ms); }
.step.reveal.is-in > :nth-child(2) { transition-delay: calc(1020ms + var(--s, 0) * 600ms); }

@media (prefers-reduced-motion: reduce) {
  .step.reveal::after,
  .step.reveal::before,
  .step.reveal > * {
    transform: none;
    opacity: 1;
  }
}

.step::before {
  counter-increment: step;
  content: "0" counter(step);
  display: block;
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  color: var(--accent);
  margin-bottom: 1.2rem;
}

.step h3 {
  font-weight: 400;
  font-size: 1.1rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 0.6rem;
}

.step p {
  font-size: 0.96rem;
  line-height: 1.7;
}

@media (max-width: 760px) {
  .steps {
    grid-template-columns: 1fr;
    gap: 2rem;
  }
}

/* ---------- Project grid (rows fill left to right) ---------- */

.grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1.75rem 1.25rem;
}

.card {
  display: block;
  color: var(--ink);
  text-decoration: none;
}

.card__media {
  display: block;
  position: relative;
  overflow: hidden;
  aspect-ratio: 4 / 5;
  background: var(--paper-2);
}

.card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 900ms cubic-bezier(0.16, 1, 0.3, 1);
}

.card:hover .card__media img {
  transform: scale(1.045);
}

.card__body {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.85rem 0.1rem 0;
}

.card__title {
  font-size: 0.86rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.11em;
}

.card__meta {
  font-size: 0.72rem;
  letter-spacing: 0.09em;
  color: var(--muted);
  white-space: nowrap;
}

/* Placeholder cards for projects still to be added */
.card--soon {
  pointer-events: none;
}

.card--soon .card__media {
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    repeating-linear-gradient(135deg, transparent 0 14px, rgba(21, 23, 26, 0.035) 14px 15px),
    var(--paper-2);
  font-size: 0.66rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
}

.card--soon .card__title {
  color: var(--muted);
}

@media (max-width: 900px) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1.1rem 0.9rem;
  }
}

/* phones keep two projects per row, with a tighter gap and smaller captions */
@media (max-width: 560px) {
  .grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 1.2rem 0.7rem;
  }

  .card__body {
    flex-direction: column;
    align-items: flex-start;
    text-align: left;
    gap: 0.15rem;
    padding-top: 0.6rem;
  }

  .card__title {
    font-size: 0.72rem;
    letter-spacing: 0.08em;
  }

  .card__meta {
    font-size: 0.64rem;
  }
}

/* ---------- Project page ---------- */

.project-head {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 clamp(1.5rem, 5vw, 4.5rem);
}

.project-layout {
  display: grid;
  grid-template-columns: 1fr 260px;
  gap: 3rem;
  align-items: start;
}

.project-body p {
  font-size: 1.02rem;
  line-height: 1.85;
  color: #3b3f45;
}

.project-body p + p {
  margin-top: 1.1rem;
}

.facts {
  display: grid;
  gap: 1.2rem;
  border-top: 1px solid var(--ink);
  padding-top: 1.4rem;
}

.fact__label {
  display: block;
  font-size: 0.64rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--muted);
  margin-bottom: 0.35rem;
}

.fact__value {
  font-size: 0.94rem;
  line-height: 1.5;
}

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

/* Gallery: a real grid, rows fill left to right */
.gallery {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  /* tall cells span two rows, dense lets the next photo close the gap */
  grid-auto-flow: dense;
  gap: 0.9rem;
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 clamp(1.5rem, 5vw, 4.5rem);
}

.gallery button {
  display: block;
  padding: 0;
  border: 0;
  background: var(--paper-2);
  cursor: zoom-in;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}

/* a portrait photo takes a full-height cell so it is not cropped to a strip */
.gallery button.is-tall {
  grid-row: span 2;
  aspect-ratio: auto;
}

.gallery img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition:
    transform 900ms cubic-bezier(0.16, 1, 0.3, 1),
    translate 900ms cubic-bezier(0.16, 1, 0.3, 1);
}

.gallery button.reveal {
  opacity: 1;
  transform: none;
  clip-path: inset(100% 0 0 0);
}

.gallery button.reveal img {
  translate: 0 -18%;
}

.gallery button.reveal.is-in {
  clip-path: inset(0 0 0 0);
  transition: clip-path 720ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--d, 0) * 70ms);
}

.gallery button.reveal.is-in img {
  translate: 0 0;
  transition-delay: 0ms, calc(var(--d, 0) * 70ms);
}

@media (prefers-reduced-motion: reduce) {
  .gallery button.reveal {
    clip-path: none;
  }

  .gallery button.reveal img {
    translate: none;
  }
}

.gallery button:hover img {
  transform: scale(1.04);
}

@media (max-width: 900px) {
  .gallery {
    grid-template-columns: repeat(2, 1fr);
    gap: 0.6rem;
  }
}

@media (max-width: 560px) {
  .gallery {
    grid-template-columns: 1fr;
  }

  .gallery button.is-tall {
    grid-row: auto;
    aspect-ratio: 3 / 4;
  }
}

.project-nav {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  max-width: 1280px;
  margin: 5rem auto 0;
  padding: 3rem clamp(1.5rem, 5vw, 4.5rem) 0;
  border-top: 1px solid var(--line);
}

.project-nav a {
  color: var(--ink);
  text-decoration: none;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  opacity: 0.65;
  transition: opacity 200ms ease;
}

.project-nav a:hover {
  opacity: 1;
}

/* ---------- Lightbox ---------- */

.lightbox {
  position: fixed;
  inset: 0;
  z-index: 50;
  background: rgba(247, 245, 241, 0.97);
  display: none;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity 260ms ease;
}

.lightbox.is-open {
  display: flex;
  opacity: 1;
}

.lightbox img {
  max-width: 92vw;
  max-height: 88vh;
  object-fit: contain;
  display: block;
  box-shadow: 0 30px 80px rgba(21, 23, 26, 0.18);
}

.lightbox__close,
.lightbox__prev,
.lightbox__next {
  position: absolute;
  background: none;
  border: 0;
  color: var(--ink);
  font-family: inherit;
  font-size: 1.6rem;
  line-height: 1;
  cursor: pointer;
  opacity: 0.55;
  padding: 1rem;
  transition: opacity 200ms ease;
}

.lightbox__close:hover,
.lightbox__prev:hover,
.lightbox__next:hover {
  opacity: 1;
}

.lightbox__close {
  top: 1rem;
  right: 1.4rem;
  font-size: 1.9rem;
}

.lightbox__prev {
  left: 0.6rem;
}

.lightbox__next {
  right: 0.6rem;
}

.lightbox__count {
  position: absolute;
  bottom: 1.3rem;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  color: var(--muted);
}

/* ---------- Stats ---------- */

.stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2rem;
  --fly: 70vw;
  overflow: hidden;
  padding: 0.4rem 0;
}

.stats .stat.reveal:nth-child(odd) {
  transform: translateX(var(--fly));
}

.stats .stat.reveal:nth-child(even) {
  transform: translateX(calc(var(--fly) * -1));
}

.stats .stat.reveal.is-in {
  opacity: 1;
  transform: none;
  transition:
    opacity 300ms ease,
    transform 1000ms cubic-bezier(0.5, 0, 0.15, 1);
}

.stats .stat.reveal:nth-child(1).is-in { transition-delay: 0ms; }
.stats .stat.reveal:nth-child(2).is-in { transition-delay: 60ms; }
.stats .stat.reveal:nth-child(3).is-in { transition-delay: 120ms; }
.stats .stat.reveal:nth-child(4).is-in { transition-delay: 180ms; }

@media (prefers-reduced-motion: reduce) {
  .stats .stat.reveal {
    transform: none;
    opacity: 1;
  }
}

.stat__num {
  display: block;
  font-weight: 200;
  font-size: clamp(2.4rem, 5vw, 3.6rem);
  line-height: 1;
  letter-spacing: 0.02em;
}

.stat__label {
  display: block;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.16em;
  color: var(--muted);
  margin-top: 0.7rem;
}

@media (max-width: 760px) {
  .stats {
    grid-template-columns: 1fr 1fr;
    gap: 2rem 1rem;
  }
}

/* ---------- About page ---------- */

.founder {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 5vw, 5rem);
  align-items: center;
  max-width: 1100px;
  margin: 0 auto;
  text-align: left;
}

.founder__photo {
  aspect-ratio: 4 / 5;
  background: var(--paper-2);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.66rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--muted);
  text-align: center;
  padding: 1rem;
}

.founder__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

@media (max-width: 760px) {
  .founder {
    grid-template-columns: 1fr;
  }
}

/* ---------- Contact ---------- */

.contact-grid {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: center;
  gap: clamp(2rem, 7vw, 6.5rem);
  margin-top: 3rem;
  text-align: center;
  --fly: 60vw;
  --drop: 40vh;
}

.block--sweep {
  overflow: hidden;
}

.contact-grid .reveal {
  opacity: 1;
  transform: none;
}

.c-row {
  display: block;
  opacity: 0;
}

.contact-grid > .reveal:nth-child(1) .c-row {
  transform: translateX(calc(var(--fly) * -1));
}

.contact-grid > .reveal:nth-child(2) .c-row {
  transform: translateY(var(--drop));
}

.contact-grid > .reveal:nth-child(3) .c-row {
  transform: translateX(var(--fly));
}

.contact-grid > .reveal.is-in .c-row {
  opacity: 1;
  transform: none;
  transition:
    opacity 620ms ease,
    transform 900ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: calc(var(--r, 0) * 90ms);
}

.contact-grid > .reveal:nth-child(3).is-in .c-row {
  transition-delay: calc(var(--r, 0) * 90ms + 60ms);
}

.contact-grid > .reveal:nth-child(2).is-in .c-row {
  transition-delay: calc(var(--r, 0) * 90ms + 160ms);
}

@media (prefers-reduced-motion: reduce) {
  .c-row,
  .contact-grid > .reveal.is-in .c-row {
    transform: none;
    opacity: 1;
  }
}

.contact-grid a {
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid rgba(21, 23, 26, 0.28);
  padding-bottom: 1px;
  transition: border-color 200ms ease;
}

.contact-grid a:hover {
  border-color: var(--ink);
}

.contact-grid p {
  font-size: 0.98rem;
  line-height: 1.8;
}

.contact-grid .eyebrow::before {
  display: none;
}

@media (max-width: 760px) {
  .contact-grid {
    grid-template-columns: auto;
    justify-content: center;
    gap: 2.5rem;
  }
}

/* ---------- Contact form ---------- */

/* Fields are a label above a hairline: no boxes, in step with the rest of
   the page. The line darkens on focus. */
.form {
  display: grid;
  gap: 1.9rem;
}

.form__row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.9rem 2rem;
}

.field {
  display: block;
}

.field__label {
  display: block;
  font-size: 0.66rem;
  font-weight: 400;
  text-transform: uppercase;
  letter-spacing: 0.24em;
  color: var(--muted);
  margin-bottom: 0.45rem;
}

.field input,
.field textarea {
  display: block;
  width: 100%;
  padding: 0.55rem 0 0.7rem;
  border: 0;
  border-bottom: 1px solid rgba(21, 23, 26, 0.28);
  border-radius: 0;
  background: transparent;
  font: inherit;
  font-size: 1.02rem;
  font-weight: 300;
  color: var(--ink);
  outline: none;
  transition: border-color 250ms ease;
}

.field textarea {
  resize: vertical;
  min-height: 7rem;
  line-height: 1.6;
}

.field input:focus,
.field textarea:focus {
  border-bottom-color: var(--ink);
}

.form__note {
  font-size: 0.8rem;
  color: var(--muted);
}

.form .btn {
  justify-self: start;
  font-family: inherit;
  cursor: pointer;
}

/* the emerge timings of the sequence, shifted so the fields come first */
.form.seq.is-in .btn--emerge {
  transition:
    box-shadow 1000ms cubic-bezier(0.16, 1, 0.3, 1) 1000ms,
    color 400ms ease;
}

.form.seq.is-in .btn__label {
  transition: opacity 600ms ease 1500ms;
}

@media (max-width: 620px) {
  .form__row {
    grid-template-columns: 1fr;
  }
}

/* ---------- Footer ---------- */

.footer {
  position: relative;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 1.4rem clamp(1.5rem, 5vw, 2rem);
  font-size: 0.78rem;
  color: var(--muted);
  background: var(--paper);
  border-top: 1px solid var(--line);
}

.footer__credit {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  color: inherit;
  text-decoration: none;
  transition: color 200ms ease;
}

.footer__credit:hover {
  color: var(--ink);
}

.footer__credit img {
  height: 18px;
  width: auto;
  display: block;
}
