/* The Drip Room home page scripts v1 (2026-10-09): hero script + scroll runtime, moved out of the page body. */ (function(){var root=document.querySelector('.dr3');if(!root)return;var stage=root.querySelector('.dr3-stage'),word=root.querySelector('.dr3-wordwrap'),video=root.querySelector('.dr3-video');var reduced=matchMedia('(prefers-reduced-motion: reduce)').matches; if(reduced){if(video){video.removeAttribute('autoplay');video.pause();}return;} var active=true,sync=function(){var on=active&&!document.hidden;if(!video)return;if(on){var p=video.play();if(p&&p.then)p.then(null,function(){});}else{video.pause();}}; if('IntersectionObserver'in window){new IntersectionObserver(function(es){active=es[0].isIntersecting;sync();}).observe(stage);} document.addEventListener('visibilitychange',sync); if(!matchMedia('(pointer: fine)').matches)return; var tx=0,ty=0,cx=0,cy=0,portrait=function(){return stage.clientHeight>stage.clientWidth;}; stage.addEventListener('pointermove',function(e){var r=stage.getBoundingClientRect();tx=(e.clientX-r.left)/r.width-.5;ty=(e.clientY-r.top)/r.height-.5;},{passive:true}); stage.addEventListener('pointerleave',function(){tx=0;ty=0;}); (function loop(){requestAnimationFrame(loop);if(!active||document.hidden||portrait())return;cx+=(tx-cx)*.06;cy+=(ty-cy)*.06; word.style.transform='translate3d('+(cx*16).toFixed(2)+'px,'+(cy*10).toFixed(2)+'px,0)';})();})(); /* Drip Room scroll runtime. Vanilla JS, no dependencies, no DOM generation beyond word masks. Follows the ScrollCraft data-sc-* contract (Nate Herk's skill) so the page could move to the official engine later: data-sc-in (+ data-sc-stagger on the parent) once-only reveal, adds .sc-in data-sc-kinetic="words" masked word reveal, driven by scroll progress, holds once complete data-sc-reveal="up" clip-path wipe as the element enters, holds once complete data-sc-parallax="0.4" translateY = rate * (p - 0.5) * 100px over the element's travel data-sc-act="pan" + .sc-stage + [data-sc-pan] pinned horizontal rail, one scrolled pixel = one panned pixel data-sc-count="0 150" ticks a real number once on entry data-sc-magnet="0.3" pointer magnet, fine pointer only data-sc-spotlight pointer glow via --sc-mx / --sc-my, fine pointer only Reduced motion: the runtime exits before adding sc-ready, so nothing is ever hidden and the page shows its composed state. */ (function () { 'use strict'; var doc = document, root = doc.documentElement; if (matchMedia('(prefers-reduced-motion: reduce)').matches) return; var fine = matchMedia('(pointer: fine)').matches; var vh = innerHeight, vw = innerWidth; var clamp = function (v, a, b) { return v < a ? a : v > b ? b : v; }; var easeOut = function (t) { return 1 - Math.pow(1 - t, 3); }; var q = function (sel, ctx) { return Array.prototype.slice.call((ctx || doc).querySelectorAll(sel)); }; root.classList.add('sc-ready'); /* --- once-only reveals --- */ var inEls = q('[data-sc-in]'); q('[data-sc-stagger]').forEach(function (parent) { var step = parseFloat(parent.getAttribute('data-sc-stagger')) || 60, i = 0; Array.prototype.forEach.call(parent.children, function (child) { if (child.hasAttribute('data-sc-in')) { child.style.transitionDelay = (i * step) + 'ms'; i++; } }); }); var counters = q('[data-sc-count]'); function tick(el) { var parts = (el.getAttribute('data-sc-count') || '0 0').split(/\s+/); var from = parseFloat(parts[0].replace(/,/g, '')) || 0, toRaw = parts[1] || '0'; var to = parseFloat(toRaw.replace(/,/g, '')) || 0, decimals = (toRaw.split('.')[1] || '').length, sep = toRaw.indexOf(',') >= 0; var ms = parseFloat(el.getAttribute('data-sc-count-ms')) || 1400, t0 = null; var fmt = function (n) { var s = n.toFixed(decimals); return sep ? s.replace(/\B(?=(\d{3})+(?!\d))/g, ',') : s; }; var step = function (now) { if (t0 === null) t0 = now; var t = clamp((now - t0) / ms, 0, 1); el.textContent = fmt(from + (to - from) * easeOut(t)); if (t < 1) requestAnimationFrame(step); }; requestAnimationFrame(step); } if ('IntersectionObserver' in window) { var io = new IntersectionObserver(function (entries) { entries.forEach(function (e) { if (!e.isIntersecting) return; var el = e.target; if (el.hasAttribute('data-sc-count')) tick(el); else el.classList.add('sc-in'); io.unobserve(el); }); }, { threshold: 0, rootMargin: '0px 0px -8% 0px' }); inEls.concat(counters).forEach(function (el) { io.observe(el); }); } else { inEls.forEach(function (el) { el.classList.add('sc-in'); }); counters.forEach(tick); } /* --- progress-driven devices --- */ var items = []; function splitWords(el) { var words = []; (function walk(node) { Array.prototype.slice.call(node.childNodes).forEach(function (n) { if (n.nodeType === 3) { var frag = doc.createDocumentFragment(); n.nodeValue.split(/(\s+)/).forEach(function (part) { if (!part) return; if (/^\s+$/.test(part)) { frag.appendChild(doc.createTextNode(part)); return; } var w = doc.createElement('span'); w.className = 'sc-w'; var inner = doc.createElement('span'); inner.className = 'sc-wi'; inner.textContent = part; w.appendChild(inner); frag.appendChild(w); words.push(inner); }); node.replaceChild(frag, n); } else if (n.nodeType === 1 && n.tagName !== 'BR') walk(n); }); })(el); return words; } q('[data-sc-kinetic]').forEach(function (el) { var words = splitWords(el); items.push({ el: el, done: false, update: function (pe) { var n = words.length; words.forEach(function (w, i) { var t = clamp((pe - (i / n) * 0.55) / 0.45, 0, 1); w.style.transform = 'translateY(' + ((1 - easeOut(t)) * 110).toFixed(2) + '%)'; }); return pe >= 1; } }); }); q('[data-sc-reveal]').forEach(function (el) { items.push({ el: el, done: false, update: function (pe) { el.style.clipPath = 'inset(' + ((1 - easeOut(pe)) * 100).toFixed(2) + '% 0 0 0)'; return pe >= 1; } }); }); q('[data-sc-parallax]').forEach(function (el) { var rate = parseFloat(el.getAttribute('data-sc-parallax')) || 0; items.push({ el: el, travel: true, update: function (p) { el.style.transform = 'translateY(' + (rate * (p - 0.5) * 100).toFixed(2) + 'px) scale(1.12)'; return false; } }); }); /* --- pinned pan rail: one scrolled pixel is one panned pixel, desktop only --- */ var pans = []; q('[data-sc-act="pan"]').forEach(function (act) { var box = act.querySelector('.sc-act'), stage = act.querySelector('.sc-stage'), rail = act.querySelector('[data-sc-pan]'); if (box && stage && rail) pans.push({ act: act, box: box, stage: stage, rail: rail, travel: 0, on: false }); }); function stickyTop() { return (parseFloat(getComputedStyle(root).getPropertyValue('--nav-height')) || 88) + 16; } function layoutPans() { pans.forEach(function (pn) { var enable = vw >= 1024 && vh >= 760; if (!enable) { pn.on = false; pn.box.style.height = ''; pn.rail.style.transform = ''; return; } pn.rail.style.transform = ''; var railLeft = pn.rail.getBoundingClientRect().left; pn.travel = Math.max(0, Math.round(pn.rail.scrollWidth + railLeft - vw + Math.min(railLeft, 60))); pn.box.style.height = Math.round(pn.stage.offsetHeight + pn.travel) + 'px'; pn.on = pn.travel > 0; }); } /* --- one frame loop, driven by scroll and resize --- */ var dirty = true, raf = 0; function frame() { raf = 0; items.forEach(function (it) { if (it.done) return; var r = it.el.getBoundingClientRect(); if (it.travel) { it.update(clamp((vh - r.top) / (vh + r.height), 0, 1)); return; } var pe = clamp((vh - r.top) / (vh * 0.55), 0, 1); if (it.update(pe)) it.done = true; }); pans.forEach(function (pn) { if (!pn.on) return; var p = clamp((stickyTop() - pn.box.getBoundingClientRect().top) / pn.travel, 0, 1); pn.rail.style.transform = 'translate3d(' + (-pn.travel * p).toFixed(1) + 'px,0,0)'; }); } function schedule() { if (!raf) raf = requestAnimationFrame(frame); } addEventListener('scroll', schedule, { passive: true }); addEventListener('resize', function () { vh = innerHeight; vw = innerWidth; layoutPans(); schedule(); }); var arm = function () { layoutPans(); schedule(); }; if (doc.fonts && doc.fonts.ready) doc.fonts.ready.then(arm); else arm(); addEventListener('load', arm); arm(); /* --- pointer devices, fine pointer only; touch never fires them --- */ if (!fine) return; q('[data-sc-magnet]').forEach(function (el) { var k = parseFloat(el.getAttribute('data-sc-magnet')) || 0.3, zone = el.parentElement || el; zone.addEventListener('pointermove', function (e) { var r = el.getBoundingClientRect(), cx = r.left + r.width / 2, cy = r.top + r.height / 2; var dx = e.clientX - cx, dy = e.clientY - cy, d = Math.hypot(dx, dy), radius = Math.max(r.width, 120); if (d > radius) { el.style.transform = ''; return; } el.style.transform = 'translate(' + (dx * k).toFixed(1) + 'px,' + (dy * k).toFixed(1) + 'px)'; }); zone.addEventListener('pointerleave', function () { el.style.transform = ''; }); }); q('[data-sc-spotlight]').forEach(function (band) { band.addEventListener('pointermove', function (e) { var r = band.getBoundingClientRect(); band.style.setProperty('--sc-mx', (e.clientX - r.left).toFixed(0) + 'px'); band.style.setProperty('--sc-my', (e.clientY - r.top).toFixed(0) + 'px'); band.classList.add('sc-hot'); }); band.addEventListener('pointerleave', function () { band.classList.remove('sc-hot'); }); }); })();