PageSourceSearch

https://ytadvisors.com/wp-content/themes/ytadvisors-2026/assets/amy-glaze-20260913.js

js ytadvisors.com collected 2026-10-04 17:49:42 UTC 6,312 bytes, 186 lines download raw bytes

1/* Film → 10s final-frame hold → Amy-only fade → campaign diagonal reveal. */
2(() => {
3  const host = document.querySelector('[data-amy-reveal]');
4  if (!host) return;
5  const video = host.querySelector('[data-amy-reveal-video]');
6  const toggle = host.querySelector('[data-amy-reveal-toggle]');
7  const media = host.querySelector('.amy-reveal__media');
8  const root = host.closest('[data-yta-marketing]');
9  const reduce = matchMedia('(prefers-reduced-motion: reduce)');
10  const mobile = matchMedia('(max-width: 600px)');
11  const connection = navigator.connection;
12  const assetBase = new URL('.', document.currentScript.src);
13  const overlay = document.createElement('div');
14  overlay.className = 'yta-glaze';
15  overlay.setAttribute('aria-hidden', 'true');
16  const plate = document.createElement('img');
17  plate.className = 'yta-glaze__plate';
18  plate.alt = '';
19  const panel = document.createElement('div');
20  panel.className = 'yta-glaze__panel';
21  overlay.append(plate, panel);
22  media.append(overlay);
23  const setPlate = () => {
24    plate.src = new URL(mobile.matches ? 'amy-background-mobile-20260913.webp' : 'amy-background-desktop-20260913.webp', assetBase).href;
25  };
26  setPlate();
27
28  let phase = 'idle';
29  let elapsed = 0;
30  let visible = false;
31  let userPaused = false;
32  let manualPlay = false;
33  let pending = false;
34  let generation = 0;
35  let raf = 0;
36  let lastFrame = 0;
37  const quiet = () => reduce.matches || connection?.saveData;
38  const blocked = () => document.hidden || !visible || userPaused ||
39    root.classList.contains('yta-motion-paused') || (quiet() && !manualPlay);
40  function label() {
41    const action = userPaused || (phase === 'idle' && !pending) ? 'Play' : 'Pause';
42    toggle.textContent = action + ' intro';
43    toggle.setAttribute('aria-label', action + ' Amy introduction');
44  }
45  function stop() {
46    generation += 1;
47    pending = false;
48    cancelAnimationFrame(raf);
49    raf = 0;
50    lastFrame = 0;
51    video.pause();
52    label();
53  }
54  function poster() {
55    stop();
56    phase = 'idle';
57    elapsed = 0;
58    plate.style.opacity = '0';
59    panel.style.visibility = 'hidden';
60    host.classList.remove('is-showing-video');
61    label();
62  }
63  function schedule() {
64    if (!raf && !blocked() && ['hold', 'fade', 'wipe'].includes(phase)) {
65      raf = requestAnimationFrame(tick);
66    }
67  }
68  function tick(now) {
69    raf = 0;
70    if (blocked()) { lastFrame = 0; return; }
71    // Background tabs and offscreen/paused time never count toward the hold.
72    elapsed += lastFrame ? Math.min(now - lastFrame, 100) : 0;
73    lastFrame = now;
74    if (phase === 'hold' && elapsed >= 10000) {
75      // Failed plate download must not flash an empty or broken-image frame.
76      if (!plate.complete || !plate.naturalWidth) { userPaused = true; label(); return; }
77      phase = 'fade';
78      elapsed = 0;
79    }
80    if (phase === 'fade') {
81      const progress = Math.min(elapsed / 1000, 1);
82      plate.style.opacity = String(progress * progress * (3 - 2 * progress));
83      if (progress === 1) {
84        lastFrame = 0;
85        if (!pending) play(true);
86        return;
87      }
88    }
89    if (phase === 'wipe') {
90      // Exact social Wipe: 24 frames, -18deg, 70%-wide coral/navy, mint edge.
91      const progress = Math.min(elapsed / 800, 1);
92      const left = progress < .5 ? -40 + progress * 130 : 25 + (progress - .5) * 250;
93      panel.style.left = left + '%';
94      panel.style.backgroundColor = progress < .5 ? '#ff785d' : '#020b24';
95      panel.style.visibility = 'visible';
96      plate.style.opacity = progress < .5 && plate.complete && plate.naturalWidth ? '1' : '0';
97      if (progress === 1) {
98        panel.style.visibility = 'hidden';
99        phase = 'video';
100        elapsed = 0;
101        lastFrame = 0;
102      }
103    }
104    schedule();
105  }
106  async function play(restart = false) {
107    if (blocked() || pending) return;
108    const request = ++generation;
109    pending = true;
110    label();
111    if (!video.getAttribute('src')) {
112      video.src = mobile.matches ? video.dataset.mobileSrc : video.dataset.desktopSrc;
113    }
114    if (restart || video.ended) video.currentTime = 0;
115    try {
116      await video.play();
117      if (request !== generation || blocked()) return;
118      host.classList.add('is-showing-video');
119      if (restart || phase === 'idle') {
120        phase = quiet() ? 'video' : 'wipe';
121        elapsed = 0;
122        lastFrame = 0;
123        plate.style.opacity = quiet() ? '0' : plate.style.opacity;
124      }
125      schedule();
126    } catch {
127      if (request === generation) { userPaused = true; poster(); }
128    } finally {
129      if (request === generation) pending = false;
130      label();
131    }
132  }
133  function update() {
134    if (blocked()) { stop(); return; }
135    if (phase === 'idle') play(true);
136    else if (phase === 'video' || phase === 'wipe') {
137      if (video.paused) play();
138      else schedule();
139    } else schedule();
140  }
141  toggle.hidden = false;
142  label();
143  toggle.addEventListener('click', () => {
144    if (!userPaused && (pending || phase !== 'idle')) {
145      userPaused = true;
146      stop();
147    } else {
148      userPaused = false;
149      manualPlay = true;
150      update();
151    }
152  });
153  video.addEventListener('ended', () => {
154    cancelAnimationFrame(raf);
155    raf = 0;
156    panel.style.visibility = 'hidden';
157    plate.style.opacity = '0';
158    // Keep the real final video frame visible, rather than reverting to its poster.
159    phase = 'hold';
160    elapsed = 0;
161    lastFrame = 0;
162    if (quiet()) { userPaused = true; poster(); }
163    else { label(); schedule(); }
164  });
165  video.addEventListener('error', () => { userPaused = true; poster(); });
166  new IntersectionObserver(entries => {
167    visible = entries[0].isIntersecting && entries[0].intersectionRatio >= .15;
168    update();
169  }, {threshold: .15}).observe(host);
170  document.addEventListener('visibilitychange', update);
171  new MutationObserver(update).observe(root, {attributes: true, attributeFilter: ['class']});
172  function preferenceChange() {
173    manualPlay = false;
174    if (quiet()) poster();
175    else update();
176  }
177  reduce.addEventListener('change', preferenceChange);
178  connection?.addEventListener('change', preferenceChange);
179  mobile.addEventListener('change', () => {
180    poster();
181    video.removeAttribute('src');
182    video.load();
183    setPlate();
184    update();
185  });
186})();

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.