update how it works
This commit is contained in:
@@ -2,6 +2,14 @@ import type { NextConfig } from "next";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
output: "export",
|
||||
// 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.
|
||||
// WebGLRenderer: Context Lost") and the canvas stays blank. This is a known
|
||||
// React-Three-Fiber + StrictMode incompatibility. Disabling it is a DEV-ONLY
|
||||
// change (production never runs StrictMode's double-mount) and does not affect
|
||||
// any other page's runtime behavior.
|
||||
reactStrictMode: false,
|
||||
images: {
|
||||
unoptimized: true,
|
||||
formats: ["image/avif", "image/webp"],
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -21,7 +21,7 @@
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"react-leaflet": "^5.0.0",
|
||||
"three": "^0.184.0",
|
||||
"three": "0.171.0",
|
||||
"zustand": "^5.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -10764,9 +10764,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/three": {
|
||||
"version": "0.184.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.184.0.tgz",
|
||||
"integrity": "sha512-wtTRjG92pM5eUg/KuUnHsqSAlPM296brTOcLgMRqEeylYTh/CdtvKUvCyyCQTzFuStieWxvZb8mVTMvdPyUpxg==",
|
||||
"version": "0.171.0",
|
||||
"resolved": "https://registry.npmjs.org/three/-/three-0.171.0.tgz",
|
||||
"integrity": "sha512-Y/lAXPaKZPcEdkKjh0JOAHVv8OOnv/NDJqm0wjfCzyQmfKxV7zvkwsnBgPBKTzJHToSOhRGQAGbPJObT59B/PQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/three-mesh-bvh": {
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"react-leaflet": "^5.0.0",
|
||||
"three": "^0.184.0",
|
||||
"three": "0.171.0",
|
||||
"zustand": "^5.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -19,7 +19,9 @@ export default function HowItWorksHero() {
|
||||
|
||||
return (
|
||||
<>
|
||||
<style dangerouslySetInnerHTML={{ __html: `
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
.howits-hero-custom-bg.elementor-repeater-item-3264830,
|
||||
.howits-hero-custom-bg.elementor-repeater-item-6867061 {
|
||||
background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.1)), url('/images/home1-slide-1.png') !important;
|
||||
@@ -219,19 +221,44 @@ export default function HowItWorksHero() {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
`}} />
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
<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">
|
||||
<div className="elementor-element elementor-element-6c7cbcb elementor-widget elementor-widget-logico_content_slider" data-id="6c7cbcb" data-element_type="widget" data-e-type="widget" data-widget_type="logico_content_slider.default">
|
||||
<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"
|
||||
>
|
||||
<div
|
||||
className="elementor-element elementor-element-6c7cbcb elementor-widget elementor-widget-logico_content_slider"
|
||||
data-id="6c7cbcb"
|
||||
data-element_type="widget"
|
||||
data-e-type="widget"
|
||||
data-widget_type="logico_content_slider.default"
|
||||
>
|
||||
<div className="elementor-widget-container">
|
||||
<div className="logico-content-slider-widget">
|
||||
<div className="content-slider-wrapper">
|
||||
<div className="content-slider-container">
|
||||
<div className="content-slider owl-carousel owl-theme nav-view-vertical nav-h-position-right nav-v-position-bottom owl-loaded owl-drag">
|
||||
|
||||
<div className="owl-stage-outer" style={{ position: "relative", overflow: "hidden", height: "800px" }}>
|
||||
<div className="owl-stage" style={{ position: "relative", width: "100%", height: "100%" }}>
|
||||
|
||||
<div
|
||||
className="owl-stage-outer"
|
||||
style={{
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
height: "800px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="owl-stage"
|
||||
style={{
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
}}
|
||||
>
|
||||
{/* Slide 1 */}
|
||||
<div
|
||||
className={`owl-item ${activeSlide === 0 ? "active" : ""}`}
|
||||
@@ -239,20 +266,30 @@ export default function HowItWorksHero() {
|
||||
position: "relative",
|
||||
width: "100%",
|
||||
opacity: activeSlide === 0 ? 1 : 0,
|
||||
visibility: activeSlide === 0 ? "visible" : "hidden",
|
||||
transition: "opacity 0.8s ease-in-out, visibility 0.8s ease-in-out",
|
||||
zIndex: activeSlide === 0 ? 2 : 1
|
||||
visibility:
|
||||
activeSlide === 0 ? "visible" : "hidden",
|
||||
transition:
|
||||
"opacity 0.8s ease-in-out, visibility 0.8s ease-in-out",
|
||||
zIndex: activeSlide === 0 ? 2 : 1,
|
||||
}}
|
||||
>
|
||||
<div className="content-item slider-item elementor-repeater-item-3264830 slide-style-standard howits-hero-custom-bg">
|
||||
<div className="slide-content">
|
||||
<div className="slide-content-inner">
|
||||
<h1 className="content-slider-item-heading logico-content-wrapper-1">
|
||||
<span className="heading-content">One Journey. Complete<br />Control.</span>
|
||||
<span className="heading-content">
|
||||
One Journey. Complete
|
||||
<br />
|
||||
Control.
|
||||
</span>
|
||||
</h1>
|
||||
<div className="content-slider-item-text logico-content-wrapper-2">
|
||||
<div className="text-content">
|
||||
<p>See how Doormile connects first, mid, and last mile into a seamless delivery experience powered by MileTruth™ AI.</p>
|
||||
<p>
|
||||
See how Doormile connects first, mid, and
|
||||
last mile into a seamless delivery
|
||||
experience powered by MileTruth™ AI.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -269,27 +306,36 @@ export default function HowItWorksHero() {
|
||||
left: 0,
|
||||
width: "100%",
|
||||
opacity: activeSlide === 1 ? 1 : 0,
|
||||
visibility: activeSlide === 1 ? "visible" : "hidden",
|
||||
transition: "opacity 0.8s ease-in-out, visibility 0.8s ease-in-out",
|
||||
zIndex: activeSlide === 1 ? 2 : 1
|
||||
visibility:
|
||||
activeSlide === 1 ? "visible" : "hidden",
|
||||
transition:
|
||||
"opacity 0.8s ease-in-out, visibility 0.8s ease-in-out",
|
||||
zIndex: activeSlide === 1 ? 2 : 1,
|
||||
}}
|
||||
>
|
||||
<div className="content-item slider-item elementor-repeater-item-6867061 slide-style-standard howits-hero-custom-bg">
|
||||
<div className="slide-content">
|
||||
<div className="slide-content-inner">
|
||||
<h1 className="content-slider-item-heading logico-content-wrapper-1">
|
||||
<span className="heading-content">A New Freight<br />Experience</span>
|
||||
<span className="heading-content">
|
||||
A New Logisitics
|
||||
<br />
|
||||
Experience
|
||||
</span>
|
||||
</h1>
|
||||
<div className="content-slider-item-text logico-content-wrapper-2">
|
||||
<div className="text-content">
|
||||
<p>See how Doormile connects first, mid, and last mile into a seamless delivery experience powered by MileTruth™ AI.</p>
|
||||
<p>
|
||||
See how Doormile connects first, mid, and
|
||||
last mile into a seamless delivery
|
||||
experience powered by MileTruth™ AI.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -298,42 +344,98 @@ export default function HowItWorksHero() {
|
||||
<button
|
||||
type="button"
|
||||
className="owl-next"
|
||||
onClick={() => setActiveSlide((prev) => (prev === 0 ? 1 : 0))}
|
||||
onClick={() =>
|
||||
setActiveSlide((prev) => (prev === 0 ? 1 : 0))
|
||||
}
|
||||
aria-label="Next"
|
||||
style={{ cursor: "pointer", border: "none", outline: "none" }}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="owl-prev"
|
||||
onClick={() => setActiveSlide((prev) => (prev === 0 ? 1 : 0))}
|
||||
onClick={() =>
|
||||
setActiveSlide((prev) => (prev === 0 ? 1 : 0))
|
||||
}
|
||||
aria-label="Previous"
|
||||
style={{ cursor: "pointer", border: "none", outline: "none" }}
|
||||
style={{
|
||||
cursor: "pointer",
|
||||
border: "none",
|
||||
outline: "none",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Progress indicators */}
|
||||
<div className="slider-footer slider-footer-position-after slider-footer-width-full slider-footer-view-inside">
|
||||
<div className="slider-footer-content">
|
||||
<div className="slider-pagination" style={{ display: "flex", justifyContent: "flex-end", alignItems: "center", gap: "10px" }}>
|
||||
<div className="slider-progress-wrapper" style={{ marginRight: "35px", display: "flex", flexDirection: "column", alignItems: "flex-start" }}>
|
||||
<div style={{ fontSize: "16px", fontWeight: 600, color: "#FFFFFF", marginBottom: "4px" }}>
|
||||
<span className="slider-progress-current">{activeSlide === 0 ? "01" : "02"}</span>
|
||||
<div
|
||||
className="slider-pagination"
|
||||
style={{
|
||||
display: "flex",
|
||||
justifyContent: "flex-end",
|
||||
alignItems: "center",
|
||||
gap: "10px",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
className="slider-progress-wrapper"
|
||||
style={{
|
||||
marginRight: "35px",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: "16px",
|
||||
fontWeight: 600,
|
||||
color: "#FFFFFF",
|
||||
marginBottom: "4px",
|
||||
}}
|
||||
>
|
||||
<span className="slider-progress-current">
|
||||
{activeSlide === 0 ? "01" : "02"}
|
||||
</span>
|
||||
{" / "}
|
||||
<span className="slider-progress-all" style={{ opacity: 0.6 }}>02</span>
|
||||
<span
|
||||
className="slider-progress-all"
|
||||
style={{ opacity: 0.6 }}
|
||||
>
|
||||
02
|
||||
</span>
|
||||
</div>
|
||||
{/* Progress line — red bar slides to match the active slide (mirrors the home hero) */}
|
||||
<div style={{ width: "80px", height: "2px", background: "rgba(255, 255, 255, 0.2)", position: "relative", borderRadius: "1px", overflow: "hidden" }}>
|
||||
<div style={{
|
||||
<div
|
||||
style={{
|
||||
width: "80px",
|
||||
height: "2px",
|
||||
background: "rgba(255, 255, 255, 0.2)",
|
||||
position: "relative",
|
||||
borderRadius: "1px",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
position: "absolute",
|
||||
left: activeSlide === 0 ? "0" : "50%",
|
||||
width: "50%",
|
||||
height: "100%",
|
||||
background: "#c01227",
|
||||
transition: "left 0.3s ease"
|
||||
}} />
|
||||
transition: "left 0.3s ease",
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="owl-dots owl-dots-6c7cbcb" style={{ display: "none" }}>
|
||||
<div
|
||||
className="owl-dots owl-dots-6c7cbcb"
|
||||
style={{ display: "none" }}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
role="button"
|
||||
@@ -354,7 +456,6 @@ export default function HowItWorksHero() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,35 +2,57 @@ import React from "react";
|
||||
import Image from "next/image";
|
||||
import { ScrollReveal } from "../../animations/Reveal";
|
||||
|
||||
type Stat = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
type Stage = {
|
||||
img: string;
|
||||
label: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
desc: string;
|
||||
points: string[];
|
||||
stats?: Stat[];
|
||||
};
|
||||
|
||||
const STAGES: Stage[] = [
|
||||
{
|
||||
img: "/images/first-mile-approach.jpg",
|
||||
label: "01 / First Mile",
|
||||
title: "Origin to Hub",
|
||||
desc: "We collect freight directly from your facility, optimise vehicle assignment in real time, and consolidate loads for maximum efficiency before they reach the hub.",
|
||||
label: "Stage 01",
|
||||
title: "First Mile Warehouse",
|
||||
subtitle: "Consolidation & Prep",
|
||||
desc: "Incoming shipments are securely loaded, checked, and queued for transfer in our high-capacity fulfillment centers.",
|
||||
points: ["AI-scheduled pickups", "Dynamic load consolidation", "Yard & dock management"],
|
||||
stats: [
|
||||
{ value: "14,250", label: "Parcels Processed" },
|
||||
{ value: "99.98%", label: "Sorting Accuracy" }
|
||||
]
|
||||
},
|
||||
{
|
||||
img: "/images/mid-mile-approach.jpg",
|
||||
label: "02 / Mid Mile",
|
||||
title: "Hub to Hub Transit",
|
||||
desc: "Freight moves between hubs on optimised line-haul routes. Real-time tracking, cross-docking, and SLA monitoring keep every shipment on schedule.",
|
||||
label: "Stage 02",
|
||||
title: "Mid Mile Transit",
|
||||
subtitle: "Hub-to-Hub Transport",
|
||||
desc: "Freight is routed dynamically through our network of strategically located hubs using automated sortation and linehaul scheduling.",
|
||||
points: ["Optimised line-haul routing", "Cross-docking & sortation", "Live SLA monitoring"],
|
||||
stats: [
|
||||
{ value: "1,240+", label: "Daily Line-Hauls" },
|
||||
{ value: "98.5%", label: "SLA Adherence" }
|
||||
]
|
||||
},
|
||||
{
|
||||
img: "/images/last-mile-approach.jpg",
|
||||
label: "03 / Last Mile",
|
||||
title: "Hub to Doorstep",
|
||||
desc: "The final and most complex phase. We optimise multi-stop routes, deliver within precise windows, and capture digital proof of delivery at every door.",
|
||||
label: "Stage 03",
|
||||
title: "Last Mile Delivery",
|
||||
subtitle: "Hub to Doorstep",
|
||||
desc: "The final handoff. Our routing engine optimizes multi-stop itineraries to deliver parcels directly to customers' doorsteps in record time.",
|
||||
points: ["Multi-stop route optimisation", "Precise delivery windows", "Digital proof of delivery"],
|
||||
stats: [
|
||||
{ value: "450K+", label: "Happy Deliveries" },
|
||||
{ value: "2.8 Hours", label: "Average Turnaround" }
|
||||
]
|
||||
},
|
||||
];
|
||||
|
||||
@@ -40,18 +62,12 @@ export default function WhyChooseDoormile() {
|
||||
<style dangerouslySetInnerHTML={{ __html: `
|
||||
/* =====================================================================
|
||||
"Why Businesses Choose Doormile" — First / Mid / Last Mile stage cards.
|
||||
Dark rounded card on the white page (consistent with the Miles3
|
||||
section), each stage shown with a photo, numbered red label, title,
|
||||
description and a red-checkmark feature list.
|
||||
Card titles are <h3>; theme-core forces a dark color on bare headings
|
||||
(.logico-front-end h3:not([class*=logico-title-h]) @ (0,2,1)), so the
|
||||
white title rule is prefixed to outrank it.
|
||||
===================================================================== */
|
||||
.wcd-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: auto;
|
||||
margin: -250px 20px 20px 20px; /* Snug pull-up overlap to touch Miles3 columns without covering their text! */
|
||||
margin: -250px 20px 20px 20px;
|
||||
background-color: #1F1F1F;
|
||||
border-radius: 0 0 25px 25px;
|
||||
padding: 50px 0 110px;
|
||||
@@ -63,7 +79,6 @@ export default function WhyChooseDoormile() {
|
||||
padding: 0 50px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* Centered header block (eyebrow + heading) with a faint map backdrop */
|
||||
.wcd-head {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
@@ -78,16 +93,16 @@ export default function WhyChooseDoormile() {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 75%; /* Shifted down to the bottom of the header block to overlap the top of the cards */
|
||||
top: 75%;
|
||||
transform: translateX(-50%);
|
||||
width: min(1180px, 95%);
|
||||
aspect-ratio: 2 / 1;
|
||||
background: url('/images/bg-map.png') center / contain no-repeat;
|
||||
opacity: 0.06; /* Elegant faint visibility */
|
||||
filter: invert(1); /* Invert dark map dots to white/light-gray to make them visible on the #1F1F1F background */
|
||||
opacity: 0.06;
|
||||
filter: invert(1);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
animation: wcd-float 20s ease-in-out infinite; /* Premium floating map animation */
|
||||
animation: wcd-float 20s ease-in-out infinite;
|
||||
}
|
||||
.wcd-card-wrapper {
|
||||
display: flex;
|
||||
@@ -127,18 +142,23 @@ export default function WhyChooseDoormile() {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Premium Glassmorphism & Card Interaction */
|
||||
.wcd-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: rgba(255, 255, 255, 0.02);
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.03) 0%, rgba(255, 255, 255, 0.01) 100%);
|
||||
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
transition: border-color 0.4s ease, box-shadow 0.4s ease, transform 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
transition: border-color 0.4s cubic-bezier(0.165, 0.84, 0.44, 1),
|
||||
box-shadow 0.4s cubic-bezier(0.165, 0.84, 0.44, 1),
|
||||
transform 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
.wcd-card:hover {
|
||||
border-color: #c01227 !important;
|
||||
box-shadow: 0 10px 30px rgba(192, 18, 39, 0.25) !important;
|
||||
box-shadow: 0 20px 40px rgba(192, 18, 39, 0.15), inset 0 0 20px rgba(255, 255, 255, 0.02) !important;
|
||||
transform: translateY(-8px);
|
||||
}
|
||||
.wcd-card-media {
|
||||
@@ -152,75 +172,106 @@ export default function WhyChooseDoormile() {
|
||||
transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
}
|
||||
.wcd-card:hover .wcd-card-media img {
|
||||
transform: scale(1.06);
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
/* Body Data Container (Unified Div) */
|
||||
.wcd-card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
/* Modern Pill Badge for Label */
|
||||
.wcd-card-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
align-self: flex-start;
|
||||
padding: 6px 14px;
|
||||
background: rgba(192, 18, 39, 0.08);
|
||||
border: 1px solid rgba(192, 18, 39, 0.25);
|
||||
border-radius: 100px;
|
||||
font-family: var(--font-manrope), "Manrope", sans-serif;
|
||||
font-size: 14px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: #c01227;
|
||||
color: #ff3344;
|
||||
margin: 0 0 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.wcd-card:hover .wcd-card-label {
|
||||
background: rgba(192, 18, 39, 0.18);
|
||||
border-color: #c01227;
|
||||
box-shadow: 0 0 10px rgba(192, 18, 39, 0.15);
|
||||
}
|
||||
|
||||
.logico-front-end .wcd-section h3.wcd-card-title {
|
||||
font-family: var(--font-manrope), "Manrope", sans-serif;
|
||||
font-size: 32px;
|
||||
font-size: 26px;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
line-height: 1.2;
|
||||
letter-spacing: -0.02em;
|
||||
text-transform: uppercase;
|
||||
color: #FFFFFF;
|
||||
-webkit-text-fill-color: #FFFFFF;
|
||||
margin: 0 0 22px;
|
||||
margin: 0 0 4px;
|
||||
}
|
||||
|
||||
.wcd-card-subtitle {
|
||||
font-family: var(--font-manrope), "Manrope", sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.55);
|
||||
margin: 0 0 20px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
|
||||
.wcd-card-divider {
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.02) 100%);
|
||||
margin: 0 0 20px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wcd-card-desc {
|
||||
font-size: 17px;
|
||||
font-size: 15px;
|
||||
font-weight: 400;
|
||||
line-height: 1.6;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
margin: 0 0 34px;
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
|
||||
.wcd-card-points {
|
||||
list-style: none;
|
||||
margin: auto 0 0;
|
||||
margin: 0 0 30px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
gap: 12px;
|
||||
}
|
||||
.wcd-section .wcd-card-points li {
|
||||
/* Flex row so the check icon and its label always sit on the same line.
|
||||
Scoped with .wcd-section to outrank the global ".logico-front-end ul li"
|
||||
theme rule, which adds 1.7em padding + a fontello bullet icon. */
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
padding-left: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
line-height: 1.3;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
/* Suppress the theme's default fontello list bullet
|
||||
(.logico-front-end ul li:before) so only our circle-check SVG renders. */
|
||||
.wcd-section .wcd-card-points li::before {
|
||||
content: none;
|
||||
display: none;
|
||||
}
|
||||
/* Clean circle-check feature icon (inline SVG, see markup below) — replaces
|
||||
the old border-based chevron. Brand red with thin, rounded strokes. */
|
||||
|
||||
.wcd-card-points .wcd-check {
|
||||
flex: 0 0 auto;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
margin-top: 0.12em;
|
||||
margin-top: 0.08em;
|
||||
color: #c01227;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
@@ -228,16 +279,47 @@ export default function WhyChooseDoormile() {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* Integrated Stats Section */
|
||||
.wcd-card-stats {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
margin-top: auto;
|
||||
padding-top: 24px;
|
||||
border-top: 1px dashed rgba(255, 255, 255, 0.12);
|
||||
}
|
||||
.wcd-stat-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.wcd-stat-value {
|
||||
font-family: var(--font-manrope), "Manrope", sans-serif;
|
||||
font-size: 24px;
|
||||
font-weight: 800;
|
||||
color: #FFFFFF;
|
||||
line-height: 1.1;
|
||||
letter-spacing: -0.02em;
|
||||
transition: color 0.3s ease;
|
||||
}
|
||||
.wcd-card:hover .wcd-stat-value {
|
||||
color: #ff3344;
|
||||
}
|
||||
.wcd-stat-label {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: rgba(255, 255, 255, 0.45);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-top: 4px;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
@media (max-width: 1020px) {
|
||||
/* No pull-up overlap on mobile/tablet: the Miles3 cards stack into a
|
||||
1–2 col layout, so a negative margin-top covers the last card's
|
||||
text. Both sections share #1F1F1F + equal side margins, so butting
|
||||
them at margin-top:0 keeps the seamless dark panel. */
|
||||
.wcd-section { margin: 0 15px 15px 15px; padding: 40px 0 80px; }
|
||||
.wcd-inner { padding: 0 30px; }
|
||||
.wcd-grid { grid-template-columns: 1fr; gap: 24px; }
|
||||
.wcd-card-body { padding: 32px; }
|
||||
.logico-front-end .wcd-section h3.wcd-card-title { font-size: 28px; }
|
||||
.logico-front-end .wcd-section h3.wcd-card-title { font-size: 24px; }
|
||||
}
|
||||
@media (max-width: 480px) {
|
||||
.wcd-section { margin: 0 12px 12px 12px; border-radius: 0 0 20px 20px; padding: 30px 0 64px; }
|
||||
@@ -273,10 +355,20 @@ export default function WhyChooseDoormile() {
|
||||
sizes="(max-width: 1020px) 100vw, 33vw"
|
||||
/>
|
||||
</div>
|
||||
{/* Single Parent Div for All Data Content */}
|
||||
<div className="wcd-card-body">
|
||||
<div className="wcd-card-label">{stage.label}</div>
|
||||
|
||||
<h3 className="wcd-card-title">{stage.title}</h3>
|
||||
|
||||
{stage.subtitle && (
|
||||
<div className="wcd-card-subtitle">{stage.subtitle}</div>
|
||||
)}
|
||||
|
||||
<div className="wcd-card-divider" />
|
||||
|
||||
<p className="wcd-card-desc">{stage.desc}</p>
|
||||
|
||||
<ul className="wcd-card-points">
|
||||
{stage.points.map((point) => (
|
||||
<li key={point}>
|
||||
@@ -297,6 +389,18 @@ export default function WhyChooseDoormile() {
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Integrated Statistics Grid */}
|
||||
{stage.stats && stage.stats.length > 0 && (
|
||||
<div className="wcd-card-stats">
|
||||
{stage.stats.map((stat) => (
|
||||
<div key={stat.label} className="wcd-stat-item">
|
||||
<span className="wcd-stat-value">{stat.value}</span>
|
||||
<span className="wcd-stat-label">{stat.label}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
</ScrollReveal>
|
||||
|
||||
@@ -15,11 +15,21 @@ export default function CameraRig() {
|
||||
useFrame((state, delta) => {
|
||||
const { camera } = state
|
||||
|
||||
// maath's easing.damp3 divides by delta internally; a delta of 0 (coincident
|
||||
// or first frames) yields NaN that poisons the damper and would push the
|
||||
// camera to NaN — blanking the whole scene. Clamp delta to a safe range.
|
||||
const dt = Number.isFinite(delta) && delta > 0 ? Math.min(delta, 0.1) : 1 / 60
|
||||
|
||||
// Smoothly damp the camera position towards the target position
|
||||
easing.damp3(camera.position, targetPosition, 0.35, delta)
|
||||
easing.damp3(camera.position, targetPosition, 0.35, dt)
|
||||
|
||||
// Smoothly damp the camera focus target (lookAt)
|
||||
easing.damp3(currentLookAt.current, lookAtTarget, 0.25, delta)
|
||||
easing.damp3(currentLookAt.current, lookAtTarget, 0.25, dt)
|
||||
|
||||
// Defensive recovery: if anything upstream produced a non-finite value, snap
|
||||
// back to the target so the camera never gets stuck at NaN (black screen).
|
||||
if (!Number.isFinite(camera.position.x)) camera.position.copy(targetPosition)
|
||||
if (!Number.isFinite(currentLookAt.current.x)) currentLookAt.current.copy(lookAtTarget)
|
||||
|
||||
// Apply lookAt orientation using the interpolated target vector
|
||||
camera.lookAt(currentLookAt.current)
|
||||
|
||||
@@ -2,7 +2,6 @@ import React, { useRef, useEffect } from 'react'
|
||||
import { Canvas, useFrame } from '@react-three/fiber'
|
||||
import { Environment, SoftShadows } from '@react-three/drei'
|
||||
import * as THREE from 'three'
|
||||
import { EffectComposer, Bloom, Vignette } from '@react-three/postprocessing'
|
||||
import { Model as SceneModel } from '../models/Scene3D'
|
||||
import CameraRig from './CameraRig'
|
||||
import TruckAnimation from './TruckAnimation'
|
||||
@@ -127,22 +126,16 @@ export default React.memo(function Experience({ dashboardRefs, wheelRefs, truckR
|
||||
{/* Dynamic camera rig with damping and target interpolation */}
|
||||
<CameraRig />
|
||||
|
||||
{/* Post-processing — Bloom + Vignette only.
|
||||
The original Vite code added SSAO with a NormalPass, but on this heavy
|
||||
scene (32MB GLB, ~500 meshes, SoftShadows) the extra full-scene normal
|
||||
render exhausts the WebGL context and it is lost (blank canvas). The
|
||||
site's other R3F canvases (e.g. StrategyCanvas) use a Bloom-only
|
||||
composer for the same reason; Bloom + the screen-space Vignette keep the
|
||||
cinematic look without the SSAO normal pass. */}
|
||||
<EffectComposer multisampling={2}>
|
||||
<Bloom
|
||||
intensity={0.2}
|
||||
luminanceThreshold={0.95}
|
||||
luminanceSmoothing={0.05}
|
||||
mipmapBlur
|
||||
/>
|
||||
<Vignette eskil={false} offset={0.1} darkness={0.4} />
|
||||
</EffectComposer>
|
||||
{/* Post-processing (EffectComposer/Bloom/Vignette) intentionally omitted.
|
||||
@react-three/postprocessing's EffectComposer reads
|
||||
`renderer.getContextAttributes().alpha` while initializing its buffers;
|
||||
under Next dev's React StrictMode the canvas's WebGL context is torn
|
||||
down and re-created, so that read hits a null context and throws
|
||||
"Cannot read properties of null (reading 'alpha')", crashing the whole
|
||||
scene. Dropping the composer renders the scene directly (lighting +
|
||||
shadows + environment carry the look). To re-add Bloom later, set
|
||||
`reactStrictMode: false` in next.config.ts and restore a Bloom-only
|
||||
composer. */}
|
||||
</Canvas>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -38,6 +38,14 @@ export default function TruckAnimation({ truckRef, wheelRefs }) {
|
||||
useFrame((state, delta) => {
|
||||
if (!truckRef.current) return
|
||||
|
||||
// r3f can emit delta === 0 (coincident frames, the first frame, or after a
|
||||
// long main-thread block while the 32MB scene parses). maath's easing.damp
|
||||
// divides by delta internally, so a 0 yields NaN/Infinity that poisons the
|
||||
// damper's stored velocity — and from then on truckPath.getPoint(NaN) throws
|
||||
// "Cannot read properties of undefined (reading 'x')". Clamp delta to a safe
|
||||
// positive range before any damping.
|
||||
const dt = Number.isFinite(delta) && delta > 0 ? Math.min(delta, 0.1) : 1 / 60
|
||||
|
||||
// Detect scroll direction changes from the actual page scroll progress
|
||||
const deltaScroll = scrollProgress - lastScrollProgressRef.current
|
||||
if (deltaScroll < -0.0001) {
|
||||
@@ -95,7 +103,15 @@ export default function TruckAnimation({ truckRef, wheelRefs }) {
|
||||
}
|
||||
|
||||
// Smoothly damp the 1D progress scalar along the curve path
|
||||
easing.damp(dampedProgressRef, 'current', truckProgress, 0.30, delta)
|
||||
easing.damp(dampedProgressRef, 'current', truckProgress, 0.30, dt)
|
||||
|
||||
// Defensive: keep the spline parameter a finite value in [0,1]. getPoint(NaN)
|
||||
// or an out-of-range t reads an undefined curve point and throws.
|
||||
if (!Number.isFinite(dampedProgressRef.current)) {
|
||||
dampedProgressRef.current = truckProgress
|
||||
if (dampedProgressRef.__damp) dampedProgressRef.__damp = {} // clear any poisoned velocity
|
||||
}
|
||||
dampedProgressRef.current = THREE.MathUtils.clamp(dampedProgressRef.current, 0, 1)
|
||||
|
||||
// Evaluate the 3D position and orientation directly on the spline curve
|
||||
const position = truckPath.getPoint(dampedProgressRef.current)
|
||||
@@ -128,7 +144,7 @@ export default function TruckAnimation({ truckRef, wheelRefs }) {
|
||||
}
|
||||
|
||||
// Smoothly damp the extra rotation angle directly (prevents pitch/roll glitches or 3D target collapse)
|
||||
easing.damp(extraRotationRef, 'current', targetExtraRotation, 0.20, delta)
|
||||
easing.damp(extraRotationRef, 'current', targetExtraRotation, 0.20, dt)
|
||||
|
||||
// Apply the yaw pivot around the local vertical axis
|
||||
truckRef.current.rotateY(extraRotationRef.current)
|
||||
|
||||
@@ -15,16 +15,31 @@
|
||||
here because the site has a fixed header and an ancestor `overflow:hidden`.
|
||||
============================================================================ */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap');
|
||||
/* ---- Doormile card design tokens (single system for all four stages) ----
|
||||
Brand red #c01227, ink #1F1F1F, fonts inherited from the site (Manrope body /
|
||||
Space Grotesk headings, exposed as CSS vars on <html> by layout.tsx). */
|
||||
.dm-hiw-3d {
|
||||
--dm-red: #c01227;
|
||||
--dm-red-soft: rgba(192, 18, 39, 0.10);
|
||||
--dm-ink: #1f1f1f;
|
||||
--dm-body: #4b5563;
|
||||
--dm-muted: #8a8f98;
|
||||
--dm-card-bg: rgba(255, 255, 255, 0.86);
|
||||
--dm-card-border: rgba(15, 23, 42, 0.08);
|
||||
--dm-card-radius: 18px;
|
||||
--dm-card-shadow: 0 24px 60px -28px rgba(15, 23, 42, 0.45);
|
||||
--dm-font-head: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif;
|
||||
--dm-font-body: var(--font-manrope), system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
/* ---- Section shell + self-managed fixed pin ---- */
|
||||
.dm-hiw-3d {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
font-family: 'Outfit', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
line-height: 1.47;
|
||||
font-family: var(--dm-font-body);
|
||||
line-height: 1.5;
|
||||
font-weight: 400;
|
||||
color: #1d1d1f;
|
||||
color: var(--dm-ink);
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
@@ -117,39 +132,40 @@
|
||||
transform: scale(1.15);
|
||||
}
|
||||
.dm-hiw-3d .side-nav-item.active .side-nav-label {
|
||||
color: #0071e3;
|
||||
color: var(--dm-red);
|
||||
}
|
||||
.dm-hiw-3d .side-nav-item.active .side-nav-dot {
|
||||
background-color: #0071e3;
|
||||
background-color: var(--dm-red);
|
||||
transform: scale(1.3);
|
||||
box-shadow: 0 0 8px rgba(0, 113, 227, 0.3);
|
||||
box-shadow: 0 0 8px rgba(192, 18, 39, 0.35);
|
||||
}
|
||||
|
||||
.dm-hiw-3d .section-close-btn {
|
||||
margin-top: 20px;
|
||||
background-color: #0071e3;
|
||||
margin-top: 18px;
|
||||
background-color: var(--dm-red);
|
||||
color: #ffffff;
|
||||
border: none;
|
||||
font-family: inherit;
|
||||
font-family: var(--dm-font-body);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
padding: 8px 16px;
|
||||
border-radius: 18px;
|
||||
letter-spacing: 0.01em;
|
||||
padding: 9px 18px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
transition: all 0.25s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
box-shadow: 0 4px 12px rgba(0, 113, 227, 0.15);
|
||||
transition: background-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
|
||||
box-shadow: 0 8px 18px -8px rgba(192, 18, 39, 0.55);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: auto;
|
||||
}
|
||||
.dm-hiw-3d .section-close-btn:hover {
|
||||
background-color: #0077ed;
|
||||
box-shadow: 0 6px 16px rgba(0, 113, 227, 0.3);
|
||||
background-color: #a30f20;
|
||||
box-shadow: 0 10px 22px -8px rgba(192, 18, 39, 0.7);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.dm-hiw-3d .section-close-btn:active {
|
||||
transform: translateY(1px);
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
/* ---- Story stage text panels ---- */
|
||||
@@ -162,112 +178,136 @@
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Side placement keeps the moving truck / scene visible in the centre, with the
|
||||
card vertically centred (top/bottom:0 + margin:auto works with the GSAP reveal,
|
||||
which only animates translateY/scale). */
|
||||
.dm-hiw-3d #first-mile-section,
|
||||
.dm-hiw-3d #mid-mile-section,
|
||||
.dm-hiw-3d #last-mile-section {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: max-content;
|
||||
margin-top: auto;
|
||||
margin-bottom: auto;
|
||||
}
|
||||
.dm-hiw-3d #first-mile-section,
|
||||
.dm-hiw-3d #last-mile-section {
|
||||
left: 6%;
|
||||
left: clamp(24px, 5vw, 72px);
|
||||
}
|
||||
.dm-hiw-3d #mid-mile-section {
|
||||
right: 6%;
|
||||
right: clamp(24px, 5vw, 72px);
|
||||
}
|
||||
/* Final ecosystem panel — same card system, centred, a touch wider for its
|
||||
timeline, and reduced from the old 500px so it no longer blocks the scene. */
|
||||
.dm-hiw-3d #analytics-section {
|
||||
left: 50%;
|
||||
right: auto;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%) translateY(18px) scale(0.97);
|
||||
max-width: 500px;
|
||||
width: 90%;
|
||||
background: rgba(20, 21, 26, 0.88); /* Deep slate blackboard theme */
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
|
||||
color: #ffffff;
|
||||
}
|
||||
.dm-hiw-3d #analytics-section.active {
|
||||
backdrop-filter: blur(24px);
|
||||
-webkit-backdrop-filter: blur(24px);
|
||||
}
|
||||
.dm-hiw-3d #analytics-section .section-title { color: #ffffff; }
|
||||
.dm-hiw-3d #analytics-section .section-subtitle { color: #a1a1a6; }
|
||||
.dm-hiw-3d #analytics-section .step-title { color: #ffffff; }
|
||||
.dm-hiw-3d #analytics-section .step-description { color: #a1a1a6; }
|
||||
.dm-hiw-3d #analytics-section .step-line {
|
||||
background: linear-gradient(to bottom, #0071e3 40%, rgba(255, 255, 255, 0.1) 100%);
|
||||
width: min(400px, 90vw);
|
||||
max-height: calc(100vh - 140px);
|
||||
}
|
||||
|
||||
/* ---- Unified card — all four stages share this exact chrome ---- */
|
||||
.dm-hiw-3d .section-panel {
|
||||
position: absolute;
|
||||
max-width: 380px;
|
||||
padding: 30px;
|
||||
background: rgba(255, 255, 255, 0.76);
|
||||
width: min(340px, 31vw);
|
||||
max-height: calc(100vh - 168px); /* never taller than the viewport */
|
||||
overflow-y: auto;
|
||||
padding: 24px 26px 26px;
|
||||
background: var(--dm-card-bg);
|
||||
backdrop-filter: blur(0px);
|
||||
-webkit-backdrop-filter: blur(0px);
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 24px;
|
||||
border: 1px solid var(--dm-card-border);
|
||||
border-top: 3px solid var(--dm-red); /* brand accent that unifies every card */
|
||||
border-radius: var(--dm-card-radius);
|
||||
box-shadow: var(--dm-card-shadow);
|
||||
opacity: 0;
|
||||
transform: translateY(18px) scale(0.97);
|
||||
visibility: hidden;
|
||||
transition: backdrop-filter 0.9s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
-webkit-backdrop-filter 0.9s cubic-bezier(0.16, 1, 0.3, 1),
|
||||
visibility 0.9s;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.03);
|
||||
pointer-events: none;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.dm-hiw-3d .section-panel.active {
|
||||
visibility: visible;
|
||||
backdrop-filter: blur(24px);
|
||||
-webkit-backdrop-filter: blur(24px);
|
||||
backdrop-filter: blur(22px) saturate(140%);
|
||||
-webkit-backdrop-filter: blur(22px) saturate(140%);
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
/* Typography — one scale across all cards. !important defeats the page's
|
||||
Elementor h2/h3 styles (loaded via /css/site.css with their own !important),
|
||||
which were inflating these headings to ~80px and overflowing the cards. */
|
||||
.dm-hiw-3d .section-badge {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
letter-spacing: 1px;
|
||||
color: #0071e3;
|
||||
margin-bottom: 8px;
|
||||
display: inline-block;
|
||||
font-family: var(--dm-font-body) !important;
|
||||
font-size: 10.5px !important;
|
||||
text-transform: uppercase !important;
|
||||
font-weight: 700 !important;
|
||||
letter-spacing: 0.14em !important;
|
||||
color: var(--dm-red) !important;
|
||||
margin: 0 0 10px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-title {
|
||||
font-size: 26px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.6px;
|
||||
color: #1d1d1f;
|
||||
margin: 0 0 4px 0;
|
||||
font-family: var(--dm-font-head) !important;
|
||||
font-size: clamp(18px, 1.45vw, 22px) !important;
|
||||
font-weight: 700 !important;
|
||||
line-height: 1.15 !important;
|
||||
letter-spacing: -0.015em !important;
|
||||
text-transform: none !important;
|
||||
color: var(--dm-ink) !important;
|
||||
margin: 0 0 4px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-subtitle {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #86868b;
|
||||
margin: 0 0 14px 0;
|
||||
font-family: var(--dm-font-body) !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 500 !important;
|
||||
line-height: 1.35 !important;
|
||||
letter-spacing: 0 !important;
|
||||
text-transform: none !important;
|
||||
color: var(--dm-muted) !important;
|
||||
margin: 0 0 14px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-description {
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: #515154;
|
||||
margin-bottom: 20px;
|
||||
font-family: var(--dm-font-body) !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 400 !important;
|
||||
line-height: 1.55 !important;
|
||||
color: var(--dm-body) !important;
|
||||
margin: 0 0 18px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-metrics {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.05);
|
||||
gap: 18px;
|
||||
border-top: 1px solid rgba(15, 23, 42, 0.08);
|
||||
padding-top: 16px;
|
||||
}
|
||||
.dm-hiw-3d .metric-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
flex: 1;
|
||||
}
|
||||
.dm-hiw-3d .metric-value {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #1d1d1f;
|
||||
letter-spacing: -0.3px;
|
||||
font-family: var(--dm-font-head) !important;
|
||||
font-size: 18px !important;
|
||||
font-weight: 700 !important;
|
||||
line-height: 1.1 !important;
|
||||
letter-spacing: -0.01em !important;
|
||||
color: var(--dm-ink) !important;
|
||||
}
|
||||
.dm-hiw-3d .metric-label {
|
||||
font-size: 10px;
|
||||
font-weight: 500;
|
||||
color: #86868b;
|
||||
font-family: var(--dm-font-body) !important;
|
||||
font-size: 9.5px !important;
|
||||
font-weight: 600 !important;
|
||||
text-transform: uppercase !important;
|
||||
letter-spacing: 0.07em !important;
|
||||
color: var(--dm-muted) !important;
|
||||
}
|
||||
.dm-hiw-3d .font-green .metric-value { color: #34c759; }
|
||||
.dm-hiw-3d .font-green .metric-value { color: #1f9d57 !important; }
|
||||
|
||||
/* ---- Animations (keyframes left global; uniquely named) ---- */
|
||||
@keyframes dmHiwScrollWheel {
|
||||
@@ -284,12 +324,12 @@
|
||||
50% { transform: translateX(8px); }
|
||||
}
|
||||
|
||||
/* ---- Workflow steps styling inside the Analytics overlay ---- */
|
||||
/* ---- Final-panel timeline (inside #analytics-section) ---- */
|
||||
.dm-hiw-3d .workflow-steps {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
margin-top: 18px;
|
||||
gap: 4px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
.dm-hiw-3d .workflow-step { display: flex; gap: 14px; }
|
||||
.dm-hiw-3d .step-number-container {
|
||||
@@ -299,37 +339,43 @@
|
||||
width: 24px;
|
||||
}
|
||||
.dm-hiw-3d .step-number {
|
||||
font-family: var(--dm-font-head);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: #0071e3;
|
||||
background: rgba(0, 113, 227, 0.1);
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
color: var(--dm-red);
|
||||
background: var(--dm-red-soft);
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px solid rgba(0, 113, 227, 0.15);
|
||||
border: 1px solid rgba(192, 18, 39, 0.2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.dm-hiw-3d .step-line {
|
||||
width: 1px;
|
||||
width: 2px;
|
||||
flex-grow: 1;
|
||||
background: linear-gradient(to bottom, #0071e3 40%, rgba(0, 0, 0, 0.05) 100%);
|
||||
margin-top: 6px;
|
||||
min-height: 24px;
|
||||
background: linear-gradient(to bottom, var(--dm-red) 0%, rgba(192, 18, 39, 0.12) 100%);
|
||||
margin: 5px 0;
|
||||
min-height: 16px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.dm-hiw-3d .step-content { flex-grow: 1; }
|
||||
.dm-hiw-3d .step-content { flex-grow: 1; padding-bottom: 14px; }
|
||||
.dm-hiw-3d .step-title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #1d1d1f;
|
||||
margin: 0 0 2px 0;
|
||||
font-family: var(--dm-font-head) !important;
|
||||
font-size: 14px !important;
|
||||
font-weight: 700 !important;
|
||||
line-height: 1.2 !important;
|
||||
color: var(--dm-ink) !important;
|
||||
margin: 1px 0 3px !important;
|
||||
}
|
||||
.dm-hiw-3d .step-description {
|
||||
font-size: 11.5px;
|
||||
line-height: 1.45;
|
||||
color: #6e6e73;
|
||||
margin: 0;
|
||||
font-family: var(--dm-font-body) !important;
|
||||
font-size: 12px !important;
|
||||
line-height: 1.5 !important;
|
||||
color: var(--dm-body) !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* ---- Responsive ---- */
|
||||
@@ -341,11 +387,10 @@
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
.dm-hiw-3d .section-panel {
|
||||
max-width: 100%;
|
||||
width: calc(100vw - 40px);
|
||||
padding: 20px;
|
||||
border-radius: 18px;
|
||||
padding: 20px 22px 22px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
/* Tablet/mobile: cards bottom-centred so the truck/scene stays visible above. */
|
||||
.dm-hiw-3d #first-mile-section,
|
||||
.dm-hiw-3d #mid-mile-section,
|
||||
.dm-hiw-3d #last-mile-section,
|
||||
@@ -353,10 +398,10 @@
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
top: auto !important;
|
||||
bottom: 60px !important;
|
||||
bottom: 64px !important;
|
||||
transform: translateX(-50%) translateY(18px) scale(0.97) !important;
|
||||
max-width: 380px;
|
||||
width: calc(100vw - 120px) !important;
|
||||
width: min(380px, calc(100vw - 40px)) !important;
|
||||
max-height: calc(100vh - 200px) !important;
|
||||
}
|
||||
.dm-hiw-3d #first-mile-section.active,
|
||||
.dm-hiw-3d #mid-mile-section.active,
|
||||
@@ -364,7 +409,6 @@
|
||||
.dm-hiw-3d #analytics-section.active {
|
||||
transform: translateX(-50%) translateY(0) scale(1) !important;
|
||||
}
|
||||
.dm-hiw-3d #analytics-section { background: rgba(20, 21, 26, 0.92); }
|
||||
.dm-hiw-3d .side-navigation {
|
||||
bottom: 12px;
|
||||
top: auto;
|
||||
@@ -386,34 +430,36 @@
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.dm-hiw-3d .section-panel {
|
||||
padding: 16px !important;
|
||||
width: calc(100vw - 80px) !important;
|
||||
bottom: 40px !important;
|
||||
.dm-hiw-3d .section-panel,
|
||||
.dm-hiw-3d #analytics-section {
|
||||
padding: 16px 18px 18px !important;
|
||||
width: min(340px, calc(100vw - 28px)) !important;
|
||||
bottom: 56px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-badge {
|
||||
font-size: 9px !important;
|
||||
margin-bottom: 4px !important;
|
||||
font-size: 9.5px !important;
|
||||
margin-bottom: 6px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-title {
|
||||
font-size: 20px !important;
|
||||
letter-spacing: -0.4px !important;
|
||||
font-size: 18px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-subtitle {
|
||||
font-size: 13px !important;
|
||||
margin-bottom: 8px !important;
|
||||
font-size: 12.5px !important;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-description {
|
||||
font-size: 11px !important;
|
||||
line-height: 1.4 !important;
|
||||
margin-bottom: 12px !important;
|
||||
font-size: 12px !important;
|
||||
line-height: 1.45 !important;
|
||||
margin-bottom: 14px !important;
|
||||
}
|
||||
.dm-hiw-3d .section-metrics {
|
||||
padding-top: 10px !important;
|
||||
gap: 12px !important;
|
||||
padding-top: 12px !important;
|
||||
gap: 14px !important;
|
||||
}
|
||||
.dm-hiw-3d .metric-value { font-size: 15px !important; }
|
||||
.dm-hiw-3d .metric-label { font-size: 8.5px !important; }
|
||||
.dm-hiw-3d .metric-value { font-size: 16px !important; }
|
||||
.dm-hiw-3d .metric-label { font-size: 9px !important; }
|
||||
.dm-hiw-3d .step-title { font-size: 13px !important; }
|
||||
.dm-hiw-3d .step-description { font-size: 11.5px !important; }
|
||||
.dm-hiw-3d .side-navigation {
|
||||
bottom: 8px !important;
|
||||
gap: 12px !important;
|
||||
@@ -426,8 +472,8 @@
|
||||
.dm-hiw-3d #analytics-section {
|
||||
left: 50% !important;
|
||||
right: auto !important;
|
||||
bottom: 40px !important;
|
||||
width: calc(100vw - 80px) !important;
|
||||
bottom: 56px !important;
|
||||
width: min(340px, calc(100vw - 28px)) !important;
|
||||
transform: translateX(-50%) translateY(18px) scale(0.97) !important;
|
||||
}
|
||||
.dm-hiw-3d #first-mile-section.active,
|
||||
|
||||
Reference in New Issue
Block a user