// Tatava Studio, Hero variants + cinematic video backdrop
// Loads after site-data.js. Exports to window at the bottom.
const { useRef, useEffect, useState } = React;
const TD = window.TATAVA_DATA;
/* ------------------------------------------------------------
FadeLoopVideo, seamless manual loop with fade in/out.
rAF monitors currentTime/duration; fades opacity over 0.5s
at both ends; on `ended` waits 100ms, resets, replays.
------------------------------------------------------------ */
function FadeLoopVideo({ src, fallback, className, style }) {
const videoRef = useRef(null);
const vsrc = useDeferredVideoSrc(src);
useEffect(() => {
if (!vsrc) return undefined;
const video = videoRef.current;
if (!video) return;
let raf;
const FADE = 0.5;
const tick = () => {
const d = video.duration;
if (d && !isNaN(d)) {
const t = video.currentTime;
let opacity = 1;
if (t < FADE) opacity = t / FADE;else
if (t > d - FADE) opacity = Math.max(0, (d - t) / FADE);
video.style.opacity = opacity.toFixed(3);
}
raf = requestAnimationFrame(tick);
};
const onEnded = () => {
video.style.opacity = "0";
setTimeout(() => {
video.currentTime = 0;
const p = video.play();
if (p && p.catch) p.catch(() => {});
}, 100);
};
video.addEventListener("ended", onEnded);
const p = video.play();
if (p && p.catch) p.catch(() => {});
raf = requestAnimationFrame(tick);
return () => {
cancelAnimationFrame(raf);
video.removeEventListener("ended", onEnded);
};
}, [vsrc]);
return (
{fallback ?

: null}
{vsrc ?
: null}
);
}
/* defer video fetch until the page has finished loading,
keeps first paint fast and the load event unblocked */
function useDeferredVideoSrc(src) {
const [vsrc, setVsrc] = React.useState(
document.readyState === "complete" ? src : null
);
React.useEffect(() => {
if (vsrc) return undefined;
const arm = () => setVsrc(src);
if (document.readyState === "complete") { arm(); return undefined; }
window.addEventListener("load", arm);
return () => window.removeEventListener("load", arm);
}, [src]);
return vsrc;
}
/* ------------------------------------------------------------
Headline, one orange italic word per brand pattern.
twoLine=true breaks on h.line1 / h.pre2 for cinematic hero.
------------------------------------------------------------ */
function Headline({ variant, className, copy, twoLine, anchor }) {
const h = copy || TD.headlines[variant] || TD.headlines.racing;
const props = { className: "headline " + (className || "") };
if (anchor) props["data-comment-anchor"] = anchor;
if (twoLine && h.line1) {
return (
{h.line1}
{h.pre2}{h.hl}{h.post}
);
}
return (
{h.pre}{h.hl}{h.post}
);
}
function HeroCtas({ delayClass }) {
return (
);
}
function HeroBadges({ delayClass }) {
return (
{TD.trustBadges.map((b) =>
{b}
)}
);
}
/* ------------------------------------------------------------
Variant A, Cinematic: full-bleed video low in the frame,
cream scrims top + bottom, centered serif headline.
------------------------------------------------------------ */
function HeroCinematic({ headline, showVideo }) {
return (
{showVideo ?
:

}
{TD.eyebrow}
{TD.subhead}
Consultation By Appointment Only
);
}
/* ------------------------------------------------------------
Variant B, Editorial split: brand's two-column rhythm,
video living inside the tilted embossed-poster frame.
------------------------------------------------------------ */
function HeroSplit({ headline, showVideo }) {
return (
{TD.eyebrow}
{TD.subhead}
Consultation By Appointment Only
{showVideo ?
:

}
Accepting new clients
);
}
/* ------------------------------------------------------------
Variant C, Poster band: centered copy on cream, then the
video as a wide embossed-poster band beneath.
------------------------------------------------------------ */
function HeroPosterBand({ headline, showVideo }) {
return (
{TD.eyebrow}
{TD.subhead}
Consultation By Appointment Only
{showVideo ?
:

}
In person in Delhi ยท Online anywhere
);
}
Object.assign(window, { FadeLoopVideo, Headline, HeroCtas, HeroBadges, HeroCinematic, HeroSplit, HeroPosterBand, useDeferredVideoSrc });