/* ============================================================
Horizons GSAP + Utility Scripts (Optimised 27 Oct 2025)
============================================================ */
// ⭐ Helper: Wait until GSAP is loaded before running any animations
function onGSAPReady(fn, attempts = 0) {
if (window.gsap && window.ScrollTrigger) {
gsap.registerPlugin(ScrollTrigger);
fn();
} else if (attempts < 50) {
setTimeout(() => onGSAPReady(fn, attempts + 1), 50);
}
}
// ============================================================
// 1️⃣ GSAP animations
// ============================================================
onGSAPReady(() => {
console.log("✅ GSAP + ScrollTrigger ready");
// Animate items with [data-animate="in"]
gsap.utils.toArray('[data-animate="in"]').forEach(el => {
gsap.from(el, {
opacity: 0,
y: 30,
duration: 0.6,
scrollTrigger: {
trigger: el,
start: 'top 80%',
},
});
});
// Hero parallax
const h1 = document.querySelector('.hero h1');
if (h1) {
gsap.to(h1, {
yPercent: -6,
ease: 'none',
scrollTrigger: {
trigger: '.hero',
start: 'top top',
end: 'bottom top',
scrub: true,
},
});
}
// Staggered content fade-ins
gsap.utils.toArray(".content-item").forEach((element) => {
gsap.set(element, { opacity: 0 });
});
ScrollTrigger.batch(".content-item", {
onEnter: batch =>
gsap.to(batch, { opacity: 1, y: 0, stagger: { each: 0.6 }, overwrite: true }),
onLeaveBack: batch =>
gsap.set(batch, { opacity: 0, y: 100, overwrite: true }),
});
ScrollTrigger.addEventListener("refreshInit", () =>
gsap.set(".content-item", { y: 0 })
);
// Hero banner fade-in
gsap.fromTo(
".hero-banner-text",
{ y: 30, opacity: 0 },
{ y: 0, opacity: 1, duration: 1.2, delay: 0.3, ease: "power3.out" }
);
// ⭐ Safer Gambling Rotator (now safely inside GSAP ready)
const txt = document.getElementById("saferRotator");
if (txt) {
const messages = [
"Set your limit before you play.",
"Take Time to Think.",
"Need a break? We can help.",
];
let i = 0;
txt.textContent = messages[0];
const reduceMotion =
window.matchMedia &&
window.matchMedia("(prefers-reduced-motion: reduce)").matches;
if (!reduceMotion) {
const tl = gsap.timeline({ repeat: -1, repeatDelay: 4 });
tl.to({}, { duration: 2 })
.to(txt, { duration: 0.25, autoAlpha: 0 })
.add(() => {
i = (i + 1) % messages.length;
txt.textContent = messages[i];
})
.to(txt, { duration: 0.25, autoAlpha: 1 })
.to({}, { duration: 4 });
document.addEventListener("visibilitychange", () => {
if (!document.hidden) tl.play();
});
}
}
// ⭐ Matrix Jackpot background (guarded to avoid null errors)document.addEventListener("DOMContentLoaded", () => {
if (typeof gsap === "undefined") return;
const container = document.querySelector(".matrix-bg");
if (!container) return;
const chipSVG = `
`;
const total = 15;
for (let i = 0; i < total; i++) {
const el = document.createElement("div");
el.innerHTML = chipSVG;
el.classList.add("falling-chip");
el.style.position = "absolute";
el.style.left = Math.random() * 100 + "%";
el.style.top = "-50px";
el.style.width = `${Math.random() * 32 + 24}px`;
el.style.opacity = 0.7 + Math.random() * 0.3;
container.appendChild(el);
animateChip(el);
}
function animateChip(el) {
const duration = gsap.utils.random(8, 14);
// vertical fall + rotation
gsap.fromTo(
el,
{ y: -100, rotation: gsap.utils.random(-180, 180) },
{
y: "110vh",
rotation: "+=" + gsap.utils.random(-720, 720),
duration,
repeat: -1,
delay: gsap.utils.random(0, 8),
ease: "none",
}
);
// ⭐ parallax sway (horizontal drift)
gsap.to(el, {
x: `+=${gsap.utils.random(40, 100)}`,
duration: gsap.utils.random(3, 6),
ease: "sine.inOut",
yoyo: true,
repeat: -1,
delay: gsap.utils.random(0, 4),
});
// ⭐ Shimmer pulse
gsap.to(el.querySelector("svg circle:last-of-type"), {
duration: 2,
repeat: -1,
opacity: 0.6,
yoyo: true,
ease: "sine.inOut",
delay: gsap.utils.random(0, 3),
});
}
});
// ============================================================
// 2️⃣ Concierge & General Utility JS (non-GSAP)
// ============================================================
(function () {
const toggle = document.querySelector(".concierge-toggle");
const panel = document.getElementById("conciergePanel");
const closeBtn = document.getElementById("conciergeClose");
const form = document.getElementById("conciergeForm");
const input = document.getElementById("conciergeInput");
const log = document.getElementById("conciergeLog");
function appendBubble(text, role) {
const div = document.createElement("div");
div.className = "mb-2";
div.innerHTML =
role === "user"
? "You: " + text
: "Host: " + text;
log.appendChild(div);
log.scrollTop = log.scrollHeight;
}
function answer(q) {
q = (q || "").toLowerCase();
if (q.match(/hour|open|time|close|closing|tables|slots/))
return "Slots & Electronic Roulette from 10:00. Tables 11:00–06:00.";
if (q.match(/id|photo id|identit/))
return "If you look under 25 or for higher limits—bring photo ID.";
if (q.match(/dress|code|wear|clothing/))
return "Smart-casual. Sportswear, football shirts, and soiled clothing aren’t permitted; management discretion applies.";
if (q.match(/offer|deal|what.*on|tonight|event/))
return "Check “What’s On & Offers” above—new items appear regularly. Need specifics? Call us on +44 (0) 20 7494 3126.";
if (q.match(/roulette|blackjack|baccarat|poker|slot|minimum|min/))
return "Typical minimums: Roulette from £2, Blackjack £10, Baccarat £20, 3-Card Poker £5, Slots £0.01–£5 (may vary).";
if (q.match(/direction|where|map|address|tube|station/))
return "We’re at Queens House, 1 Leicester Place, WC2H 7BP. Tube: Leicester Square (2 min), Piccadilly Circus (6 min).";
if (q.match(/book|reserve|table|area|party|celebrat/))
return "We can reserve areas for groups. Share your date, time, and party size—or call us and we’ll sort it.";
return "I can help with hours, dress, offers, table minimums, directions, or reservations. Prefer a human? Call +44 (0) 20 7494 3126.";
}
if (toggle && panel) {
toggle.addEventListener("click", () => panel.classList.toggle("show"));
}
if (closeBtn) {
closeBtn.addEventListener("click", () => panel.classList.remove("show"));
}
if (form) {
form.addEventListener("submit", function (e) {
e.preventDefault();
const q = input.value.trim();
if (!q) return;
appendBubble(q, "user");
setTimeout(() => appendBubble(answer(q), "bot"), 200);
input.value = "";
});
}
})();
// ============================================================
// 3️⃣ Header behaviour
// ============================================================
(function () {
const header = document.getElementById("siteHeader");
if (!header) return;
// Prevent content from hiding under fixed header
function setBodyPad() {
document.body.classList.add("has-fixed-header");
document.documentElement.style.setProperty(
"--header-h",
header.offsetHeight + "px"
);
}
setBodyPad();
window.addEventListener("resize", setBodyPad);
// Toggle scrolled state
const activate = () => header.classList.add("header-scrolled");
const deactivate = () => header.classList.remove("header-scrolled");
onGSAPReady(() => {
ScrollTrigger.create({
trigger: document.querySelector(".hero") || document.body,
start: "bottom top+=20",
onEnter: activate,
onLeaveBack: deactivate,
});
});
})();