/* ═══════════════════════════════════════════════════════════
   TUNEX — POINTER TILT
   The interactive half of the 21st.dev "Tilt Flip Card".

   That component pairs a pointer-following 3D tilt with a
   click-to-flip. The tilt is taken; the flip is not, and the
   reason is worth keeping next to the code: its front face
   carries only a logo and a card number, with the title, body
   and image all on the back. Mapped onto these grids that
   turns nine described services into nine anonymous icons a
   visitor has to click one at a time. The copy on these cards
   is what sells, so it stays on the front.

   Tilt maths match the original: the pointer position across
   the card maps to rotateY -15..15deg and rotateX 15..-15deg,
   reset on leave. A soft spotlight follows the pointer as well,
   which the original gets from its card background.

   One delegated pointer listener drives every card rather than
   ~90 individual ones, and writes are batched into a single rAF
   so a fast sweep across a grid still costs one frame.

   Desktop and fine pointers only, and off for reduced motion —
   the tilt is decoration and never gates any content.
   ═══════════════════════════════════════════════════════════ */

/* The tilt needs a perspective ancestor. Applying it to the grid
   rather than the card means neighbouring cards share one vanishing
   point, so a row tilts like a row instead of each card pivoting in
   its own private space. */
.tlt-scope {
  perspective: 1200px;
  perspective-origin: 50% 50%;
}

.tlt {
  transform: rotateX(var(--tlt-rx, 0deg)) rotateY(var(--tlt-ry, 0deg));
  transform-style: preserve-3d;
  transition: transform .45s cubic-bezier(.16, 1, .3, 1), box-shadow .45s;
  will-change: auto;
}

/* while the pointer is on the card, track it immediately; the slow
   transition above is only for settling back on leave */
.tlt.is-tilting {
  transition: transform .08s linear, box-shadow .45s;
  will-change: transform;
}

/* spotlight — a radial highlight that follows the pointer. Sits above
   the card's own background but below its content. */
.tlt-shine {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity .35s;
  background: radial-gradient(
    620px circle at var(--tlt-mx, 50%) var(--tlt-my, 50%),
    rgba(255, 255, 255, .13),
    transparent 42%
  );
  z-index: 0;
}

/* on the light cards a white wash is invisible; warm it toward the
   brand red instead so the highlight actually reads */
.is-light .tlt-shine,
.tlt--light .tlt-shine {
  background: radial-gradient(
    620px circle at var(--tlt-mx, 50%) var(--tlt-my, 50%),
    rgba(196, 30, 58, .10),
    transparent 42%
  );
}

.tlt.is-tilting .tlt-shine { opacity: 1 }

/* the card's own children need to sit above the shine layer */
.tlt > *:not(.tlt-shine) { position: relative; z-index: 1 }

/* lift, so the tilt reads as the card coming toward the pointer */
.tlt.is-tilting { box-shadow: 0 26px 56px rgba(0, 0, 0, .30) }
.is-light .tlt.is-tilting,
.tlt--light.tlt.is-tilting { box-shadow: 0 26px 56px rgba(0, 0, 0, .13) }

/* Cards that already animate transform on hover would fight the tilt —
   the JS writes transform on the element, and a hover rule setting
   translateY would simply replace it. Neutralise those hover rules
   only while tilt is active; the lift above stands in for them. */
.tlt-on .svcg-card:hover,
.tlt-on .case-card:hover,
.tlt-on .bsr-card:hover,
.tlt-on .res-card:hover,
.tlt-on .pricing-card:hover {
  transform: rotateX(var(--tlt-rx, 0deg)) rotateY(var(--tlt-ry, 0deg));
}

@media (prefers-reduced-motion: reduce) {
  .tlt { transition: none }
  .tlt-shine { display: none }
}
