/* Kit-local helpers for the NOTHUMAN site. Loaded before the section files. */
const VS = window.VamshotDesignSystem_18ef2a;
const vsReduced = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
const vsTouch = window.matchMedia('(hover:none),(pointer:coarse)').matches;
const vsMQ = { mobile: '(max-width:767px)', handheld: '(max-width:900px)', tablet: '(max-width:1024px)' };
/* One rAF loop for every scroll-driven effect on the page. All reads happen in
the same frame, so nothing interleaves read/write and thrashes layout. */
const vsScroll = (function () {
const subs = new Set();
let raf = 0;
const flush = function () {
raf = 0;
const y = window.scrollY, h = window.innerHeight;
subs.forEach(function (fn) { fn(y, h); });
};
const schedule = function () { if (!raf) raf = requestAnimationFrame(flush); };
window.addEventListener('scroll', schedule, { passive: true });
window.addEventListener('resize', schedule);
return {
add: function (fn) { subs.add(fn); schedule(); return function () { subs.delete(fn); }; },
ping: schedule
};
})();
const vsClamp = function (v, a, b) { a = a === undefined ? 0 : a; b = b === undefined ? 1 : b; return Math.min(b, Math.max(a, v)); };
const vsRange = function (p, a, b) { return vsClamp((p - a) / (b - a)); };
function useReveal() {
const ref = React.useRef(null);
React.useEffect(function () {
const el = ref.current; if (!el) return undefined;
const io = new IntersectionObserver(function (entries) {
entries.forEach(function (e) { if (e.isIntersecting) { e.target.classList.add('is-in'); io.unobserve(e.target); } });
}, { threshold: 0.12, rootMargin: '0px 0px -6% 0px' });
el.querySelectorAll('.vs-r,.vs-line,.vs-mask').forEach(function (n) { io.observe(n); });
if (el.classList.contains('vs-r')) io.observe(el);
return function () { io.disconnect(); };
}, []);
return ref;
}
function Reveal({ children, delay, style, className, ...rest }) {
return
{children}
;
}
function Line({ delay, accent, style }) {
return ;
}
/* 0 -> 1 across the scrollable travel of a tall section. Rides the shared
rAF bus; pass enabled=false (mobile, reduced motion) to opt out entirely. */
function useProgress(ref, enabled) {
const live = enabled !== false;
const [p, setP] = React.useState(0);
React.useEffect(function () {
const el = ref.current;
if (!el || !live) return undefined;
let last = -1;
return vsScroll.add(function (y, h) {
const r = el.getBoundingClientRect();
const total = r.height - h;
const next = total > 0 ? vsClamp(-r.top / total) : 0;
if (Math.abs(next - last) < 0.0015) return;
last = next;
setP(next);
});
}, [ref, live]);
return live ? p : 0;
}
function useMedia(query) {
const [match, setMatch] = React.useState(function () { return window.matchMedia(query).matches; });
React.useEffect(function () {
const mq = window.matchMedia(query);
const on = function () { setMatch(mq.matches); };
mq.addEventListener('change', on);
return function () { mq.removeEventListener('change', on); };
}, [query]);
return match;
}
/* Image treatments — cool, desaturated, high contrast, grain-forward. */
const vsTones = {
cool: 'grayscale(0.45) contrast(1.14) brightness(0.9) saturate(0.8)',
mono: 'grayscale(1) contrast(1.16) brightness(0.86)',
deep: 'grayscale(0.9) contrast(1.3) brightness(0.62)'
};
function Media({ src, alt, ratio, tone, label, position, fill, style, imgStyle, mask, cursor }) {
const [hover, setHover] = React.useState(false);
return (
{label ? (
{label}
) : null}
);
}
function Marks({ children, style }) {
return {children};
}
/* One-word-sentence stack — the brand's list form. */
function SentenceStack({ items, color, size, style }) {
return (
{items.map(function (t, i) {
const accent = typeof t !== 'string';
return {accent ? t.text : t};
})}
);
}
function Shell({ id, children, ruled, style, innerStyle }) {
const ref = useReveal();
return (
);
}
/* ₹ counters, Indian grouping, counted up once in view. */
function vsIndian(n) {
const s = String(n);
if (s.length <= 3) return s;
const last = s.slice(-3);
let rest = s.slice(0, -3), out = '';
while (rest.length > 2) { out = ',' + rest.slice(-2) + out; rest = rest.slice(0, -2); }
return (rest ? rest + out : out.slice(1)) + ',' + last;
}
function CountUp({ value, prefix, accent, style }) {
const ref = React.useRef(null);
const [n, setN] = React.useState(vsReduced ? value : 0);
React.useEffect(function () {
if (vsReduced) return undefined;
const el = ref.current; if (!el) return undefined;
let raf = 0;
const io = new IntersectionObserver(function (entries) {
if (!entries[0].isIntersecting) return;
io.disconnect();
const start = performance.now(), dur = 1600;
const tick = function (t) {
const k = vsClamp((t - start) / dur);
setN(Math.round(value * (1 - Math.pow(1 - k, 3))));
if (k < 1) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
}, { threshold: 0.6 });
io.observe(el);
return function () { io.disconnect(); cancelAnimationFrame(raf); };
}, [value]);
return (
{(prefix || '') + vsIndian(n)}
);
}
Object.assign(window, { VS, vsReduced, vsTouch, vsMQ, vsScroll, vsClamp, vsRange, useReveal, Reveal, Line, useProgress, useMedia, Media, Marks, SentenceStack, Shell, CountUp, vsTones });