1 Commits

Author SHA1 Message Date
71875ea20f cleanup the repo unused images 2026-07-03 16:35:42 +05:30
139 changed files with 262 additions and 7536 deletions

3
.gitignore vendored
View File

@@ -42,3 +42,6 @@ next-env.d.ts
# Local backups of pre-optimized GLB source models (not served)
.glb-originals/
# Scraped demo assets
demo.artureanec.com/

View File

@@ -2,19 +2,10 @@ import type { NextConfig } from "next";
import path from "path";
const nextConfig: NextConfig = {
async redirects() {
return [
{
source: "/air-freight",
destination: "/doormile-wings",
permanent: true,
},
];
},
turbopack: {
root: path.resolve(__dirname),
},
output: "export",
// output: "export" removed to allow server actions
// Required by the How It Works 3D experience. React StrictMode double-invokes
// mount/effects in dev, which tears down and re-creates the WebGL context of
// the heavy 32MB scene mid-initialization — the context is lost ("THREE.
@@ -24,7 +15,6 @@ const nextConfig: NextConfig = {
// any other page's runtime behavior.
reactStrictMode: false,
images: {
unoptimized: true,
formats: ["image/avif", "image/webp"],
},
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 532 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 264 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 376 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 999 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 182 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 136 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 546 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

49
scripts/fix_preload.mjs Normal file
View File

@@ -0,0 +1,49 @@
import fs from 'fs';
import path from 'path';
const files = [
"src/components/sections/BlogsHero.tsx",
"src/components/sections/AboutHero.tsx",
"src/components/sections/SingleBlog.tsx",
"src/components/sections/ContactsHero.tsx",
"src/components/sections/IndexHero.tsx",
"src/components/sections/SolutionsHero.tsx",
"src/components/sections/MileTruthHero.tsx",
"src/components/sections/HowItWorksHero.tsx"
];
for (const f of files) {
let content = fs.readFileSync(f, 'utf8');
const match = content.match(/<link\s+rel="preload"\s+as="image"\s+href="([^"]+)"\s*\/>/);
if (match) {
const href = match[1];
if (!content.includes('import { preload } from "react-dom"')) {
const lastImportIndex = content.lastIndexOf('import ');
const endOfLastImport = content.indexOf('\n', lastImportIndex);
content = content.substring(0, endOfLastImport + 1) + 'import { preload } from "react-dom";\n' + content.substring(endOfLastImport + 1);
}
content = content.replace(match[0], '');
// Find the default export function
const fnMatch = content.match(/export default function\s+\w+\s*\([^)]*\)\s*\{/);
if (fnMatch) {
const insertIdx = fnMatch.index + fnMatch[0].length;
content = content.substring(0, insertIdx) + `\n preload("${href}", { as: "image" });` + content.substring(insertIdx);
} else {
// try non-default export
const fnMatch2 = content.match(/export function\s+\w+\s*\([^)]*\)\s*\{/);
if (fnMatch2) {
const insertIdx = fnMatch2.index + fnMatch2[0].length;
content = content.substring(0, insertIdx) + `\n preload("${href}", { as: "image" });` + content.substring(insertIdx);
} else {
console.log("Could not find function for " + f);
}
}
fs.writeFileSync(f, content, 'utf8');
console.log("Fixed " + f);
}
}

View File

@@ -2,52 +2,61 @@
import nodemailer from "nodemailer";
export async function sendContactEmail(data: {
interface ContactFormData {
fullName: string;
email: string;
subject: string;
message: string;
}) {
}
export async function sendEmailAction(data: ContactFormData) {
const { fullName, email, subject, message } = data;
if (!fullName || !email || !subject || !message) {
return { success: false, error: "All fields are required." };
if (!fullName || !email || !message) {
return { success: false, error: "Name, email, and message are required." };
}
let password = process.env.SMTP_PASSWORD || "";
if (password.startsWith('"') && password.endsWith('"')) {
password = password.slice(1, -1);
}
// Remove spaces just in case
password = password.replace(/\s+/g, "");
const transporter = nodemailer.createTransport({
host: process.env.SMTP_HOST,
port: Number(process.env.SMTP_PORT) || 465,
secure: true,
secure: true,
auth: {
user: process.env.SMTP_USER,
pass: process.env.SMTP_PASSWORD,
pass: password,
},
});
try {
await transporter.sendMail({
from: `"${fullName}" <${process.env.SMTP_USER}>`,
to: process.env.CONTACT_RECEIVER,
replyTo: email,
subject: `New Contact Form Submission: ${subject}`,
from: `"${fullName}" <${process.env.SMTP_USER}>`,
to: process.env.CONTACT_RECEIVER,
replyTo: email,
subject: `New Contact Form Submission: ${subject || "No Subject"}`,
html: `
<div style="font-family: sans-serif; color: #333; max-width: 600px; margin: 0 auto; border: 1px solid #eee; padding: 20px; border-radius: 8px;">
<h2 style="color: #c01227; border-bottom: 2px solid #c01227; padding-bottom: 10px; margin-top: 0;">New Contact Form Submission</h2>
<div style="font-family: sans-serif; color: #333;">
<h2>New Contact Form Submission</h2>
<p><strong>Name:</strong> ${fullName}</p>
<p><strong>Email:</strong> ${email}</p>
<p><strong>Subject:</strong> ${subject}</p>
<p><strong>Message:</strong></p>
<blockquote style="background: #f9f9f9; padding: 15px; border-left: 4px solid #c01227; margin: 0; font-style: italic;">
${message.replace(/\n/g, "<br />")}
<blockquote style="background: #f9f9f9; padding: 15px; border-left: 4px solid #ccc;">
${message.replace(/\n/g, "<br>")}
</blockquote>
</div>
`,
});
return { success: true };
} catch (error) {
} catch (error: any) {
console.error("Email send error:", error);
const errorMessage = error instanceof Error ? error.message : "Failed to send the email. Please try again later.";
return { success: false, error: errorMessage };
const errorMessage = error instanceof Error ? error.message : String(error);
return { success: false, error: errorMessage || "Failed to send the email. Please try again later." };
}
}

View File

@@ -22,7 +22,7 @@ export default function AboutUsPage() {
<AboutHero />
<TheDoormileWay />
<EVLogisticSection />
{/* <WomenSection /> */}
<WomenSection />
<CompetitiveEdge />
<IntelligenceGrid />
<OurTeam />

View File

@@ -1,15 +0,0 @@
import { permanentRedirect } from "next/navigation";
export const metadata = {
alternates: {
canonical: "https://doormile.com/doormile-wings",
},
robots: {
index: false,
follow: true,
},
};
export default function AirFreightRedirectPage() {
permanentRedirect("/doormile-wings");
}

View File

@@ -1,62 +0,0 @@
import WingsHero from "@/components/sections/WingsHero";
import WingsWhyGrid from "@/components/sections/WingsWhyGrid";
import WingsSpeedAdvantage from "@/components/sections/WingsSpeedAdvantage";
import WingsFeatureGrid from "@/components/sections/WingsFeatureGrid";
import WingsIndustries from "@/components/sections/WingsIndustries";
import WingsHowItWorks from "@/components/sections/WingsHowItWorks";
import WingsFleetStrategy from "@/components/sections/WingsFleetStrategy";
import WingsInfrastructure from "@/components/sections/WingsInfrastructure";
import WingsMileTruth from "@/components/sections/WingsMileTruth";
import WingsRoadmap from "@/components/sections/WingsRoadmap";
export const metadata = {
title: "Doormile Wings Redefining Domestic Air Logistics Through AI",
description: "Doormile Wings links India's major regional capitals with AI-powered, same-day air logistics and zero-emission EV last-mile delivery — where quick commerce meets quick logistics.",
alternates: {
canonical: "https://doormile.com/doormile-wings",
},
openGraph: {
title: "Doormile Wings Redefining Domestic Air Logistics Through AI",
description: "Same-day air logistics linking India's major regional capitals, powered by MileTruth™ AI and zero-emission EV last-mile delivery.",
url: "https://doormile.com/doormile-wings",
siteName: "Doormile",
type: "website",
images: [
{
url: "https://doormile.com/images/wings-logo.png",
width: 1200,
height: 630,
alt: "Doormile Wings",
},
],
},
twitter: {
card: "summary_large_image",
title: "Doormile Wings Redefining Domestic Air Logistics Through AI",
description: "Same-day air logistics linking India's major regional capitals, powered by MileTruth™ AI and zero-emission EV last-mile delivery.",
images: ["https://doormile.com/images/wings-logo.png"],
},
};
export default function DoormileWingsPage() {
return (
<div id="doormile-wings" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content">
<div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="95" className="elementor elementor-95 elementor-59">
<WingsHero />
<WingsWhyGrid />
<WingsSpeedAdvantage />
<WingsFeatureGrid />
<WingsIndustries />
<WingsHowItWorks />
<WingsFleetStrategy />
<WingsInfrastructure />
<WingsMileTruth />
<WingsRoadmap />
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,123 +0,0 @@
import EmpowermentHero from "@/components/sections/EmpowermentHero";
import EmpowermentVision from "@/components/sections/EmpowermentVision";
import EmpowermentStory from "@/components/sections/EmpowermentStory";
import WomenSection from "@/components/sections/WomenSection";
import EmpowermentPrograms from "@/components/sections/EmpowermentPrograms";
import EmpowermentImpact from "@/components/sections/EmpowermentImpact";
import EmpowermentHowItWorks from "@/components/sections/EmpowermentHowItWorks";
import EmpowermentStories from "@/components/sections/EmpowermentStories";
import EmpowermentPartners from "@/components/sections/EmpowermentPartners";
import EmpowermentCTA from "@/components/sections/EmpowermentCTA";
const WINGS_WOMEN_STATS = [
{ value: "500", plus: "+", label: "Women Founders" },
{ value: "35", plus: "+", label: "Cities Active" },
{ value: "100", plus: "+", label: "Enterprises Launched" },
];
const WINGS_WOMEN_CARDS = [
{
title: "Venture Building",
desc: "End-to-end support for women launching logistics enterprises — from ideation to market entry.",
icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<path d="M4 19V5" />
<path d="M4 7h12l-2 4 2 4H4" />
<path d="M18 19h2" />
</svg>
),
},
{
title: "Entrepreneurship",
desc: "Women building, owning, and scaling sustainable businesses within the logistics ecosystem.",
icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<rect x="3" y="7" width="18" height="13" rx="2" />
<path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M3 12h18" />
</svg>
),
},
{
title: "Founder Networks",
desc: "Peer communities and strategic connections that accelerate growth and open new markets.",
icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<circle cx="8" cy="8" r="3" />
<circle cx="16" cy="8" r="3" />
<path d="M2.5 20c.7-3.1 2.8-5 5.5-5s4.8 1.9 5.5 5" />
<path d="M10.5 20c.7-3.1 2.8-5 5.5-5s4.8 1.9 5.5 5" />
</svg>
),
},
{
title: "Enterprise Leadership",
desc: "Strategic decision-making, operational excellence, and vision-driven leadership for women founders.",
icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="9" r="5" />
<path d="M9 13.5 7.5 21 12 18l4.5 3-1.5-7.5" />
</svg>
),
},
];
export const metadata = {
title: "Doormile Wings — Women Entrepreneurship Initiative",
description: "Doormile Wings empowers women to build, own, and lead enterprises within the logistics ecosystem through strategic partnerships, founder networks, and entrepreneurial infrastructure.",
alternates: {
canonical: "https://doormile.com/empowerment",
},
openGraph: {
title: "Doormile Wings — Women Entrepreneurship Initiative",
description: "An entrepreneurial ecosystem empowering women to launch, grow, and lead logistics enterprises with strategic partnerships and industry collaboration.",
url: "https://doormile.com/empowerment",
siteName: "Doormile",
type: "website",
images: [
{
url: "https://doormile.com/images/wings-logo.png",
width: 1200,
height: 630,
alt: "Doormile Wings — Women Entrepreneurship Initiative",
},
],
},
twitter: {
card: "summary_large_image",
title: "Doormile Wings — Women Entrepreneurship Initiative",
description: "An entrepreneurial ecosystem empowering women to launch, grow, and lead logistics enterprises with strategic partnerships and industry collaboration.",
images: ["https://doormile.com/images/wings-logo.png"],
},
};
export default function EmpowermentPage() {
return (
<div id="empowerment" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content">
<div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="86" className="elementor elementor-86 elementor-59 elementor-empowerment">
<EmpowermentHero />
<EmpowermentVision />
<EmpowermentStory />
<WomenSection
eyebrow="/ Doormile Wings /"
bannerLabel="Women Entrepreneurship"
bannerCopy="An ecosystem where women build, own, and scale enterprises across logistics."
ctaLabel="Explore the ecosystem"
ctaHref="#wings-empowerment-programs"
heading="Women Building The Next Mile"
stats={WINGS_WOMEN_STATS}
cards={WINGS_WOMEN_CARDS}
/>
<EmpowermentPrograms />
<EmpowermentImpact />
<EmpowermentHowItWorks />
<EmpowermentStories />
<EmpowermentPartners />
<EmpowermentCTA />
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,133 +0,0 @@
import type { Metadata } from "next";
import Link from "next/link";
export const metadata: Metadata = {
title: "Express Doormile",
description: "Doormile Express — a fast-lane logistics service for time-critical shipments. Coming soon.",
alternates: {
canonical: "https://doormile.com/express",
},
robots: {
index: false,
follow: true,
},
};
export default function ExpressPage() {
return (
<div id="express" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content">
<div className="content-inner">
<section className="express-coming-soon">
<div className="express-coming-soon-inner">
<span className="express-coming-soon-eyebrow">Solutions / Express</span>
<h1 className="express-coming-soon-heading">
Doormile Express is <span className="express-coming-soon-accent">on its way</span>
</h1>
<p className="express-coming-soon-text">
We&apos;re building a fast-lane logistics service for time-critical shipments.
Check back soon, or explore what Doormile already delivers today.
</p>
<div className="express-coming-soon-actions">
<Link href="/doormile-wings" prefetch className="logico-alter-button">
Explore Doormile Wings
</Link>
<Link href="/contact" className="express-coming-soon-link">
Get notified when we launch
</Link>
</div>
</div>
</section>
</div>
</div>
{/* Self-contained placeholder styling, scoped to .express-coming-soon.
Re-declares --logico-dark-text-color/--logico-light-text-color/
--logico-accent-color locally: the sitewide :root fallback is the
light-theme value (dark text on white), which would render near-
black text on this dark section without the override. */}
<style
suppressHydrationWarning
dangerouslySetInnerHTML={{
__html: `
.express-coming-soon {
--logico-dark-text-color: #ffffff;
--logico-light-text-color: #bebebe;
--logico-accent-color: #C01227;
min-height: calc(100vh - 160px);
display: flex;
align-items: center;
justify-content: center;
padding: clamp(48px, 8vw, 120px) 24px;
background: radial-gradient(120% 120% at 50% 0%, rgba(192, 18, 39, 0.18) 0%, rgba(26, 26, 26, 0.98) 55%, #111111 100%);
}
.express-coming-soon-inner {
max-width: 720px;
text-align: center;
}
.express-coming-soon-eyebrow {
display: inline-block;
font-size: 13px;
font-weight: 700;
letter-spacing: 0.14em;
text-transform: uppercase;
color: var(--logico-accent-color);
margin-bottom: 20px;
}
.express-coming-soon-heading {
font-family: 'Manrope', sans-serif;
font-size: clamp(32px, 5vw, 56px);
line-height: 1.15em;
font-weight: 700;
color: var(--logico-dark-text-color);
margin: 0 0 20px;
}
.express-coming-soon-accent {
color: var(--logico-accent-color);
}
.express-coming-soon-text {
font-size: 16px;
line-height: 1.75em;
color: var(--logico-light-text-color);
margin: 0 auto 36px;
max-width: 520px;
}
.express-coming-soon-actions {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: center;
gap: 24px;
}
.express-coming-soon .logico-alter-button {
color: #ffffff !important;
border-color: rgba(255, 255, 255, 0.45) !important;
background: transparent !important;
}
.express-coming-soon .logico-alter-button:hover {
background: #ffffff !important;
color: #1a1a1a !important;
border-color: #ffffff !important;
}
.express-coming-soon-link {
font-size: 15px;
font-weight: 600;
color: var(--logico-light-text-color);
text-decoration: underline;
text-underline-offset: 4px;
}
.express-coming-soon-link:hover {
color: #ffffff;
}
@media (max-width: 480px) {
.express-coming-soon-actions {
flex-direction: column;
gap: 16px;
}
}
`,
}}
/>
</div>
);
}

View File

@@ -10,27 +10,6 @@ html {
scroll-behavior: smooth;
}
/* ============================================================
SITE-WIDE STICKY FIX
The legacy Elementor build sets `.body-container { overflow: hidden }`
(public/css/site.css, generated — do not edit there). Any ancestor
with non-visible overflow becomes the containing block for
`position: sticky` descendants; since `.body-container` itself never
actually scrolls (the real scrollbar lives on <body>), that made every
`position: sticky` element on the site inert — it just scrolled away
in normal flow instead of pinning.
Note: setting overflow-x:hidden paired with overflow-y:visible gets
silently coerced to overflow-y:auto by the CSS spec (an "auto" value on
one axis when the other is non-visible) — which ALSO still counts as a
scroll container and breaks sticky the same way. So this must be fully
visible on both axes. <body> already has overflow-x: hidden site-wide
(from the same legacy stylesheet), so horizontal bleed is still safely
clipped there — this rule doesn't need to repeat it.
============================================================ */
.body-container {
overflow: visible !important;
}
/* ============================================================
GLOBAL SPACING SYSTEM
A single shared vertical-rhythm scale so every section uses
@@ -174,21 +153,16 @@ body {
#masthead .elementor-element.elementor-element-0b7bf6f > .elementor-widget-container {
padding: 0 42px !important;
border-radius: 22px !important;
height: 58px !important;
display: flex !important;
align-items: center !important;
}
#masthead .elementor-element.elementor-element-0b7bf6f .header-menu-container .main-menu > li > a {
padding-top: 6px !important;
padding-bottom: 6px !important;
padding-top: 15px !important;
padding-bottom: 16px !important;
padding-left: 14px !important;
padding-right: 14px !important;
font-size: 15px !important;
line-height: 1.2 !important;
white-space: nowrap !important;
min-height: auto !important;
height: auto !important;
}
#masthead .header-menu-container .main-menu {
@@ -205,11 +179,7 @@ body {
}
#masthead .elementor-element.elementor-element-466de1b.dm-header-scrolled {
top: 15px !important;
left: 20px !important;
right: 20px !important;
border-radius: 24px !important;
width: auto !important;
top: 0 !important;
}
}
@@ -994,49 +964,6 @@ body {
box-sizing: border-box;
}
/* ============================================================
SHARED PAGE CONTENT CONTAINER
Matches the Hero/Header/Footer's real rendered OUTER edge, not the
Footer's narrower inner text column. The Hero card
(.owl-stage-outer, via .wings-hero-page.elementor-element-741f56c)
and the Footer's outer band (.elementor-element-b29b8fc) both sit a
fixed, uncapped 20px in from the viewport edge at every desktop
width (verified 1280px-2400px — no max-width ever kicks in). Apply
this class to any Wings-page section's content wrapper to guarantee
its cards/timeline/grid hug those same edges instead of sitting
narrower and more centered.
============================================================ */
.dm-section-container {
width: 100%;
margin-left: auto;
margin-right: auto;
box-sizing: border-box;
padding-left: 20px;
padding-right: 20px;
}
@media (max-width: 1024px) {
.dm-section-container {
padding-left: var(--dm-mobile-gutter, 10px);
padding-right: var(--dm-mobile-gutter, 10px);
}
}
/* Doormile Wings hero frame: the generic vendor CSS assigns 32px padding to
.elementor-element-741f56c on all pages; Home's frame is 20px (scoped
to .elementor-61). Scope the same 20px frame to the Doormile Wings hero so
the card alignment matches Home and Solutions exactly. */
@media (min-width: 1025px) {
.wings-hero-page.elementor-element-741f56c,
.af-hero-page.elementor-element-741f56c {
--padding-top: 20px;
--padding-right: 20px;
--padding-bottom: 20px;
--padding-left: 20px;
padding: 20px !important;
}
}
/* Custom premium animations keyframes */
@keyframes shimmer-sweep {
0% {
@@ -1239,4 +1166,3 @@ body {
margin-top: var(--space-section) !important;
}
}

View File

@@ -2,7 +2,6 @@ import React from "react";
import MileTruthHero from "../../components/sections/MileTruthHero";
import Workflow1 from "../../components/sections/Workflow1";
import Workflow2 from "../../components/sections/Workflow2";
import Workflow4 from "../../components/sections/Workflow4";
import Workflow3Lazy from "../../components/sections/Workflow3Lazy";
export const metadata = {
@@ -19,7 +18,6 @@ export default function MileTruthPage() {
<MileTruthHero />
<Workflow1 />
<Workflow2 />
<Workflow4 />
<Workflow3Lazy />
</div>
</div>

View File

@@ -1,80 +1,32 @@
import type { MetadataRoute } from "next";
export const dynamic = "force-static";
const BASE_URL = "https://doormile.com";
import { MetadataRoute } from "next";
import { blogPosts, SITE_URL } from "@/data/blog";
export default function sitemap(): MetadataRoute.Sitemap {
const now = new Date();
const staticRoutes = [
"",
"/about",
"/contact",
"/blog",
"/air-freight",
"/first-mile-delivery",
"/mid-mile-delivery",
"/last-mile-delivery",
"/ev-logistics",
"/strategy-optimization",
"/how-it-works",
].map((route) => ({
url: `${SITE_URL}${route}`,
lastModified: new Date().toISOString().split("T")[0],
changeFrequency: "weekly" as const,
priority: route === "" ? 1 : 0.8,
}));
const staticRoutes: MetadataRoute.Sitemap = [
{
url: `${BASE_URL}/`,
lastModified: now,
changeFrequency: "weekly",
priority: 1.0,
},
{
url: `${BASE_URL}/how-it-works`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.8,
},
{
url: `${BASE_URL}/miletruth`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.8,
},
{
url: `${BASE_URL}/solutions`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.8,
},
{
url: `${BASE_URL}/doormile-wings`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.8,
},
{
url: `${BASE_URL}/about-us`,
lastModified: now,
changeFrequency: "monthly",
priority: 0.7,
},
{
url: `${BASE_URL}/blog`,
lastModified: now,
changeFrequency: "weekly",
priority: 0.7,
},
{
url: `${BASE_URL}/contact`,
lastModified: now,
changeFrequency: "yearly",
priority: 0.6,
},
{
url: `${BASE_URL}/privacy-policy`,
lastModified: now,
changeFrequency: "yearly",
priority: 0.3,
},
{
url: `${BASE_URL}/terms-of-service`,
lastModified: now,
changeFrequency: "yearly",
priority: 0.3,
},
{
url: `${BASE_URL}/cookie-policy`,
lastModified: now,
changeFrequency: "yearly",
priority: 0.3,
},
];
const postRoutes = blogPosts.map((post) => ({
url: `${SITE_URL}/blog/${post.slug}`,
lastModified: post.date,
changeFrequency: "monthly" as const,
priority: 0.6,
}));
return staticRoutes;
return [...staticRoutes, ...postRoutes];
}

View File

@@ -65,7 +65,6 @@ export default function BlogSearch() {
setOpen(true);
}}
onFocus={() => setOpen(true)}
aria-expanded={showPanel}
aria-controls="dm-blog-search-results"
/>
<span className="dm-blog-search-icon" aria-hidden="true">

View File

@@ -3,7 +3,7 @@
import React, { useState, useEffect } from "react";
import Link from "next/link";
import Image from "next/image";
import { submitContactForm } from "@/lib/contactForm";
import { sendEmailAction } from "@/actions/sendEmail";
import { ScrollReveal } from "@/animations/Reveal";
export default function Footer() {
@@ -55,13 +55,13 @@ export default function Footer() {
setFormStatus("submitting");
try {
const res = await submitContactForm(formData);
const res = await sendEmailAction(formData);
if (res.success) {
setFormStatus("success");
setFormData({ fullName: "", email: "", subject: "", message: "" });
} else {
console.error("Failed to submit contact form");
console.error("Failed to submit contact form:", res.error);
setFormStatus("error");
}
} catch (err) {
@@ -138,7 +138,7 @@ export default function Footer() {
<svg className="dm-foot-ic" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z" />
</svg>
<span>Contect</span>
<span>Contact</span>
</div>
</div>
</div>
@@ -337,8 +337,8 @@ export default function Footer() {
{formStatus === "submitting" ? "Sending..." : "Send a message"}
</button>
{formStatus === "success" && (
<div style={{ color: "#4caf50", marginTop: "10px", fontSize: "14px" }}>
Message sent successfully.
<div style={{ marginTop: "16px", color: "#4CAF50", fontSize: "14px", fontWeight: "bold" }}>
Message sent successfully!
</div>
)}
{formStatus === "error" && (
@@ -381,7 +381,7 @@ export default function Footer() {
<div className="elementor-element elementor-element-b5c897d elementor-widget elementor-widget-image" data-id="b5c897d" data-element_type="widget" data-e-type="widget" data-widget_type="image.default">
<div className="elementor-widget-container">
<Link href="/">
<Image width={300} height={57} src="/images/logo-slogan.png" style={{ width: "280px", height: "auto" }} className="attachment-full size-full wp-image-5851" alt="Doormile Tagline" />
<Image width={748} height={184} src="/images/logo-slogan.png" style={{ width: "280px", height: "auto" }} className="attachment-full size-full wp-image-5851" alt="Doormile Tagline" />
</Link>
</div>
</div>
@@ -458,9 +458,6 @@ export default function Footer() {
<div className="elementor-element elementor-element-a219b7d elementor-widget elementor-widget-logico_custom_navigation_menu" data-id="a219b7d" data-element_type="widget" data-e-type="widget" data-widget_type="logico_custom_navigation_menu.default">
<div className="elementor-widget-container">
<ul className="logico-custom-menu-widget">
<li>
<Link href="/doormile-wings">Doormile Wings</Link>
</li>
<li>
<Link href="/about-us">About Us</Link>
</li>
@@ -490,7 +487,7 @@ export default function Footer() {
<Link href="/privacy-policy">Privacy Policy</Link>
</li>
<li>
<Link href="/terms-of-service">Terms Service</Link>
<Link href="/terms-of-service">Terms of Service</Link>
</li>
<li>
<Link href="/cookie-policy">Cookie Policy</Link>

View File

@@ -7,7 +7,7 @@
* Menu open/close + sidebar state is read from HeaderUIProvider so BodyOverlay (sibling at body level) can react.
*/
import { KeyboardEvent, FocusEvent, MouseEvent, useEffect, useState } from "react";
import { MouseEvent, useEffect, useState } from "react";
import Link from "next/link";
import Image from "next/image";
import { usePathname } from "next/navigation";
@@ -19,11 +19,13 @@ const CURRENT_PAGE_MAP: Record<string, string> = {
"/how-it-works": "how-it-works",
"/miletruth": "miletruth",
"/solutions": "solutions",
"/doormile-wings": "doormile-wings",
"/empowerment": "entarpreneurship",
"/express": "express",
"/about": "about",
"/about-us": "about",
"/blog": "blogs",
"/contact": "contact",
"/privacy-policy": "privacy-policy",
"/terms-of-service": "terms-of-service",
"/cookie-policy": "cookie-policy",
};
// Mirror of header.php $current_page_aliases (lines 37-44)
@@ -31,13 +33,13 @@ const CURRENT_PAGE_ALIASES: Record<string, string[]> = {
home: ["home"],
"how-it-works": ["how-it-works", "how-its-works"],
miletruth: ["miletruth"],
// Solutions is now a plain top-level link — no child pages own its active state.
solutions: ["solutions"],
"doormile-wings": ["doormile-wings"],
entarpreneurship: ["entarpreneurship"],
about: ["about", "about-us", "women"],
blogs: ["blogs"],
// Company is the dropdown parent for About + Blogs + How It Works — active if any is.
company: ["blogs", "how-it-works", "about"],
contact: ["contact"],
"privacy-policy": ["privacy-policy"],
"terms-of-service": ["terms-of-service"],
"cookie-policy": ["cookie-policy"],
};
export default function Header() {
@@ -45,7 +47,10 @@ export default function Header() {
const { isMenuOpen, isSidebarOpen, toggleMenu, toggleSidebar, closeAll } = useHeaderUI();
const isHome = pathname === "/";
const currentPage = CURRENT_PAGE_MAP[pathname] ?? "";
let currentPage = CURRENT_PAGE_MAP[pathname] ?? "";
if (pathname.startsWith("/blog/")) {
currentPage = "blogs";
}
// Mirror of header.php $dm_header_active (lines 45-47)
const dmHeaderActive = (key: string) =>
@@ -67,7 +72,7 @@ export default function Header() {
targetId: string,
shouldCloseMenu = false,
) => {
if (shouldCloseMenu) closeMobileMenu();
if (shouldCloseMenu) closeAll();
if (pathname !== targetPath) return;
@@ -85,23 +90,6 @@ export default function Header() {
const [visibleScrolled, setVisibleScrolled] = useState(false);
const [isScrolled, setIsScrolled] = useState(false);
// Company dropdown (desktop) / accordion (mobile). Local to Header —
// unlike isMenuOpen/isSidebarOpen (HeaderUIProvider), no sibling component
// needs to read this.
const [isDesktopCompanyOpen, setDesktopCompanyOpen] = useState(false);
const [isMobileCompanyOpen, setMobileCompanyOpen] = useState(false);
// Collapse the accordion whenever the off-canvas menu closes, so it isn't
// left expanded the next time the menu opens.
const closeMobileMenu = () => {
setMobileCompanyOpen(false);
closeAll();
};
const toggleMobileMenu = () => {
setMobileCompanyOpen(false);
toggleMenu();
};
useEffect(() => {
const handle = requestAnimationFrame(() => {
setVisibleScrolled(true);
@@ -433,8 +421,8 @@ export default function Header() {
<Link data-elementor-open-lightbox="" className="elementor-clickable" href="/">
<div className="hfe-site-logo-set">
<div className="hfe-site-logo-container">
<Image width={400} height={76} className="hfe-site-logo-img logo-desktop elementor-animation-" src="/images/doormile-white.png" alt="doormile-logo" priority />
<Image width={400} height={76} className="hfe-site-logo-img logo-mobile elementor-animation-" src="/images/doormile-black.png" alt="doormile-logo" priority />
<Image width={748} height={100} className="hfe-site-logo-img logo-desktop elementor-animation-" src="/images/doormile-white.png" alt="doormile-logo" priority />
<Image width={748} height={100} className="hfe-site-logo-img logo-mobile elementor-animation-" src="/images/doormile-black.png" alt="doormile-logo" priority />
</div>
</div>
</Link>
@@ -460,86 +448,25 @@ export default function Header() {
<li id="menu-item-10508" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}>
<Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home")}>Home</Link>
</li>
<li id="menu-item-10509" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("doormile-wings")}`}>
<Link href="/doormile-wings" prefetch>Wings</Link>
<li id="menu-item-10509" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("how-it-works")}`}>
<Link href="/how-it-works#how-it-works" onClick={(event) => handleNavClick(event, "/how-it-works", "how-it-works")}>How It Works</Link>
</li>
<li id="menu-item-10510" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
<Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth")}>MileTruth&trade; AI</Link>
</li>
<li id="menu-item-10511" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10511${dmHeaderActive("solutions")}`}>
<Link href="/solutions" prefetch>Solutions</Link>
<li id="menu-item-10511" className={`menu-item menu-item-type-custom menu-item-10511${dmHeaderActive("solutions")}`}>
<Link href="/solutions#solutions" onClick={(event) => handleNavClick(event, "/solutions", "solutions")}>Solutions</Link>
</li>
<li id="menu-item-10513" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10513${dmHeaderActive("entarpreneurship")}`}>
<Link href="/empowerment" prefetch>Entrepreneurship</Link>
<li id="menu-item-10512" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10512${dmHeaderActive("about")}`}>
<Link href="/about-us#about" onClick={(event) => handleNavClick(event, "/about-us", "about")}>About</Link>
</li>
<li
id="menu-item-10535"
className={`menu-item menu-item-type-custom menu-item-has-children menu-item-10535${dmHeaderActive("company")}`}
onMouseEnter={() => setDesktopCompanyOpen(true)}
onMouseLeave={() => setDesktopCompanyOpen(false)}
onKeyDown={(event: KeyboardEvent<HTMLLIElement>) => {
if (event.key !== "Escape") return;
setDesktopCompanyOpen(false);
event.currentTarget.querySelector<HTMLAnchorElement>(":scope > a")?.focus();
}}
onBlur={(event: FocusEvent<HTMLLIElement>) => {
if (!event.currentTarget.contains(event.relatedTarget as Node)) {
setDesktopCompanyOpen(false);
}
}}
>
<a
href="#"
aria-haspopup="true"
aria-expanded={isDesktopCompanyOpen}
onFocus={() => setDesktopCompanyOpen(true)}
onClick={(event) => {
event.preventDefault();
setDesktopCompanyOpen((open) => !open);
}}
>
Company
</a>
<ul className="dm-solutions-dropdown">
<li className={currentPage === "about" ? "dm-child-active" : ""}>
<Link
href="/about-us#about"
onClick={(event) => {
setDesktopCompanyOpen(false);
handleNavClick(event, "/about-us", "about");
}}
>
About
</Link>
</li>
<li className={currentPage === "blogs" ? "dm-child-active" : ""}>
<Link
href="/blog#blogs"
onClick={(event) => {
setDesktopCompanyOpen(false);
handleNavClick(event, "/blog", "blogs");
}}
>
Blogs
</Link>
</li>
<li className={currentPage === "how-it-works" ? "dm-child-active" : ""}>
<Link
href="/how-it-works#how-it-works"
onClick={(event) => {
setDesktopCompanyOpen(false);
handleNavClick(event, "/how-it-works", "how-it-works");
}}
>
How It Works
</Link>
</li>
</ul>
<li id="menu-item-10535" className={`menu-item menu-item-type-post_type menu-item-object-page menu-item-10535${dmHeaderActive("blogs")}`}>
<Link href="/blog#blogs" onClick={(event) => handleNavClick(event, "/blog", "blogs")}>Blogs</Link>
</li>
</ul>
</nav>
</div>
<div className="menu-trigger" data-id="0b7bf6f" onClick={toggleMobileMenu}>
<div className="menu-trigger" data-id="0b7bf6f" onClick={toggleMenu}>
<span className="menu-trigger-icon">
<span className="hamburger">
<span></span>
@@ -559,7 +486,7 @@ export default function Header() {
<div className="header-icon login-logout">
<a href="#" title="Login/Register" className="link-login"></a>
</div>
<div className="header-icon menu-close" onClick={closeMobileMenu}>
<div className="header-icon menu-close" onClick={closeAll}>
<span className="menu-close-icon"></span>
</div>
</div>
@@ -569,53 +496,20 @@ export default function Header() {
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}>
<Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home", true)}>Home</Link>
</li>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("doormile-wings")}`}>
<Link href="/doormile-wings" prefetch onClick={closeMobileMenu}>Wings</Link>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("how-it-works")}`}>
<Link href="/how-it-works#how-it-works" onClick={(event) => handleNavClick(event, "/how-it-works", "how-it-works", true)}>How It Works</Link>
</li>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
<Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth", true)}>MileTruth&trade; AI</Link>
</li>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10511${dmHeaderActive("solutions")}`}>
<Link href="/solutions" prefetch onClick={closeMobileMenu}>Solutions</Link>
<li className={`menu-item menu-item-type-custom menu-item-10511${dmHeaderActive("solutions")}`}>
<Link href="/solutions#solutions" onClick={(event) => handleNavClick(event, "/solutions", "solutions", true)}>Solutions</Link>
</li>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10513${dmHeaderActive("entarpreneurship")}`}>
<Link href="/empowerment" prefetch onClick={closeMobileMenu}>Entrepreneurship</Link>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-10512${dmHeaderActive("about")}`}>
<Link href="/about-us#about" onClick={(event) => handleNavClick(event, "/about-us", "about", true)}>About</Link>
</li>
<li
className={`menu-item menu-item-type-custom menu-item-10535 dm-mobile-solutions${dmHeaderActive("company")}${isMobileCompanyOpen ? " dm-accordion-open" : ""}`}
>
<div className="dm-mobile-solutions-row">
<a
href="#"
onClick={(event) => {
event.preventDefault();
setMobileCompanyOpen((open) => !open);
}}
>
Company
</a>
<button
type="button"
className="dm-mobile-solutions-toggle"
aria-label={isMobileCompanyOpen ? "Close Company menu" : "Open Company menu"}
aria-haspopup="true"
aria-expanded={isMobileCompanyOpen}
onClick={() => setMobileCompanyOpen((open) => !open)}
>
<span className="dm-accordion-chevron" aria-hidden="true"></span>
</button>
</div>
<ul className="dm-mobile-solutions-panel">
<li className={currentPage === "about" ? "dm-child-active" : ""}>
<Link href="/about-us#about" onClick={(event) => handleNavClick(event, "/about-us", "about", true)}>About</Link>
</li>
<li className={currentPage === "blogs" ? "dm-child-active" : ""}>
<Link href="/blog#blogs" onClick={(event) => handleNavClick(event, "/blog", "blogs", true)}>Blogs</Link>
</li>
<li className={currentPage === "how-it-works" ? "dm-child-active" : ""}>
<Link href="/how-it-works#how-it-works" onClick={(event) => handleNavClick(event, "/how-it-works", "how-it-works", true)}>How It Works</Link>
</li>
</ul>
<li className={`menu-item menu-item-type-post_type menu-item-object-page menu-item-10535${dmHeaderActive("blogs")}`}>
<Link href="/blog#blogs" onClick={(event) => handleNavClick(event, "/blog", "blogs", true)}>Blogs</Link>
</li>
</ul>
</nav>
@@ -973,281 +867,6 @@ export default function Header() {
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='5' y1='5' x2='19' y2='19'/%3E%3Cline x1='19' y1='5' x2='5' y2='19'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='5' y1='5' x2='19' y2='19'/%3E%3Cline x1='19' y1='5' x2='5' y2='19'/%3E%3C/svg%3E");
}
/* ── Company dropdown (desktop) / accordion (mobile) ──
Company (Blogs + How It Works) is a non-navigating parent —
its trigger is a plain anchor with no href target, only a
dropdown toggle. Class names below (.dm-solutions-dropdown,
.dm-mobile-solutions*) are unchanged from when this same
component/markup belonged to Solutions, reusing site.css's dead
.menu-item-has-children chevron (fontello glyph, already
coloured white for this exact header widget) instead of
inventing a new arrow. Desktop opens on hover/focus; mobile is a
height-collapsing accordion controlled by the chevron button. */
@media (min-width: 1025px) {
#masthead .header-menu-container nav {
display: flex;
align-items: center;
}
#masthead .header-menu-container .main-menu {
display: inline-flex !important;
align-items: center;
gap: 16px !important;
margin: 0 !important;
line-height: 1;
}
#masthead .header-menu-container .main-menu > li {
display: flex;
align-items: center;
margin-right: 0 !important;
}
#masthead .header-menu-container .main-menu > li > a {
display: inline-flex !important;
align-items: center;
justify-content: center;
padding-top: 0 !important;
padding-bottom: 0 !important;
min-height: 48px;
line-height: 1.2 !important;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children {
position: relative;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children:after {
content: "";
position: absolute;
top: 100%;
left: 50%;
width: 100%;
min-width: 360px;
height: 24px;
transform: translateX(-50%);
pointer-events: auto;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown {
position: absolute;
top: 100%;
left: 50%;
transform: translate(-50%, 34px);
width: 240px;
min-width: 240px;
margin: 0;
padding: 12px;
list-style: none;
background: linear-gradient(180deg, rgba(31, 31, 31, 0.98) 0%, rgba(18, 18, 18, 0.98) 100%);
-webkit-backdrop-filter: blur(20px);
backdrop-filter: blur(20px);
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.08);
border: 1px solid rgba(255, 255, 255, 0.09);
border-radius: 18px;
opacity: 0;
visibility: hidden;
pointer-events: none;
transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
z-index: 20;
}
/* .main-menu li:not(:last-of-type) (site.css) is a descendant
selector that also matches these nested <li>s, pushing every
row but the last 12px to the right. Cancel it here. */
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li {
margin-right: 0 !important;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li + li {
margin-top: 10px;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 10px;
height: 48px;
padding: 0 16px;
border-radius: 10px;
/* Hardcoded rather than var(--logico-dark-text-color): that
variable is only overridden to white inside a few unrelated
scoped contexts (e.g. #side-panel-2f31137) and otherwise
falls back to the sitewide light-theme value (#111111),
which would be invisible on this dark glass panel. */
color: rgba(255, 255, 255, 0.85);
font-family: var(--font-manrope), "Manrope", sans-serif;
font-size: 15px;
font-weight: 600;
line-height: 1.25;
letter-spacing: normal;
white-space: nowrap;
text-decoration: none;
transition: background-color 0.25s ease, color 0.25s ease, padding-left 0.25s ease;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:before {
content: "";
width: 11px;
height: 11px;
margin-left: -16px;
flex: 0 0 11px;
background-color: #c01227;
opacity: 0;
transform: translateX(-10px);
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12 12 4'/%3E%3Cpath d='M5 4h7v7'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12 12 4'/%3E%3Cpath d='M5 4h7v7'/%3E%3C/svg%3E");
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: contain;
mask-size: contain;
transition: opacity 0.25s ease-in-out, transform 0.25s ease-in-out, background-color 0.25s ease-in-out;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:hover,
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:focus-visible {
background-color: rgba(255, 255, 255, 0.08);
color: #ffffff;
padding-left: 22px;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:hover:before,
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:focus-visible:before {
opacity: 1;
transform: translateX(0);
background-color: #c01227;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li.dm-child-active > a {
color: #ffffff;
font-weight: 700;
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children > a[aria-expanded="true"] ~ .dm-solutions-dropdown {
opacity: 1;
visibility: visible;
pointer-events: auto;
transform: translate(-50%, 24px);
}
#masthead .header-menu-container .main-menu > li.menu-item-has-children > a[aria-expanded="true"]:after {
transform: rotate(180deg);
}
/* Outlines are globally suppressed for .main-menu>li>a above;
restore a visible focus ring scoped only to this trigger. */
#masthead .header-menu-container .main-menu > li.menu-item-has-children > a:focus-visible {
outline: 2px solid var(--logico-dark-text-color, #ffffff);
outline-offset: 6px;
border-radius: 4px;
}
}
#masthead .mobile-header-menu-container .dm-mobile-solutions-row {
display: flex !important;
align-items: center;
justify-content: space-between;
}
#masthead .mobile-header-menu-container .dm-mobile-solutions-row > a {
display: block;
flex: 1 1 auto;
padding: 0.75em 0;
color: var(--logico-default-text-color);
text-decoration: none;
}
#masthead .mobile-header-menu-container .dm-mobile-solutions-toggle {
display: inline-flex;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
margin: 0;
padding: 0;
flex: 0 0 44px;
border: 0;
background: transparent;
color: var(--logico-default-text-color);
cursor: pointer;
}
#masthead .mobile-header-menu-container .dm-mobile-solutions-toggle:focus-visible {
outline: 2px solid currentColor;
outline-offset: 2px;
border-radius: 4px;
}
#masthead .mobile-header-menu-container .dm-accordion-chevron {
display: inline-block;
width: 0.7em;
height: 0.7em;
flex: 0 0 auto;
background-color: currentColor;
-webkit-mask-repeat: no-repeat;
mask-repeat: no-repeat;
-webkit-mask-position: center;
mask-position: center;
-webkit-mask-size: contain;
mask-size: contain;
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
transition: transform 0.3s ease;
}
#masthead .mobile-header-menu-container .dm-mobile-solutions.dm-accordion-open .dm-accordion-chevron {
transform: rotate(180deg);
}
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel {
max-height: 0;
overflow: hidden;
list-style: none;
margin: 0;
padding: 0;
padding-left: 18px;
transition: max-height 0.3s ease;
}
#masthead .mobile-header-menu-container .dm-mobile-solutions.dm-accordion-open .dm-mobile-solutions-panel {
max-height: 160px;
}
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li {
font-size: 0.9em;
}
/* .mobile-header-menu-container ul.main-menu>li>a (site.css) only
matches direct children of .main-menu, so these nested links
fall through to the sitewide generic underlined-anchor default.
Restore the plain sibling-item look explicitly. */
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li > a {
display: block;
padding: 0.35em 0;
color: var(--logico-default-text-color);
text-decoration: none;
}
/* A vendor li:before rule (checkmark bullet, likely intended for a
feature-list style elsewhere) leaks through here since the reset
at .mobile-header-menu-container ul.main-menu>li:before only
matches direct children of .main-menu, not this nested li.
Suppress it on both the <li> and <a> — these child rows aren't
top-level nav items and shouldn't carry any bullet/indicator. */
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li:before,
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li > a:before {
content: none !important;
display: none !important;
}
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li.dm-child-active > a {
color: var(--logico-light-text-color);
text-decoration: underline;
}
`,
}}
/>

View File

@@ -1,6 +1,6 @@
"use client";
import React, { useEffect, useRef, useState, useSyncExternalStore } from "react";
import React, { useRef, useState, useEffect } from "react";
import dynamic from "next/dynamic";
import { motion, useMotionValue, useTransform, type MotionValue } from "framer-motion";
import gsap from "gsap";

View File

@@ -228,7 +228,7 @@ export default function OfficeMap() {
{OFFICE_LOCATIONS.map((office) => {
const isFocused = focus?.id === office.id;
const isHovered = hoveredOfficeId === office.id;
return (
<Marker

View File

@@ -54,6 +54,7 @@ function CameraRig({ progress, framing }: { progress: React.RefObject<number>; f
// construction-time prop on <Canvas>, so update it imperatively here.
useEffect(() => {
if ("fov" in camera) {
// eslint-disable-next-line react-hooks/immutability
(camera as THREE_PerspectiveCamera).fov = framing.fov;
(camera as THREE_PerspectiveCamera).updateProjectionMatrix();
}

View File

@@ -1,9 +1,11 @@
import React from "react";
import { preload } from "react-dom";
export default function AboutHero() {
preload("/images/about-bg.webp", { as: "image" });
return (
<>
<link rel="preload" as="image" href="/images/about-bg.webp" />
<style dangerouslySetInnerHTML={{ __html: `
.about-us-hero-content {
width: 100% !important;

View File

@@ -1,9 +1,11 @@
import React from "react";
import { preload } from "react-dom";
export default function BlogsHero() {
preload("/images/home2-banner-1.webp", { as: "image" });
return (
<>
<link rel="preload" as="image" href="/images/home2-banner-1.webp" />
<style dangerouslySetInnerHTML={{ __html: `
.blogs-hero-title {
color: #ffffff !important;

View File

@@ -23,6 +23,14 @@ export default function ConnectedLogistics() {
overflow-wrap: break-word !important;
}
/* Fix text colors for the dark background */
.elementor-element-71c3e1d .logico-title {
color: #ffffff !important;
}
.elementor-element-71c3e1d p {
color: rgba(255, 255, 255, 0.85) !important;
}
/* Make sure paragraphs are responsive on all screen widths */
.elementor-element-165dfa5 {
width: 100% !important;
@@ -198,7 +206,7 @@ export default function ConnectedLogistics() {
<ScrollReveal delay={0.2} duration={0.85} yOffset={25}>
<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">
<h3 className="logico-title">Smart logistics solutions we deliver for modern supply chains</h3>
<h2 className="logico-title">Smart logistics solutions we deliver for modern supply chains</h2>
</div>
</div>
</ScrollReveal>

View File

@@ -1,9 +1,11 @@
import React from "react";
import { preload } from "react-dom";
export default function ContactsHero() {
preload("/images/home2-banner-3.webp", { as: "image" });
return (
<>
<link rel="preload" as="image" href="/images/home2-banner-3.webp" />
<style dangerouslySetInnerHTML={{ __html: `
.contacts-hero-custom {
background-color: #0b0b0b !important;

View File

@@ -42,9 +42,6 @@ export interface EVStat {
decimals?: number;
suffix: string;
label: string;
/** Optional static text override — when set, renders this text instead of
* an animated CountUp (for non-numeric KPI concepts, e.g. "QUBO"). */
text?: string;
}
/** Accent theming for the dashboard column so it matches the scene on the left
@@ -86,19 +83,10 @@ interface EVSectionProps {
* static homepage feature list. The bottom stat bar is dropped in this mode
* to avoid repeating the metrics. */
metrics?: EVStat[];
/** Optional distinct copy for the bottom `.evnd__bar` strip in dashboard
* mode. Defaults to reusing `metrics` (WF1/WF2 behaviour) when omitted. */
barStats?: EVStat[];
/** Heading above the dashboard, e.g. "PERFORMANCE INSIGHT". */
cardsHeading?: string;
/** Accent theme for the dashboard column (defaults to the section red). */
cardsTheme?: EVCardsTheme;
/** Dashboard system label, top-right of the header row. Defaults to "AI ENGINE". */
sysLabel?: string;
/** Extra class appended to `.evnd__imgwrap`, for workflow-specific media
* sizing overrides (e.g. video object-fit) defined in the caller's own
* scoped <style> tag rather than here. */
mediaClassName?: string;
}
/* ---- Default content = original homepage EV Logistics section ---- */
@@ -272,13 +260,11 @@ function DashboardPanel({
features,
heading,
theme,
sysLabel = "AI ENGINE",
}: {
metrics: EVStat[];
features: EVFeature[];
heading?: string;
theme?: EVCardsTheme;
sysLabel?: string;
}) {
const themeVars = theme
? ({
@@ -301,7 +287,7 @@ function DashboardPanel({
<span className="evnd__dash-livedot" />
{heading}
</span>
<span className="evnd__dash-sys">{sysLabel}</span>
<span className="evnd__dash-sys">AI ENGINE</span>
</div>
)}
@@ -309,16 +295,12 @@ function DashboardPanel({
<div className="evnd__dash-kpis">
{metrics.map((m) => (
<div className="evnd__kpi" key={m.label}>
{m.text ? (
<b className="evnd__kpi-val evnd__kpi-val--text">{m.text}</b>
) : (
<CountUp
value={m.value}
decimals={m.decimals}
suffix={m.suffix}
className="evnd__kpi-val"
/>
)}
<CountUp
value={m.value}
decimals={m.decimals}
suffix={m.suffix}
className="evnd__kpi-val"
/>
<span className="evnd__kpi-label">{m.label}</span>
</div>
))}
@@ -360,15 +342,11 @@ export default function EVSection({
ariaLabel,
mediaSlot,
metrics,
barStats,
cardsHeading,
cardsTheme,
sysLabel,
mediaClassName,
}: EVSectionProps) {
const bannerRef = useRef<HTMLDivElement>(null);
const useDashboard = !!metrics && metrics.length > 0;
const isVideo = image.endsWith(".mp4");
/* In dashboard mode the workflow's accent theme is applied at the .evnd
section level so the shared animated network backdrop (below) is tinted to
@@ -483,6 +461,10 @@ export default function EVSection({
--padding-top: 46px;
--padding-bottom: 38px;
}
/* Fix missing font-size for card title that caused it to inherit 80px h2 */
.elementor-element.elementor-element-239afbb .logico-title {
font-size: clamp(28px, 3.5vw, 40px) !important;
}
.elementor-element.elementor-element-5aea22e {
--min-height: 38px;
--padding-top: 14px;
@@ -617,31 +599,13 @@ export default function EVSection({
background: #ef4444;
}
/* ---- Heading → content gap ----
Deliberately set here (on the wrapper div) rather than as
margin-bottom on .evnd__title (the <h2>): a global site.css rule
(.logico-front-end h2:not([class*="logico-title-h"]):last-child)
outweighs .evnd__title's own specificity and zeroes any bottom
margin placed on the heading itself, since the h2 is always the
last child of .evnd__heading. Setting it here sidesteps that
collision and gives every workflow section + Fleet Strategy one
shared, consistent gap. */
.evnd__heading {
margin-bottom: 40px;
}
@media (max-width: 991px) and (min-width: 768px) {
.evnd__heading {
margin-bottom: 34px;
}
}
.evnd__title {
color: #fff !important;
font-weight: 800 !important;
font-size: clamp(32px, 3.8vw, 48px) !important;
line-height: 1.15 !important;
letter-spacing: -0.01em;
margin: 0;
margin: 0 0 36px 0;
}
@media (min-width: 768px) {
.evnd__title {
@@ -792,7 +756,7 @@ export default function EVSection({
.evnd-feature__desc {
color: rgba(255,255,255,0.65) !important;
font-weight: 400 !important;
font-size: 16px !important;
font-size: 14px !important;
line-height: 1.6 !important;
margin: 0;
}
@@ -907,13 +871,6 @@ export default function EVSection({
font-size: 12.5px;
letter-spacing: 0.01em;
}
/* Non-numeric KPI concepts (EVStat.text) — same gradient treatment as
the counted values, sized down so short codes/words don't inherit
the numeric clamp() meant for 2-3 digit figures. */
.evnd__kpi-val--text {
font-size: clamp(18px, 1.6vw, 22px) !important;
letter-spacing: 0.02em;
}
/* ============================================================
Large-screen layout balance (WF1/WF2 dashboard only, ≥1440px).
@@ -1016,8 +973,8 @@ export default function EVSection({
.evnd__dfeat-desc {
color: rgba(255,255,255,0.6) !important;
font-weight: 400 !important;
font-size: 16px !important;
line-height: 1.55 !important;
font-size: 13px !important;
line-height: 1.45 !important;
margin: 0 !important;
}
@@ -1088,14 +1045,6 @@ export default function EVSection({
text-transform: none;
opacity: 0.9;
}
/* Non-numeric bar values (EVStat.text) — the bar's numeric clamp()
(up to 56px) is too large for words/phrases; size down and let
longer phrases (e.g. "Cost + Distance + SLA") wrap cleanly. */
.evnd__bar-val--text {
font-size: clamp(16px, 1.8vw, 22px);
line-height: 1.25;
white-space: normal;
}
@keyframes evndGlow { 0%,100% { opacity: 0.75; } 50% { opacity: 1; } }
@@ -1107,6 +1056,7 @@ export default function EVSection({
@media (max-width: 991px) {
.evnd { padding: 48px 32px 56px; }
.evnd__grid { grid-template-columns: 1fr; gap: 40px; }
.evnd__title { margin-bottom: 28px; }
.evnd__features { gap: 14px; }
/* Stacked: KPI row can spread to 4-up since it now has full width. */
.evnd__dash-kpis { grid-template-columns: repeat(4, 1fr); }
@@ -1342,7 +1292,7 @@ export default function EVSection({
data-widget_type="logico_heading.default"
>
<div className="elementor-widget-container">
<h5 className="logico-title">{cardTitle}</h5>
<h2 className="logico-title">{cardTitle}</h2>
</div>
</div>
<div
@@ -1385,7 +1335,7 @@ export default function EVSection({
<div
className={`evnd__inner${useDashboard ? " evnd__inner--cards" : ""}${
useDashboard && !mediaSlot && !isVideo ? " evnd__inner--img" : ""
useDashboard && !mediaSlot ? " evnd__inner--img" : ""
}`}
>
<div className="evnd__heading">
@@ -1399,7 +1349,7 @@ export default function EVSection({
{/* MAIN GRID */}
<div
className={`evnd__grid${useDashboard ? " evnd__grid--cards" : ""}${
useDashboard && !mediaSlot && !isVideo ? " evnd__grid--img" : ""
useDashboard && !mediaSlot ? " evnd__grid--img" : ""
}`}
>
{/* Left column */}
@@ -1407,11 +1357,11 @@ export default function EVSection({
<div className="evnd__media">
<div className="evnd__glow" />
<div
className={`evnd__imgwrap${mediaSlot ? " evnd__imgwrap--media" : ""}${isVideo ? " evnd__imgwrap--video" : ""}${mediaClassName ? ` ${mediaClassName}` : ""}`}
className={`evnd__imgwrap${mediaSlot ? " evnd__imgwrap--media" : ""}${image.endsWith(".mp4") ? " evnd__imgwrap--video" : ""}`}
>
{mediaSlot ? (
mediaSlot
) : isVideo ? (
) : image.endsWith(".mp4") ? (
<video
className="evnd__video"
autoPlay
@@ -1455,7 +1405,6 @@ export default function EVSection({
features={features}
heading={cardsHeading}
theme={cardsTheme}
sysLabel={sysLabel}
/>
) : (
<div className="evnd__features">
@@ -1489,19 +1438,15 @@ export default function EVSection({
all breakpoints. In dashboard mode it summarises the same workflow
metrics; the homepage section uses its own stat set. */}
<div className="evnd__bar">
{(useDashboard ? (barStats ?? metrics!) : stats).map((s) => (
{(useDashboard ? metrics! : stats).map((s) => (
<div className="evnd__bar-item" key={s.label}>
<span className="evnd__bar-label">{s.label}</span>
{s.text ? (
<b className="evnd__bar-val evnd__bar-val--text">{s.text}</b>
) : (
<CountUp
value={s.value}
decimals={s.decimals}
suffix={s.suffix}
className="evnd__bar-val"
/>
)}
<CountUp
value={s.value}
decimals={s.decimals}
suffix={s.suffix}
className="evnd__bar-val"
/>
</div>
))}
</div>

Some files were not shown because too many files have changed in this diff Show More