/*
 * PAP Bubbly Motion — stylesheet
 *
 * Rules of this file:
 *   1. It never declares a colour, background, font, size or spacing value.
 *      Only transform / translate / rotate / scale / opacity / transition.
 *      Everything visual stays owned by Elementor.
 *   2. Every hover effect is gated on (hover:hover) and (pointer:fine) —
 *      a capability test, not a width test.
 *   3. Nothing here hides content. If the JS never boots, the page is
 *      exactly the page Elementor rendered.
 */

:root {
  /* Measured eases — see bubbly-motion-skill/references/css-tokens.md */
  --pap-ease-smooth:         cubic-bezier(0.32, 0.72, 0, 1);
  --pap-ease-soft-overshoot: cubic-bezier(0.59, 1, 0.88, 1.01);
  --pap-ease-bounce:         cubic-bezier(0.34, 1.57, 0.64, 1);
  --pap-ease-soft-bounce:    cubic-bezier(0.34, 1.17, 0.64, 1);
  --pap-ease-click:          cubic-bezier(0.4, 0, 0.2, 1);

  /* Buttons get their own, springier bounce. 2.27 is not a typo. */
  --pap-jig-ease-bounce:     cubic-bezier(0.34, 2.27, 0.64, 1);
}

/*
 * Guard against the word-unfurl reveal (xPercent: 40) pushing a word past the
 * right edge mid-tween and producing a transient horizontal scrollbar.
 * `clip` rather than `hidden`: clip does NOT create a scroll container, so it
 * cannot break position:sticky anywhere on the page.
 */
html.pap-motion { overflow-x: clip; }


/* ==========================================================================
   1. The jig button
   Reciprocal pairs on split schedules: an offset in translate/rotate/scale and
   its exact inverse in transform, with different timings. Net displacement is
   zero, so there is nothing to undo on hover-out.
   ========================================================================== */

.pap-motion .elementor-widget-button .elementor-button {
  transform-origin: center bottom;
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {

  /* 1a. The button box squashes toward its base, like something taking weight.
         0.955 x 1.0471204188 = 1 and 0.925 x 1.0810810811 = 1 exactly — those
         are 1/0.955 and 1/0.925. If they did not invert, the button would
         creep off-centre a little more on every hover. */
  /* :not(.pap-is-magnetic) matters. The magnetic CTA is driven by GSAP, which
     writes an INLINE transform — and an inline transform beats a stylesheet
     one. On that button the counter-scale below would simply be overwritten,
     leaving it squashed to 95.5% for the whole hover instead of springing
     back. It gets the magnetic pull and the per-letter bounce instead. */
  .pap-motion .elementor-widget-button .elementor-button:not(.pap-is-magnetic):is(:hover, :focus-visible) {
    scale: 0.955 0.925;
    transform: scale(1.0471204188, 1.0810810811);
    transition: scale     0.15s       var(--pap-ease-soft-overshoot),
                transform 0.35s 0.15s var(--pap-jig-ease-bounce);
  }

  /* 1b. …and the content counter-squashes by exactly the inverse, on exactly
         the same schedule, so the painted background deforms but the glyphs
         inside never do. This is what replaces the reference implementation's
         separate .jig__bg layer — Elementor buttons have no such layer, and
         adding one would mean taking ownership of the button's background. */
  .pap-motion .elementor-widget-button .elementor-button:not(.pap-is-magnetic):is(:hover, :focus-visible) .elementor-button-content-wrapper {
    scale: 1.0471204188 1.0810810811;
    transform: scale(0.955, 0.925);
    transition: scale     0.15s       var(--pap-ease-soft-overshoot),
                transform 0.35s 0.15s var(--pap-jig-ease-bounce);
  }

  /* 1c. The label kicks down-right and rotates, then springs home slow and
         springy. Kick out fast (0.15s), return slow (0.35s) after a delay that
         equals the kick exactly, so the return starts the instant it lands.

         This is the NO-JS fallback. Once the engine has split a label into
         characters it adds .pap-has-letters to the button and each letter bounces
         individually instead — moving the wrapper and its own characters at
         the same time muddies the stagger into noise, so only one of the two
         ever runs. */
  .pap-motion .elementor-button:not(.pap-has-letters):is(:hover, :focus-visible) .elementor-button-text {
    display: inline-block;
    translate: 0.25em 0.125em 0;
    rotate: 4deg;
    transform: rotate(-4deg) translate3d(-0.25em, -0.125em, 0);
    transition: translate 0.15s       var(--pap-ease-soft-overshoot),
                rotate    0.15s       var(--pap-ease-soft-overshoot),
                transform 0.35s 0.15s var(--pap-jig-ease-bounce);
  }

  /* 1d. If a button ever gets an icon in Elementor, it kicks the OPPOSITE way
         and ~4.5x harder. That contrast is what makes the button read as
         separate parts with separate mass rather than one rigid block. */
  .pap-motion .elementor-button:is(:hover, :focus-visible) .elementor-button-icon {
    display: inline-block;
    translate: -0.5em 0.25em 0;
    rotate: -18deg;
    transform: rotate(18deg) translate3d(0.5em, -0.25em, 0);
    transition: translate 0.15s       var(--pap-ease-soft-overshoot),
                rotate    0.15s       var(--pap-ease-soft-overshoot),
                transform 0.35s 0.15s var(--pap-jig-ease-bounce);
  }
}

/* The press reuses the hover squash values, so a click feels continuous with
   the hover rather than like a second, unrelated effect. Not gated on hover
   capability — touch users press too. */
@media (prefers-reduced-motion: no-preference) {
  .pap-motion .elementor-widget-button .elementor-button:active {
    scale: 0.955 0.925;
    transition: scale 0.1s var(--pap-ease-click);
  }
}


/* ==========================================================================
   2. Layered card hover
   JS tags card-shaped containers with .pap-card and writes a per-card
   --pap-card-rotate. CSS decides how that number is expressed, so the depth
   constants never leave this stylesheet.
   Cards REST FLAT (rotate 0) — the tilt exists only during hover, so the
   page's designed layout is unchanged when nothing is being pointed at.
   ========================================================================== */

.pap-motion .pap-card {
  transition: translate 0.45s var(--pap-ease-soft-bounce),
              rotate    0.45s var(--pap-ease-soft-bounce),
              scale     0.45s var(--pap-ease-soft-bounce);
}

.pap-motion .pap-card .elementor-widget-image img {
  transition: scale 0.5s var(--pap-ease-soft-bounce),
              rotate 0.5s var(--pap-ease-soft-bounce);
  will-change: scale;
}

.pap-motion .pap-card .elementor-widget-heading {
  transition: translate 0.4s 0.03s var(--pap-ease-soft-bounce);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {

  .pap-motion .pap-card:hover,
  .pap-motion .pap-card:focus-within {
    translate: 0 -0.5em;
    rotate: var(--pap-card-rotate, 0deg);
    scale: 1.02;
  }

  /* The image leans the opposite way to the card, so the two layers separate
     instead of moving as one slab. */
  .pap-motion .pap-card:hover .elementor-widget-image img,
  .pap-motion .pap-card:focus-within .elementor-widget-image img {
    scale: 1.055;
    rotate: calc(var(--pap-card-rotate, 0deg) * -0.6);
  }

  .pap-motion .pap-card:hover .elementor-widget-heading,
  .pap-motion .pap-card:focus-within .elementor-widget-heading {
    translate: 0 -0.15em;
  }
}


/* ==========================================================================
   2b. Tabs
   The JS pops the pill and bounces its letters. This only eases the state
   swap the widget already performs — no colour is declared here, the two
   values being transitioned are Elementor's own.
   ========================================================================== */

.pap-motion .myTabs .r-tabs-anchor,
.pap-motion .elementor-widget-ucaddon_uc_bullet_tabs .r-tabs-anchor {
  transform-origin: center center;
  transition: background-color 0.35s var(--pap-ease-soft-bounce),
              color            0.35s var(--pap-ease-soft-bounce);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* Inactive tabs lift a little so the row feels live before you commit.
     The active one is excluded — GSAP owns its transform during the pop. */
  .pap-motion .myTabs .r-tabs-tab:not(.r-tabs-state-active) .r-tabs-anchor:hover {
    translate: 0 -0.12em;
    rotate: -1.5deg;
    transition: translate 0.3s var(--pap-ease-soft-bounce),
                rotate    0.3s var(--pap-ease-soft-bounce);
  }
}

/* ==========================================================================
   2c. The funky row
   Pointer zones and all transforms live in JS. These two rules only make the
   row a clean stage for it.
   ========================================================================== */

.pap-motion .pap-funky-item {
  transform-origin: center center;
  /* The cards move over one another as the row parts, so they must not be
     clipped by the grid track they sit in. */
  position: relative;
  z-index: 0;
}

.pap-motion .pap-funky-item > * {
  transform-origin: center center;
}

/* Belt and braces: a funky card must never also run the .pap-card hover.
   The JS already refuses to add both classes. */
.pap-motion .pap-funky-item.pap-card:hover {
  translate: none;
  rotate: none;
  scale: none;
}

/* ==========================================================================
   2d. Book cards — the layered :has() hover

   Every selector below is pinned to .pap-book__cover, the single image widget
   the JS identified as the cover. It is deliberately NOT ".pap-book
   .elementor-widget-image": that form reaches every image inside the card's
   subtree, and when the card detection was too loose it painted backing
   panels behind the page's full-bleed divider graphics.

   Colours are custom properties read off the card's own button. Sizes are
   measured from the image itself. Nothing here is declared.
   ========================================================================== */

.pap-motion .pap-book .pap-book__cover {
  position: relative;
  isolation: isolate;
}

.pap-motion .pap-book .pap-book__cover::before,
.pap-motion .pap-book .pap-book__cover::after {
  content: "";
  position: absolute;
  top: var(--pap-book-top, 0px);
  left: var(--pap-book-left, 0px);
  width: var(--pap-book-w, 100%);
  height: var(--pap-book-h, 100%);
  border-radius: var(--pap-book-radius, 16px);
  pointer-events: none;
  /* Sits exactly behind the cover, so nothing is visible until hover. */
  z-index: -1;
}

.pap-motion .pap-book .pap-book__cover::before {
  background: var(--pap-book-accent, transparent);
  transition: translate 0.35s 0.1s var(--pap-ease-bounce),
              rotate    0.35s 0.1s var(--pap-ease-bounce);
}

/* The deeper layer travels twice as far, which is what reads as parallax
   rather than one slab sliding. */
.pap-motion .pap-book .pap-book__cover::after {
  background: var(--pap-book-accent, transparent);
  opacity: 0.35;
  z-index: -2;
  transition: translate 0.45s 0.1s var(--pap-ease-bounce);
}

.pap-motion .pap-book .pap-book__cover img {
  transition: translate 0.35s var(--pap-ease-soft-bounce),
              rotate    0.35s var(--pap-ease-soft-bounce),
              scale     0.35s var(--pap-ease-soft-bounce);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {

  .pap-motion .pap-book:hover .pap-book__cover img,
  .pap-motion .pap-book:focus-within .pap-book__cover img {
    translate: 0 -0.5em;
    rotate: var(--pap-book-rotate, 0deg);
    scale: 1.015;
  }

  .pap-motion .pap-book:hover .pap-book__cover::before,
  .pap-motion .pap-book:focus-within .pap-book__cover::before {
    translate: 0 1.5em;
    rotate: calc(var(--pap-book-rotate, 0deg) * -1.3);
  }

  .pap-motion .pap-book:hover .pap-book__cover::after,
  .pap-motion .pap-book:focus-within .pap-book__cover::after {
    translate: 0 3em;
  }
}

/* ==========================================================================
   2e. The travelling nav pill
   The pill's colour, radius and shadow are copied from the active link by
   the JS. This file only positions it and gets the original out of the way.
   ========================================================================== */

.pap-motion .elementskit-navbar-nav.pap-navpill-on {
  position: relative;
}

.pap-motion .pap-navpill {
  position: absolute;
  top: 0;
  left: 0;
  width: 0;
  height: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
}

/* Reuses the site's own overlay tokens so the moving pill keeps the same
   sheen the static one had. */
.pap-motion .pap-navpill::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(
    ellipse at top left,
    var(--overlay-color, transparent) 0%,
    var(--overlay-transparent, transparent) var(--overlay-spread, 70%)
  );
  pointer-events: none;
}

/* The original active pill steps aside — but only while ours exists. The
   class is removed on teardown and below 751px, so the static pill comes
   straight back. */
.pap-motion .pap-navpill-on .ekit-menu-nav-link.active {
  background-color: transparent !important;
  box-shadow: none !important;
}
.pap-motion .pap-navpill-on .ekit-menu-nav-link.active::before {
  opacity: 0 !important;
}
.pap-motion .pap-navpill-on .ekit-menu-nav-link {
  position: relative;
  z-index: 1;
}

/* ==========================================================================
   3. Small micro-interactions
   ========================================================================== */

/* 3a. Nav links lift and tilt very slightly. Kept far gentler than the button
       jig: a nav is scanned, not aimed at, and a big motion here reads as noise. */
.pap-motion .ekit-menu-nav-link {
  transition: translate 0.3s var(--pap-ease-soft-bounce),
              rotate    0.3s var(--pap-ease-soft-bounce);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .pap-motion .ekit-menu-nav-link:hover {
    translate: 0 -0.15em;
    rotate: -1.5deg;
  }
}

/* 3b. Footer list links slide toward the reader on hover. */
.pap-motion .elementor-widget-icon-list .elementor-icon-list-item > a,
.pap-motion .elementor-widget-icon-list .elementor-icon-list-item > span {
  transition: translate 0.32s var(--pap-ease-soft-bounce);
}

@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .pap-motion .elementor-widget-icon-list .elementor-icon-list-item:hover > a,
  .pap-motion .elementor-widget-icon-list .elementor-icon-list-item:hover > span {
    translate: 0.35em 0;
  }
}

/* 3c. Form fields give a small springy acknowledgement on focus. */
.pap-motion .elementor-field-group .elementor-field-textual {
  transition: scale 0.3s var(--pap-ease-bounce);
  transform-origin: center left;
}

@media (prefers-reduced-motion: no-preference) {
  .pap-motion .elementor-field-group .elementor-field-textual:focus {
    scale: 1.012;
  }
}

/* 3c-bis. Split CTA characters. inline-block is applied by the engine too,
   but declaring it here means the letters never reflow between the split and
   the first frame of the tween. */
.pap-motion .pap-char,
.pap-motion .pap-hero-char {
  display: inline-block;
  will-change: auto;
}

/* 3d. Elements the JS is about to take over get promoted once, up front,
       rather than the browser discovering it mid-tween. JS clears this on
       completion so we are not holding layers for the life of the page. */
.pap-motion [data-pap-busy] { will-change: transform, opacity; }

/* 3e. The celebratory burst. Fixed to the viewport, so it can never affect
       the layout of the section that triggers it, and never interactive. */
.pap-emoji-rain {
  position: fixed;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 2147483000;
  contain: strict;
}
.pap-emoji-rain span {
  position: absolute;
  top: 0;
  left: 0;
  display: block;
  line-height: 1;
  user-select: none;
}


/* ==========================================================================
   4. Reduced motion — a hard stop, not a softening
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .pap-motion .elementor-button,
  .pap-motion .elementor-button-content-wrapper,
  .pap-motion .elementor-button-text,
  .pap-motion .elementor-button-icon,
  .pap-motion .pap-char,
  .pap-motion .pap-hero-char,
  .pap-motion .pap-funky-item,
  .pap-motion .pap-funky-item > *,
  .pap-motion .r-tabs-anchor,
  .pap-motion .pap-book .pap-book__cover img,
  .pap-motion .pap-book .pap-book__cover::before,
  .pap-motion .pap-book .pap-book__cover::after,
  .pap-motion .pap-navpill,
  .pap-motion .pap-card,
  .pap-motion .pap-card .elementor-widget-image img,
  .pap-motion .pap-card .elementor-widget-heading,
  .pap-motion .ekit-menu-nav-link,
  .pap-motion .elementor-icon-list-item > a,
  .pap-motion .elementor-field-textual {
    transition: none !important;
    animation: none !important;
    translate: none !important;
    rotate: none !important;
    scale: none !important;
    transform: none !important;
  }
  .pap-emoji-rain { display: none !important; }
}


/* ==========================================================================
   5. Editor safety net
   The PHP never loads this file inside the Elementor editor or its preview
   iframe. This block is belt-and-braces for the case where a caching layer
   serves a cached front-end page into an editor context.
   ========================================================================== */

body.elementor-editor-active .pap-motion *,
body.elementor-editor-preview .pap-motion * {
  opacity: 1 !important;
  visibility: visible !important;
  transform: none !important;
  translate: none !important;
  rotate: none !important;
  scale: none !important;
}
