323 lines
10 KiB
TypeScript
323 lines
10 KiB
TypeScript
"use client";
|
|
|
|
import React, { useState } from "react";
|
|
import IndustryWorldMap from "./IndustryWorldMap";
|
|
|
|
type Tab = "ch" | "so";
|
|
|
|
interface Section {
|
|
id: number;
|
|
title: string;
|
|
image: string;
|
|
alt: string;
|
|
desc: string;
|
|
ch: string[];
|
|
so: string[];
|
|
}
|
|
|
|
const SECTIONS: Section[] = [
|
|
{
|
|
id: 1,
|
|
title: "FMCG",
|
|
image: "/images/tab-pic-1-solution.webp",
|
|
alt: "FMCG logistics",
|
|
desc:
|
|
"FMCG logistics demands speed, precision, and continuous fulfillment across high-volume delivery networks. Businesses must balance tight delivery timelines, inventory movement, and operational efficiency without compromising product availability.",
|
|
ch: [
|
|
"Unpredictable demand spikes create delivery pressure and reduce operational efficiency during peak periods.",
|
|
"Fresh product expiry constraints require faster, precisely timed deliveries to maintain product quality.",
|
|
"Multi-stop route complexity increases travel time, operational costs, and delivery coordination challenges.",
|
|
"Inventory stockout risks increase when delivery delays disrupt fast-moving product distribution.",
|
|
],
|
|
so: [
|
|
"AI demand forecasting adapts delivery plans instantly to real-time order demand.",
|
|
"Expiry-aware routing prioritises perishable goods for on-time freshness.",
|
|
"Smart multi-stop optimisation groups orders to cut cost and travel time.",
|
|
"Real-time inventory sync prevents stockouts and improves fulfilment accuracy.",
|
|
],
|
|
},
|
|
{
|
|
id: 2,
|
|
title: "Pharma",
|
|
image: "/images/tab-pic-2-solution.webp",
|
|
alt: "Pharma logistics",
|
|
desc:
|
|
"Pharma logistics requires precision, compliance, and real-time monitoring so every shipment arrives safely and on time — from temperature-sensitive medicines to urgent emergency deliveries.",
|
|
ch: [
|
|
"Cold chain integrity demands precise temperature control throughout transit.",
|
|
"Regulatory compliance must be tracked and documented on every delivery.",
|
|
"Critical delivery time windows require highly accurate scheduling.",
|
|
"Emergency shipments need instant dispatch and zero-delay execution.",
|
|
],
|
|
so: [
|
|
"Cold chain monitoring with automatic re-routing keeps shipments in-spec.",
|
|
"Compliance engine with audit trails ensures full chain-of-custody visibility.",
|
|
"Precision scheduling locks in critical delivery windows reliably.",
|
|
"Priority dispatch queue fast-tracks urgent, life-critical shipments.",
|
|
],
|
|
},
|
|
{
|
|
id: 3,
|
|
title: "Enterprise & B2B",
|
|
image: "/images/tab-pic-3-solution.webp",
|
|
alt: "Enterprise and B2B logistics",
|
|
desc:
|
|
"Enterprise and B2B logistics require coordination and reliability to manage high-value shipments at scale — with appointment scheduling, white-glove standards, and strict SLA commitments.",
|
|
ch: [
|
|
"Appointment scheduling requires precise timing across many locations.",
|
|
"White-glove delivery standards demand premium handling and accuracy.",
|
|
"Multi-location routing complexity grows with large-scale operations.",
|
|
"Strict SLA commitments pressure teams to stay timely and error-free.",
|
|
],
|
|
so: [
|
|
"Intelligent appointment engine streamlines and automates delivery slots.",
|
|
"White-glove workflow module enforces premium handling end to end.",
|
|
"Enterprise route planner coordinates efficient multi-location delivery.",
|
|
"SLA monitoring dashboard tracks commitments and flags risk in real time.",
|
|
],
|
|
},
|
|
];
|
|
|
|
function Card({ sec }: { sec: Section }) {
|
|
const [tab, setTab] = useState<Tab>("ch");
|
|
const bullets = tab === "ch" ? sec.ch : sec.so;
|
|
|
|
return (
|
|
<section className="istk" aria-label={`${sec.title} solutions`}>
|
|
<div className="istk__card">
|
|
<IndustryWorldMap />
|
|
|
|
<div className="istk__row">
|
|
{/* Image panel */}
|
|
<div className="istk__media">
|
|
{/* eslint-disable-next-line @next/next/no-img-element */}
|
|
<img className="istk__img" src={sec.image} alt={sec.alt} decoding="async" loading="lazy" />
|
|
</div>
|
|
|
|
{/* Content panel */}
|
|
<div className="istk__content">
|
|
<h3 className="istk__title">{sec.title}</h3>
|
|
<p className="istk__desc">{sec.desc}</p>
|
|
|
|
<div className="istk__tabs" role="tablist" aria-label="Challenges or Solutions">
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected={tab === "ch"}
|
|
className={`istk__tab ${tab === "ch" ? "active" : ""}`}
|
|
onClick={() => setTab("ch")}
|
|
>
|
|
Challenges
|
|
</button>
|
|
<button
|
|
type="button"
|
|
role="tab"
|
|
aria-selected={tab === "so"}
|
|
className={`istk__tab ${tab === "so" ? "active" : ""}`}
|
|
onClick={() => setTab("so")}
|
|
>
|
|
Solutions
|
|
</button>
|
|
</div>
|
|
|
|
<ul className="istk__list" key={tab}>
|
|
{bullets.map((b, i) => (
|
|
<li key={b} style={{ animationDelay: `${i * 80}ms` }}>
|
|
{b}
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
export default function IndustryStack() {
|
|
return (
|
|
<>
|
|
<style dangerouslySetInnerHTML={{ __html: CSS }} />
|
|
<div id="ind-stack">
|
|
{SECTIONS.map((sec) => (
|
|
<Card key={sec.id} sec={sec} />
|
|
))}
|
|
</div>
|
|
</>
|
|
);
|
|
}
|
|
|
|
/* ============================================================
|
|
Styles — faithful to the original PHP Solutions layout:
|
|
one large rounded dark card per industry, inset rounded image
|
|
on the left, simple large title, paragraph, Challenges/Solutions
|
|
pill tabs with an underline rule, and round red-dot bullets.
|
|
Uses the site's Manrope (no extra typefaces).
|
|
============================================================ */
|
|
const CSS = `
|
|
/* No outer frame — the cards float on the page background. */
|
|
#ind-stack { background: transparent; }
|
|
|
|
/* The kit-5 theme forces heading size/color on h3 via !important — re-assert. */
|
|
#ind-stack .istk__title {
|
|
font-family: var(--font-manrope), 'Manrope', system-ui, sans-serif !important;
|
|
font-size: clamp(42px, 4.4vw, 62px) !important;
|
|
font-weight: 600 !important;
|
|
line-height: 1.04 !important;
|
|
letter-spacing: -0.02em !important;
|
|
color: #ffffff !important;
|
|
text-transform: none !important;
|
|
margin: 0 0 22px !important;
|
|
}
|
|
|
|
#ind-stack .istk {
|
|
padding: 14px 20px;
|
|
}
|
|
#ind-stack .istk:first-child { padding-top: 24px; }
|
|
#ind-stack .istk:last-child { padding-bottom: 24px; }
|
|
|
|
#ind-stack .istk__card {
|
|
position: relative;
|
|
overflow: hidden;
|
|
background: #0d0d0d;
|
|
border: 1px solid rgba(255,255,255,0.06);
|
|
border-radius: 40px;
|
|
padding: 44px;
|
|
}
|
|
|
|
/* Procedurally-drawn dotted world map (continent dots + hub pulses + routes).
|
|
Vector canvas, so it never distorts on the card's aspect ratio the way a
|
|
stretched bitmap did. Faint gray dots read as a subtle backdrop on the dark card. */
|
|
#ind-stack .ind__map {
|
|
position: absolute;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 0;
|
|
opacity: 0.7;
|
|
pointer-events: none;
|
|
}
|
|
|
|
#ind-stack .istk__row {
|
|
position: relative;
|
|
z-index: 2;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: clamp(44px, 6vw, 104px);
|
|
}
|
|
|
|
/* ---- Image — inset rounded, portrait, drives the card height ---- */
|
|
#ind-stack .istk__media {
|
|
flex: 0 0 35%;
|
|
align-self: stretch;
|
|
min-height: 560px;
|
|
}
|
|
#ind-stack .istk__img {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
border-radius: 26px;
|
|
}
|
|
|
|
/* ---- Content ---- */
|
|
#ind-stack .istk__content {
|
|
flex: 1;
|
|
min-width: 0;
|
|
max-width: 680px;
|
|
}
|
|
#ind-stack .istk__desc {
|
|
font-size: clamp(16px, 1.25vw, 20px);
|
|
font-weight: 400;
|
|
line-height: 1.62;
|
|
color: rgba(255,255,255,0.8);
|
|
margin: 0 0 30px;
|
|
}
|
|
|
|
/* Tabs — pills above a full-width underline rule */
|
|
#ind-stack .istk__tabs {
|
|
display: flex;
|
|
gap: 10px;
|
|
padding-bottom: 16px;
|
|
margin-bottom: 26px;
|
|
border-bottom: 1px solid rgba(255,255,255,0.2);
|
|
}
|
|
#ind-stack .istk__tab {
|
|
appearance: none;
|
|
border: none;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
font-weight: 700;
|
|
letter-spacing: 0.05em;
|
|
text-transform: uppercase;
|
|
padding: 13px 30px;
|
|
border-radius: 9px;
|
|
background: #f3dede;
|
|
color: #555;
|
|
transition: background-color 0.25s ease, color 0.25s ease;
|
|
}
|
|
#ind-stack .istk__tab:hover { background: #ecd2d2; }
|
|
#ind-stack .istk__tab.active {
|
|
background: #dc2626;
|
|
color: #fff;
|
|
box-shadow: 0 8px 22px -10px rgba(220,38,38,0.8);
|
|
}
|
|
|
|
/* Bullets — round red dots, full-sentence items */
|
|
#ind-stack .istk__list {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
#ind-stack .istk__list li {
|
|
position: relative;
|
|
padding-left: 28px;
|
|
margin-bottom: 22px;
|
|
font-size: clamp(15px, 1.15vw, 18px);
|
|
font-weight: 400;
|
|
line-height: 1.55;
|
|
color: rgba(255,255,255,0.85);
|
|
animation: istkSlideIn 0.4s ease both;
|
|
}
|
|
#ind-stack .istk__list li:last-child { margin-bottom: 0; }
|
|
#ind-stack .istk__list li::before {
|
|
content: '';
|
|
position: absolute;
|
|
left: 2px;
|
|
top: 9px;
|
|
width: 9px;
|
|
height: 9px;
|
|
border-radius: 50%;
|
|
background: #dc2626;
|
|
box-shadow: 0 0 8px rgba(220,38,38,0.55);
|
|
}
|
|
|
|
@keyframes istkSlideIn {
|
|
from { opacity: 0; transform: translateY(8px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
|
|
/* ---- Responsive ---- */
|
|
@media (max-width: 1024px) {
|
|
#ind-stack .istk__card { padding: 28px; border-radius: 28px; }
|
|
#ind-stack .istk__row { flex-direction: column; align-items: stretch; gap: 28px; }
|
|
#ind-stack .istk__media { flex: none; min-height: 0; height: 300px; }
|
|
#ind-stack .istk__content { max-width: none; }
|
|
}
|
|
@media (max-width: 600px) {
|
|
#ind-stack .istk { padding: 10px; }
|
|
#ind-stack .istk__card { padding: 22px 20px; border-radius: 24px; }
|
|
#ind-stack .istk__media { height: 240px; }
|
|
#ind-stack .istk__title { margin-bottom: 16px !important; }
|
|
#ind-stack .istk__desc { margin: 0 0 24px; }
|
|
/* Two equal-width pills that always fit the card — no horizontal overflow. */
|
|
#ind-stack .istk__tabs { gap: 8px; margin-bottom: 22px; }
|
|
#ind-stack .istk__tab { flex: 1 1 0; min-width: 0; padding: 12px 8px; text-align: center; font-size: 13px; }
|
|
#ind-stack .istk__list li { margin-bottom: 18px; }
|
|
}
|
|
@media (prefers-reduced-motion: reduce) {
|
|
#ind-stack .istk__list li { animation: none !important; opacity: 1; transform: none; }
|
|
}
|
|
`;
|