"use client";

import { useEffect, useRef } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import { MotionPathPlugin } from "gsap/MotionPathPlugin";
import { DrawSVGPlugin } from "gsap/DrawSVGPlugin";
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";

let hasDrawSVG = false;
if (typeof window !== "undefined") {
  gsap.registerPlugin(ScrollTrigger, MotionPathPlugin);
  try {
    // DrawSVGPlugin ships free with gsap 3.13+. Registration (not the import
    // itself) is guarded so the draw step can fall back to plain
    // stroke-dasharray if it's ever unavailable, without touching timing.
    gsap.registerPlugin(DrawSVGPlugin);
    hasDrawSVG = true;
  } catch {
    hasDrawSVG = false;
  }
}

const MOBILE_BQ = 600;
const AIRCRAFT_ROTATION_OFFSET = 90; // glyph's rest pose points "up"
const WAVE_AMPLITUDE_MIN = 20;
const WAVE_AMPLITUDE_MAX = 38;

const FEATURES = [
  {
    title: "Same-Day Air Logistics",
    desc: "Max 3-hour door-to-door delivery between India's major regional capitals.",
    icon: (
      <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
        <circle cx="12" cy="12" r="10" />
        <polyline points="12 6 12 12 16 14" />
      </svg>
    ),
  },
  {
    title: "AI-Powered Operations",
    desc: "MileTruth™ AI continuously forecasts demand and optimizes every route and load.",
    icon: (
      <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
        <rect x="4" y="4" width="16" height="16" rx="3" />
        <path d="M9 9h6v6H9z" />
        <path d="M9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3" />
      </svg>
    ),
  },
  {
    title: "Regional Connectivity",
    desc: "Dedicated air capacity linking major economic hubs, not passenger-belly space.",
    icon: (
      <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
        <circle cx="6" cy="6" r="2.5" />
        <circle cx="18" cy="6" r="2.5" />
        <circle cx="12" cy="18" r="2.5" />
        <path d="M8.2 7.2 10 16M15.8 7.2 14 16M8.5 6h7" />
      </svg>
    ),
  },
  {
    title: "EV Last Mile",
    desc: "Zero-emission electric vehicles complete every first- and final-mile delivery.",
    icon: (
      <svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
        <path d="M3 13h13l3 4h2v3H3z" />
        <circle cx="7" cy="20" r="1.6" />
        <circle cx="16" cy="20" r="1.6" />
        <path d="M6 13V8a1 1 0 0 1 1-1h5l3 6" />
      </svg>
    ),
  },
];

interface Point {
  x: number;
  y: number;
}

function buildPathD(points: Point[]): string {
  if (points.length < 2) return "";
  let d = `M ${points[0].x} ${points[0].y}`;
  for (let i = 0; i < points.length - 1; i++) {
    const p0 = points[i];
    const p1 = points[i + 1];
    const dx = (p1.x - p0.x) / 2;
    d += ` C ${p0.x + dx} ${p0.y}, ${p1.x - dx} ${p1.y}, ${p1.x} ${p1.y}`;
  }
  return d;
}

// Inserts one raised "crest" point between every consecutive pair of card
// anchors, turning the mostly-straight spline into an alternating
// trough-crest-trough wave: each card position stays a trough (so the pulse
// still arrives level with its icon, unchanged), while the gap between cards
// arcs upward. buildPathD's per-segment horizontal-tangent control points
// already keep every trough/crest transition smooth (no sharp bends) without
// needing a fancier spline — only the input point list changes.
//
// Each crest's height and horizontal position are nudged by a small,
// index-derived (not random) offset, so consecutive humps are never
// identical — reading as a real, slightly irregular air route rather than a
// generated sine wave — while staying perfectly stable across re-measures
// (resize, etc.) since the offsets depend only on segment index, not chance.
// Each offset sums two sine terms at unrelated frequencies/phases rather
// than one, so the sequence of crest heights doesn't itself read as a
// single obvious repeating rhythm.
function buildWavePoints(points: Point[], amplitude: number): Point[] {
  if (points.length < 2 || amplitude <= 0) return points;
  const waved: Point[] = [points[0]];
  for (let i = 0; i < points.length - 1; i++) {
    const p0 = points[i];
    const p1 = points[i + 1];
    const ampFactor =
      1 + 0.28 * Math.sin(i * 2.9 + 0.4) + 0.15 * Math.sin(i * 5.3 + 2.1);
    const xJitter =
      (p1.x - p0.x) * (0.08 * Math.sin(i * 1.7 + 1.3) + 0.06 * Math.sin(i * 3.6 + 0.2));
    waved.push({
      x: (p0.x + p1.x) / 2 + xJitter,
      y: Math.min(p0.y, p1.y) - amplitude * ampFactor,
    });
    waved.push(p1);
  }
  return waved;
}

export default function WingsWhyGrid() {
  const wrapRef = useRef<HTMLDivElement>(null);
  const svgRef = useRef<SVGSVGElement>(null);
  const pathRef = useRef<SVGPathElement>(null);
  // A second copy of the same "d", drawn brighter/thicker/blurred. A moving
  // dasharray window (kept in sync with the pulse's own progress, see
  // onUpdate below) makes a soft, feathered segment of the route glow right
  // under the pulse and fade back to the resting line just behind it — the
  // route being "energized" as the pulse passes, not just a static line.
  const illumRef = useRef<SVGPathElement>(null);
  // The traveler is the primary visual: a bright optical pulse riding the
  // route. It's the outer group MotionPathPlugin translates. The aircraft is
  // nested inside it as a small, secondary glyph — never its own focal point.
  const travelerRef = useRef<SVGGElement>(null);
  // Only this inner group rotates to face the direction of travel (the
  // halo). Kept separate from travelerRef with an explicit 0,0 transform
  // origin so rotation never pivots around a fill-box-derived point.
  const directionalRef = useRef<SVGGElement>(null);
  // The aircraft's own rotation wrapper, painted as a sibling *after* the
  // core circle (see JSX) so its silhouette stays readable on top of the
  // bright core instead of being buried under it. Mirrors directionalRef's
  // rotation exactly (set alongside it in onUpdate) so both halves of the
  // traveler always face the same way despite being separate DOM branches.
  const aircraftDirectionalRef = useRef<SVGGElement>(null);
  const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
  const iconRefs = useRef<(HTMLDivElement | null)[]>([]);

  useEffect(() => {
    const wrap = wrapRef.current;
    const svg = svgRef.current;
    const path = pathRef.current;
    const illum = illumRef.current;
    const traveler = travelerRef.current;
    const directional = directionalRef.current;
    const aircraftDirectional = aircraftDirectionalRef.current;
    if (!wrap || !svg || !path || !illum || !traveler || !directional || !aircraftDirectional) return;

    // Declared here (not inside the gsap.context callback below) so the
    // cleanup function — which must run on every unmount, including ones
    // gsap.context() itself doesn't know about, like these plain rAF/
    // ResizeObserver handles — can always reach them.
    let resizeRaf = 0;
    let initialRaf = 0;
    let rafIn = 0;
    let ro: ResizeObserver | undefined;
    let loadHandler: (() => void) | undefined;
    // Also assigned (not re-declared) inside the gsap.context callback below,
    // for the same reason: cleanup needs to reach them even though
    // ctx.revert() already kills anything the context tracked, since the
    // ScrollTrigger is created synchronously (tracked) but the flight
    // timeline is only created later, asynchronously, once onEnter actually
    // fires (not reliably tracked by the context in that case).
    let st: ScrollTrigger | null = null;
    let playedTimeline: gsap.core.Timeline | null = null;

    // Everything GSAP-related (ScrollTrigger, timelines, gsap.set calls) is
    // created inside this context so a single ctx.revert() on unmount kills
    // all of it — this is what makes the whole setup safe to re-run on every
    // mount (initial load, client-side route navigation, back/forward, hot
    // reload) instead of only working after a hard refresh, where a prior
    // mount's leftover ScrollTrigger/timeline could otherwise linger or hold
    // a stale measurement.
    const ctx = gsap.context(() => {
    const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
    let played = false;

    const isMobileLayout = () => window.matchMedia(`(max-width: ${MOBILE_BQ}px)`).matches;

    const measure = (): { points: Point[]; isMobile: boolean } => {
      const wrapRect = wrap.getBoundingClientRect();
      const isMobile = isMobileLayout();

      const points = cardRefs.current
        .map((card, i) => {
          const icon = iconRefs.current[i];
          if (!card || !icon) return null;
          const cardRect = card.getBoundingClientRect();
          const iconRect = icon.getBoundingClientRect();
          if (isMobile) {
            return {
              x: 12,
              y: iconRect.top + iconRect.height / 2 - wrapRect.top,
            };
          }
          return {
            x: iconRect.left + iconRect.width / 2 - wrapRect.left,
            y: cardRect.top - wrapRect.top - 14,
          };
        })
        .filter((p): p is Point => p !== null);

      svg.setAttribute("width", String(wrapRect.width));
      svg.setAttribute("height", String(wrapRect.height));
      svg.setAttribute("viewBox", `0 0 ${wrapRect.width} ${wrapRect.height}`);

      // Wave only applies to the horizontal (non-mobile) layout — mobile
      // keeps its existing straight vertical line unchanged. Amplitude
      // scales with the section's width so it stays "gentle" (never a
      // straight line, never overly aggressive) at any breakpoint.
      const amplitude = isMobile
        ? 0
        : Math.max(WAVE_AMPLITUDE_MIN, Math.min(WAVE_AMPLITUDE_MAX, wrapRect.width / 20));
      const routePoints = isMobile ? points : buildWavePoints(points, amplitude);
      const d = buildPathD(routePoints);
      path.setAttribute("d", d);
      illum.setAttribute("d", d);

      return { points, isMobile };
    };

    ro = new ResizeObserver(() => {
      cancelAnimationFrame(resizeRaf);
      resizeRaf = requestAnimationFrame(() => {
        if (!reduced) {
          buildSequence();
        } else {
          measure();
        }
      });
    });
    ro.observe(wrap);

    // Initial synchronous measurement so `path` has real geometry before any
    // gsap.set/timeline touches it (ResizeObserver's first callback is async).
    // Followed by one deferred re-measure: StaggerChildren's own mount effect
    // (a sibling/child effect) can still have cards translated by their
    // pre-reveal yOffset at this exact tick, so the very first synchronous
    // read can be transiently stale — a post-paint rAF corrects it.
    measure();
    initialRaf = requestAnimationFrame(() => {
      measure();
      // Recalculates every ScrollTrigger's start/end position against the
      // now-settled layout. Without this, a trigger created a moment before
      // web fonts/images finish shifting the page can end up with a stale
      // "top 85%" position that a normal scroll never actually crosses —
      // the classic reason a GSAP ScrollTrigger works after a hard refresh
      // (cache-warm, layout already stable) but not after a fresh client-side
      // navigation.
      ScrollTrigger.refresh();
    });

    // Extra safety net: images loading in after this point can still shift
    // layout. One more refresh once the whole page (including images) has
    // finished loading catches that — a no-op if `load` already fired.
    if (document.readyState !== "complete") {
      loadHandler = () => ScrollTrigger.refresh();
      window.addEventListener("load", loadHandler, { once: true });
    }

    const activateCard = (i: number) => {
      const card = cardRefs.current[i];
      const icon = iconRefs.current[i];
      if (!card) return;
      // Arrival: border gently strengthens, ambient glow grows, icon
      // brightens, card lifts a touch (CSS-driven, eased over ~0.5s — see
      // .wwg-card / .wwg-card-glow transition durations below — so it grows
      // and settles rather than snapping on).
      card.classList.add("wwg-card-flown", "wwg-card-pulse");
      // Icon's own gentle scale pulse starts a beat after the arrival
      // begins, so the sequence reads as "arrives, then responds" rather
      // than everything happening at once.
      if (icon) {
        gsap.timeline({ delay: 0.28 })
          .to(icon, { scale: 1.02, duration: 0.32, ease: "expo.out" })
          .to(icon, { scale: 1, duration: 0.32, ease: "sine.inOut" });
      }
      // Hold the arrival state, then ease into the subtler, persistent
      // "flown" resting state.
      gsap.delayedCall(0.95, () => card.classList.remove("wwg-card-pulse"));
    };

    const buildSequence = () => {
      if (playedTimeline) playedTimeline.kill();

      const { points, isMobile } = measure();
      if (points.length < 2) return;

      const restOpacity = isMobile ? 0.15 : 0.18;
      const total = path.getTotalLength();
      // The illuminated window trailing the pulse: roughly a fifth of the
      // route's length, so it reads as a genuine glowing trail rather than
      // a small blip or the entire line at once.
      const trailLen = total * 0.22;
      const illumDashGap = total + 200;
      illum.setAttribute("stroke-dasharray", `${trailLen} ${illumDashGap}`);
      const fractions = points.map((pt) => {
        let best = 0;
        let bestDist = Infinity;
        const step = Math.max(2, total / 400);
        for (let l = 0; l <= total; l += step) {
          const p = path.getPointAtLength(l);
          const dist = (p.x - pt.x) ** 2 + (p.y - pt.y) ** 2;
          if (dist < bestDist) {
            bestDist = dist;
            best = l;
          }
        }
        return best / total;
      });
      const fired = fractions.map(() => false);

      let currentAngle = 0;
      let angleInit = false;

      const tl = gsap.timeline({
        scrollTrigger: {
          trigger: wrap,
          start: "top 75%",
          end: "bottom 35%",
          scrub: 1,
        },
      });
      playedTimeline = tl;

      // The energy pulse's own journey — calm and deliberate, ~4.8-5.5s so
      // it glides rather than rushes from the first card to the last.
      const FLIGHT_DURATION = 5.2;
      // The route draws slightly faster than the pulse travels, both
      // starting together at t=0 — so the drawn extent always leads the
      // pulse's position (never the other way round) and the whole route
      // is guaranteed fully illuminated well before the pulse's own journey
      // ends, while both still read as one continuous, synchronized motion.
      const DRAW_DURATION = FLIGHT_DURATION * 0.9;

      if (hasDrawSVG) {
        tl.to(path, { drawSVG: "100%", duration: DRAW_DURATION, ease: "power2.inOut" }, 0);
      } else {
        gsap.set(path, { strokeDasharray: total, strokeDashoffset: total });
        tl.to(path, { strokeDashoffset: 0, duration: DRAW_DURATION, ease: "power2.inOut" }, 0);
      }
      // The base route ramps straight to its final resting opacity as it
      // draws — never to an elevated "traveling" brightness — so there is
      // only ever one illuminated layer on screen: the bright illum trail
      // riding with the pulse (below). Without this, the whole drawn route
      // would sit at a visibly elevated opacity for the entire flight,
      // reading as a second, competing "lit" route behind the pulse.
      tl.to(path, { opacity: restOpacity, duration: 0.9, ease: "sine.out" }, 0);

      // GSAP computes its own rotation pivot for SVG elements from the
      // target's bounding box (stamped as a data-svg-origin attribute) and
      // ignores plain CSS transform-origin for that purpose — so without
      // this, both directional groups were actually rotating around their
      // bbox corner instead of (0,0), visibly orbiting off the path's
      // centerline as they turned. svgOrigin is GSAP's own equivalent,
      // stated in the element's literal SVG coordinates (not bbox-relative
      // percentages), and only needs setting once — the per-frame
      // rotation-only gsap.set calls below then reuse it automatically.
      gsap.set([directional, aircraftDirectional], { svgOrigin: "0 0" });

      // A soft fade-in rather than an instant pop, so the pulse's arrival
      // reads as one continuous gesture from the very first frame.
      tl.to(traveler, { opacity: 1, duration: 0.3, ease: "sine.out" }, 0);
      tl.to(illum, { opacity: 1, duration: 0.4, ease: "sine.out" }, 0);
      tl.to(traveler, {
        // alignOrigin locks the traveler's own center (not a default
        // corner/bbox reference) to the path, matching the explicit
        // transform-origin: 50% 50% above — keeps the pulse/aircraft
        // precisely on the route's centerline with no vertical drift.
        motionPath: { path, autoRotate: false, alignOrigin: [0.5, 0.5] },
        duration: FLIGHT_DURATION,
        ease: "power2.inOut",
        onUpdate: function () {
          const p = this.progress();
          const l = p * total;
          const eps = Math.max(1, total * 0.01);
          const a = path.getPointAtLength(Math.max(0, l - eps));
          const b = path.getPointAtLength(Math.min(total, l + eps));
          const target = (Math.atan2(b.y - a.y, b.x - a.x) * 180) / Math.PI + AIRCRAFT_ROTATION_OFFSET;

          if (!angleInit) {
            currentAngle = target;
            angleInit = true;
          } else {
            let delta = target - currentAngle;
            // shortest angular path so the lerp never spins the long way round
            delta = ((delta + 180) % 360 + 360) % 360 - 180;
            currentAngle += delta * 0.12;
          }
          // Only the two directional groups (trail + halo, and the aircraft)
          // rotate — the core stays a perfectly round point of light on the
          // path. Both are set to the same angle every frame so they always
          // face the same way despite being separate DOM branches (see
          // aircraftDirectionalRef for why they're split).
          gsap.set(directional, { rotation: currentAngle });
          gsap.set(aircraftDirectional, { rotation: currentAngle });

          // Slide the illuminated window's trailing edge to l - trailLen and
          // its leading edge to l (i.e. right under the pulse) by shifting
          // the dash pattern's phase — the CSS blur on .wwg-illum-trail turns
          // those two hard edges into a soft glow that fades into the resting
          // route, rather than a sharp on/off cut.
          illum.setAttribute("stroke-dashoffset", String(trailLen - l));

          fractions.forEach((f, i) => {
            if (!fired[i] && p >= f) {
              fired[i] = true;
              activateCard(i);
            } else if (fired[i] && p < f) {
              fired[i] = false;
              // Reset card state if scrubbed backwards
              cardRefs.current[i]?.classList.remove("wwg-card-flown", "wwg-card-pulse");
            }
          });
        },
      }, 0);

      // Only once the pulse's own journey is complete does it fade out —
      // the route itself is already at its resting opacity by this point
      // (see above), so nothing further happens to it here.
      tl.to(traveler, { opacity: 0, duration: 0.6, ease: "sine.out" }, FLIGHT_DURATION);
      tl.to(illum, { opacity: 0, duration: 0.6, ease: "sine.out" }, FLIGHT_DURATION);
    };

    if (reduced) {
      const { isMobile } = measure();
      gsap.set(path, { opacity: isMobile ? 0.15 : 0.18 });
      if (hasDrawSVG) {
        gsap.set(path, { drawSVG: "100%" });
      } else {
        gsap.set(path, { strokeDasharray: "none", strokeDashoffset: 0 });
      }
      gsap.set(traveler, { opacity: 0 });
      gsap.set(illum, { opacity: 0 });
    } else {
      gsap.set(path, { opacity: 0 });
      if (hasDrawSVG) {
        gsap.set(path, { drawSVG: "0%" });
      }
      gsap.set(traveler, { opacity: 0 });
      gsap.set(illum, { opacity: 0 });

      // Build the scrubbing sequence immediately
      buildSequence();
    }
    }, wrap);

    return () => {
      ro?.disconnect();
      cancelAnimationFrame(resizeRaf);
      cancelAnimationFrame(initialRaf);
      cancelAnimationFrame(rafIn);
      if (loadHandler) window.removeEventListener("load", loadHandler);
      st?.kill();
      playedTimeline?.kill();
      // Reverts/kills everything gsap.context tracked (ScrollTrigger.create,
      // gsap.set calls, etc.) as a safety net beyond the explicit kills
      // above, which exist specifically because the flight timeline is
      // created asynchronously (inside onEnter) and so isn't guaranteed to
      // be tracked by the context itself.
      ctx.revert();
    };
  }, []);

  return (
    <>
      <style
        dangerouslySetInnerHTML={{
          __html: `
        .wwg-section {
          background: #ffffff;
          padding: var(--space-section-lg, 80px) 0;
        }

        .wwg-header {
          margin: 0 auto 48px auto;
          max-width: 720px;
          text-align: center;
        }

        .wwg-eyebrow {
          font-size: 13px;
          font-weight: 700;
          letter-spacing: 2px;
          text-transform: uppercase;
          color: rgba(17, 17, 17, 0.45);
          margin-bottom: 16px;
        }

        .wwg-title {
          font-size: clamp(28px, 3.2vw, 46px) !important;
          font-weight: 800 !important;
          letter-spacing: -0.03em !important;
          line-height: 1.15 !important;
          text-transform: none !important;
          color: #111111 !important;
          margin: 0;
        }

        .wwg-inner {
          max-width: 1380px;
          margin: 0 auto;
        }

        .wwg-grid-wrap {
          position: relative;
          overflow: visible;
        }

        .wwg-flightpath {
          position: absolute;
          inset: 0;
          pointer-events: none;
          overflow: visible;
          z-index: 5;
        }

        .wwg-flightpath-line {
          fill: none;
          stroke: #c8102e;
          stroke-width: 2;
          stroke-linecap: round;
          stroke-linejoin: round;
          opacity: 0;
        }

        /* A brighter, blurred copy of the same route, painted *behind* the
           crisp foreground line (see JSX order). Its dasharray is driven
           per-frame (see onUpdate) to keep a soft window of it trailing
           directly beneath the pulse; because it sits behind rather than on
           top, its blur reads as a glow radiating from under the route
           instead of visually thickening the route's own edges. Stroke
           width intentionally matches the base route so it never peeks out
           wider than the crisp line drawn on top of it. */
        .wwg-illum-trail {
          fill: none;
          stroke: #ff3d5a;
          stroke-width: 2;
          stroke-linecap: round;
          filter: blur(5px);
          opacity: 0;
        }

        /* The traveler is the primary visual: a single centered optical
           pulse riding the route, with a small secondary aircraft glyph
           carried inside it. Position (translate) lives on .wwg-traveler;
           only the inner .wwg-traveler-directional group rotates to face
           the direction of travel — the round pulse core stays outside it
           so it never rotates, always reading as a clean point of light. */
        .wwg-traveler {
          opacity: 0;
          pointer-events: none;
          will-change: transform;
          /* Explicit center alignment (paired with alignOrigin: [0.5, 0.5]
             on the motionPath tween) so the traveler's own center — not a
             corner/default reference point — is what MotionPathPlugin locks
             to the route, keeping it precisely on the path's centerline
             with no vertical drift. */
          transform-origin: 50% 50%;
        }

        .wwg-traveler-directional {
          transform-origin: 0px 0px;
          will-change: transform;
        }

        /* Large, radially-symmetric ambient glow — the true hero of the
           traveler. Centered on the pulse with no directional offset, so
           there is never a brighter patch leading ahead of the aircraft.
           Non-directional, so it lives outside the rotating
           .wwg-traveler-directional group. */
        .wwg-pulse-bloom {
          fill: #c8102e;
          opacity: 0.2;
          filter: blur(18px);
        }

        .wwg-pulse-halo {
          fill: #c8102e;
          opacity: 0.52;
          filter: blur(7px);
        }

        .wwg-pulse-core {
          fill: #ffffff;
          filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.98)) drop-shadow(0 0 9px rgba(200, 16, 46, 0.85))
            drop-shadow(0 0 15px rgba(200, 16, 46, 0.5));
        }

        /* Deliberately no drop-shadow of its own — the bloom/halo/trail
           above supply all the glow. Sized to be clearly recognizable as an
           aircraft without becoming the focal point: the pulse is still the
           brightest, largest element on screen at every moment. */
        .wwg-aircraft {
          color: #c8102e;
          opacity: 0.65;
        }

        .wwg-grid {
          display: grid;
          grid-template-columns: repeat(4, minmax(0, 1fr));
          gap: 24px;
        }

        .wwg-card {
          position: relative;
          min-width: 0;
          padding: 30px 24px;
          border: 1px solid rgba(17, 17, 17, 0.08);
          border-radius: 18px;
          background: #fafafa;
          /* Staggered delays so activation reads as a cascade — glow moves
             first (see .wwg-card-glow below, no delay), then border/shape,
             then the background tint settles last — rather than every
             property snapping at once. The "expo-out" curve (rather than
             plain ease) is what gives it a soft, premium settle instead of a
             mechanical linear-ish snap. */
          transition:
            border-color 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.08s,
            box-shadow 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.08s,
            transform 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.08s,
            background-color 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.22s;
        }

        .wwg-card:hover {
          border-color: rgba(200, 16, 46, 0.35);
          box-shadow: 0 20px 40px rgba(0, 0, 0, 0.06);
          transform: translateY(-4px);
        }

        .wwg-card-glow {
          position: absolute;
          inset: -12px;
          border-radius: 22px;
          background: radial-gradient(circle, rgba(200, 16, 46, 0.45) 0%, rgba(200, 16, 46, 0) 70%);
          opacity: 0;
          z-index: -1;
          pointer-events: none;
          filter: blur(6px);
          transition: opacity 0.75s cubic-bezier(0.16, 1, 0.3, 1);
        }

        .wwg-card-flown {
          border-color: rgba(200, 16, 46, 0.25);
          background-color: #fdf7f8;
        }

        .wwg-card-flown .wwg-card-glow {
          opacity: 0.32;
        }

        .wwg-card-pulse {
          border-color: rgba(200, 16, 46, 0.5);
          box-shadow: 0 16px 34px rgba(0, 0, 0, 0.07);
          transform: translateY(-3px);
        }

        .wwg-card-pulse .wwg-card-glow {
          opacity: 0.85;
        }

        .wwg-icon {
          width: 52px;
          height: 52px;
          display: flex;
          align-items: center;
          justify-content: center;
          border-radius: 14px;
          background: rgba(200, 16, 46, 0.06);
          color: #c8102e;
          margin-bottom: 20px;
          transition:
            background-color 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.16s,
            filter 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.16s;
        }

        .wwg-card-flown .wwg-icon {
          background: rgba(200, 16, 46, 0.1);
        }

        .wwg-card-pulse .wwg-icon {
          background: rgba(200, 16, 46, 0.14);
          filter: brightness(1.12);
        }

        .wwg-card-title {
          font-size: 17px !important;
          font-weight: 700 !important;
          line-height: 1.3 !important;
          text-transform: none !important;
          color: #111111 !important;
          margin: 0 0 8px 0;
        }

        .wwg-card-desc {
          font-size: 14.5px;
          line-height: 1.55;
          color: rgba(17, 17, 17, 0.6);
          margin: 0;
        }

        @media (max-width: 1024px) {
          .wwg-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
        }

        @media (max-width: 600px) {
          .wwg-grid { grid-template-columns: 1fr; padding-left: 28px; }
          /* var(--space-section-lg) floors at a fixed 52px on any phone
             width; trimmed to a real mobile value to match the section
             rhythm near the footer. Matches this file's own MOBILE_BQ
             breakpoint used by the flight-path animation logic. */
          .wwg-section { padding-top: 44px; padding-bottom: 44px; }
        }

        @media (prefers-reduced-motion: reduce) {
          .wwg-card, .wwg-card-glow, .wwg-icon {
            transition: none;
          }
        }
      `,
        }}
      />

      <section className="wwg-section" id="wings-why">
        <div className="dm-section-container">
          <div className="wwg-inner">
            <ScrollReveal delay={0.05} duration={0.75} yOffset={25} className="wwg-header">
              <div className="wwg-eyebrow">/ Why Doormile Wings /</div>
              <h2 className="wwg-title">Built for the speed of the Gen-Z economy</h2>
            </ScrollReveal>

            <div className="wwg-grid-wrap" ref={wrapRef}>
              <svg ref={svgRef} className="wwg-flightpath" aria-hidden="true" focusable="false">
                {/* Rendered first (behind) so its blur/extra width reads as a
                    soft glow sitting under the route rather than visually
                    thickening it — the crisp .wwg-flightpath-line painted
                    after it always defines the route's actual edges. */}
                <path ref={illumRef} className="wwg-illum-trail" d="" />
                <path ref={pathRef} className="wwg-flightpath-line" d="" />
                <g ref={travelerRef} className="wwg-traveler">
                  {/* Large feathered bloom: the true hero of the traveler, an
                      ambient glow with no direction of its own. */}
                  <circle className="wwg-pulse-bloom" cx="0" cy="0" r="26" />
                  <g ref={directionalRef} className="wwg-traveler-directional">
                    <ellipse className="wwg-pulse-halo" cx="0" cy="0" rx="11" ry="6" />
                  </g>
                  <circle className="wwg-pulse-core" cx="0" cy="0" r="2.6" />
                  {/* Painted after (on top of) the core so the aircraft's
                      silhouette stays readable instead of being buried under
                      the bright core dot. Rotation mirrors directionalRef
                      exactly (see onUpdate) despite living in its own group. */}
                  <g ref={aircraftDirectionalRef} className="wwg-traveler-directional">
                    {/* The stock glyph's original 24x24 viewBox had ~3.5
                        units of dead padding on every side around the actual
                        ink (true silhouette bbox was only 17x20) — scaling
                        from the nominal 24-unit box would have kept baking
                        that padding in and rendering smaller than intended.
                        These coordinates instead start from the silhouette's
                        own tight bbox (no padding), centered on (0,0), then
                        scaled 1.3x — giving a true rendered size of ~22x26px,
                        clearing 22px on both axes. Deliberately no
                        scale()/translate() transform: baked directly into
                        the path data so it renders at full native
                        resolution rather than through a transform stacked on
                        top of this group's own rotation transform. */}
                    <path
                      className="wwg-aircraft"
                      d="M0,-13 C-0.78,-13 -1.3,-12.22 -1.3,-11.31 L-1.3,-3.9 L-11.05,1.3 L-11.05,3.9 L-1.3,0.91 L-1.3,9.1 L-4.55,11.44 L-4.55,13 L0,11.7 L4.55,13 L4.55,11.44 L1.3,9.1 L1.3,0.91 L11.05,3.9 L11.05,1.3 L1.3,-3.9 L1.3,-11.31 C1.3,-12.22 0.78,-13 0,-13 Z"
                      fill="currentColor"
                    />
                  </g>
                </g>
              </svg>

              <StaggerChildren stagger={0.1} duration={0.7} yOffset={30} className="wwg-grid">
                {FEATURES.map((f, i) => (
                  <div
                    key={f.title}
                    className="wwg-card"
                    ref={(el) => {
                      cardRefs.current[i] = el;
                    }}
                  >
                    <span className="wwg-card-glow" aria-hidden="true" />
                    <div
                      className="wwg-icon"
                      aria-hidden="true"
                      ref={(el) => {
                        iconRefs.current[i] = el;
                      }}
                    >
                      {f.icon}
                    </div>
                    <h3 className="wwg-card-title">{f.title}</h3>
                    <p className="wwg-card-desc">{f.desc}</p>
                  </div>
                ))}
              </StaggerChildren>
            </div>
          </div>
        </div>
      </section>
    </>
  );
}
