/* global React, ReactDOM */
const { useState, useEffect, useRef } = React;
// ====== SHARED COMPONENTS ======
function BrandMark({ size = 38, color, copperColor }) {
const navy = color || "var(--navy-700)";
const copper = copperColor || "var(--accent)";
return (
);
}
function Header({ active = "home", t, lang, setLang, transparent = false }) {
const [scrolled, setScrolled] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 30);
onScroll();
window.addEventListener("scroll", onScroll);
return () => window.removeEventListener("scroll", onScroll);
}, []);
return (
);
}
function Footer({ t }) {
return (
);
}
// ====== HOOKS ======
function useReveal() {
useEffect(() => {
const els = document.querySelectorAll(".reveal");
const io = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add("in"); io.unobserve(e.target); } });
}, { threshold: 0.12 });
els.forEach(el => io.observe(el));
// Stats section — trigger count-up animation when visible
const stats = document.querySelectorAll(".stats");
const statsIo = new IntersectionObserver((entries) => {
entries.forEach(e => { if (e.isIntersecting) { e.target.classList.add("in-view"); statsIo.unobserve(e.target); } });
}, { threshold: 0.3 });
stats.forEach(el => statsIo.observe(el));
// Subtle parallax for hero media
const heroMedia = document.querySelector(".hero-media");
let ticking = false;
const onScroll = () => {
if (!ticking) {
requestAnimationFrame(() => {
const y = window.scrollY;
if (heroMedia && y < window.innerHeight) {
heroMedia.style.transform = `translateY(${y * 0.25}px)`;
}
ticking = false;
});
ticking = true;
}
};
window.addEventListener("scroll", onScroll, { passive: true });
return () => {
io.disconnect();
statsIo.disconnect();
window.removeEventListener("scroll", onScroll);
};
}, []);
}
function useLang() {
const [lang, setLangState] = useState(window.PT_LANG);
useEffect(() => {
const onChange = (e) => setLangState(e.detail);
window.addEventListener("pt-lang-change", onChange);
return () => window.removeEventListener("pt-lang-change", onChange);
}, []);
const setLang = (l) => window.setPTLang(l);
return [lang, setLang, window.PT_CONTENT[lang]];
}
// ====== EDITORIAL FIGURE PLACEHOLDER ======
function Figure({ label, caption, className = "", tone = "navy" }) {
return (
{label}
{caption &&
{caption}
}
);
}
// ====== SPAIN MAP ======
function SpainMap({ ports }) {
// Approximate normalized coordinates for Spanish ports on a stylized map
const pins = [
{ code: "BCN", name: "Barcelona", x: 760, y: 220, major: true },
{ code: "VLC", name: "Valencia", x: 660, y: 320, major: true },
{ code: "ALG", name: "Algeciras", x: 320, y: 510, major: true },
{ code: "BIO", name: "Bilbao", x: 480, y: 130, major: true },
{ code: "VGO", name: "Vigo", x: 110, y: 160 },
{ code: "TRG", name: "Tarragona", x: 720, y: 250 },
{ code: "CTG", name: "Cartagena", x: 580, y: 430 },
{ code: "MLG", name: "Málaga", x: 380, y: 490 },
{ code: "CAD", name: "Cádiz", x: 250, y: 510 },
{ code: "HUV", name: "Huelva", x: 220, y: 480 },
{ code: "FER", name: "Ferrol", x: 130, y: 100 },
{ code: "GIJ", name: "Gijón", x: 320, y: 110 },
{ code: "CST", name: "Castellón", x: 690, y: 280 },
{ code: "LPA", name: "Las Palmas", x: 240, y: 690, major: true },
{ code: "TFE", name: "S.C. Tenerife", x: 180, y: 700 },
];
return (
);
}
// ====== EXPORT ======
Object.assign(window, {
BrandMark, Header, Footer, Figure, SpainMap, useReveal, useLang
});