/* Motion for every page. Nothing here applies unless html has the .motion class, which
   each page adds in its head only when the visitor has not asked for reduced motion.
   Everything animates transform, opacity or clip-path so it stays on the compositor. */

:root {
  --ease-out: cubic-bezier(.19, 1, .22, 1);
  --ease-curtain: cubic-bezier(.77, 0, .18, 1);
}

/* Page arrival, which also masks the moment motion.js sets up its initial states */
.motion body { animation: page-in 0.7s ease both; }

.motion body.is-leaving { opacity: 0; transition: opacity 0.28s ease; }

@keyframes page-in { from { opacity: 0; } to { opacity: 1; } }

/* Headlines rise word by word from behind a mask */
.motion .split .w { display: inline-block; overflow: hidden; vertical-align: top; padding-bottom: 0.08em; margin-bottom: -0.08em; }

.motion .split .wi { display: inline-block; transform: translateY(108%); transition: transform 1.2s var(--ease-out); }

.motion .split.is-in .wi { transform: none; }

/* Blocks fade and lift as they enter the viewport */
.motion .reveal { opacity: 0; transform: translateY(26px); transition: opacity 1s ease, transform 1.2s var(--ease-out); }

.motion .reveal.is-in { opacity: 1; transform: none; }

/* Images unveil upwards, settling from a slight zoom */
.motion .reveal-img { clip-path: inset(100% 0 0 0); transition: clip-path 1.3s var(--ease-curtain); }

.motion .reveal-img.is-in { clip-path: inset(0 0 0 0); }

/* Case study frames: parallax drift inside the frame, zoom settle on reveal, lift on hover.
   Individual translate and scale properties let the three compose without fighting. */
.motion .case-frame img { transform: none; translate: 0 var(--py, 0px); scale: 1.1; transition: scale 1.6s var(--ease-out); will-change: translate; }

.motion .case-frame.reveal-img:not(.is-in) img { scale: 1.3; }

.motion .case-frame:hover img { transform: none; scale: 1.14; }

/* Home hero image: slow settle on arrival, parallax on the wrapper */
.motion .hero-media-inner { translate: 0 var(--py, 0px); will-change: translate; }

.motion .hero-media img { transition: scale 2.6s var(--ease-out), opacity 1.6s ease; }

/* Only hidden once motion.js has marked it pending, so the image still shows if the script fails */
.motion .hero-media.is-pending img { scale: 1.12; opacity: 0; }

/* The masthead tucks away on the way down and returns on the way up */
.motion .masthead { transition: transform 0.45s var(--ease-out); }

.motion .masthead.is-hidden { transform: translateY(-100%); }

/* Custom cursor label over films and photos, on devices with a precise pointer only.
   The native cursor stays everywhere else. */
.has-cursor button.film, .has-cursor a.film, .has-cursor .case-frame { cursor: none; }

.cursor-label {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 90;
  width: 84px;
  height: 84px;
  margin: -42px 0 0 -42px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(246, 246, 242, 0.94);
  color: var(--ink);
  font-family: 'Jost', sans-serif;
  font-weight: 500;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  pointer-events: none;
  opacity: 0;
  scale: 0.4;
  transition: opacity 0.25s ease, scale 0.4s var(--ease-out), background 0.25s ease, color 0.25s ease;
  box-shadow: 0 6px 24px rgba(28, 28, 25, 0.18);
}

.cursor-label.is-on { opacity: 1; scale: 1; }

.cursor-label.is-film { background: var(--navy); color: var(--paper); }

/* The label replaces a film's own play button while the pointer is over it */
.has-cursor .film .play { transition: opacity 0.2s ease, transform 0.3s ease; }

.has-cursor .film:hover .play { opacity: 0; }
