/* 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 (
PORT & TERMINAL Services Spain
/
{t.cta.quote}
); } 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 ( {/* Stylized Iberia outline (illustrative, not geographically exact) */} {/* Portugal cutout */} {/* Balearic */} {/* Canary box */} ISLAS CANARIAS BALEARES {pins.map(p => ( {p.major && } {p.name} ))} ); } // ====== EXPORT ====== Object.assign(window, { BrandMark, Header, Footer, Figure, SpainMap, useReveal, useLang });