1// HeroScene.jsx â looping hero visual for Conversion Works. 2// A central "YOUR WEBSITE" device powers on first; then, one surface at a time, 3// a connector arrow draws toward a CRO card and that card loads in. The order 4// sweeps around the device: top-left â middle-left â bottom-left â bottom â 5// bottom-right â middle-right â top-right. After the full state, it holds, then 6// loops. Cards are transparent cutouts (normal blend); arrows are glow-on-black 7// (screen blend). Honors prefers-reduced-motion (everything shown at rest). 8 9const HS_A = (p) => assetUrl("assets/hero-scene/" + p); 10 11const HS_CARDS = [ 12 { k: "card-pdp-v3.avif", cls: "hs-pdp" }, 13 { k: "card-checkout-v3.avif", cls: "hs-checkout" }, 14 { k: "card-reviews-v3.avif", cls: "hs-reviews" }, 15 { k: "card-heatmap-v3.avif", cls: "hs-heatmap" }, 16 { k: "card-cart-v3.avif", cls: "hs-cart" }, 17 { k: "card-tracking-v3.avif", cls: "hs-tracking" }, 18 { k: "card-analytics-v3.avif", cls: "hs-analytics" }, 19]; 20 21// connector arrows â each art-on-black asset bridges its card to the central device. 22const HS_STREAKS = [ 23 { k: "arrow-pdp.avif", cls: "hs-ar-pdp" }, 24 { k: "arrow-cart.avif", cls: "hs-ar-cart" }, 25 { k: "arrow-heatmap.avif", cls: "hs-ar-heatmap" }, 26 { k: "arrow-checkout.avif", cls: "hs-ar-checkout" }, 27 { k: "arrow-reviews.avif", cls: "hs-ar-reviews" }, 28 { k: "arrow-tracking.avif", cls: "hs-ar-tracking" }, 29 { k: "arrow-analytics.avif", cls: "hs-ar-analytics" }, 30]; 31 32// reveal order: device, then arrowâcard around the layout. Keys match the class 33// suffix after "hs-" (e.g. card "hs-pdp" â "pdp", arrow "hs-ar-pdp" â "ar-pdp"). 34const HS_ORDER = [ 35 "tablet", 36 "ar-pdp", "pdp", 37 "ar-heatmap", "heatmap", 38 "ar-reviews", "reviews", 39 "ar-tracking", "tracking", 40 "ar-analytics", "analytics", 41 "ar-checkout", "checkout", 42 "ar-cart", "cart", 43]; 44 45// per-key reveal time (ms). The card loads almost the instant its arrow lands, and 46// the next arrow follows a split-second later â no empty beats. 47const HS_TIMING = (() => { 48 const t = { tablet: 250 }; 49 const pairs = ["pdp", "heatmap", "reviews", "tracking", "analytics", "checkout", "cart"]; 50 pairs.forEach((p, i) => { 51 const arrow = 480 + i * 290; // next arrow fires before the previous card finishes 52 t["ar-" + p] = arrow; 53 t[p] = arrow + 90; // card loads right behind its arrow 54 }); 55 return t; 56})(); 57const HS_LOOP_AT = 480 + 6 * 290 + 90 + 3200; // last card + ~3.2s hold, then loop 58 59function HeroScene() { 60 const reduce = typeof window !== "undefined" && window.matchMedia && 61 window.matchMedia("(prefers-reduced-motion: reduce)").matches; 62 const [shown, setShown] = useState(() => reduce ? new Set(HS_ORDER) : new Set()); 63 64 useEffect(() => { 65 if (reduce) return; 66 let timers = []; 67 const run = () => { 68 setShown(new Set()); 69 HS_ORDER.forEach((key) => { 70 timers.push(setTimeout(() => { 71 setShown((prev) => { const n = new Set(prev); n.add(key); return n; }); 72 }, HS_TIMING[key])); 73 }); 74 timers.push(setTimeout(run, HS_LOOP_AT)); 75 }; 76 const start = setTimeout(run, 300); 77 timers.push(start); 78 return () => timers.forEach(clearTimeout); 79 }, []); 80 81 const sc = (cls) => cls.replace("hs-", ""); // class â reveal key 82 83 return ( 84 <div className="hs-box" aria-hidden="true"> 85 <div className="hs-haze" /> 86 <div className="hs-slab" /> 87 <img className="hs-beam hs-beam-top" src={assetUrl("assets/hero-beam-purple.avif")} alt="" /> 88 <img className="hs-beam hs-beam-bottom" src={assetUrl("assets/hero-beam-purple.avif")} alt="" /> 89 <div className="hs-scene"> 90 {HS_STREAKS.map((s) => ( 91 <img key={s.k} src={HS_A(s.k)} alt="" 92 className={"hs-streak " + s.cls + (shown.has(sc(s.cls)) ? " is-shown" : "")} /> 93 ))} 94 <img className={"hs-tablet" + (shown.has("tablet") ? " is-shown" : "")} 95 src={HS_A("tablet-1920.avif")} alt="" /> 96 {HS_CARDS.map((c) => ( 97 <img key={c.k} src={HS_A(c.k)} alt="" 98 className={"hs-card " + c.cls + (shown.has(sc(c.cls)) ? " is-s
98hown" : "")} /> 99 ))} 100 </div> 101 </div> 102 ); 103} 104 105Object.assign(window, { HeroScene });
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.