/* ============================================================================
   Scout Your Stars — premium motion layer
   ----------------------------------------------------------------------------
   Purely additive. Touches no existing rule, changes no content, no layout, no
   colour of anything already on the page. Everything here is motion, or polish
   that only becomes visible during motion.

   Palette is taken from the site's own Elementor globals:
     accent gold  #C6A75E      cream    #EBDCB8
     lavender     #B18AFF      ink      #111111 / #242328

   SAFETY CONTRACT
   Pre-animation "hidden" states are scoped to html.sys-anim, a class added by
   premium.js only when it has GSAP and the browser can honour motion. If the
   JS fails to load, throws, or is blocked, that class never appears and every
   element renders exactly as WordPress shipped it. premium.js also carries a
   watchdog that force-reveals everything if anything stalls. Content must never
   be invisible because a script had a bad day.
   ========================================================================== */

:root {
  --sys-gold: #c6a75e;
  --sys-cream: #ebdcb8;
  --sys-lav: #b18aff;
  --sys-ink: #111111;
  --sys-ink-2: #242328;

  /* One easing vocabulary, used everywhere. Restraint is the whole point:
     short distances, long durations, no bounce. Bounce reads as cheap. */
  --sys-ease: cubic-bezier(0.22, 1, 0.36, 1);
  --sys-ease-soft: cubic-bezier(0.33, 1, 0.68, 1);
  --sys-dur: 0.9s;
  --sys-rise: 16px;
}

/* ---------------------------------------------------------------- reveals  */
/* Only ever applied once JS confirms it can animate them back in again.      */

html.sys-anim [data-sys-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--sys-rise), 0);
  will-change: opacity, transform;
}

html.sys-anim [data-sys-reveal="fade"] {
  transform: none;
}

html.sys-anim [data-sys-reveal="scale"] {
  opacity: 0;
  transform: translate3d(0, 12px, 0) scale(0.985);
}

/* Media reveals behind a wipe rather than just fading: the image itself stays
   put and the mask retracts, which reads as deliberate rather than droppy. */
html.sys-anim [data-sys-reveal="media"] {
  opacity: 0;
  clip-path: inset(0 0 100% 0);
  transform: none;
}

/* Cleared by JS after a reveal completes — leaving will-change on hundreds of
   nodes permanently is a real memory and compositing cost. */
[data-sys-done] {
  will-change: auto !important;
  /* The media reveal animates clip-path from hidden to inset(0%) and, left
     alone, keeps inset(0%) forever. That clips anything drawn outside the
     element's box — which silently cut off the gold frame on the hero image.
     Once a reveal has finished, the clip has no job, so remove it. */
  clip-path: none !important;
}

/* Booting. premium.js is deferred, so without this the browser paints the hero
   as plain HTML first — wheel, photo and headline on screen — and premium.js
   then hides it to animate it back in: a flash before the entrance. The inline
   <head> script adds html.sys-boot before first paint (not for reduced
   motion); premium.js removes it the moment it takes over, in the same task in
   which it sets the real pre-animation states, so nothing flashes in between.
   The hero is the same element premium.js animates: the home's .v2-hero, or
   an Elementor page's first container.
   The floating WhatsApp button is held too: it waits for the hero to be
   scrolled past (premium.js, buildFloatGate), and without this it blinked on
   for a moment before that took over.
   If premium.js never runs (blocked, failed), the failsafe animation shows
   both anyway after 3s. */
html.sys-boot:not(.sys-anim) .v2-hero,
html.sys-boot:not(.sys-anim) [data-sys-root] > [data-sys-section]:first-child,
html.sys-boot:not(.sys-anim) .elementor[data-elementor-type="wp-page"] > .e-con.e-parent:first-child,
html.sys-boot:not(.sys-anim) .elementor-fixed {
  opacity: 0;
  visibility: hidden;
  animation: sys-boot-failsafe 0.4s linear 3s forwards;
}

@keyframes sys-boot-failsafe {
  to { opacity: 1; visibility: visible; }
}

/* ------------------------------------------------------------ hero words   */
/* Headline lines are wrapped by JS in a clipping span so the text rises out
   of its own baseline instead of floating up from nowhere.                   */

.sys-line-mask {
  display: block;
  overflow: hidden;
  /* Descenders (g, y, p) get clipped by a tight mask, so pad and pull back. */
  padding-bottom: 0.14em;
  margin-bottom: -0.14em;
}

.sys-line {
  display: block;
  will-change: transform;
}

/* NOTE: the pre-animation offset for lines is deliberately NOT set here.
   SplitText can re-split a heading (font load, resize), and any CSS rule
   keyed on .sys-line would re-hide the fresh lines while GSAP is still
   animating the discarded ones — stranding the headline off-screen. The
   offset is applied in JS at split time instead, so a re-split can only ever
   produce visible text. The wrapper's own opacity:0 covers the brief window
   before the split runs, so there is no flash. */

/* ------------------------------------------------------- page transitions  */

#sys-curtain {
  position: fixed;
  inset: 0;
  z-index: 2147483646;
  pointer-events: none;
  opacity: 0;
  background: var(--u-curtain);
  /* No transform here on purpose. GSAP decomposes an existing CSS transform
     into its own baseline and then applies yPercent ON TOP of it, so a
     translate3d(0,100%,0) here would park the curtain at 200% and it would
     animate to 100% — permanently off-screen, transition invisible.
     The off-screen start is set in JS instead. Until that runs the curtain is
     full-screen but opacity:0 and pointer-events:none, so it is inert. */
}

#sys-curtain.is-active {
  pointer-events: all;
}

/* A hairline of brand gold rides the leading edge of the curtain. Small
   detail, but it is the difference between "a black box slid over the page"
   and something that looks designed. */
#sys-curtain::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--u-gold) 35%, var(--u-gold-soft) 50%, var(--u-gold) 65%, transparent);
  opacity: 0.9;
}

#sys-curtain .sys-curtain-mark {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  opacity: 0;
}

/* Arrival cover.
   premium.js is deferred, so it cannot run before first paint. Without this the
   new page paints uncovered and the JS curtain then snaps back over content the
   visitor can already see — which reads as the brand mark appearing a second
   time. A tiny synchronous script in <head> adds .sys-arriving when the visit
   came from a curtain transition, so the page is covered from the very first
   frame and the mark is continuous across the navigation.

   The gradient is duplicated from #sys-curtain on purpose: the handoff to the
   real curtain must be pixel-identical or it flickers. Keep the two in sync. */
html.sys-arriving::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 2147483646;
  pointer-events: none;
  background: var(--u-curtain);
}

html.sys-arriving::after {
  content: "Scout Your Stars";
  position: fixed;
  inset: 0;
  z-index: 2147483646;
  pointer-events: none;
  display: grid;
  place-items: center;
  font-family: "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: clamp(11px, 1.1vw, 13px);
  letter-spacing: 0.42em;
  text-indent: 0.42em;
  text-transform: uppercase;
  color: var(--u-curtain-ink);
  opacity: 0.82;
}

/* The cover must never outlive the script. If premium.js fails to load at all,
   this clears it after a beat rather than leaving a black screen forever. */
@keyframes sys-arrival-failsafe {
  to { opacity: 0; visibility: hidden; }
}

html.sys-arriving::before,
html.sys-arriving::after {
  animation: sys-arrival-failsafe 0.4s linear 4s forwards;
}

#sys-curtain .sys-curtain-mark span {
  font-family: "Montserrat", system-ui, -apple-system, "Segoe UI", sans-serif;
  font-size: clamp(11px, 1.1vw, 13px);
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--u-curtain-ink);
  opacity: 0.82;
  /* Optical correction: letter-spacing pads the right side only. */
  text-indent: 0.42em;
}

/* --------------------------------------------------------- scroll progress */

#sys-progress {
  position: fixed;
  top: 0;
  left: 0;
  height: 2px;
  width: 100%;
  z-index: 2147483645;
  pointer-events: none;
  transform: scaleX(0);
  transform-origin: 0 50%;
  background: var(--u-metal);
  opacity: 0.95;
}

/* ------------------------------------------------------------- header lift */
/* Astra's header keeps its own colours; this only adds depth once the page
   has actually moved, and only if the header is sticky to begin with.        */

.sys-header-scrolled #masthead[data-sys-sticky="1"] {
  box-shadow: var(--u-shadow-header);
  backdrop-filter: saturate(1.25) blur(9px);
  -webkit-backdrop-filter: saturate(1.25) blur(9px);
  transition: box-shadow 0.5s var(--sys-ease-soft), backdrop-filter 0.5s var(--sys-ease-soft);
}

/* -------------------------------------------------------- micro-interaction */
/* Hover polish only. No change to resting appearance, so nothing looks
   different from the site as it stands until a cursor is actually on it.     */

@media (hover: hover) and (pointer: fine) {
  .elementor-button,
  .ekit-btn,
  .elementskit-btn {
    transition: transform 0.45s var(--sys-ease), box-shadow 0.45s var(--sys-ease), filter 0.45s var(--sys-ease);
  }

  .elementor-button:hover,
  .ekit-btn:hover,
  .elementskit-btn:hover {
    transform: translate3d(0, -2px, 0);
    box-shadow: var(--u-shadow-btn);
    filter: saturate(1.06);
  }

  .elementor-button:active,
  .ekit-btn:active {
    transform: translate3d(0, 0, 0);
    transition-duration: 0.12s;
  }

  /* Slow, small image push-in. Anything faster or larger looks like a
     stock-photo carousel rather than a considered site. */
  [data-sys-imgzoom] {
    overflow: hidden;
  }

  [data-sys-imgzoom] img {
    transition: transform 1.4s var(--sys-ease-soft);
    will-change: transform;
  }

  [data-sys-imgzoom]:hover img {
    transform: scale(1.045);
  }

  .elementor-icon-box-wrapper {
    transition: transform 0.5s var(--sys-ease);
  }

  .elementor-icon-box-wrapper:hover {
    transform: translate3d(0, -4px, 0);
  }
}

/* Keyboard users get the same affordances a mouse user gets. */
.elementor-button:focus-visible,
a:focus-visible {
  outline: 2px solid var(--u-gold-deep);
  outline-offset: 3px;
  border-radius: 2px;
}

/* -------------------------------------------------------------- smoothing  */

html.sys-lenis,
html.sys-lenis body {
  /* Lenis drives scrolling itself; leaving native smooth on fights it. */
  scroll-behavior: auto;
}

html.sys-lenis.lenis-stopped body {
  overflow: hidden;
}

/* --------------------------------------------------------- reduced motion  */
/* Not a checkbox exercise. Vestibular disorders are real and this is a
   wellness-adjacent audience. Everything still reveals — instantly.          */

@media (prefers-reduced-motion: reduce) {
  html.sys-anim [data-sys-reveal],
  html.sys-anim .sys-line {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
  }

  #sys-curtain,
  #sys-progress,
  html.sys-arriving::before,
  html.sys-arriving::after {
    display: none !important;
  }

  [data-sys-imgzoom] img,
  .elementor-button,
  .elementor-icon-box-wrapper {
    transition: none !important;
  }
}

/* Printing a page mid-animation should not print blank boxes. */
@media print {
  html.sys-anim [data-sys-reveal],
  html.sys-anim .sys-line,
  html.sys-boot .v2-hero,
  html.sys-boot .elementor[data-elementor-type="wp-page"] > .e-con.e-parent:first-child {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
  }

  #sys-curtain,
  #sys-progress {
    display: none !important;
  }
}
