update about page

This commit is contained in:
2026-06-02 14:10:44 +05:30
parent a59a5e79dc
commit bae2fa0daa
24 changed files with 1903 additions and 1052 deletions

10
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "doormile-next",
"version": "0.1.0",
"dependencies": {
"@emailjs/browser": "^4.4.1",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.6.1",
"@react-three/postprocessing": "^3.0.4",
@@ -294,6 +295,15 @@
"node": ">=6.9.0"
}
},
"node_modules/@emailjs/browser": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-4.4.1.tgz",
"integrity": "sha512-DGSlP9sPvyFba3to2A50kDtZ+pXVp/0rhmqs2LmbMS3I5J8FSOgLwzY2Xb4qfKlOVHh29EAutLYwe5yuEZmEFg==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@emnapi/core": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",

View File

@@ -9,6 +9,7 @@
"lint": "eslint"
},
"dependencies": {
"@emailjs/browser": "^4.4.1",
"@react-three/drei": "^10.7.7",
"@react-three/fiber": "^9.6.1",
"@react-three/postprocessing": "^3.0.4",

View File

@@ -5244,7 +5244,6 @@ h1:where(.wp-block-heading).has-background,
.elementor-5180 .elementor-element.elementor-element-846e53d .hfe-site-logo .hfe-site-logo-container img {
/* width: 153px; */
margin-left: 30px;
dis
}
.elementor-5180 .elementor-element.elementor-element-846e53d .widget-image-caption {
@@ -12606,7 +12605,7 @@ h1:where(.wp-block-heading).has-background,
}
{}
h1.page-title {
@@ -12854,7 +12853,7 @@ h1:where(.wp-block-heading).has-background,
--widgets-spacing-column: 40px;
}
{}
h1.page-title {
display: var(--page-title-display);
@@ -17470,7 +17469,7 @@ body.rtl .elementor-61 .elementor-element.elementor-element-ab691ea {
<style>
/* Container */
.container {
@@ -21698,8 +21697,8 @@ img.wp-smiley,
background-position: center center;
background-repeat: no-repeat;
background-size: cover;
transform: none !important; */
}
transform: none !important;
} */
body:not(.rtl) .elementor-element.elementor-element-dcf16f2 {
left: 10px;
@@ -25349,7 +25348,7 @@ img.wp-smiley,
--widgets-spacing-column: 40px;
}
{}
h1.page-title {
display: var(--page-title-display);
@@ -29927,13 +29926,6 @@ img.wp-smiley,
}
.elementor-element.elementor-element-e8ee54e .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;
--counter-prefix-grow: 1;
--counter-suffix-grow: 0;
--counter-number-grow: 0;
@@ -29944,13 +29936,6 @@ img.wp-smiley,
}
.elementor-element.elementor-element-09638df .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;
--counter-prefix-grow: 1;
--counter-suffix-grow: 0;
--counter-number-grow: 0;
@@ -30832,13 +30817,6 @@ img.wp-smiley,
}
.elementor-element.elementor-element-e8ee54e .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;
--counter-prefix-grow: 1;
--counter-suffix-grow: 1;
--counter-number-grow: 0;
@@ -30849,13 +30827,6 @@ img.wp-smiley,
}
.elementor-element.elementor-element-1ea8a4b .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;
--counter-prefix-grow: 1;
--counter-suffix-grow: 1;
--counter-number-grow: 0;
@@ -30866,13 +30837,6 @@ img.wp-smiley,
}
.elementor-element.elementor-element-09638df .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;
--counter-prefix-grow: 1;
--counter-suffix-grow: 1;
--counter-number-grow: 0;
@@ -30883,13 +30847,6 @@ img.wp-smiley,
}
.elementor-element.elementor-element-59be3c9 .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;
--counter-prefix-grow: 1;
--counter-suffix-grow: 1;
--counter-number-grow: 0;
@@ -39759,13 +39716,7 @@ body.rtl .elementor-6473 .elementor-element.elementor-element-1a450c2 {
}
.elementor-6473 .elementor-element.elementor-element-e8ee54e .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;--counter-prefix-grow: 1;
--counter-prefix-grow: 1;
--counter-suffix-grow: 0;
--counter-number-grow: 0;
}
@@ -39775,13 +39726,7 @@ body.rtl .elementor-6473 .elementor-element.elementor-element-1a450c2 {
}
.elementor-6473 .elementor-element.elementor-element-09638df .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;--counter-prefix-grow: 1;
--counter-prefix-grow: 1;
--counter-suffix-grow: 0;
--counter-number-grow: 0;
}
@@ -40652,13 +40597,7 @@ body.rtl .elementor-6473 .elementor-element.elementor-element-1a450c2 {
}
.elementor-6473 .elementor-element.elementor-element-e8ee54e .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;--counter-prefix-grow: 1;
--counter-prefix-grow: 1;
--counter-suffix-grow: 1;
--counter-number-grow: 0;
}
@@ -40668,13 +40607,7 @@ body.rtl .elementor-6473 .elementor-element.elementor-element-1a450c2 {
}
.elementor-6473 .elementor-element.elementor-element-1ea8a4b .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;--counter-prefix-grow: 1;
--counter-prefix-grow: 1;
--counter-suffix-grow: 1;
--counter-number-grow: 0;
}
@@ -40684,13 +40617,7 @@ body.rtl .elementor-6473 .elementor-element.elementor-element-1a450c2 {
}
.elementor-6473 .elementor-element.elementor-element-09638df .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;--counter-prefix-grow: 1;
--counter-prefix-grow: 1;
--counter-suffix-grow: 1;
--counter-number-grow: 0;
}
@@ -40700,13 +40627,7 @@ body.rtl .elementor-6473 .elementor-element.elementor-element-1a450c2 {
}
.elementor-6473 .elementor-element.elementor-element-59be3c9 .elementor-counter-number-wrapper {
text-align: {
{
VALUE
}
}
;--counter-prefix-grow: 1;
--counter-prefix-grow: 1;
--counter-suffix-grow: 1;
--counter-number-grow: 0;
}

View File

@@ -1,6 +1,7 @@
"use client";
import React, { useEffect } from "react";
import { usePathname } from "next/navigation";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
@@ -10,6 +11,51 @@ import { ScrollTrigger } from "gsap/ScrollTrigger";
* and provides smooth defaults.
*/
export default function AnimationProvider({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const initDecorativeBlocks = () => {
// Clean up previous block triggers to avoid duplicates
ScrollTrigger.getAll().forEach((t) => {
if (t.vars && (t.vars as any).id === "block-deco") {
t.kill();
}
});
const vh = window.innerHeight || document.documentElement.clientHeight;
const decorativeBlocks = document.querySelectorAll(".block-decoration");
decorativeBlocks.forEach((block) => {
ScrollTrigger.create({
id: "block-deco",
trigger: block,
start: "top 92%",
onEnter: () => {
setTimeout(() => {
block.classList.add("animated");
}, 150);
},
onEnterBack: () => {
setTimeout(() => {
block.classList.add("animated");
}, 150);
},
onLeave: () => {
block.classList.remove("animated");
},
onLeaveBack: () => {
block.classList.remove("animated");
},
});
// ScrollTrigger does not fire onEnter for blocks already in view at
// creation — on larger / taller screens those stayed un-animated.
// Reveal any block already intersecting the viewport.
const r = block.getBoundingClientRect();
if (r.top < vh && r.bottom > 0) {
block.classList.add("animated");
}
});
};
useEffect(() => {
gsap.registerPlugin(ScrollTrigger);
@@ -24,72 +70,9 @@ export default function AnimationProvider({ children }: { children: React.ReactN
// re-hidden animations on phones. Ignore those spurious resizes.
ScrollTrigger.config({ ignoreMobileResize: true });
const initDecorativeBlocks = () => {
// Clean up previous block triggers to avoid duplicates
ScrollTrigger.getAll().forEach((t) => {
if (t.vars && (t.vars as any).id === "block-deco") {
t.kill();
}
});
const vh = window.innerHeight || document.documentElement.clientHeight;
const decorativeBlocks = document.querySelectorAll(".block-decoration");
decorativeBlocks.forEach((block) => {
ScrollTrigger.create({
id: "block-deco",
trigger: block,
start: "top 92%",
onEnter: () => {
setTimeout(() => {
block.classList.add("animated");
}, 150);
},
onEnterBack: () => {
setTimeout(() => {
block.classList.add("animated");
}, 150);
},
onLeave: () => {
block.classList.remove("animated");
},
onLeaveBack: () => {
block.classList.remove("animated");
},
});
// ScrollTrigger does not fire onEnter for blocks already in view at
// creation — on larger / taller screens those stayed un-animated.
// Reveal any block already intersecting the viewport.
const r = block.getBoundingClientRect();
if (r.top < vh && r.bottom > 0) {
block.classList.add("animated");
}
});
};
// Run initializations
initDecorativeBlocks();
// Refresh ScrollTrigger at staggered intervals to account for lazy-loaded assets/images over the network
const timeouts = [
setTimeout(() => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
}, 100),
setTimeout(() => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
}, 500),
setTimeout(() => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
}, 1500),
setTimeout(() => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
}, 3000),
];
// Refresh on full window load (when all images, styles, and fonts are in place)
const handleLoad = () => {
initDecorativeBlocks();
@@ -111,7 +94,6 @@ export default function AnimationProvider({ children }: { children: React.ReactN
window.addEventListener("resize", handleResize);
return () => {
timeouts.forEach(clearTimeout);
clearTimeout(resizeTimer);
window.removeEventListener("load", handleLoad);
window.removeEventListener("resize", handleResize);
@@ -119,6 +101,29 @@ export default function AnimationProvider({ children }: { children: React.ReactN
};
}, []);
// Listen for route changes and refresh ScrollTriggers so newly rendered content animates in correctly
useEffect(() => {
const refreshAnimations = () => {
initDecorativeBlocks();
ScrollTrigger.refresh(true);
};
// Run route change handler immediately on navigation
refreshAnimations();
// Staggered refreshes to accommodate page layout calculations and paint frames
const timers = [
setTimeout(refreshAnimations, 100),
setTimeout(refreshAnimations, 400),
setTimeout(refreshAnimations, 800),
setTimeout(refreshAnimations, 1500),
];
return () => {
timers.forEach(clearTimeout);
};
}, [pathname]);
return <>{children}</>;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

BIN
src/app/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,5 +1,5 @@
import type { Metadata } from "next";
import { Manrope, Space_Grotesk, Syne, DM_Sans } from "next/font/google";
import { Manrope, Space_Grotesk, Syne, DM_Sans, Inter } from "next/font/google";
import "./globals.css";
import Header from "@/components/layout/Header";
import Footer from "@/components/layout/Footer";
@@ -34,9 +34,21 @@ const dmSans = DM_Sans({
weight: ["400", "500"],
});
// Body font for the Solutions stacked industry sections.
const inter = Inter({
subsets: ["latin"],
variable: "--font-inter",
weight: ["400", "500", "600"],
});
export const metadata: Metadata = {
title: "Doormile — Last-Mile Logistics Intelligence",
description: "Doormile powers last-mile logistics with MileTruth™ AI, providing connected miles, SLA protection, and carrier management.",
icons: {
icon: "/images/cropped-image-2.png",
shortcut: "/images/cropped-image-2.png",
apple: "/images/cropped-image-2.png",
},
robots: {
index: true,
follow: true,
@@ -49,7 +61,7 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en-US" className={`${manrope.variable} ${spaceGrotesk.variable} ${syne.variable} ${dmSans.variable}`}>
<html lang="en-US" className={`${manrope.variable} ${spaceGrotesk.variable} ${syne.variable} ${dmSans.variable} ${inter.variable}`}>
<head>
{/* FontAwesome icons */}
<link

View File

@@ -3,7 +3,6 @@ import MileTruthHero from "../../components/sections/MileTruthHero";
import Workflow1 from "../../components/sections/Workflow1";
import Workflow2 from "../../components/sections/Workflow2";
import Workflow3 from "../../components/sections/Workflow3";
import OptimizationSection from "../../components/optimization/OptimizationSection";
import PerformanceSection from "../../components/performance/PerformanceSection";
export const metadata = {
@@ -18,7 +17,6 @@ export default function MileTruthPage() {
<div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59">
<MileTruthHero />
<OptimizationSection />
<Workflow1 />
<Workflow2 />
<Workflow3 />

View File

@@ -1,6 +1,6 @@
import React from "react";
import SolutionsHero from "@/components/sections/SolutionsHero";
import SolutionCard1 from "@/components/sections/SolutionCard1";
import IndustryStack from "@/components/sections/IndustryStack";
export const metadata = {
title: "Solutions Doormile",
@@ -55,7 +55,7 @@ export default function SolutionsPage() {
<div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59">
<SolutionsHero />
<SolutionCard1 />
<IndustryStack />
</div>
</div>
</div>

View File

@@ -431,7 +431,7 @@ const styles = `
position: relative;
height: 230vh;
background: transparent;
margin-bottom: 120px;
margin-bottom: 0;
}
.dm-opt-sticky {
position: absolute;
@@ -452,7 +452,7 @@ const styles = `
left: 40px !important;
right: 40px !important;
bottom: 24px !important;
border-radius: 60px !important;
border-radius: 35px !important;
overflow: hidden !important;
background: linear-gradient(165deg, #06101f 0%, #020617 35%, #040d1c 70%, #030a18 100%) !important;
border: 1.5px solid ${rgba("#ffffff", 0.08)} !important;

View File

@@ -8,11 +8,11 @@ export default function AboutHero() {
width: 100% !important;
text-align: center !important;
color: #fff !important;
padding: 60px 40px 100px 40px !important;
padding: 40px !important;
z-index: 2;
display: flex !important;
flex-direction: column !important;
justify-content: flex-end !important;
justify-content: center !important;
align-items: center !important;
height: 100% !important;
}
@@ -34,8 +34,8 @@ export default function AboutHero() {
<div
style={{
backgroundImage: "url('/images/about-bg.png')",
["--hero-overlay" as any]: "linear-gradient(to bottom, rgba(0, 0, 0, 0.15) 0%, rgba(0, 0, 0, 0.15) 60%, rgba(0, 0, 0, 0.55) 100%)"
}}
"--hero-overlay": "linear-gradient(to bottom, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0.92) 60%, rgba(0, 0, 0, 0.98) 100%)"
} as React.CSSProperties}
className="custom-standard-hero-card"
>
<div className="about-us-hero-content">

View File

@@ -11,14 +11,22 @@ if (typeof window !== "undefined") {
export default function CompetitiveEdge() {
const sectionRef = useRef<HTMLDivElement>(null);
const tableWrapperRef = useRef<HTMLDivElement>(null);
const moatRef = useRef<HTMLDivElement>(null);
const headerRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const section = sectionRef.current;
const tableWrapper = tableWrapperRef.current;
const moat = moatRef.current;
const header = headerRef.current;
if (!section || !tableWrapper || !moat) return;
if (!section || !tableWrapper || !header) return;
const rows = section.querySelectorAll(".table-row-hover");
const badges = section.querySelectorAll(".yes-badge, .advanced-badge");
// Dynamic initial state settings via GSAP to prevent SSR flash
gsap.set([header, tableWrapper], { opacity: 0, y: 45 });
gsap.set(rows, { opacity: 0, y: 20 });
gsap.set(badges, { opacity: 0, scale: 0.8 });
// GSAP Timeline ScrollTrigger for viewport entrances
const entryTl = gsap.timeline({
@@ -30,22 +38,16 @@ export default function CompetitiveEdge() {
});
entryTl
// 1. Reveal Table & Right panel
.fromTo([tableWrapper, moat], {
opacity: 0,
y: 45,
}, {
// 1. Reveal Header & Table
.to([header, tableWrapper], {
opacity: 1,
y: 0,
duration: 0.95,
stagger: 0.12,
stagger: 0.15,
ease: "power4.out",
})
// 2. Stagger slide up row items
.fromTo(section.querySelectorAll(".table-row-hover"), {
opacity: 0,
y: 20,
}, {
.to(rows, {
opacity: 1,
y: 0,
duration: 0.75,
@@ -53,10 +55,7 @@ export default function CompetitiveEdge() {
ease: "power3.out",
}, "-=0.6")
// 3. Pop checkmarks and badges cleanly
.fromTo(section.querySelectorAll(".yes-badge, .advanced-badge"), {
opacity: 0,
scale: 0.8,
}, {
.to(badges, {
opacity: 1,
scale: 1,
duration: 0.55,
@@ -73,143 +72,140 @@ export default function CompetitiveEdge() {
<div className="comparison-bg-dots" />
<div className="container">
<div className="comparison-layout">
{/* Comparison Matrix Table (69% on Desktop) */}
<div className="table-wrapper" ref={tableWrapperRef}>
<table className="comparison-table">
<thead>
<tr>
<th>Capability</th>
<th className="col-highlight">Doormile</th>
<th>Aggregators</th>
<th>Local Couriers</th>
<th>Software Platforms</th>
</tr>
</thead>
<tbody>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<rect x="1" y="3" width="15" height="13"></rect>
<polygon points="16 8 20 8 23 11 23 16 16 16 16 8"></polygon>
<circle cx="5.5" cy="18.5" r="2.5"></circle>
<circle cx="18.5" cy="18.5" r="2.5"></circle>
</svg>
Owned fleet control
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="no-text">No</td>
<td className="partial-text">Yes</td>
<td className="no-text">No</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 22c5.523 0 10-4.477 10-10S17.523 2 12 2 2 6.477 2 12s4.477 10 10 10z"></path>
<path d="M12 6v6l4 2"></path>
</svg>
Dynamic routing & dispatch
</td>
<td className="col-highlight"><span className="advanced-badge"> Advanced</span></td>
<td className="partial-text">Basic</td>
<td className="no-text">No</td>
<td className="partial-text">Advanced</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
</svg>
EV-aware planning
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="no-text">No</td>
<td className="no-text">No</td>
<td className="no-text">No</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<line x1="16" y1="13" x2="8" y2="13"></line>
<line x1="16" y1="17" x2="8" y2="17"></line>
<polyline points="10 9 9 9 8 9"></polyline>
</svg>
Documentation & proof trail
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="partial-text">Partial</td>
<td className="no-text">No</td>
<td className="partial-text">Yes</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
Real-time tracking
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="partial-text">Yes</td>
<td className="no-text">No</td>
<td className="partial-text">Yes</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
</svg>
Verified handling
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="partial-text">Partial</td>
<td className="no-text">No</td>
<td className="no-text">No</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
</svg>
Hyperlocal learning
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="no-text">No</td>
<td className="no-text">No</td>
<td className="no-text">No</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="22" y1="12" x2="18" y2="12"></line>
<line x1="6" y1="12" x2="2" y2="12"></line>
<line x1="12" y1="6" x2="12" y2="2"></line>
<line x1="12" y1="22" x2="12" y2="18"></line>
</svg>
SLA accountability
</td>
<td className="col-highlight"><span className="advanced-badge"> High</span></td>
<td className="no-text">Low</td>
<td className="partial-text">Medium</td>
<td className="no-text">Low</td>
</tr>
</tbody>
</table>
</div>
{/* Section Header */}
<div className="comparison-header" ref={headerRef}>
<div className="advantage-badge">DoorMile Advantage</div>
<h2 className="moat-heading">WHERE DOORMILE SITS AND WHY IT WINS</h2>
<p className="moat-desc">
A side-by-side technical capabilities comparison showing how operational fleet ownership and dynamic AI planning disrupt basic aggregators.
</p>
</div>
{/* Moat Highlight Card (28% on Desktop) */}
<div className="moat-panel" ref={moatRef}>
<div className="moat-accent-line" />
<div className="advantage-badge">DoorMile Advantage</div>
<h3 className="moat-heading">WHERE DOORMILE SITS AND WHY IT WINS</h3>
<p className="moat-desc">
A side-by-side technical capabilities comparison showing how operational fleet ownership and dynamic AI planning disrupt basic aggregators.
</p>
</div>
{/* Comparison Matrix Table (Full width on desktop) */}
<div className="table-wrapper" ref={tableWrapperRef}>
<table className="comparison-table">
<thead>
<tr>
<th>Capability</th>
<th className="col-highlight">Doormile</th>
<th>Aggregators</th>
<th>Local Couriers</th>
<th>Software Platforms</th>
</tr>
</thead>
<tbody>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<rect x="1" y="3" width="15" height="13"></rect>
<polygon points="16 8 20 8 23 11 23 16 16 16 16 8"></polygon>
<circle cx="5.5" cy="18.5" r="2.5"></circle>
<circle cx="18.5" cy="18.5" r="2.5"></circle>
</svg>
Owned fleet control
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="no-text">No</td>
<td className="partial-text">Yes</td>
<td className="no-text">No</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
Dynamic routing & dispatch
</td>
<td className="col-highlight"><span className="advanced-badge"> Advanced</span></td>
<td className="partial-text">Basic</td>
<td className="no-text">No</td>
<td className="partial-text">Advanced</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon>
</svg>
EV-aware planning
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="no-text">No</td>
<td className="no-text">No</td>
<td className="no-text">No</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
<polyline points="14 2 14 8 20 8"></polyline>
<line x1="16" y1="13" x2="8" y2="13"></line>
<line x1="16" y1="17" x2="8" y2="17"></line>
<polyline points="10 9 9 9 8 9"></polyline>
</svg>
Documentation & proof trail
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="partial-text">Partial</td>
<td className="no-text">No</td>
<td className="partial-text">Yes</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<polyline points="12 6 12 12 16 14"></polyline>
</svg>
Real-time tracking
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="partial-text">Yes</td>
<td className="no-text">No</td>
<td className="partial-text">Yes</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
</svg>
Verified handling
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="partial-text">Partial</td>
<td className="no-text">No</td>
<td className="no-text">No</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
</svg>
Hyperlocal learning
</td>
<td className="col-highlight"><span className="yes-badge"> Yes</span></td>
<td className="no-text">No</td>
<td className="no-text">No</td>
<td className="no-text">No</td>
</tr>
<tr className="table-row-hover">
<td className="capability-cell">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="12" r="10"></circle>
<line x1="22" y1="12" x2="18" y2="12"></line>
<line x1="6" y1="12" x2="2" y2="12"></line>
<line x1="12" y1="6" x2="12" y2="2"></line>
<line x1="12" y1="22" x2="12" y2="18"></line>
</svg>
SLA accountability
</td>
<td className="col-highlight"><span className="advanced-badge"> High</span></td>
<td className="no-text">Low</td>
<td className="partial-text">Medium</td>
<td className="no-text">Low</td>
</tr>
</tbody>
</table>
</div>
</div>
@@ -229,9 +225,9 @@ export default function CompetitiveEdge() {
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 900px;
height: 900px;
background: radial-gradient(circle, rgba(200, 16, 46, 0.03) 0%, transparent 70%);
width: 1000px;
height: 1000px;
background: radial-gradient(circle, rgba(200, 16, 46, 0.035) 0%, transparent 70%);
z-index: 0;
pointer-events: none;
}
@@ -250,23 +246,81 @@ export default function CompetitiveEdge() {
.comparison-section .container {
position: relative;
z-index: 2;
max-width: 1400px;
max-width: 1400px; /* Restored/Expanded for gorgeous wide balance on large screens */
margin: 0 auto;
padding: 0 40px;
}
/* Proportional flex layout scaling */
.comparison-layout {
/* Section Header Layout */
.comparison-header {
text-align: center;
margin-bottom: 60px;
position: relative;
z-index: 2;
display: flex;
align-items: stretch;
justify-content: space-between;
gap: 32px;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* Spacious table styling wrapper (69% width) */
/* DoorMile Advantage Badge */
.advantage-badge {
font-family: 'Manrope', sans-serif;
font-weight: 800;
font-size: 0.75rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: #c8102e;
background: rgba(200, 16, 46, 0.06);
padding: 6px 14px;
border-radius: 30px;
margin: 0 auto 24px auto;
border: 1.5px solid rgba(200, 16, 46, 0.15);
display: inline-block;
white-space: nowrap;
}
/* Centered heading with bottom accent underline */
.moat-heading {
font-family: 'Manrope', sans-serif;
font-size: clamp(2rem, 3.4vw, 3.2rem); /* Slightly enlarged for premium visual weight */
font-weight: 800;
line-height: 1.15;
color: #111111;
margin: 0 auto 20px auto;
letter-spacing: -0.03em;
text-transform: uppercase;
word-wrap: break-word;
overflow-wrap: break-word;
display: flex;
flex-direction: column;
align-items: center;
}
/* Centered horizontal red underline accent decoration */
.moat-heading::after {
content: "";
display: block;
width: 72px;
height: 4px;
background: #c8102e;
margin-top: 18px;
border-radius: 2px;
}
.moat-desc {
font-family: 'Manrope', sans-serif;
font-size: 1.05rem;
line-height: 1.65;
color: #585c67;
margin: 16px auto 0 auto !important;
max-width: 760px !important;
text-align: center !important;
}
/* Spacious table styling wrapper (100% width on Desktop) */
.table-wrapper {
flex: 0 0 69%;
max-width: 69%;
width: 100%;
background: #ffffff;
border-radius: 24px;
border: 1px solid rgba(0, 0, 0, 0.05);
@@ -288,9 +342,9 @@ export default function CompetitiveEdge() {
/* Enlarge row paddings and metrics size */
.comparison-table th,
.comparison-table td {
padding: 22px 24px;
padding: 24px 28px;
border-bottom: 1px solid #f0f0f4;
font-size: 0.96rem;
font-size: 1rem;
color: #2b2b2b;
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
@@ -303,7 +357,7 @@ export default function CompetitiveEdge() {
font-family: 'Manrope', sans-serif;
font-weight: 700;
text-transform: uppercase;
font-size: 0.8rem;
font-size: 0.85rem;
letter-spacing: 0.1em;
color: #8a8f9d;
background: rgba(15, 23, 42, 0.02);
@@ -315,7 +369,7 @@ export default function CompetitiveEdge() {
color: #ffffff !important;
text-align: center;
font-weight: 800;
font-size: 0.85rem;
font-size: 0.9rem;
letter-spacing: 0.1em;
border-left: 2.5px solid #c8102e;
border-right: 2.5px solid #c8102e;
@@ -327,7 +381,7 @@ export default function CompetitiveEdge() {
text-align: center;
color: #c8102e !important;
font-weight: 700;
background: linear-gradient(180deg, rgba(200, 16, 46, 0.02) 0%, rgba(200, 16, 46, 0.005) 100%) !important;
background: linear-gradient(180deg, rgba(200, 16, 46, 0.025) 0%, rgba(200, 16, 46, 0.005) 100%) !important;
border-left: 2.5px solid rgba(200, 16, 46, 0.12);
border-right: 2.5px solid rgba(200, 16, 46, 0.12);
position: relative;
@@ -340,17 +394,15 @@ export default function CompetitiveEdge() {
transition: background-color 0.3s ease;
}
.table-row-hover:hover {
background-color: #fafbfd;
.table-row-hover:hover td {
background-color: rgba(200, 16, 46, 0.01) !important;
}
.table-row-hover:hover td.col-highlight {
background: linear-gradient(180deg, rgba(200, 16, 46, 0.045) 0%, rgba(200, 16, 46, 0.015) 100%) !important;
background: linear-gradient(180deg, rgba(200, 16, 46, 0.06) 0%, rgba(200, 16, 46, 0.02) 100%) !important;
border-left-color: rgba(200, 16, 46, 0.35);
border-right-color: rgba(200, 16, 46, 0.35);
box-shadow: inset 0 0 16px rgba(200, 16, 46, 0.04);
transform: scale(1.015);
z-index: 10;
}
/* Soft Breathing Box Shadow Red Glow Pulse Loop */
@@ -359,7 +411,7 @@ export default function CompetitiveEdge() {
box-shadow: inset 0 0 0 0px rgba(200, 16, 46, 0);
}
50% {
box-shadow: inset 0 0 15px 1.5px rgba(200, 16, 46, 0.045);
box-shadow: inset 0 0 18px 2px rgba(200, 16, 46, 0.05);
}
100% {
box-shadow: inset 0 0 0 0px rgba(200, 16, 46, 0);
@@ -369,10 +421,10 @@ export default function CompetitiveEdge() {
.capability-cell {
display: flex;
align-items: center;
gap: 12px;
gap: 14px;
font-weight: 700;
color: #111111;
font-size: 1.05rem;
font-size: 1.1rem;
}
.capability-cell svg {
@@ -427,124 +479,11 @@ export default function CompetitiveEdge() {
font-weight: 500;
}
/* --- RIGHT SIDE ADVANTAGE CARD PANEL (Centered content alignment) --- */
.moat-panel {
flex: 0 0 28%;
max-width: 28%;
background: linear-gradient(135deg, #ffffff 0%, #fbfbfc 100%);
border: 1px solid rgba(0, 0, 0, 0.05);
border-radius: 24px;
padding: 40px 24px;
box-shadow: 0 20px 45px rgba(0, 0, 0, 0.025), inset 0 1px 0 #ffffff;
display: flex;
flex-direction: column;
align-items: center; /* Centers items horizontally */
justify-content: center;
text-align: center; /* Centers all text */
position: relative;
overflow: hidden;
transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.4s ease;
will-change: transform;
}
.moat-panel:hover {
transform: translateY(-5px);
box-shadow: 0 35px 70px rgba(0, 0, 0, 0.045);
}
.moat-accent-line {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 5px;
background: #c8102e;
}
/* DoorMile Advantage Badge */
.advantage-badge {
font-family: 'Manrope', sans-serif;
font-weight: 800;
font-size: 0.72rem;
letter-spacing: 0.12em;
text-transform: uppercase;
color: #c8102e;
background: rgba(200, 16, 46, 0.06);
padding: 6px 14px;
border-radius: 30px;
margin: 0 auto 24px auto;
border: 1.5px solid rgba(200, 16, 46, 0.15);
display: inline-block;
white-space: nowrap;
}
/* Centered heading with bottom accent underline instead of left bar */
.moat-heading {
font-family: 'Manrope', sans-serif;
font-size: clamp(1.4rem, 2.3vw, 2.1rem);
font-weight: 800;
line-height: 1.25;
color: #111111;
margin: 0 auto 20px auto;
letter-spacing: -0.025em;
text-transform: uppercase;
word-wrap: break-word;
overflow-wrap: break-word;
display: flex;
flex-direction: column;
align-items: center;
}
/* Centered horizontal red underline accent decoration */
.moat-heading::after {
content: "";
display: block;
width: 48px;
height: 3.5px;
background: #c8102e;
margin-top: 16px;
border-radius: 2px;
}
.moat-desc {
font-family: 'Manrope', sans-serif;
font-size: 0.98rem;
line-height: 1.62;
color: #585c67;
margin: 8px auto 0 auto;
max-width: 340px; /* Bounded width for beautiful block wrapping */
}
/* --- RESPONSIVE WORKFLOWS & BREAKPOINTS --- */
@media (max-width: 1200px) {
.comparison-section .container {
padding: 0 24px;
}
.comparison-layout {
gap: 24px;
}
}
@media (max-width: 1024px) {
.comparison-layout {
flex-direction: column-reverse;
gap: 40px;
}
.table-wrapper,
.moat-panel {
flex: 0 0 100%;
max-width: 100%;
}
.moat-panel {
padding: 48px;
}
.moat-heading {
font-size: 2.2rem;
}
}
@media (max-width: 768px) {
@@ -552,6 +491,10 @@ export default function CompetitiveEdge() {
padding: 80px 0;
}
.moat-heading {
font-size: 1.8rem;
}
/* Capability Matrix table gains responsive horizontal swipe scrolls */
.table-wrapper {
width: 100%;

View File

@@ -3,6 +3,17 @@
import React, { useState } from "react";
import Image from "next/image";
import { ScrollReveal } from "@/animations/Reveal";
import emailjs from "@emailjs/browser";
// Type definitions for EmailJS template parameters
interface EmailJSTemplateParams extends Record<string, unknown> {
name: string;
email: string;
phone: string;
company: string;
subject: string;
message: string;
}
export default function ContactForm() {
const socialIconSpacing = {
@@ -19,27 +30,90 @@ export default function ContactForm() {
message: "",
});
const [formStatus, setFormStatus] = useState<"idle" | "submitting" | "success" | "error">("idle");
const [errorMessage, setErrorMessage] = useState<string>("");
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData((prev) => ({ ...prev, [name]: value }));
};
// Pre-submission validation function
const validateForm = (): string | null => {
if (!formData.fullName.trim()) {
return "Full name is required.";
}
if (formData.fullName.trim().length < 2) {
return "Full name must be at least 2 characters.";
}
if (!formData.email.trim()) {
return "Email is required.";
}
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(formData.email.trim())) {
return "Please enter a valid email address.";
}
if (!formData.subject.trim()) {
return "Subject is required.";
}
if (!formData.message.trim()) {
return "Message is required.";
}
if (formData.message.trim().length < 10) {
return "Message must be at least 10 characters.";
}
return null;
};
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setErrorMessage("");
if (!formData.fullName || !formData.email || !formData.subject || !formData.message) {
// Validate inputs before submitting to EmailJS
const validationError = validateForm();
if (validationError) {
setErrorMessage(validationError);
setFormStatus("error");
return;
}
setFormStatus("submitting");
// Fetch credentials from Next.js environment variables
const serviceId = process.env.NEXT_PUBLIC_EMAILJS_SERVICE_ID;
const templateId = process.env.NEXT_PUBLIC_EMAILJS_TEMPLATE_ID;
const publicKey = process.env.NEXT_PUBLIC_EMAILJS_PUBLIC_KEY;
if (!serviceId || !templateId || !publicKey) {
console.error("EmailJS credentials are not configured in environment variables.");
setErrorMessage("Email service configuration error. Please contact the administrator.");
setFormStatus("error");
return;
}
try {
console.log("Contact form submitted:", formData);
await new Promise((resolve) => setTimeout(resolve, 1000));
// Map form fields to EmailJS template variables matching user's template config
const templateParams: EmailJSTemplateParams = {
name: formData.fullName.trim(),
email: formData.email.trim(),
phone: "", // phone is not available in the current contact form UI
company: "", // company is not available in the current contact form UI
subject: formData.subject.trim(),
message: formData.message.trim(),
};
// Send email via EmailJS API
await emailjs.send(serviceId, templateId, templateParams, publicKey);
setFormStatus("success");
// Reset form fields after successful submission
setFormData({ fullName: "", email: "", subject: "", message: "" });
} catch {
} catch (error) {
console.error("EmailJS Error:", error);
setErrorMessage("Failed to send message. Please try again later.");
setFormStatus("error");
}
};
@@ -266,7 +340,7 @@ export default function ContactForm() {
)}
{formStatus === "error" && (
<div style={{ color: "#f44336", marginTop: "10px", fontSize: "14px" }}>
Something went wrong. Please try again.
{errorMessage || "Something went wrong. Please try again."}
</div>
)}
</div>

View File

@@ -115,89 +115,109 @@ export default function EVLogisticSection() {
<style dangerouslySetInnerHTML={{ __html: `
/* Custom CSS Scoped to EV Logistics Section - New Premium Look */
.ev-logistic-section {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 1320px;
margin: 10px auto 120px auto; /* Centered horizontally with auto margins */
padding: 80px 60px; /* Restored original balanced left/right paddings */
box-sizing: border-box;
background: #ffffff;
font-family: 'Manrope', sans-serif;
gap: 60px;
position: relative;
display: flex !important;
flex-direction: column !important;
width: 100% !important;
max-width: 1320px !important;
margin: 10px auto 120px auto !important; /* Centered horizontally with auto margins */
padding: 80px 60px !important; /* Restored original balanced left/right paddings */
box-sizing: border-box !important;
background: #ffffff !important;
font-family: 'Manrope', sans-serif !important;
position: relative !important;
}
/* ENLARGED Left Column - Image columns takes up 60% for high visual weight */
.ev-logistic-header {
width: 100% !important;
max-width: 100% !important;
align-self: stretch !important;
border-bottom: 2px solid rgba(17, 17, 17, 0.09) !important;
padding-bottom: 16px !important;
margin-bottom: 48px !important;
display: block !important;
text-align: left !important;
}
.ev-logistic-body-grid {
display: flex !important;
flex-direction: row !important;
align-items: center !important;
justify-content: space-between !important;
width: 100% !important;
gap: 40px !important;
}
/* Balanced Left Column - Image column takes up 58% */
.ev-logistic-image-col {
flex: 1 1 60%;
max-width: 40%;
display: flex;
align-items: center;
justify-content: flex-start; /* Align left edge */
position: relative;
min-height: 580px;
margin-left: -150px; /* Shifted left towards boundary */
flex: 1 1 58% !important;
max-width: 58% !important;
display: flex !important;
align-items: center !important;
justify-content: flex-start !important;
position: relative !important;
min-height: auto !important;
margin: 0 !important;
}
/* Branded glow spotlight halo behind the vehicle */
.ev-logistic-image-glow {
position: absolute;
left: 50%;
top: 50%;
width: 80%;
height: 70%;
transform: translate(-50%, -50%);
background: radial-gradient(circle, rgba(192, 18, 39, 0.05) 0%, rgba(192, 18, 39, 0.01) 55%, transparent 75%);
filter: blur(54px);
z-index: 0;
pointer-events: none;
position: absolute !important;
left: 50% !important;
top: 50% !important;
width: 80% !important;
height: 70% !important;
transform: translate(-50%, -50%) !important;
background: radial-gradient(circle, rgba(192, 18, 39, 0.05) 0%, rgba(192, 18, 39, 0.01) 55%, transparent 75%) !important;
filter: blur(54px) !important;
z-index: 0 !important;
pointer-events: none !important;
}
/* Increased max-width wrapper so image scales much larger */
/* Image wrapper scaling naturally inside its column container */
.ev-logistic-image-wrapper {
width: 100%;
height: 100%;
max-width: 1000px; /* Enlarged vehicle size */
display: flex;
align-items: center;
justify-content: flex-start;
overflow: visible;
position: relative;
will-change: transform;
transform: scale(1.25);
transform-origin: left center;
width: 100% !important;
max-width: 100% !important;
display: flex !important;
align-items: center !important;
justify-content: flex-start !important;
overflow: visible !important;
position: relative !important;
transform: scale(1.15) !important; /* Scale up image to make it larger and more dominant */
transform-origin: left center !important;
margin-left: -80px !important; /* Offset image to the left to anchor it to the container edge */
}
.ev-logistic-image-wrapper img {
width: 100%;
height: auto;
object-fit: contain;
filter: none; /* Blends solid white JPEG edges seamlessly into pure white background */
will-change: transform;
width: 100% !important;
height: auto !important;
object-fit: contain !important;
filter: none !important; /* Blends solid white JPEG edges seamlessly into pure white background */
will-change: transform !important;
}
/* Balanced right column - takes up 40% for crisp textual reading */
/* Balanced right column - takes up 42% for crisp textual reading */
.ev-logistic-content-col {
flex: 1 1 40%;
max-width: 40%;
display: flex;
flex-direction: column;
justify-content: center;
flex: 1 1 42% !important;
max-width: 42% !important;
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
margin: 0 !important;
}
.ev-logistic-kicker {
font-size: 13px;
font-weight: 700;
letter-spacing: 0px; /* Expands to 3px on scroll */
text-transform: lowercase;
color: #888888;
margin-bottom: 20px;
margin-left: 10px; /* Shifted exactly 10px to the right as requested */
font-size: 14px !important;
font-weight: 500 !important;
line-height: 2.1429em !important;
letter-spacing: 0px !important; /* Expands to 3px on scroll */
text-transform: lowercase !important;
color: #111111 !important;
margin: 0 !important;
opacity: 0;
transform: translateY(-12px);
will-change: transform, opacity, letter-spacing;
text-align: left !important;
display: inline-block !important;
}
.ev-logistic-title-wrapper {
@@ -384,23 +404,27 @@ export default function EVLogisticSection() {
/* Responsiveness constraints */
@media (max-width: 1024px) {
.ev-logistic-section {
flex-direction: column;
padding: 60px 24px;
gap: 50px;
margin-bottom: 60px;
}
.ev-logistic-body-grid {
flex-direction: column;
gap: 50px;
}
.ev-logistic-image-col {
flex: 1 1 100%;
max-width: 100%;
min-height: auto;
margin-left: 0; /* Reset margins on mobile */
justify-content: center; /* Center layout on mobile */
}
.ev-logistic-image-wrapper {
max-width: 580px;
transform: none; /* Reset scaling on mobile */
max-width: 580px !important;
transform: none !important; /* Reset scale transform on mobile/tablet */
margin-left: 0 !important; /* Reset left margin offset on mobile/tablet */
justify-content: center !important;
}
.ev-logistic-content-col {
@@ -416,11 +440,6 @@ export default function EVLogisticSection() {
@media (max-width: 768px) {
.ev-logistic-section {
padding: 40px 16px;
gap: 40px;
}
.ev-logistic-image-wrapper {
max-width: 100%;
}
.ev-logistic-title {
@@ -443,90 +462,94 @@ export default function EVLogisticSection() {
ref={containerRef}
className="ev-logistic-section"
>
{/* Left Column: Enlarged Floating Wrapper & Ambient glow */}
<div className="ev-logistic-image-col">
<div className="ev-logistic-image-glow"></div>
<div ref={imageWrapperRef} className="ev-logistic-image-wrapper">
<Image
ref={imageRef as any}
src="/images/ev.jpeg"
alt="EV Logistics"
width={1050}
height={854}
priority
/>
</div>
{/* Top Header Row with / features / kicker */}
<div className="ev-logistic-header">
<div className="ev-logistic-kicker">/ features /</div>
</div>
{/* Right Column: Refined Accordion list with letter-by-letter animation */}
<div className="ev-logistic-content-col">
{/* Animated features kicker */}
<div className="ev-logistic-kicker">/ features /</div>
<div className="ev-logistic-body-grid">
{/* Left Column: Enlarged Floating Wrapper & Ambient glow */}
<div className="ev-logistic-image-col">
<div className="ev-logistic-image-glow"></div>
{/* Character-by-character masked entrance wave reveal on scroll */}
<div className="ev-logistic-title-wrapper">
<h3 className="ev-logistic-title">
{headingWords.map((word, wordIndex) => (
<span key={wordIndex} className="ev-word-inline">
{word.split("").map((letter, letterIndex) => (
<span key={letterIndex} className="ev-char-wrapper">
<span className="ev-char">{letter}</span>
</span>
))}
<span className="ev-char-space">&nbsp;</span>
</span>
))}
</h3>
<div ref={imageWrapperRef} className="ev-logistic-image-wrapper">
<Image
ref={imageRef}
src="/images/ev.jpeg"
alt="EV Logistics"
width={1050}
height={854}
priority
/>
</div>
</div>
<div className="ev-logistic-accordion">
{ACCORDION_DATA.map((item) => (
<div
key={item.index}
className={`ev-logistic-accordion-item ${openIndex === item.index ? "active" : ""}`}
>
<button
className="ev-logistic-accordion-header"
onClick={() => toggleAccordion(item.index)}
aria-expanded={openIndex === item.index}
>
<span>{item.num}. {item.title}</span>
<span className="ev-logistic-accordion-arrow-container">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="7" y1="17" x2="17" y2="7"></line>
<polyline points="7 7 17 7 17 17"></polyline>
</svg>
{/* Right Column: Refined Accordion list with letter-by-letter animation */}
<div className="ev-logistic-content-col">
{/* Character-by-character masked entrance wave reveal on scroll */}
<div className="ev-logistic-title-wrapper">
<h3 className="ev-logistic-title">
{headingWords.map((word, wordIndex) => (
<span key={wordIndex} className="ev-word-inline">
{word.split("").map((letter, letterIndex) => (
<span key={letterIndex} className="ev-char-wrapper">
<span className="ev-char">{letter}</span>
</span>
))}
<span className="ev-char-space">&nbsp;</span>
</span>
</button>
))}
</h3>
</div>
<div className="ev-logistic-accordion-content">
<div className="ev-logistic-accordion-content-inner">
{/* Kinetic slide-up and fade subtitle reveal */}
<p style={{
transform: openIndex === item.index ? "translateY(0)" : "translateY(12px)",
opacity: openIndex === item.index ? 1 : 0,
transition: "transform 0.5s cubic-bezier(0.25, 1, 0.5, 1), opacity 0.5s ease",
transitionDelay: "0.08s",
margin: 0
}}>
{item.desc}
</p>
<div className="ev-logistic-accordion">
{ACCORDION_DATA.map((item) => (
<div
key={item.index}
className={`ev-logistic-accordion-item ${openIndex === item.index ? "active" : ""}`}
>
<button
className="ev-logistic-accordion-header"
onClick={() => toggleAccordion(item.index)}
aria-expanded={openIndex === item.index}
>
<span>{item.num}. {item.title}</span>
<span className="ev-logistic-accordion-arrow-container">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="7" y1="17" x2="17" y2="7"></line>
<polyline points="7 7 17 7 17 17"></polyline>
</svg>
</span>
</button>
<div className="ev-logistic-accordion-content">
<div className="ev-logistic-accordion-content-inner">
{/* Kinetic slide-up and fade subtitle reveal */}
<p style={{
transform: openIndex === item.index ? "translateY(0)" : "translateY(12px)",
opacity: openIndex === item.index ? 1 : 0,
transition: "transform 0.5s cubic-bezier(0.25, 1, 0.5, 1), opacity 0.5s ease",
transitionDelay: "0.08s",
margin: 0
}}>
{item.desc}
</p>
</div>
</div>
{/* Underline track & sweep animated red bar */}
<div className="ev-logistic-accordion-progress-track">
<div className="ev-logistic-accordion-progress-bar"></div>
</div>
</div>
{/* Underline track & sweep animated red bar */}
<div className="ev-logistic-accordion-progress-track">
<div className="ev-logistic-accordion-progress-bar"></div>
</div>
</div>
))}
))}
</div>
</div>
</div>
</div>

View File

@@ -36,6 +36,52 @@ export default function HowItWorksHero() {
background-size: cover !important;
}
/* Center alignments for standard content slider elements */
.elementor-element.elementor-element-6c7cbcb .slide-content {
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
text-align: center !important;
width: 100% !important;
height: 100% !important;
padding: 0 40px !important;
box-sizing: border-box !important;
}
.elementor-element.elementor-element-6c7cbcb .slide-content-inner {
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
text-align: center !important;
width: 100% !important;
max-width: 1000px !important;
margin: 0 auto !important;
}
.elementor-element.elementor-element-6c7cbcb .content-slider-item-heading,
.elementor-element.elementor-element-6c7cbcb .content-slider-item-heading .heading-content {
text-align: center !important;
display: block !important;
width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
}
.elementor-element.elementor-element-6c7cbcb .content-slider-item-text {
display: flex !important;
justify-content: center !important;
width: 100% !important;
margin-top: 23px !important;
}
.elementor-element.elementor-element-6c7cbcb .text-content {
text-align: center !important;
max-width: 800px !important;
margin: 0 auto !important;
}
@media (min-width: 1025px) {
.elementor-element.elementor-element-6c7cbcb .owl-carousel.owl-theme .content-item {
height: 800px !important;

View File

@@ -0,0 +1,326 @@
"use client";
import React, { useState } from "react";
import IndustryStackMap from "./IndustryStackMap";
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.jpeg",
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.jpeg",
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.jpeg",
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">
<span className="istk__map" aria-hidden="true" />
<IndustryStackMap />
<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;
}
/* Static dotted world map (image) — inverted to light dots on the dark card */
#ind-stack .istk__map {
position: absolute;
inset: 0;
z-index: 0;
background: url('/images/bg-map.png') center / 100% 100% no-repeat;
filter: invert(1) brightness(1.4);
opacity: 0.16;
pointer-events: none;
animation: istkMapBreathe 7s ease-in-out infinite;
}
/* Animated logistics overlay (hub pulses + travelling packets) sits over the map */
#ind-stack .istk__canvas {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 1;
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); }
}
@keyframes istkMapBreathe {
0%, 100% { opacity: 0.12; }
50% { opacity: 0.2; }
}
/* ---- 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 (prefers-reduced-motion: reduce) {
#ind-stack .istk__list li { animation: none !important; opacity: 1; transform: none; }
#ind-stack .istk__map { animation: none !important; }
}
`;

View File

@@ -0,0 +1,198 @@
"use client";
import React, { useEffect, useRef } from "react";
/**
* Animated logistics overlay drawn over the static dotted world map image
* (`/images/bg-map.png`, set as the card background in IndustryStack).
*
* - 6 city hub nodes placed on real continents (lon/lat → normalised coords),
* each with a sin() expanding pulse ring + a solid red centre dot.
* - 4 delivery packets (#ef4444) travelling quadratic-Bézier arcs between
* random node pairs at 0.0030.006 / frame; on arrival they pick a fresh
* random pair and restart — reading as live routes across the map.
*
* The canvas is full-bleed over the card and lines up with the background
* map because both fill the same rect (the map uses background-size 100% 100%).
*/
// 6 hub cities, normalised to an equirectangular map: x=(lon+180)/360, y=(90-lat)/180.
// Kept to the right/centre so they sit in the content area (the left is the photo).
const NODES: [number, number][] = [
[0.5, 0.217], // London
[0.653, 0.361], // Dubai
[0.703, 0.394], // Mumbai
[0.836, 0.328], // Shanghai
[0.789, 0.493], // Singapore
[0.919, 0.689], // Sydney
];
interface Packet {
from: number;
to: number;
t: number;
speed: number;
}
const randNode = () => Math.floor(Math.random() * NODES.length);
function newPacket(): Packet {
let from = randNode();
let to = randNode();
while (to === from) to = randNode();
return { from, to, t: 0, speed: 0.003 + Math.random() * 0.003 };
}
export default function IndustryStackMap() {
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
const canvas = canvasRef.current;
const parent = canvas?.parentElement;
if (!canvas || !parent) return;
const ctx = canvas.getContext("2d");
if (!ctx) return;
const reduced = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
let w = 0;
let h = 0;
let packets: Packet[] = Array.from({ length: 4 }, newPacket);
let raf = 0;
let startTs = 0;
const resize = () => {
const rect = parent.getBoundingClientRect();
w = Math.max(1, rect.width);
h = Math.max(1, rect.height);
const dpr = Math.min(window.devicePixelRatio || 1, 2);
canvas.width = Math.round(w * dpr);
canvas.height = Math.round(h * dpr);
canvas.style.width = w + "px";
canvas.style.height = h + "px";
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
};
const nodePx = (i: number) => ({ x: NODES[i][0] * w, y: NODES[i][1] * h });
const ctrl = (p0: { x: number; y: number }, p1: { x: number; y: number }) => {
const mx = (p0.x + p1.x) / 2;
const my = (p0.y + p1.y) / 2;
const lift = Math.hypot(p1.x - p0.x, p1.y - p0.y) * 0.26;
return { x: mx, y: my - lift };
};
const bezier = (
p0: { x: number; y: number },
c: { x: number; y: number },
p1: { x: number; y: number },
t: number
) => {
const u = 1 - t;
return {
x: u * u * p0.x + 2 * u * t * c.x + t * t * p1.x,
y: u * u * p0.y + 2 * u * t * c.y + t * t * p1.y,
};
};
const draw = (time: number, advance: boolean) => {
ctx.clearRect(0, 0, w, h);
// Faint route arcs between every hub pair
ctx.save();
ctx.lineWidth = 1;
for (let a = 0; a < NODES.length; a++) {
for (let b = a + 1; b < NODES.length; b++) {
const p0 = nodePx(a);
const p1 = nodePx(b);
const c = ctrl(p0, p1);
ctx.strokeStyle = "rgba(220,38,38,0.10)";
ctx.beginPath();
ctx.moveTo(p0.x, p0.y);
ctx.quadraticCurveTo(c.x, c.y, p1.x, p1.y);
ctx.stroke();
}
}
ctx.restore();
// Delivery packets along Bézier arcs
for (const p of packets) {
const p0 = nodePx(p.from);
const p1 = nodePx(p.to);
const c = ctrl(p0, p1);
const pos = bezier(p0, c, p1, p.t);
// soft trail
const tt = Math.max(0, p.t - 0.06);
const prev = bezier(p0, c, p1, tt);
const grad = ctx.createLinearGradient(prev.x, prev.y, pos.x, pos.y);
grad.addColorStop(0, "rgba(239,68,68,0)");
grad.addColorStop(1, "rgba(239,68,68,0.55)");
ctx.strokeStyle = grad;
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(prev.x, prev.y);
ctx.lineTo(pos.x, pos.y);
ctx.stroke();
ctx.shadowColor = "#ef4444";
ctx.shadowBlur = 12;
ctx.fillStyle = "#ef4444";
ctx.beginPath();
ctx.arc(pos.x, pos.y, 3.5, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
if (advance) {
p.t += p.speed;
if (p.t >= 1) Object.assign(p, newPacket());
}
}
// City hub nodes — expanding pulse ring + solid centre dot
for (let i = 0; i < NODES.length; i++) {
const n = nodePx(i);
const phase = (Math.sin(time * 1.6 + i * 1.1) + 1) / 2; // 0..1
const radius = 3 + phase * 20;
const alpha = (1 - phase) * 0.5;
ctx.beginPath();
ctx.strokeStyle = `rgba(220,38,38,${alpha})`;
ctx.lineWidth = 1.5;
ctx.arc(n.x, n.y, radius, 0, Math.PI * 2);
ctx.stroke();
ctx.fillStyle = "#dc2626";
ctx.shadowColor = "#dc2626";
ctx.shadowBlur = 8;
ctx.beginPath();
ctx.arc(n.x, n.y, 2.5, 0, Math.PI * 2);
ctx.fill();
ctx.shadowBlur = 0;
}
};
const loop = (ts: number) => {
if (!startTs) startTs = ts;
draw((ts - startTs) / 1000, true);
raf = requestAnimationFrame(loop);
};
resize();
if (reduced) {
draw(0, false);
} else {
raf = requestAnimationFrame(loop);
}
const ro = new ResizeObserver(() => {
resize();
if (reduced) draw(0, false);
});
ro.observe(parent);
return () => {
cancelAnimationFrame(raf);
ro.disconnect();
};
}, []);
return <canvas ref={canvasRef} className="istk__canvas" aria-hidden="true" />;
}

View File

@@ -104,17 +104,46 @@ export default function MileTruthHero() {
max-width: 800px;
}
.miletruth-hero .slide-content {
display: flex;
align-items: center;
justify-content: center;
text-align: center;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
text-align: center !important;
width: 100% !important;
height: 100% !important;
padding: 0 40px !important;
box-sizing: border-box !important;
}
.miletruth-hero .slide-content-inner {
margin-left: auto;
margin-right: auto;
max-width: 1500px;
padding: 0 32px;
text-align: center;
display: flex !important;
flex-direction: column !important;
align-items: center !important;
justify-content: center !important;
text-align: center !important;
margin-left: auto !important;
margin-right: auto !important;
max-width: 1000px !important;
width: 100% !important;
}
.miletruth-hero .content-slider-item-heading,
.miletruth-hero .content-slider-item-heading .heading-content {
text-align: center !important;
display: block !important;
width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
}
.miletruth-hero .content-slider-item-text {
display: flex !important;
justify-content: center !important;
width: 100% !important;
margin-top: 23px !important;
text-align: center !important;
}
.miletruth-hero .text-content {
text-align: center !important;
max-width: 800px !important;
margin: 0 auto !important;
}
/* The "logico" slider reveal animation leaves these at opacity:0 until
its JS runs (absent in this rebuild), hiding the heading + subtitle.

View File

@@ -33,6 +33,29 @@ export default function OurTeam() {
return (
<>
<style dangerouslySetInnerHTML={{ __html: `
/* Box this section to the same inset width the sibling sections use.
The container's "display: var(--display)" resolves to the invalid
fallback (inline) here, so max-width was being ignored and the section
spanned edge-to-edge (heading clipped at the viewport edge). Force the
flex display back on and pin the inner to the shared 1480px inset. */
.elementor-86 .elementor-element-c2c601a {
display: flex;
}
.elementor-86 .elementor-element-c2c601a > .e-con-inner {
display: flex;
flex-direction: column;
max-width: 1480px;
width: 100%;
margin-left: auto;
margin-right: auto;
}
/* Breathing room above and below the "Meet our the best crew" heading. */
.elementor-86 .elementor-element-c46350e > .elementor-widget-container {
margin-top: 40px;
margin-bottom: 56px;
}
/* Team photos: grayscale by default, full colour on hover (matches design) */
.team-listing-wrapper.team-grid-listing .team-item .post-media img {
filter: grayscale(100%);
@@ -47,12 +70,12 @@ export default function OurTeam() {
.team-listing-wrapper.team-grid-listing {
display: flex;
flex-wrap: wrap;
margin: 0 -16px -44px;
margin: 0 -16px -40px;
}
.team-listing-wrapper.team-grid-listing .team-item-wrapper {
width: 33.3333%;
padding: 0 16px;
margin-bottom: 44px;
margin-bottom: 40px;
box-sizing: border-box;
}
@media (max-width: 1020px) {
@@ -67,14 +90,31 @@ export default function OurTeam() {
display: flex;
flex-direction: row;
align-items: center;
gap: 16px;
gap: 22px;
}
.team-listing-wrapper.team-grid-listing .team-item-media {
/* Compact portrait photo. Extra .team-item in the selector raises the
specificity so it beats the cached vendor rule (width: 45.65%). */
.team-listing-wrapper.team-grid-listing .team-item .team-item-media {
flex-shrink: 0;
width: 160px;
margin: 0;
}
.team-listing-wrapper.team-grid-listing .team-item-content {
.team-listing-wrapper.team-grid-listing .team-item .post-media {
height: 0;
padding-top: 120%;
border-radius: 16px;
overflow: hidden;
}
.team-listing-wrapper.team-grid-listing .team-item .post-media img {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
.team-listing-wrapper.team-grid-listing .team-item .team-item-content {
flex: 1;
margin: 0;
}
`}} />
<div className="elementor-element elementor-element-c2c601a e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent" data-id="c2c601a" data-element_type="container" data-e-type="container">
@@ -108,8 +148,8 @@ export default function OurTeam() {
src={member.image}
alt={member.name}
width={300}
height={300}
style={{ objectFit: "cover", width: "100%", height: "auto" }}
height={360}
style={{ objectFit: "cover", width: "100%", height: "100%" }}
/>
</a>
</div>

View File

@@ -35,6 +35,52 @@ export default function SolutionsHero() {
background-size: cover !important;
}
/* Center alignments for standard content slider elements */
.elementor-element.elementor-element-6c7cbcb .slide-content {
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
text-align: center !important;
width: 100% !important;
height: 100% !important;
padding: 0 40px !important;
box-sizing: border-box !important;
}
.elementor-element.elementor-element-6c7cbcb .slide-content-inner {
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
text-align: center !important;
width: 100% !important;
max-width: 1000px !important;
margin: 0 auto !important;
}
.elementor-element.elementor-element-6c7cbcb .content-slider-item-heading,
.elementor-element.elementor-element-6c7cbcb .content-slider-item-heading .heading-content {
text-align: center !important;
display: block !important;
width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
}
.elementor-element.elementor-element-6c7cbcb .content-slider-item-text {
display: flex !important;
justify-content: center !important;
width: 100% !important;
margin-top: 23px !important;
}
.elementor-element.elementor-element-6c7cbcb .text-content {
text-align: center !important;
max-width: 800px !important;
margin: 0 auto !important;
}
@media (min-width: 1025px) {
.elementor-element.elementor-element-6c7cbcb .owl-carousel.owl-theme .content-item {
height: 800px !important;

View File

@@ -171,14 +171,20 @@ export default function WomenSection() {
line-height: 1.6;
margin: 0;
font-weight: 900;
font-family: 'Manrope', sans-serif;
font-size: 16px;
}
.elementor-element-778840d .logico-title {
white-space: nowrap !important;
}
/* Stack the two columns below the desktop breakpoint so the box group
always gets full width and stays centered — never clipped. */
@media (max-width: 1024px) {
.elementor-element-778840d .logico-title {
white-space: normal !important;
}
.elementor-element.elementor-element-2ed47f3 { grid-template-columns: 1fr !important; }
#ws-stories { max-width: 640px; margin: 0 auto; }
}

View File

@@ -1,173 +1,224 @@
"use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
import OptimizationSection from "../optimization/OptimizationSection";
export default function Workflow1() {
const [activeSlide, setActiveSlide] = useState(0);
const slides = [
{
title: "Performance",
title: "PERFORMANCE",
text: "Our AI-powered routing system reduces unnecessary travel by selecting the most efficient delivery paths across the city. This helps lower fuel and battery consumption while improving delivery speed and operational efficiency. Businesses can complete more deliveries in less time with significantly reduced logistics costs."
},
{
title: "Performance", // original code had only title in the first slide, but let's keep title for consistency
title: "PERFORMANCE",
text: "The optimization engine intelligently groups and balances deliveries, allowing the same order volume to be fulfilled with fewer vehicles. This improves fleet utilization, reduces maintenance and staffing costs, and increases overall delivery efficiency. Even with fewer vehicles, the platform maintains smooth and reliable operations."
},
{
title: "Performance",
title: "PERFORMANCE",
text: "Real-time route optimization ensures predictable and on-time deliveries across all delivery zones. By reducing delays and improving route planning, businesses can maintain high customer satisfaction and strong SLA performance. The system delivers lower operational costs while consistently maintaining 100% order fulfillment."
}
];
return (
<>
<style dangerouslySetInnerHTML={{ __html: `
.miletruth-workflow-heading {
font-size: 24px;
font-weight: 700;
margin-bottom: 15px;
color: #1c1c1c;
}
.testimonials-slider-container {
position: relative;
}
.testimonial-item {
opacity: 0;
visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out;
}
.testimonial-item.active {
opacity: 1;
visibility: visible;
position: relative;
}
`}} />
<section className="dm-wf1" aria-label="Workflow 1 — Impact of Optimisation & Performance">
<div className="elementor-63">
<div className="elementor-element elementor-element-285c828 e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent" data-id="285c828" data-element_type="container" data-e-type="container">
{/* ── Top sub-section: the full interactive "Impact of Optimisation" experience ── */}
<OptimizationSection />
{/* Left Column: Image with overlay */}
<div className="elementor-element elementor-element-f3478fa e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="f3478fa" data-element_type="container" data-e-type="container" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}">
<div className="elementor-element elementor-element-e8ee5be elementor-widget elementor-widget-logico_image_carousel" data-id="e8ee5be" data-element_type="widget" data-e-type="widget" data-widget_type="logico_image_carousel.default">
<div className="elementor-widget-container">
<div className="logico-image-carousel-widget">
<div className="image-slider">
<div className="image-item slider-item">
<div className="image-item-card with-height">
<img
loading="lazy"
decoding="async"
width="1733"
height="773"
src="/images/miletruth-1.png"
className="attachment-full size-full wp-image-5026"
alt="Workflow 1"
/>
<div className="image-title">
<div className="elementor-element elementor-element-7500280 elementor-widget elementor-widget-logico_heading" data-id="7500280" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
<div className="elementor-widget-container" style={{ backgroundColor: "white", padding: "20px", borderRadius: "10px", opacity: 0.9 }}>
<h4 className="logico-title" style={{ color: "#C01227", margin: 0 }}>
The Impact of Optimisation
</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* ── Bottom sub-section: Performance content, flush + colour-matched to the
optimisation section above so the whole workflow reads as one container ── */}
<div className="dm-wf1-card">
{/* Left Column: Overlapping Chevron Graphic */}
<div className="dm-workflow-left">
<svg viewBox="0 0 320 280" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-svg">
<path
d="M 30,20 C 22,20 16,26 16,34 L 78,85 C 81,88 81,92 78,95 L 16,146 C 16,154 22,160 30,160 L 130,160 C 138,160 145,154 148,146 L 204,95 C 207,92 207,88 204,85 L 148,34 C 145,26 138,20 130,20 Z"
stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.25"
/>
<path
d="M 110,100 C 102,100 96,106 96,114 L 158,165 C 161,168 161,172 158,175 L 96,226 C 96,234 102,240 110,240 L 210,240 C 218,240 225,234 228,226 L 284,175 C 287,172 287,168 284,165 L 228,114 C 225,106 218,100 210,100 Z"
stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.85"
/>
</svg>
</div>
{/* Right Column: Quotes & Text Content */}
<div className="dm-workflow-right">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-quote">
<rect x="2" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
<rect x="16" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
</svg>
<h3 className="dm-workflow-title">{slides[activeSlide].title}</h3>
<div className="dm-workflow-text-container">
<AnimatePresence mode="wait">
<motion.p
key={activeSlide}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
transition={{ duration: 0.28, ease: "easeInOut" }}
className="dm-workflow-text"
>
{slides[activeSlide].text}
</motion.p>
</AnimatePresence>
</div>
{/* Right Column: Testimonial/Text Slider */}
<div className="elementor-element elementor-element-79ba100 e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-child" data-id="79ba100" data-element_type="container" data-e-type="container" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}">
<div className="e-con-inner">
<div className="elementor-element elementor-element-9c38369 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="9c38369" data-element_type="container" data-e-type="container">
{/* Secondary Image inside right col */}
<div className="elementor-element elementor-element-8f3f74d e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="8f3f74d" data-element_type="container" data-e-type="container">
<div className="elementor-element elementor-element-f5a66b2 elementor-widget elementor-widget-image" data-id="f5a66b2" data-element_type="widget" data-e-type="widget" data-widget_type="image.default">
<div className="elementor-widget-container">
<img
loading="lazy"
decoding="async"
width="491"
height="373"
src="/images/home2-pic-2.png"
className="attachment-full size-full wp-image-4396"
alt="Decorative pic"
/>
</div>
</div>
</div>
{/* Slider */}
<div className="elementor-element elementor-element-4071ec8 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="4071ec8" data-element_type="container" data-e-type="container">
<div className="elementor-element elementor-element-0a76e77 elementor-widget elementor-widget-logico_testimonial_carousel" data-id="0a76e77" data-element_type="widget" data-e-type="widget" data-widget_type="logico_testimonial_carousel.default">
<div className="elementor-widget-container">
<div className="logico-testimonial-carousel-widget">
<div className="testimonial-carousel-wrapper witch-icon">
<div className="testimonials-slider-container">
<div className="testimonials-slider">
{slides.map((slide, index) => (
<div
key={index}
className={`testimonial-item slider-item ${index === activeSlide ? "active" : ""}`}
>
<div className="testimonial-text">
<h4 className="miletruth-workflow-heading">{slide.title}</h4>
<p>{slide.text}</p>
</div>
</div>
))}
</div>
{/* Slider Navigation Footer */}
<div className="slider-footer slider-footer-view-outside slider-footer-position-after slider-footer-width-full miletruth-workflow-progress">
<div className="slider-footer-content" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<div className="slider-pagination" style={{ display: "flex", alignItems: "center", gap: "15px" }}>
<div className="slider-progress-wrapper">
<span className="slider-progress-current">0{activeSlide + 1}</span>
/
<span className="slider-progress-all">03</span>
</div>
<div className="owl-dots owl-dots-workflow-1" style={{ display: "flex", gap: "8px" }}>
{slides.map((_, index) => (
<button
key={index}
type="button"
role="button"
className={`owl-dot ${index === activeSlide ? "active" : ""}`}
onClick={() => setActiveSlide(index)}
style={{ width: "10px", height: "10px", borderRadius: "50%", padding: 0 }}
>
<span></span>
</button>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className="dm-workflow-nav">
<span className="dm-workflow-counter">0{activeSlide + 1}/03</span>
<div className="dm-workflow-bars">
{slides.map((_, index) => (
<button
key={index}
type="button"
aria-label={`Go to slide ${index + 1}`}
className={`dm-workflow-bar ${index === activeSlide ? "is-active" : ""}`}
onClick={() => setActiveSlide(index)}
/>
))}
</div>
</div>
</div>
</div>
</>
<style dangerouslySetInnerHTML={{ __html: styles }} />
</section>
);
}
const styles = `
/* ============================================================
Workflow 1 = ONE container:
├─ Impact of Optimisation (full interactive OptimizationSection)
└─ Performance (content card, flush + colour-matched)
The Performance card is pulled up to butt against the optimisation
card's flat bottom and shares its dark-navy surface, so the two
read as a single continuous container with no gap / no break.
============================================================ */
.dm-wf1 {
position: relative;
margin: 0 auto 24px;
}
/* Performance card — aligned to the optimisation card (40px side insets),
navy-matched, flat top, rounded bottom, pulled up to close the seam. */
.dm-wf1-card {
position: relative;
z-index: 2;
margin: -28px 40px 0;
background: linear-gradient(180deg, #030a18 0%, #06101f 100%);
border: 1px solid rgba(255, 255, 255, 0.05);
border-top: none;
border-radius: 0 0 35px 35px;
box-shadow: 0 10px 40px -10px rgba(0, 0, 0, 0.5);
padding: 48px 60px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 40px;
overflow: hidden;
box-sizing: border-box;
}
.dm-workflow-left {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
max-width: 440px;
}
.dm-workflow-svg {
width: 100%;
height: auto;
filter: drop-shadow(0 8px 24px rgba(0,0,0,0.3));
}
.dm-workflow-right {
flex: 1.2;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 20px;
}
.dm-workflow-quote { margin-bottom: 5px; }
.dm-workflow-title {
font-family: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif;
font-size: 38px;
font-weight: 700;
color: #F8FAFC !important;
letter-spacing: -0.015em;
margin: 0 !important;
padding: 0 !important;
text-transform: uppercase;
}
.dm-workflow-text-container { min-height: 110px; width: 100%; }
.dm-workflow-text {
font-family: var(--font-manrope), system-ui, sans-serif;
font-size: 16px;
line-height: 1.65;
color: #A3A3A3;
margin: 0 !important;
padding: 0 !important;
}
.dm-workflow-nav {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
align-self: flex-end;
margin-top: 10px;
}
.dm-workflow-counter {
font-family: var(--font-space-grotesk), sans-serif;
font-size: 13px;
font-weight: 700;
color: #737373;
letter-spacing: 0.08em;
}
.dm-workflow-bars { display: flex; gap: 8px; }
.dm-workflow-bar {
width: 40px;
height: 3px;
border: none;
padding: 0;
background: rgba(255, 255, 255, 0.15);
border-radius: 999px;
cursor: pointer;
transition: all 0.3s ease;
}
.dm-workflow-bar.is-active { background: #C01227; }
.dm-workflow-bar:hover { background: rgba(255, 255, 255, 0.35); }
.dm-workflow-bar.is-active:hover { background: #C01227; }
/* ── Responsive — keep insets/radius aligned to the optimisation card ── */
@media (max-width: 1024px) {
.dm-wf1-card {
margin: -20px 20px 0;
border-radius: 0 0 42px 42px;
padding: 44px 44px;
gap: 44px;
}
.dm-workflow-title { font-size: 32px; }
}
@media (max-width: 768px) {
.dm-wf1-card {
margin: -14px 10px 0;
border-radius: 0 0 28px 28px;
padding: 36px 28px;
gap: 36px;
flex-direction: column;
}
.dm-workflow-left { max-width: 280px; }
.dm-workflow-right { width: 100%; }
.dm-workflow-title { font-size: 28px; }
.dm-workflow-text-container { min-height: auto; }
}
`;

View File

@@ -22,79 +22,83 @@ export default function Workflow2() {
];
return (
<section className="dm-workflow-section" aria-label="Doormile Innovation Workflow">
<div className="dm-workflow-card">
{/* Left Column: Overlapping Chevron Graphic */}
<div className="dm-workflow-left">
<svg viewBox="0 0 320 280" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-svg">
{/* Top-Left Chevron Outline (Low Opacity) */}
<path
d="M 30,20 C 22,20 16,26 16,34 L 78,85 C 81,88 81,92 78,95 L 16,146 C 16,154 22,160 30,160 L 130,160 C 138,160 145,154 148,146 L 204,95 C 207,92 207,88 204,85 L 148,34 C 145,26 138,20 130,20 Z"
stroke="white"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
opacity="0.25"
/>
{/* Bottom-Right Chevron Outline (High Opacity) */}
<path
d="M 110,100 C 102,100 96,106 96,114 L 158,165 C 161,168 161,172 158,175 L 96,226 C 96,234 102,240 110,240 L 210,240 C 218,240 225,234 228,226 L 284,175 C 287,172 287,168 284,165 L 228,114 C 225,106 218,100 210,100 Z"
stroke="white"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
opacity="0.85"
/>
</svg>
<section className="dm-workflow" aria-label="Workflow 2 — Competitive Edge & Innovation">
<div className="dm-workflow__container">
{/* ── Top sub-section: Competitive Edge banner ── */}
<div className="dm-workflow-banner">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
className="dm-workflow-banner__img"
src="/images/miletruth-2.png"
alt="Our Competitive Edge"
width={1733}
height={773}
loading="lazy"
decoding="async"
/>
<div className="dm-workflow-banner__caption">
<span className="dm-workflow-banner__title-text">Our Competitive Edge</span>
</div>
</div>
{/* Right Column: Quotes & Text Content */}
<div className="dm-workflow-right">
{/* Slanted red quotes block */}
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-quote">
<rect x="2" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
<rect x="16" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
</svg>
{/* Heading */}
<h3 className="dm-workflow-title">
{slides[activeSlide].title}
</h3>
{/* Slide Paragraph with premium fade-in transition */}
<div className="dm-workflow-text-container">
<AnimatePresence mode="wait">
<motion.p
key={activeSlide}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
transition={{ duration: 0.28, ease: "easeInOut" }}
className="dm-workflow-text"
>
{slides[activeSlide].text}
</motion.p>
</AnimatePresence>
{/* ── Bottom sub-section: Innovation content ── */}
<div className="dm-workflow-card">
{/* Left Column: Overlapping Chevron Graphic */}
<div className="dm-workflow-left">
<svg viewBox="0 0 320 280" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-svg">
<path
d="M 30,20 C 22,20 16,26 16,34 L 78,85 C 81,88 81,92 78,95 L 16,146 C 16,154 22,160 30,160 L 130,160 C 138,160 145,154 148,146 L 204,95 C 207,92 207,88 204,85 L 148,34 C 145,26 138,20 130,20 Z"
stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.25"
/>
<path
d="M 110,100 C 102,100 96,106 96,114 L 158,165 C 161,168 161,172 158,175 L 96,226 C 96,234 102,240 110,240 L 210,240 C 218,240 225,234 228,226 L 284,175 C 287,172 287,168 284,165 L 228,114 C 225,106 218,100 210,100 Z"
stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.85"
/>
</svg>
</div>
{/* Right-aligned Navigation (Counter + Indication lines) */}
<div className="dm-workflow-nav">
<span className="dm-workflow-counter">
0{activeSlide + 1}/03
</span>
<div className="dm-workflow-bars">
{slides.map((_, index) => (
<button
key={index}
type="button"
aria-label={`Go to slide ${index + 1}`}
className={`dm-workflow-bar ${index === activeSlide ? "is-active" : ""}`}
onClick={() => setActiveSlide(index)}
/>
))}
{/* Right Column: Quotes & Text Content */}
<div className="dm-workflow-right">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-quote">
<rect x="2" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
<rect x="16" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
</svg>
<h3 className="dm-workflow-title">{slides[activeSlide].title}</h3>
<div className="dm-workflow-text-container">
<AnimatePresence mode="wait">
<motion.p
key={activeSlide}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
transition={{ duration: 0.28, ease: "easeInOut" }}
className="dm-workflow-text"
>
{slides[activeSlide].text}
</motion.p>
</AnimatePresence>
</div>
<div className="dm-workflow-nav">
<span className="dm-workflow-counter">0{activeSlide + 1}/03</span>
<div className="dm-workflow-bars">
{slides.map((_, index) => (
<button
key={index}
type="button"
aria-label={`Go to slide ${index + 1}`}
className={`dm-workflow-bar ${index === activeSlide ? "is-active" : ""}`}
onClick={() => setActiveSlide(index)}
/>
))}
</div>
</div>
</div>
</div>
</div>
<style dangerouslySetInnerHTML={{ __html: styles }} />
@@ -103,31 +107,77 @@ export default function Workflow2() {
}
const styles = `
.dm-workflow-section {
max-width: 1700px;
margin: 20px auto;
/* ============================================================
Workflow = ONE container: image-title banner (top) flush with
the dark content card (bottom). Single overflow:hidden wrapper
rounds the whole stack — no gap, no separate backgrounds.
============================================================ */
.dm-workflow {
max-width: 100%;
margin: 24px auto;
padding: 0 40px;
box-sizing: border-box;
}
.dm-workflow__container {
position: relative;
border-radius: 40px;
overflow: hidden;
background: #181818;
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: 0 14px 50px -16px rgba(0, 0, 0, 0.55);
}
/* ── Banner (top) ── */
.dm-workflow-banner {
position: relative;
display: block;
width: 100%;
font-size: 0;
line-height: 0;
}
.dm-workflow-banner__img {
display: block;
width: 100%;
height: auto;
max-height: 380px;
object-fit: cover;
object-position: center;
}
.dm-workflow-banner__caption {
position: absolute;
left: 40px;
bottom: 32px;
background: rgba(255, 255, 255, 0.92);
padding: 16px 26px;
border-radius: 12px;
box-shadow: 0 10px 28px -10px rgba(0, 0, 0, 0.45);
line-height: normal;
}
.dm-workflow-banner__title-text {
display: block;
color: #C01227 !important;
font-family: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif !important;
font-size: clamp(20px, 2.4vw, 34px) !important;
font-weight: 700 !important;
line-height: 1.1 !important;
letter-spacing: -0.01em;
text-transform: none !important;
}
/* ── Content card (bottom), flush under the banner ── */
.dm-workflow-card {
position: relative;
background: #181818;
border-radius: 50px;
border: 1px solid rgba(255, 255, 255, 0.05);
box-shadow:
0 10px 40px -10px rgba(0, 0, 0, 0.5),
inset 0 1px 0 rgba(255, 255, 255, 0.05);
padding: 40px 60px;
border-top: 1px solid rgba(255, 255, 255, 0.06);
padding: 48px 60px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 40px;
overflow: hidden;
box-sizing: border-box;
}
/* Left Column - Graphic */
.dm-workflow-left {
flex: 1;
display: flex;
@@ -135,14 +185,12 @@ const styles = `
justify-content: center;
max-width: 440px;
}
.dm-workflow-svg {
width: 100%;
height: auto;
filter: drop-shadow(0 8px 24px rgba(0,0,0,0.3));
}
/* Right Column - Text & Slide Content */
.dm-workflow-right {
flex: 1.2;
display: flex;
@@ -150,10 +198,7 @@ const styles = `
align-items: flex-start;
gap: 20px;
}
.dm-workflow-quote {
margin-bottom: 5px;
}
.dm-workflow-quote { margin-bottom: 5px; }
.dm-workflow-title {
font-family: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif;
@@ -166,11 +211,7 @@ const styles = `
text-transform: uppercase;
}
.dm-workflow-text-container {
min-height: 110px; /* Prevent layout shift when swapping slide texts */
width: 100%;
}
.dm-workflow-text-container { min-height: 110px; width: 100%; }
.dm-workflow-text {
font-family: var(--font-manrope), system-ui, sans-serif;
font-size: 16px;
@@ -180,7 +221,6 @@ const styles = `
padding: 0 !important;
}
/* Navigation footer */
.dm-workflow-nav {
display: flex;
flex-direction: column;
@@ -189,7 +229,6 @@ const styles = `
align-self: flex-end;
margin-top: 10px;
}
.dm-workflow-counter {
font-family: var(--font-space-grotesk), sans-serif;
font-size: 13px;
@@ -197,12 +236,7 @@ const styles = `
color: #737373;
letter-spacing: 0.08em;
}
.dm-workflow-bars {
display: flex;
gap: 8px;
}
.dm-workflow-bars { display: flex; gap: 8px; }
.dm-workflow-bar {
width: 40px;
height: 3px;
@@ -213,49 +247,32 @@ const styles = `
cursor: pointer;
transition: all 0.3s ease;
}
.dm-workflow-bar.is-active { background: #C01227; }
.dm-workflow-bar:hover { background: rgba(255, 255, 255, 0.35); }
.dm-workflow-bar.is-active:hover { background: #C01227; }
.dm-workflow-bar.is-active {
background: #C01227;
}
.dm-workflow-bar:hover {
background: rgba(255, 255, 255, 0.35);
}
.dm-workflow-bar.is-active:hover {
background: #C01227;
}
/* Responsive design */
/* ── Responsive ── */
@media (max-width: 1024px) {
.dm-workflow-card {
padding: 50px 50px;
gap: 50px;
border-radius: 40px;
}
.dm-workflow-title {
font-size: 32px;
}
.dm-workflow__container { border-radius: 32px; }
.dm-workflow-banner__img { max-height: 300px; }
.dm-workflow-banner__caption { left: 28px; bottom: 24px; padding: 13px 20px; }
.dm-workflow-card { padding: 44px 44px; gap: 44px; }
.dm-workflow-title { font-size: 32px; }
}
@media (max-width: 768px) {
.dm-workflow { padding: 0 16px; }
.dm-workflow__container { border-radius: 24px; }
.dm-workflow-banner__img { max-height: 220px; }
.dm-workflow-banner__caption { left: 16px; bottom: 16px; padding: 11px 16px; }
.dm-workflow-card {
flex-direction: column;
padding: 40px 30px;
gap: 40px;
border-radius: 30px;
}
.dm-workflow-left {
max-width: 280px;
}
.dm-workflow-right {
width: 100%;
}
.dm-workflow-title {
font-size: 28px;
}
.dm-workflow-text-container {
min-height: auto;
padding: 36px 28px;
gap: 36px;
}
.dm-workflow-left { max-width: 280px; }
.dm-workflow-right { width: 100%; }
.dm-workflow-title { font-size: 28px; }
.dm-workflow-text-container { min-height: auto; }
}
`;

View File

@@ -1,173 +1,278 @@
"use client";
import React, { useState } from "react";
import { motion, AnimatePresence } from "framer-motion";
export default function Workflow3() {
const [activeSlide, setActiveSlide] = useState(0);
const slides = [
{
title: "Strategy",
title: "STRATEGY",
text: "Our grading engine continuously evaluates fulfillment performance, SLA compliance, and route efficiency before every dispatch. By comparing legacy routing methods with unified optimization, the system ensures smarter and more reliable delivery planning. This helps businesses maintain operational consistency while improving overall delivery performance."
},
{
title: "Strategy",
title: "STRATEGY",
text: "Every EV route is pre-validated against real battery capacity and charging feasibility before a rider leaves the hub. This reduces the risk of delivery interruptions, charging failures, or delayed orders during operations. The platform ensures reliable route execution while maximizing EV fleet efficiency and rider confidence."
},
{
title: "Strategy",
title: "STRATEGY",
text: "The system provides actionable fleet insights and optimized workload distribution to improve both rider experience and operational productivity. Balanced route allocation helps reduce rider fatigue, improve retention, and maintain consistent delivery quality across zones. Managers gain better visibility into fleet performance, enabling faster and more informed decision-making."
}
];
return (
<>
<style dangerouslySetInnerHTML={{ __html: `
.miletruth-workflow-heading {
font-size: 24px;
font-weight: 700;
margin-bottom: 15px;
color: #1c1c1c;
}
.testimonials-slider-container {
position: relative;
}
.testimonial-item {
opacity: 0;
visibility: hidden;
position: absolute;
top: 0;
left: 0;
width: 100%;
transition: opacity 0.5s ease-in-out, visibility 0.5s ease-in-out;
}
.testimonial-item.active {
opacity: 1;
visibility: visible;
position: relative;
}
`}} />
<section className="dm-workflow" aria-label="Workflow 3 — Happier Riders & Strategy">
<div className="dm-workflow__container">
<div className="elementor-63">
<div className="elementor-element elementor-element-285c828 e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent" data-id="285c828" data-element_type="container" data-e-type="container">
{/* Left Column: Image with overlay */}
<div className="elementor-element elementor-element-f3478fa e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="f3478fa" data-element_type="container" data-e-type="container" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}">
<div className="elementor-element elementor-element-e8ee5be elementor-widget elementor-widget-logico_image_carousel" data-id="e8ee5be" data-element_type="widget" data-e-type="widget" data-widget_type="logico_image_carousel.default">
<div className="elementor-widget-container">
<div className="logico-image-carousel-widget">
<div className="image-slider">
<div className="image-item slider-item">
<div className="image-item-card with-height">
<img
loading="lazy"
decoding="async"
width="1733"
height="773"
src="/images/miletruth-3.png"
className="attachment-full size-full wp-image-5026"
alt="Workflow 3"
/>
<div className="image-title">
<div className="elementor-element elementor-element-7500280 elementor-widget elementor-widget-logico_heading" data-id="7500280" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
<div className="elementor-widget-container" style={{ backgroundColor: "white", padding: "20px", borderRadius: "10px", opacity: 0.9 }}>
<h4 className="logico-title" style={{ color: "#C01227", margin: 0 }}>
Happier Riders. Higher Fulfillment
</h4>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* ── Top sub-section: Happier Riders banner ── */}
<div className="dm-workflow-banner">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
className="dm-workflow-banner__img"
src="/images/miletruth-3.png"
alt="Happier Riders. Higher Fulfillment"
width={1733}
height={773}
loading="lazy"
decoding="async"
/>
<div className="dm-workflow-banner__caption">
<span className="dm-workflow-banner__title-text">Happier Riders. Higher Fulfillment</span>
</div>
{/* Right Column: Testimonial/Text Slider */}
<div className="elementor-element elementor-element-79ba100 e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-child" data-id="79ba100" data-element_type="container" data-e-type="container" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}">
<div className="e-con-inner">
<div className="elementor-element elementor-element-9c38369 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="9c38369" data-element_type="container" data-e-type="container">
{/* Secondary Image inside right col */}
<div className="elementor-element elementor-element-8f3f74d e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="8f3f74d" data-element_type="container" data-e-type="container">
<div className="elementor-element elementor-element-f5a66b2 elementor-widget elementor-widget-image" data-id="f5a66b2" data-element_type="widget" data-e-type="widget" data-widget_type="image.default">
<div className="elementor-widget-container">
<img
loading="lazy"
decoding="async"
width="491"
height="373"
src="/images/home2-pic-2.png"
className="attachment-full size-full wp-image-4396"
alt="Decorative pic"
/>
</div>
</div>
</div>
{/* Slider */}
<div className="elementor-element elementor-element-4071ec8 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="4071ec8" data-element_type="container" data-e-type="container">
<div className="elementor-element elementor-element-0a76e77 elementor-widget elementor-widget-logico_testimonial_carousel" data-id="0a76e77" data-element_type="widget" data-e-type="widget" data-widget_type="logico_testimonial_carousel.default">
<div className="elementor-widget-container">
<div className="logico-testimonial-carousel-widget">
<div className="testimonial-carousel-wrapper witch-icon">
<div className="testimonials-slider-container">
<div className="testimonials-slider">
{slides.map((slide, index) => (
<div
key={index}
className={`testimonial-item slider-item ${index === activeSlide ? "active" : ""}`}
>
<div className="testimonial-text">
<h4 className="miletruth-workflow-heading">{slide.title}</h4>
<p>{slide.text}</p>
</div>
</div>
))}
</div>
{/* Slider Navigation Footer */}
<div className="slider-footer slider-footer-view-outside slider-footer-position-after slider-footer-width-full miletruth-workflow-progress">
<div className="slider-footer-content" style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
<div className="slider-pagination" style={{ display: "flex", alignItems: "center", gap: "15px" }}>
<div className="slider-progress-wrapper">
<span className="slider-progress-current">0{activeSlide + 1}</span>
/
<span className="slider-progress-all">03</span>
</div>
<div className="owl-dots owl-dots-workflow-3" style={{ display: "flex", gap: "8px" }}>
{slides.map((_, index) => (
<button
key={index}
type="button"
role="button"
className={`owl-dot ${index === activeSlide ? "active" : ""}`}
onClick={() => setActiveSlide(index)}
style={{ width: "10px", height: "10px", borderRadius: "50%", padding: 0 }}
>
<span></span>
</button>
))}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{/* ── Bottom sub-section: Strategy content ── */}
<div className="dm-workflow-card">
{/* Left Column: Overlapping Chevron Graphic */}
<div className="dm-workflow-left">
<svg viewBox="0 0 320 280" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-svg">
<path
d="M 30,20 C 22,20 16,26 16,34 L 78,85 C 81,88 81,92 78,95 L 16,146 C 16,154 22,160 30,160 L 130,160 C 138,160 145,154 148,146 L 204,95 C 207,92 207,88 204,85 L 148,34 C 145,26 138,20 130,20 Z"
stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.25"
/>
<path
d="M 110,100 C 102,100 96,106 96,114 L 158,165 C 161,168 161,172 158,175 L 96,226 C 96,234 102,240 110,240 L 210,240 C 218,240 225,234 228,226 L 284,175 C 287,172 287,168 284,165 L 228,114 C 225,106 218,100 210,100 Z"
stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.85"
/>
</svg>
</div>
{/* Right Column: Quotes & Text Content */}
<div className="dm-workflow-right">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-quote">
<rect x="2" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
<rect x="16" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
</svg>
<h3 className="dm-workflow-title">{slides[activeSlide].title}</h3>
<div className="dm-workflow-text-container">
<AnimatePresence mode="wait">
<motion.p
key={activeSlide}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
transition={{ duration: 0.28, ease: "easeInOut" }}
className="dm-workflow-text"
>
{slides[activeSlide].text}
</motion.p>
</AnimatePresence>
</div>
<div className="dm-workflow-nav">
<span className="dm-workflow-counter">0{activeSlide + 1}/03</span>
<div className="dm-workflow-bars">
{slides.map((_, index) => (
<button
key={index}
type="button"
aria-label={`Go to slide ${index + 1}`}
className={`dm-workflow-bar ${index === activeSlide ? "is-active" : ""}`}
onClick={() => setActiveSlide(index)}
/>
))}
</div>
</div>
</div>
</div>
</div>
</>
<style dangerouslySetInnerHTML={{ __html: styles }} />
</section>
);
}
const styles = `
/* ============================================================
Workflow = ONE container: image-title banner (top) flush with
the dark content card (bottom). Single overflow:hidden wrapper
rounds the whole stack — no gap, no separate backgrounds.
============================================================ */
.dm-workflow {
max-width: 100%;
margin: 24px auto;
padding: 0 40px;
box-sizing: border-box;
}
.dm-workflow__container {
position: relative;
border-radius: 40px;
overflow: hidden;
background: #181818;
border: 1px solid rgba(255, 255, 255, 0.06);
box-shadow: 0 14px 50px -16px rgba(0, 0, 0, 0.55);
}
/* ── Banner (top) ── */
.dm-workflow-banner {
position: relative;
display: block;
width: 100%;
font-size: 0;
line-height: 0;
}
.dm-workflow-banner__img {
display: block;
width: 100%;
height: auto;
max-height: 380px;
object-fit: cover;
object-position: center;
}
.dm-workflow-banner__caption {
position: absolute;
left: 40px;
bottom: 32px;
background: rgba(255, 255, 255, 0.92);
padding: 16px 26px;
border-radius: 12px;
box-shadow: 0 10px 28px -10px rgba(0, 0, 0, 0.45);
line-height: normal;
}
.dm-workflow-banner__title-text {
display: block;
color: #C01227 !important;
font-family: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif !important;
font-size: clamp(20px, 2.4vw, 34px) !important;
font-weight: 700 !important;
line-height: 1.1 !important;
letter-spacing: -0.01em;
text-transform: none !important;
}
/* ── Content card (bottom), flush under the banner ── */
.dm-workflow-card {
position: relative;
background: #181818;
border-top: 1px solid rgba(255, 255, 255, 0.06);
padding: 48px 60px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 40px;
box-sizing: border-box;
}
.dm-workflow-left {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
max-width: 440px;
}
.dm-workflow-svg {
width: 100%;
height: auto;
filter: drop-shadow(0 8px 24px rgba(0,0,0,0.3));
}
.dm-workflow-right {
flex: 1.2;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 20px;
}
.dm-workflow-quote { margin-bottom: 5px; }
.dm-workflow-title {
font-family: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif;
font-size: 38px;
font-weight: 700;
color: #F8FAFC !important;
letter-spacing: -0.015em;
margin: 0 !important;
padding: 0 !important;
text-transform: uppercase;
}
.dm-workflow-text-container { min-height: 110px; width: 100%; }
.dm-workflow-text {
font-family: var(--font-manrope), system-ui, sans-serif;
font-size: 16px;
line-height: 1.65;
color: #A3A3A3;
margin: 0 !important;
padding: 0 !important;
}
.dm-workflow-nav {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
align-self: flex-end;
margin-top: 10px;
}
.dm-workflow-counter {
font-family: var(--font-space-grotesk), sans-serif;
font-size: 13px;
font-weight: 700;
color: #737373;
letter-spacing: 0.08em;
}
.dm-workflow-bars { display: flex; gap: 8px; }
.dm-workflow-bar {
width: 40px;
height: 3px;
border: none;
padding: 0;
background: rgba(255, 255, 255, 0.15);
border-radius: 999px;
cursor: pointer;
transition: all 0.3s ease;
}
.dm-workflow-bar.is-active { background: #C01227; }
.dm-workflow-bar:hover { background: rgba(255, 255, 255, 0.35); }
.dm-workflow-bar.is-active:hover { background: #C01227; }
/* ── Responsive ── */
@media (max-width: 1024px) {
.dm-workflow__container { border-radius: 32px; }
.dm-workflow-banner__img { max-height: 300px; }
.dm-workflow-banner__caption { left: 28px; bottom: 24px; padding: 13px 20px; }
.dm-workflow-card { padding: 44px 44px; gap: 44px; }
.dm-workflow-title { font-size: 32px; }
}
@media (max-width: 768px) {
.dm-workflow { padding: 0 16px; }
.dm-workflow__container { border-radius: 24px; }
.dm-workflow-banner__img { max-height: 220px; }
.dm-workflow-banner__caption { left: 16px; bottom: 16px; padding: 11px 16px; }
.dm-workflow-card {
flex-direction: column;
padding: 36px 28px;
gap: 36px;
}
.dm-workflow-left { max-width: 280px; }
.dm-workflow-right { width: 100%; }
.dm-workflow-title { font-size: 28px; }
.dm-workflow-text-container { min-height: auto; }
}
`;