/* =================================================================
   MOTION
   Scroll-down cue, scroll reveal, hero headline intro, staggered grid reveal.
   Loaded by inc/enqueue.php (order matters: see the list there).
   ================================================================= */

/* ---------------------------------------------------------------
   Scroll-down cue — gentle bob to invite a click.
   Add .scroll-down to the icon widget. Honours reduced-motion.
--------------------------------------------------------------- */
.scroll-down {
    cursor: pointer;
    animation: scroll-down-bob 1.6s ease-in-out infinite;
}

@keyframes scroll-down-bob {
    0%, 100% { transform: translateY(0); }
    50%      { transform: translateY(6px); }   /* down, then back */
}

/* Respect users who prefer less motion */
@media (prefers-reduced-motion: reduce) {
    .scroll-down { animation: none; }
}

/* ---------------------------------------------------------------
   Reveal on scroll — subtle fade-in-up for headings & paragraphs.
   Applied site-wide to Elementor heading + text widgets by the
   IntersectionObserver in assets/js/reveal.js (adds .is-revealed as each
   enters the viewport).

   The hidden start-state is scoped to html.ip-reveal, a class the
   script sets before first paint. If JS is off or fails, the class
   is never added, so content shows normally — it can never get
   stuck invisible.

   Opt a section out by adding .no-reveal to it (or any ancestor).
--------------------------------------------------------------- */
.ip-reveal .elementor-widget-heading,
.ip-reveal .elementor-widget-text-editor {
    opacity: 0;
    transform: translateY(18px);
    transition: opacity 0.8s cubic-bezier(0.22, 1, 0.36, 1),
                transform 0.8s cubic-bezier(0.22, 1, 0.36, 1);
}

.ip-reveal .elementor-widget-heading.is-revealed,
.ip-reveal .elementor-widget-text-editor.is-revealed {
    opacity: 1;
    transform: none;
}

/* Carousels (Swiper): reveal ALL slide content together when the carousel
   scrolls into view. Per-slide observation is unreliable in a carousel
   because Swiper clones slides and moves them with transforms — so the
   script tags the .swiper with .is-revealed-carousel, and this descendant
   rule flips every heading/text inside it (clones included), so off-screen
   slides are already revealed before you swipe to them. */
.ip-reveal .swiper.is-revealed-carousel .elementor-widget-heading,
.ip-reveal .swiper.is-revealed-carousel .elementor-widget-text-editor {
    opacity: 1;
    transform: none;
}

/* Never hide opted-out content, even before the script runs */
.ip-reveal .no-reveal .elementor-widget-heading,
.ip-reveal .no-reveal .elementor-widget-text-editor,
.ip-reveal .elementor-widget-heading.no-reveal,
.ip-reveal .elementor-widget-text-editor.no-reveal {
    opacity: 1;
    transform: none;
    transition: none;
}

@media (prefers-reduced-motion: reduce) {
    .ip-reveal .elementor-widget-heading,
    .ip-reveal .elementor-widget-text-editor {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}

/* ---------------------------------------------------------------
   Hero headline — staggered per-line intro (hero only).
   The four .key-headline lines fade up one after another on load:
   "Protect your idea," -> "brand or product" -> "with registered"
   -> "IP attorneys".

   Scoped to .key-headline-wrap, which is unique to the hero. The
   first rule also opts the hero out of the site-wide scroll reveal
   above, so there's no need to add .no-reveal to it.
--------------------------------------------------------------- */

/* Hero runs its own intro — keep it out of the site-wide reveal */
.ip-reveal .key-headline-wrap.elementor-widget-heading {
    opacity: 1;
    transform: none;
    transition: none;
}

/* Each beat (.hl-beat) starts hidden and eases up in turn.
   inline-block so it can transform yet still sit inline within a
   line. Order is set by .b1..b6; .b6 is used on BOTH closing lines
   ("with registered" + "IP attorneys") so they arrive together.

   Choreography: "Protect / your / idea" ripple in quick succession
   (0.15s apart), then idea, brand and product each get a held
   "moment" (longer gaps), and the closing line lands a bit quicker.
   Retune the delays to change the pacing. */
.key-headline-wrap h1 .hl-beat {
    display: inline-block;
    animation: hero-line-in 0.9s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.key-headline-wrap h1 .hl-beat.b1 { animation-delay: 0.30s; }  /* Protect  \             */
.key-headline-wrap h1 .hl-beat.b2 { animation-delay: 0.45s; }  /* your      } quick trio */
.key-headline-wrap h1 .hl-beat.b3 { animation-delay: 0.60s; }  /* idea,    /  -> moment   */
.key-headline-wrap h1 .hl-beat.b4 { animation-delay: 1.20s; }  /* brand       -> moment   */
.key-headline-wrap h1 .hl-beat.b5 { animation-delay: 1.80s; }  /* or product  -> moment   */
.key-headline-wrap h1 .hl-beat.b6 { animation-delay: 2.40s; }  /* with registered IP attorneys */

@keyframes hero-line-in {
    from { opacity: 0; transform: translateY(0.3em); }
    to   { opacity: 1; transform: none; }
}

/* Reduced motion: reveal it all at once, no movement */
@media (prefers-reduced-motion: reduce) {
    .key-headline-wrap h1 .hl-beat { animation: none; }
}
/* ---------------------------------------------------------------
   Staggered grid reveal — loop-grid items fade up one after another
   as the grid scrolls into view. Elementor's own animations can't
   sequence loop items; assets/js/reveal.js assigns each
   .e-loop-item an escalating --stagger delay (capped so the full
   wave lands in ~1s regardless of item count) and tags the grid
   .is-revealed-grid when it enters the viewport.

   Two-class model:
     .stagger-grid   the mechanism (put on any loop grid)
     .client-logos   the logo wall — opts into it below, so the name
                     stays semantic and the effect stays reusable.

   The delay lives ONLY on the revealed state, so it applies on the
   way in; a hide (if ever) is instant and uniform, never a reverse
   wave.

   FAIL-SAFE + NO-FLASH: the hide is gated on html.stagger-armed,
   set by a tiny wp_head script (inc/gates.php) BEFORE first paint (same pattern as
   .ip-reveal for headings). Gating on <html> pre-paint is what stops
   the "wiggle": if the hide were applied later by the footer script,
   the logos would paint visible first, then snap hidden, then slide
   in — reading as a jitter. Pre-paint = hidden from frame one, so
   the reveal is a clean fade-up. And revealAll() strips the class on
   any failure path (stale reveal.js, JS error, no IO support),
   so the wall can never be stuck white — it just shows normally.
   Tuning: --stagger-step (per-item gap), 0.6s (fade), 14px (rise).
--------------------------------------------------------------- */
.client-logos { --stagger-step: 45; }

.stagger-armed .stagger-grid .e-loop-item,
.stagger-armed .client-logos .e-loop-item {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 0.6s cubic-bezier(0.22, 1, 0.36, 1),
                transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}
.stagger-armed .stagger-grid.is-revealed-grid .e-loop-item,
.stagger-armed .client-logos.is-revealed-grid .e-loop-item {
    opacity: 1;
    transform: none;
    transition-delay: var(--stagger, 0ms);   /* stagger on the way IN only */
}

@media (prefers-reduced-motion: reduce) {
    .stagger-armed .stagger-grid .e-loop-item,
    .stagger-armed .client-logos .e-loop-item {
        opacity: 1 !important;
        transform: none !important;
        transition: none !important;
    }
}
