// HeroMorph.jsx — Cinematic transition. The green winning agent cell
// from the hero swarm lifts out and grows to fullscreen, morphing into
// the 4-step "How it works" layout. Driven by window.scrollY directly
// (NOT by a sticky wrap) so the morph begins while the hero is still
// in view — the ghost overlays the real cell at scroll-start and
// interpolates from there.

const HM_CSS = `
/* The wrap is just a tall spacer. The hero stays as-is above it; the
   morph itself is rendered as a fixed overlay on <body>. */
.hm-wrap{position:relative;height:120vh;background:var(--ink-900);
  /* During the second half, fade in a soft galaxy bg behind the steps */
  isolation:isolate;}
.hm-wrap__bg{position:sticky;top:0;height:100vh;width:100%;
  pointer-events:none;opacity:0;transition:none;
  background:
    radial-gradient(ellipse 70% 60% at 50% 40%, rgba(193,250,29,0.05) 0%, transparent 55%),
    radial-gradient(ellipse 60% 70% at 50% 90%, rgba(140,110,255,0.07) 0%, transparent 60%);}
.hm-wrap__grid{position:sticky;top:0;height:100vh;width:100%;margin-top:-100vh;
  pointer-events:none;opacity:0;
  background-image:linear-gradient(rgba(255,255,255,0.018) 1px, transparent 1px),
                   linear-gradient(90deg, rgba(255,255,255,0.018) 1px, transparent 1px);
  background-size:48px 48px;
  -webkit-mask-image:radial-gradient(ellipse at 50% 50%, black 30%, transparent 80%);
  mask-image:radial-gradient(ellipse at 50% 50%, black 30%, transparent 80%);}

/* The flying ghost — fixed-position, interpolates from the source cell
   rect to a centered fullscreen rect. */
.hm-ghost{position:fixed;left:0;top:0;
  background:#2A2538;
  border:1px solid rgba(193,250,29,0.5);
  border-radius:8px;
  box-shadow:inset 0 0 0 1px rgba(193,250,29,0.15),
             0 22px 60px -20px rgba(0,0,0,0.6);
  overflow:hidden;
  pointer-events:none;
  will-change:transform,width,height;
  z-index:50;
  display:none;}
.hm-ghost[data-on="true"]{display:block;}
.hm-ghost[data-phase="2"]{
  background:var(--ink-900);
  border-color:rgba(255,255,255,0.08);
  box-shadow:none;}

/* Cell content — what the green cell looks like at scroll-start.
   Must visually MATCH the hero's .psaH__cell.is-winner so the handoff
   is invisible. */
.hm-cell{position:absolute;inset:0;padding:12px;
  display:flex;flex-direction:column;gap:10px;
  font-family:var(--font-mono);font-size:10px;
  color:rgba(255,255,255,0.75);
  will-change:opacity;}
.hm-cell__head{display:flex;justify-content:space-between;align-items:center;gap:8px;
  font-size:10px;letter-spacing:-0.02em;min-width:0;}
.hm-cell__head .id{display:inline-flex;align-items:center;gap:7px;
  color:var(--lime-500);font-weight:500;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex:1;}
.hm-cell__head .id .dot{width:6px;height:6px;border-radius:50%;flex-shrink:0;
  background:var(--lime-500);box-shadow:0 0 6px rgba(193,250,29,0.55);}
.hm-cell__head .proj{color:rgba(247,247,237,0.38);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;flex-shrink:1;}
.hm-cell__rows{display:flex;flex-direction:column;gap:6px;}
.hm-cell__line{height:15px;display:flex;align-items:center;
  font-size:11px;letter-spacing:-0.01em;line-height:15px;font-weight:500;
  color:rgba(247,247,237,0.55);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.hm-cell__delta{margin-top:auto;text-align:right;font-size:10px;letter-spacing:0.02em;
  color:var(--lime-500);font-weight:500;}

/* 4-step grid that fades in once ghost is huge */
.hm-steps{position:absolute;inset:0;
  display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:1fr;
  background:rgba(255,255,255,0.08);
  will-change:opacity;}
.hm-step{background:var(--ink-900);
  padding:36px 28px;
  display:flex;flex-direction:column;gap:14px;
  position:relative;min-width:0;
  border-right:1px solid rgba(255,255,255,0.08);}
.hm-step:last-child{border-right:none;}
.hm-step--verify{background:linear-gradient(180deg, rgba(193,250,29,0.06) 0%, var(--ink-900) 100%);}
.hm-step--verify::before{content:"";position:absolute;left:0;right:0;top:0;height:2px;
  background:var(--lime-500);opacity:0.7;}
.hm-step__icon{height:120px;
  display:flex;align-items:stretch;
  margin:-4px -8px 6px;padding:14px;border-radius:6px;
  background:rgba(255,255,255,0.025);
  border:1px solid rgba(255,255,255,0.06);
  color:rgba(247,247,237,0.85);position:relative;overflow:hidden;}
.hm-step__icon svg{height:100%;width:100%;display:block;}
.hm-step__icon .accent{color:var(--lime-500);}
.hm-step--verify .hm-step__icon{
  background:rgba(193,250,29,0.04);
  border-color:rgba(193,250,29,0.18);}
.hm-step__num{font-family:var(--font-mono);font-size:11px;letter-spacing:0.08em;
  text-transform:uppercase;color:var(--lime-500);}
.hm-step__title{font-family:var(--font-display);font-size:22px;font-weight:400;
  letter-spacing:-0.015em;line-height:1.2;margin:0;color:var(--cream-50);}
.hm-step__desc{font-family:var(--font-sans);font-size:14px;line-height:1.55;
  color:rgba(247,247,237,0.68);margin:0;}

/* Headline that fades in during the morph */
.hm-headline{position:fixed;left:0;right:0;top:max(120px, 14vh);z-index:51;
  text-align:center;padding:0 40px;
  font-family:var(--font-display);font-weight:400;
  font-size:clamp(36px,4.4vw,56px);line-height:1.05;letter-spacing:-0.03em;
  color:var(--cream-50);
  opacity:0;transform:translateY(20px);
  pointer-events:none;margin:0;
  display:none;}
.hm-headline[data-on="true"]{display:block;}
.hm-headline em{font-family:var(--font-serif);font-style:italic;}
.hm-headline .kicker{display:block;font-family:var(--font-mono);font-size:11px;letter-spacing:0.14em;
  text-transform:uppercase;color:var(--lime-500);margin-bottom:14px;font-weight:400;}
.hm-mobile{display:none;}

@media(max-height:620px) and (min-width:769px){
  .hm-headline{
    top:82px;
    font-size:clamp(28px,3.4vw,38px);
  }
  .hm-step{
    padding:18px 18px;
    gap:8px;
  }
  .hm-step__icon{
    height:70px;
    margin:0 0 4px;
    padding:8px;
  }
  .hm-step__num{
    font-size:10px;
    letter-spacing:0.07em;
  }
  .hm-step__title{
    font-size:19px;
    line-height:1.15;
  }
  .hm-step__desc{
    font-size:12px;
    line-height:1.38;
  }
}

@media(max-width:768px){
  .hm-wrap{
    height:auto;
    min-height:0;
    padding:84px 16px 72px;
  }
  .hm-wrap__bg,
  .hm-wrap__grid{display:none;}
  .hm-ghost,
  .hm-headline{display:none !important;}
  .hm-mobile{
    display:block;
    max-width:560px;
    margin:0 auto;
  }
  .hm-mobile__title{
    margin:0 0 28px;
    font-family:var(--font-display);
    font-weight:400;
    font-size:clamp(36px,10vw,52px);
    line-height:1.04;
    letter-spacing:-0.035em;
    color:var(--cream-50);
    text-align:center;
  }
  .hm-mobile__title em{font-family:var(--font-serif);font-style:italic;}
  .hm-mobile__steps{
    display:grid;
    grid-template-columns:1fr;
    border:1px solid rgba(255,255,255,0.08);
    border-radius:14px;
    overflow:hidden;
    background:rgba(255,255,255,0.02);
  }
  .hm-mobile__step{
    padding:24px;
    border-bottom:1px solid rgba(255,255,255,0.08);
  }
  .hm-mobile__step:last-child{border-bottom:0;}
  .hm-mobile__step--verify{
    background:linear-gradient(180deg, rgba(193,250,29,0.08) 0%, rgba(14,13,20,0) 100%);
    box-shadow:inset 3px 0 0 rgba(193,250,29,0.8);
  }
  .hm-mobile__icon{
    height:96px;
    margin-bottom:18px;
    padding:12px;
    border:1px solid rgba(255,255,255,0.06);
    border-radius:8px;
    background:rgba(255,255,255,0.025);
    color:rgba(247,247,237,0.85);
  }
  .hm-mobile__icon svg{width:100%;height:100%;display:block;}
  .hm-mobile__icon .accent{color:var(--lime-500);}
  .hm-mobile__num{
    margin-bottom:12px;
    font-family:var(--font-mono);
    font-size:11px;
    letter-spacing:0.08em;
    text-transform:uppercase;
    color:var(--lime-500);
  }
  .hm-mobile__step h3{
    margin:0 0 10px;
    font-family:var(--font-display);
    font-size:28px;
    font-weight:400;
    line-height:1.12;
    letter-spacing:-0.02em;
    color:var(--cream-50);
  }
  .hm-mobile__step p{
    margin:0;
    font-size:15px;
    line-height:1.55;
    color:rgba(247,247,237,0.68);
  }
}
`;

const HM_STEPS = [
  { num: "01 · HYPOTHESIZE", title: "Sees every paper.",            desc: "Extracts methods and hypothesizes combinations and innovations specific to your model." },
  { num: "02 · EXPERIMENT",  title: "Experiments on your model.",    desc: "Mira spins up isolated training runs, tracks loss curves, compares against your baseline." },
  { num: "03 · VERIFY",      title: "Verifies on your eval.",        desc: "No agent decides what \u201cbetter\u201d means. Your eval set, your metrics, your significance threshold \u2014 deterministic pass/fail. No p-hacking.", verify: true },
  { num: "04 · SHIP",        title: "Ships improvements as PRs.",    desc: "Nothing merges until it beats your baseline on your verifier. Each PR ships with the run logs, ablations, and a writeup your team can audit." },
];

// History rail removed — the 4-step panels themselves serve as the
// "inside the agent" content (revealed as the box grows).

const lerp = (a, b, t) => a + (b - a) * t;
const clamp = (x, a = 0, b = 1) => Math.max(a, Math.min(b, x));
const smooth = (t) => t * t * (3 - 2 * t);
const phase = (t, a, b) => smooth(clamp((t - a) / (b - a), 0, 1));

const HeroMorph = () => {
  const wrapRef    = React.useRef(null);
  const bgRef      = React.useRef(null);
  const gridRef    = React.useRef(null);
  const ghostRef   = React.useRef(null);
  const cellRef    = React.useRef(null);
  const stepsRef   = React.useRef(null);
  const headRef    = React.useRef(null);

  React.useEffect(() => {
    let raf = 0;

    // Locked viewport Y of the cell, captured at p=0.05 (start of
    // move phase). Used as the constant vertical center for the entire
    // morph — box stays at this on-screen Y from p=0.05 through end.
    let lockedSrcVY = null;

    // Compute the source cell's document position. Re-run on resize.
    const getSourceRect = () => {
      const cell = document.querySelector(".psaH .psaH__cell.is-winner");
      if (!cell) return null;
      const r = cell.getBoundingClientRect();
      return {
        docX: r.left + window.scrollX,
        docY: r.top  + window.scrollY,
        w: r.width,
        h: r.height,
      };
    };

    const update = () => {
      raf = 0;
      const wrap   = wrapRef.current;
      const ghost  = ghostRef.current;
      const cell   = cellRef.current;
      const steps  = stepsRef.current;
      const head   = headRef.current;
      const bg     = bgRef.current;
      const grid   = gridRef.current;
      if (!wrap || !ghost) return;

      if (window.innerWidth <= 768) {
        ghost.dataset.on = "false";
        if (head) head.dataset.on = "false";
        const heroCard = document.querySelector(".cB-hero");
        const winnerCell = document.querySelector(".psaH .psaH__cell.is-winner");
        if (heroCard) heroCard.style.opacity = "";
        if (winnerCell) winnerCell.style.visibility = "";
        return;
      }

      const heroCard = document.querySelector(".cB-hero");
      const winnerCell = document.querySelector(".psaH .psaH__cell.is-winner");

      const wr = wrap.getBoundingClientRect();
      const vh = window.innerHeight;
      const wrapH = wrap.offsetHeight;

      // Scroll progress through the wrap (0 = wrap top at viewport bottom, 1 = wrap bottom at viewport bottom)
      const scrolled = vh - wr.top;
      const pRaw = scrolled / wrapH;
      const p = clamp(pRaw, 0, 1);

      // Extra "release" progress — used for the exit fade only.
      // Continues past p=1 as the user keeps scrolling and Outcomes
      // scrolls up under the (still fixed) ghost overlay.
      // pRaw = 1.0 → Outcomes top at viewport bottom
      // pRaw = 1 + vh/wrapH → Outcomes top at viewport top
      const releaseT = clamp((pRaw - 1.0) / (vh / wrapH), 0, 1);

      // Detect "fully past" state — when we've scrolled past the entire wrap.
      // In that case, hide the ghost + headline so they don't overlay
      // the next sections (Outcomes etc).
      const past = wr.bottom <= 0;
      const before = wr.top > vh; // wrap hasn't entered viewport yet

      if (past || before) {
        ghost.dataset.on = "false";
        if (head) head.dataset.on = "false";
        // Restore hero appearance when not in the morph window
        if (heroCard) heroCard.style.opacity = "";
        if (winnerCell) winnerCell.style.visibility = "";
        // Reset the lock so the next entry recaptures at p=0.05
        if (before) lockedSrcVY = null;
        return;
      }

      // Below the move threshold, leave the real cell visible and hide
      // the ghost entirely. This avoids a one-frame sync gap between
      // the ghost (positioned via rAF after a scroll event) and the
      // cell (laid out with the page) that produced visible jitter
      // while the box was still in the agent grid.
      if (p < 0.05) {
        ghost.dataset.on = "false";
        if (head) head.dataset.on = "false";
        if (heroCard) heroCard.style.opacity = "";
        if (winnerCell) winnerCell.style.visibility = "";
        lockedSrcVY = null;
        return;
      }

      // Source rect (cell in the hero, in viewport coords)
      const src = getSourceRect();
      if (!src || !heroCard) return;
      const srcX = src.docX - window.scrollX;
      const srcY = src.docY - window.scrollY;
      const srcW = src.w;
      const srcH = src.h;

      // Capture the cell's viewport Y at the moment the move phase
      // begins (p ≥ 0.05). Below 0.05 the lock is released so the
      // ghost tracks the live cell exactly — this prevents overshoot
      // when the user scrolls back up through the morph.
      if (p < 0.05) {
        lockedSrcVY = null;
      } else if (lockedSrcVY === null) {
        lockedSrcVY = srcY;
      }
      const lockedCY = (lockedSrcVY !== null ? lockedSrcVY : srcY) + srcH / 2;

      // Target rect — fixed-size centered card. On short viewports, use
      // compact CSS and give the card more of the viewport so text fits.
      const shortViewport = vh <= 620;
      const padX = 40;
      const tgtW = Math.min(window.innerWidth - padX * 2, 1280);
      const cardTop = shortViewport ? 140 : Math.max(96 + 80, vh * 0.30);
      const bottomPad = shortViewport ? 16 : 64;
      const tgtH = shortViewport
        ? Math.min(360, vh - cardTop - bottomPad)
        : Math.min(420, vh - 240); // breathing room above + below
      const tgtX = (window.innerWidth - tgtW) / 2;
      // tgtY/tgtCY: locked to the captured viewport Y so the vertical
      // center is constant across the whole animation.
      const tgtCX = tgtX + tgtW / 2;
      const tgtCY = lockedCY;
      const tgtY = tgtCY - tgtH / 2;

      // Center of source
      const srcCX = srcX + srcW / 2;
      const srcCY = srcY + srcH / 2;

      // Phase split:
      //   move:    0.05 → 0.30 — cell translates to target center (still small)
      //   grow:    0.30 → 0.55 — cell scales up from center to target size
      //   cellOut: 0.40 → 0.60 — cell content fades
      //   stepsIn: 0.60 → 0.85
      //   headIn:  0.60 → 0.95
      // Phase split (in p ∈ [0,1]):
      //   move:    0.05 → 0.20 — cell translates to target center (still small)
      //   grow:    0.20 → 0.40 — cell scales up from center to target size
      //   cellOut: 0.30 → 0.45 — cell content fades
      //   stepsIn: 0.45 → 0.65
      //   headIn:  0.45 → 0.70
      //   (dwell:  0.70 → 0.95 — fully morphed, user reads)
      //   exit:    0.95 → 1.00 — fade out
      const moveT = phase(p, 0.05, 0.20);
      const growT = phase(p, 0.20, 0.40);

      // Move phase: slide horizontally to target X at the locked Y.
      // Grow phase: scale symmetrically around the locked center.
      // The vertical center stays at lockedCY for the entire animation.
      const moveCX = lerp(srcCX, tgtCX, moveT);
      const cx = lerp(moveCX, tgtCX, growT);
      // Drop under the headline during reveal. Short viewports use less
      // headline reservation and compact card styling.
      const finalCY = cardTop + tgtH / 2;
      const dropT = phase(p, 0.45, 0.70);
      const cy = lerp(lockedCY, finalCY, dropT);

      // Size interpolates from src size to tgt size, but only after move
      const w = lerp(srcW, tgtW, growT);
      const h = lerp(srcH, tgtH, growT);

      // Top-left from center
      const x = cx - w / 2;
      const y = cy - h / 2;

      ghost.dataset.on = "true";
      ghost.style.transform = `translate(${x}px, ${y}px)`;
      ghost.style.width  = w + "px";
      ghost.style.height = h + "px";

      // Hide underlying cell once ghost is engaged
      if (winnerCell) winnerCell.style.visibility = "hidden";

      // Hero card fade-out: starts a bit later so the headline lingers
      const heroFade = phase(p, 0.18, 0.38);
      heroCard.style.opacity = (1 - heroFade).toFixed(3);

      // Cell content fade-out / Steps fade-in
      // Steps reveal during grow so the 4 panels emerge as the box
      // expands — feels like zooming into the agent.
      const cellOut = phase(p, 0.25, 0.40);
      // Gradually interpolate ghost chrome (bg, border, shadow) from
      // "winner cell" look to "morphed card" look.
      // Phase 1: bg #2A2538, border lime 0.5, lime inner shadow + drop shadow
      // Phase 2: bg ink-900,  border white 0.08, no shadow
      const chromeT = phase(p, 0.30, 0.55);
      // bg: #2A2538 (42,37,56) → #0E0D14 (ink-900, 14,13,20)
      const bgR = Math.round(lerp(42, 14, chromeT));
      const bgG = Math.round(lerp(37, 13, chromeT));
      const bgB = Math.round(lerp(56, 20, chromeT));
      ghost.style.background = `rgb(${bgR},${bgG},${bgB})`;
      // border: rgba(193,250,29,0.5) → rgba(255,255,255,0.08)
      const bR = Math.round(lerp(193, 255, chromeT));
      const bG = Math.round(lerp(250, 255, chromeT));
      const bB = Math.round(lerp(29, 255, chromeT));
      const bA = lerp(0.5, 0.08, chromeT);
      ghost.style.borderColor = `rgba(${bR},${bG},${bB},${bA.toFixed(3)})`;
      // shadow: lime inner + drop → none
      const shA1 = lerp(0.15, 0, chromeT);
      const shA2 = lerp(0.6, 0, chromeT);
      ghost.style.boxShadow =
        `inset 0 0 0 1px rgba(193,250,29,${shA1.toFixed(3)}),` +
        `0 22px 60px -20px rgba(0,0,0,${shA2.toFixed(3)})`;
      const stepsIn = phase(p, 0.28, 0.50);
      if (cell)  cell.style.opacity  = (1 - cellOut).toFixed(3);
      if (steps) steps.style.opacity = stepsIn.toFixed(3);

      // Background reveal — opacity applied below combined with exit fade
      const bgIn = phase(p, 0.25, 0.55);

      // Headline
      const headIn = phase(p, 0.45, 0.70);
      // Exit fade is driven by `releaseT` (the page scrolling past the
      // wrap), NOT by `p` clamping. This way the ghost fades out exactly
      // as the next section scrolls up underneath, instead of fading to
      // empty black. Fade completes quickly (in ~35% of the release) so
      // the next section becomes the focus.
      const exitFade = clamp(releaseT / 0.35, 0, 1);
      const exitOpacity = 1 - exitFade;

      if (head) {
        head.dataset.on = (headIn > 0 && exitOpacity > 0) ? "true" : "false";
        head.style.opacity   = (headIn * exitOpacity).toFixed(3);
        head.style.transform = `translateY(${lerp(20, 0, headIn)}px)`;
      }

      // Apply exit fade to the whole ghost
      ghost.style.opacity = exitOpacity.toFixed(3);
      if (exitOpacity <= 0) {
        ghost.dataset.on = "false";
      }
      // Also fade bg
      if (bg)   bg.style.opacity   = (bgIn * exitOpacity).toFixed(3);
      if (grid) grid.style.opacity = (bgIn * exitOpacity).toFixed(3);

      // When morph is fully complete (p>=1), restore the hero card
      // and cell visibility and let the ghost stay parked at fullscreen.
      // (User won't see the hero — it's scrolled out anyway.)
    };

    const onScroll = () => {
      if (raf) return;
      raf = requestAnimationFrame(update);
    };
    const onResize = () => { lockedSrcVY = null; onScroll(); };

    update();
    setTimeout(update, 100);
    setTimeout(update, 500);

    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onResize);

    return () => {
      cancelAnimationFrame(raf);
      window.removeEventListener("scroll", onScroll);
      window.removeEventListener("resize", onResize);
      // Restore hero
      const heroCard = document.querySelector(".cB-hero");
      if (heroCard) heroCard.style.opacity = "";
      const winnerCell = document.querySelector(".psaH .psaH__cell.is-winner");
      if (winnerCell) winnerCell.style.visibility = "";
    };
  }, []);

  const HowIcons = window.HOW_ICONS || [];

  return (
    <React.Fragment>
      <style dangerouslySetInnerHTML={{ __html: HM_CSS }} />
      <section ref={wrapRef} className="hm-wrap" data-screen-label="02 How it works (cinematic)">
        <div ref={bgRef}   className="hm-wrap__bg" />
        <div ref={gridRef} className="hm-wrap__grid" />
        <div className="hm-mobile">
          <h2 className="hm-mobile__title">
            Mira improves ML models <em>at machine speed</em>.
          </h2>
          <div className="hm-mobile__steps">
            {HM_STEPS.map((s, i) => {
              const Icon = HowIcons[i];
              return (
                <div key={s.num} className={"hm-mobile__step" + (s.verify ? " hm-mobile__step--verify" : "")}>
                  {Icon && <div className="hm-mobile__icon"><Icon /></div>}
                  <div className="hm-mobile__num">{s.num}</div>
                  <h3>{s.title}</h3>
                  <p>{s.desc}</p>
                </div>
              );
            })}
          </div>
        </div>
      </section>

      {/* Headline + ghost are fixed/portal-style — they live OUTSIDE the
          wrap so they overlay the page. */}
      <h2 ref={headRef} className="hm-headline" data-on="false">
        Mira improves ML models <em>at machine speed</em>.
      </h2>

      <div ref={ghostRef} className="hm-ghost" data-on="false" data-phase="1">
        {/* Cell-state content — visually identical to the hero winner cell */}
        <div ref={cellRef} className="hm-cell">
          <div className="hm-cell__head">
            <span className="id"><span className="dot" />agent-a107</span>
            <span className="proj">moe-gated-v4</span>
          </div>
          {window.PSA_Cycler && (
            <window.PSA_Cycler
              thoughts={["aux-free balancing", "converged", "\u2713 passed eval"]}
              dim={false}
              prefix="psaH"
            />
          )}
          <div className="hm-cell__delta">+4.1%</div>
        </div>

        {/* Steps — fades in */}
        <div ref={stepsRef} className="hm-steps" style={{ opacity: 0 }}>
          {HM_STEPS.map((s, i) => {
            const Icon = HowIcons[i];
            return (
              <div key={s.num} className={"hm-step" + (s.verify ? " hm-step--verify" : "")}>
                {Icon && <div className="hm-step__icon"><Icon /></div>}
                <div className="hm-step__num">{s.num}</div>
                <h3 className="hm-step__title">{s.title}</h3>
                <p className="hm-step__desc">{s.desc}</p>
              </div>
            );
          })}
        </div>
      </div>
    </React.Fragment>
  );
};

window.HeroMorph = HeroMorph;
