// Shared atoms and small components — exported to window so other Babel scripts can use them.
const { useState, useEffect, useRef, useMemo, useCallback } = React;
// SVG logo mark — a stylized "N" / converging-line mark that echoes the Designsmith N glyph.
// Originally drawn — not lifted from the source logo.
function LogoMark({ size = 24, color = "currentColor" }) {
return (
);
}
function Wordmark({ onClick }) {
return (
DESIGNSMITH.
);
}
function Eyebrow({ children, light = false }) {
return (
{children}
);
}
function SectionHeader({ eyebrow, title, action, light = false }) {
return (
{eyebrow ? {eyebrow} : null}
{title}
{action ?
{action}
: null}
);
}
function Button({ children, onClick, variant = "primary", arrow = true, href }) {
const className = "btn " + (variant === "ghost" ? "btn-ghost" : "");
const inner = (
<>
{children}
{arrow ? → : null}
>
);
if (href) return {inner} ;
return {inner} ;
}
function BtnLink({ children, onClick, arrow = true }) {
return (
{children}
{arrow ? → : null}
);
}
// Stats strip — uses the reconciled set.
function StatsStrip({ inverted = false }) {
const { brand } = window.DS_DATA;
const items = [
{ num: brand.years, label: "Years building", sup: "" },
{ num: brand.projects, label: "Projects delivered" },
{ num: brand.factorySqft, label: "sq ft Al Quoz factory" },
{ num: brand.headcount, label: "In-house team" },
{ num: brand.licence, label: "Licensed contractor" },
];
return (
{items.map((s, i) => (
))}
);
}
// A small "ledger" sub-component (used in hero) — minimal mono-typed numeric strip
function LedgerStat({ num, label }) {
return (
{num}
{label}
);
}
// Sector tile
function SectorTile({ sector, onClick }) {
return (
{ if (e.key === "Enter") onClick(); }}>
{String(sector.count).padStart(2, "0")} projects
/{sector.id.slice(0, 3).toUpperCase()}
{sector.label}
View sector
→
);
}
// Project card
function ProjectCard({ project, onClick }) {
return (
{ if (e.key === "Enter") onClick(); }}>
{project.sectorLabel}
{project.year}
{project.name}
{project.subtitle} · {project.scope}
);
}
// Lightbox
function Lightbox({ images, startIndex = 0, onClose }) {
const [idx, setIdx] = useState(startIndex);
useEffect(() => {
const onKey = (e) => {
if (e.key === "Escape") onClose();
if (e.key === "ArrowRight") setIdx((i) => (i + 1) % images.length);
if (e.key === "ArrowLeft") setIdx((i) => (i - 1 + images.length) % images.length);
};
window.addEventListener("keydown", onKey);
return () => window.removeEventListener("keydown", onKey);
}, [images.length, onClose]);
return (
{String(idx + 1).padStart(2, "0")} / {String(images.length).padStart(2, "0")}
setIdx((i) => (i - 1 + images.length) % images.length)}>← Prev
setIdx((i) => (i + 1) % images.length)}>Next →
✕ Close
);
}
// Reveal — fades-in on mount; cheap and used everywhere
function Reveal({ children, delay = 0 }) {
const ref = useRef(null);
useEffect(() => {
const el = ref.current; if (!el) return;
el.style.opacity = "0";
el.style.transform = "translateY(10px)";
el.style.transition = `opacity 0.6s ease ${delay}ms, transform 0.6s cubic-bezier(0.22, 0.61, 0.36, 1) ${delay}ms`;
const io = new IntersectionObserver(([entry]) => {
if (entry.isIntersecting) {
el.style.opacity = "1";
el.style.transform = "none";
io.disconnect();
}
}, { threshold: 0.12 });
io.observe(el);
return () => io.disconnect();
}, [delay]);
return {children}
;
}
Object.assign(window, { LogoMark, Wordmark, Eyebrow, SectionHeader, Button, BtnLink, StatsStrip, LedgerStat, SectorTile, ProjectCard, Lightbox, Reveal });