// ============================================================
// WhsSolutionsMobile — MOBILE port of the desktop "Solutions for this
// sector" section (WhsSolutions) from whs2-page-2.jsx.
//
// Desktop = interactive distribution-warehouse schematic (left) + 5-row hover
// list (right), on a Soft-Green surface with Paper-White text.
// Mobile keeps the SAME story, palette, type & iconography but swaps hover for
// touch, following the approved Manufacturing mobile pattern:
// • Intro (eyebrow + headline + supporting copy), stacked
// • Interactive diagram, always visible, the visual focus
// • Solution cards as a horizontal swipe carousel below the diagram
// • Tapping a card / diagram marker -> updates the active diagram zone
// • A dedicated "Explore …" link inside each card handles navigation,
// so a first tap activates and the link tap navigates (no touch conflict)
// • Default active zone: 01 — Energy Audits (per approved screenshot)
// ============================================================
const WHS_AMBER = 'rgba(233,199,134,1)'; // --gi-amber-soft
const WHS_AMBER_FILL = 'rgba(233,199,134,0.18)';
const WHS_SOFT_GREEN = '#375844';
// ============================================================
// WhsFacilitySchematicMobile — identical SVG schematic to desktop, with the
// numbered markers made tappable (large invisible hit targets).
// 5 zones · 01 Audit · 02 High-Bay LED · 03 Rooftop PV · 04 Battery · 05 EV/Forklift
// ============================================================
const WhsFacilitySchematicMobile = ({ activeIndex, onSelectZone }) => {
const W = 540;
const H = 360;
const amber = WHS_AMBER;
const amberFill= WHS_AMBER_FILL;
const ink = 'rgba(255,255,255,0.55)';
const inkDim = 'rgba(255,255,255,0.32)';
const inkFaint = 'rgba(255,255,255,0.18)';
const inkText = 'rgba(255,255,255,0.62)';
// Building footprint (flat-roof warehouse) — x 70..470, y 100..282
const zones = [
// 01 Energy Audits — whole facility ring
{ id: 0, label: '01', cx: 270, cy: 200,
hl: },
// 02 High-Bay LED — interior ceiling row
{ id: 1, label: '02', cx: 270, cy: 132,
hl: },
// 03 Solar PV — flat rooftop
{ id: 2, label: '03', cx: 270, cy: 92,
hl: },
// 04 Battery — BESS pad
{ id: 3, label: '04', cx: 502, cy: 244,
hl: },
// 05 EV / Forklift Charging — front apron
{ id: 4, label: '05', cx: 250, cy: 308,
hl: },
];
return (
);
};
// ============================================================
// WhsSolutionsMobile — full mobile section
// ============================================================
const WhsSolutionsMobile = () => {
const rows = [
{ idx: '01', icon: 'clipboard-list', name: 'Energy Audits', linkLabel: 'Energy Audits', zone: 'Whole facility', tag: 'Establish the documented baseline.', why: 'A commercial energy audit produces the facility-specific baseline that sizes every subsequent measure — load profile, demand-charge exposure, refrigeration efficiency, and the strongest first-dollar opportunities.', href: 'Solutions - Energy Audits.html' },
{ idx: '02', icon: 'lightbulb', name: 'High-Bay LED Lighting', linkLabel: 'LED Lighting', zone: 'Interior · 24/7', tag: 'The fastest payback in the building.', why: 'High-bay LED retrofits in 24/7 warehouses typically pay back in two to four years through reduced electricity, longer maintenance intervals, and reduced HVAC heat-rejection load on cooled spaces.', href: 'Solutions - LED Lighting.html' },
{ idx: '03', icon: 'sun', name: 'Commercial Solar', linkLabel: 'Commercial Solar', zone: 'Rooftop', tag: 'Large roofs, large arrays.', why: 'Modern distribution facilities have the largest unobstructed flat roofs in any commercial sector — well-suited to ballasted PV arrays that offset daytime base load and reduce exposure to rising utility rates.', href: 'Solutions - Commercial Solar.html' },
{ idx: '04', icon: 'battery-charging', name: 'Battery Storage', linkLabel: 'Battery Storage', zone: 'Mechanical pad', tag: 'Cut peak demand charges.', why: 'Battery storage discharges during short peak events to hold metered demand below the threshold that sets the monthly bill — operating cost reductions independent of energy savings, plus resilience for cold-chain and automation systems.', href: 'Solutions - Battery Storage.html' },
{ idx: '05', icon: 'plug-zap', name: 'EV / Forklift Charging', linkLabel: 'EV Charging', zone: 'Yard · dock apron', tag: 'Fleet electrification on a managed load.', why: 'Truck-fleet, last-mile, and forklift charging — planned around available service capacity, smart load management, and fleet replacement schedules rather than emergency service upgrades under deadline pressure.', href: 'Solutions - EV Charging.html' },
];
// Default active zone: 01 — Energy Audits (index 0), per approved screenshot.
const DEFAULT_INDEX = 0;
const [active, setActive] = React.useState(DEFAULT_INDEX);
const scrollerRef = React.useRef(null);
const cardRefs = React.useRef([]);
const programmatic = React.useRef(false);
const rafId = React.useRef(0);
const settleTimer = React.useRef(0);
const didInit = React.useRef(false);
// (Re)draw lucide icons whenever the active card changes.
React.useEffect(() => {
const t = setTimeout(() => window.lucide && window.lucide.createIcons({ attrs: { 'stroke-width': 1.4 } }), 20);
return () => clearTimeout(t);
}, [active]);
const scrollToCard = (i, instant) => {
const sc = scrollerRef.current;
const card = cardRefs.current[i];
if (!sc || !card) return;
programmatic.current = true;
window.clearTimeout(settleTimer.current);
const target = card.offsetLeft - (sc.clientWidth - card.clientWidth) / 2;
const max = sc.scrollWidth - sc.clientWidth;
const end = Math.max(0, Math.min(max, target));
const start = sc.scrollLeft;
const dist = end - start;
window.cancelAnimationFrame(scrollToCard._raf);
if (instant) {
sc.scrollLeft = end;
window.requestAnimationFrame(() => {
sc.scrollLeft = end;
programmatic.current = false;
});
return;
}
if (Math.abs(dist) < 1) {
programmatic.current = false;
return;
}
const dur = 420;
const t0 = (window.performance || Date).now();
const ease = (t) => 1 - Math.pow(1 - t, 3); // easeOutCubic
const step = (now) => {
const p = Math.min(1, (now - t0) / dur);
sc.scrollLeft = start + dist * ease(p);
if (p < 1) {
scrollToCard._raf = window.requestAnimationFrame(step);
} else {
programmatic.current = false;
}
};
scrollToCard._raf = window.requestAnimationFrame(step);
};
// Center the default card on first paint. The flex children may not have
// their final layout on the first frame (offsetLeft still 0), so retry
// across a few frames until a card reports a non-zero offset, then jump.
React.useEffect(() => {
if (didInit.current) return;
let tries = 0;
const tryCenter = () => {
const sc = scrollerRef.current;
const card = cardRefs.current[DEFAULT_INDEX];
if (sc && card && (DEFAULT_INDEX === 0 || card.offsetLeft > 0)) {
didInit.current = true;
scrollToCard(DEFAULT_INDEX, true);
return;
}
if (tries++ < 40) requestAnimationFrame(tryCenter);
};
requestAnimationFrame(tryCenter);
const onLoad = () => { didInit.current = false; tries = 0; requestAnimationFrame(tryCenter); };
window.addEventListener('load', onLoad);
return () => window.removeEventListener('load', onLoad);
}, []);
// Tap a card OR a diagram marker -> select that zone + center its card.
const selectZone = (i) => {
setActive(i);
scrollToCard(i);
};
const nearestCard = () => {
const sc = scrollerRef.current;
if (!sc) return 0;
const center = sc.scrollLeft + sc.clientWidth / 2;
let best = 0, bestDist = Infinity;
cardRefs.current.forEach((c, i) => {
if (!c) return;
const cc = c.offsetLeft + c.clientWidth / 2;
const d = Math.abs(cc - center);
if (d < bestDist) { bestDist = d; best = i; }
});
return best;
};
// Swipe -> live-update the active zone to the nearest card, then JS-snap to
// center it once the user stops scrolling.
const handleScroll = () => {
if (programmatic.current) return;
window.clearTimeout(settleTimer.current);
settleTimer.current = window.setTimeout(() => {
const i = nearestCard();
scrollToCard(i);
}, 110);
if (rafId.current) return;
rafId.current = requestAnimationFrame(() => {
rafId.current = 0;
const best = nearestCard();
setActive((prev) => (prev === best ? prev : best));
});
};
return (
{/* Top amber tick — mirrors desktop */}
{/* ===== 1 · INTRO ===== */}
Solutions for this sector
What warehousing & logistics projects typically involve.
A complete warehousing energy programme begins with a documented
audit and proceeds through high-bay LED, rooftop solar, battery
storage, and EV/forklift charging — sequenced for payback velocity
and operational continuity over a multi-year capital plan.