/* ------------------------------------------------------------------
   Lux Peptides — motion library

   Purpose: everything Elementor Pro's Motion Effects panel does, as
   plain CSS classes you type into the free editor's "CSS Classes"
   field (Advanced tab). No Pro licence, no data attributes — Custom
   Attributes is itself a Pro feature, so every option below is a
   modifier class instead.

   Pairs with motion.js. If JavaScript fails, every element stays
   visible: the hidden state is applied by JS, never by CSS alone.
   That ordering is deliberate — a CSS-first hidden state means a JS
   error blanks the page.
   ------------------------------------------------------------------ */

/* --- Scroll reveal ----------------------------------------------- */
/* Usage: add `lx-reveal` to any container/widget.
   Direction:  lx-reveal--up (default) | --down | --left | --right | --zoom
   Timing:     lx-reveal--slow | --fast
   Delay:      lx-reveal--delay-1 .. --delay-4                        */

.lx-reveal {
  transition:
    opacity var(--duration-extra-long, 600ms) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1)),
    transform var(--duration-extra-long, 600ms) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1));
  transition-delay: calc(var(--lx-order, 0) * 90ms + var(--lx-delay, 0ms));
}

/* JS adds this before the element scrolls into view. */
.lx-reveal.is-offscreen {
  opacity: 0;
  transform: translateY(28px);
}

.lx-reveal--down.is-offscreen  { transform: translateY(-28px); }
.lx-reveal--left.is-offscreen  { transform: translateX(-36px); }
.lx-reveal--right.is-offscreen { transform: translateX(36px); }
.lx-reveal--zoom.is-offscreen  { transform: scale(0.94); }

.lx-reveal--fast { transition-duration: var(--duration-medium, 300ms); }
.lx-reveal--slow { transition-duration: 900ms; }

.lx-reveal--delay-1 { --lx-delay: 100ms; }
.lx-reveal--delay-2 { --lx-delay: 200ms; }
.lx-reveal--delay-3 { --lx-delay: 350ms; }
.lx-reveal--delay-4 { --lx-delay: 500ms; }

/* --- Stagger ------------------------------------------------------ */
/* Put `lx-stagger` on a parent. Its direct children reveal in
   sequence — JS sets --lx-order per child, so the children don't need
   individual delay classes. This is the Shopify theme's data-cascade
   behaviour, rebuilt.                                                */

.lx-stagger > * {
  transition:
    opacity var(--duration-extra-long, 600ms) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1)),
    transform var(--duration-extra-long, 600ms) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1));
  transition-delay: calc(var(--lx-order, 0) * 90ms);
}

.lx-stagger.is-offscreen > * {
  opacity: 0;
  transform: translateY(24px);
}

/* --- Parallax ----------------------------------------------------- */
/* Vertical drift as the element crosses the viewport.
   lx-parallax--slow | (default) | --fast                             */

.lx-parallax {
  --lx-parallax-strength: 40;
  will-change: transform;
}

.lx-parallax--slow { --lx-parallax-strength: 20; }
.lx-parallax--fast { --lx-parallax-strength: 70; }

/* --- Scroll scene -------------------------------------------------- */
/* A tall wrapper holding a sticky panel. Its own scroll progress drives
   any `lx-parallax` children, which is the only way parallax can work
   inside something that is pinned to the viewport.

   Movement distance defaults to strength x 6, since a scene scrolls for
   far longer than an element passing through the viewport. Override per
   element with --lx-parallax-distance, and reverse with
   --lx-parallax-direction: down.                                       */

.lx-scene {
  position: relative;
}

.lx-scene__pin {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

@media (prefers-reduced-motion: reduce) {
  .lx-scene { height: auto !important; }
  .lx-scene__pin { position: static; height: auto; padding: 80px 0; }
}

/* --- Scroll rotate ------------------------------------------------ */
/* The gallery tilt from the Shopify theme's parallax section: items
   rotate in alternating directions as they scroll up. Add
   `lx-tilt` to the gallery wrapper; JS handles alternation.          */

.lx-tilt > * {
  will-change: transform;
  transition: transform 120ms linear;
}

/* --- Marquee ------------------------------------------------------ */
/* The "PRECISION PURITY PERFORMANCE" band. Add `lx-marquee` to a
   container; JS duplicates the content so the loop is seamless.
   lx-marquee--reverse | --slow | --fast                              */

.lx-marquee {
  --lx-marquee-duration: 28s;
  display: flex;
  overflow: hidden;
  width: 100%;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}

.lx-marquee__track {
  display: flex;
  flex: 0 0 auto;
  align-items: center;
  gap: 40px;
  padding-inline-end: 40px;
  min-width: 100%;
  animation: lx-marquee-scroll var(--lx-marquee-duration) linear infinite;
}

.lx-marquee--reverse .lx-marquee__track { animation-direction: reverse; }
.lx-marquee--slow    { --lx-marquee-duration: 45s; }
.lx-marquee--fast    { --lx-marquee-duration: 16s; }

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

@keyframes lx-marquee-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(-100%); }
}

/* --- Hover zoom on media ------------------------------------------ */

.lx-hover-zoom { overflow: hidden; }

.lx-hover-zoom img {
  transition: transform var(--onhover-picture-duration, 1.5s) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1));
}

@media (hover: hover) {
  .lx-hover-zoom:hover img { transform: scale(var(--onhover-picture-scale, 1.05)); }
}

/* --- Sticky ------------------------------------------------------- */
/* Elementor's sticky is Pro. This is position: sticky, which covers
   the common cases (sticky header, sticky product summary column).
   Note: it only works if no ancestor has overflow: hidden — that's
   the usual reason "sticky isn't working".                           */

.lx-sticky {
  position: sticky;
  top: var(--lx-sticky-offset, 24px);
  z-index: 10;
}

.lx-sticky--header {
  --lx-sticky-offset: 0px;
  transition: transform var(--duration-medium, 300ms) ease,
              box-shadow var(--duration-medium, 300ms) ease;
}

/* JS toggles this once the page has scrolled past the header. */
.lx-sticky--header.is-stuck {
  box-shadow: 0 1px 0 var(--color-header-border, rgba(0, 0, 0, 0.08));
}

/* Hide-on-scroll-down, show-on-scroll-up. Add alongside --header. */
.lx-sticky--auto-hide.is-hidden { transform: translateY(-100%); }

/* --- Count up ----------------------------------------------------- */
/* Add `lx-count` to a heading whose text is a number ("120,000+").
   JS animates from 0 to that value once, on first view. Formatting,
   prefixes, and suffixes in the original text are preserved.         */

.lx-count { font-variant-numeric: tabular-nums; }

/* --- Underline sweep ---------------------------------------------- */

.lx-link-sweep {
  position: relative;
  text-decoration: none;
}

.lx-link-sweep::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform var(--duration-medium, 300ms) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1));
}

@media (hover: hover) {
  .lx-link-sweep:hover::after {
    transform: scaleX(1);
    transform-origin: left;
  }
}

/* --- Reduced motion ----------------------------------------------- */
/* Anyone who has asked their OS to reduce motion gets the content,
   not the choreography. Elements land in their final state rather
   than being stuck mid-animation. */

@media (prefers-reduced-motion: reduce) {
  .lx-reveal,
  .lx-reveal.is-offscreen,
  .lx-stagger > *,
  .lx-stagger.is-offscreen > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  .lx-marquee__track { animation: none; }
  .lx-parallax,
  .lx-tilt > * { transform: none !important; }
}

/* --- Scrollytelling ------------------------------------------------ */
/* The "image with scrolling text" pattern: media pins in place while
   text steps scroll past, and each step swaps the visible media.

   Markup in Elementor:
     Container            -> lx-scrollytell
       Column A           -> lx-scrollytell__media   (children = the images)
       Column B           -> lx-scrollytell__steps   (children = text blocks)

   JS matches them by index, so step 3 shows image 3. Extra images are
   ignored; extra steps keep the last image.                          */

.lx-scrollytell__media {
  position: sticky;
  top: var(--lx-scrollytell-offset, 15vh);
  align-self: start;
}

.lx-scrollytell__media > * {
  transition: opacity var(--duration-long, 500ms) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1));
}

/* Stack the media items so they cross-fade in place instead of
   stacking down the page. The first child keeps normal flow so the
   container still has a height. */
.lx-scrollytell__media > * + * {
  position: absolute;
  inset: 0;
}

.lx-scrollytell__media.is-ready > * { opacity: 0; }
.lx-scrollytell__media.is-ready > .is-active { opacity: 1; }

.lx-scrollytell__steps > * {
  min-height: 70vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
}

/* --- Blur on scroll ------------------------------------------------ */
/* Hero softens and scales down as it leaves the viewport — the blur
   banner effect. Strength scales with how far past it you've scrolled. */

.lx-blur-scroll {
  will-change: filter, transform;
  transform-origin: center top;
}

@media (prefers-reduced-motion: reduce) {
  .lx-blur-scroll { filter: none !important; transform: none !important; }
  .lx-scrollytell__media.is-ready > * { opacity: 1; }
  .lx-scrollytell__media > * + * { position: static; }
}

/* --- Stagger for shortcode-rendered product grids ------------------ */
/* `lx-stagger` targets direct children, but a Shortcode widget nests
   the grid two levels down (widget > .woocommerce > ul.products > li).
   This variant reaches the list items instead. Put it on the Shortcode
   widget itself.                                                      */

.lx-stagger-products ul.products > li {
  transition:
    opacity var(--duration-extra-long, 600ms) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1)),
    transform var(--duration-extra-long, 600ms) var(--ease-out-slow, cubic-bezier(0, 0, 0.3, 1));
  transition-delay: calc(var(--lx-order, 0) * 90ms);
}

.lx-stagger-products.is-offscreen ul.products > li {
  opacity: 0;
  transform: translateY(24px);
}

@media (prefers-reduced-motion: reduce) {
  .lx-stagger-products ul.products > li,
  .lx-stagger-products.is-offscreen ul.products > li {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}
