/* ============================================================
   PRAVI — SHARED MOTION ENGINE
   Generalized from the Pravi Labs page so Home / About / Services /
   Careers / Resources share one identical cinematic motion layer.
   Layout & content are untouched — this only adds scroll reveals,
   word-level heading rises, scroll-progress, ambient blobs, an intro
   curtain (Home only), parallax, and chrome (nav/footer/cta) motion.

   IMPORTANT: This file is loaded ONLY on the five pages above, never on
   the Labs page, so the Labs page stays byte-for-byte unchanged.
   All animation is hand-built (CSS keyframes + IntersectionObserver +
   rAF scroll) — no external motion library (vanilla React via CDN).
   ============================================================ */

(function () {
  const { useRef, useEffect, useState } = React;

  const REDUCED = typeof window !== 'undefined' && window.matchMedia
    && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  /* Intro curtain on-screen duration (ms). Above-fold hero reveals are held
     until this elapses so they play AS the curtain lifts (Home only). */
  const INTRO_MS = 1450;

  /* ===================================================================
     REVEAL ENGINE — IntersectionObserver. Adds `.in` to reveal elements
     and `.seen` to section dividers. Non-destructive: only ever adds the
     class, so scroll-linked effects survive. Stagger handled in CSS via an
     inline `--d` delay. Also auto-tags shared chrome (footer cols, CTA)
     with reveal classes so the common components animate without editing
     common.jsx (which the Labs page also renders).
     =================================================================== */
  function useReveal(rootRef) {
    useScrollFX(rootRef);
    useEffect(() => {
      if (!rootRef.current) return;
      const root = rootRef.current;

      /* Auto-tag shared chrome (only these pages load motion.jsx). */
      const tag = (el, cls, d) => {
        if (!el || el.classList.contains(cls)) return;
        el.classList.add(cls);
        if (d != null) el.style.setProperty('--d', d + 'ms');
      };

      // Inject ambient scrolling background elements if not already present
      if (!root.querySelector('.ambient-scroller')) {
        const scroller = document.createElement('div');
        scroller.className = 'ambient-scroller';
        scroller.innerHTML = `
          <div class="ambient-grid"></div>
          <div class="ambient-blob tone-blue"></div>
          <div class="ambient-blob tone-orange"></div>
        `;
        root.appendChild(scroller);
      }

      // Check if loader should show in this session lifecycle (fresh load or page reload)
      const isReload = window.performance && window.performance.getEntriesByType && 
                       window.performance.getEntriesByType('navigation')[0] && 
                       window.performance.getEntriesByType('navigation')[0].type === 'reload';
      const hasPlayed = sessionStorage.getItem('pravi_loader_played');
      const shouldShowLoader = (isReload || !hasPlayed) && !REDUCED;

      let hasLoader = !!root.querySelector('.cinematic-loader');
      let loader = null;

      if (shouldShowLoader) {
        sessionStorage.setItem('pravi_loader_played', 'true');
        if (!hasLoader) {
          hasLoader = true;
          loader = document.createElement('div');
          loader.className = 'cinematic-loader';
          loader.innerHTML = `
            <div class="cinematic-loader-content">
              <div class="cinematic-logo-container">
                <div class="cinematic-logo-mark">
                  <span class="b1"></span>
                  <span class="b2"></span>
                </div>
                <div class="cinematic-logo-word">
                  <span>Pravi</span>
                  <em>Research</em>
                </div>
              </div>
            </div>
          `;
          document.body.appendChild(loader);

          setTimeout(() => {
            loader.classList.add('animate-content');
          }, 80);

          setTimeout(() => {
            loader.classList.add('lift');
          }, INTRO_MS);

          setTimeout(() => {
            loader.remove();
          }, INTRO_MS + 1100);
        }
      } else {
        // Skip loader on internal navigation
        const existingReactLoader = root.querySelector('.cinematic-loader');
        if (existingReactLoader) {
          existingReactLoader.remove();
        }
        hasLoader = false;
      }

      // footer columns + brand
      root.querySelectorAll('.home-footer-grid > div').forEach((el, i) => tag(el, 'reveal', i * 80));
      // footer bottom bar
      const fbar = root.querySelector('footer .container > div:last-child');
      if (fbar && fbar.querySelector('a[href*="Privacy"]')) tag(fbar, 'reveal', 360);

      // Auto-stagger direct sibling reveals
      const revealElements = root.querySelectorAll('.reveal, .reveal-l, .reveal-r');
      const parents = new Set();
      revealElements.forEach(el => {
        if (el.parentElement) parents.add(el.parentElement);
      });
      parents.forEach(parent => {
        const children = Array.from(parent.children).filter(c => 
          c.classList.contains('reveal') || c.classList.contains('reveal-l') || c.classList.contains('reveal-r')
        );
        if (children.length > 1) {
          children.forEach((child, i) => {
            if (!child.style.getPropertyValue('--d') && !child.style.transitionDelay) {
              child.style.setProperty('--d', (i * 75) + 'ms');
            }
          });
        }
      });

      const cls = (el) => el.classList.contains('motion-sec') ? 'seen' : 'in';
      const sel = '.reveal, .reveal-l, .reveal-r, .motion-sec, .motion-head';
      const els = root.querySelectorAll(sel);
      if (REDUCED || !('IntersectionObserver' in window)) {
        els.forEach(el => el.classList.add(cls(el)));
        return;
      }
      const io = new IntersectionObserver((entries) => {
        entries.forEach(e => {
          const c = cls(e.target);
          if (e.isIntersecting) e.target.classList.add(c);
          else if (c === 'in') e.target.classList.remove('in'); // replay reveals on re-scroll
        });
      }, { threshold: 0.12, rootMargin: '0px 0px -60px 0px' });

      // If a Home-style intro curtain is present, hold observation until it lifts.
      const start = () => root.querySelectorAll(sel).forEach(el => io.observe(el));
      let t;
      if (hasLoader) t = setTimeout(start, INTRO_MS);
      else start();

      // Safety: never leave content invisible if something goes wrong.
      const safety = setTimeout(() => {
        if (REDUCED) return;
        root.querySelectorAll(sel).forEach(el => {
          const r = el.getBoundingClientRect();
          if (r.top < window.innerHeight && r.bottom > 0) el.classList.add(cls(el));
        });
      }, (hasLoader ? INTRO_MS : 0) + 1600);

      return () => { if (t) clearTimeout(t); clearTimeout(safety); io.disconnect(); };
    }, []);
  }

  /* ===================================================================
     SCROLL FX — single rAF-throttled listener → drives parallax
     (--scrollY on stage) and the top scroll-progress bar (--p, 0..1).
     =================================================================== */
  function useScrollFX(rootRef) {
    useEffect(() => {
      if (!rootRef.current || REDUCED) return;
      const root = rootRef.current;
      if (root.dataset.scrollFxActive) return;
      root.dataset.scrollFxActive = 'true';
      let ticking = false;
      const apply = () => {
        const y = window.scrollY || window.pageYOffset || 0;
        const max = (document.documentElement.scrollHeight - window.innerHeight) || 1;
        root.style.setProperty('--scrollY', y + 'px');
        root.style.setProperty('--p', Math.min(1, Math.max(0, y / max)).toFixed(4));
        // Viewport-relative, bounded parallax for images (no edge gaps on long pages).
        const vh = window.innerHeight || 1;
        root.querySelectorAll('.motion-par-img').forEach(el => {
          const r = el.getBoundingClientRect();
          const center = r.top + r.height / 2;
          const pp = Math.max(-1, Math.min(1, (center - vh / 2) / (vh / 2)));
          el.style.setProperty('--pp', pp.toFixed(3));
        });
        ticking = false;
      };
      const onScroll = () => { if (!ticking) { ticking = true; requestAnimationFrame(apply); } };
      apply();
      window.addEventListener('scroll', onScroll, { passive: true });
      window.addEventListener('resize', onScroll, { passive: true });
      return () => { window.removeEventListener('scroll', onScroll); window.removeEventListener('resize', onScroll); };
    }, []);
  }

  /* ===================================================================
     SPLIT TEXT — word-level mask-rise for headings. Accepts an array of
     { t, c, br } segments. `c` = 'blue' | 'orange' colours those words,
     `br:true` inserts a hard line break (to preserve existing layout).
     Words animate when an ancestor `.reveal/.reveal-l/.reveal-r` gains
     `.in` (CSS-driven). Words & punctuation are preserved verbatim.
     =================================================================== */
  function SplitText({ segments, base = 0, step = 55 }) {
    let wi = 0;
    return (
      <span className="split">
        {segments.map((seg, si) => {
          if (seg.br) return <br key={'br-' + si} />;
          const words = seg.t.split(/\s+/).filter(Boolean);
          const cc = seg.c === 'orange' ? ' split-orange' : seg.c === 'blue' ? ' split-blue' : '';
          const ital = seg.i ? ' split-ital' : '';
          return words.map((w, k) => {
            const d = base + (wi++) * step;
            return (
              <React.Fragment key={si + '-' + k}>
                <span className="split-w">
                  <span className={'split-i' + cc + ital} style={{ ['--d']: d + 'ms' }}>{w}</span>
                </span>
                {' '}
              </React.Fragment>
            );
          });
        })}
      </span>
    );
  }

  /* ===================================================================
     HEAD — mask-rise heading. Preserves the heading's existing markup,
     fonts and colours verbatim (em/i, line breaks, gradients) while the
     whole heading rises out of an overflow-hidden mask when scrolled in.
     Used instead of per-word SplitText because these headings mix Italiana
     and DM Serif fonts that per-word splitting would flatten.
     =================================================================== */
  function Head({ tag = 'h2', className = '', style, d = 0, children }) {
    const Tag = tag;
    return (
      <Tag className={'motion-head' + (className ? ' ' + className : '')} style={style}>
        <span className="mh-mask"><span className="mh-i" style={{ ['--d']: d + 'ms' }}>{children}</span></span>
      </Tag>
    );
  }

  /* small arrow used in buttons (matches Labs) */
  function Arrow({ size = 16, color = 'currentColor' }) {
    return (
      <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke={color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ display: 'block', flexShrink: 0 }}>
        <line x1="4" y1="12" x2="19" y2="12" />
        <path d="M13 6l6 6-6 6" />
      </svg>
    );
  }

  /* ===================================================================
     INTRO LOADER — logo bars draw in, wordmark fades up, curtain lifts.
     Home only. `word` + `em` form the wordmark (e.g. "Pravi" / "Research").
     =================================================================== */
  function IntroLoader({ word = 'Pravi', em = 'Research' }) {
    const isReload = typeof window !== 'undefined' && window.performance && window.performance.getEntriesByType && 
                     window.performance.getEntriesByType('navigation')[0] && 
                     window.performance.getEntriesByType('navigation')[0].type === 'reload';
    const hasPlayed = typeof sessionStorage !== 'undefined' && sessionStorage.getItem('pravi_loader_played');
    const shouldShow = isReload || !hasPlayed;

    const [done, setDone] = useState(REDUCED || !shouldShow);
    const [animate, setAnimate] = useState(false);

    useEffect(() => {
      if (REDUCED || !shouldShow) return;
      const tAnimate = setTimeout(() => setAnimate(true), 80);
      const tDone = setTimeout(() => setDone(true), INTRO_MS);
      return () => {
        clearTimeout(tAnimate);
        clearTimeout(tDone);
      };
    }, []);

    if (REDUCED || !shouldShow) return null;

    return (
      <div className={'cinematic-loader' + (done ? ' lift' : '') + (animate ? ' animate-content' : '')} aria-hidden="true">
        <div className="cinematic-loader-content">
          <div className="cinematic-logo-container">
            <div className="cinematic-logo-mark">
              <span className="b1"></span>
              <span className="b2"></span>
            </div>
            <div className="cinematic-logo-word">
              <span>{word}</span>
              <em>{em}</em>
            </div>
          </div>
        </div>
      </div>
    );
  }

  /* Thin scroll-progress bar (gradient, scaleX driven by --p on the stage). */
  function ProgressBar() {
    return <div className="motion-progress" aria-hidden="true"><span /></div>;
  }

  /* Ambient drifting blurred colour blob. tone: 'blue' | 'orange'. */
  function Blob({ tone = 'blue', className = '', style = {} }) {
    return <div className={'motion-blob motion-blob--' + (tone === 'orange' ? 'o' : 'b') + (className ? ' ' + className : '')} style={style} aria-hidden="true" />;
  }

  /* ===================================================================
     SCOPED CSS — page-agnostic motion layer (scoped to .dir-editorial).
     Injected once into <head>. Loaded only on the five pages.
     =================================================================== */
  function injectStyles() {
    if (document.getElementById('pravi-motion-css')) return;
    const css = `
    .dir-editorial.motion-stage { position: relative; }
    .dir-editorial .page,
    .dir-kinetic .page,
    .dir-civic .page { position: relative; z-index: 1; }

    /* Ambient Scroller injected by Motion Engine */
    .dir-editorial:not(.labs-stage) .ambient-scroller,
    .dir-kinetic:not(.labs-stage) .ambient-scroller,
    .dir-civic:not(.labs-stage) .ambient-scroller {
      position: absolute;
      inset: 0;
      pointer-events: none;
      z-index: 0;
      overflow: hidden;
    }
    .dir-editorial:not(.labs-stage) .ambient-grid,
    .dir-kinetic:not(.labs-stage) .ambient-grid,
    .dir-civic:not(.labs-stage) .ambient-grid {
      position: absolute;
      inset: 0;
      opacity: 0.1;
      background-image:
        linear-gradient(var(--ink) 1px, transparent 1px),
        linear-gradient(90deg, var(--ink) 1px, transparent 1px);
      background-size: 64px 64px;
      transform: translateY(calc(var(--scrollY, 0px) * 0.12));
      will-change: transform;
    }
    .dir-editorial:not(.labs-stage) .ambient-blob,
    .dir-kinetic:not(.labs-stage) .ambient-blob,
    .dir-civic:not(.labs-stage) .ambient-blob {
      position: absolute;
      border-radius: 50%;
      filter: blur(80px);
      opacity: 0.15;
      will-change: transform;
    }
    .dir-editorial:not(.labs-stage) .ambient-blob.tone-blue,
    .dir-kinetic:not(.labs-stage) .ambient-blob.tone-blue,
    .dir-civic:not(.labs-stage) .ambient-blob.tone-blue {
      width: 450px;
      height: 450px;
      background: var(--pravi-blue);
      top: 15%;
      left: -150px;
      transform: translateY(calc(var(--scrollY, 0px) * -0.05));
    }
    .dir-editorial:not(.labs-stage) .ambient-blob.tone-orange,
    .dir-kinetic:not(.labs-stage) .ambient-blob.tone-orange,
    .dir-civic:not(.labs-stage) .ambient-blob.tone-orange {
      width: 400px;
      height: 400px;
      background: var(--pravi-orange);
      bottom: 20%;
      right: -120px;
      transform: translateY(calc(var(--scrollY, 0px) * -0.08));
    }

    /* Ensure the wrappers host background and clip it */
    .dir-editorial:not(.labs-stage), .dir-kinetic, .dir-civic {
      position: relative;
      overflow: hidden;
    }

    /* Inline text link hover transitions */
    .dir-editorial:not(.labs-stage) p a,
    .dir-kinetic:not(.labs-stage) p a,
    .dir-civic:not(.labs-stage) p a {
      position: relative;
      color: var(--pravi-blue);
      font-weight: 500;
      transition: color 0.3s cubic-bezier(0.2, 0.6, 0.2, 1);
    }
    .dir-editorial:not(.labs-stage) p a::after,
    .dir-kinetic:not(.labs-stage) p a::after,
    .dir-civic:not(.labs-stage) p a::after {
      content: '';
      position: absolute;
      width: 100%;
      transform: scaleX(0);
      height: 1.5px;
      bottom: -1px;
      left: 0;
      background-color: var(--pravi-orange);
      transform-origin: bottom right;
      transition: transform 0.3s cubic-bezier(0.2, 0.6, 0.2, 1);
    }
    .dir-editorial:not(.labs-stage) p a:hover::after,
    .dir-kinetic:not(.labs-stage) p a:hover::after,
    .dir-civic:not(.labs-stage) p a:hover::after {
      transform: scaleX(1);
      transform-origin: bottom left;
    }
    .dir-editorial:not(.labs-stage) p a:hover,
    .dir-kinetic:not(.labs-stage) p a:hover,
    .dir-civic:not(.labs-stage) p a:hover {
      color: var(--pravi-orange);
    }

    /* Emphasized inline text hover */
    .dir-editorial:not(.labs-stage) p em,
    .dir-kinetic:not(.labs-stage) p em,
    .dir-civic:not(.labs-stage) p em,
    .dir-editorial:not(.labs-stage) li em,
    .dir-kinetic:not(.labs-stage) li em,
    .dir-civic:not(.labs-stage) li em {
      display: inline-block;
      transition: color 0.35s cubic-bezier(0.2, 0.6, 0.2, 1), transform 0.35s cubic-bezier(0.2, 0.6, 0.2, 1);
    }
    .dir-editorial:not(.labs-stage) p em:hover,
    .dir-kinetic:not(.labs-stage) p em:hover,
    .dir-civic:not(.labs-stage) p em:hover,
    .dir-editorial:not(.labs-stage) li em:hover,
    .dir-kinetic:not(.labs-stage) li em:hover,
    .dir-civic:not(.labs-stage) li em:hover {
      color: var(--pravi-blue);
      transform: translateY(-1px) scale(1.04);
    }

    .dir-editorial:not(.labs-stage) p i,
    .dir-kinetic:not(.labs-stage) p i,
    .dir-civic:not(.labs-stage) p i,
    .dir-editorial:not(.labs-stage) li i,
    .dir-kinetic:not(.labs-stage) li i,
    .dir-civic:not(.labs-stage) li i {
      display: inline-block;
      transition: color 0.35s cubic-bezier(0.2, 0.6, 0.2, 1), transform 0.35s cubic-bezier(0.2, 0.6, 0.2, 1);
    }
    .dir-editorial:not(.labs-stage) p i:hover,
    .dir-kinetic:not(.labs-stage) p i:hover,
    .dir-civic:not(.labs-stage) p i:hover,
    .dir-editorial:not(.labs-stage) li i:hover,
    .dir-kinetic:not(.labs-stage) li i:hover,
    .dir-civic:not(.labs-stage) li i:hover {
      color: var(--pravi-orange);
      transform: translateY(-1px) scale(1.04);
    }

    /* ---------- reveal primitives ---------- */
    .dir-editorial .reveal   { opacity: 0; transform: translateY(30px); transition: opacity .9s cubic-bezier(.2,.6,.2,1), transform .9s cubic-bezier(.2,.6,.2,1); transition-delay: var(--d, 0ms); will-change: opacity, transform; }
    .dir-editorial .reveal-l { opacity: 0; transform: translateX(-44px); transition: opacity .95s cubic-bezier(.2,.6,.2,1), transform .95s cubic-bezier(.2,.6,.2,1); transition-delay: var(--d, 0ms); }
    .dir-editorial .reveal-r { opacity: 0; transform: translateX(44px); transition: opacity .95s cubic-bezier(.2,.6,.2,1), transform .95s cubic-bezier(.2,.6,.2,1); transition-delay: var(--d, 0ms); }
    .dir-editorial .reveal.in, .dir-editorial .reveal-l.in, .dir-editorial .reveal-r.in { opacity: 1; transform: none; }

    /* ---------- heading mask-rise (Head) ---------- */
    .dir-editorial .motion-head { overflow: visible; }
    .dir-editorial .mh-mask { display: block;overflow: hidden;padding-bottom: 0.15em; }
    .dir-editorial .motion-head .mh-i { display: block; transform: translateY(110%); transition: transform .95s cubic-bezier(.16,.84,.28,1); transition-delay: var(--d, 0ms); will-change: transform; }
    .dir-editorial .motion-head.in .mh-i { transform: translateY(0); }

    /* ---------- word mask-rise (SplitText, optional) ---------- */
    .dir-editorial .split { display: inline; }
    .dir-editorial .split-w { display: inline-block; overflow: hidden; vertical-align: top; padding: 0 .03em; }
    .dir-editorial .split-i { display: inline-block; transform: translateY(115%); transition: transform .85s cubic-bezier(.16,.84,.28,1); transition-delay: var(--d, 0ms); will-change: transform; }
    .dir-editorial .reveal.in .split-i, .dir-editorial .reveal-l.in .split-i, .dir-editorial .reveal-r.in .split-i { transform: translateY(0); }
    .dir-editorial .split-blue { color: var(--pravi-blue); }
    .dir-editorial .split-orange { color: var(--pravi-orange); }
    .dir-editorial .split-ital { font-style: italic; }

    /* ---------- scroll progress bar ---------- */
    .dir-editorial .motion-progress { position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 1200; background: transparent; pointer-events: none; }
    .dir-editorial .motion-progress span { display: block; height: 100%; width: 100%; transform: scaleX(var(--p, 0)); transform-origin: left; background: linear-gradient(90deg, var(--pravi-blue), var(--pravi-orange)); }

    /* ---------- cinematic loader ---------- */
    .cinematic-loader {
      position: fixed;
      inset: 0;
      z-index: 99999;
      background: var(--paper);
      display: flex;
      align-items: center;
      justify-content: center;
      overflow: hidden;
      transition: transform 1.1s cubic-bezier(0.85, 0, 0.15, 1), opacity 0.8s ease 0.3s;
    }
    .cinematic-loader.lift {
      transform: translateY(-100%);
      opacity: 0;
      pointer-events: none;
    }
    .cinematic-loader-content {
      position: relative;
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 28px;
      width: 100%;
      max-width: 600px;
      padding: 40px;
      text-align: center;
    }
    .cinematic-logo-container {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 16px;
    }
    .cinematic-logo-mark {
      display: flex;
      gap: 8px;
      opacity: 0;
      transform: translateY(15px);
      transition: opacity 0.8s cubic-bezier(0.25, 1, 0.5, 1) 0.3s, transform 0.8s cubic-bezier(0.25, 1, 0.5, 1) 0.3s;
    }
    .cinematic-loader.animate-content .cinematic-logo-mark {
      opacity: 1;
      transform: translateY(0);
    }
    .cinematic-logo-mark .b1 {
      width: 0;
      height: 5px;
      background: var(--pravi-orange);
      transition: width 0.7s cubic-bezier(0.25, 1, 0.5, 1) 0.5s;
    }
    .cinematic-loader.animate-content .cinematic-logo-mark .b1 {
      width: 52px;
    }
    .cinematic-logo-mark .b2 {
      width: 0;
      height: 5px;
      background: var(--pravi-blue);
      transition: width 0.7s cubic-bezier(0.25, 1, 0.5, 1) 0.65s;
    }
    .cinematic-loader.animate-content .cinematic-logo-mark .b2 {
      width: 20px;
    }
    .cinematic-logo-word {
      font-family: 'DM Serif Display', serif;
      font-size: clamp(36px, 5vw, 48px);
      letter-spacing: -0.01em;
      color: var(--ink);
      overflow: hidden;
      display: flex;
      gap: 12px;
    }
    .cinematic-logo-word span, .cinematic-logo-word em {
      display: inline-block;
      transform: translateY(100%);
      transition: transform 0.8s cubic-bezier(0.16, 1, 0.3, 1);
    }
    .cinematic-logo-word span {
      color: var(--ink);
      transition-delay: 0.5s;
    }
    .cinematic-logo-word em {
      font-style: italic;
      color: var(--pravi-orange);
      transition-delay: 0.6s;
    }
    .cinematic-loader.animate-content .cinematic-logo-word span,
    .cinematic-loader.animate-content .cinematic-logo-word em {
      transform: translateY(0);
    }

    /* ---------- home hero entrance — orchestrated, load-fired ---------- */
    /* Atmosphere settles (ken-burns bloom), the headline rises (mask), then a
       branded underline draws beneath the italic accents and the chevron scales
       into place. A composed reveal that replaces the full-screen intro curtain. */
    .dir-editorial .motion-hero { position: relative; }

    .dir-editorial .motion-hero-bloom { opacity: 0; animation: heroBloom 1.7s cubic-bezier(.2,.7,.2,1) .05s both; }
    @keyframes heroBloom { 0% { opacity: 0; transform: scale(1.14); } 100% { opacity: 1; transform: scale(1); } }

    .dir-editorial .motion-hero .display em,
    .dir-editorial .motion-hero .display i { position: relative; display: inline-block; }
    .dir-editorial .motion-hero .display em::after,
    .dir-editorial .motion-hero .display i::after {
      content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
      background: currentColor; border-radius: 2px;
      transform: scaleX(0); transform-origin: left center;
      animation: heroUnderline .85s cubic-bezier(.2,.7,.2,1) 1.05s both;
    }
    @keyframes heroUnderline { from { transform: scaleX(0); } to { transform: scaleX(1); } }

    .dir-editorial .motion-hero-chev { animation: heroChevFade 1.1s ease .3s both; }
    @keyframes heroChevFade { from { opacity: 0; } to { opacity: .2; } }
    .dir-editorial .motion-hero-chev svg { transform-origin: 65% 50%; animation: heroChevIn 1.5s cubic-bezier(.2,.7,.2,1) .3s both; }
    @keyframes heroChevIn { from { transform: scale(.5) translateX(36px) rotate(-6deg); } to { transform: scale(1) translateX(0) rotate(0); } }

    /* ---------- ambient blobs ---------- */
    .dir-editorial .motion-blob { position: absolute; border-radius: 50%; filter: blur(60px); opacity: .42; pointer-events: none; z-index: 0; }
    .dir-editorial .motion-blob--b { background: radial-gradient(circle at 30% 30%, var(--pravi-blue-soft), transparent 68%); }
    .dir-editorial .motion-blob--o { background: radial-gradient(circle at 30% 30%, var(--pravi-orange-soft), transparent 68%); }
    .dir-editorial .motion-blob.b-1 { width: 460px; height: 460px; top: -120px; left: -100px; animation: motionDrift1 14s ease-in-out infinite; }
    .dir-editorial .motion-blob.b-2 { width: 400px; height: 400px; bottom: -140px; right: -120px; opacity: .3; animation: motionDrift2 17s ease-in-out infinite; transform: translateY(calc(var(--scrollY, 0px) * -0.02)); }
    .dir-editorial .motion-blob.b-3 { width: 420px; height: 420px; top: 8%; right: -140px; opacity: .26; animation: motionDrift2 19s ease-in-out infinite; }
    .dir-editorial .motion-blob.b-4 { width: 400px; height: 400px; bottom: -120px; left: -120px; opacity: .26; animation: motionDrift1 16s ease-in-out infinite; }
    @keyframes motionDrift1 { 0%,100% { transform: translate(0,0) scale(1); } 50% { transform: translate(40px,30px) scale(1.12); } }
    @keyframes motionDrift2 { 0%,100% { transform: translate(0,0) scale(1); } 50% { transform: translate(-36px,-26px) scale(1.1); } }

    /* sections that host blobs must clip + stack content */
    .dir-editorial .motion-sec { position: relative; overflow: hidden; }
    .dir-editorial .motion-sec > .container { position: relative; z-index: 2; }

    /* ---------- parallax helpers ---------- */
    .dir-editorial .motion-par-up { transform: translate3d(0, calc(var(--scrollY, 0px) * -0.05), 0); will-change: transform; }
    .dir-editorial .motion-par-img { overflow: hidden; }
    .dir-editorial .motion-par-img img { transform: translateY(calc(var(--pp, 0) * 12px)) scale(1.1); will-change: transform; }

    /* ---------- hover lifts (cards / tiles) ---------- */
    .dir-editorial .motion-lift { transition: transform .35s cubic-bezier(.2,.7,.2,1), box-shadow .35s ease, border-color .35s ease; }
    .dir-editorial .motion-lift:hover { transform: translateY(-6px); box-shadow: 0 18px 40px -22px rgba(11,20,48,.45); }

    /* ---------- 3D flip + pop-in card reveal (Team Pravi) ---------- */
    .dir-editorial .reveal.flip-3d {
      opacity: 0;
      transform: perspective(1200px) rotateY(80deg) scale(0.85) translateY(30px);
      transform-origin: center;
      transition: opacity 1.1s cubic-bezier(.34, 1.56, .64, 1), transform 1.1s cubic-bezier(.34, 1.56, .64, 1) !important;
      transition-delay: var(--d, 0ms) !important;
      will-change: opacity, transform;
    }
    .dir-editorial .reveal.flip-3d.in {
      opacity: 1;
      transform: perspective(1200px) rotateY(0deg) scale(1) translateY(0) !important;
    }
    .dir-editorial .reveal.flip-3d:hover {
      transform: perspective(1200px) translateY(-10px) rotateY(-8deg) scale(1.03) !important;
      box-shadow: 0 20px 45px -15px rgba(26, 79, 189, 0.28);
      border-color: var(--pravi-blue) !important;
      z-index: 5;
    }

    /* ---------- mosaic card zoom-on-hover with clipping ---------- */
    .dir-editorial .r-mosaic-card {
      position: relative;
      overflow: hidden;
      border: 1px solid var(--rule);
      background: var(--paper-deep);
    }
    .dir-editorial .r-mosaic-card img {
      transition: transform 0.7s cubic-bezier(0.25, 1, 0.5, 1) !important;
      will-change: transform;
    }
    .dir-editorial .r-mosaic-card:hover img {
      transform: scale(1.1) !important;
    }

    /* ---------- button glow ---------- */
    .dir-editorial .motion-glow { animation: motionGlow 2.8s ease-in-out infinite; }
    @keyframes motionGlow { 0%,100% { box-shadow: 0 0 0 0 rgba(26,79,189,0); } 50% { box-shadow: 0 8px 30px -6px rgba(26,79,189,.5); } }

    /* ---------- nav entrance + active underline (these pages only) ---------- */
    .dir-editorial .nav { animation: motionNavDrop .8s cubic-bezier(.2,.7,.2,1) both; }
    @keyframes motionNavDrop { from { opacity: 0; transform: translateY(-14px); } to { opacity: 1; transform: none; } }
    .dir-editorial .nav-desktop > li > a { position: relative; }
    .dir-editorial .nav-desktop > li > a::after { content: ''; position: absolute; left: 0; right: 0; bottom: -3px; height: 2px; background: var(--pravi-orange); transform: scaleX(0); transform-origin: left; transition: transform .3s cubic-bezier(.2,.7,.2,1); }
    .dir-editorial .nav-desktop > li:hover > a::after { transform: scaleX(1); }

    /* ---------- CTA band (shared) — heading rise + chevron drift ---------- */
    .dir-editorial .motion-cta h2 { }
    .dir-editorial .motion-cta-svg { transform: translateY(-50%) translateX(calc(var(--p, 0) * -34px)); }

    /* ---------- reduced motion ---------- */
    @media (prefers-reduced-motion: reduce) {
      .dir-editorial .reveal, .dir-editorial .reveal-l, .dir-editorial .reveal-r { opacity: 1 !important; transform: none !important; transition: none !important; }
      .dir-editorial .split-i { transform: none !important; transition: none !important; }
      .dir-editorial .motion-head .mh-i { transform: none !important; transition: none !important; }
      .dir-editorial .motion-blob { animation: none !important; transform: none !important; }
      .dir-editorial .motion-hero-bloom { animation: none !important; opacity: 1 !important; transform: none !important; }
      .dir-editorial .motion-hero .display em::after, .dir-editorial .motion-hero .display i::after { animation: none !important; transform: scaleX(1) !important; }
      .dir-editorial .motion-hero-chev, .dir-editorial .motion-hero-chev svg { animation: none !important; }
      .dir-editorial .motion-par-up, .dir-editorial .motion-par-img img { transform: none !important; }
      .dir-editorial .nav { animation: none !important; }
      .dir-editorial .motion-glow { animation: none !important; }
      .dir-editorial .motion-loader { display: none !important; }
    }
    `;
    const style = document.createElement('style');
    style.id = 'pravi-motion-css';
    style.textContent = css;
    document.head.appendChild(style);
  }
  injectStyles();

  /* Override the shared reveal hook so every page picks up the rich engine
     without per-page wiring (pages read window.praviUseReveal at eval time,
     and this file loads after common.jsx but before each page module). */
  Object.assign(window, {
    praviUseReveal: useReveal,
    praviUseScrollFX: useScrollFX,
    PraviSplitText: SplitText,
    PraviHead: Head,
    PraviIntroLoader: IntroLoader,
    PraviProgressBar: ProgressBar,
    PraviBlob: Blob,
    PraviArrow: Arrow,
    PRAVI_MOTION: true,
  });
})();
