/* components.jsx — Shared chrome (dark, with language toggle) */
const { useState, useEffect, useRef } = React;
/* ─────────────────────── Icons ───────────────────── */
function DcIcon({ name, size = 18, strokeWidth = 1.5, style }) {
const paths = {
search: <>>,
user: <>>,
menu: <>>,
x: <>>,
arrow: ,
arrowLeft: ,
arrowRight: ,
chevronRight: ,
chevronDown: ,
chevronLeft: ,
play: ,
server: <>>,
cpu: <>>,
layers: <>>,
film: <>>,
sparkles: <>>,
box: <>>,
map: <>>,
mail: <>>,
phone: ,
calendar: <>>,
clock: <>>,
check: ,
globe: <>>,
eye: <>>,
zap: ,
pause: <>>,
};
return (
);
}
/* ─────────────────────── Buttons ───────────────────── */
function DcBtn({ children, variant = 'primary', onClick, type = 'button', disabled, style }) {
const cls = {
primary: 'dc-btn dc-btn-primary',
secondary: 'dc-btn dc-btn-secondary',
onDark: 'dc-btn dc-btn-on-dark',
}[variant];
return (
);
}
function DcTextLink({ children, onClick, style }) {
return (
);
}
/* ─────────────────────── Photo (real image) ───────────────────── */
function DcPhoto({ src, caption, tag, tagAccent, aspectRatio = '16/10', style, children }) {
return (
{tag &&
{tag}}
{caption &&
{caption}}
{children}
);
}
/* ─────────────────────── NAV ───────────────────── */
const NAV_ITEMS = [
{ id: 'home', key: 'home' },
{ id: 'portfolio', key: 'portfolio' },
{ id: 'services', key: 'services' },
{ id: 'about', key: 'about' },
{ id: 'contact', key: 'contact' },
];
function DcNav({ currentPage, onNavigate }) {
const [scrolled, setScrolled] = useState(false);
const { t, lang, setLang } = useLang();
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 80);
window.addEventListener('scroll', onScroll, { passive: true });
onScroll();
return () => window.removeEventListener('scroll', onScroll);
}, []);
// when on home and not scrolled, nav is transparent over hero
const heroMode = currentPage === 'home' && !scrolled;
return (
);
}
/* ─────────────────────── FOOTER ───────────────────── */
function DcFooter({ onNavigate }) {
const { t } = useLang();
const cols = [
{ h: t.footer.columns.capabilities, links: [['Cinematic Visualization', 'services'], ['Real-Time Walkthroughs', 'services'], ['AI Workflows', 'services'], ['Pre-Visualization', 'services']] },
{ h: t.footer.columns.portfolio, links: [['QTS Hyperscale', 'portfolio'], ['Pampa Energía', 'portfolio'], ['All projects', 'portfolio']] },
{ h: t.footer.columns.studio, links: [[t.nav.about, 'about'], ['Process', 'about'], ['Team', 'about']] },
{ h: t.footer.columns.contact, links: [[t.nav.cta, 'contact'], ['contact@jmcrealities.com', 'contact'], ['+54 (911) 2646 4578', 'contact']] },
];
return (
);
}
/* ─────────────────────── Spec Row ───────────────────── */
function DcSpecRow({ specs }) {
return (
{specs.map((s, i) => (
))}
);
}
/* ─────────────────────── CTA Band ───────────────────── */
function DcCTABand({ eyebrow, title, body, primaryLabel, secondaryLabel, onPrimary, onSecondary, image }) {
return (
{image && (
<>
>
)}
{eyebrow &&
{eyebrow}
}
{title}
{body &&
{body}
}
{primaryLabel && (
)}
{secondaryLabel && (
)}
);
}
/* ─────────────────────── Section Header ───────────────────── */
function DcSectionHeader({ eyebrow, title, rightSlot, maxWidth }) {
return (
{eyebrow &&
{eyebrow}
}
{title}
{rightSlot}
);
}
/* ─────────────────────── Marquee ───────────────────── */
function DcMarquee({ items }) {
// Duplicate items so we can loop without seams
const doubled = [...items, ...items];
return (
{doubled.map((item, i) => (
{item}
))}
);
}
/* ─────────────────────── Reveal on scroll ───────────────────── */
function DcReveal({ children, delay = 0 }) {
const ref = useRef(null);
const [visible, setVisible] = useState(false);
useEffect(() => {
const io = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
setTimeout(() => setVisible(true), delay);
io.disconnect();
}
}, { threshold: 0.12 });
if (ref.current) io.observe(ref.current);
return () => io.disconnect();
}, [delay]);
return (
{children}
);
}
/* expose */
Object.assign(window, {
DcIcon, DcBtn, DcTextLink, DcPhoto, DcNav, DcFooter,
DcSpecRow, DcCTABand, DcSectionHeader, DcMarquee, DcReveal, NAV_ITEMS,
});