// ────────────────────────────────────────────────────────────────
// motion.jsx  —  Ver2 アニメーション共通ユーティリティ
//
// 依存: GSAP 3.13+ (Webflowが2025年4月に全プラグイン無料化)
//   - gsap                  (コア)
//   - ScrollTrigger         (スクロール連動)
//   - MorphSVGPlugin        (SVGパスモーフィング・demos.gsap.com/demo/shape-index/ と同じプラグイン)
//
// 提供:
//   - useReveal(ref, options)    : 要素が画面に入った時にfade+slide
//   - useScrollProgress()        : 0..1 のスクロール進捗
//   - ShapeBackdrop              : ヒーロー背景で滑らかに変形するSVGブロブ
//   - ShapeAccent                : セクション間に置く装飾シェイプ
//   - ScrollProgressBar          : 画面上部のスクロール進捗バー
//   - useGsapPageEnter           : ページマウント時のフェードイン
//   - MotionReady                : ScrollTrigger.refresh 初期化
// ────────────────────────────────────────────────────────────────

(function registerGsap() {
  if (typeof window === 'undefined') return;
  if (window.gsap && !window.__GSAP_REGISTERED__) {
    try {
      const plugins = [];
      if (window.ScrollTrigger) plugins.push(window.ScrollTrigger);
      if (window.MorphSVGPlugin) plugins.push(window.MorphSVGPlugin);
      if (window.MotionPathPlugin) plugins.push(window.MotionPathPlugin);
      if (plugins.length) window.gsap.registerPlugin(...plugins);
      window.__GSAP_REGISTERED__ = true;
      window.__GSAP_STATUS__ = {
        gsap: !!window.gsap,
        ScrollTrigger: !!window.ScrollTrigger,
        MorphSVGPlugin: !!window.MorphSVGPlugin,
        MotionPathPlugin: !!window.MotionPathPlugin,
      };
      if (!window.MorphSVGPlugin) console.warn('[motion] MorphSVGPlugin not loaded');
      if (!window.ScrollTrigger) console.warn('[motion] ScrollTrigger not loaded');
    } catch (e) { console.warn('[motion] registerPlugin failed:', e); }
  }
})();

// Find the scrolling ancestor for a given element. Returns null (= window) if none found.
const findScrollRoot = (el) => {
  let p = el && el.parentElement;
  while (p) {
    if (p.hasAttribute && p.hasAttribute('data-scroll-root')) return p;
    p = p.parentElement;
  }
  return null;
};

// ── useReveal ────────────────────────────────────────────────
const useReveal = (ref, options = {}) => {
  const { y = 80, x = 0, duration = 1.2, delay = 0, stagger = 0.15, start = 'top 80%', once = true, scale = 1 } = options;
  React.useEffect(() => {
    const el = ref.current;
    if (!el || !window.gsap) return;
    const targets = stagger ? el.children : el;
    // Fallback: if ScrollTrigger isn't available OR we can't find a container, just show immediately
    if (!window.ScrollTrigger) return;
    const scroller = findScrollRoot(el); // null → default (window)
    const ctx = window.gsap.context(() => {
      window.gsap.from(targets, {
        opacity: 0, y, x, scale,
        duration, delay, stagger,
        ease: 'power4.out',
        scrollTrigger: {
          trigger: el,
          scroller: scroller || undefined,
          start,
          toggleActions: once ? 'play none none none' : 'play reverse play reverse',
          // Safety: if ScrollTrigger fails to detect visibility, still play
          once: once,
        },
      });
    });
    return () => ctx.revert();
  }, [ref, y, x, duration, delay, stagger, start, once]);
};

// ── useScrollProgress ────────────────────────────────────────
// Listens to BOTH the window and any [data-scroll-root] inner container.
// Returns whichever source is actively scrolling.
const useScrollProgress = () => {
  const [p, setP] = React.useState(0);
  React.useEffect(() => {
    const computeFromWindow = () => {
      const h = document.documentElement;
      const total = (h.scrollHeight - h.clientHeight) || 1;
      return Math.min(1, Math.max(0, (window.scrollY || 0) / total));
    };
    const computeFromEl = (el) => {
      const total = (el.scrollHeight - el.clientHeight) || 1;
      return Math.min(1, Math.max(0, el.scrollTop / total));
    };
    const scrollRoot = document.querySelector('[data-scroll-root]');
    const onScroll = () => {
      // Use inner scroll container if it exists and actually scrolls; else window.
      if (scrollRoot && scrollRoot.scrollHeight > scrollRoot.clientHeight + 1) {
        setP(computeFromEl(scrollRoot));
      } else {
        setP(computeFromWindow());
      }
    };
    window.addEventListener('scroll', onScroll, { passive: true });
    if (scrollRoot) scrollRoot.addEventListener('scroll', onScroll, { passive: true });
    onScroll();
    return () => {
      window.removeEventListener('scroll', onScroll);
      if (scrollRoot) scrollRoot.removeEventListener('scroll', onScroll);
    };
  }, []);
  return p;
};

// ── 四方良しをモチーフにした4つのシェイプ（同じ点数でモーフィングが滑らかになる） ──
// demos.gsap.com/demo/shape-index/ の #flower パス（4つの円が繋がった花びら形）を基準
// viewBox 0 0 100 100 基準
const SHAPES = [
  // 旧互換: ShapeAccent 等で使用
  'M 50,15 Q 85,15 85,50 Q 85,85 50,85 Q 15,85 15,50 Q 15,15 50,15 Z',
  'M 50,10 Q 80,20 85,50 Q 80,80 50,90 Q 20,80 15,50 Q 20,20 50,10 Z',
  'M 50,15 Q 80,15 85,50 Q 80,85 50,85 Q 20,85 15,50 Q 20,15 50,15 Z',
  'M 50,8 Q 82,20 85,50 Q 82,80 50,92 Q 18,80 15,50 Q 18,20 50,8 Z',
];

// デモ準拠のシェイプ群（すべて 0..100 viewBox）
// 同じ点数なので MorphSVG がスムーズに補間し、shapeIndex 値で始点が変わる
const DEMO_SHAPES = {
  // 花びら/四つ葉（デモの #flower をスケール）
  flower:   'M 73.87,3.51 C 86.35,3.51 96.48,13.63 96.48,26.13 C 96.48,38.62 86.35,48.75 73.87,48.75 C 72.23,48.75 70.63,48.57 69.09,48.24 L 69.09,52.27 C 70.63,51.94 72.23,51.76 73.87,51.76 C 86.35,51.76 96.48,61.89 96.48,74.38 C 96.48,86.87 86.35,97 73.87,97 C 61.38,97 51.25,86.87 51.25,74.38 C 51.25,72.9 51.4,71.44 51.67,70.04 L 47.82,70.04 C 48.09,71.44 48.24,72.9 48.24,74.38 C 48.24,86.87 38.12,97 25.63,97 C 13.14,97 3.02,86.87 3.02,74.38 C 3.02,61.89 13.14,51.76 25.63,51.76 C 27.18,51.76 28.69,51.92 30.15,52.21 L 30.15,48.29 C 28.69,48.59 27.18,48.75 25.63,48.75 C 13.14,48.75 3.02,38.62 3.02,26.13 C 3.02,13.63 13.14,3.51 25.63,3.51 C 38.12,3.51 48.24,13.63 48.24,26.13 C 48.24,27.68 48.08,29.19 47.79,30.65 L 51.71,30.65 C 51.41,29.19 51.25,27.68 51.25,26.13 C 51.25,13.63 61.38,3.51 73.87,3.51 Z',
  // 四角（デモの #square）
  square:   'M 100,96.55 L 0,96.55 L 0,0 L 100,0 L 100,96.55 Z',
  // 菱形
  diamond:  'M 50,5 L 95,50 L 50,95 L 5,50 L 50,5 Z',
  // 円（シンプル）
  circle:   'M 95,50 C 95,74.85 74.85,95 50,95 C 25.15,95 5,74.85 5,50 C 5,25.15 25.15,5 50,5 C 74.85,5 95,25.15 95,50 Z',
};

// MorphSVGを使ってパスを順番にモーフィングするタイムライン生成
const createMorphTimeline = (pathEl, options = {}) => {
  const { duration = 4, ease = 'sine.inOut', startIdx = 0 } = options;
  if (!window.gsap || !window.MorphSVGPlugin) return null;
  const tl = window.gsap.timeline({ repeat: -1 });
  const sequence = [];
  for (let i = 0; i < SHAPES.length; i++) {
    sequence.push(SHAPES[(startIdx + i + 1) % SHAPES.length]);
  }
  sequence.forEach((shape) => {
    tl.to(pathEl, {
      morphSVG: { shape, shapeIndex: 0 },
      duration,
      ease,
    });
  });
  return tl;
};

// Culina mark approximation as four independent paths.
// All four pieces begin as the same circle, so the animation reads as:
// one circle -> four-way Culina mark -> one circle.
const CULINA_MARK_PATHS = {
  circle: 'M 95,50 C 95,74.85 74.85,95 50,95 C 25.15,95 5,74.85 5,50 C 5,25.15 25.15,5 50,5 C 74.85,5 95,25.15 95,50 Z',
  pieces: [
    'M 18,8 L 47,8 C 49,8 50,9 50,11 L 50,35 C 50,43.28 43.28,50 35,50 L 11,50 C 9,50 8,49 8,47 L 8,18 C 8,12.48 12.48,8 18,8 Z',
    'M 53,8 L 82,8 C 87.52,8 92,12.48 92,18 L 92,47 C 92,49 91,50 89,50 L 65,50 C 56.72,50 50,43.28 50,35 L 50,11 C 50,9 51,8 53,8 Z',
    'M 11,50 L 35,50 C 43.28,50 50,56.72 50,65 L 50,89 C 50,91 49,92 47,92 L 18,92 C 12.48,92 8,87.52 8,82 L 8,53 C 8,51 9,50 11,50 Z',
    'M 65,50 L 89,50 C 91,50 92,51 92,53 L 92,82 C 92,87.52 87.52,92 82,92 L 53,92 C 51,92 50,91 50,89 L 50,65 C 50,56.72 56.72,50 65,50 Z',
  ],
};

const BACKDROP_MORPH_FRAMES = {
  flowerRound: [
    DEMO_SHAPES.flower,
    DEMO_SHAPES.flower,
    DEMO_SHAPES.flower,
    DEMO_SHAPES.flower,
  ],
  fourSquares: [
    'M 10,10 L 42,10 L 42,42 L 10,42 Z',
    'M 58,10 L 90,10 L 90,42 L 58,42 Z',
    'M 10,58 L 42,58 L 42,90 L 10,90 Z',
    'M 58,58 L 90,58 L 90,90 L 58,90 Z',
  ],
  fourPetals: [
    'M 50,50 C 34,50 21,37 21,24 C 21,13 30,5 42,5 C 55,5 64,18 61,31 C 59,41 53,47 50,50 Z',
    'M 50,50 C 50,34 63,21 76,21 C 87,21 95,30 95,42 C 95,55 82,64 69,61 C 59,59 53,53 50,50 Z',
    'M 50,50 C 66,50 79,63 79,76 C 79,87 70,95 58,95 C 45,95 36,82 39,69 C 41,59 47,53 50,50 Z',
    'M 50,50 C 50,66 37,79 24,79 C 13,79 5,70 5,58 C 5,45 18,36 31,39 C 41,41 47,47 50,50 Z',
  ],
  oneSquare: [
    'M 8,8 L 92,8 L 92,92 L 8,92 Z',
    'M 8,8 L 92,8 L 92,92 L 8,92 Z',
    'M 8,8 L 92,8 L 92,92 L 8,92 Z',
    'M 8,8 L 92,8 L 92,92 L 8,92 Z',
  ],
  oneCircle: [
    DEMO_SHAPES.circle,
    DEMO_SHAPES.circle,
    DEMO_SHAPES.circle,
    DEMO_SHAPES.circle,
  ],
};

// ── ShapeBackdrop ────────────────────────────────────────────
// Hero backdrop: rotating morph sequence
// flower-round -> four squares -> four petals -> one square -> one circle.
const ShapeBackdrop = () => {
  const { theme } = useTheme();
  const pathRefs = React.useRef([]);
  const rotateRef = React.useRef(null);
  const gradId = 'culina-stroke-grad';

  React.useEffect(() => {
    if (!window.gsap || !window.MorphSVGPlugin) return;
    const paths = pathRefs.current.filter(Boolean);
    if (!paths.length) return;

    const ctx = window.gsap.context(() => {
      if (rotateRef.current) {
        window.gsap.to(rotateRef.current, {
          rotation: 360,
          svgOrigin: '50 50',
          duration: 34,
          repeat: -1,
          ease: 'none',
        });
      }

      window.gsap.set(paths, {
        attr: { d: DEMO_SHAPES.flower },
        transformOrigin: '50% 50%',
      });

      const tl = window.gsap.timeline({ repeat: -1, repeatDelay: 0.25 });
      [
        BACKDROP_MORPH_FRAMES.fourSquares,
        BACKDROP_MORPH_FRAMES.fourPetals,
        BACKDROP_MORPH_FRAMES.oneSquare,
        BACKDROP_MORPH_FRAMES.oneCircle,
        BACKDROP_MORPH_FRAMES.flowerRound,
      ].forEach((frame, frameIndex) => {
        const returnsToFlower = frame === BACKDROP_MORPH_FRAMES.flowerRound;
        tl.to(paths, {
          morphSVG: (i) => ({
            shape: frame[i],
            shapeIndex: returnsToFlower ? 8 : (frameIndex % 2 ? 0 : 5),
          }),
          duration: returnsToFlower ? 3.1 : 2.25,
          stagger: returnsToFlower ? 0 : 0.045,
          ease: returnsToFlower ? 'sine.inOut' : 'power3.inOut',
        })
        .to(paths, {
          scale: frameIndex % 2 ? 1.018 : 0.992,
          duration: 0.75,
          yoyo: true,
          repeat: 1,
          ease: 'sine.inOut',
        }, '>-0.08');
      });
    });
    return () => ctx.revert();
  }, []);

  return (
    <div aria-hidden style={{
      position: 'absolute', inset: 0, overflow: 'hidden', pointerEvents: 'none', zIndex: 0,
    }}>
      <svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet" style={{
        position: 'absolute',
        left: '50%', top: '50%',
        width: 'min(70vmin, 700px)', height: 'min(70vmin, 700px)',
        transform: 'translate(-50%, -50%)',
        overflow: 'visible',
      }}>
        <defs>
          {/* ユーザー座標系でのグラデーション（デモの gradientUnits="userSpaceOnUse" 相当） */}
          <linearGradient id={gradId} x1="0" y1="0" x2="100" y2="100" gradientUnits="userSpaceOnUse">
            <stop offset="0.2" stopColor={theme.accentDeep || theme.accent} />
            <stop offset="0.7" stopColor={theme.accent} />
          </linearGradient>
        </defs>

        <g ref={rotateRef}>
          {BACKDROP_MORPH_FRAMES.flowerRound.map((shape, i) => (
            <path
              key={i}
              ref={(el) => { pathRefs.current[i] = el; }}
              d={shape}
              fill="none"
              stroke={`url(#${gradId})`}
              strokeWidth="1.1"
              strokeLinejoin="round"
              strokeLinecap="round"
              vectorEffect="non-scaling-stroke"
              opacity={0.72 + i * 0.05}
              style={{
                strokeWidth: 2,
                filter: i === 0 ? `drop-shadow(0 0 10px ${theme.accent}55)` : 'none',
              }}
            />
          ))}
        </g>
      </svg>
    </div>
  );
};

// ── MorphLineMotif ────────────────────────────────────────────
// Smaller section ornament that uses the same morphing line language as ShapeBackdrop.
const MorphLineMotif = ({
  size = 220,
  top,
  right,
  bottom,
  left,
  opacity = 0.24,
  strokeWidth = 1.25,
  delay = 0,
  rotateDuration = 42,
  reverse = false,
  color,
}) => {
  const { theme } = useTheme();
  const pathRefs = React.useRef([]);
  const rotateRef = React.useRef(null);
  const gradId = React.useMemo(() => `mlm-${Math.random().toString(36).slice(2, 8)}`, []);
  const strokeColor = color || theme.accent;

  React.useEffect(() => {
    if (!window.gsap || !window.MorphSVGPlugin) return;
    const paths = pathRefs.current.filter(Boolean);
    if (!paths.length) return;

    const ctx = window.gsap.context(() => {
      if (rotateRef.current) {
        window.gsap.to(rotateRef.current, {
          rotation: reverse ? -360 : 360,
          svgOrigin: '50 50',
          duration: rotateDuration,
          repeat: -1,
          ease: 'none',
        });
      }

      window.gsap.set(paths, {
        attr: { d: DEMO_SHAPES.flower },
        transformOrigin: '50% 50%',
      });

      const tl = window.gsap.timeline({ repeat: -1, repeatDelay: 0.2, delay });
      [
        BACKDROP_MORPH_FRAMES.fourSquares,
        BACKDROP_MORPH_FRAMES.fourPetals,
        BACKDROP_MORPH_FRAMES.oneSquare,
        BACKDROP_MORPH_FRAMES.oneCircle,
        BACKDROP_MORPH_FRAMES.flowerRound,
      ].forEach((frame, frameIndex) => {
        const returnsToFlower = frame === BACKDROP_MORPH_FRAMES.flowerRound;
        tl.to(paths, {
          morphSVG: (i) => ({
            shape: frame[i],
            shapeIndex: returnsToFlower ? 8 : (frameIndex % 2 ? 0 : 5),
          }),
          duration: returnsToFlower ? 2.9 : 2.1,
          stagger: returnsToFlower ? 0 : 0.035,
          ease: returnsToFlower ? 'sine.inOut' : 'power3.inOut',
        });
      });
    });
    return () => ctx.revert();
  }, [delay, reverse, rotateDuration]);

  const posStyle = {};
  if (top !== undefined) posStyle.top = top;
  if (right !== undefined) posStyle.right = right;
  if (bottom !== undefined) posStyle.bottom = bottom;
  if (left !== undefined) posStyle.left = left;

  return (
    <div aria-hidden style={{
      position: 'absolute',
      ...posStyle,
      width: size,
      height: size,
      opacity,
      pointerEvents: 'none',
      zIndex: 0,
      overflow: 'visible',
    }}>
      <svg viewBox="0 0 100 100" preserveAspectRatio="xMidYMid meet" style={{ width: '100%', height: '100%', overflow: 'visible' }}>
        <defs>
          <linearGradient id={gradId} x1="0" y1="0" x2="100" y2="100" gradientUnits="userSpaceOnUse">
            <stop offset="0" stopColor={theme.accentDeep || strokeColor} stopOpacity="0.7" />
            <stop offset="1" stopColor={strokeColor} stopOpacity="1" />
          </linearGradient>
        </defs>
        <g ref={rotateRef}>
          {BACKDROP_MORPH_FRAMES.flowerRound.map((shape, i) => (
            <path
              key={i}
              ref={(el) => { pathRefs.current[i] = el; }}
              d={shape}
              fill="none"
              stroke={`url(#${gradId})`}
              strokeWidth={strokeWidth}
              strokeLinejoin="round"
              strokeLinecap="round"
              vectorEffect="non-scaling-stroke"
              opacity={0.62 + i * 0.08}
            />
          ))}
        </g>
      </svg>
    </div>
  );
};

// ── ScrollProgressBar ────────────────────────────────────────
const ScrollProgressBar = () => {
  const p = useScrollProgress();
  const { theme } = useTheme();
  return (
    <div aria-hidden style={{
      position: 'fixed', top: 0, left: 0, right: 0, height: 4,
      background: 'transparent', zIndex: 60, pointerEvents: 'none',
    }}>
      <div style={{
        height: '100%',
        width: `${p * 100}%`,
        background: theme.accent,
        boxShadow: `0 0 12px ${theme.accent}`,
        transition: 'width 0.1s linear',
      }} />
    </div>
  );
};

// ── ShapeAccent ──────────────────────────────────────────────
// セクション装飾用の小さな動くシェイプ
const ShapeAccent = ({ size = 120, top = 40, right = 40, bottom, left, color, opacity = 0.55 }) => {
  const { theme } = useTheme();
  const ref = React.useRef(null);
  const pathRef = React.useRef(null);
  const fill = color || theme.accent;

  React.useEffect(() => {
    if (!window.gsap || !window.MorphSVGPlugin) return;
    const el = pathRef.current;
    if (!el) return;
    const ctx = window.gsap.context(() => {
      createMorphTimeline(el, { duration: 5, ease: 'sine.inOut' });
      if (ref.current) {
        window.gsap.to(ref.current, {
          rotation: 360,
          transformOrigin: '50% 50%',
          duration: 50,
          repeat: -1,
          ease: 'none',
        });
      }
    });
    return () => ctx.revert();
  }, []);

  const posStyle = {};
  if (top !== undefined) posStyle.top = top;
  if (right !== undefined) posStyle.right = right;
  if (bottom !== undefined) posStyle.bottom = bottom;
  if (left !== undefined) posStyle.left = left;

  return (
    <div ref={ref} aria-hidden style={{
      position: 'absolute', ...posStyle, width: size, height: size,
      pointerEvents: 'none', opacity,
    }}>
      <svg viewBox="0 0 600 600" width="100%" height="100%">
        <path ref={pathRef} d={SHAPES[0]} fill={fill} />
      </svg>
    </div>
  );
};

// ── DecorativeMotionField ─────────────────────────────────────
// Section/page ornament: animated score lines plus soft filled marks.
const DecorativeMotionField = ({ density = 8, intensity = 0.55, variant = 'score', position = 'absolute' }) => {
  const { theme } = useTheme();
  const uid = React.useMemo(() => `dmf-${Math.random().toString(36).slice(2, 8)}`, []);
  const lines = Array.from({ length: density });
  const petals = Array.from({ length: Math.max(2, Math.round(density / 3)) });
  return (
    <div aria-hidden className="culina-decorative-motion-field" style={{
      position, inset: 0, overflow: 'hidden', pointerEvents: 'none', zIndex: 0,
      opacity: intensity,
    }}>
      <style>{`
        @keyframes ${uid}-dash { to { stroke-dashoffset: -320; } }
        @keyframes ${uid}-spin { to { transform: rotate(360deg); } }
        @keyframes ${uid}-breathe { 0%, 100% { transform: scale(.94); opacity: .18; } 50% { transform: scale(1.08); opacity: .42; } }
      `}</style>
      <svg viewBox="0 0 1200 700" preserveAspectRatio="none" style={{ width: '100%', height: '100%' }}>
        <defs>
          <radialGradient id={`${uid}-fill`} cx="50%" cy="50%" r="55%">
            <stop offset="0" stopColor={theme.accent} stopOpacity={theme.mode === 'dark' ? 0.36 : 0.22} />
            <stop offset="1" stopColor={theme.accent} stopOpacity="0" />
          </radialGradient>
        </defs>
        {lines.map((_, i) => {
          const y = 70 + i * (620 / Math.max(1, density - 1));
          const bend = variant === 'crest' ? 110 : 70;
          return (
            <path
              key={i}
              d={`M -120 ${y} C ${180 + i * 28} ${y - bend}, ${450 - i * 10} ${y + bend}, ${710 + i * 12} ${y - 20} S ${980 - i * 18} ${y + bend}, 1320 ${y - 10}`}
              fill="none"
              stroke={i % 3 === 0 ? theme.accent : theme.line}
              strokeWidth={i % 3 === 0 ? 1.15 : 0.7}
              strokeDasharray={i % 3 === 0 ? '18 22' : '9 18'}
              strokeOpacity={i % 3 === 0 ? 0.65 : 0.55}
              style={{ animation: `${uid}-dash ${11 + i * 0.9}s linear infinite` }}
            />
          );
        })}
        {petals.map((_, i) => (
          <g key={i} style={{
            transformOrigin: `${220 + i * 290}px ${150 + (i % 2) * 310}px`,
            animation: `${uid}-spin ${38 + i * 9}s linear infinite ${i % 2 ? 'reverse' : ''}`,
          }}>
            <path
              d="M 0,-48 C 20,-48 30,-22 22,0 C 48,-8 76,8 76,34 C 76,58 48,74 22,66 C 30,90 18,116 0,116 C -18,116 -30,90 -22,66 C -48,74 -76,58 -76,34 C -76,8 -48,-8 -22,0 C -30,-22 -20,-48 0,-48 Z"
              transform={`translate(${220 + i * 290} ${150 + (i % 2) * 310})`}
              fill={`url(#${uid}-fill)`}
              style={{ animation: `${uid}-breathe ${7 + i}s ease-in-out infinite` }}
            />
          </g>
        ))}
      </svg>
    </div>
  );
};

// ── useGsapPageEnter ─────────────────────────────────────────
const useGsapPageEnter = (ref) => {
  React.useEffect(() => {
    const el = ref.current;
    if (!el || !window.gsap) return;
    const ctx = window.gsap.context(() => {
      window.gsap.from(el, {
        opacity: 0, y: 20, duration: 0.8, ease: 'power3.out',
      });
    });
    return () => ctx.revert();
  }, [ref]);
};

// ── MotionReady ──────────────────────────────────────────────
const MotionReady = () => {
  React.useEffect(() => {
    if (!window.ScrollTrigger) return;
    if (document.fonts && document.fonts.ready) {
      document.fonts.ready.then(() => window.ScrollTrigger.refresh());
    } else {
      setTimeout(() => window.ScrollTrigger.refresh(), 500);
    }
  }, []);
  return null;
};

// ── LogoMorph ────────────────────────────────────────────────
// ヒーロー用: 円 → Culina ロゴ（4つのパーツ）へ
const LOGO_SHAPES = {
  // 1. 大きな円
  circle:   'M 100,300 C 100,189.54 189.54,100 300,100 C 410.46,100 500,189.54 500,300 C 500,410.46 410.46,500 300,500 C 189.54,500 100,410.46 100,300 Z',
  // 2. 角丸正方形
  rounded:  'M 140,100 L 460,100 C 482.09,100 500,117.91 500,140 L 500,460 C 500,482.09 482.09,500 460,500 L 140,500 C 117.91,500 100,482.09 100,460 L 100,140 C 100,117.91 117.91,100 140,100 Z',
  // 3. 十字（四方良しの方角を暗示）
  cross:    'M 240,100 L 360,100 L 360,240 L 500,240 L 500,360 L 360,360 L 360,500 L 240,500 L 240,360 L 100,360 L 100,240 L 240,240 Z',
  // 4. Culina ロゴ: 4つの正方形 (2x2グリッド)
  fourSquares: 'M 100,100 L 275,100 L 275,275 L 100,275 Z M 325,100 L 500,100 L 500,275 L 325,275 Z M 100,325 L 275,325 L 275,500 L 100,500 Z M 325,325 L 500,325 L 500,500 L 325,500 Z',
};

const LogoMorph = ({ size = 400, color, stroke = false }) => {
  const { theme } = useTheme();
  const pathRefs = React.useRef([]);
  const wrapRef = React.useRef(null);
  const fill = color || theme.accent;

  React.useEffect(() => {
    if (!window.gsap || !window.MorphSVGPlugin) {
      console.warn('[LogoMorph] gsap/MorphSVG not ready');
      return;
    }
    const paths = pathRefs.current.filter(Boolean);
    const wrap = wrapRef.current;
    if (!paths.length || !wrap) return;

    let ctx;
    ctx = window.gsap.context(() => {
      window.gsap.set(paths, {
        attr: { d: CULINA_MARK_PATHS.circle },
        transformOrigin: '50% 50%',
      });
      window.gsap.to(wrap, {
        rotation: 360,
        transformOrigin: '50% 50%',
        duration: 90,
        repeat: -1,
        ease: 'none',
      });

      window.gsap.timeline({ repeat: -1, repeatDelay: 1 })
        .to(paths, {
          morphSVG: (i) => ({ shape: CULINA_MARK_PATHS.pieces[i], shapeIndex: 0 }),
          duration: 2.4,
          stagger: 0.055,
          ease: 'power3.inOut',
        })
        .to(paths, {
          scale: 1.035,
          duration: 1.4,
          yoyo: true,
          repeat: 1,
          ease: 'sine.inOut',
        })
        .to(paths, {
          morphSVG: { shape: CULINA_MARK_PATHS.circle, shapeIndex: 0 },
          duration: 2.1,
          stagger: { each: 0.04, from: 'center' },
          ease: 'power3.inOut',
        }, '+=0.15');
    });
    return () => { if (ctx) ctx.revert(); };
  }, []);

  return (
    <div ref={wrapRef} aria-hidden style={{
      width: size, height: size, position: 'relative',
      display: 'inline-block', willChange: 'transform',
    }}>
      <svg viewBox="0 0 100 100" width="100%" height="100%" style={{ overflow: 'visible' }}>
        {CULINA_MARK_PATHS.pieces.map((_, i) => (
          <path
            key={i}
            ref={(el) => { pathRefs.current[i] = el; }}
            d={CULINA_MARK_PATHS.circle}
            fill={stroke ? 'none' : fill}
            stroke={stroke ? fill : 'none'}
            strokeWidth={stroke ? 1.8 : 0}
            strokeLinejoin="round"
            strokeLinecap="round"
            opacity={stroke ? 0.92 : 1}
          />
        ))}
      </svg>
    </div>
  );
};

Object.assign(window, {
  useReveal, useScrollProgress, useGsapPageEnter,
  ShapeBackdrop, ShapeAccent, ScrollProgressBar, MotionReady,
  LogoMorph, LOGO_SHAPES, CULINA_MARK_PATHS, DecorativeMotionField, MorphLineMotif,
  SHAPES, createMorphTimeline,
});
