cleanup the repo unused images

This commit is contained in:
2026-07-03 16:35:42 +05:30
parent dd565e349a
commit 71875ea20f
89 changed files with 186 additions and 214 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

@@ -5,7 +5,7 @@ const nextConfig: NextConfig = {
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.
@@ -15,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: 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: 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: 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

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." };
}
}

32
src/app/sitemap.ts Normal file
View File

@@ -0,0 +1,32 @@
import { MetadataRoute } from "next";
import { blogPosts, SITE_URL } from "@/data/blog";
export default function sitemap(): MetadataRoute.Sitemap {
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 postRoutes = blogPosts.map((post) => ({
url: `${SITE_URL}/blog/${post.slug}`,
lastModified: post.date,
changeFrequency: "monthly" as const,
priority: 0.6,
}));
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>
@@ -487,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

@@ -19,8 +19,13 @@ const CURRENT_PAGE_MAP: Record<string, string> = {
"/how-it-works": "how-it-works",
"/miletruth": "miletruth",
"/solutions": "solutions",
"/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,6 +36,10 @@ const CURRENT_PAGE_ALIASES: Record<string, string[]> = {
solutions: ["solutions"],
about: ["about", "about-us", "women"],
blogs: ["blogs"],
contact: ["contact"],
"privacy-policy": ["privacy-policy"],
"terms-of-service": ["terms-of-service"],
"cookie-policy": ["cookie-policy"],
};
export default function Header() {
@@ -38,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) =>
@@ -409,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>

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

@@ -461,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;
@@ -1288,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

View File

@@ -1,8 +1,10 @@
"use client";
import React, { useState, useEffect } from "react";
import { preload } from "react-dom";
export default function HowItWorksHero() {
preload("/images/home2-banner-1.webp", { as: "image" });
const [activeSlide, setActiveSlide] = useState(0);
// Auto-slide every 7 seconds — slower, more readable, professional pacing
@@ -19,7 +21,7 @@ export default function HowItWorksHero() {
return (
<>
<link rel="preload" as="image" href="/images/home2-banner-1.webp" />
<style
dangerouslySetInnerHTML={{
__html: `

View File

@@ -3,8 +3,10 @@
import React, { useState, useEffect, useRef } from "react";
import gsap from "gsap";
import { ShimmerText } from "@/animations/Reveal";
import { preload } from "react-dom";
export default function IndexHero() {
preload("/images/home-bg-1.webp", { as: "image" });
const [activeSlide, setActiveSlide] = useState(0);
const containerRef = useRef<HTMLDivElement>(null);
@@ -52,7 +54,7 @@ export default function IndexHero() {
return (
<div className="elementor-element elementor-element-741f56c e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent" data-id="741f56c" data-element_type="container" data-e-type="container">
<link rel="preload" as="image" href="/images/home-bg-1.webp" />
<style dangerouslySetInnerHTML={{ __html: `
/* Fluid responsive font size override for hero headings */
.logico-content-slider-widget .content-slider-item-heading {

View File

@@ -133,11 +133,11 @@ export default function IndustrySolutions() {
data-widget_type="logico_heading.default"
>
<div className="elementor-widget-container" style={{ margin: "30px 0 0 0"}}>
<h3 className="logico-title" style={{ fontSize: "clamp(28px, 3.5vw, 48px)", lineHeight: "1.1", fontWeight: 800, textTransform: "uppercase", maxWidth: "900px" }}>
<h2 className="logico-title" style={{ fontSize: "clamp(28px, 3.5vw, 48px)", lineHeight: "1.1", fontWeight: 800, textTransform: "uppercase", maxWidth: "900px" }}>
<ScrollReveal delay={0.05} duration={0.8} yOffset={25}>
Smart solutions built exclusively for your <span style={{ color: "#c01227" }}>industry</span>
</ScrollReveal>
</h3>
</h2>
</div>
</div>
</div>

View File

@@ -1,6 +1,7 @@
"use client";
import React, { useState, useEffect } from "react";
import { preload } from "react-dom";
interface CounterProps {
from: number;
@@ -36,6 +37,7 @@ function Counter({ from, to, duration = 2000, decimals = 0, suffix = "" }: Count
}
export default function MileTruthHero() {
preload("/images/miletruth-bg.webp", { as: "image" });
const [mounted, setMounted] = useState(false);
useEffect(() => {
@@ -44,7 +46,7 @@ export default function MileTruthHero() {
return (
<>
<link rel="preload" as="image" href="/images/miletruth-bg.webp" />
<style dangerouslySetInnerHTML={{ __html: `
/* ── Hero wrapper: column layout, zero gap between hero + stats ── */
.miletruth-hero .elementor-element-86f3204 {

View File

@@ -1,8 +1,10 @@
"use client";
import React, { useState, useEffect } from "react";
import { preload } from "react-dom";
export default function SolutionsHero() {
preload("/images/home1-slide-1.webp", { as: "image" });
const [activeSlide, setActiveSlide] = useState(0);
// Auto-slide every 7 seconds — slower, more readable, professional pacing
@@ -19,7 +21,7 @@ export default function SolutionsHero() {
return (
<>
<link rel="preload" as="image" href="/images/home1-slide-1.webp" />
<style dangerouslySetInnerHTML={{ __html: `
.howits-hero-custom-bg.elementor-repeater-item-3264830,
.howits-hero-custom-bg.elementor-repeater-item-6867061 {

View File

@@ -6,6 +6,15 @@ import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
export default function TheDoormileWay() {
return (
<div className="elementor-61">
<style dangerouslySetInnerHTML={{ __html: `
/* Fix oversized h2 font size */
.elementor-element-9363070 .logico-title {
font-size: clamp(32px, 4.5vw, 64px) !important;
line-height: 1.1 !important;
word-wrap: break-word !important;
overflow-wrap: break-word !important;
}
`}} />
{/* Approach title container */}
<div className="elementor-element elementor-element-88745f4 e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent" data-id="88745f4" data-element_type="container" data-e-type="container">
<div className="e-con-inner">
@@ -38,7 +47,7 @@ export default function TheDoormileWay() {
<div className="elementor-element elementor-element-5ba6bbf e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="5ba6bbf" data-element_type="container" data-e-type="container">
<div className="elementor-element elementor-element-9363070 elementor-widget elementor-widget-logico_heading" data-id="9363070" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
<div className="elementor-widget-container">
<h3 className="logico-title"> Smarter, Faster, Connected Logistics</h3>
<h2 className="logico-title"> Smarter, Faster, Connected Logistics</h2>
</div>
</div>
</div>

View File

@@ -20,7 +20,7 @@ export default function TheProblem() {
<ScrollReveal delay={0.15} duration={0.85} yOffset={30}>
<div className="elementor-element elementor-element-ffd1f7d elementor-widget__width-initial elementor-widget elementor-widget-logico_heading" data-id="ffd1f7d" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
<div className="elementor-widget-container">
<h3 className="logico-title">Fragmented Logistics is Broken</h3>
<h2 className="logico-title">Fragmented Logistics is Broken</h2>
</div>
</div>
</ScrollReveal>

View File

@@ -6,7 +6,7 @@
* this is plain data resolved at build time. No CMS, no runtime fetching.
*/
export const SITE_URL = "https://www.doormile.com";
export const SITE_URL = "https://doormile.com";
export type ContentBlock =
| { type: "paragraph"; text: string }

View File

@@ -1,39 +0,0 @@
import emailjs from "@emailjs/browser";
interface ContactFormData {
fullName: string;
email: string;
subject: string;
message: string;
}
export async function submitContactForm(data: ContactFormData) {
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.warn("EmailJS credentials are not configured. Falling back to mock success.");
// Simulate network delay
await new Promise((resolve) => setTimeout(resolve, 800));
return { success: true, mock: true };
}
try {
await emailjs.send(
serviceId,
templateId,
{
name: data.fullName,
email: data.email,
subject: data.subject,
message: data.message,
},
publicKey
);
return { success: true };
} catch (error) {
console.error("EmailJS sending failed:", error);
throw error;
}
}

View File

@@ -1,21 +0,0 @@
import gsap from 'gsap'
// Optional GSAP timeline utility to animate custom camera effects (like micro-shake)
export const playCameraTransition = (camera, target, duration = 1.0) => {
if (!camera) return
const tl = gsap.timeline()
// Add a subtle drift to camera position to make it feel organic and premium
tl.to(camera.position, {
x: '+=0.3',
y: '+=0.1',
z: '-=0.2',
duration: duration,
yoyo: true,
repeat: 1,
ease: 'power1.inOut',
})
return tl
}

View File

@@ -1,18 +0,0 @@
import gsap from 'gsap'
// Play a subtle engine idle vibration when the truck is active
export const playTruckEngineVibration = (truckGroup, isActive = true) => {
if (!truckGroup) return null
if (isActive) {
return gsap.to(truckGroup.position, {
y: '+=0.015',
duration: 0.08,
yoyo: true,
repeat: -1,
ease: 'sine.inOut',
})
}
return null
}

View File

@@ -1,35 +0,0 @@
import * as THREE from 'three'
// Premium Apple-inspired cinematic keyframes looking directly at the front of each building
export const cameraPositions = [
{
// Stage 01: First Mile Warehouse (Front-on view of loading bays, lowered target to center truck)
progress: 0.0,
position: new THREE.Vector3(19.727, 7.5, -14.0),
target: new THREE.Vector3(19.727, 2.0, -31.02),
},
{
// Transition 01: Highway Cruise (Looking down at the highway joining road)
progress: 0.25,
position: new THREE.Vector3(0.0, 12.0, -12.0),
target: new THREE.Vector3(6.447, 2.0, -19.06),
},
{
// Stage 02: Mid Mile Hub (Front-on view of loading bays, lowered target to center truck)
progress: 0.5,
position: new THREE.Vector3(-19.146, 6.5, 10.0),
target: new THREE.Vector3(-19.146, 1.5, -6.00),
},
{
// Stage 03: Last Mile Delivery Center (Front-on view of local hub, lowered target to center truck)
progress: 0.75,
position: new THREE.Vector3(19.263, 5.5, 27.0),
target: new THREE.Vector3(19.263, 1.2, 4.0),
},
{
// Stage 04: Centralized Dashboard (Front-on view of the analytics monitor screen)
progress: 1.0,
position: new THREE.Vector3(-13.5, 5.0, 31.0),
target: new THREE.Vector3(-7.7, 3.8, 25.4),
},
]

View File

@@ -1,38 +0,0 @@
/**
* useDeviceTier — lightweight client-side device capability detection.
*
* Returns 'desktop' | 'tablet' | 'mobile' based on viewport width,
* touch capability, and hardware concurrency. The result is computed
* once on mount and never changes (no resize listener — the 3D scene
* should not swap LOD tiers mid-session).
*
* Also exports a plain `getDeviceTier()` function for non-React contexts.
*/
import { useMemo } from 'react'
/** Compute device tier from current browser globals. */
export function getDeviceTier() {
if (typeof window === 'undefined') return 'desktop'
const w = window.innerWidth
const hasTouch = navigator.maxTouchPoints > 0
const cores = navigator.hardwareConcurrency || 4
// Mobile: narrow viewport OR low-core touch device
if (w <= 600 || (hasTouch && w <= 768)) return 'mobile'
// Tablet: mid-width touch device (iPad, Android tablet, etc.)
if (w <= 1024 && hasTouch) return 'tablet'
// Low-powered desktops with ≤ 2 cores get tablet-tier rendering
if (cores <= 2) return 'tablet'
return 'desktop'
}
/** React hook — memoized once per component lifetime. */
export function useDeviceTier() {
return useMemo(() => getDeviceTier(), [])
}
export default useDeviceTier

View File

@@ -1,16 +0,0 @@
import { useSceneStore } from '../store/useSceneStore'
export const useScrollProgress = () => {
const scrollProgress = useSceneStore((state) => state.scrollProgress)
const activeSection = useSceneStore((state) => state.activeSection)
const setScrollProgress = useSceneStore((state) => state.setScrollProgress)
const setActiveSection = useSceneStore((state) => state.setActiveSection)
return {
scrollProgress,
activeSection,
setScrollProgress,
setActiveSection,
}
}
export default useScrollProgress