/* ═══════════════════════════════════════════════════════════════════════════
   MOTION — entrance primitives.

   Deliberately restrained. The earlier build swung panels through 3-space; on
   a site whose job is to earn the confidence of investors and international
   clients, that reads as showmanship. What is left is a short rise-and-fade
   that guides the eye down the page and then gets out of the way.
   ═════════════════════════════════════════════════════════════════════════ */

.reveal {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition: opacity .7s var(--ease-out), transform .7s var(--ease-out);
  will-change: transform, opacity;
}
.reveal.in { opacity: 1; transform: none; }

/* Stagger ladder — declared in markup as data-d="1..5" */
[data-d="1"] { transition-delay: .07s; }
[data-d="2"] { transition-delay: .14s; }
[data-d="3"] { transition-delay: .21s; }
[data-d="4"] { transition-delay: .28s; }
[data-d="5"] { transition-delay: .35s; }

/* Parallax hook — translate written by motion.js */
[data-par] { will-change: transform; }

/* ── Reduced motion ────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
  [data-par] { transform: none !important; }
  .marquee-row { animation: none !important; }
  * { scroll-behavior: auto !important; animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ── Richer entrances ──────────────────────────────────────────────────────
   Variants on the base rise. Each is still transform/opacity only, and each
   still resolves and stops — nothing loops once it has arrived. */

/* Image wipe: the frame opens from the bottom while the picture settles out
   of a slight over-scale, so the photograph feels revealed rather than faded.
   The clip-path lives on the FRAME (first child), never on .reveal-img itself —
   self-clipping the observed element to zero visible height stops Chrome's
   IntersectionObserver from ever reporting it as intersecting, so it can never
   reach .in and stays invisible forever. Keeping .reveal-img itself unclipped
   (opacity-only) is what lets the observer actually fire. */
.reveal-img {
  opacity: 0;
  transition: opacity .5s var(--ease-out);
  will-change: opacity;
}
.reveal-img > :first-child {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.05s var(--ease-out);
  will-change: clip-path;
}
.reveal-img.in { opacity: 1; }
.reveal-img.in > :first-child { clip-path: inset(0 0 0 0); }
.reveal-img img { transform: scale(1.12); transition: transform 1.4s var(--ease-out); }
.reveal-img.in img { transform: none; }

/* Directional slides for split layouts. */
.reveal-l, .reveal-r {
  opacity: 0;
  transition: opacity .8s var(--ease-out), transform .9s var(--ease-out);
  will-change: transform, opacity;
}
.reveal-l { transform: translate3d(-28px, 0, 0); }
.reveal-r { transform: translate3d(28px, 0, 0); }
.reveal-l.in, .reveal-r.in { opacity: 1; transform: none; }

/* Cards that lift into place with a touch of depth. */
.reveal-lift {
  opacity: 0;
  transform: translate3d(0, 26px, 0) scale(.975);
  transition: opacity .75s var(--ease-out), transform .85s var(--ease-out);
  will-change: transform, opacity;
}
.reveal-lift.in { opacity: 1; transform: none; }

/* Headline that arrives a beat before its supporting copy. */
.reveal-head {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity .8s var(--ease-out), transform .9s var(--ease-out);
}
.reveal-head.in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .reveal-img, .reveal-l, .reveal-r, .reveal-lift, .reveal-head {
    opacity: 1 !important; transform: none !important; clip-path: none !important; transition: none !important;
  }
  .reveal-img > :first-child { clip-path: none !important; }
  .reveal-img img { transform: none !important; }
}
