/* =========================================================
   UN Geneva — 2025 Annual Report
   Motion

   Two mechanisms, each used where it is actually the right tool:

   1. ONE-SHOT entrances (reveals, wipes, count-up) use
      IntersectionObserver + an .is-in class. One-shot motion
      must not run backwards when the user scrolls up, which
      is exactly what a scroll-linked timeline would do.

   2. CONTINUOUS scroll-linked motion (progress bar, parallax)
      uses native CSS scroll-driven animations — no scroll
      listeners, no main-thread work. Browsers without support
      simply get the static final state.

   Everything here is disabled under prefers-reduced-motion,
   with content left in its final state.
   ========================================================= */

/* ---------------------------------------------------------
   1. One-shot reveals
   --------------------------------------------------------- */
.reveal{
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--dur-l) var(--ease-out),
              transform var(--dur-l) var(--ease-out);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
.reveal.is-in{ opacity: 1; transform: none; }

/* Masked line reveal — used for the hero title and section titles.
   JS wraps each rendered line in .line > span. */
.line{ display: block; overflow: hidden; }
.line > span{
  display: block;
  transform: translateY(105%);
  transition: transform var(--dur-l) var(--ease-out);
  transition-delay: calc(var(--i, 0) * var(--stagger));
}
.is-in .line > span,
.hero__slide.is-active .line > span{ transform: none; }

/* ---------------------------------------------------------
   2. Hero
   --------------------------------------------------------- */
.hero__slide{
  transition: opacity var(--dur-l) var(--ease-out),
              visibility var(--dur-l) var(--ease-out);
}
/* Counter-drift cross-dissolve */
.hero__img{
  transform: scale(1.06) translateX(2%);
  transition: transform var(--dur-l) var(--ease-out);
}
.hero__slide.is-active .hero__img{
  transform: scale(1.06) translateX(0);
  /* Slow continuous Ken Burns while the slide is on screen */
  animation: ken-burns 9s var(--ease-out) forwards;
}
@keyframes ken-burns{
  from{ transform: scale(1.06) translateX(0); }
  to{   transform: scale(1)    translateX(0); }
}

.hero__eyebrow,
.hero__cta{
  opacity: 0;
  transform: translateY(14px);
  transition: opacity var(--dur-m) var(--ease-out), transform var(--dur-m) var(--ease-out);
}
.hero__slide.is-active .hero__eyebrow{ opacity: 1; transform: none; transition-delay: .15s; }
.hero__slide.is-active .hero__cta{ opacity: 1; transform: none; transition-delay: .55s; }

/* Tick-ring autoplay progress — quotes the print's radial motif.
   r=20 → circumference 125.66; dasharray set in CSS to match. */
.hero__ring .ring-progress{
  stroke-dasharray: 125.66;
  stroke-dashoffset: 125.66;
}
.hero.is-playing .hero__ring .ring-progress{
  animation: ring-sweep var(--autoplay-ms, 6500ms) linear infinite;
}
@keyframes ring-sweep{
  from{ stroke-dashoffset: 125.66; }
  to{   stroke-dashoffset: 0; }
}

.hero__scroll-cue svg{ animation: bob 2s var(--ease-in-out) infinite; }
@keyframes bob{
  0%, 100%{ transform: translateY(0); }
  50%{      transform: translateY(5px); }
}

/* ---------------------------------------------------------
   3. Chapters — wipe in, then parallax
   --------------------------------------------------------- */
/* Driven from the parent .chapter: an element clipped to zero area
   is not reported as intersecting, so it can never observe itself. */
.chapter__media{
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1s var(--ease-out);
}
.chapter.is-in .chapter__media{ clip-path: inset(0 0 0 0); }

/* The panel's contents rise as the image wipes */
.chapter__panel > *{
  opacity: 0;
  transform: translateY(20px);
  transition: opacity var(--dur-l) var(--ease-out), transform var(--dur-l) var(--ease-out);
}
.chapter.is-in .chapter__panel > *{ opacity: 1; transform: none; }
.chapter.is-in .chapter__panel > :nth-child(1){ transition-delay: .10s; }
.chapter.is-in .chapter__panel > :nth-child(2){ transition-delay: .20s; }
.chapter.is-in .chapter__panel > :nth-child(3){ transition-delay: .30s; }
.chapter.is-in .chapter__panel > :nth-child(4){ transition-delay: .40s; }

/* 861px+: the chapter is pinned (see style.css), so the wipe above is
   replaced by the 2024 site's entrance — the photograph and the panel
   both settle in from an oversized, faded state as the chapter arrives
   at the top of the viewport and pins. The per-child stagger above still
   runs on top of this, unchanged. */
@media (min-width: 861px){
  .chapter__media{
    clip-path: none;
    opacity: 0;
    transform: scale(1.4) translateY(6%);
    transition: opacity var(--dur-hero) var(--ease-out),
                transform var(--dur-hero) var(--ease-out);
  }
  .chapter.is-in .chapter__media{ opacity: 1; transform: none; }

  .chapter__panel{
    transform: scale(1.14);
    transition: transform var(--dur-hero) var(--ease-out);
  }
  .chapter.is-in .chapter__panel{ transform: none; }
}

/* ---------------------------------------------------------
   4. Numbers
   --------------------------------------------------------- */
/* Share menu: a short rise-and-fade each time it opens. */
.share-panel:not([hidden]){ animation: share-in .18s var(--ease-out); }
@keyframes share-in{
  from{ opacity: 0; transform: translateY(6px); }
  to{   opacity: 1; transform: none; }
}

/* Each entry rises as it enters, cascading left to right across its grid
   row. --i is the column index, derived here rather than in JS because the
   column count changes with the breakpoint. */
.numbers__grid > .stat:nth-child(2n+1){ --i: 0; }
.numbers__grid > .stat:nth-child(2n){   --i: 1; }
@media (min-width: 1250px){
  .numbers__grid > .stat:nth-child(3n+1){ --i: 0; }
  .numbers__grid > .stat:nth-child(3n+2){ --i: 1; }
  .numbers__grid > .stat:nth-child(3n){   --i: 2; }
}
@media (max-width: 900px){
  .numbers__grid > .stat{ --i: 0; }
}

.stat{
  opacity: 0;
  transform: translateY(20px);
  transition: opacity .75s var(--ease-out), transform .75s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 90ms);
}
.stat.is-in{ opacity: 1; transform: none; }

.stat__icon img{
  clip-path: inset(0 0 100% 0);
  transition: clip-path .85s var(--ease-out);
  transition-delay: calc(var(--i, 0) * 90ms + .18s);
}
.stat.is-in .stat__icon img{ clip-path: inset(0 0 0 0); }

/* ---------------------------------------------------------
   5. Gallery
   --------------------------------------------------------- */
.gallery__item img{ transition: transform .7s var(--ease-out); }
.gallery__item:hover img,
.gallery__item:focus-visible img{ transform: scale(1.06); }

/* The FLIP clone the lightbox animates between thumbnail and final rect */
.flip-clone{
  position: fixed;
  z-index: 1300;
  object-fit: cover;
  pointer-events: none;
  margin: 0;
}

/* ---------------------------------------------------------
   6. Continuous scroll-linked motion (native timelines)
   --------------------------------------------------------- */
@supports (animation-timeline: scroll()){
  .scroll-progress{
    animation: progress-grow linear both;
    animation-timeline: scroll(root block);
  }
  @keyframes progress-grow{
    from{ transform: scaleX(0); }
    to{   transform: scaleX(1); }
  }
}

@supports (animation-timeline: view()){
  /* Gentle parallax inside the chapter frame. The image is
     oversized so the translation never exposes an edge. */
  .chapter__media img{
    animation: chapter-parallax linear both;
    animation-timeline: view();
    animation-range: cover;
    will-change: transform;
  }
  @keyframes chapter-parallax{
    from{ transform: translateY(-5%) scale(1.14); }
    to{   transform: translateY(5%)  scale(1.14); }
  }
}

/* ---------------------------------------------------------
   7. Reduced motion
   Scoped deliberately: transforms, parallax, autoplay and
   count-up stop; focus and hover feedback survive, because
   removing them would cost usability rather than comfort.
   --------------------------------------------------------- */
@media (prefers-reduced-motion: reduce){
  html{ scroll-behavior: auto; }

  .reveal,
  .chapter__panel > *{
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .line > span{ transform: none !important; transition: none !important; }
  .chapter__media,
  .stat__icon img{ clip-path: none !important; transition: none !important; }
  /* The pin+stack scroll effect (style.css, 861px+) is disabled outright,
     not just detransitioned: a box that locks in place while the page
     scrolls past it is itself the kind of motion this preference is
     asking to avoid, independent of any transform/opacity animation. */
  .chapter{ min-height: 0 !important; }
  .chapter__pin{ position: static !important; overflow: visible !important; }
  .chapter__media{ opacity: 1 !important; transform: none !important; }
  .chapter__panel{ transform: none !important; }
  .stat{ opacity: 1 !important; transform: none !important; transition: none !important; }
  .share-panel{ animation: none !important; }

  .hero__slide{ transition: opacity .2s linear; }
  .hero__img,
  .hero__slide.is-active .hero__img{
    transform: none !important;
    animation: none !important;
  }
  .hero__eyebrow,
  .hero__cta{ opacity: 1 !important; transform: none !important; transition: none !important; }
  .hero.is-playing .hero__ring .ring-progress{ animation: none !important; }
  .hero__scroll-cue svg{ animation: none !important; }

  .gallery__item img{ transition: none !important; }
  .gallery__item:hover img,
  .gallery__item:focus-visible img{ transform: none !important; }

  /* Positional hover feedback snaps rather than slides. The arrow nudge
     and the nav underline are kept: they are sub-pixel-scale affordances
     that carry meaning, not motion for its own sake. */
  .gallery__caption{ transition: opacity var(--dur-s) var(--ease-out) !important; transform: none !important; }
  .lang-switch__pill{ transition: none !important; }

  .chapter__media img{ animation: none !important; transform: none !important; }
  .scroll-progress{ animation: none !important; transform: scaleX(0) !important; }
}
