/* ==========================================================================
   MOTION — presentation for the pointer effects in assets/js/motion.js.

   The script only writes numbers into custom properties; every visual
   decision lives here, and every property has a resting default, so the
   page looks exactly as designed when the script never runs (coarse
   pointer, reduced motion, JS off).

   Load LAST — the tilt transform has to win over the plain :hover lift each
   card already carries.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. BANNER PARALLAX  →  motion.js scene()

   --px / --py run -1…1 from the centre of the banner.
   -------------------------------------------------------------------------- */
.hero,
.page-hero,
.contact-hero,
.px-banner,
[data-pointer-scene] {
  --px: 0;
  --py: 0;

  /* Travel of the artwork at the very edge of the banner — the whole effect
     is one drift, so this is the only number worth touching. The copy follows
     the same way at half the distance; moving the two apart reads as the
     banner coming loose rather than as depth. */
  --parallax-x: 24px;
  --parallax-y: 17px;
}

/* The script marks these two layers (see layers() in motion.js); it always
   picks a wrapper rather than the artwork itself, because the artwork owns a
   reveal transform of its own and only one of the two can win. */
.js .is-parallax-art {
  transform: translate3d(
    calc(var(--px) * var(--parallax-x)),
    calc(var(--py) * var(--parallax-y)),
    0
  );
}

.js .is-parallax-copy {
  transform: translate3d(
    calc(var(--px) * var(--parallax-x) * 0.5),
    calc(var(--py) * var(--parallax-y) * 0.5),
    0
  );
}

/* The banner photograph drifts too, but it has to keep the overscale that
   hides its edges — so the scale is folded in rather than replaced. */
.js .hero__media.is-parallax-art {
  transform: scale(1.1)
    translate3d(
      calc(var(--px) * var(--parallax-x)),
      calc(var(--py) * var(--parallax-y)),
      0
    );
}

/* The about banner's decorative layer is centred with its own translateY, so
   the drift has to be folded into that rather than replace it. */
.js .about-hero__fx.is-parallax-art {
  transform: translate3d(
    calc(var(--px) * var(--parallax-x)),
    calc(-50% + var(--py) * var(--parallax-y)),
    0
  );
}

/* The figure carries a 0.5s transform transition for the slide change; on top
   of a value that is already eased frame by frame it only reads as lag. Hand
   transform over to the script and leave the fade alone. */
.js .hero__slide .hero__figure {
  transition: opacity 0.5s var(--ease);
}

/* --------------------------------------------------------------------------
   2. CARD TILT  →  motion.js tilt()

   [data-tilt] is set by the script, so these rules only ever apply where the
   effect is actually running. The `html.js` prefix is not decoration: it
   lifts the specificity above each card's own `.card:hover { transform }`,
   whichever stylesheet that lives in.
   -------------------------------------------------------------------------- */
html.js [data-tilt],
html.js [data-tilt]:hover {
  position: relative;
  transform: perspective(1000px) rotateX(var(--tilt-rx, 0deg))
    rotateY(var(--tilt-ry, 0deg)) translate3d(0, var(--tilt-lift, 0px), 0);
  /* The script eases the transform frame by frame; a CSS transition on top
     of that would only add lag. Shadow and the rest still animate. */
  transition: box-shadow var(--t-base), border-color var(--t-base);
  transform-style: preserve-3d;
}

/* The sheen. Clipped by its own inherited radius, so it never squares off a
   rounded card, and pinned under the pointer via --gx / --gy. */
.tilt-glare {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  opacity: var(--tilt-glare, 0);
  background: radial-gradient(
    260px circle at var(--gx, 50%) var(--gy, 50%),
    rgba(255, 255, 255, 0.4) 0%,
    rgba(255, 255, 255, 0) 60%
  );
}

/* --------------------------------------------------------------------------
   3. IMAGE SKEW  →  motion.js skew()

   The same idea as the card, at half the strength and with no sheen: a
   picture leans a couple of degrees toward the cursor and settles back. The
   script only marks images that do not already own a hover transform, so the
   media zooms elsewhere on the site are left alone.
   -------------------------------------------------------------------------- */
html.js [data-skew] {
  transform: perspective(1000px) rotateX(var(--skew-rx, 0deg))
    rotateY(var(--skew-ry, 0deg)) scale(var(--skew-scale, 1));
  backface-visibility: hidden;
}

/* Cards whose own surface is dark or gradient-filled take a cooler, weaker
   sheen — white at 40% on navy reads as fog. */
.industry-card--violet .tilt-glare,
.industry-card--cyan .tilt-glare,
.solution-card .tilt-glare,
.project .tilt-glare,
.media-featured .tilt-glare,
.lead-story .tilt-glare,
.cta__panel .tilt-glare {
  background: radial-gradient(
    260px circle at var(--gx, 50%) var(--gy, 50%),
    rgba(255, 255, 255, 0.22) 0%,
    rgba(255, 255, 255, 0) 62%
  );
}

/* Nothing here should survive into a print. */
@media print {
  .tilt-glare {
    display: none;
  }
}
