/* ==========================================================================
   Motion: keyframes and animation classes, copied from the canvas.
   Load order: headline rises, intro follows (0.45s), photo drops (1.3s),
   note and arrow draw (2.2s), marquee runs. The paper is static (039).
   Reduced motion switches all of this off in base.css.
   ========================================================================== */

/* Phones move a little less (reference/homepage-mobile.dc.html) */
:root {
  --rise-y: 40px;
  --drop-y: -70px;
  --drop-bounce: 8px;
}
@media (max-width: 759.98px) {
  :root {
    --rise-y: 32px;
    --drop-y: -56px;
    --drop-bounce: 6px;
  }
}

@keyframes swRise {
  from { opacity: 0; transform: translateY(var(--rise-y)); }
  to   { opacity: 1; transform: none; }
}

/* Desktop photo settles at 3deg */
@keyframes swDrop {
  0%   { opacity: 0; transform: translateY(var(--drop-y)) rotate(-8deg); }
  65%  { opacity: 1; transform: translateY(var(--drop-bounce)) rotate(5deg); }
  100% { opacity: 1; transform: rotate(3deg); }
}

/* Phone photo settles at -3deg: the mirror of swDrop. (The canvas reuses
   swDrop here, which would leave the photo at +3deg after the drop.) */
@keyframes swDropL {
  0%   { opacity: 0; transform: translateY(var(--drop-y)) rotate(8deg); }
  65%  { opacity: 1; transform: translateY(var(--drop-bounce)) rotate(-5deg); }
  100% { opacity: 1; transform: rotate(-3deg); }
}

@keyframes swDraw {
  from { stroke-dashoffset: 220; }
  to   { stroke-dashoffset: 0; }
}

@keyframes swFade {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes swMarquee {
  to { transform: translateX(-50%); }
}

/* View case ticket: sticks on once the card has lifted */
@keyframes ticketIn {
  0%   { opacity: 0; scale: .8; }
  60%  { opacity: 1; scale: 1.06; }
  100% { opacity: 1; scale: 1; }
}

.sw-rise { animation: swRise .9s cubic-bezier(.2, .7, .2, 1) backwards; } /* backwards: leaves hover transforms free afterwards */
.sw-d3   { animation-delay: .45s; }
.sw-drop { animation: swDrop 1s cubic-bezier(.2, .7, .2, 1) 1.3s both; }
.sw-draw path { stroke-dasharray: 220; animation: swDraw .8s ease-out 2.2s both; }
.sw-note { animation: swFade .4s ease-out 2.2s both; }
.sw-run  { animation: swMarquee 30s linear infinite; }

@media (max-width: 759.98px) {
  .sw-drop { animation-name: swDropL; }
  .sw-run  { animation-duration: 24s; }
}

/* Paused by js/main.js on hover and while off screen */
.is-paused .sw-run {
  animation-play-state: paused;
}

/* ==========================================================================
   Opening a project (021, 040), run by js/transition.js. Only under
   no-preference: with reduced motion there is no transition at all.
   Desktop: a cross-document View Transition (.is-vt-morph on <html>).
   Phone: no View Transition, so it works in every browser. .vt-rip is the
   paper: on the old page it tears up showing 0%; on the new page
   (.vt-rip.is-in) it covers the page from its first paint, counts the
   first screen's loading up to 100% and only then tears off (.is-off).
   ========================================================================== */
@media (prefers-reduced-motion: no-preference) {
  @view-transition {
    navigation: auto;
  }
}

/* --- Desktop, 1: the stub tears off along the perforation (0.34s) ------- */
@keyframes tdRip {
  0%   { translate: 0 0;        rotate: 0deg;  opacity: 1; }
  30%  { translate: -3px 2px;   rotate: -2deg; opacity: 1; }
  45%  { translate: 5px -3px;   rotate: 6deg;  opacity: 1; }
  100% { translate: 56px -36px; rotate: 26deg; opacity: 0; }
}
@keyframes tdHold {
  0%   { translate: 0 0; }
  30%  { translate: 2px 1px; }
  45%  { translate: -3px 0; }
  100% { translate: 0 0; }
}
.ticket.is-torn {
  display: block;
  animation: none;
}
.ticket.is-torn .tk-perf {
  display: none;
}
.ticket.is-torn .tk-stub {
  transform-origin: 82px 50%;
  animation: tdRip .34s cubic-bezier(.3, 0, .6, 1) both;
}
.ticket.is-torn .tk-main {
  animation: tdHold .34s ease both;
}
/* The card stays lifted while it opens, even if the pointer moves off, but
   straightens (0.2s, inside the 0.34s tear): the View Transition captures
   it at 0deg, so the morph only moves and resizes, never rotates (a rotated
   snapshot stretched into the straight hero reads as a skew). */
.card.is-opening .card-img {
  transform: translateY(-8px);
  box-shadow: 0 32px 64px rgba(0, 0, 0, 0.6);
  transition: transform .2s cubic-bezier(.2, .7, .2, 1), box-shadow .2s;
}

/* --- Desktop, 2: the photo grows into the hero (0.7s), the rest rises --- */
@keyframes vtFadeOut {
  to { opacity: 0; }
}
@keyframes vtUp {
  from { opacity: 0; transform: translateY(32px); }
  to   { opacity: 1; transform: none; }
}
.is-vt-morph .cs-hero .media {
  view-transition-name: cs-hero;
}
.is-vt-morph .site-header {
  view-transition-name: site-header;
}
.is-vt-morph::view-transition-group(cs-hero) {
  animation-duration: .7s;
  animation-timing-function: cubic-bezier(.65, 0, .2, 1);
}
/* One photo grows, as the export: no crossfade between card and hero (their
   crops differ, which ghosts). The card (with its pill) drops out at once,
   the hero snapshot grows from the card's spot. Different ratios: crop,
   don't squash. */
.is-vt-morph::view-transition-old(cs-hero),
.is-vt-morph::view-transition-new(cs-hero) {
  height: 100%;
  object-fit: cover;
  mix-blend-mode: normal;
}
.is-vt-morph::view-transition-old(cs-hero) {
  animation: vtFadeOut .12s ease both;
}
.is-vt-morph::view-transition-new(cs-hero) {
  animation: none;
}
.is-vt-morph::view-transition-old(root) {
  animation: vtFadeOut .35s ease both;
}
.is-vt-morph::view-transition-new(root) {
  animation: vtUp .5s cubic-bezier(.2, .7, .2, 1) .75s both;
}

/* --- Phone, 1: the image lifts, the paper tears up (0.6s), jerky -------- */
@keyframes tpUp {
  0%   { transform: translateY(105%); }
  18%  { transform: translateY(84%); }
  34%  { transform: translateY(62%); }
  48%  { transform: translateY(55%); }
  66%  { transform: translateY(24%); }
  82%  { transform: translateY(12%); }
  100% { transform: translateY(0); }
}
.card-img.is-lift,
.next-prev.is-lift {
  transform: translateY(-6px) scale(1.02); /* no tilt on a tap (021) */
  box-shadow: 0 22px 40px rgba(0, 0, 0, 0.7);
  transition: transform .18s cubic-bezier(.2, .7, .2, 1), box-shadow .18s;
}
/* The torn top and bottom edges of the paper (bone fibre rim, then the
   destination project's colour, 056) */
:root {
  --rip-fibre: polygon(0.0% 1.0%, 2.8% 0.5%, 5.6% 2.0%, 8.3% 0.2%, 11.1% 1.6%, 13.9% 1.1%, 16.7% 0.2%, 19.4% 1.5%, 22.2% 0.1%, 25.0% 1.3%, 27.8% 0.2%, 30.6% 0.3%, 33.3% 1.3%, 36.1% 2.5%, 38.9% 0.4%, 41.7% 0.7%, 44.4% 1.9%, 47.2% 2.8%, 50.0% 1.7%, 52.8% 1.2%, 55.6% 2.9%, 58.3% 0.1%, 61.1% 2.6%, 63.9% 0.9%, 66.7% 0.4%, 69.4% 0.4%, 72.2% 0.9%, 75.0% 2.4%, 77.8% 0.5%, 80.6% 1.7%, 83.3% 1.9%, 86.1% 1.1%, 88.9% 1.6%, 91.7% 0.2%, 94.4% 0.2%, 97.2% 0.6%, 100.0% 2.0%, 100.0% 100.0%, 97.2% 98.9%, 94.4% 99.1%, 91.7% 97.2%, 88.9% 99.0%, 86.1% 98.4%, 83.3% 99.8%, 80.6% 99.5%, 77.8% 98.4%, 75.0% 98.7%, 72.2% 98.8%, 69.4% 99.1%, 66.7% 97.9%, 63.9% 99.6%, 61.1% 98.7%, 58.3% 99.3%, 55.6% 99.0%, 52.8% 97.1%, 50.0% 98.5%, 47.2% 97.5%, 44.4% 99.3%, 41.7% 98.3%, 38.9% 97.4%, 36.1% 99.9%, 33.3% 97.9%, 30.6% 99.2%, 27.8% 99.6%, 25.0% 98.6%, 22.2% 98.7%, 19.4% 97.7%, 16.7% 99.1%, 13.9% 99.4%, 11.1% 97.9%, 8.3% 98.4%, 5.6% 98.8%, 2.8% 97.9%, 0.0% 98.3%);
  --rip-paper: polygon(0.0% 1.9%, 2.8% 2.2%, 5.6% 3.3%, 8.3% 1.9%, 11.1% 2.0%, 13.9% 2.3%, 16.7% 1.1%, 19.4% 2.0%, 22.2% 2.4%, 25.0% 2.9%, 27.8% 0.8%, 30.6% 1.4%, 33.3% 0.8%, 36.1% 2.9%, 38.9% 2.6%, 41.7% 0.6%, 44.4% 3.4%, 47.2% 3.4%, 50.0% 2.5%, 52.8% 2.3%, 55.6% 1.0%, 58.3% 0.5%, 61.1% 2.1%, 63.9% 0.7%, 66.7% 1.1%, 69.4% 1.2%, 72.2% 0.6%, 75.0% 1.9%, 77.8% 1.8%, 80.6% 3.0%, 83.3% 2.1%, 86.1% 2.4%, 88.9% 2.0%, 91.7% 2.5%, 94.4% 1.9%, 97.2% 1.3%, 100.0% 3.5%, 100.0% 98.7%, 97.2% 97.1%, 94.4% 97.8%, 91.7% 98.2%, 88.9% 97.0%, 86.1% 98.9%, 83.3% 96.7%, 80.6% 96.8%, 77.8% 97.2%, 75.0% 96.9%, 72.2% 98.8%, 69.4% 99.4%, 66.7% 97.5%, 63.9% 96.8%, 61.1% 97.3%, 58.3% 98.8%, 55.6% 98.4%, 52.8% 96.7%, 50.0% 97.7%, 47.2% 99.4%, 44.4% 97.9%, 41.7% 99.2%, 38.9% 97.1%, 36.1% 96.5%, 33.3% 99.0%, 30.6% 99.4%, 27.8% 97.7%, 25.0% 99.0%, 22.2% 97.7%, 19.4% 98.8%, 16.7% 96.7%, 13.9% 97.4%, 11.1% 97.2%, 8.3% 97.4%, 5.6% 98.6%, 2.8% 99.0%, 0.0% 99.5%);
}
.vt-rip {
  position: fixed;
  inset: 0;
  z-index: var(--z-transition);
  pointer-events: none;
  animation: tpUp .6s linear both;
}
.vt-rip-fibre,
.vt-rip-paper {
  position: absolute;
  left: -8px;
  right: -8px;
}
.vt-rip-fibre {
  top: -20px;
  bottom: -20px;
  /* A paint tile over the colour, like the paper: forced dark mode would
     turn a plain bone rim dark */
  background: url('/img/paper/fibre.webp') var(--fibre);
  clip-path: var(--rip-fibre);
}
.vt-rip-paper {
  top: -14px;
  bottom: -14px;
  /* The project's paint tile and accent, set by js/transition.js; yellow
     when the page isn't a project */
  background: var(--rip-paint, var(--yellow-paint)) var(--rip-bg, var(--yellow));
  clip-path: var(--rip-paper);
}

/* Loading percentage on the paper (021): 'Loading' (label size) over the
   number (H1 size, heading font) in the project's --on-accent (ink on
   yellow), bottom left on the page margins,
   clear of the torn bottom edge and the system bar */
.vt-rip-load {
  position: absolute;
  left: calc(8px + max(var(--margin), env(safe-area-inset-left, 0px)));
  bottom: calc(14px + var(--sp-40) + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: var(--sp-8);
  color: var(--rip-fg, var(--ink));
}
.vt-rip-label {
  font-size: var(--fs-label);
  line-height: var(--lh-label);
  font-weight: 600;
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
}
.vt-rip-pct {
  font-family: var(--font-head);
  font-weight: var(--head-weight);
  font-stretch: var(--head-stretch);
  font-size: var(--fs-h1);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* --- Phone, 2: the paper tears off the top, the case study is under it - */
/* On the new page the paper covers the screen from the first paint (put
   there by js/transition.js in <head>) and waits; at 100% it tears off
   straight up (no rotate: the export's -5deg read as a tilt on a phone). */
@keyframes tpOff {
  0%   { transform: translateY(0); }
  100% { transform: translateY(-118vh); }
}
.vt-rip.is-in {
  animation: none;
}
.vt-rip.is-off {
  animation: tpOff .5s cubic-bezier(.55, 0, .8, .2) both;
}
