/* ==========================================================================
   Collabrum Advisory — Animation utilities
   Mirrors the reference's motion language: a per-character heading reveal on
   ScrollTrigger "top 90%", block fade-up with staggered delays, and hover
   transitions at .3–.4s. Nothing beyond what the reference does.
   ========================================================================== */

/* ---------- Heading character reveal --------------------------------------
   Reference: .tm-split-text.split-in-right — perspective 400px, each glyph
   from translate(50px, 0) + opacity 0, staggered, plays once on enter.
   JS (SplitType + GSAP) drives the animation; these rules hold the pre-state
   so nothing flashes before the timeline attaches.                           */

.split-text {
  perspective: 400px;
  /* `text-wrap: balance` (set on all headings) re-flows lines after SplitType
     has already measured and frozen them, which pushes the tail of a line past
     the container. Balancing is off while a heading is split. */
  text-wrap: wrap;
}

.split-text .line {
  max-width: 100%;
}

.split-text .char {
  display: inline-block;
  will-change: transform, opacity;
}

/* Applied by JS once the characters are wrapped, removed when the tween runs.
   Never applied when the split fails, so text can't get stranded invisible. */
.split-text[data-split-ready="true"] .char {
  opacity: 0;
  transform: translateX(50px);
}

/* The 50px pre-offset would otherwise push the last glyph of a line past the
   container and widen the document. `clip` on one axis only, so descenders on
   the final line are untouched; the rule disappears when the split reverts. */
.split-text[data-split-ready="true"] {
  overflow-x: clip;
}

/* ---------- Block reveal ---------------------------------------------------
   Reference uses WOW.js fadeInUp with data-wow-delay in 300ms steps.         */

.reveal {
  opacity: 0;
  transform: translateY(40px);
  transition: opacity .8s var(--ease-out), transform .8s var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: transform, opacity;
}

.reveal[data-revealed="true"] {
  opacity: 1;
  transform: none;
}


/* Stagger — the reference steps `data-wow-delay` in 300ms increments across a
   row. The cycle restarts each row rather than running the length of the grid,
   so a six-card grid does not end up with a 1.5s delay on its last item. */
.stagger > *:nth-child(3n + 1) { --reveal-delay: 0ms; }
.stagger > *:nth-child(3n + 2) { --reveal-delay: 300ms; }
.stagger > *:nth-child(3n + 3) { --reveal-delay: 600ms; }

.stagger--4 > *:nth-child(4n + 1) { --reveal-delay: 0ms; }
.stagger--4 > *:nth-child(4n + 2) { --reveal-delay: 300ms; }
.stagger--4 > *:nth-child(4n + 3) { --reveal-delay: 600ms; }
.stagger--4 > *:nth-child(4n + 4) { --reveal-delay: 900ms; }

.stagger--2 > *:nth-child(2n + 1) { --reveal-delay: 0ms; }
.stagger--2 > *:nth-child(2n + 2) { --reveal-delay: 300ms; }

/* Once the grid has collapsed to one column the cards enter one at a time as
   they scroll past, so a queued delay only makes each one feel late. */
@media (max-width: 560px) {
  .stagger > *,
  .stagger--2 > *,
  .stagger--4 > * { --reveal-delay: 0ms; }
}

/* ---------- Marquee --------------------------------------------------------
   Reference runs a moving-text repeater under the hero and above the footer. */

.marquee {
  display: flex;
  overflow: hidden;
  user-select: none;
  gap: var(--marquee-gap, 40px);
}

.marquee__track {
  display: flex;
  flex-shrink: 0;
  align-items: center;
  gap: var(--marquee-gap, 40px);
  min-width: 100%;
  animation: marquee-scroll var(--marquee-duration, 40s) linear infinite;
}

.marquee:hover .marquee__track { animation-play-state: paused; }

@keyframes marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(-100% - var(--marquee-gap, 40px))); }
}

/* ---------- Image reveal ---------------------------------------------------- */

.img-reveal { overflow: hidden; }

.img-reveal img {
  transform: scale(1.12);
  transition: transform 1.2s var(--ease-out);
}

.img-reveal[data-revealed="true"] img { transform: scale(1); }

/* ---------- Counters --------------------------------------------------------
   CountUp.js writes the value; this only prevents layout shift while at 0.   */

.counter__value { font-variant-numeric: tabular-nums; }

/* ---------- Drawer / scrim -------------------------------------------------
   Measured from the reference: .25s cubic-bezier(.645,.045,.355,1).          */

.drawer {
  transform: translateX(-100%);
  transition: transform var(--dur-fast) var(--ease);
}

.drawer[data-open="true"] { transform: translateX(0); }

.scrim {
  opacity: 0;
  visibility: hidden;
  background: rgba(0, 0, 0, .6);
  transition: opacity var(--dur-fast) var(--ease),
              visibility var(--dur-fast) var(--ease);
}

.scrim[data-open="true"] { opacity: 1; visibility: visible; }

/* ---------- Reduced motion --------------------------------------------------
   Everything lands in its final state immediately. No element may depend on
   an animation to become visible.                                            */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }

  .reveal,
  .split-text[data-split-ready="true"] .char {
    opacity: 1 !important;
    transform: none !important;
    --reveal-delay: 0ms;
  }

  .img-reveal img { transform: none !important; }

  .marquee__track { animation: none; }

  .tile:hover img,
  .post:hover .post__media img,
  .service-card:hover .service-card__media img { transform: none; }
}
