Compare commits

...

14 Commits

Author SHA1 Message Date
0560b86b87 fix mobile view issue 2026-06-15 18:20:48 +05:30
205924e057 update image and about section 2026-06-13 00:27:45 +05:30
2bc01b5952 update ev section 2026-06-12 17:07:38 +05:30
ba34c80761 update blog page issue 2026-06-12 14:55:29 +05:30
f412b9f71e fix contect from 2026-06-10 17:31:10 +05:30
1e6653de96 fix how it work loading issue 2026-06-10 14:12:36 +05:30
10d73b6d31 fix how it work loading screen 2026-06-10 12:27:57 +05:30
d56e710e28 fix workflow left image 2026-06-09 19:57:56 +05:30
c4722a6c99 Merge pull request 'Update How It Works 3D page' (#1) from bharath-how-it-works into main
Reviewed-on: #1
2026-06-09 12:34:47 +00:00
86207fee86 update contect hero section 2026-06-09 17:56:46 +05:30
8c85a11698 update how it works card update 2026-06-09 17:42:42 +05:30
R-Bharathraj
45b4e7a109 Update How It Works 3D page 2026-06-09 15:32:58 +05:30
0ef51540e9 update how it works 2026-06-08 22:21:42 +05:30
3d53f82e7b feat(how-it-works): integrate scroll-driven 3D experience
Migrate the standalone Vite + React Three Fiber experience into the existing
Next.js site as the body of the How It Works page, replacing the Miles3 /
WhyChooseDoormile / TheDoormileWay content sections while preserving the
Elementor hero, global Header/Footer, layout, routing and SEO.

- New self-contained module: src/modules/how-it-works-3d/ (R3F scene, hooks,
  zustand store, animations, curves, constants, utils, scoped CSS). App.jsx →
  Experience3D.jsx; 3d_scene.jsx → models/Scene3D.jsx.
- 32MB GLB moved to public/models/3d_scene_final.glb; useGLTF paths updated.
- Client-only entry via dynamic ssr:false loader (Experience3DLoader).
- Self-managed fixed pin (tall section + absolute stage toggled
  absolute(top)→fixed→absolute(bottom) from ScrollTrigger pin state), mirroring
  the site's StrategySection, since the fixed header + ancestor overflow:hidden
  break CSS sticky / GSAP pin.
- experience.css fully scoped under .dm-hiw-3d to avoid colliding with the
  site's Elementor CSS.
- Global Lenis disabled on /how-it-works; module runs its own tuned Lenis;
  jump-to-section scroll math made spacer-relative.
- Added zustand + maath; ESLint-ignored the ported module.

Rendering fixes (root causes found by driving headless Chrome):
- Bump three 0.171 → 0.184 to match @react-three/fiber@9.6 / drei@10.7 /
  postprocessing@6.39 (0.171 silently failed to render this GLB and caused the
  EffectComposer getContextAttributes().alpha crash). Other 3D routes verified.
- EffectComposer: Bloom + Vignette only. SSAO needs a NormalPass (v3 dropped
  the old `disableNormalPass`), and that extra full-scene pass exhausted the
  WebGL context on this heavy scene.
- Cap Canvas dpr to [1,1.5] to bound framebuffer memory on retina displays.
- Defer Canvas mount via IntersectionObserver (mountScene), matching
  StrategySection, to ease StrictMode/first-render GPU pressure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 20:47:10 +05:30
135 changed files with 17852 additions and 2090 deletions

0
3d_scene_final.jsx Normal file
View File

View File

@@ -14,6 +14,9 @@ const eslintConfig = defineConfig([
"next-env.d.ts", "next-env.d.ts",
// Vendored third-party JS shipped to /public is not ours to lint. // Vendored third-party JS shipped to /public is not ours to lint.
"public/**", "public/**",
// Ported 3D experience (incl. the ~11.6k-line gltfjsx-generated model) — kept
// as faithful .jsx/.js from the standalone app; not linted to ours rules.
"src/modules/how-it-works-3d/**",
]), ]),
]); ]);

View File

@@ -1,7 +1,19 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
import path from "path";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
turbopack: {
root: path.resolve(__dirname),
},
output: "export", 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: { images: {
unoptimized: true, unoptimized: true,
formats: ["image/avif", "image/webp"], formats: ["image/avif", "image/webp"],

36
package-lock.json generated
View File

@@ -16,11 +16,13 @@
"gsap": "^3.15.0", "gsap": "^3.15.0",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"lenis": "^1.3.23", "lenis": "^1.3.23",
"maath": "^0.10.8",
"next": "16.2.6", "next": "16.2.6",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4", "react-dom": "19.2.4",
"react-leaflet": "^5.0.0", "react-leaflet": "^5.0.0",
"three": "^0.171.0" "three": "0.171.0",
"zustand": "^5.0.14"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
@@ -31,7 +33,7 @@
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@types/three": "^0.171.0", "@types/three": "^0.184.0",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "16.2.6", "eslint-config-next": "16.2.6",
"jest": "^30.4.2", "jest": "^30.4.2",
@@ -726,6 +728,12 @@
"node": ">=18" "node": ">=18"
} }
}, },
"node_modules/@dimforge/rapier3d-compat": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/@dimforge/rapier3d-compat/-/rapier3d-compat-0.12.0.tgz",
"integrity": "sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==",
"license": "Apache-2.0"
},
"node_modules/@emailjs/browser": { "node_modules/@emailjs/browser": {
"version": "4.4.1", "version": "4.4.1",
"resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-4.4.1.tgz", "resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-4.4.1.tgz",
@@ -3085,17 +3093,17 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/three": { "node_modules/@types/three": {
"version": "0.171.0", "version": "0.184.1",
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.171.0.tgz", "resolved": "https://registry.npmjs.org/@types/three/-/three-0.184.1.tgz",
"integrity": "sha512-oLuT1SAsT+CUg/wxUTFHo0K3NtJLnx9sJhZWQJp/0uXqFpzSk1hRHmvWvpaAWSfvx2db0lVKZ5/wV0I0isD2mQ==", "integrity": "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@dimforge/rapier3d-compat": "~0.12.0",
"@tweenjs/tween.js": "~23.1.3", "@tweenjs/tween.js": "~23.1.3",
"@types/stats.js": "*", "@types/stats.js": "*",
"@types/webxr": "*", "@types/webxr": ">=0.5.17",
"@webgpu/types": "*",
"fflate": "~0.8.2", "fflate": "~0.8.2",
"meshoptimizer": "~0.18.1" "meshoptimizer": "~1.1.1"
} }
}, },
"node_modules/@types/tough-cookie": { "node_modules/@types/tough-cookie": {
@@ -3761,12 +3769,6 @@
"react": ">= 16.8.0" "react": ">= 16.8.0"
} }
}, },
"node_modules/@webgpu/types": {
"version": "0.1.70",
"resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.70.tgz",
"integrity": "sha512-LFiNHHKMvmAEvwVew3JLJmTdShhbdwRFSImUshGhE2mGE8ybQzIo63l5uRp+YKnNx+8Qno8Kf6gN+DKMreIJCA==",
"license": "BSD-3-Clause"
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.16.0", "version": "8.16.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
@@ -8754,9 +8756,9 @@
} }
}, },
"node_modules/meshoptimizer": { "node_modules/meshoptimizer": {
"version": "0.18.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
"integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", "integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/micromatch": { "node_modules/micromatch": {

View File

@@ -21,11 +21,13 @@
"gsap": "^3.15.0", "gsap": "^3.15.0",
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"lenis": "^1.3.23", "lenis": "^1.3.23",
"maath": "^0.10.8",
"next": "16.2.6", "next": "16.2.6",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4", "react-dom": "19.2.4",
"react-leaflet": "^5.0.0", "react-leaflet": "^5.0.0",
"three": "^0.171.0" "three": "0.171.0",
"zustand": "^5.0.14"
}, },
"devDependencies": { "devDependencies": {
"@tailwindcss/postcss": "^4", "@tailwindcss/postcss": "^4",
@@ -36,7 +38,7 @@
"@types/node": "^20", "@types/node": "^20",
"@types/react": "^19", "@types/react": "^19",
"@types/react-dom": "^19", "@types/react-dom": "^19",
"@types/three": "^0.171.0", "@types/three": "^0.184.0",
"eslint": "^9", "eslint": "^9",
"eslint-config-next": "16.2.6", "eslint-config-next": "16.2.6",
"jest": "^30.4.2", "jest": "^30.4.2",

View File

@@ -507,7 +507,7 @@ body:not(.rtl) .elementor-element.elementor-element-13a7637 {
} }
.elementor-element.elementor-element-7da6646:not(.elementor-motion-effects-element-type-background) { .elementor-element.elementor-element-7da6646:not(.elementor-motion-effects-element-type-background) {
background-image: url("/images/home4-banner-4.jpg"); background-image: url("/images/bg-header-women.webp");
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -5776,12 +5776,12 @@ body:not(.block-editor-page) .content-wrapper .widget p {
@media (max-width: 1024px) { @media (max-width: 1024px) {
.elementor-element.elementor-element-99768ba .elementor-widget-container img.wp-image-4481 { .elementor-element.elementor-element-99768ba .elementor-widget-container img.wp-image-4481 {
width: 100% !important; width: 100% !important;
max-width: 450px !important; max-width: 600px !important;
/* Prevent oversized/zoomed appearance on tablets */ /* Prevent oversized/zoomed appearance on tablets */
height: auto !important; height: auto !important;
aspect-ratio: auto !important; aspect-ratio: auto !important;
/* Maintain original proportions */ /* Maintain original proportions */
object-fit: contain !important; object-fit: cover !important;
/* Ensure no cropping of important content */ /* Ensure no cropping of important content */
margin: 0 auto !important; margin: 0 auto !important;
/* Centering */ /* Centering */
@@ -5799,10 +5799,10 @@ body:not(.block-editor-page) .content-wrapper .widget p {
/* Specific scaling for small mobile phones */ /* Specific scaling for small mobile phones */
@media (max-width: 767px) { @media (max-width: 767px) {
.elementor-element.elementor-element-99768ba .elementor-widget-container img.wp-image-4481 { .elementor-element.elementor-element-99768ba .elementor-widget-container img.wp-image-4481 {
max-width: 90% !important; max-width: 100% !important;
/* Reduce scale on small screens */ /* Reduce scale on small screens */
margin: 0 auto !important; margin: 0 auto !important;
border-radius: 18px !important; border-radius: 25px !important;
/* Consistent rounded edges */ /* Consistent rounded edges */
} }
} }
@@ -8650,7 +8650,7 @@ h1:where(.wp-block-heading).has-background,
} }
.elementor .elementor-element.elementor-element-6c7cbcb .elementor-repeater-item-3264830 { .elementor .elementor-element.elementor-element-6c7cbcb .elementor-repeater-item-3264830 {
background-image: url("/images/home2-slide-1.jpg"); background-image: url("/images/home2-slide-1.webp");
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -8710,7 +8710,7 @@ h1:where(.wp-block-heading).has-background,
} }
.elementor .elementor-element.elementor-element-6c7cbcb .elementor-repeater-item-6867061 { .elementor .elementor-element.elementor-element-6c7cbcb .elementor-repeater-item-6867061 {
background-image: url("/images/home2-slide-2.jpg"); background-image: url("/images/home2-slide-2.webp");
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -8833,7 +8833,7 @@ h1:where(.wp-block-heading).has-background,
} }
.elementor .elementor-element.elementor-element-ca6bc63:not(.elementor-motion-effects-element-type-background) { .elementor .elementor-element.elementor-element-ca6bc63:not(.elementor-motion-effects-element-type-background) {
background-image: linear-gradient(to right, #00000080, #00000010), url("/images/home2-banner-1.jpg"); background-image: linear-gradient(to right, #00000080, #00000010), url("/images/home2-banner-1.webp");
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -8952,7 +8952,7 @@ h1:where(.wp-block-heading).has-background,
} }
.elementor .elementor-element.elementor-element-f003242:not(.elementor-motion-effects-element-type-background) { .elementor .elementor-element.elementor-element-f003242:not(.elementor-motion-effects-element-type-background) {
background-image: url("/images/home2-banner-3.jpg"); background-image: url("/images/home2-banner-3.webp");
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -10216,7 +10216,7 @@ h1:where(.wp-block-heading).has-background,
} }
#side-panel-2f31137:before { #side-panel-2f31137:before {
background-image: url("/images/bg-slide-sidebar.jpg"); background-image: url("/images/bg-slide-sidebar.webp");
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -11406,7 +11406,7 @@ h1:where(.wp-block-heading).has-background,
} }
.elementor-61 .elementor-element.elementor-element-6c7cbcb .elementor-repeater-item-3264830 { .elementor-61 .elementor-element.elementor-element-6c7cbcb .elementor-repeater-item-3264830 {
background-image: url("/images/home2-slide-1.jpg"); background-image: url("/images/home2-slide-1.webp");
background-position: bottom center; background-position: bottom center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -11458,7 +11458,7 @@ h1:where(.wp-block-heading).has-background,
} }
.elementor-61 .elementor-element.elementor-element-6c7cbcb .elementor-repeater-item-6867061 { .elementor-61 .elementor-element.elementor-element-6c7cbcb .elementor-repeater-item-6867061 {
background-image: url("/images/home2-slide-2.jpg"); background-image: url("/images/home2-slide-2.webp");
background-position: bottom center; background-position: bottom center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -11569,7 +11569,7 @@ h1:where(.wp-block-heading).has-background,
} }
.elementor-61 .elementor-element.elementor-element-ca6bc63:not(.elementor-motion-effects-element-type-background) { .elementor-61 .elementor-element.elementor-element-ca6bc63:not(.elementor-motion-effects-element-type-background) {
background-image: url("/images/home2-banner-1.jpg"); background-image: url("/images/home2-banner-1.webp");
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -11684,7 +11684,7 @@ h1:where(.wp-block-heading).has-background,
} }
.elementor-61 .elementor-element.elementor-element-f003242:not(.elementor-motion-effects-element-type-background) { .elementor-61 .elementor-element.elementor-element-f003242:not(.elementor-motion-effects-element-type-background) {
background-image: url("/images/home2-banner-3.jpg"); background-image: url("/images/home2-banner-3.webp");
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -14856,7 +14856,7 @@ img:is([sizes=auto i]) {
} }
.elementor-element.elementor-element-7da6646:not(.elementor-motion-effects-element-type-background) { .elementor-element.elementor-element-7da6646:not(.elementor-motion-effects-element-type-background) {
background-image: url("/images/bg-header-5.png"); background-image: url("/images/bg-header-5.webp");
background-position: center center; background-position: center center;
background-repeat: no-repeat; background-repeat: no-repeat;
background-size: cover; background-size: cover;
@@ -18143,14 +18143,101 @@ img:is([sizes=auto i]){contain-intrinsic-size:3000px 1500px}
/* STYLE BLOCK 50 */ /* STYLE BLOCK 50 */
.howits-hero-custom-bg.elementor-repeater-item-3264830, .howits-hero-custom-bg.elementor-repeater-item-3264830,
.howits-hero-custom-bg.elementor-repeater-item-6867061 { .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; background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.1)), url('/images/home1-slide-1.webp') !important;
background-position: center !important; background-position: center !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
background-size: cover !important; background-size: cover !important;
} }
.howits-hero-custom-bg.elementor-repeater-item-6867061 { .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-2.png') !important; background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.1)), url('/images/home1-slide-2.webp') !important;
background-position: center !important; background-position: center !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
background-size: cover !important; background-size: cover !important;
} }
/* STYLE BLOCK 51: unified mobile gutters for card/content sections */
@media (max-width: 767px) {
:root { --dm-mobile-gutter: 10px; }
.elementor.elementor-61 .elementor-element.elementor-element-89a0ca1,
.elementor.elementor-61 .elementor-element.elementor-element-88745f4,
.elementor.elementor-86 .elementor-element.elementor-element-c2c601a,
.elementor.elementor-104 .elementor-element.elementor-element-c70681e {
--padding-left: var(--dm-mobile-gutter) !important;
--padding-right: var(--dm-mobile-gutter) !important;
padding-left: var(--dm-mobile-gutter) !important;
padding-right: var(--dm-mobile-gutter) !important;
box-sizing: border-box !important;
}
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc {
--padding-left: 10px !important;
--padding-right: 10px !important;
padding-left: 10px !important;
padding-right: 10px !important;
box-sizing: border-box !important;
}
.elementor.elementor-61 .elementor-element.elementor-element-89a0ca1 > .e-con-inner,
.elementor.elementor-61 .elementor-element.elementor-element-88745f4 > .e-con-inner,
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc > .e-con-inner,
.elementor.elementor-86 .elementor-element.elementor-element-c2c601a > .e-con-inner,
.elementor.elementor-104 .elementor-element.elementor-element-c70681e > .e-con-inner {
width: 100% !important;
max-width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
.elementor.elementor-61 .elementor-element.elementor-element-89a0ca1 > .e-con-inner > .e-con,
.elementor.elementor-61 .elementor-element.elementor-element-88745f4 > .e-con-inner > .e-con,
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc > .e-con-inner > .e-con,
.elementor.elementor-86 .elementor-element.elementor-element-c2c601a > .e-con-inner > .e-con,
.elementor.elementor-104 .elementor-element.elementor-element-c70681e > .e-con-inner > .e-con {
--padding-left: 0px !important;
--padding-right: 0px !important;
width: 100% !important;
max-width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
.elementor.elementor-61 .elementor-element.elementor-element-6829276 {
--e-con-grid-template-columns: minmax(0, 1fr) !important;
--grid-justify-content: stretch !important;
--justify-items: stretch !important;
grid-template-columns: minmax(0, 1fr) !important;
justify-content: stretch !important;
justify-items: stretch !important;
min-width: 0 !important;
}
.elementor.elementor-61 .industry-solutions-grid,
.elementor.elementor-61 .industry-card-link {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
}
.comparison-section .container {
width: calc(100% - (var(--dm-mobile-gutter) * 2)) !important;
max-width: calc(100% - (var(--dm-mobile-gutter) * 2)) !important;
margin-left: auto !important;
margin-right: auto !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
.wcd-inner {
width: 100% !important;
max-width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
}

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -0,0 +1,116 @@
var $jscomp=$jscomp||{};$jscomp.scope={};$jscomp.arrayIteratorImpl=function(h){var n=0;return function(){return n<h.length?{done:!1,value:h[n++]}:{done:!0}}};$jscomp.arrayIterator=function(h){return{next:$jscomp.arrayIteratorImpl(h)}};$jscomp.makeIterator=function(h){var n="undefined"!=typeof Symbol&&Symbol.iterator&&h[Symbol.iterator];return n?n.call(h):$jscomp.arrayIterator(h)};$jscomp.ASSUME_ES5=!1;$jscomp.ASSUME_NO_NATIVE_MAP=!1;$jscomp.ASSUME_NO_NATIVE_SET=!1;$jscomp.SIMPLE_FROUND_POLYFILL=!1;
$jscomp.ISOLATE_POLYFILLS=!1;$jscomp.FORCE_POLYFILL_PROMISE=!1;$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION=!1;$jscomp.getGlobal=function(h){h=["object"==typeof globalThis&&globalThis,h,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var n=0;n<h.length;++n){var k=h[n];if(k&&k.Math==Math)return k}throw Error("Cannot find global object");};$jscomp.global=$jscomp.getGlobal(this);
$jscomp.defineProperty=$jscomp.ASSUME_ES5||"function"==typeof Object.defineProperties?Object.defineProperty:function(h,n,k){if(h==Array.prototype||h==Object.prototype)return h;h[n]=k.value;return h};$jscomp.IS_SYMBOL_NATIVE="function"===typeof Symbol&&"symbol"===typeof Symbol("x");$jscomp.TRUST_ES6_POLYFILLS=!$jscomp.ISOLATE_POLYFILLS||$jscomp.IS_SYMBOL_NATIVE;$jscomp.polyfills={};$jscomp.propertyToPolyfillSymbol={};$jscomp.POLYFILL_PREFIX="$jscp$";
var $jscomp$lookupPolyfilledValue=function(h,n){var k=$jscomp.propertyToPolyfillSymbol[n];if(null==k)return h[n];k=h[k];return void 0!==k?k:h[n]};$jscomp.polyfill=function(h,n,k,p){n&&($jscomp.ISOLATE_POLYFILLS?$jscomp.polyfillIsolated(h,n,k,p):$jscomp.polyfillUnisolated(h,n,k,p))};
$jscomp.polyfillUnisolated=function(h,n,k,p){k=$jscomp.global;h=h.split(".");for(p=0;p<h.length-1;p++){var l=h[p];if(!(l in k))return;k=k[l]}h=h[h.length-1];p=k[h];n=n(p);n!=p&&null!=n&&$jscomp.defineProperty(k,h,{configurable:!0,writable:!0,value:n})};
$jscomp.polyfillIsolated=function(h,n,k,p){var l=h.split(".");h=1===l.length;p=l[0];p=!h&&p in $jscomp.polyfills?$jscomp.polyfills:$jscomp.global;for(var y=0;y<l.length-1;y++){var f=l[y];if(!(f in p))return;p=p[f]}l=l[l.length-1];k=$jscomp.IS_SYMBOL_NATIVE&&"es6"===k?p[l]:null;n=n(k);null!=n&&(h?$jscomp.defineProperty($jscomp.polyfills,l,{configurable:!0,writable:!0,value:n}):n!==k&&(void 0===$jscomp.propertyToPolyfillSymbol[l]&&(k=1E9*Math.random()>>>0,$jscomp.propertyToPolyfillSymbol[l]=$jscomp.IS_SYMBOL_NATIVE?
$jscomp.global.Symbol(l):$jscomp.POLYFILL_PREFIX+k+"$"+l),$jscomp.defineProperty(p,$jscomp.propertyToPolyfillSymbol[l],{configurable:!0,writable:!0,value:n})))};
$jscomp.polyfill("Promise",function(h){function n(){this.batch_=null}function k(f){return f instanceof l?f:new l(function(q,u){q(f)})}if(h&&(!($jscomp.FORCE_POLYFILL_PROMISE||$jscomp.FORCE_POLYFILL_PROMISE_WHEN_NO_UNHANDLED_REJECTION&&"undefined"===typeof $jscomp.global.PromiseRejectionEvent)||!$jscomp.global.Promise||-1===$jscomp.global.Promise.toString().indexOf("[native code]")))return h;n.prototype.asyncExecute=function(f){if(null==this.batch_){this.batch_=[];var q=this;this.asyncExecuteFunction(function(){q.executeBatch_()})}this.batch_.push(f)};
var p=$jscomp.global.setTimeout;n.prototype.asyncExecuteFunction=function(f){p(f,0)};n.prototype.executeBatch_=function(){for(;this.batch_&&this.batch_.length;){var f=this.batch_;this.batch_=[];for(var q=0;q<f.length;++q){var u=f[q];f[q]=null;try{u()}catch(A){this.asyncThrow_(A)}}}this.batch_=null};n.prototype.asyncThrow_=function(f){this.asyncExecuteFunction(function(){throw f;})};var l=function(f){this.state_=0;this.result_=void 0;this.onSettledCallbacks_=[];this.isRejectionHandled_=!1;var q=this.createResolveAndReject_();
try{f(q.resolve,q.reject)}catch(u){q.reject(u)}};l.prototype.createResolveAndReject_=function(){function f(A){return function(F){u||(u=!0,A.call(q,F))}}var q=this,u=!1;return{resolve:f(this.resolveTo_),reject:f(this.reject_)}};l.prototype.resolveTo_=function(f){if(f===this)this.reject_(new TypeError("A Promise cannot resolve to itself"));else if(f instanceof l)this.settleSameAsPromise_(f);else{a:switch(typeof f){case "object":var q=null!=f;break a;case "function":q=!0;break a;default:q=!1}q?this.resolveToNonPromiseObj_(f):
this.fulfill_(f)}};l.prototype.resolveToNonPromiseObj_=function(f){var q=void 0;try{q=f.then}catch(u){this.reject_(u);return}"function"==typeof q?this.settleSameAsThenable_(q,f):this.fulfill_(f)};l.prototype.reject_=function(f){this.settle_(2,f)};l.prototype.fulfill_=function(f){this.settle_(1,f)};l.prototype.settle_=function(f,q){if(0!=this.state_)throw Error("Cannot settle("+f+", "+q+"): Promise already settled in state"+this.state_);this.state_=f;this.result_=q;2===this.state_&&this.scheduleUnhandledRejectionCheck_();
this.executeOnSettledCallbacks_()};l.prototype.scheduleUnhandledRejectionCheck_=function(){var f=this;p(function(){if(f.notifyUnhandledRejection_()){var q=$jscomp.global.console;"undefined"!==typeof q&&q.error(f.result_)}},1)};l.prototype.notifyUnhandledRejection_=function(){if(this.isRejectionHandled_)return!1;var f=$jscomp.global.CustomEvent,q=$jscomp.global.Event,u=$jscomp.global.dispatchEvent;if("undefined"===typeof u)return!0;"function"===typeof f?f=new f("unhandledrejection",{cancelable:!0}):
"function"===typeof q?f=new q("unhandledrejection",{cancelable:!0}):(f=$jscomp.global.document.createEvent("CustomEvent"),f.initCustomEvent("unhandledrejection",!1,!0,f));f.promise=this;f.reason=this.result_;return u(f)};l.prototype.executeOnSettledCallbacks_=function(){if(null!=this.onSettledCallbacks_){for(var f=0;f<this.onSettledCallbacks_.length;++f)y.asyncExecute(this.onSettledCallbacks_[f]);this.onSettledCallbacks_=null}};var y=new n;l.prototype.settleSameAsPromise_=function(f){var q=this.createResolveAndReject_();
f.callWhenSettled_(q.resolve,q.reject)};l.prototype.settleSameAsThenable_=function(f,q){var u=this.createResolveAndReject_();try{f.call(q,u.resolve,u.reject)}catch(A){u.reject(A)}};l.prototype.then=function(f,q){function u(w,B){return"function"==typeof w?function(R){try{A(w(R))}catch(Z){F(Z)}}:B}var A,F,v=new l(function(w,B){A=w;F=B});this.callWhenSettled_(u(f,A),u(q,F));return v};l.prototype.catch=function(f){return this.then(void 0,f)};l.prototype.callWhenSettled_=function(f,q){function u(){switch(A.state_){case 1:f(A.result_);
break;case 2:q(A.result_);break;default:throw Error("Unexpected state: "+A.state_);}}var A=this;null==this.onSettledCallbacks_?y.asyncExecute(u):this.onSettledCallbacks_.push(u);this.isRejectionHandled_=!0};l.resolve=k;l.reject=function(f){return new l(function(q,u){u(f)})};l.race=function(f){return new l(function(q,u){for(var A=$jscomp.makeIterator(f),F=A.next();!F.done;F=A.next())k(F.value).callWhenSettled_(q,u)})};l.all=function(f){var q=$jscomp.makeIterator(f),u=q.next();return u.done?k([]):new l(function(A,
F){function v(R){return function(Z){w[R]=Z;B--;0==B&&A(w)}}var w=[],B=0;do w.push(void 0),B++,k(u.value).callWhenSettled_(v(w.length-1),F),u=q.next();while(!u.done)})};return l},"es6","es3");$jscomp.owns=function(h,n){return Object.prototype.hasOwnProperty.call(h,n)};$jscomp.assign=$jscomp.TRUST_ES6_POLYFILLS&&"function"==typeof Object.assign?Object.assign:function(h,n){for(var k=1;k<arguments.length;k++){var p=arguments[k];if(p)for(var l in p)$jscomp.owns(p,l)&&(h[l]=p[l])}return h};
$jscomp.polyfill("Object.assign",function(h){return h||$jscomp.assign},"es6","es3");$jscomp.checkStringArgs=function(h,n,k){if(null==h)throw new TypeError("The 'this' value for String.prototype."+k+" must not be null or undefined");if(n instanceof RegExp)throw new TypeError("First argument to String.prototype."+k+" must not be a regular expression");return h+""};
$jscomp.polyfill("String.prototype.startsWith",function(h){return h?h:function(n,k){var p=$jscomp.checkStringArgs(this,n,"startsWith");n+="";var l=p.length,y=n.length;k=Math.max(0,Math.min(k|0,p.length));for(var f=0;f<y&&k<l;)if(p[k++]!=n[f++])return!1;return f>=y}},"es6","es3");
$jscomp.polyfill("Array.prototype.copyWithin",function(h){function n(k){k=Number(k);return Infinity===k||-Infinity===k?k:k|0}return h?h:function(k,p,l){var y=this.length;k=n(k);p=n(p);l=void 0===l?y:n(l);k=0>k?Math.max(y+k,0):Math.min(k,y);p=0>p?Math.max(y+p,0):Math.min(p,y);l=0>l?Math.max(y+l,0):Math.min(l,y);if(k<p)for(;p<l;)p in this?this[k++]=this[p++]:(delete this[k++],p++);else for(l=Math.min(l,y+p-k),k+=l-p;l>p;)--l in this?this[--k]=this[l]:delete this[--k];return this}},"es6","es3");
$jscomp.typedArrayCopyWithin=function(h){return h?h:Array.prototype.copyWithin};$jscomp.polyfill("Int8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint8ClampedArray.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
$jscomp.polyfill("Uint16Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Int32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Uint32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float32Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");$jscomp.polyfill("Float64Array.prototype.copyWithin",$jscomp.typedArrayCopyWithin,"es6","es5");
var DracoDecoderModule=function(){var h="undefined"!==typeof document&&document.currentScript?document.currentScript.src:void 0;"undefined"!==typeof __filename&&(h=h||__filename);return function(n){function k(e){return a.locateFile?a.locateFile(e,U):U+e}function p(e,b){if(e){var c=ia;var d=e+b;for(b=e;c[b]&&!(b>=d);)++b;if(16<b-e&&c.buffer&&ra)c=ra.decode(c.subarray(e,b));else{for(d="";e<b;){var g=c[e++];if(g&128){var t=c[e++]&63;if(192==(g&224))d+=String.fromCharCode((g&31)<<6|t);else{var aa=c[e++]&
63;g=224==(g&240)?(g&15)<<12|t<<6|aa:(g&7)<<18|t<<12|aa<<6|c[e++]&63;65536>g?d+=String.fromCharCode(g):(g-=65536,d+=String.fromCharCode(55296|g>>10,56320|g&1023))}}else d+=String.fromCharCode(g)}c=d}}else c="";return c}function l(){var e=ja.buffer;a.HEAP8=W=new Int8Array(e);a.HEAP16=new Int16Array(e);a.HEAP32=ca=new Int32Array(e);a.HEAPU8=ia=new Uint8Array(e);a.HEAPU16=new Uint16Array(e);a.HEAPU32=Y=new Uint32Array(e);a.HEAPF32=new Float32Array(e);a.HEAPF64=new Float64Array(e)}function y(e){if(a.onAbort)a.onAbort(e);
e="Aborted("+e+")";da(e);sa=!0;e=new WebAssembly.RuntimeError(e+". Build with -sASSERTIONS for more info.");ka(e);throw e;}function f(e){try{if(e==P&&ea)return new Uint8Array(ea);if(ma)return ma(e);throw"both async and sync fetching of the wasm failed";}catch(b){y(b)}}function q(){if(!ea&&(ta||fa)){if("function"==typeof fetch&&!P.startsWith("file://"))return fetch(P,{credentials:"same-origin"}).then(function(e){if(!e.ok)throw"failed to load wasm binary file at '"+P+"'";return e.arrayBuffer()}).catch(function(){return f(P)});
if(na)return new Promise(function(e,b){na(P,function(c){e(new Uint8Array(c))},b)})}return Promise.resolve().then(function(){return f(P)})}function u(e){for(;0<e.length;)e.shift()(a)}function A(e){this.excPtr=e;this.ptr=e-24;this.set_type=function(b){Y[this.ptr+4>>2]=b};this.get_type=function(){return Y[this.ptr+4>>2]};this.set_destructor=function(b){Y[this.ptr+8>>2]=b};this.get_destructor=function(){return Y[this.ptr+8>>2]};this.set_refcount=function(b){ca[this.ptr>>2]=b};this.set_caught=function(b){W[this.ptr+
12>>0]=b?1:0};this.get_caught=function(){return 0!=W[this.ptr+12>>0]};this.set_rethrown=function(b){W[this.ptr+13>>0]=b?1:0};this.get_rethrown=function(){return 0!=W[this.ptr+13>>0]};this.init=function(b,c){this.set_adjusted_ptr(0);this.set_type(b);this.set_destructor(c);this.set_refcount(0);this.set_caught(!1);this.set_rethrown(!1)};this.add_ref=function(){ca[this.ptr>>2]+=1};this.release_ref=function(){var b=ca[this.ptr>>2];ca[this.ptr>>2]=b-1;return 1===b};this.set_adjusted_ptr=function(b){Y[this.ptr+
16>>2]=b};this.get_adjusted_ptr=function(){return Y[this.ptr+16>>2]};this.get_exception_ptr=function(){if(ua(this.get_type()))return Y[this.excPtr>>2];var b=this.get_adjusted_ptr();return 0!==b?b:this.excPtr}}function F(){function e(){if(!la&&(la=!0,a.calledRun=!0,!sa)){va=!0;u(oa);wa(a);if(a.onRuntimeInitialized)a.onRuntimeInitialized();if(a.postRun)for("function"==typeof a.postRun&&(a.postRun=[a.postRun]);a.postRun.length;)xa.unshift(a.postRun.shift());u(xa)}}if(!(0<ba)){if(a.preRun)for("function"==
typeof a.preRun&&(a.preRun=[a.preRun]);a.preRun.length;)ya.unshift(a.preRun.shift());u(ya);0<ba||(a.setStatus?(a.setStatus("Running..."),setTimeout(function(){setTimeout(function(){a.setStatus("")},1);e()},1)):e())}}function v(){}function w(e){return(e||v).__cache__}function B(e,b){var c=w(b),d=c[e];if(d)return d;d=Object.create((b||v).prototype);d.ptr=e;return c[e]=d}function R(e){if("string"===typeof e){for(var b=0,c=0;c<e.length;++c){var d=e.charCodeAt(c);127>=d?b++:2047>=d?b+=2:55296<=d&&57343>=
d?(b+=4,++c):b+=3}b=Array(b+1);c=0;d=b.length;if(0<d){d=c+d-1;for(var g=0;g<e.length;++g){var t=e.charCodeAt(g);if(55296<=t&&57343>=t){var aa=e.charCodeAt(++g);t=65536+((t&1023)<<10)|aa&1023}if(127>=t){if(c>=d)break;b[c++]=t}else{if(2047>=t){if(c+1>=d)break;b[c++]=192|t>>6}else{if(65535>=t){if(c+2>=d)break;b[c++]=224|t>>12}else{if(c+3>=d)break;b[c++]=240|t>>18;b[c++]=128|t>>12&63}b[c++]=128|t>>6&63}b[c++]=128|t&63}}b[c]=0}e=r.alloc(b,W);r.copy(b,W,e);return e}return e}function Z(e){if("object"===
typeof e){var b=r.alloc(e,W);r.copy(e,W,b);return b}return e}function X(){throw"cannot construct a VoidPtr, no constructor in IDL";}function S(){this.ptr=za();w(S)[this.ptr]=this}function Q(){this.ptr=Aa();w(Q)[this.ptr]=this}function V(){this.ptr=Ba();w(V)[this.ptr]=this}function x(){this.ptr=Ca();w(x)[this.ptr]=this}function D(){this.ptr=Da();w(D)[this.ptr]=this}function G(){this.ptr=Ea();w(G)[this.ptr]=this}function H(){this.ptr=Fa();w(H)[this.ptr]=this}function E(){this.ptr=Ga();w(E)[this.ptr]=
this}function T(){this.ptr=Ha();w(T)[this.ptr]=this}function C(){throw"cannot construct a Status, no constructor in IDL";}function I(){this.ptr=Ia();w(I)[this.ptr]=this}function J(){this.ptr=Ja();w(J)[this.ptr]=this}function K(){this.ptr=Ka();w(K)[this.ptr]=this}function L(){this.ptr=La();w(L)[this.ptr]=this}function M(){this.ptr=Ma();w(M)[this.ptr]=this}function N(){this.ptr=Na();w(N)[this.ptr]=this}function O(){this.ptr=Oa();w(O)[this.ptr]=this}function z(){this.ptr=Pa();w(z)[this.ptr]=this}function m(){this.ptr=
Qa();w(m)[this.ptr]=this}n=void 0===n?{}:n;var a="undefined"!=typeof n?n:{},wa,ka;a.ready=new Promise(function(e,b){wa=e;ka=b});var Ra=!1,Sa=!1;a.onRuntimeInitialized=function(){Ra=!0;if(Sa&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.onModuleParsed=function(){Sa=!0;if(Ra&&"function"===typeof a.onModuleLoaded)a.onModuleLoaded(a)};a.isVersionSupported=function(e){if("string"!==typeof e)return!1;e=e.split(".");return 2>e.length||3<e.length?!1:1==e[0]&&0<=e[1]&&5>=e[1]?!0:0!=e[0]||10<
e[1]?!1:!0};var Ta=Object.assign({},a),ta="object"==typeof window,fa="function"==typeof importScripts,Ua="object"==typeof process&&"object"==typeof process.versions&&"string"==typeof process.versions.node,U="";if(Ua){var Va=require("fs"),pa=require("path");U=fa?pa.dirname(U)+"/":__dirname+"/";var Wa=function(e,b){e=e.startsWith("file://")?new URL(e):pa.normalize(e);return Va.readFileSync(e,b?void 0:"utf8")};var ma=function(e){e=Wa(e,!0);e.buffer||(e=new Uint8Array(e));return e};var na=function(e,
b,c){e=e.startsWith("file://")?new URL(e):pa.normalize(e);Va.readFile(e,function(d,g){d?c(d):b(g.buffer)})};1<process.argv.length&&process.argv[1].replace(/\\/g,"/");process.argv.slice(2);a.inspect=function(){return"[Emscripten Module object]"}}else if(ta||fa)fa?U=self.location.href:"undefined"!=typeof document&&document.currentScript&&(U=document.currentScript.src),h&&(U=h),U=0!==U.indexOf("blob:")?U.substr(0,U.replace(/[?#].*/,"").lastIndexOf("/")+1):"",Wa=function(e){var b=new XMLHttpRequest;b.open("GET",
e,!1);b.send(null);return b.responseText},fa&&(ma=function(e){var b=new XMLHttpRequest;b.open("GET",e,!1);b.responseType="arraybuffer";b.send(null);return new Uint8Array(b.response)}),na=function(e,b,c){var d=new XMLHttpRequest;d.open("GET",e,!0);d.responseType="arraybuffer";d.onload=function(){200==d.status||0==d.status&&d.response?b(d.response):c()};d.onerror=c;d.send(null)};a.print||console.log.bind(console);var da=a.printErr||console.warn.bind(console);Object.assign(a,Ta);Ta=null;var ea;a.wasmBinary&&
(ea=a.wasmBinary);"object"!=typeof WebAssembly&&y("no native wasm support detected");var ja,sa=!1,ra="undefined"!=typeof TextDecoder?new TextDecoder("utf8"):void 0,W,ia,ca,Y,ya=[],oa=[],xa=[],va=!1,ba=0,qa=null,ha=null;var P="draco_decoder_gltf.wasm";P.startsWith("data:application/octet-stream;base64,")||(P=k(P));var pd=0,qd={b:function(e,b,c){(new A(e)).init(b,c);pd++;throw e;},a:function(){y("")},d:function(e,b,c){ia.copyWithin(e,b,b+c)},c:function(e){var b=ia.length;e>>>=0;if(2147483648<e)return!1;
for(var c=1;4>=c;c*=2){var d=b*(1+.2/c);d=Math.min(d,e+100663296);var g=Math;d=Math.max(e,d);g=g.min.call(g,2147483648,d+(65536-d%65536)%65536);a:{d=ja.buffer;try{ja.grow(g-d.byteLength+65535>>>16);l();var t=1;break a}catch(aa){}t=void 0}if(t)return!0}return!1}};(function(){function e(g,t){a.asm=g.exports;ja=a.asm.e;l();oa.unshift(a.asm.f);ba--;a.monitorRunDependencies&&a.monitorRunDependencies(ba);0==ba&&(null!==qa&&(clearInterval(qa),qa=null),ha&&(g=ha,ha=null,g()))}function b(g){e(g.instance)}
function c(g){return q().then(function(t){return WebAssembly.instantiate(t,d)}).then(function(t){return t}).then(g,function(t){da("failed to asynchronously prepare wasm: "+t);y(t)})}var d={a:qd};ba++;a.monitorRunDependencies&&a.monitorRunDependencies(ba);if(a.instantiateWasm)try{return a.instantiateWasm(d,e)}catch(g){da("Module.instantiateWasm callback failed with error: "+g),ka(g)}(function(){return ea||"function"!=typeof WebAssembly.instantiateStreaming||P.startsWith("data:application/octet-stream;base64,")||
P.startsWith("file://")||Ua||"function"!=typeof fetch?c(b):fetch(P,{credentials:"same-origin"}).then(function(g){return WebAssembly.instantiateStreaming(g,d).then(b,function(t){da("wasm streaming compile failed: "+t);da("falling back to ArrayBuffer instantiation");return c(b)})})})().catch(ka);return{}})();var Xa=a._emscripten_bind_VoidPtr___destroy___0=function(){return(Xa=a._emscripten_bind_VoidPtr___destroy___0=a.asm.h).apply(null,arguments)},za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=
function(){return(za=a._emscripten_bind_DecoderBuffer_DecoderBuffer_0=a.asm.i).apply(null,arguments)},Ya=a._emscripten_bind_DecoderBuffer_Init_2=function(){return(Ya=a._emscripten_bind_DecoderBuffer_Init_2=a.asm.j).apply(null,arguments)},Za=a._emscripten_bind_DecoderBuffer___destroy___0=function(){return(Za=a._emscripten_bind_DecoderBuffer___destroy___0=a.asm.k).apply(null,arguments)},Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=function(){return(Aa=a._emscripten_bind_AttributeTransformData_AttributeTransformData_0=
a.asm.l).apply(null,arguments)},$a=a._emscripten_bind_AttributeTransformData_transform_type_0=function(){return($a=a._emscripten_bind_AttributeTransformData_transform_type_0=a.asm.m).apply(null,arguments)},ab=a._emscripten_bind_AttributeTransformData___destroy___0=function(){return(ab=a._emscripten_bind_AttributeTransformData___destroy___0=a.asm.n).apply(null,arguments)},Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=function(){return(Ba=a._emscripten_bind_GeometryAttribute_GeometryAttribute_0=
a.asm.o).apply(null,arguments)},bb=a._emscripten_bind_GeometryAttribute___destroy___0=function(){return(bb=a._emscripten_bind_GeometryAttribute___destroy___0=a.asm.p).apply(null,arguments)},Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=function(){return(Ca=a._emscripten_bind_PointAttribute_PointAttribute_0=a.asm.q).apply(null,arguments)},cb=a._emscripten_bind_PointAttribute_size_0=function(){return(cb=a._emscripten_bind_PointAttribute_size_0=a.asm.r).apply(null,arguments)},db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=
function(){return(db=a._emscripten_bind_PointAttribute_GetAttributeTransformData_0=a.asm.s).apply(null,arguments)},eb=a._emscripten_bind_PointAttribute_attribute_type_0=function(){return(eb=a._emscripten_bind_PointAttribute_attribute_type_0=a.asm.t).apply(null,arguments)},fb=a._emscripten_bind_PointAttribute_data_type_0=function(){return(fb=a._emscripten_bind_PointAttribute_data_type_0=a.asm.u).apply(null,arguments)},gb=a._emscripten_bind_PointAttribute_num_components_0=function(){return(gb=a._emscripten_bind_PointAttribute_num_components_0=
a.asm.v).apply(null,arguments)},hb=a._emscripten_bind_PointAttribute_normalized_0=function(){return(hb=a._emscripten_bind_PointAttribute_normalized_0=a.asm.w).apply(null,arguments)},ib=a._emscripten_bind_PointAttribute_byte_stride_0=function(){return(ib=a._emscripten_bind_PointAttribute_byte_stride_0=a.asm.x).apply(null,arguments)},jb=a._emscripten_bind_PointAttribute_byte_offset_0=function(){return(jb=a._emscripten_bind_PointAttribute_byte_offset_0=a.asm.y).apply(null,arguments)},kb=a._emscripten_bind_PointAttribute_unique_id_0=
function(){return(kb=a._emscripten_bind_PointAttribute_unique_id_0=a.asm.z).apply(null,arguments)},lb=a._emscripten_bind_PointAttribute___destroy___0=function(){return(lb=a._emscripten_bind_PointAttribute___destroy___0=a.asm.A).apply(null,arguments)},Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=function(){return(Da=a._emscripten_bind_AttributeQuantizationTransform_AttributeQuantizationTransform_0=a.asm.B).apply(null,arguments)},mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=
function(){return(mb=a._emscripten_bind_AttributeQuantizationTransform_InitFromAttribute_1=a.asm.C).apply(null,arguments)},nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=function(){return(nb=a._emscripten_bind_AttributeQuantizationTransform_quantization_bits_0=a.asm.D).apply(null,arguments)},ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=function(){return(ob=a._emscripten_bind_AttributeQuantizationTransform_min_value_1=a.asm.E).apply(null,arguments)},pb=
a._emscripten_bind_AttributeQuantizationTransform_range_0=function(){return(pb=a._emscripten_bind_AttributeQuantizationTransform_range_0=a.asm.F).apply(null,arguments)},qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=function(){return(qb=a._emscripten_bind_AttributeQuantizationTransform___destroy___0=a.asm.G).apply(null,arguments)},Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=function(){return(Ea=a._emscripten_bind_AttributeOctahedronTransform_AttributeOctahedronTransform_0=
a.asm.H).apply(null,arguments)},rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=function(){return(rb=a._emscripten_bind_AttributeOctahedronTransform_InitFromAttribute_1=a.asm.I).apply(null,arguments)},sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=function(){return(sb=a._emscripten_bind_AttributeOctahedronTransform_quantization_bits_0=a.asm.J).apply(null,arguments)},tb=a._emscripten_bind_AttributeOctahedronTransform___destroy___0=function(){return(tb=
a._emscripten_bind_AttributeOctahedronTransform___destroy___0=a.asm.K).apply(null,arguments)},Fa=a._emscripten_bind_PointCloud_PointCloud_0=function(){return(Fa=a._emscripten_bind_PointCloud_PointCloud_0=a.asm.L).apply(null,arguments)},ub=a._emscripten_bind_PointCloud_num_attributes_0=function(){return(ub=a._emscripten_bind_PointCloud_num_attributes_0=a.asm.M).apply(null,arguments)},vb=a._emscripten_bind_PointCloud_num_points_0=function(){return(vb=a._emscripten_bind_PointCloud_num_points_0=a.asm.N).apply(null,
arguments)},wb=a._emscripten_bind_PointCloud___destroy___0=function(){return(wb=a._emscripten_bind_PointCloud___destroy___0=a.asm.O).apply(null,arguments)},Ga=a._emscripten_bind_Mesh_Mesh_0=function(){return(Ga=a._emscripten_bind_Mesh_Mesh_0=a.asm.P).apply(null,arguments)},xb=a._emscripten_bind_Mesh_num_faces_0=function(){return(xb=a._emscripten_bind_Mesh_num_faces_0=a.asm.Q).apply(null,arguments)},yb=a._emscripten_bind_Mesh_num_attributes_0=function(){return(yb=a._emscripten_bind_Mesh_num_attributes_0=
a.asm.R).apply(null,arguments)},zb=a._emscripten_bind_Mesh_num_points_0=function(){return(zb=a._emscripten_bind_Mesh_num_points_0=a.asm.S).apply(null,arguments)},Ab=a._emscripten_bind_Mesh___destroy___0=function(){return(Ab=a._emscripten_bind_Mesh___destroy___0=a.asm.T).apply(null,arguments)},Ha=a._emscripten_bind_Metadata_Metadata_0=function(){return(Ha=a._emscripten_bind_Metadata_Metadata_0=a.asm.U).apply(null,arguments)},Bb=a._emscripten_bind_Metadata___destroy___0=function(){return(Bb=a._emscripten_bind_Metadata___destroy___0=
a.asm.V).apply(null,arguments)},Cb=a._emscripten_bind_Status_code_0=function(){return(Cb=a._emscripten_bind_Status_code_0=a.asm.W).apply(null,arguments)},Db=a._emscripten_bind_Status_ok_0=function(){return(Db=a._emscripten_bind_Status_ok_0=a.asm.X).apply(null,arguments)},Eb=a._emscripten_bind_Status_error_msg_0=function(){return(Eb=a._emscripten_bind_Status_error_msg_0=a.asm.Y).apply(null,arguments)},Fb=a._emscripten_bind_Status___destroy___0=function(){return(Fb=a._emscripten_bind_Status___destroy___0=
a.asm.Z).apply(null,arguments)},Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=function(){return(Ia=a._emscripten_bind_DracoFloat32Array_DracoFloat32Array_0=a.asm._).apply(null,arguments)},Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=function(){return(Gb=a._emscripten_bind_DracoFloat32Array_GetValue_1=a.asm.$).apply(null,arguments)},Hb=a._emscripten_bind_DracoFloat32Array_size_0=function(){return(Hb=a._emscripten_bind_DracoFloat32Array_size_0=a.asm.aa).apply(null,arguments)},Ib=
a._emscripten_bind_DracoFloat32Array___destroy___0=function(){return(Ib=a._emscripten_bind_DracoFloat32Array___destroy___0=a.asm.ba).apply(null,arguments)},Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=function(){return(Ja=a._emscripten_bind_DracoInt8Array_DracoInt8Array_0=a.asm.ca).apply(null,arguments)},Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=function(){return(Jb=a._emscripten_bind_DracoInt8Array_GetValue_1=a.asm.da).apply(null,arguments)},Kb=a._emscripten_bind_DracoInt8Array_size_0=
function(){return(Kb=a._emscripten_bind_DracoInt8Array_size_0=a.asm.ea).apply(null,arguments)},Lb=a._emscripten_bind_DracoInt8Array___destroy___0=function(){return(Lb=a._emscripten_bind_DracoInt8Array___destroy___0=a.asm.fa).apply(null,arguments)},Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=function(){return(Ka=a._emscripten_bind_DracoUInt8Array_DracoUInt8Array_0=a.asm.ga).apply(null,arguments)},Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=function(){return(Mb=a._emscripten_bind_DracoUInt8Array_GetValue_1=
a.asm.ha).apply(null,arguments)},Nb=a._emscripten_bind_DracoUInt8Array_size_0=function(){return(Nb=a._emscripten_bind_DracoUInt8Array_size_0=a.asm.ia).apply(null,arguments)},Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=function(){return(Ob=a._emscripten_bind_DracoUInt8Array___destroy___0=a.asm.ja).apply(null,arguments)},La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=function(){return(La=a._emscripten_bind_DracoInt16Array_DracoInt16Array_0=a.asm.ka).apply(null,arguments)},Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=
function(){return(Pb=a._emscripten_bind_DracoInt16Array_GetValue_1=a.asm.la).apply(null,arguments)},Qb=a._emscripten_bind_DracoInt16Array_size_0=function(){return(Qb=a._emscripten_bind_DracoInt16Array_size_0=a.asm.ma).apply(null,arguments)},Rb=a._emscripten_bind_DracoInt16Array___destroy___0=function(){return(Rb=a._emscripten_bind_DracoInt16Array___destroy___0=a.asm.na).apply(null,arguments)},Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=function(){return(Ma=a._emscripten_bind_DracoUInt16Array_DracoUInt16Array_0=
a.asm.oa).apply(null,arguments)},Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=function(){return(Sb=a._emscripten_bind_DracoUInt16Array_GetValue_1=a.asm.pa).apply(null,arguments)},Tb=a._emscripten_bind_DracoUInt16Array_size_0=function(){return(Tb=a._emscripten_bind_DracoUInt16Array_size_0=a.asm.qa).apply(null,arguments)},Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=function(){return(Ub=a._emscripten_bind_DracoUInt16Array___destroy___0=a.asm.ra).apply(null,arguments)},Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=
function(){return(Na=a._emscripten_bind_DracoInt32Array_DracoInt32Array_0=a.asm.sa).apply(null,arguments)},Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=function(){return(Vb=a._emscripten_bind_DracoInt32Array_GetValue_1=a.asm.ta).apply(null,arguments)},Wb=a._emscripten_bind_DracoInt32Array_size_0=function(){return(Wb=a._emscripten_bind_DracoInt32Array_size_0=a.asm.ua).apply(null,arguments)},Xb=a._emscripten_bind_DracoInt32Array___destroy___0=function(){return(Xb=a._emscripten_bind_DracoInt32Array___destroy___0=
a.asm.va).apply(null,arguments)},Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=function(){return(Oa=a._emscripten_bind_DracoUInt32Array_DracoUInt32Array_0=a.asm.wa).apply(null,arguments)},Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=function(){return(Yb=a._emscripten_bind_DracoUInt32Array_GetValue_1=a.asm.xa).apply(null,arguments)},Zb=a._emscripten_bind_DracoUInt32Array_size_0=function(){return(Zb=a._emscripten_bind_DracoUInt32Array_size_0=a.asm.ya).apply(null,arguments)},$b=a._emscripten_bind_DracoUInt32Array___destroy___0=
function(){return($b=a._emscripten_bind_DracoUInt32Array___destroy___0=a.asm.za).apply(null,arguments)},Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=function(){return(Pa=a._emscripten_bind_MetadataQuerier_MetadataQuerier_0=a.asm.Aa).apply(null,arguments)},ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=function(){return(ac=a._emscripten_bind_MetadataQuerier_HasEntry_2=a.asm.Ba).apply(null,arguments)},bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=function(){return(bc=a._emscripten_bind_MetadataQuerier_GetIntEntry_2=
a.asm.Ca).apply(null,arguments)},cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=function(){return(cc=a._emscripten_bind_MetadataQuerier_GetIntEntryArray_3=a.asm.Da).apply(null,arguments)},dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=function(){return(dc=a._emscripten_bind_MetadataQuerier_GetDoubleEntry_2=a.asm.Ea).apply(null,arguments)},ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=function(){return(ec=a._emscripten_bind_MetadataQuerier_GetStringEntry_2=a.asm.Fa).apply(null,
arguments)},fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=function(){return(fc=a._emscripten_bind_MetadataQuerier_NumEntries_1=a.asm.Ga).apply(null,arguments)},gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=function(){return(gc=a._emscripten_bind_MetadataQuerier_GetEntryName_2=a.asm.Ha).apply(null,arguments)},hc=a._emscripten_bind_MetadataQuerier___destroy___0=function(){return(hc=a._emscripten_bind_MetadataQuerier___destroy___0=a.asm.Ia).apply(null,arguments)},Qa=a._emscripten_bind_Decoder_Decoder_0=
function(){return(Qa=a._emscripten_bind_Decoder_Decoder_0=a.asm.Ja).apply(null,arguments)},ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=function(){return(ic=a._emscripten_bind_Decoder_DecodeArrayToPointCloud_3=a.asm.Ka).apply(null,arguments)},jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=function(){return(jc=a._emscripten_bind_Decoder_DecodeArrayToMesh_3=a.asm.La).apply(null,arguments)},kc=a._emscripten_bind_Decoder_GetAttributeId_2=function(){return(kc=a._emscripten_bind_Decoder_GetAttributeId_2=
a.asm.Ma).apply(null,arguments)},lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=function(){return(lc=a._emscripten_bind_Decoder_GetAttributeIdByName_2=a.asm.Na).apply(null,arguments)},mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=function(){return(mc=a._emscripten_bind_Decoder_GetAttributeIdByMetadataEntry_3=a.asm.Oa).apply(null,arguments)},nc=a._emscripten_bind_Decoder_GetAttribute_2=function(){return(nc=a._emscripten_bind_Decoder_GetAttribute_2=a.asm.Pa).apply(null,arguments)},
oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=function(){return(oc=a._emscripten_bind_Decoder_GetAttributeByUniqueId_2=a.asm.Qa).apply(null,arguments)},pc=a._emscripten_bind_Decoder_GetMetadata_1=function(){return(pc=a._emscripten_bind_Decoder_GetMetadata_1=a.asm.Ra).apply(null,arguments)},qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=function(){return(qc=a._emscripten_bind_Decoder_GetAttributeMetadata_2=a.asm.Sa).apply(null,arguments)},rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=
function(){return(rc=a._emscripten_bind_Decoder_GetFaceFromMesh_3=a.asm.Ta).apply(null,arguments)},sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=function(){return(sc=a._emscripten_bind_Decoder_GetTriangleStripsFromMesh_2=a.asm.Ua).apply(null,arguments)},tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=function(){return(tc=a._emscripten_bind_Decoder_GetTrianglesUInt16Array_3=a.asm.Va).apply(null,arguments)},uc=a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=function(){return(uc=
a._emscripten_bind_Decoder_GetTrianglesUInt32Array_3=a.asm.Wa).apply(null,arguments)},vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=function(){return(vc=a._emscripten_bind_Decoder_GetAttributeFloat_3=a.asm.Xa).apply(null,arguments)},wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=function(){return(wc=a._emscripten_bind_Decoder_GetAttributeFloatForAllPoints_3=a.asm.Ya).apply(null,arguments)},xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=function(){return(xc=a._emscripten_bind_Decoder_GetAttributeIntForAllPoints_3=
a.asm.Za).apply(null,arguments)},yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=function(){return(yc=a._emscripten_bind_Decoder_GetAttributeInt8ForAllPoints_3=a.asm._a).apply(null,arguments)},zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=function(){return(zc=a._emscripten_bind_Decoder_GetAttributeUInt8ForAllPoints_3=a.asm.$a).apply(null,arguments)},Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=function(){return(Ac=a._emscripten_bind_Decoder_GetAttributeInt16ForAllPoints_3=
a.asm.ab).apply(null,arguments)},Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=function(){return(Bc=a._emscripten_bind_Decoder_GetAttributeUInt16ForAllPoints_3=a.asm.bb).apply(null,arguments)},Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=function(){return(Cc=a._emscripten_bind_Decoder_GetAttributeInt32ForAllPoints_3=a.asm.cb).apply(null,arguments)},Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=function(){return(Dc=a._emscripten_bind_Decoder_GetAttributeUInt32ForAllPoints_3=
a.asm.db).apply(null,arguments)},Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=function(){return(Ec=a._emscripten_bind_Decoder_GetAttributeDataArrayForAllPoints_5=a.asm.eb).apply(null,arguments)},Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=function(){return(Fc=a._emscripten_bind_Decoder_SkipAttributeTransform_1=a.asm.fb).apply(null,arguments)},Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=function(){return(Gc=a._emscripten_bind_Decoder_GetEncodedGeometryType_Deprecated_1=
a.asm.gb).apply(null,arguments)},Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=function(){return(Hc=a._emscripten_bind_Decoder_DecodeBufferToPointCloud_2=a.asm.hb).apply(null,arguments)},Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=function(){return(Ic=a._emscripten_bind_Decoder_DecodeBufferToMesh_2=a.asm.ib).apply(null,arguments)},Jc=a._emscripten_bind_Decoder___destroy___0=function(){return(Jc=a._emscripten_bind_Decoder___destroy___0=a.asm.jb).apply(null,arguments)},Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=
function(){return(Kc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_INVALID_TRANSFORM=a.asm.kb).apply(null,arguments)},Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=function(){return(Lc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_NO_TRANSFORM=a.asm.lb).apply(null,arguments)},Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=function(){return(Mc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_QUANTIZATION_TRANSFORM=
a.asm.mb).apply(null,arguments)},Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=function(){return(Nc=a._emscripten_enum_draco_AttributeTransformType_ATTRIBUTE_OCTAHEDRON_TRANSFORM=a.asm.nb).apply(null,arguments)},Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=function(){return(Oc=a._emscripten_enum_draco_GeometryAttribute_Type_INVALID=a.asm.ob).apply(null,arguments)},Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=function(){return(Pc=a._emscripten_enum_draco_GeometryAttribute_Type_POSITION=
a.asm.pb).apply(null,arguments)},Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=function(){return(Qc=a._emscripten_enum_draco_GeometryAttribute_Type_NORMAL=a.asm.qb).apply(null,arguments)},Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=function(){return(Rc=a._emscripten_enum_draco_GeometryAttribute_Type_COLOR=a.asm.rb).apply(null,arguments)},Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=function(){return(Sc=a._emscripten_enum_draco_GeometryAttribute_Type_TEX_COORD=
a.asm.sb).apply(null,arguments)},Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=function(){return(Tc=a._emscripten_enum_draco_GeometryAttribute_Type_GENERIC=a.asm.tb).apply(null,arguments)},Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=function(){return(Uc=a._emscripten_enum_draco_EncodedGeometryType_INVALID_GEOMETRY_TYPE=a.asm.ub).apply(null,arguments)},Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=function(){return(Vc=a._emscripten_enum_draco_EncodedGeometryType_POINT_CLOUD=
a.asm.vb).apply(null,arguments)},Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=function(){return(Wc=a._emscripten_enum_draco_EncodedGeometryType_TRIANGULAR_MESH=a.asm.wb).apply(null,arguments)},Xc=a._emscripten_enum_draco_DataType_DT_INVALID=function(){return(Xc=a._emscripten_enum_draco_DataType_DT_INVALID=a.asm.xb).apply(null,arguments)},Yc=a._emscripten_enum_draco_DataType_DT_INT8=function(){return(Yc=a._emscripten_enum_draco_DataType_DT_INT8=a.asm.yb).apply(null,arguments)},Zc=
a._emscripten_enum_draco_DataType_DT_UINT8=function(){return(Zc=a._emscripten_enum_draco_DataType_DT_UINT8=a.asm.zb).apply(null,arguments)},$c=a._emscripten_enum_draco_DataType_DT_INT16=function(){return($c=a._emscripten_enum_draco_DataType_DT_INT16=a.asm.Ab).apply(null,arguments)},ad=a._emscripten_enum_draco_DataType_DT_UINT16=function(){return(ad=a._emscripten_enum_draco_DataType_DT_UINT16=a.asm.Bb).apply(null,arguments)},bd=a._emscripten_enum_draco_DataType_DT_INT32=function(){return(bd=a._emscripten_enum_draco_DataType_DT_INT32=
a.asm.Cb).apply(null,arguments)},cd=a._emscripten_enum_draco_DataType_DT_UINT32=function(){return(cd=a._emscripten_enum_draco_DataType_DT_UINT32=a.asm.Db).apply(null,arguments)},dd=a._emscripten_enum_draco_DataType_DT_INT64=function(){return(dd=a._emscripten_enum_draco_DataType_DT_INT64=a.asm.Eb).apply(null,arguments)},ed=a._emscripten_enum_draco_DataType_DT_UINT64=function(){return(ed=a._emscripten_enum_draco_DataType_DT_UINT64=a.asm.Fb).apply(null,arguments)},fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=
function(){return(fd=a._emscripten_enum_draco_DataType_DT_FLOAT32=a.asm.Gb).apply(null,arguments)},gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=function(){return(gd=a._emscripten_enum_draco_DataType_DT_FLOAT64=a.asm.Hb).apply(null,arguments)},hd=a._emscripten_enum_draco_DataType_DT_BOOL=function(){return(hd=a._emscripten_enum_draco_DataType_DT_BOOL=a.asm.Ib).apply(null,arguments)},id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=function(){return(id=a._emscripten_enum_draco_DataType_DT_TYPES_COUNT=
a.asm.Jb).apply(null,arguments)},jd=a._emscripten_enum_draco_StatusCode_OK=function(){return(jd=a._emscripten_enum_draco_StatusCode_OK=a.asm.Kb).apply(null,arguments)},kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=function(){return(kd=a._emscripten_enum_draco_StatusCode_DRACO_ERROR=a.asm.Lb).apply(null,arguments)},ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=function(){return(ld=a._emscripten_enum_draco_StatusCode_IO_ERROR=a.asm.Mb).apply(null,arguments)},md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=
function(){return(md=a._emscripten_enum_draco_StatusCode_INVALID_PARAMETER=a.asm.Nb).apply(null,arguments)},nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=function(){return(nd=a._emscripten_enum_draco_StatusCode_UNSUPPORTED_VERSION=a.asm.Ob).apply(null,arguments)},od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=function(){return(od=a._emscripten_enum_draco_StatusCode_UNKNOWN_VERSION=a.asm.Pb).apply(null,arguments)};a._malloc=function(){return(a._malloc=a.asm.Qb).apply(null,arguments)};
a._free=function(){return(a._free=a.asm.Rb).apply(null,arguments)};var ua=function(){return(ua=a.asm.Sb).apply(null,arguments)};a.___start_em_js=11660;a.___stop_em_js=11758;var la;ha=function b(){la||F();la||(ha=b)};if(a.preInit)for("function"==typeof a.preInit&&(a.preInit=[a.preInit]);0<a.preInit.length;)a.preInit.pop()();F();v.prototype=Object.create(v.prototype);v.prototype.constructor=v;v.prototype.__class__=v;v.__cache__={};a.WrapperObject=v;a.getCache=w;a.wrapPointer=B;a.castObject=function(b,
c){return B(b.ptr,c)};a.NULL=B(0);a.destroy=function(b){if(!b.__destroy__)throw"Error: Cannot destroy object. (Did you create it yourself?)";b.__destroy__();delete w(b.__class__)[b.ptr]};a.compare=function(b,c){return b.ptr===c.ptr};a.getPointer=function(b){return b.ptr};a.getClass=function(b){return b.__class__};var r={buffer:0,size:0,pos:0,temps:[],needed:0,prepare:function(){if(r.needed){for(var b=0;b<r.temps.length;b++)a._free(r.temps[b]);r.temps.length=0;a._free(r.buffer);r.buffer=0;r.size+=
r.needed;r.needed=0}r.buffer||(r.size+=128,r.buffer=a._malloc(r.size),r.buffer||y(void 0));r.pos=0},alloc:function(b,c){r.buffer||y(void 0);b=b.length*c.BYTES_PER_ELEMENT;b=b+7&-8;r.pos+b>=r.size?(0<b||y(void 0),r.needed+=b,c=a._malloc(b),r.temps.push(c)):(c=r.buffer+r.pos,r.pos+=b);return c},copy:function(b,c,d){d>>>=0;switch(c.BYTES_PER_ELEMENT){case 2:d>>>=1;break;case 4:d>>>=2;break;case 8:d>>>=3}for(var g=0;g<b.length;g++)c[d+g]=b[g]}};X.prototype=Object.create(v.prototype);X.prototype.constructor=
X;X.prototype.__class__=X;X.__cache__={};a.VoidPtr=X;X.prototype.__destroy__=X.prototype.__destroy__=function(){Xa(this.ptr)};S.prototype=Object.create(v.prototype);S.prototype.constructor=S;S.prototype.__class__=S;S.__cache__={};a.DecoderBuffer=S;S.prototype.Init=S.prototype.Init=function(b,c){var d=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);Ya(d,b,c)};S.prototype.__destroy__=S.prototype.__destroy__=function(){Za(this.ptr)};Q.prototype=Object.create(v.prototype);
Q.prototype.constructor=Q;Q.prototype.__class__=Q;Q.__cache__={};a.AttributeTransformData=Q;Q.prototype.transform_type=Q.prototype.transform_type=function(){return $a(this.ptr)};Q.prototype.__destroy__=Q.prototype.__destroy__=function(){ab(this.ptr)};V.prototype=Object.create(v.prototype);V.prototype.constructor=V;V.prototype.__class__=V;V.__cache__={};a.GeometryAttribute=V;V.prototype.__destroy__=V.prototype.__destroy__=function(){bb(this.ptr)};x.prototype=Object.create(v.prototype);x.prototype.constructor=
x;x.prototype.__class__=x;x.__cache__={};a.PointAttribute=x;x.prototype.size=x.prototype.size=function(){return cb(this.ptr)};x.prototype.GetAttributeTransformData=x.prototype.GetAttributeTransformData=function(){return B(db(this.ptr),Q)};x.prototype.attribute_type=x.prototype.attribute_type=function(){return eb(this.ptr)};x.prototype.data_type=x.prototype.data_type=function(){return fb(this.ptr)};x.prototype.num_components=x.prototype.num_components=function(){return gb(this.ptr)};x.prototype.normalized=
x.prototype.normalized=function(){return!!hb(this.ptr)};x.prototype.byte_stride=x.prototype.byte_stride=function(){return ib(this.ptr)};x.prototype.byte_offset=x.prototype.byte_offset=function(){return jb(this.ptr)};x.prototype.unique_id=x.prototype.unique_id=function(){return kb(this.ptr)};x.prototype.__destroy__=x.prototype.__destroy__=function(){lb(this.ptr)};D.prototype=Object.create(v.prototype);D.prototype.constructor=D;D.prototype.__class__=D;D.__cache__={};a.AttributeQuantizationTransform=
D;D.prototype.InitFromAttribute=D.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!mb(c,b)};D.prototype.quantization_bits=D.prototype.quantization_bits=function(){return nb(this.ptr)};D.prototype.min_value=D.prototype.min_value=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return ob(c,b)};D.prototype.range=D.prototype.range=function(){return pb(this.ptr)};D.prototype.__destroy__=D.prototype.__destroy__=function(){qb(this.ptr)};G.prototype=
Object.create(v.prototype);G.prototype.constructor=G;G.prototype.__class__=G;G.__cache__={};a.AttributeOctahedronTransform=G;G.prototype.InitFromAttribute=G.prototype.InitFromAttribute=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return!!rb(c,b)};G.prototype.quantization_bits=G.prototype.quantization_bits=function(){return sb(this.ptr)};G.prototype.__destroy__=G.prototype.__destroy__=function(){tb(this.ptr)};H.prototype=Object.create(v.prototype);H.prototype.constructor=H;H.prototype.__class__=
H;H.__cache__={};a.PointCloud=H;H.prototype.num_attributes=H.prototype.num_attributes=function(){return ub(this.ptr)};H.prototype.num_points=H.prototype.num_points=function(){return vb(this.ptr)};H.prototype.__destroy__=H.prototype.__destroy__=function(){wb(this.ptr)};E.prototype=Object.create(v.prototype);E.prototype.constructor=E;E.prototype.__class__=E;E.__cache__={};a.Mesh=E;E.prototype.num_faces=E.prototype.num_faces=function(){return xb(this.ptr)};E.prototype.num_attributes=E.prototype.num_attributes=
function(){return yb(this.ptr)};E.prototype.num_points=E.prototype.num_points=function(){return zb(this.ptr)};E.prototype.__destroy__=E.prototype.__destroy__=function(){Ab(this.ptr)};T.prototype=Object.create(v.prototype);T.prototype.constructor=T;T.prototype.__class__=T;T.__cache__={};a.Metadata=T;T.prototype.__destroy__=T.prototype.__destroy__=function(){Bb(this.ptr)};C.prototype=Object.create(v.prototype);C.prototype.constructor=C;C.prototype.__class__=C;C.__cache__={};a.Status=C;C.prototype.code=
C.prototype.code=function(){return Cb(this.ptr)};C.prototype.ok=C.prototype.ok=function(){return!!Db(this.ptr)};C.prototype.error_msg=C.prototype.error_msg=function(){return p(Eb(this.ptr))};C.prototype.__destroy__=C.prototype.__destroy__=function(){Fb(this.ptr)};I.prototype=Object.create(v.prototype);I.prototype.constructor=I;I.prototype.__class__=I;I.__cache__={};a.DracoFloat32Array=I;I.prototype.GetValue=I.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gb(c,
b)};I.prototype.size=I.prototype.size=function(){return Hb(this.ptr)};I.prototype.__destroy__=I.prototype.__destroy__=function(){Ib(this.ptr)};J.prototype=Object.create(v.prototype);J.prototype.constructor=J;J.prototype.__class__=J;J.__cache__={};a.DracoInt8Array=J;J.prototype.GetValue=J.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Jb(c,b)};J.prototype.size=J.prototype.size=function(){return Kb(this.ptr)};J.prototype.__destroy__=J.prototype.__destroy__=function(){Lb(this.ptr)};
K.prototype=Object.create(v.prototype);K.prototype.constructor=K;K.prototype.__class__=K;K.__cache__={};a.DracoUInt8Array=K;K.prototype.GetValue=K.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Mb(c,b)};K.prototype.size=K.prototype.size=function(){return Nb(this.ptr)};K.prototype.__destroy__=K.prototype.__destroy__=function(){Ob(this.ptr)};L.prototype=Object.create(v.prototype);L.prototype.constructor=L;L.prototype.__class__=L;L.__cache__={};a.DracoInt16Array=
L;L.prototype.GetValue=L.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Pb(c,b)};L.prototype.size=L.prototype.size=function(){return Qb(this.ptr)};L.prototype.__destroy__=L.prototype.__destroy__=function(){Rb(this.ptr)};M.prototype=Object.create(v.prototype);M.prototype.constructor=M;M.prototype.__class__=M;M.__cache__={};a.DracoUInt16Array=M;M.prototype.GetValue=M.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Sb(c,b)};
M.prototype.size=M.prototype.size=function(){return Tb(this.ptr)};M.prototype.__destroy__=M.prototype.__destroy__=function(){Ub(this.ptr)};N.prototype=Object.create(v.prototype);N.prototype.constructor=N;N.prototype.__class__=N;N.__cache__={};a.DracoInt32Array=N;N.prototype.GetValue=N.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Vb(c,b)};N.prototype.size=N.prototype.size=function(){return Wb(this.ptr)};N.prototype.__destroy__=N.prototype.__destroy__=function(){Xb(this.ptr)};
O.prototype=Object.create(v.prototype);O.prototype.constructor=O;O.prototype.__class__=O;O.__cache__={};a.DracoUInt32Array=O;O.prototype.GetValue=O.prototype.GetValue=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Yb(c,b)};O.prototype.size=O.prototype.size=function(){return Zb(this.ptr)};O.prototype.__destroy__=O.prototype.__destroy__=function(){$b(this.ptr)};z.prototype=Object.create(v.prototype);z.prototype.constructor=z;z.prototype.__class__=z;z.__cache__={};a.MetadataQuerier=
z;z.prototype.HasEntry=z.prototype.HasEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return!!ac(d,b,c)};z.prototype.GetIntEntry=z.prototype.GetIntEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return bc(d,b,c)};z.prototype.GetIntEntryArray=z.prototype.GetIntEntryArray=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===
typeof c?c.ptr:R(c);d&&"object"===typeof d&&(d=d.ptr);cc(g,b,c,d)};z.prototype.GetDoubleEntry=z.prototype.GetDoubleEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return dc(d,b,c)};z.prototype.GetStringEntry=z.prototype.GetStringEntry=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return p(ec(d,b,c))};z.prototype.NumEntries=z.prototype.NumEntries=function(b){var c=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);return fc(c,b)};z.prototype.GetEntryName=z.prototype.GetEntryName=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return p(gc(d,b,c))};z.prototype.__destroy__=z.prototype.__destroy__=function(){hc(this.ptr)};m.prototype=Object.create(v.prototype);m.prototype.constructor=m;m.prototype.__class__=m;m.__cache__={};a.Decoder=m;m.prototype.DecodeArrayToPointCloud=m.prototype.DecodeArrayToPointCloud=function(b,c,d){var g=
this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(ic(g,b,c,d),C)};m.prototype.DecodeArrayToMesh=m.prototype.DecodeArrayToMesh=function(b,c,d){var g=this.ptr;r.prepare();"object"==typeof b&&(b=Z(b));c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return B(jc(g,b,c,d),C)};m.prototype.GetAttributeId=m.prototype.GetAttributeId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
(c=c.ptr);return kc(d,b,c)};m.prototype.GetAttributeIdByName=m.prototype.GetAttributeIdByName=function(b,c){var d=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);return lc(d,b,c)};m.prototype.GetAttributeIdByMetadataEntry=m.prototype.GetAttributeIdByMetadataEntry=function(b,c,d){var g=this.ptr;r.prepare();b&&"object"===typeof b&&(b=b.ptr);c=c&&"object"===typeof c?c.ptr:R(c);d=d&&"object"===typeof d?d.ptr:R(d);return mc(g,b,c,d)};m.prototype.GetAttribute=
m.prototype.GetAttribute=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(nc(d,b,c),x)};m.prototype.GetAttributeByUniqueId=m.prototype.GetAttributeByUniqueId=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(oc(d,b,c),x)};m.prototype.GetMetadata=m.prototype.GetMetadata=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return B(pc(c,b),T)};m.prototype.GetAttributeMetadata=m.prototype.GetAttributeMetadata=
function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(qc(d,b,c),T)};m.prototype.GetFaceFromMesh=m.prototype.GetFaceFromMesh=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!rc(g,b,c,d)};m.prototype.GetTriangleStripsFromMesh=m.prototype.GetTriangleStripsFromMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);
return sc(d,b,c)};m.prototype.GetTrianglesUInt16Array=m.prototype.GetTrianglesUInt16Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!tc(g,b,c,d)};m.prototype.GetTrianglesUInt32Array=m.prototype.GetTrianglesUInt32Array=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!uc(g,b,c,d)};m.prototype.GetAttributeFloat=m.prototype.GetAttributeFloat=
function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!vc(g,b,c,d)};m.prototype.GetAttributeFloatForAllPoints=m.prototype.GetAttributeFloatForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!wc(g,b,c,d)};m.prototype.GetAttributeIntForAllPoints=m.prototype.GetAttributeIntForAllPoints=function(b,c,d){var g=this.ptr;
b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!xc(g,b,c,d)};m.prototype.GetAttributeInt8ForAllPoints=m.prototype.GetAttributeInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!yc(g,b,c,d)};m.prototype.GetAttributeUInt8ForAllPoints=m.prototype.GetAttributeUInt8ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=
b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!zc(g,b,c,d)};m.prototype.GetAttributeInt16ForAllPoints=m.prototype.GetAttributeInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Ac(g,b,c,d)};m.prototype.GetAttributeUInt16ForAllPoints=m.prototype.GetAttributeUInt16ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&
(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Bc(g,b,c,d)};m.prototype.GetAttributeInt32ForAllPoints=m.prototype.GetAttributeInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);return!!Cc(g,b,c,d)};m.prototype.GetAttributeUInt32ForAllPoints=m.prototype.GetAttributeUInt32ForAllPoints=function(b,c,d){var g=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===
typeof d&&(d=d.ptr);return!!Dc(g,b,c,d)};m.prototype.GetAttributeDataArrayForAllPoints=m.prototype.GetAttributeDataArrayForAllPoints=function(b,c,d,g,t){var aa=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);d&&"object"===typeof d&&(d=d.ptr);g&&"object"===typeof g&&(g=g.ptr);t&&"object"===typeof t&&(t=t.ptr);return!!Ec(aa,b,c,d,g,t)};m.prototype.SkipAttributeTransform=m.prototype.SkipAttributeTransform=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);Fc(c,
b)};m.prototype.GetEncodedGeometryType_Deprecated=m.prototype.GetEncodedGeometryType_Deprecated=function(b){var c=this.ptr;b&&"object"===typeof b&&(b=b.ptr);return Gc(c,b)};m.prototype.DecodeBufferToPointCloud=m.prototype.DecodeBufferToPointCloud=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===typeof c&&(c=c.ptr);return B(Hc(d,b,c),C)};m.prototype.DecodeBufferToMesh=m.prototype.DecodeBufferToMesh=function(b,c){var d=this.ptr;b&&"object"===typeof b&&(b=b.ptr);c&&"object"===
typeof c&&(c=c.ptr);return B(Ic(d,b,c),C)};m.prototype.__destroy__=m.prototype.__destroy__=function(){Jc(this.ptr)};(function(){function b(){a.ATTRIBUTE_INVALID_TRANSFORM=Kc();a.ATTRIBUTE_NO_TRANSFORM=Lc();a.ATTRIBUTE_QUANTIZATION_TRANSFORM=Mc();a.ATTRIBUTE_OCTAHEDRON_TRANSFORM=Nc();a.INVALID=Oc();a.POSITION=Pc();a.NORMAL=Qc();a.COLOR=Rc();a.TEX_COORD=Sc();a.GENERIC=Tc();a.INVALID_GEOMETRY_TYPE=Uc();a.POINT_CLOUD=Vc();a.TRIANGULAR_MESH=Wc();a.DT_INVALID=Xc();a.DT_INT8=Yc();a.DT_UINT8=Zc();a.DT_INT16=
$c();a.DT_UINT16=ad();a.DT_INT32=bd();a.DT_UINT32=cd();a.DT_INT64=dd();a.DT_UINT64=ed();a.DT_FLOAT32=fd();a.DT_FLOAT64=gd();a.DT_BOOL=hd();a.DT_TYPES_COUNT=id();a.OK=jd();a.DRACO_ERROR=kd();a.IO_ERROR=ld();a.INVALID_PARAMETER=md();a.UNSUPPORTED_VERSION=nd();a.UNKNOWN_VERSION=od()}va?b():oa.unshift(b)})();if("function"===typeof a.onModuleParsed)a.onModuleParsed();a.Decoder.prototype.GetEncodedGeometryType=function(b){if(b.__class__&&b.__class__===a.DecoderBuffer)return a.Decoder.prototype.GetEncodedGeometryType_Deprecated(b);
if(8>b.byteLength)return a.INVALID_GEOMETRY_TYPE;switch(b[7]){case 0:return a.POINT_CLOUD;case 1:return a.TRIANGULAR_MESH;default:return a.INVALID_GEOMETRY_TYPE}};return n.ready}}();"object"===typeof exports&&"object"===typeof module?module.exports=DracoDecoderModule:"function"===typeof define&&define.amd?define([],function(){return DracoDecoderModule}):"object"===typeof exports&&(exports.DracoDecoderModule=DracoDecoderModule);

BIN
public/images/Aravinth.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
public/images/Fazul.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
public/images/Investor.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
public/images/Parthi.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
public/images/Suriya.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

BIN
public/images/about-bg.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 176 KiB

BIN
public/images/ev.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

BIN
public/images/mile-1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 MiB

BIN
public/images/mile-1.webp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

Binary file not shown.

View File

@@ -27,7 +27,10 @@ import Lenis from "lenis";
* Re-evaluates on every route change: the effect cleanup destroys the previous * Re-evaluates on every route change: the effect cleanup destroys the previous
* instance and re-inits on the next route. * instance and re-inits on the next route.
*/ */
const DISABLED_ROUTES: string[] = []; // /how-it-works runs its own tuned Lenis inside the embedded 3D experience
// (src/modules/how-it-works-3d); the global instance is gated off there so two
// Lenis instances don't fight over the same document scroll.
const DISABLED_ROUTES: string[] = ["/how-it-works"];
export default function SmoothScroll() { export default function SmoothScroll() {
const pathname = usePathname(); const pathname = usePathname();

View File

@@ -15,7 +15,7 @@ export const metadata = {
export default function AboutUsPage() { export default function AboutUsPage() {
return ( return (
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none"> <div id="about" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content"> <div className="content">
<div className="content-inner"> <div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="86" className="elementor elementor-86 elementor-59"> <div data-elementor-type="wp-page" data-elementor-id="86" className="elementor elementor-86 elementor-59">

View File

@@ -7,7 +7,7 @@ import { getPostBySlug, getAllSlugs, SITE_URL } from "@/data/blog";
type Params = { slug: string }; type Params = { slug: string };
// Required for `output: "export"` prerender every post at build time. // Required for `output: "export"`: prerender every post at build time.
export function generateStaticParams(): Params[] { export function generateStaticParams(): Params[] {
return getAllSlugs().map((slug) => ({ slug })); return getAllSlugs().map((slug) => ({ slug }));
} }

View File

@@ -4,12 +4,12 @@ import BlogGrid from "@/components/sections/BlogGrid";
export const metadata = { export const metadata = {
title: "Blog Doormile", title: "Blog Doormile",
description: "Insights and logistics intelligence from the team behind Doormile. Learn how AI is transforming EV planning and last-mile operations.", description: "Practical notes on delivery planning, EV fleet operations, route optimisation, charging, and last-mile performance from the Doormile team.",
}; };
export default function BlogPage() { export default function BlogPage() {
return ( return (
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none"> <div id="blogs" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content"> <div className="content">
<div className="content-inner"> <div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="104" className="elementor elementor-104"> <div data-elementor-type="wp-page" data-elementor-id="104" className="elementor elementor-104">

View File

@@ -25,6 +25,7 @@ html {
--space-section: clamp(40px, 5vw, 64px); --space-section: clamp(40px, 5vw, 64px);
--space-section-lg: clamp(52px, 6vw, 80px); --space-section-lg: clamp(52px, 6vw, 80px);
--space-hero-gap: clamp(36px, 4.5vw, 64px); --space-hero-gap: clamp(36px, 4.5vw, 64px);
--dm-mobile-gutter: 10px;
} }
.dm-section { .dm-section {
@@ -460,7 +461,7 @@ body {
@media (max-width: 720px) { @media (max-width: 720px) {
.dm-contact-left { .dm-contact-left {
min-height: auto; min-height: auto;
padding: 42px 22px 58px; padding: 42px 20px 58px;
} }
.dm-contact-title { .dm-contact-title {
@@ -478,43 +479,43 @@ body {
} }
.dm-contact-card { .dm-contact-card {
margin: 0 16px 32px; margin: 0 20px 28px;
padding: 44px 22px 34px; padding: 28px 20px 24px;
border-radius: 24px; border-radius: 28px;
} }
.dm-contact-card h3 { .dm-contact-card h3 {
margin-bottom: 34px; margin-bottom: 24px;
} }
.dm-contact-form { .dm-contact-form {
gap: 20px; gap: 18px;
} }
.dm-contact-form input { .dm-contact-form input {
height: 60px; height: 56px;
padding: 0 20px; padding: 0 18px;
} }
.dm-contact-form textarea { .dm-contact-form textarea {
min-height: 190px; min-height: 170px;
padding: 20px; padding: 18px;
} }
.dm-contact-submit { .dm-contact-submit {
width: 100%; width: 100%;
height: 64px; height: 60px;
} }
.dm-contact-submit span:first-child { .dm-contact-submit span:first-child {
flex: 1 1 auto; flex: 1 1 auto;
min-width: 0; min-width: 0;
padding: 0 22px; padding: 0 20px;
} }
.dm-contact-submit-icon { .dm-contact-submit-icon {
width: 64px; width: 60px;
min-width: 64px; min-width: 60px;
} }
} }
@@ -537,7 +538,131 @@ body {
.industry-solutions-grid { .industry-solutions-grid {
grid-template-columns: 1fr; grid-template-columns: 1fr;
} }
}.industry-card-link { .elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc {
--padding-left: 10px !important;
--padding-right: 10px !important;
padding-left: 10px !important;
padding-right: 10px !important;
box-sizing: border-box !important;
}
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc > .e-con-inner {
width: 100% !important;
max-width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
}
@media (max-width: 767px) {
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc > .e-con-inner {
max-width: 100% !important;
width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
.elementor.elementor-61 .elementor-element.elementor-element-d602f7f,
.elementor.elementor-61 .elementor-element.elementor-element-f64bd88,
.elementor.elementor-61 .elementor-element.elementor-element-5ed2dbb,
.elementor.elementor-61 .elementor-element.elementor-element-6829276 {
--padding-left: 0px !important;
--padding-right: 0px !important;
--margin-left: 0px !important;
--margin-right: 0px !important;
width: 100% !important;
max-width: 100% !important;
margin-left: 0 !important;
margin-right: 0 !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box;
}
.elementor.elementor-61 .elementor-element.elementor-element-6829276 {
--e-con-grid-template-columns: minmax(0, 1fr) !important;
--grid-justify-content: stretch !important;
--justify-items: stretch !important;
grid-template-columns: minmax(0, 1fr) !important;
justify-content: stretch !important;
justify-items: stretch !important;
min-width: 0 !important;
}
.elementor.elementor-61 .industry-solutions-grid,
.elementor.elementor-61 .industry-card-link {
max-width: 100%;
width: 100%;
min-width: 0;
}
.elementor.elementor-61 .industry-solutions-grid {
margin-left: 0 !important;
margin-right: 0 !important;
}
}
@media (max-width: 767px) {
.elementor.elementor-61 .elementor-element.elementor-element-89a0ca1,
.elementor.elementor-61 .elementor-element.elementor-element-88745f4,
.elementor.elementor-86 .elementor-element.elementor-element-c2c601a,
.elementor.elementor-104 .elementor-element.elementor-element-c70681e {
--padding-left: var(--dm-mobile-gutter) !important;
--padding-right: var(--dm-mobile-gutter) !important;
padding-left: var(--dm-mobile-gutter) !important;
padding-right: var(--dm-mobile-gutter) !important;
box-sizing: border-box !important;
}
.elementor.elementor-61 .elementor-element.elementor-element-89a0ca1 > .e-con-inner,
.elementor.elementor-61 .elementor-element.elementor-element-88745f4 > .e-con-inner,
.elementor.elementor-86 .elementor-element.elementor-element-c2c601a > .e-con-inner,
.elementor.elementor-104 .elementor-element.elementor-element-c70681e > .e-con-inner {
width: 100% !important;
max-width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
.elementor.elementor-61 .elementor-element.elementor-element-89a0ca1 > .e-con-inner > .e-con,
.elementor.elementor-61 .elementor-element.elementor-element-88745f4 > .e-con-inner > .e-con,
.elementor.elementor-86 .elementor-element.elementor-element-c2c601a > .e-con-inner > .e-con,
.elementor.elementor-104 .elementor-element.elementor-element-c70681e > .e-con-inner > .e-con {
--padding-left: 0px !important;
--padding-right: 0px !important;
width: 100% !important;
max-width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
.comparison-section .container {
width: calc(100% - (var(--dm-mobile-gutter) * 2)) !important;
max-width: calc(100% - (var(--dm-mobile-gutter) * 2)) !important;
margin-left: auto !important;
margin-right: auto !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
.wcd-inner {
width: 100% !important;
max-width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
}
.industry-card-link {
position: relative; position: relative;
height: 600px; height: 600px;
border-radius: 30px; border-radius: 30px;
@@ -899,12 +1024,14 @@ body {
to 600px at <= 840px. Earlier this card shrank at <= 1536px, which is why the to 600px at <= 840px. Earlier this card shrank at <= 1536px, which is why the
About hero looked smaller than Home on MacBook M1/Pro (their ~1440-1512px logical About hero looked smaller than Home on MacBook M1/Pro (their ~1440-1512px logical
width falls below 1536px). Match the home breakpoint instead. */ width falls below 1536px). Match the home breakpoint instead. */
@media (max-width: 840px) { @media (max-width: 1024px) {
.custom-standard-hero-container { .custom-standard-hero-container {
/* No top gap on mobile: the hero card sits flush to the top so the floating /* No top gap on mobile/tablet: the hero card sits flush to the top so the floating
navbar rests directly on the hero instead of leaving a band above it. */ navbar rests directly on the hero instead of leaving a band above it. */
padding: 0 10px 10px 10px !important; padding: 0 10px 10px 10px !important;
} }
}
@media (max-width: 840px) {
.custom-standard-hero-card { .custom-standard-hero-card {
height: 600px !important; height: 600px !important;
min-height: 600px !important; min-height: 600px !important;
@@ -929,5 +1056,30 @@ body {
.miletruth-hero-container { .miletruth-hero-container {
margin-top: 0 !important; margin-top: 0 !important;
} }
}
/* Audit & align section rhythm on mobile/tablet across all content pages */
.elementor-86 #women-entrepreneurship.elementor-element-bbc6760,
.elementor-86 .comparison-section,
.elementor-86 .elementor-element.elementor-element-bbc6760:not(#women-entrepreneurship),
.elementor-86 .elementor-element.elementor-element-c2c601a,
.elementor-59 .elementor-element.elementor-element-bbc6760,
.elementor-104 .elementor-element.elementor-element-c70681e {
margin-top: var(--space-section) !important;
margin-bottom: 0 !important;
}
/* Solutions page: First section under Solutions Hero gets the hero gap */
.elementor-59 .elementor-element.elementor-element-3b4a7cc {
margin-top: var(--space-hero-gap) !important;
margin-bottom: 0 !important;
}
/* About page: First section under About Hero gets the hero gap */
.elementor-86 .elementor-element.elementor-element-88745f4:first-of-type {
margin-top: var(--space-hero-gap) !important;
}
/* About page: Subsequent sections get standard section spacing */
.elementor-86 .elementor-element.elementor-element-88745f4 ~ .elementor-element.elementor-element-88745f4 {
margin-top: var(--space-section) !important;
}
}

View File

@@ -1,8 +1,6 @@
import React from "react"; import React from "react";
import HowItWorksHero from "../../components/sections/HowItWorksHero"; import HowItWorksHero from "../../components/sections/HowItWorksHero";
import Miles3 from "../../components/sections/Miles3"; import Experience3DLoader from "@/modules/how-it-works-3d/Experience3DLoader";
import WhyChooseDoormile from "../../components/sections/WhyChooseDoormile";
import TheDoormileWay from "../../components/sections/TheDoormileWay";
export const metadata = { export const metadata = {
title: "How It Works Doormile", title: "How It Works Doormile",
@@ -11,14 +9,15 @@ export const metadata = {
export default function HowItWorksPage() { export default function HowItWorksPage() {
return ( return (
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none"> <div id="how-it-works" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content"> <div className="content">
<div className="content-inner"> <div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59"> <div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59">
<HowItWorksHero /> <HowItWorksHero />
<Miles3 /> {/* The first/mid/last-mile story is now told by the scroll-driven 3D
<WhyChooseDoormile /> experience, which replaces the former Miles3 / WhyChooseDoormile /
<TheDoormileWay /> TheDoormileWay content sections on this page. */}
<Experience3DLoader />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -90,7 +90,7 @@ export default function RootLayout({
│ └─ footer │ └─ footer
SSR ships body with shared WP/Elementor classes; BodyClasses (client) refines per route. SSR ships body with shared WP/Elementor classes; BodyClasses (client) refines per route.
*/} */}
<body className={SHARED_BODY_CLASSES}> <body className={SHARED_BODY_CLASSES} suppressHydrationWarning>
<BodyClasses /> <BodyClasses />
<LoadingScreen /> <LoadingScreen />
<AnimationProvider> <AnimationProvider>

View File

@@ -11,7 +11,7 @@ export const metadata = {
export default function MileTruthPage() { export default function MileTruthPage() {
return ( return (
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none"> <div id="miletruth" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content"> <div className="content">
<div className="content-inner"> <div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59"> <div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59">

View File

@@ -17,7 +17,7 @@ export const metadata: Metadata = {
export default function Home() { export default function Home() {
return ( return (
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none"> <div id="home" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content"> <div className="content">
<div className="content-inner"> <div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="61" className="elementor elementor-61"> <div data-elementor-type="wp-page" data-elementor-id="61" className="elementor elementor-61">

View File

@@ -50,7 +50,7 @@ export default function SolutionsPage() {
} }
`}} /> `}} />
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none"> <div id="solutions" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
<div className="content"> <div className="content">
<div className="content-inner"> <div className="content-inner">
<div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59"> <div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59">

View File

@@ -6,7 +6,7 @@ import { blogPosts } from "@/data/blog";
/** /**
* Client-side blog search. The site is a static export, so there is no search * Client-side blog search. The site is a static export, so there is no search
* server — we filter the known posts in the browser and link straight to the * server. We filter the known posts in the browser and link straight to the
* matching /blog/[slug] routes. * matching /blog/[slug] routes.
*/ */
export default function BlogSearch() { export default function BlogSearch() {

View File

@@ -72,10 +72,10 @@ export default function BlogSidebar({ current }: { current?: BlogPost }) {
{/* CTA Card */} {/* CTA Card */}
<section className="dm-blog-widget dm-blog-cta-card"> <section className="dm-blog-widget dm-blog-cta-card">
<h2 className="dm-blog-cta-title">Ready to optimise your fleet?</h2> <h2 className="dm-blog-cta-title">Planning delivery routes?</h2>
<p className="dm-blog-cta-text"> <p className="dm-blog-cta-text">
See how MileTruth AI cuts distance, vehicles and emissions without Talk to us about reducing wasted distance, missed windows, and avoidable
missing an SLA. vehicle time.
</p> </p>
<Link href="/contact" className="dm-blog-cta-btn"> <Link href="/contact" className="dm-blog-cta-btn">
Contact Us Contact Us

View File

@@ -87,6 +87,25 @@ export default function Footer() {
return ( return (
<> <>
{/* Floating-label fix: the theme only lifts the label on :focus-within, so a
filled-but-blurred field drops its label back onto the typed value and the
two overlap. Keep the label lifted (and the notch in the top border open)
whenever the field carries a value, via the .dm-field-filled class below. */}
<style dangerouslySetInnerHTML={{ __html: `
.logico-form-field.dm-field-filled .logico-label-wrapper>label {
font-size: 14px;
top: -14px;
color: var(--logico-dark-text-color);
}
.logico-form-field.dm-field-filled .logico-label-placeholder .logico-label-placeholder-text:before {
right: 100%;
left: initial;
}
.logico-form-field.dm-field-filled .logico-label-placeholder .logico-label-placeholder-text:after {
left: 100%;
right: initial;
}
` }} />
<footer <footer
data-rocket-location-hash="1eeca93394c4fc14089e9d12a2a92e22" data-rocket-location-hash="1eeca93394c4fc14089e9d12a2a92e22"
itemScope itemScope
@@ -134,7 +153,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"> <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" /> <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> </svg>
<span>Call Center</span> <span>Contect</span>
</div> </div>
</div> </div>
</div> </div>
@@ -152,7 +171,7 @@ export default function Footer() {
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" /> <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
<circle cx="12" cy="10" r="3" /> <circle cx="12" cy="10" r="3" />
</svg> </svg>
<span>Our Location</span> <span>Address</span>
</div> </div>
</div> </div>
</div> </div>
@@ -197,14 +216,14 @@ export default function Footer() {
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49" /> <line x1="8.59" y1="13.51" x2="15.42" y2="17.49" />
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49" /> <line x1="15.41" y1="6.51" x2="8.59" y2="10.49" />
</svg> </svg>
<span>Social network</span> <span>Social</span>
</div> </div>
</div> </div>
</div> </div>
<div className="elementor-element elementor-element-a6bccba elementor-shape-square elementor-grid-0 elementor-widget elementor-widget-social-icons" data-id="a6bccba" data-element_type="widget" data-e-type="widget" data-widget_type="social-icons.default"> <div className="elementor-element elementor-element-a6bccba elementor-shape-square elementor-grid-0 elementor-widget elementor-widget-social-icons" data-id="a6bccba" data-element_type="widget" data-e-type="widget" data-widget_type="social-icons.default">
<div className="elementor-widget-container"> <div className="elementor-widget-container">
<div className="elementor-social-icons-wrapper elementor-grid" role="list" style={socialIconSpacing}> <div className="elementor-social-icons-wrapper elementor-grid" role="list" style={socialIconSpacing}>
<span className="elementor-grid-item" role="listitem" style={{padding:"0 15px"}}> {/* <span className="elementor-grid-item" role="listitem" style={{padding:"0 15px"}}>
<a className="elementor-icon elementor-social-icon elementor-social-icon-facebook-f elementor-repeater-item-3fbe893" href="https://www.facebook.com" target="_blank" rel="noopener noreferrer"> <a className="elementor-icon elementor-social-icon elementor-social-icon-facebook-f elementor-repeater-item-3fbe893" href="https://www.facebook.com" target="_blank" rel="noopener noreferrer">
<span className="elementor-screen-only">Facebook</span> <span className="elementor-screen-only">Facebook</span>
<svg aria-hidden="true" className="e-font-icon-svg e-fab-facebook-f" viewBox="0 0 320 512" xmlns="http://www.w3.org/2000/svg"> <svg aria-hidden="true" className="e-font-icon-svg e-fab-facebook-f" viewBox="0 0 320 512" xmlns="http://www.w3.org/2000/svg">
@@ -219,7 +238,7 @@ export default function Footer() {
<path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"></path> <path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"></path>
</svg> </svg>
</a> </a>
</span> </span> */}
<span className="elementor-grid-item" role="listitem"style={{padding:"0 15px"}}> <span className="elementor-grid-item" role="listitem"style={{padding:"0 15px"}}>
<a className="elementor-icon elementor-social-icon elementor-social-icon-linkedin-in elementor-repeater-item-38e1bcc" href="https://www.linkedin.com" target="_blank" rel="noopener noreferrer"> <a className="elementor-icon elementor-social-icon elementor-social-icon-linkedin-in elementor-repeater-item-38e1bcc" href="https://www.linkedin.com" target="_blank" rel="noopener noreferrer">
<span className="elementor-screen-only">LinkedIn</span> <span className="elementor-screen-only">LinkedIn</span>
@@ -255,7 +274,7 @@ export default function Footer() {
<div className="wpforms-container wpforms-render-modern" id="wpforms-369"> <div className="wpforms-container wpforms-render-modern" id="wpforms-369">
<form id="wpforms-form-369" className="wpforms-validate wpforms-form" onSubmit={handleSubmit}> <form id="wpforms-form-369" className="wpforms-validate wpforms-form" onSubmit={handleSubmit}>
<div className="wpforms-field-container"> <div className="wpforms-field-container">
<div className="wpforms-field-wrapper logico-form-field"> <div className={`wpforms-field-wrapper logico-form-field${formData.fullName ? " dm-field-filled" : ""}`}>
<div className="logico-label-wrapper" style={{marginBottom:"12px"}}> <div className="logico-label-wrapper" style={{marginBottom:"12px"}}>
<div className="logico-label-placeholder"> <div className="logico-label-placeholder">
<div className="logico-label-placeholder-text">Full name</div> <div className="logico-label-placeholder-text">Full name</div>
@@ -272,7 +291,7 @@ export default function Footer() {
required required
/> />
</div> </div>
<div className="wpforms-field-wrapper logico-form-field"> <div className={`wpforms-field-wrapper logico-form-field${formData.email ? " dm-field-filled" : ""}`}>
<div className="logico-label-wrapper" style={{marginBottom:"12px"}}> <div className="logico-label-wrapper" style={{marginBottom:"12px"}}>
<div className="logico-label-placeholder"> <div className="logico-label-placeholder">
<div className="logico-label-placeholder-text">Email</div> <div className="logico-label-placeholder-text">Email</div>
@@ -289,7 +308,7 @@ export default function Footer() {
required required
/> />
</div> </div>
<div className="wpforms-field-wrapper logico-form-field"> <div className={`wpforms-field-wrapper logico-form-field${formData.subject ? " dm-field-filled" : ""}`}>
<div className="logico-label-wrapper" style={{marginBottom:"12px"}}> <div className="logico-label-wrapper" style={{marginBottom:"12px"}}>
<div className="logico-label-placeholder"> <div className="logico-label-placeholder">
<div className="logico-label-placeholder-text">Subject</div> <div className="logico-label-placeholder-text">Subject</div>
@@ -306,7 +325,7 @@ export default function Footer() {
required required
/> />
</div> </div>
<div className="wpforms-field-wrapper logico-form-field"> <div className={`wpforms-field-wrapper logico-form-field${formData.message ? " dm-field-filled" : ""}`}>
<div className="logico-label-wrapper" style={{marginBottom:"12px"}}> <div className="logico-label-wrapper" style={{marginBottom:"12px"}}>
<div className="logico-label-placeholder"> <div className="logico-label-placeholder">
<div className="logico-label-placeholder-text">Message</div> <div className="logico-label-placeholder-text">Message</div>
@@ -384,7 +403,7 @@ export default function Footer() {
<div className="elementor-element elementor-element-e4e6486 elementor-shape-square elementor-grid-0 elementor-widget elementor-widget-social-icons" data-id="e4e6486" data-element_type="widget" data-e-type="widget" data-widget_type="social-icons.default"> <div className="elementor-element elementor-element-e4e6486 elementor-shape-square elementor-grid-0 elementor-widget elementor-widget-social-icons" data-id="e4e6486" data-element_type="widget" data-e-type="widget" data-widget_type="social-icons.default">
<div className="elementor-widget-container"> <div className="elementor-widget-container">
<div className="elementor-social-icons-wrapper elementor-grid" role="list" style={socialIconSpacing}> <div className="elementor-social-icons-wrapper elementor-grid" role="list" style={socialIconSpacing}>
<span className="elementor-grid-item" role="listitem" style={{padding:"0 15px"}}> {/* <span className="elementor-grid-item" role="listitem" style={{padding:"0 15px"}}>
<a className="elementor-icon elementor-social-icon elementor-social-icon-facebook-f" href="https://www.facebook.com" target="_blank" rel="noopener noreferrer"> <a className="elementor-icon elementor-social-icon elementor-social-icon-facebook-f" href="https://www.facebook.com" target="_blank" rel="noopener noreferrer">
<span className="elementor-screen-only">Facebook</span> <span className="elementor-screen-only">Facebook</span>
<svg aria-hidden="true" className="e-font-icon-svg e-fab-facebook-f" viewBox="0 0 320 512" xmlns="http://www.w3.org/2000/svg"> <svg aria-hidden="true" className="e-font-icon-svg e-fab-facebook-f" viewBox="0 0 320 512" xmlns="http://www.w3.org/2000/svg">
@@ -399,7 +418,7 @@ export default function Footer() {
<path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"></path> <path d="M389.2 48h70.6L305.6 224.2 487 464H345L233.7 318.6 106.5 464H35.8L200.7 275.5 26.8 48H172.4L272.9 180.9 389.2 48zM364.4 421.8h39.1L151.1 88h-42L364.4 421.8z"></path>
</svg> </svg>
</a> </a>
</span> </span> */}
<span className="elementor-grid-item" role="listitem" style={{padding:"0 15px"}}> <span className="elementor-grid-item" role="listitem" style={{padding:"0 15px"}}>
<a className="elementor-icon elementor-social-icon elementor-social-icon-linkedin-in" href="https://www.linkedin.com" target="_blank" rel="noopener noreferrer"> <a className="elementor-icon elementor-social-icon elementor-social-icon-linkedin-in" href="https://www.linkedin.com" target="_blank" rel="noopener noreferrer">
<span className="elementor-screen-only">LinkedIn</span> <span className="elementor-screen-only">LinkedIn</span>

View File

@@ -7,7 +7,7 @@
* Menu open/close + sidebar state is read from HeaderUIProvider so BodyOverlay (sibling at body level) can react. * Menu open/close + sidebar state is read from HeaderUIProvider so BodyOverlay (sibling at body level) can react.
*/ */
import { useEffect, useState } from "react"; import { MouseEvent, useEffect, useState } from "react";
import Link from "next/link"; import Link from "next/link";
import Image from "next/image"; import Image from "next/image";
import { usePathname } from "next/navigation"; import { usePathname } from "next/navigation";
@@ -44,6 +44,34 @@ export default function Header() {
const dmHeaderActive = (key: string) => const dmHeaderActive = (key: string) =>
(CURRENT_PAGE_ALIASES[key] ?? []).includes(currentPage) ? " active" : ""; (CURRENT_PAGE_ALIASES[key] ?? []).includes(currentPage) ? " active" : "";
const scrollToNavTarget = (targetId: string) => {
const target = document.getElementById(targetId);
if (target) {
target.scrollIntoView({ behavior: "smooth", block: "start" });
return;
}
window.scrollTo({ top: 0, behavior: "smooth" });
};
const handleNavClick = (
event: MouseEvent<HTMLAnchorElement>,
targetPath: string,
targetId: string,
shouldCloseMenu = false,
) => {
if (shouldCloseMenu) closeAll();
if (pathname !== targetPath) return;
event.preventDefault();
const targetUrl = `${targetPath}#${targetId}`;
if (`${window.location.pathname}${window.location.hash}` !== targetUrl) {
window.history.pushState(null, "", targetUrl);
}
requestAnimationFrame(() => scrollToNavTarget(targetId));
};
// Mirror of header.php <script> block (lines 628-660): // Mirror of header.php <script> block (lines 628-660):
// - on doc.ready: $('.header-hide-until-scroll').addClass('header-visible-scrolled') // - on doc.ready: $('.header-hide-until-scroll').addClass('header-visible-scrolled')
// - on scroll: toggleClass('dm-header-scrolled', scrollTop > 50) // - on scroll: toggleClass('dm-header-scrolled', scrollTop > 50)
@@ -142,23 +170,26 @@ export default function Header() {
> >
<div className="slide-sidebar-close" onClick={closeAll}></div> <div className="slide-sidebar-close" onClick={closeAll}></div>
<div className="slide-sidebar"> <div className="slide-sidebar">
<div className="slide-sidebar-content"> {/* Header — does not scroll. */}
<div className="slide-sidebar-header">
<figure className="wp-block-image size-full is-resized">
<Image
width={305}
height={58}
src="/images/doormile-logo.png"
alt="Doormile logo"
className="wp-image-5851"
style={{ width: "210px", height: "auto" }}
sizes="(max-width: 305px) 100vw, 305px"
/>
</figure>
</div>
{/* Scrollable content — tabIndex makes it keyboard-scrollable. */}
<div className="slide-sidebar-content" tabIndex={0} role="region" aria-label="Menu content">
<div id="block-37" className="widget widget_block"> <div id="block-37" className="widget widget_block">
<div className="widget-wrapper"> <div className="widget-wrapper">
<div className="dm-block-group is-layout-constrained dm-block-group-is-layout-constrained"> <div className="dm-block-group is-layout-constrained dm-block-group-is-layout-constrained">
<figure className="wp-block-image size-full is-resized">
<Image
width={305}
height={58}
src="/images/doormile-logo.png"
alt="Doormile logo"
className="wp-image-5851"
style={{ width: "150px", height: "auto" }}
sizes="(max-width: 305px) 100vw, 305px"
/>
</figure>
<div style={{ height: "46px" }} aria-hidden="true" className="wp-block-spacer"></div>
<div className="wp-block-title"> <div className="wp-block-title">
<h6 <h6
@@ -171,11 +202,87 @@ export default function Header() {
textTransform: "none", textTransform: "none",
}} }}
> >
Our Location Address
</h6> </h6>
</div> </div>
<p>5th Floor, Vision Ultima, Street No.3, Jayabheri Enclave, Gachibowli, Hyderabad, Telangana 500032.</p> <h6
className="wp-block-heading has-text-font-font-family"
style={{
fontSize: "14px",
fontStyle: "normal",
fontWeight: 700,
letterSpacing: "0px",
textTransform: "none",
}}
>
Hyderabad
</h6>
<p>
5th Floor, Vision Ultima,
<br />
Street No.3, Jayabheri Enclave,
<br />
Gachibowli, Hyderabad,
<br />
Telangana 500032.
</p>
<div style={{ height: "12px" }} aria-hidden="true" className="wp-block-spacer"></div>
<h6
className="wp-block-heading has-text-font-font-family"
style={{
fontSize: "14px",
fontStyle: "normal",
fontWeight: 700,
letterSpacing: "0px",
textTransform: "none",
}}
>
Coimbatore
</h6>
<p>
Mayflower Valencia,
<br />
Near Nava India Bus Stop,
<br />
Avinashi Road,
<br />
Udayampalayam,
<br />
Tamil Nadu 641037.
</p>
<div style={{ height: "12px" }} aria-hidden="true" className="wp-block-spacer"></div>
<h6
className="wp-block-heading has-text-font-font-family"
style={{
fontSize: "14px",
fontStyle: "normal",
fontWeight: 700,
letterSpacing: "0px",
textTransform: "none",
}}
>
Bengaluru
</h6>
<p>
C612, 6th Floor,
<br />
Trifecta Starlight,
<br />
ITPL Road,
<br />
Garudacharapalya,
<br />
Mahadevapura,
<br />
Bangalore 560048,
<br />
Karnataka, India.
</p>
<div style={{ height: "3px" }} aria-hidden="true" className="wp-block-spacer"></div> <div style={{ height: "3px" }} aria-hidden="true" className="wp-block-spacer"></div>
@@ -272,19 +379,21 @@ export default function Header() {
</li> </li>
</ul> </ul>
<div style={{ height: "137px" }} aria-hidden="true" className="wp-block-spacer"></div>
<div className="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div className="wp-block-button is-style-simple is-style-theme">
<Link href="/contact" className="wp-block-button__link wp-element-button" style={{ borderRadius: "10px" }}>
Get in touch
</Link>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{/* CTA — pinned at the bottom; never scrolls away. */}
<div className="slide-sidebar-cta">
<div className="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div className="wp-block-button is-style-simple is-style-theme">
<Link href="/contact" className="wp-block-button__link wp-element-button" style={{ borderRadius: "10px" }}>
Get in touch
</Link>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -325,22 +434,22 @@ export default function Header() {
<nav> <nav>
<ul id="menu-main-menu" className="main-menu"> <ul id="menu-main-menu" className="main-menu">
<li id="menu-item-10508" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}> <li id="menu-item-10508" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}>
<Link href="/">Home</Link> <Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home")}>Home</Link>
</li> </li>
<li id="menu-item-10509" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("how-it-works")}`}> <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</Link> <Link href="/how-it-works#how-it-works" onClick={(event) => handleNavClick(event, "/how-it-works", "how-it-works")}>How It Works</Link>
</li> </li>
<li id="menu-item-10510" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}> <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&trade; AI</Link> <Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth")}>MileTruth&trade; AI</Link>
</li> </li>
<li id="menu-item-10511" className={`menu-item menu-item-type-custom menu-item-10511${dmHeaderActive("solutions")}`}> <li id="menu-item-10511" className={`menu-item menu-item-type-custom menu-item-10511${dmHeaderActive("solutions")}`}>
<Link href="/solutions">Solutions</Link> <Link href="/solutions#solutions" onClick={(event) => handleNavClick(event, "/solutions", "solutions")}>Solutions</Link>
</li> </li>
<li id="menu-item-10512" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10512${dmHeaderActive("about")}`}> <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</Link> <Link href="/about-us#about" onClick={(event) => handleNavClick(event, "/about-us", "about")}>About</Link>
</li> </li>
<li id="menu-item-10535" className={`menu-item menu-item-type-post_type menu-item-object-page menu-item-10535${dmHeaderActive("blogs")}`}> <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</Link> <Link href="/blog#blogs" onClick={(event) => handleNavClick(event, "/blog", "blogs")}>Blogs</Link>
</li> </li>
</ul> </ul>
</nav> </nav>
@@ -362,14 +471,6 @@ export default function Header() {
> >
<div className="mobile-header-row"> <div className="mobile-header-row">
<div className="header-icons-container"> <div className="header-icons-container">
<div className="header-icon mini-cart">
<a href="#" className="mini-cart-trigger">
<i className="mini-cart-count"></i>
</a>
</div>
<a className="header-icon search-link" href="#">
<span className="search-trigger-icon"></span>
</a>
<div className="header-icon login-logout"> <div className="header-icon login-logout">
<a href="#" title="Login/Register" className="link-login"></a> <a href="#" title="Login/Register" className="link-login"></a>
</div> </div>
@@ -381,22 +482,22 @@ export default function Header() {
<nav> <nav>
<ul id="menu-main-menu-1" className="main-menu"> <ul id="menu-main-menu-1" className="main-menu">
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}> <li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}>
<Link href="/" onClick={closeAll}>Home</Link> <Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home", true)}>Home</Link>
</li> </li>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("how-it-works")}`}> <li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("how-it-works")}`}>
<Link href="/how-it-works" onClick={closeAll}>How It Works</Link> <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>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}> <li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
<Link href="/miletruth" onClick={closeAll}>MileTruth&trade; AI</Link> <Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth", true)}>MileTruth&trade; AI</Link>
</li> </li>
<li className={`menu-item menu-item-type-custom menu-item-10511${dmHeaderActive("solutions")}`}> <li className={`menu-item menu-item-type-custom menu-item-10511${dmHeaderActive("solutions")}`}>
<Link href="/solutions" onClick={closeAll}>Solutions</Link> <Link href="/solutions#solutions" onClick={(event) => handleNavClick(event, "/solutions", "solutions", true)}>Solutions</Link>
</li> </li>
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-10512${dmHeaderActive("about")}`}> <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" onClick={closeAll}>About</Link> <Link href="/about-us#about" onClick={(event) => handleNavClick(event, "/about-us", "about", true)}>About</Link>
</li> </li>
<li className={`menu-item menu-item-type-post_type menu-item-object-page menu-item-10535${dmHeaderActive("blogs")}`}> <li className={`menu-item menu-item-type-post_type menu-item-object-page menu-item-10535${dmHeaderActive("blogs")}`}>
<Link href="/blog" onClick={closeAll}>Blogs</Link> <Link href="/blog#blogs" onClick={(event) => handleNavClick(event, "/blog", "blogs", true)}>Blogs</Link>
</li> </li>
</ul> </ul>
</nav> </nav>
@@ -491,6 +592,69 @@ export default function Header() {
suppressHydrationWarning suppressHydrationWarning
dangerouslySetInnerHTML={{ dangerouslySetInnerHTML={{
__html: ` __html: `
/* ── Off-canvas menu: full-height flex column ──
Header (logo) at top, scrollable content in the middle, and the
"Get in touch" CTA pinned at the bottom — so the panel stays
usable however much content (e.g. multiple office addresses) it
holds. Scoped to #side-panel-2f31137; no other sidebar is touched.
Width, colors, the slide-in animation, and open/close behaviour
(driven by the .active class on the wrapper) are all unchanged. */
#side-panel-2f31137 .slide-sidebar {
display: flex !important;
flex-direction: column !important;
height: 100% !important;
padding: 0 !important; /* the three sections own their padding */
overflow: hidden !important; /* scrolling lives on the content area */
}
/* Fit the *visible* viewport. The panel height is calc(100vh - 20px),
but on mobile 100vh is the larger, URL-bar-inclusive height, which
pushed the bottom of the scroll list + the CTA below the fold and
made scrolling appear broken. dvh tracks the actually-visible area;
vh is kept as a fallback for older browsers. */
#side-panel-2f31137.slide-sidebar-wrapper {
height: calc(100vh - 20px);
height: calc(100dvh - 20px) !important;
}
#side-panel-2f31137 .slide-sidebar-header {
flex: 0 0 auto;
padding: 52px 36px 18px; /* top clears the floating close button */
text-align: center; /* centre the logo */
}
#side-panel-2f31137 .slide-sidebar-header figure {
margin: 0;
text-align: center;
}
#side-panel-2f31137 .slide-sidebar-header img {
display: inline-block; /* centred by the header's text-align */
}
#side-panel-2f31137 .slide-sidebar-content {
flex: 1 1 auto;
min-height: 0; /* let the flex child shrink so it can scroll */
overflow-y: auto;
overflow-x: hidden;
-webkit-overflow-scrolling: touch; /* momentum scroll on iOS */
overscroll-behavior: contain; /* don't chain scroll to the page */
padding: 18px 60px 8px; /* top gap below the logo header */
}
#side-panel-2f31137 .slide-sidebar-content:focus-visible {
outline: none; /* container is scroll-focusable, not a control */
}
#side-panel-2f31137 .slide-sidebar-cta {
flex: 0 0 auto;
padding: 16px 60px 36px;
}
/* Compact, readable address blocks (tighter line + lead-in than the
default 1.75em body spacing). */
#side-panel-2f31137 .slide-sidebar-content p {
line-height: 1.5;
margin-top: 4px;
}
/* Larger social icons — the logos-only block style renders them at 18px. */
#side-panel-2f31137 .wp-block-social-links.is-style-logos-only .wp-block-social-link a svg {
width: 26px !important;
height: 26px !important;
}
#masthead .elementor-element.elementor-element-466de1b { #masthead .elementor-element.elementor-element-466de1b {
position: absolute !important; position: absolute !important;
top: 5px !important; top: 5px !important;
@@ -611,6 +775,9 @@ export default function Header() {
/* Responsive logo adjustment on mobile/tablet */ /* Responsive logo adjustment on mobile/tablet */
@media (max-width: 1024px) { @media (max-width: 1024px) {
#masthead .elementor-element.elementor-element-466de1b {
top: 18px !important;
}
#masthead .elementor-element.elementor-element-846e53d .hfe-site-logo .hfe-site-logo-container img { #masthead .elementor-element.elementor-element-846e53d .hfe-site-logo .hfe-site-logo-container img {
width: 150px !important; width: 150px !important;
height: auto !important; height: auto !important;
@@ -629,7 +796,7 @@ export default function Header() {
to the top once scrolled past 50px, matching desktop behaviour. */ to the top once scrolled past 50px, matching desktop behaviour. */
#masthead .elementor-element.elementor-element-466de1b.dm-header-scrolled { #masthead .elementor-element.elementor-element-466de1b.dm-header-scrolled {
position: fixed !important; position: fixed !important;
top: 0 !important; top: 18px !important;
left: 0 !important; left: 0 !important;
right: 0 !important; right: 0 !important;
} }

View File

@@ -1,69 +1,170 @@
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState, type MutableRefObject } from "react";
import { usePathname } from "next/navigation";
import Image from "next/image"; import Image from "next/image";
import { usePathname } from "next/navigation";
/** /**
* LoadingScreen * LoadingScreen
* --------------------------------------------------------------------------- * ---------------------------------------------------------------------------
* Native reimplementation of the legacy WordPress page-loader: a black * Route-transition loader only: a black full-screen overlay with a centered,
* full-screen overlay with a centered, pulsing Doormile logo that fades out. * pulsing Doormile logo. It intentionally does not run on initial page render,
* image loading, lazy component loading, API requests, or scroll.
* *
* Shows on initial load (until the window finishes loading, min ~450ms to avoid * The App Router does not expose the old `next/router` routeChangeStart /
* a flash, capped at 2.5s so it never blocks) and again briefly on each route * routeChangeComplete event API. This component provides the same behavior by
* navigation. CWV-safe: fixed/out-of-flow (no layout shift), logo is priority, * detecting internal route-link starts and completing when `usePathname()`
* and it never delays hydration. * reports the committed route.
*/ */
type Phase = "visible" | "hiding" | "gone"; type Phase = "hidden" | "visible" | "hiding";
const MIN_SHOW_MS = 450; const MIN_VISIBLE_MS = 420;
const MAX_SHOW_MS = 2500; const MAX_VISIBLE_MS = 800;
const NAV_SHOW_MS = 520;
function getRoutePath(url: URL) {
return `${url.pathname}${url.search}`;
}
export default function LoadingScreen() { export default function LoadingScreen() {
const pathname = usePathname(); const pathname = usePathname();
const [phase, setPhase] = useState<Phase>("visible"); const [phase, setPhase] = useState<Phase>("hidden");
const isFirstRender = useRef(true); const phaseRef = useRef<Phase>("hidden");
const visibleSince = useRef(0);
const pendingPath = useRef<string | null>(null);
const currentRoutePath = useRef<string | null>(null);
const hideTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const safetyTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const setLoaderPhase = (nextPhase: Phase) => {
phaseRef.current = nextPhase;
setPhase(nextPhase);
};
const clearTimer = (timer: MutableRefObject<ReturnType<typeof setTimeout> | null>) => {
if (!timer.current) return;
clearTimeout(timer.current);
timer.current = null;
};
const completeTransition = () => {
pendingPath.current = null;
clearTimer(safetyTimer);
if (phaseRef.current === "hidden" || phaseRef.current === "hiding") return;
const elapsed = performance.now() - visibleSince.current;
const wait = Math.max(0, MIN_VISIBLE_MS - elapsed);
clearTimer(hideTimer);
hideTimer.current = setTimeout(() => {
setLoaderPhase("hiding");
hideTimer.current = setTimeout(() => setLoaderPhase("hidden"), 360);
}, wait);
};
// Initial load: hide once the page is ready.
useEffect(() => { useEffect(() => {
const start = performance.now(); currentRoutePath.current = `${pathname}${window.location.search}`;
let began = false; completeTransition();
let fadeTimer: ReturnType<typeof setTimeout>; // `phase` intentionally stays out of this dependency list. The route commit
// is the completion signal; phase changes should not repeatedly restart hide.
const begin = () => { // eslint-disable-next-line react-hooks/exhaustive-deps
if (began) return;
began = true;
const wait = Math.max(0, MIN_SHOW_MS - (performance.now() - start));
fadeTimer = setTimeout(() => setPhase("hiding"), wait);
};
const cap = setTimeout(begin, MAX_SHOW_MS);
const onReady = () => begin();
if (document.readyState === "complete") begin();
else window.addEventListener("load", onReady, { once: true });
return () => {
clearTimeout(cap);
clearTimeout(fadeTimer);
window.removeEventListener("load", onReady);
};
}, []);
// Route navigations: flash the loader briefly for an app-like transition.
useEffect(() => {
if (isFirstRender.current) {
isFirstRender.current = false;
return;
}
setPhase("visible");
const t = setTimeout(() => setPhase("hiding"), NAV_SHOW_MS);
return () => clearTimeout(t);
}, [pathname]); }, [pathname]);
if (phase === "gone") return null; useEffect(() => {
const startTransition = (targetPath: string, force = false) => {
if (!force && targetPath === getRoutePath(new URL(window.location.href))) return;
if (pendingPath.current === targetPath && phaseRef.current === "visible") return;
pendingPath.current = targetPath;
clearTimer(hideTimer);
clearTimer(safetyTimer);
visibleSince.current = performance.now();
setLoaderPhase("visible");
safetyTimer.current = setTimeout(() => {
completeTransition();
}, MAX_VISIBLE_MS);
};
const getInternalRouteTarget = (anchor: HTMLAnchorElement) => {
const rawHref = anchor.getAttribute("href");
if (!rawHref || rawHref.startsWith("#")) return null;
if (anchor.target && anchor.target !== "_self") return null;
if (anchor.hasAttribute("download")) return null;
if (/^(mailto:|tel:|sms:|javascript:)/i.test(rawHref)) return null;
const url = new URL(rawHref, window.location.href);
if (url.origin !== window.location.origin) return null;
const current = new URL(window.location.href);
const sameRoute = url.pathname === current.pathname && url.search === current.search;
if (sameRoute) return null;
return getRoutePath(url);
};
const handleDocumentClick = (event: MouseEvent) => {
if (event.defaultPrevented) return;
if (event.button !== 0) return;
if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
const anchor = (event.target as Element | null)?.closest("a[href]");
if (!anchor || !(anchor instanceof HTMLAnchorElement)) return;
const targetPath = getInternalRouteTarget(anchor);
if (targetPath) startTransition(targetPath);
};
const originalPushState = window.history.pushState;
const originalReplaceState = window.history.replaceState;
const scheduleHistoryTransition = (targetPath: string) => {
const currentPath = getRoutePath(new URL(window.location.href));
if (targetPath === currentPath) return;
window.setTimeout(() => {
startTransition(targetPath, true);
}, 0);
};
window.history.pushState = function patchedPushState(...args) {
const urlArg = args[2];
if (typeof urlArg === "string" || urlArg instanceof URL) {
const url = new URL(urlArg, window.location.href);
if (url.origin === window.location.origin) scheduleHistoryTransition(getRoutePath(url));
}
return originalPushState.apply(this, args);
};
window.history.replaceState = function patchedReplaceState(...args) {
const urlArg = args[2];
if (typeof urlArg === "string" || urlArg instanceof URL) {
const url = new URL(urlArg, window.location.href);
if (url.origin === window.location.origin) scheduleHistoryTransition(getRoutePath(url));
}
return originalReplaceState.apply(this, args);
};
const handlePopState = () => {
const targetPath = getRoutePath(new URL(window.location.href));
if (targetPath !== currentRoutePath.current) startTransition(targetPath, true);
};
document.addEventListener("click", handleDocumentClick, true);
window.addEventListener("popstate", handlePopState);
return () => {
document.removeEventListener("click", handleDocumentClick, true);
window.removeEventListener("popstate", handlePopState);
window.history.pushState = originalPushState;
window.history.replaceState = originalReplaceState;
clearTimer(hideTimer);
clearTimer(safetyTimer);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (phase === "hidden") return null;
return ( return (
<div <div
@@ -72,7 +173,10 @@ export default function LoadingScreen() {
aria-live="polite" aria-live="polite"
aria-label="Loading" aria-label="Loading"
onTransitionEnd={(e) => { onTransitionEnd={(e) => {
if (e.propertyName === "opacity" && phase === "hiding") setPhase("gone"); if (e.propertyName === "opacity" && phase === "hiding") {
setPhase("hidden");
phaseRef.current = "hidden";
}
}} }}
> >
<div className="dm-loader__pulse"> <div className="dm-loader__pulse">

View File

@@ -4,9 +4,9 @@
* ContactMapEmbed * ContactMapEmbed
* --------------------------------------------------------------------------- * ---------------------------------------------------------------------------
* Client boundary that lazy-loads the Leaflet map. `ssr: false` keeps Leaflet * Client boundary that lazy-loads the Leaflet map. `ssr: false` keeps Leaflet
* out of the server bundle and off the critical render path; the skeleton fills * out of the server bundle and off the critical render path. The host container
* the host container's fixed height so there is zero layout shift (CLS) while * already owns the fixed height, so the loading state stays invisible before
* the map chunk loads. * the interactive map is ready.
*/ */
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
@@ -14,7 +14,7 @@ import styles from "./OfficeMap.module.css";
const OfficeMap = dynamic(() => import("./OfficeMap"), { const OfficeMap = dynamic(() => import("./OfficeMap"), {
ssr: false, ssr: false,
loading: () => <div className={styles.skeleton} role="presentation" aria-hidden="true" />, loading: () => <div className={styles.mapMountReserve} role="presentation" aria-hidden="true" />,
}); });
export default function ContactMapEmbed() { export default function ContactMapEmbed() {

View File

@@ -1,16 +1,11 @@
/* =========================================================================== /* ===========================================================================
Office satellite map — scoped styles. Office satellite map — scoped styles.
All Leaflet global classes are namespaced under `.root` via :global() so this
module cannot leak into the rest of the app and vice-versa.
Colours/radii reference the app's theme (dark surface, #C01227 brand red).
The map fills its container 100% — no hard-coded layout values live here.
=========================================================================== */ =========================================================================== */
.root { .root {
position: relative; position: relative;
width: 100%; width: 100%;
height: 100%; height: 100%;
/* inherit the host container's rounded corners so tiles/controls clip cleanly */
border-radius: inherit; border-radius: inherit;
overflow: hidden; overflow: hidden;
background: #0b0b0b; background: #0b0b0b;
@@ -34,10 +29,6 @@
.controls { .controls {
position: absolute; position: absolute;
top: 14px; top: 14px;
/* Span the full width (with side insets) instead of left:50%+auto-width:
an absolutely-positioned auto-width box anchored at left:50% can only grow
to 50% of the map, which on a narrow phone forced the city buttons to stack
vertically. With left/right insets the row uses the full width and centres. */
left: 12px; left: 12px;
right: 12px; right: 12px;
z-index: 600; /* above tiles + markers; popups open lower so they never collide */ z-index: 600; /* above tiles + markers; popups open lower so they never collide */
@@ -47,6 +38,7 @@
gap: 8px; gap: 8px;
pointer-events: none; /* let the row be transparent to drags; buttons re-enable */ pointer-events: none; /* let the row be transparent to drags; buttons re-enable */
} }
.controlBtn { .controlBtn {
pointer-events: auto; pointer-events: auto;
appearance: none; appearance: none;
@@ -68,15 +60,18 @@
transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease, transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease,
transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s ease; transform 0.2s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.2s ease;
} }
.controlBtn:hover { .controlBtn:hover {
background: rgba(192, 18, 39, 0.9); background: rgba(192, 18, 39, 0.9);
border-color: #c01227; border-color: #c01227;
transform: translateY(-1px); transform: translateY(-1px);
} }
.controlBtn:focus-visible { .controlBtn:focus-visible {
outline: 2px solid #ffffff; outline: 2px solid #ffffff;
outline-offset: 2px; outline-offset: 2px;
} }
.controlBtnActive, .controlBtnActive,
.controlBtnActive:hover { .controlBtnActive:hover {
background: #c01227; background: #c01227;
@@ -85,12 +80,20 @@
box-shadow: 0 6px 18px rgba(192, 18, 39, 0.45); box-shadow: 0 6px 18px rgba(192, 18, 39, 0.45);
} }
/* Headquarters button — subtly elevated so it reads as the primary location. */
.controlBtnHq {
border-color: rgba(192, 18, 39, 0.55);
box-shadow: 0 0 0 1px rgba(192, 18, 39, 0.25), 0 4px 14px rgba(192, 18, 39, 0.2);
}
.controlBtnHq.controlBtnActive {
box-shadow: 0 6px 20px rgba(192, 18, 39, 0.55);
}
@media (max-width: 480px) { @media (max-width: 480px) {
.controls { .controls {
top: 12px; top: 12px;
gap: 5px; gap: 5px;
/* Keep all three city buttons on a single line — shrink them (below) so the
row fits instead of wrapping to two lines on narrow phones. */
flex-wrap: nowrap; flex-wrap: nowrap;
} }
.controlBtn { .controlBtn {
@@ -101,37 +104,88 @@
/* ---- Branded marker pin ---- */ /* ---- Branded marker pin ---- */
.markerIcon { .markerIcon {
/* divIcon resets — keep only the pin's own drop shadow */
background: transparent; background: transparent;
border: 0; border: 0;
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.55)); filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.55));
transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1); transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
} }
.markerIcon:hover, .markerIcon:hover,
.markerIcon:focus-visible { .markerIcon:focus-visible {
transform: translateY(-3px) scale(1.06); transform: translateY(-3px) scale(1.06);
} }
/* ---- Themed Leaflet internals (scoped to this map only) ---- */ /* ---- Headquarters pin — larger, glowing, pulsing, always on top ---- */
.markerIconHq {
background: transparent;
border: 0;
filter: drop-shadow(0 0 9px rgba(192, 18, 39, 0.85))
drop-shadow(0 5px 7px rgba(0, 0, 0, 0.55));
transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
}
.markerIconHq svg {
position: relative;
z-index: 2;
}
.markerIconHq:hover,
.markerIconHq:focus-visible {
transform: translateY(-3px) scale(1.05);
}
/* Soft expanding ring radiating from the HQ pin head. */
.pinPulse {
position: absolute;
top: 19px;
left: 20px;
width: 18px;
height: 18px;
margin: -9px 0 0 -9px;
border-radius: 50%;
background: rgba(192, 18, 39, 0.5);
z-index: 1;
pointer-events: none;
animation: hqPulse 2.2s ease-out infinite;
}
@keyframes hqPulse {
0% {
transform: scale(0.6);
opacity: 0.75;
}
70% {
transform: scale(3);
opacity: 0;
}
100% {
transform: scale(3);
opacity: 0;
}
}
/* ---- Leaflet Branded Elements ---- */
.root :global(.leaflet-container) { .root :global(.leaflet-container) {
background: #0b0b0b; background: #0b0b0b;
} }
/* Zoom + attribution controls — dark, on-theme */
.root :global(.leaflet-bar) { .root :global(.leaflet-bar) {
border: 1px solid rgba(255, 255, 255, 0.12); border: 1px solid rgba(255, 255, 255, 0.12);
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4); box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
} }
.root :global(.leaflet-bar a) { .root :global(.leaflet-bar a) {
background: rgba(15, 15, 17, 0.92); background: rgba(15, 15, 17, 0.92);
color: #f5f5f5; color: #f5f5f5;
border-bottom-color: rgba(255, 255, 255, 0.12); border-bottom-color: rgba(255, 255, 255, 0.12);
transition: background-color 0.2s ease, color 0.2s ease; transition: background-color 0.2s ease, color 0.2s ease;
} }
.root :global(.leaflet-bar a:hover) { .root :global(.leaflet-bar a:hover) {
background: #c01227; background: #c01227;
color: #ffffff; color: #ffffff;
} }
.root :global(.leaflet-bar a:focus-visible) { .root :global(.leaflet-bar a:focus-visible) {
outline: 2px solid #c01227; outline: 2px solid #c01227;
outline-offset: 2px; outline-offset: 2px;
@@ -146,71 +200,132 @@
border-radius: 6px 0 0 0; border-radius: 6px 0 0 0;
padding: 2px 8px; padding: 2px 8px;
} }
.root :global(.leaflet-control-attribution a) { .root :global(.leaflet-control-attribution a) {
color: rgba(255, 255, 255, 0.85); color: rgba(255, 255, 255, 0.85);
} }
/* Popup — dark card matching the app surfaces */ /* ---- Floating Popup overlays ---- */
.root :global(.leaflet-popup-content-wrapper) { .root :global(.leaflet-popup) {
background: #141416; animation: popupFade 0.2s ease-out forwards;
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 12px;
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.55);
}
.root :global(.leaflet-popup-content) {
margin: 12px 16px;
font-size: 14px;
font-weight: 700;
letter-spacing: -0.01em;
line-height: 1.3;
}
.root :global(.leaflet-popup-tip) {
background: #141416;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.root :global(.leaflet-popup-close-button) {
color: rgba(255, 255, 255, 0.6);
}
.root :global(.leaflet-popup-close-button:hover) {
color: #ffffff;
}
.root :global(.leaflet-popup-content .office-popup__name) {
display: block;
}
.root :global(.leaflet-popup-content .office-popup__dot) {
display: inline-block;
width: 7px;
height: 7px;
margin-right: 8px;
border-radius: 50%;
background: #c01227;
vertical-align: middle;
} }
/* ---- Loading skeleton (prevents CLS — fills the fixed-height host) ---- */ .root :global(.leaflet-popup-content-wrapper) {
.skeleton { background: #0f0f11;
color: #ffffff;
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px; /* Clean business card rounded corners */
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.6);
overflow: hidden;
padding: 0;
animation: popupScale 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards;
transform-origin: bottom center;
}
.root :global(.leaflet-popup-content) {
margin: 0 !important;
width: auto !important;
height: auto !important;
}
.root :global(.leaflet-popup-tip) {
background: #0f0f11;
border: 1px solid rgba(255, 255, 255, 0.08);
}
@keyframes popupFade {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes popupScale {
from { transform: scale(0.92); }
to { transform: scale(1); }
}
/* Compact Details Card inside Popup */
.card {
display: flex;
flex-direction: column;
padding: 12px 16px;
min-width: 230px;
max-width: 280px;
box-sizing: border-box;
font-family: var(--font-manrope), system-ui, -apple-system, sans-serif;
border-top: 2px solid #c01227; /* Thinner brand accent line */
background: #0f0f11;
height: auto;
}
.cardHeader {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
padding-bottom: 6px;
}
.cardIcon {
font-size: 15px;
display: inline-flex;
align-items: center;
}
.cardTitle {
font-size: 17px !important;
font-weight: 800 !important;
color: #ffffff !important; /* Force white header text */
margin: 0 !important;
letter-spacing: -0.01em !important;
}
.cardBody {
display: flex;
flex-direction: column;
gap: 4px;
}
.addressLine {
font-size: 14px !important;
line-height: 1.5 !important;
color: rgba(255, 255, 255, 0.75) !important;
margin: 0 0 6px 0 !important;
padding: 0 !important;
font-weight: 500 !important;
}
.addressLine:last-child {
margin-bottom: 0 !important;
}
@media (max-width: 480px) {
.card {
padding: 9px 12px;
min-width: 200px;
max-width: 230px;
}
.cardTitle {
font-size: 14px !important;
}
.addressLine {
font-size: 12px !important;
margin-bottom: 3px !important;
line-height: 1.4 !important;
}
}
/* ---- Invisible lazy mount reserve ---- */
.mapMountReserve {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: inherit; border-radius: inherit;
background:
linear-gradient(
100deg,
rgba(255, 255, 255, 0) 30%,
rgba(255, 255, 255, 0.05) 50%,
rgba(255, 255, 255, 0) 70%
),
#101012;
background-size: 200% 100%;
animation: shimmer 1.4s ease-in-out infinite;
}
@keyframes shimmer {
from { background-position: 200% 0; }
to { background-position: -200% 0; }
} }
@media (prefers-reduced-motion: reduce) { @media (prefers-reduced-motion: reduce) {
.skeleton { animation: none; } .markerIcon,
.markerIcon { transition: none; } .markerIconHq { transition: none; }
.pinPulse { animation: none; opacity: 0.45; }
} }
/* ---- Graceful error fallback ---- */ /* ---- Graceful error fallback ---- */
@@ -229,12 +344,14 @@
color: rgba(255, 255, 255, 0.82); color: rgba(255, 255, 255, 0.82);
font-family: var(--font-manrope), system-ui, sans-serif; font-family: var(--font-manrope), system-ui, sans-serif;
} }
.errorTitle { .errorTitle {
font-size: clamp(15px, 2.4vw, 18px); font-size: clamp(15px, 2.4vw, 18px);
font-weight: 800; font-weight: 800;
color: #ffffff; color: #ffffff;
margin: 0; margin: 0;
} }
.errorText { .errorText {
font-size: 13px; font-size: 13px;
max-width: 38ch; max-width: 38ch;
@@ -242,6 +359,7 @@
line-height: 1.55; line-height: 1.55;
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.6);
} }
.errorList { .errorList {
list-style: none; list-style: none;
margin: 4px 0 0; margin: 4px 0 0;
@@ -251,6 +369,7 @@
gap: 8px 12px; gap: 8px 12px;
justify-content: center; justify-content: center;
} }
.errorList li { .errorList li {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@@ -259,6 +378,7 @@
font-weight: 700; font-weight: 700;
color: #f1f1f1; color: #f1f1f1;
} }
.errorList li::before { .errorList li::before {
content: ""; content: "";
width: 7px; width: 7px;
@@ -267,7 +387,7 @@
background: #c01227; background: #c01227;
} }
/* ---- Screen-reader-only office list (semantic fallback) ---- */ /* ---- Screen-reader-only office list ---- */
.srOnly { .srOnly {
position: absolute; position: absolute;
width: 1px; width: 1px;

View File

@@ -7,9 +7,9 @@
* Doormile office markers, plus a row of "jump to office" buttons that fly the * Doormile office markers, plus a row of "jump to office" buttons that fly the
* map to a selected office's coordinates and open its popup. * map to a selected office's coordinates and open its popup.
* *
* Loaded via a `ssr:false` dynamic import so Leaflet (which touches `window`) * The map centers with a North latitude offset to keep markers lower in the viewport,
* never runs on the server and cannot cause hydration mismatches. Layout/spacing * avoiding collisions with the navigation tabs. Popups are compact and styled like
* is owned by the host container (see ContactMap). * clean business cards.
*/ */
import "leaflet/dist/leaflet.css"; import "leaflet/dist/leaflet.css";
@@ -20,14 +20,13 @@ import L from "leaflet";
import { MapContainer, Marker, Popup, TileLayer, ZoomControl, useMap } from "react-leaflet"; import { MapContainer, Marker, Popup, TileLayer, ZoomControl, useMap } from "react-leaflet";
import { import {
LatLng,
ESRI_WORLD_IMAGERY, ESRI_WORLD_IMAGERY,
MAP_FIT_MAX_ZOOM, HQ_OFFICE,
MAP_FIT_PADDING,
MAP_FOCUS_ZOOM, MAP_FOCUS_ZOOM,
MAP_INITIAL_CENTER, MAP_INITIAL_CENTER,
MAP_INITIAL_ZOOM, MAP_INITIAL_ZOOM,
OFFICE_LOCATIONS, OFFICE_LOCATIONS,
type LatLng,
} from "./offices"; } from "./offices";
type MapStatus = "loading" | "ready" | "error"; type MapStatus = "loading" | "ready" | "error";
@@ -35,8 +34,28 @@ type MapStatus = "loading" | "ready" | "error";
/** A request to focus a specific office. `nonce` lets the same office be re-selected. */ /** A request to focus a specific office. `nonce` lets the same office be re-selected. */
type FocusTarget = { id: string; nonce: number }; type FocusTarget = { id: string; nonce: number };
/** Build the branded SVG pin once (module scope is fine — this file is client-only). */ /**
function createMarkerIcon(): L.DivIcon { * Build a branded SVG pin. The headquarters pin is larger, carries a red glow
* and a soft pulse ring, and sits above every other marker.
*/
function createMarkerIcon(isHeadquarters: boolean): L.DivIcon {
if (isHeadquarters) {
return L.divIcon({
className: styles.markerIconHq,
html: `
<span class="${styles.pinPulse}" aria-hidden="true"></span>
<svg width="40" height="52" viewBox="0 0 30 40" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" focusable="false">
<path d="M15 0C6.72 0 0 6.72 0 15c0 10.5 13.06 23.86 13.62 24.42a1.95 1.95 0 0 0 2.76 0C16.94 38.86 30 25.5 30 15 30 6.72 23.28 0 15 0Z" fill="#C01227"/>
<path d="M15 1.5C7.54 1.5 1.5 7.54 1.5 15c0 9.6 12.3 22.4 12.83 22.94a.95.95 0 0 0 1.34 0C16.2 37.4 28.5 24.6 28.5 15 28.5 7.54 22.46 1.5 15 1.5Z" fill="none" stroke="#ffffff" stroke-width="1.2" stroke-opacity="0.9"/>
<circle cx="15" cy="15" r="5.4" fill="#ffffff"/>
</svg>
`,
iconSize: [40, 52],
iconAnchor: [20, 52],
popupAnchor: [0, -52], // Anchored to top tip of pin
});
}
return L.divIcon({ return L.divIcon({
className: styles.markerIcon, className: styles.markerIcon,
html: ` html: `
@@ -48,83 +67,62 @@ function createMarkerIcon(): L.DivIcon {
`, `,
iconSize: [30, 40], iconSize: [30, 40],
iconAnchor: [15, 40], iconAnchor: [15, 40],
popupAnchor: [0, -36], popupAnchor: [0, -40], // Anchored to top tip of pin
}); });
} }
/** /**
* Imperative map effects that need the Leaflet instance: * Imperative map effects that need the Leaflet instance:
* - fit the viewport to every marker (with edge padding) * - keep the viewport sized correctly across resizes / lazy reveals
* - keep that framing correct across resizes / lazy reveals * - snap to the offset HQ on first paint, fly thereafter and open popups.
* - fly to a single office when one is selected via the buttons
*/ */
function MapController({ function MapController({
positions,
focus, focus,
markerRefs, markerRefs,
}: { }: {
positions: LatLng[];
focus: FocusTarget | null; focus: FocusTarget | null;
markerRefs: React.RefObject<Record<string, L.Marker>>; markerRefs: React.RefObject<Record<string, L.Marker>>;
}) { }) {
const map = useMap(); const map = useMap();
const didInit = useRef(false);
// Latest focus, read inside the (stable) resize handler without resubscribing. // Keep the map correctly sized on container resize / lazy reveal.
const focusRef = useRef(focus);
useEffect(() => {
focusRef.current = focus;
}, [focus]);
const fitAll = useCallback(() => {
if (positions.length === 0) return;
map.invalidateSize();
map.fitBounds(L.latLngBounds(positions), {
padding: MAP_FIT_PADDING,
maxZoom: MAP_FIT_MAX_ZOOM,
});
}, [map, positions]);
// Initial fit, after the container has its final size.
useEffect(() => {
const raf = requestAnimationFrame(fitAll);
return () => cancelAnimationFrame(raf);
}, [fitAll]);
// Re-measure on container resize; only re-frame all markers when nothing is
// focused, so resizing while zoomed into one office doesn't jump the view away.
useEffect(() => { useEffect(() => {
const container = map.getContainer(); const container = map.getContainer();
let raf = 0; let raf = 0;
const observer = new ResizeObserver(() => { const observer = new ResizeObserver(() => {
cancelAnimationFrame(raf); cancelAnimationFrame(raf);
raf = requestAnimationFrame(() => { raf = requestAnimationFrame(() => map.invalidateSize());
map.invalidateSize();
if (!focusRef.current) {
map.fitBounds(L.latLngBounds(positions), {
padding: MAP_FIT_PADDING,
maxZoom: MAP_FIT_MAX_ZOOM,
});
}
});
}); });
observer.observe(container); observer.observe(container);
return () => { return () => {
cancelAnimationFrame(raf); cancelAnimationFrame(raf);
observer.disconnect(); observer.disconnect();
}; };
}, [map, positions]); }, [map]);
// Fly to the selected office, then open its popup once movement settles. // React to the focused office: snap on first paint, fly thereafter.
useEffect(() => { useEffect(() => {
if (!focus) return; if (!focus) return;
const office = OFFICE_LOCATIONS.find((item) => item.id === focus.id); const office = OFFICE_LOCATIONS.find((item) => item.id === focus.id);
if (!office) return; if (!office) return;
map.flyTo(office.position, MAP_FOCUS_ZOOM, { duration: 1.1 }); const openPopup = () => markerRefs.current[office.id]?.openPopup();
const marker = markerRefs.current[office.id]; // Offset the center slightly North so the marker sits lower in the viewport,
if (!marker) return; // preventing the popup card from colliding with the top controls.
const openPopup = () => marker.openPopup(); const offsetLatitude = 0.022;
const centeredPosition: LatLng = [office.position[0] + offsetLatitude, office.position[1]];
if (!didInit.current) {
didInit.current = true;
map.invalidateSize();
map.setView(centeredPosition, MAP_FOCUS_ZOOM, { animate: false });
const raf = requestAnimationFrame(openPopup);
return () => cancelAnimationFrame(raf);
}
map.flyTo(centeredPosition, MAP_FOCUS_ZOOM, { duration: 1.1 });
map.once("moveend", openPopup); map.once("moveend", openPopup);
return () => { return () => {
map.off("moveend", openPopup); map.off("moveend", openPopup);
@@ -135,18 +133,25 @@ function MapController({
} }
export default function OfficeMap() { export default function OfficeMap() {
const icon = useMemo(() => createMarkerIcon(), []); const icon = useMemo(() => createMarkerIcon(false), []);
const positions = useMemo<LatLng[]>( const hqIcon = useMemo(() => createMarkerIcon(true), []);
() => OFFICE_LOCATIONS.map((office) => office.position),
[],
);
const markerRefs = useRef<Record<string, L.Marker>>({}); const markerRefs = useRef<Record<string, L.Marker>>({});
const [focus, setFocus] = useState<FocusTarget | null>(null); // Default to the headquarters.
const [focus, setFocus] = useState<FocusTarget | null>({ id: HQ_OFFICE.id, nonce: 0 });
const focusOffice = useCallback((id: string) => { const focusOffice = useCallback((id: string) => {
setFocus((prev) => ({ id, nonce: (prev?.nonce ?? 0) + 1 })); setFocus((prev) => ({ id, nonce: (prev?.nonce ?? 0) + 1 }));
}, []); }, []);
const [hoveredOfficeId, setHoveredOfficeId] = useState<string | null>(null);
// Restore the focused office popup when mouse leaves other markers.
useEffect(() => {
if (hoveredOfficeId === null && focus?.id) {
markerRefs.current[focus.id]?.openPopup();
}
}, [hoveredOfficeId, focus]);
const [status, setStatus] = useState<MapStatus>("loading"); const [status, setStatus] = useState<MapStatus>("loading");
const loadedRef = useRef(false); const loadedRef = useRef(false);
const errorCountRef = useRef(0); const errorCountRef = useRef(0);
@@ -156,8 +161,6 @@ export default function OfficeMap() {
setStatus("ready"); setStatus("ready");
}, []); }, []);
// Only surface an error if tiles never render (network/CORS/down). Once any
// tile load succeeds the map is considered healthy and stays that way.
const handleTileError = useCallback(() => { const handleTileError = useCallback(() => {
errorCountRef.current += 1; errorCountRef.current += 1;
if (!loadedRef.current && errorCountRef.current >= 6) setStatus("error"); if (!loadedRef.current && errorCountRef.current >= 6) setStatus("error");
@@ -189,7 +192,9 @@ export default function OfficeMap() {
<button <button
key={office.id} key={office.id}
type="button" type="button"
className={`${styles.controlBtn} ${isActive ? styles.controlBtnActive : ""}`} className={`${styles.controlBtn} ${isActive ? styles.controlBtnActive : ""} ${
office.isHeadquarters ? styles.controlBtnHq : ""
}`}
aria-pressed={isActive} aria-pressed={isActive}
aria-label={`Show ${office.name} on the map`} aria-label={`Show ${office.name} on the map`}
onClick={() => focusOffice(office.id)} onClick={() => focusOffice(office.id)}
@@ -221,29 +226,63 @@ export default function OfficeMap() {
eventHandlers={{ load: handleTileLoad, tileerror: handleTileError }} eventHandlers={{ load: handleTileLoad, tileerror: handleTileError }}
/> />
{OFFICE_LOCATIONS.map((office) => ( {OFFICE_LOCATIONS.map((office) => {
<Marker const isFocused = focus?.id === office.id;
key={office.id} const isHovered = hoveredOfficeId === office.id;
position={office.position}
icon={icon}
keyboard
title={office.name}
alt={office.name}
eventHandlers={{ click: () => focusOffice(office.id) }}
ref={(instance) => {
if (instance) markerRefs.current[office.id] = instance;
}}
>
<Popup>
<span className="office-popup__name">
<span className="office-popup__dot" aria-hidden="true" />
{office.name}
</span>
</Popup>
</Marker>
))}
<MapController positions={positions} focus={focus} markerRefs={markerRefs} /> return (
<Marker
key={office.id}
position={office.position}
icon={office.isHeadquarters ? hqIcon : icon}
zIndexOffset={office.isHeadquarters ? 1000 : 0}
keyboard
title={office.name}
alt={office.name}
eventHandlers={{
click: () => focusOffice(office.id),
mouseover: (event) => {
setHoveredOfficeId(office.id);
event.target.openPopup();
},
mouseout: (event) => {
setHoveredOfficeId(null);
if (focus?.id !== office.id) {
event.target.closePopup();
}
},
}}
ref={(instance) => {
if (instance) markerRefs.current[office.id] = instance;
}}
>
<Popup
className={styles.popup}
autoPan={isFocused}
autoPanPadding={[25, 25]}
closeButton={false}
minWidth={240}
maxWidth={290}
>
<div className={styles.card}>
<div className={styles.cardHeader}>
<span className={styles.cardIcon} aria-hidden="true">📍</span>
<h4 className={styles.cardTitle}>{office.city} Office</h4>
</div>
<div className={styles.cardBody}>
{office.address.map((line, idx) => (
<p key={idx} className={styles.addressLine}>
{line}
</p>
))}
</div>
</div>
</Popup>
</Marker>
);
})}
<MapController focus={focus} markerRefs={markerRefs} />
</MapContainer> </MapContainer>
{status === "error" && ( {status === "error" && (

View File

@@ -4,6 +4,9 @@
* Kept dependency-free (no Leaflet runtime import) so it can be safely consumed * Kept dependency-free (no Leaflet runtime import) so it can be safely consumed
* by both server and client modules. Types model `[lat, lng]` tuples that are * by both server and client modules. Types model `[lat, lng]` tuples that are
* directly compatible with Leaflet's `LatLngExpression`. * directly compatible with Leaflet's `LatLngExpression`.
*
* Coordinates are the real Doormile operational sites, verified against
* satellite view — not generic city-centre points.
*/ */
/** A `[latitude, longitude]` coordinate pair. */ /** A `[latitude, longitude]` coordinate pair. */
@@ -12,21 +15,77 @@ export type LatLng = [number, number];
export interface OfficeLocation { export interface OfficeLocation {
/** Stable, unique key (used for React keys + analytics). */ /** Stable, unique key (used for React keys + analytics). */
readonly id: string; readonly id: string;
/** Short city label, shown on the navigation buttons. */ /** Short city label, shown on the navigation button. */
readonly city: string; readonly city: string;
/** Human-readable label shown in the marker popup + a11y fallback. */ /** Human-readable label shown in the a11y fallback + marker title. */
readonly name: string; readonly name: string;
/** Compact heading shown in the marker tooltip/popup (e.g. "Hyderabad HQ"). */
readonly shortLabel: string;
/** `[latitude, longitude]`. */ /** `[latitude, longitude]`. */
readonly position: LatLng; readonly position: LatLng;
/** Headquarters gets the largest, glowing, default-active marker. */
readonly isHeadquarters?: boolean;
/** Full office address lines. */
readonly address: readonly string[];
} }
/** The three permanent office markers, ordered north-to-south is irrelevant — bounds are auto-fit. */ /**
* The three permanent office markers, ordered for the navigation row by
* operational hierarchy: Hyderabad (HQ) → Bengaluru → Coimbatore. The
* headquarters is conveyed purely through styling (active state + border +
* glow), not through icons or label text.
*/
export const OFFICE_LOCATIONS: readonly OfficeLocation[] = [ export const OFFICE_LOCATIONS: readonly OfficeLocation[] = [
{ id: "coimbatore", city: "Coimbatore", name: "Coimbatore Office", position: [11.0168, 76.9558] }, {
{ id: "bengaluru", city: "Bengaluru", name: "Bengaluru Office", position: [12.9716, 77.5946] }, id: "hyderabad",
{ id: "hyderabad", city: "Hyderabad", name: "Hyderabad Office", position: [17.385, 78.4867] }, city: "Hyderabad",
name: "Doormile Headquarters",
shortLabel: "Hyderabad HQ",
// Vision Ultima, Jayabheri Enclave, Gachibowli — verified on satellite.
position: [17.4484, 78.3573],
isHeadquarters: true,
address: [
"5th Floor, Vision Ultima,",
"Street No.3, Jayabheri Enclave,",
"Gachibowli, Hyderabad,",
"Telangana 500032"
],
},
{
id: "bengaluru",
city: "Bengaluru",
name: "Bengaluru Hub",
shortLabel: "Bengaluru Hub",
// Resolved from the supplied Google Maps share link — verified on satellite.
position: [12.9929351, 77.6988599],
address: [
"C612, 6th Floor,",
"Trifecta Starlight, ITPL Road,",
"Garudacharapalya, Mahadevapura,",
"Bangalore 560048,",
"Karnataka, India"
],
},
{
id: "coimbatore",
city: "Coimbatore",
name: "Coimbatore Hub",
shortLabel: "Coimbatore Hub",
// Mayflower Valencia, Coimbatore — verified against satellite view.
position: [11.0191, 76.9883],
address: [
"Mayflower Valencia,",
"Near Nava India Bus Stop,",
"Avinashi Road, Udayampalayam,",
"Tamil Nadu 641003"
],
},
]; ];
/** The headquarters office — focused by default on load. Falls back to the first office. */
export const HQ_OFFICE: OfficeLocation =
OFFICE_LOCATIONS.find((office) => office.isHeadquarters) ?? OFFICE_LOCATIONS[0];
export interface TileLayerConfig { export interface TileLayerConfig {
readonly url: string; readonly url: string;
readonly attribution: string; readonly attribution: string;
@@ -45,15 +104,12 @@ export const ESRI_WORLD_IMAGERY: TileLayerConfig = {
maxZoom: 19, maxZoom: 19,
}; };
/** Padding (in px) applied when auto-fitting bounds so markers never touch the edges. */ /** City-level zoom used when an office is selected (and for the initial HQ view). */
export const MAP_FIT_PADDING: LatLng = [50, 50];
/** Cap the auto-fit zoom so two close offices don't zoom the map in too far. */
export const MAP_FIT_MAX_ZOOM = 7;
/** City-level zoom used when a single office is selected via the nav buttons. */
export const MAP_FOCUS_ZOOM = 13; export const MAP_FOCUS_ZOOM = 13;
/** Initial center/zoom (roughly the centroid of the offices) used before bounds are fit. */ /**
export const MAP_INITIAL_CENTER: LatLng = [14.0, 77.7]; * Initial center/zoom: the experience opens focused on the Hyderabad HQ so the
export const MAP_INITIAL_ZOOM = 5; * command-centre reads as the heart of the network the instant the map paints.
*/
export const MAP_INITIAL_CENTER: LatLng = HQ_OFFICE.position;
export const MAP_INITIAL_ZOOM = MAP_FOCUS_ZOOM;

View File

@@ -3,7 +3,31 @@ import Link from "next/link";
export default function AboutCTA() { export default function AboutCTA() {
return ( return (
<div className="we-container"> <>
<style dangerouslySetInnerHTML={{ __html: `
@media (max-width: 768px) {
.we-container {
margin-top: 40px !important;
margin-bottom: 40px !important;
padding: 0 10px !important;
}
.we-cta {
padding: 48px 20px !important;
border-radius: 22px !important;
}
.we-cta-title {
font-size: clamp(28px, 6vw, 38px) !important;
line-height: 1.25 !important;
margin-bottom: 16px !important;
}
.we-cta-sub {
font-size: 15px !important;
line-height: 1.5 !important;
margin-bottom: 24px !important;
}
}
`}} />
<div className="we-container">
<div className="we-cta reveal"> <div className="we-cta reveal">
<div className="we-cta-inner"> <div className="we-cta-inner">
<h2 className="we-cta-title"> <h2 className="we-cta-title">
@@ -26,5 +50,6 @@ export default function AboutCTA() {
</div> </div>
</div> </div>
</div> </div>
</>
); );
} }

View File

@@ -3,12 +3,13 @@ import React from "react";
export default function AboutHero() { export default function AboutHero() {
return ( return (
<> <>
<link rel="preload" as="image" href="/images/about-bg.webp" />
<style dangerouslySetInnerHTML={{ __html: ` <style dangerouslySetInnerHTML={{ __html: `
.about-us-hero-content { .about-us-hero-content {
width: 100% !important; width: 100% !important;
text-align: center !important; text-align: center !important;
color: #fff !important; color: #fff !important;
padding: 40px !important; padding: 0 15px !important;
z-index: 2; z-index: 2;
display: flex !important; display: flex !important;
flex-direction: column !important; flex-direction: column !important;
@@ -33,7 +34,7 @@ export default function AboutHero() {
<div className="custom-standard-hero-container"> <div className="custom-standard-hero-container">
<div <div
style={{ style={{
backgroundImage: "url('/images/about-bg.png')", backgroundImage: "url('/images/about-bg.webp')",
"--hero-overlay": "linear-gradient(to bottom, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0.92) 60%, rgba(0, 0, 0, 0.98) 100%)" "--hero-overlay": "linear-gradient(to bottom, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0.92) 60%, rgba(0, 0, 0, 0.98) 100%)"
} as React.CSSProperties} } as React.CSSProperties}
className="custom-standard-hero-card" className="custom-standard-hero-card"
@@ -48,4 +49,3 @@ export default function AboutHero() {
</> </>
); );
} }

View File

@@ -26,9 +26,40 @@ export default function BlogGrid() {
} }
} }
@media (max-width: 768px) { @media (max-width: 768px) {
.elementor-104 .elementor-element.elementor-element-c70681e {
margin-top: var(--space-section) !important;
margin-bottom: 16px !important;
}
.custom-blog-grid { .custom-blog-grid {
grid-template-columns: 1fr !important; grid-template-columns: 1fr !important;
gap: 48px !important; gap: 28px !important;
padding: 24px 20px !important;
}
}
@media (max-width: 767px) {
.elementor-104 .elementor-element.elementor-element-c70681e {
--padding-left: 10px !important;
--padding-right: 10px !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
.elementor-104 .elementor-element.elementor-element-3dec5cf {
--padding-left: 0px !important;
--padding-right: 0px !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
.elementor-104 .elementor-element.elementor-element-aa12479,
.elementor-104 .elementor-element.elementor-element-aa12479 > .elementor-widget-container,
.elementor-104 .archive-listing,
.elementor-104 .custom-blog-grid {
width: 100% !important;
max-width: 100% !important;
}
.elementor-104 .custom-blog-grid {
padding-left: 0 !important;
padding-right: 0 !important;
} }
} }
@@ -82,7 +113,7 @@ export default function BlogGrid() {
font-family: var(--font-manrope), sans-serif !important; font-family: var(--font-manrope), sans-serif !important;
} }
/* Bottom block pinned to the card base — keeps Read More + image at the /* Bottom block pinned to the card base. Keeps Read More + image at the
same vertical position across cards with different text lengths. */ same vertical position across cards with different text lengths. */
.custom-blog-bottom { .custom-blog-bottom {
display: flex !important; display: flex !important;

View File

@@ -147,6 +147,12 @@ const STYLES = `
padding: clamp(24px, 3vw, 32px) clamp(20px, 4vw, 40px) clamp(8px, 1.5vw, 16px); padding: clamp(24px, 3vw, 32px) clamp(20px, 4vw, 40px) clamp(8px, 1.5vw, 16px);
display: flex; flex-direction: column; gap: clamp(32px, 4vw, 48px); display: flex; flex-direction: column; gap: clamp(32px, 4vw, 48px);
} }
@media (max-width: 767px) {
.dm-blog-footer-inner {
padding-left: 16px;
padding-right: 16px;
}
}
/* Prev / Next */ /* Prev / Next */
.dm-prevnext { .dm-prevnext {

View File

@@ -1,9 +1,9 @@
import React from "react"; import React from "react";
import Link from "next/link";
export default function BlogsHero() { export default function BlogsHero() {
return ( return (
<> <>
<link rel="preload" as="image" href="/images/home2-banner-1.webp" />
<style dangerouslySetInnerHTML={{ __html: ` <style dangerouslySetInnerHTML={{ __html: `
.blogs-hero-title { .blogs-hero-title {
color: #ffffff !important; color: #ffffff !important;
@@ -15,18 +15,31 @@ export default function BlogsHero() {
letter-spacing: -1.5px !important; letter-spacing: -1.5px !important;
margin: 0 !important; margin: 0 !important;
} }
.blogs-hero-content {
position: relative !important;
width: 100% !important;
height: 100% !important;
display: flex !important;
align-items: center !important;
justify-content: center !important;
flex-direction: column !important;
max-width: 1000px !important;
margin: 0 auto !important;
padding: 0 15px !important;
box-sizing: border-box !important;
}
`}} /> `}} />
<div className="custom-standard-hero-container"> <div className="custom-standard-hero-container">
<div <div
style={{ style={{
backgroundImage: "url(/images/home2-banner-1.jpg)", backgroundImage: "url(/images/home2-banner-1.webp)",
backgroundPosition: "center center", backgroundPosition: "center center",
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
backgroundSize: "cover" backgroundSize: "cover"
}} }}
className="custom-standard-hero-card" className="custom-standard-hero-card"
> >
<div className="e-con-inner" style={{ position: "relative", width: "100%", height: "100%", display: "flex", alignItems: "center", justifyContent: "center", flexDirection: "column" }}> <div className="blogs-hero-content">
{/* Title / Heading for Blogs */} {/* Title / Heading for Blogs */}
<div style={{ textAlign: "center", color: "#fff", zIndex: 5 }}> <div style={{ textAlign: "center", color: "#fff", zIndex: 5 }}>
@@ -43,4 +56,3 @@ export default function BlogsHero() {
</> </>
); );
} }

View File

@@ -492,7 +492,11 @@ export default function CompetitiveEdge() {
@media (max-width: 768px) { @media (max-width: 768px) {
.comparison-section { .comparison-section {
padding: 80px 0; padding: 56px 0;
}
.comparison-section .container {
padding: 0 10px;
} }
.moat-heading { .moat-heading {

View File

@@ -33,6 +33,25 @@ export default function ConnectedLogistics() {
max-width: min(526px, 100%) !important; max-width: min(526px, 100%) !important;
} }
/* Sizing and identical padding on all 4 sides for Connected Logistics Image container */
.elementor-element-99768ba .elementor-widget-container {
display: flex !important;
justify-content: center !important;
align-items: center !important;
padding: 40px !important; /* Identical gap on left, right, top, and bottom */
box-sizing: border-box !important;
}
.elementor-element-99768ba img.wp-image-4481 {
width: 100% !important;
max-width: 100% !important; /* Allow natural responsive scaling on desktop */
height: auto !important;
object-fit: cover !important;
border-radius: 25px !important; /* Preserve 25px border radius */
margin: 0 auto !important;
display: block !important;
}
/* Desktop/Laptop (min-width: 1025px) column width and flex rules */ /* Desktop/Laptop (min-width: 1025px) column width and flex rules */
@media (min-width: 1025px) { @media (min-width: 1025px) {
.elementor-element-9ffed33 { .elementor-element-9ffed33 {
@@ -67,9 +86,20 @@ export default function ConnectedLogistics() {
padding-right: 0 !important; padding-right: 0 !important;
} }
/* Force ScrollReveal wrapper div to span full width */
.elementor-element-96343ba > div {
width: 100% !important;
height: auto !important;
}
/* Show image at the bottom or top depending on order */ /* Show image at the bottom or top depending on order */
.elementor-element-96343ba { .elementor-element-96343ba {
margin-top: 30px !important; margin-top: 0px !important;
}
/* Reduce padding bottom of the content section on tablet */
.elementor-element-71c3e1d {
padding-bottom: 24px !important;
} }
/* Override desktop fixed widths on mobile/tablet */ /* Override desktop fixed widths on mobile/tablet */
@@ -78,6 +108,52 @@ export default function ConnectedLogistics() {
width: 100% !important; width: 100% !important;
max-width: 100% !important; max-width: 100% !important;
} }
/* Allow widget container to take 100% width on mobile/tablet */
.elementor-element-99768ba {
width: 100% !important;
max-width: 100% !important;
}
/* Tablet overrides */
.elementor-element-99768ba .elementor-widget-container {
padding: 10px !important;
display: block !important; /* Disable flex centering layout on mobile/tablet */
width: 100% !important;
}
.elementor-element.elementor-element-99768ba .elementor-widget-container img.wp-image-4481 {
width: 100% !important;
max-width: 600px !important; /* allow up to 600px on tablet */
border-radius: 25px !important; /* Explicitly keep 25px */
}
}
@media (max-width: 767px) {
/* Mobile overrides: let container take 100% width and image fill it */
.elementor-element-99768ba {
width: 100% !important;
max-width: 100% !important;
}
/* Eliminate extra gap under the content column on mobile */
.elementor-element-71c3e1d {
padding-bottom: 12px !important;
}
.elementor-element-99768ba .elementor-widget-container {
padding: 0px !important; /* Eliminate padding to maximize image width/height */
display: block !important; /* Disable flex centering layout on mobile/tablet */
width: 100% !important;
}
.elementor-element.elementor-element-99768ba .elementor-widget-container img.wp-image-4481 {
width: 100% !important;
max-width: 100% !important;
height: auto !important;
object-fit: cover !important;
border-radius: 25px !important; /* Explicitly keep 25px */
}
} }
`}} /> `}} />
<div className="elementor-element elementor-element-9ffed33 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="9ffed33" data-element_type="container" data-e-type="container" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}"> <div className="elementor-element elementor-element-9ffed33 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="9ffed33" data-element_type="container" data-e-type="container" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}">
@@ -92,7 +168,7 @@ export default function ConnectedLogistics() {
priority priority
width={578} width={578}
height={790} height={790}
src="/images/home2-pic-3.png" src="/images/home2-pic-3.webp"
className="attachment-full size-full wp-image-4481" className="attachment-full size-full wp-image-4481"
alt="Connected Logistics" alt="Connected Logistics"
style={{ style={{

View File

@@ -41,7 +41,7 @@ export default function ContactMap() {
(bottom was square before, leaving a hard edge above the footer gap). */ (bottom was square before, leaving a hard edge above the footer gap). */
border-radius: 25px; border-radius: 25px;
overflow: hidden; overflow: hidden;
background: #0b0b0b; background: transparent;
line-height: 0; line-height: 0;
} }
@media (max-width: 840px) { @media (max-width: 840px) {

View File

@@ -1,13 +1,13 @@
import React from "react"; import React from "react";
import Link from "next/link";
export default function ContactsHero() { export default function ContactsHero() {
return ( return (
<> <>
<link rel="preload" as="image" href="/images/home2-banner-3.webp" />
<style dangerouslySetInnerHTML={{ __html: ` <style dangerouslySetInnerHTML={{ __html: `
.contacts-hero-custom { .contacts-hero-custom {
background-color: #0b0b0b !important; background-color: #0b0b0b !important;
background-image: url('/images/home2-banner-3.jpg') !important; background-image: url('/images/home2-banner-3.webp') !important;
background-size: cover !important; background-size: cover !important;
background-position: center !important; background-position: center !important;
} }
@@ -82,9 +82,9 @@ export default function ContactsHero() {
-webkit-backdrop-filter: none !important; -webkit-backdrop-filter: none !important;
border: none !important; border: none !important;
border-radius: 0 !important; border-radius: 0 !important;
padding: 0 16px !important; padding: 0 24px !important;
max-width: 820px !important; max-width: 1500px !important;
width: 90% !important; width: 92% !important;
box-shadow: none !important; box-shadow: none !important;
text-align: center !important; text-align: center !important;
} }
@@ -95,60 +95,53 @@ export default function ContactsHero() {
border-color: transparent !important; border-color: transparent !important;
} }
/* Spaced kicker */ /* Hero headline — large, light, reference-matched display type.
.contacts-hero-kicker { Size scales with the viewport so the line-to-container width ratio
display: inline-flex !important; stays constant; the cap keeps the longest line inside the 1500px
align-items: center !important; container (with nowrap on desktop) so it can never overflow/clip. */
gap: 12px !important;
margin-bottom: 24px !important;
}
.contacts-hero-kicker-line {
display: block !important;
width: 24px !important;
height: 1.5px !important;
background: #C01227 !important;
border-radius: 1px !important;
}
.contacts-hero-kicker-text {
font-size: 13px !important;
font-weight: 850 !important;
letter-spacing: 4px !important;
color: #C01227 !important;
text-transform: uppercase !important;
font-family: var(--font-manrope), "Manrope", sans-serif !important;
}
/* Bold modern typography */
.contacts-hero-title { .contacts-hero-title {
font-size: clamp(34px, 5.2vw, 62px) !important; font-size: clamp(34px, 5.9vw, 98px) !important;
font-weight: 850 !important; font-weight: 400 !important;
line-height: 1.15 !important; line-height: 0.95 !important;
color: #ffffff !important; color: #ffffff !important;
text-transform: uppercase !important; text-transform: uppercase !important;
letter-spacing: -1.8px !important; letter-spacing: -0.02em !important;
margin: 0 0 20px 0 !important; margin: 0 0 28px 0 !important;
font-family: var(--font-manrope), "Manrope", sans-serif !important; font-family: var(--font-manrope), "Manrope", sans-serif !important;
} }
.contacts-hero-title-line {
display: block !important;
}
/* Keep each line intact on desktop — never split SYSTEM or PROMISE/KEPT */
@media (min-width: 1024px) {
.contacts-hero-title-line {
white-space: nowrap !important;
}
}
.contacts-hero-title-highlight { .contacts-hero-title-highlight {
background: linear-gradient(135deg, #ffffff 40%, #c01227 100%) !important; color: #c01227 !important;
-webkit-background-clip: text !important;
-webkit-text-fill-color: transparent !important;
} }
/* Description text */ /* Description text */
.contacts-hero-desc { .contacts-hero-desc {
font-size: clamp(15px, 1.22vw, 18px) !important; font-size: clamp(15px, 1.3vw, 19px) !important;
line-height: 1.6 !important; line-height: 1.6 !important;
color: rgba(255, 255, 255, 0.75) !important; color: rgba(255, 255, 255, 0.82) !important;
max-width: 600px !important; max-width: 640px !important;
margin: 0 auto 36px auto !important; margin: 0 auto 24px auto !important;
font-weight: 500 !important; font-weight: 500 !important;
font-family: var(--font-manrope), "Manrope", sans-serif !important; font-family: var(--font-manrope), "Manrope", sans-serif !important;
} }
.contacts-hero-desc-trademark {
color: #ffffff !important;
font-weight: 700 !important;
white-space: nowrap !important;
}
/* Breadcrumb capsule */ /* Breadcrumb capsule */
.contacts-hero-breadcrumbs { .contacts-hero-breadcrumbs {
display: inline-flex !important; display: inline-flex !important;
@@ -201,6 +194,9 @@ export default function ContactsHero() {
padding: 0 16px !important; padding: 0 16px !important;
width: 95% !important; width: 95% !important;
} }
.contacts-hero-title {
letter-spacing: -1px !important;
}
} }
`}} /> `}} />
<div className="custom-standard-hero-container"> <div className="custom-standard-hero-container">
@@ -212,20 +208,12 @@ export default function ContactsHero() {
<div className="contacts-hero-glow-blue"></div> <div className="contacts-hero-glow-blue"></div>
<div className="contacts-hero-glass-card"> <div className="contacts-hero-glass-card">
<div className="contacts-hero-kicker">
<span className="contacts-hero-kicker-line"></span>
<span className="contacts-hero-kicker-text">24/7 support & sales</span>
<span className="contacts-hero-kicker-line"></span>
</div>
<h1 className="contacts-hero-title"> <h1 className="contacts-hero-title">
Get In <span className="contacts-hero-title-highlight">Touch</span> <span className="contacts-hero-title-line">Delivering Trust.</span>
<span className="contacts-hero-title-line">
Beyond <span className="contacts-hero-title-highlight">Boundaries.</span>
</span>
</h1> </h1>
<p className="contacts-hero-desc">
Have questions about our smart delivery network, pricing plans, or partner ecosystem? Let&apos;s build the future of logistics together.
</p>
</div> </div>
</div> </div>
</div> </div>
@@ -234,4 +222,3 @@ export default function ContactsHero() {
); );
} }

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useState, useEffect, useRef } from "react"; import React, { useEffect, useRef } from "react";
import Image from "next/image"; import Image from "next/image";
import gsap from "gsap"; import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger"; import { ScrollTrigger } from "gsap/ScrollTrigger";
@@ -9,43 +9,61 @@ if (typeof window !== "undefined") {
gsap.registerPlugin(ScrollTrigger); gsap.registerPlugin(ScrollTrigger);
} }
const ACCORDION_DATA = [ const CARDS_DATA = [
{ {
index: 1, index: 1,
num: "01", title: "Operational Visibility",
title: "Battery-First Planning", desc: "Real-time tracking and centralized control provide complete visibility across every shipment, vehicle, and delivery milestone.",
desc: "Routes are optimized around battery levels and charging windows, not retrofitted as an afterthought." icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<rect x="2" y="7" width="16" height="10" rx="2" ry="2" />
<line x1="22" y1="11" x2="22" y2="13" />
<line x1="6" y1="11" x2="10" y2="11" />
<line x1="6" y1="13" x2="12" y2="13" />
</svg>
)
}, },
{ {
index: 2, index: 2,
num: "02", title: "Intelligent Routing",
title: "Energy-Aware Routing", desc: "AI-powered route optimization reduces travel time, improves delivery accuracy, and maximizes fleet utilization.",
desc: "Our algorithms factor in terrain, traffic, and payload weight to maximize range efficiency." icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
</svg>
)
}, },
{ {
index: 3, index: 3,
num: "03", title: "EV-First Logistics",
title: "Smart Charging Integration", desc: "Purpose-built workflows for electric fleets improve battery efficiency, charging management, and sustainable operations.",
desc: "Seamless coordination with charging infrastructure to eliminate range anxiety for drivers." icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M12 2v10" />
<path d="M18 8V6a2 2 0 0 0-2-2H8a2 2 0 0 0-2 2v2a4 4 0 0 0 4 4h4a4 4 0 0 0 4-4Z" />
<path d="M10 22v-6" />
<path d="M14 22v-6" />
</svg>
)
}, },
{ {
index: 4, index: 4,
num: "04", title: "Scalable Network",
title: "Carbon Footprint Tracking", desc: "Flexible logistics infrastructure supports growth across cities, regions, and high-volume delivery operations without disruption.",
desc: "Real-time emissions monitoring and sustainability reports for every delivery." icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
<path d="M11 20A7 7 0 0 1 9.8 6.1C15.5 5 17 4.48 19 2c1 2 2 3.5 2 5.5a7 7 0 0 1-7 7h-3" />
<path d="M12 22V12" />
</svg>
)
} }
]; ];
export default function EVLogisticSection() { export default function EVLogisticSection() {
const [openIndex, setOpenIndex] = useState<number | null>(4); // Default to item 4 open to match user's screenshot layout
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const imageWrapperRef = useRef<HTMLDivElement>(null); const imageWrapperRef = useRef<HTMLDivElement>(null);
const imageRef = useRef<HTMLImageElement>(null); const imageRef = useRef<HTMLImageElement>(null);
const toggleAccordion = (index: number) => {
setOpenIndex((prev) => (prev === index ? null : index));
};
useEffect(() => { useEffect(() => {
const container = containerRef.current; const container = containerRef.current;
const img = imageRef.current; const img = imageRef.current;
@@ -71,21 +89,19 @@ export default function EVLogisticSection() {
}); });
entryTl entryTl
.to(container.querySelector(".ev-logistic-kicker"), { .to(container.querySelector(".ev-logistic-kicker-widget"), {
opacity: 1, opacity: 1,
y: 0, y: 0,
letterSpacing: "3px", // Kinetic letter-spacing track expand effect!
duration: 0.8, duration: 0.8,
ease: "power3.out", ease: "power3.out",
}) })
.to(container.querySelectorAll(".ev-char"), { .to(container.querySelector(".ev-logistic-title-widget"), {
y: "0%", y: 0,
opacity: 1, opacity: 1,
duration: 0.95, duration: 0.85,
stagger: 0.02, // Rapid letter-by-letter wave reveal!
ease: "power4.out", ease: "power4.out",
}, "-=0.45") }, "-=0.45")
.to(container.querySelectorAll(".ev-logistic-accordion-item"), { .to(container.querySelectorAll(".ev-feature-card"), {
opacity: 1, opacity: 1,
y: 0, y: 0,
duration: 0.6, duration: 0.6,
@@ -107,20 +123,11 @@ export default function EVLogisticSection() {
}; };
}, []); }, []);
const headingText = "LOGISTICS BUILT FOR ELECTRIC VEHICLES";
const headingWords = headingText.split(" ");
return ( return (
<> <>
<style dangerouslySetInnerHTML={{ __html: ` <style dangerouslySetInnerHTML={{ __html: `
/* Custom CSS Scoped to EV Logistics Section - New Premium Look */ /* Custom CSS Scoped to EV Logistics Section */
.ev-logistic-section { .ev-logistic-section {
display: flex !important;
flex-direction: column !important;
width: 100% !important;
max-width: 1320px !important;
margin: 10px auto 120px auto !important; /* Centered horizontally with auto margins */
padding: 80px 60px !important; /* Restored original balanced left/right paddings */
box-sizing: border-box !important; box-sizing: border-box !important;
background: #ffffff !important; background: #ffffff !important;
font-family: 'Manrope', sans-serif !important; font-family: 'Manrope', sans-serif !important;
@@ -131,8 +138,6 @@ export default function EVLogisticSection() {
width: 100% !important; width: 100% !important;
max-width: 100% !important; max-width: 100% !important;
align-self: stretch !important; align-self: stretch !important;
border-bottom: 2px solid rgba(17, 17, 17, 0.09) !important;
padding-bottom: 16px !important;
margin-bottom: 48px !important; margin-bottom: 48px !important;
display: block !important; display: block !important;
text-align: left !important; text-align: left !important;
@@ -144,13 +149,13 @@ export default function EVLogisticSection() {
align-items: center !important; align-items: center !important;
justify-content: space-between !important; justify-content: space-between !important;
width: 100% !important; width: 100% !important;
gap: 40px !important; gap: 3% !important;
} }
/* Balanced Left Column - Image column takes up 58% */ /* Left Column - Image column takes up 45% */
.ev-logistic-image-col { .ev-logistic-image-col {
flex: 1 1 58% !important; flex: 0 0 45% !important;
max-width: 58% !important; max-width: 45% !important;
display: flex !important; display: flex !important;
align-items: center !important; align-items: center !important;
justify-content: flex-start !important; justify-content: flex-start !important;
@@ -182,235 +187,139 @@ export default function EVLogisticSection() {
justify-content: flex-start !important; justify-content: flex-start !important;
overflow: visible !important; overflow: visible !important;
position: relative !important; position: relative !important;
transform: scale(1.15) !important; /* Scale up image to make it larger and more dominant */ transform: scale(1.15) !important;
transform-origin: left center !important; transform-origin: left center !important;
margin-left: -80px !important; /* Offset image to the left to anchor it to the container edge */ margin-left: -80px !important;
} }
.ev-logistic-image-wrapper img { .ev-logistic-image-wrapper img {
width: 100% !important; width: 100% !important;
height: auto !important; height: auto !important;
object-fit: contain !important; object-fit: contain !important;
filter: none !important; /* Blends solid white JPEG edges seamlessly into pure white background */ filter: none !important;
will-change: transform !important; will-change: transform !important;
} }
/* Balanced right column - takes up 42% for crisp textual reading */ /* Right column - takes up 52% for grid cards */
.ev-logistic-content-col { .ev-logistic-content-col {
flex: 1 1 42% !important; flex: 0 0 52% !important;
max-width: 42% !important; max-width: 52% !important;
display: flex !important; display: flex !important;
flex-direction: column !important; flex-direction: column !important;
justify-content: center !important; justify-content: center !important;
margin: 0 !important; margin: 0 !important;
} }
.ev-logistic-kicker { .ev-logistic-kicker-widget {
font-size: 14px !important;
font-weight: 400 !important;
line-height: 2.1429em !important;
letter-spacing: 0px !important; /* Expands to 3px on scroll */
text-transform: lowercase !important;
color: #111111 !important;
margin: 0 !important;
opacity: 0; opacity: 0;
transform: translateY(-12px); transform: translateY(-12px);
will-change: transform, opacity, letter-spacing; will-change: transform, opacity;
text-align: left !important;
display: inline-block !important;
} }
.ev-logistic-title-wrapper { .ev-logistic-title-wrapper {
margin-bottom: 48px; margin: 0 !important;
} }
/* Expanded clean headings look from the screenshot */ .ev-logistic-title-widget {
.ev-logistic-title {
font-size: clamp(45px, 6.2vw, 96px);
font-weight: 500;
line-height: 0.95;
text-transform: uppercase;
color: #111111;
margin: 0 10px 0 0; /* Clean margin-right to shift title away from borders */
letter-spacing: -1.8px;
}
/* CSS for robust letter-by-letter animation wrapping */
.ev-word-inline {
display: inline-block;
white-space: nowrap; /* Prevents awkward character line breaks */
}
.ev-char-wrapper {
display: inline-block;
overflow: hidden;
vertical-align: top;
}
.ev-char {
display: inline-block;
transform: translateY(110%);
opacity: 0; opacity: 0;
transform: translateY(24px);
will-change: transform, opacity; will-change: transform, opacity;
} }
.ev-char-space { .ev-logistic-title-widget > .elementor-widget-container {
display: inline-block; margin: 0 !important;
padding: 0 !important;
} }
.ev-logistic-accordion { .ev-logistic-title-widget .logico-title {
display: flex; margin: 0 !important;
flex-direction: column; text-align: left !important;
width: 100%;
} }
/* Sleek horizontal grid borders */ /* Card Grid Layout */
.ev-logistic-accordion-item { .ev-feature-grid {
width: 100%; display: grid !important;
border-top: 1px solid #e5e7eb; grid-template-columns: repeat(2, 1fr) !important;
gap: 24px !important;
width: 100% !important;
}
/* Premium Theme White Card */
.ev-feature-card {
display: flex !important;
flex-direction: column !important;
background: #ffffff !important;
border: 1px solid rgba(17, 17, 17, 0.09) !important;
border-radius: 20px !important;
padding: 30px !important;
color: #555555 !important;
height: 100% !important;
box-sizing: border-box !important;
opacity: 0; opacity: 0;
transform: translateY(20px); transform: translateY(20px);
will-change: transform, opacity; will-change: transform, opacity;
transition: background-color 0.4s cubic-bezier(0.25, 1, 0.5, 1); 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) !important;
box-shadow: 0 4px 24px rgba(17, 17, 17, 0.02) !important;
} }
.ev-logistic-accordion-item:last-child { .ev-feature-card:hover {
border-bottom: 1px solid #e5e7eb; border-color: #c01227 !important;
box-shadow: 0 20px 40px rgba(192, 18, 39, 0.08), 0 4px 12px rgba(17, 17, 17, 0.03) !important;
transform: translateY(-8px) !important;
} }
/* Soft highlight on row hover */ /* Red Icon Wrapper */
.ev-logistic-accordion-item:hover { .ev-feature-icon-wrapper {
background: rgba(192, 18, 39, 0.015); margin-bottom: 24px !important;
display: inline-flex !important;
align-items: center !important;
justify-content: center !important;
width: 54px !important;
height: 54px !important;
background: rgba(192, 18, 39, 0.06) !important;
border: 1px solid rgba(192, 18, 39, 0.18) !important;
border-radius: 50% !important;
color: #c01227 !important;
transition: all 0.3s ease !important;
} }
/* Spacious row padding for luxurious design - INCREASED font size for headers */ .ev-feature-card:hover .ev-feature-icon-wrapper {
.ev-logistic-accordion-header { background: rgba(192, 18, 39, 0.12) !important;
width: 100%; border-color: #c01227 !important;
display: flex; transform: scale(1.1) !important;
justify-content: space-between; box-shadow: 0 0 12px rgba(192, 18, 39, 0.15) !important;
align-items: center;
padding: 28px 16px;
cursor: pointer;
background: transparent;
border: none;
text-align: left;
outline: none;
font-family: 'Manrope', sans-serif;
font-size: clamp(24px, 2.2vw, 30px); /* Increased to clamp up to 30px! */
font-weight: 700;
color: #111111;
transition: color 0.3s ease;
} }
.ev-logistic-accordion-header span:first-child { .ev-feature-icon-wrapper svg {
transition: transform 0.35s cubic-bezier(0.25, 1, 0.5, 1); width: 24px !important;
display: inline-block; height: 24px !important;
stroke: currentColor !important;
} }
/* Row text horizontal slide nudge */ /* Title & Description Inside Cards */
.ev-logistic-accordion-header:hover { .ev-feature-card-title {
color: #c01227; font-family: 'Manrope', sans-serif !important;
font-size: 20px !important;
font-weight: 700 !important;
line-height: 1.3em !important;
color: #c01227 !important;
margin: 0 0 12px 0 !important;
text-transform: none !important;
} }
.ev-logistic-accordion-header:hover span:first-child { .ev-feature-card-desc {
transform: translateX(10px); font-family: 'Manrope', sans-serif !important;
} font-size: 14px !important;
font-weight: 500 !important;
.ev-logistic-accordion-item.active .ev-logistic-accordion-header { line-height: 1.6 !important;
color: #111111; color: #555555 !important;
} margin: 0 !important;
.ev-logistic-accordion-arrow-container {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
transition: transform 0.4s cubic-bezier(0.2, 0.8, 0.2, 1), color 0.3s ease;
}
.ev-logistic-accordion-arrow-container svg {
width: 20px;
height: 20px;
stroke-width: 2.5;
}
/* Inactive arrows: point down-right ↘ */
.ev-logistic-accordion-item:not(.active) .ev-logistic-accordion-arrow-container {
transform: rotate(90deg);
color: #111111;
}
/* Active arrows: point up-right ↗ in brand red */
.ev-logistic-accordion-item.active .ev-logistic-accordion-arrow-container {
transform: rotate(0deg);
color: #c01227;
}
/* Hover: rotate smooth to diagonal up-right */
.ev-logistic-accordion-header:hover .ev-logistic-accordion-arrow-container {
transform: rotate(0deg);
color: #c01227;
}
.ev-logistic-accordion-content {
overflow: hidden;
max-height: 0;
opacity: 0;
transition: max-height 0.45s cubic-bezier(0.25, 1, 0.5, 1), opacity 0.45s ease;
}
.ev-logistic-accordion-item.active .ev-logistic-accordion-content {
max-height: 160px;
opacity: 1;
}
/* Balanced text spacing inside descriptions - INCREASED font size for descriptions */
.ev-logistic-accordion-content-inner {
padding: 0 16px 28px 16px;
font-size: clamp(18px, 1.5vw, 20px); /* Increased to clamp up to 20px! */
line-height: 1.6;
color: #555555;
font-weight: 500;
}
/* Underline track & sweeping active red bar */
.ev-logistic-accordion-progress-track {
position: relative;
width: 100%;
height: 1.5px;
background: transparent;
margin-top: -1.5px;
overflow: hidden;
}
.ev-logistic-accordion-progress-bar {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 100%;
background: #c01227;
transform: scaleX(0);
transform-origin: left center;
transition: transform 0.6s cubic-bezier(0.25, 1, 0.5, 1);
will-change: transform;
}
.ev-logistic-accordion-item.active .ev-logistic-accordion-progress-bar {
transform: scaleX(1);
} }
/* Responsiveness constraints */ /* Responsiveness constraints */
@media (max-width: 1024px) { @media (max-width: 1024px) {
.ev-logistic-section {
padding: 60px 24px;
margin-bottom: 60px;
}
/* Base rules above use !important (flex-direction:row, the 58/42%
column widths), so these overrides MUST also use !important or the
grid never stacks and the image/text stay squished side-by-side. */
.ev-logistic-body-grid { .ev-logistic-body-grid {
flex-direction: column !important; flex-direction: column !important;
gap: 50px !important; gap: 50px !important;
@@ -420,13 +329,13 @@ export default function EVLogisticSection() {
flex: 1 1 100% !important; flex: 1 1 100% !important;
max-width: 100% !important; max-width: 100% !important;
min-height: auto !important; min-height: auto !important;
justify-content: center !important; /* Center layout on mobile */ justify-content: center !important;
} }
.ev-logistic-image-wrapper { .ev-logistic-image-wrapper {
max-width: 580px !important; max-width: 580px !important;
transform: none !important; /* Reset scale transform on mobile/tablet */ transform: none !important;
margin-left: 0 !important; /* Reset left margin offset on mobile/tablet */ margin-left: 0 !important;
justify-content: center !important; justify-content: center !important;
} }
@@ -435,123 +344,133 @@ export default function EVLogisticSection() {
max-width: 100% !important; max-width: 100% !important;
} }
.ev-logistic-title {
font-size: 38px;
}
} }
@media (max-width: 768px) { @media (max-width: 767px) {
.ev-logistic-section { .ev-logistic-section {
padding: 40px 16px; --padding-left: 10px !important;
--padding-right: 10px !important;
padding-left: 10px !important;
padding-right: 10px !important;
box-sizing: border-box !important;
} }
.ev-logistic-title { .ev-logistic-section > .e-con-inner {
font-size: 32px; width: 100% !important;
max-width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: auto !important;
margin-right: auto !important;
box-sizing: border-box !important;
} }
.ev-logistic-accordion-header { .ev-logistic-section .elementor-element-343b363 {
font-size: 19px; --padding-left: 0px !important;
padding: 22px 8px; --padding-right: 0px !important;
padding-left: 0 !important;
padding-right: 0 !important;
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box !important;
} }
.ev-logistic-accordion-content-inner { .ev-logistic-body-grid {
padding: 0; width: 100% !important;
font-size: 15.5px; max-width: 100% !important;
align-items: stretch !important;
}
.ev-logistic-content-col {
width: 100% !important;
max-width: 100% !important;
align-self: center !important;
}
.ev-feature-grid {
grid-template-columns: 1fr !important;
gap: 20px !important;
width: 100% !important;
max-width: 100% !important;
}
.ev-feature-card {
width: 100% !important;
max-width: 100% !important;
} }
} }
`}} /> `}} />
<div <div className="elementor-61">
ref={containerRef} <div
className="ev-logistic-section" ref={containerRef}
> className="elementor-element elementor-element-88745f4 e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent ev-logistic-section"
{/* Top Header Row with / features / kicker */} data-id="88745f4"
<div className="ev-logistic-header"> data-element_type="container"
<div className="ev-logistic-kicker">/ Build Electric Vehicles /</div> data-e-type="container"
</div> >
<div className="e-con-inner">
<div className="ev-logistic-body-grid"> <div className="elementor-element elementor-element-343b363 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="343b363" data-element_type="container" data-e-type="container">
{/* Left Column: Enlarged Floating Wrapper & Ambient glow */} {/* Same header hierarchy and container structure as "The Doormile Way". */}
<div className="ev-logistic-image-col"> <div className="ev-logistic-header">
<div className="ev-logistic-image-glow"></div> <div className="elementor-element elementor-element-7afb238 elementor-widget elementor-widget-logico_heading ev-logistic-kicker-widget" data-id="7afb238" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
<div className="elementor-widget-container">
<div ref={imageWrapperRef} className="ev-logistic-image-wrapper"> <div className="logico-title ev-logistic-kicker">/ Built for Electric Vehicles /</div>
<Image
ref={imageRef}
src="/images/ev.jpeg"
alt="EV Logistics"
width={1050}
height={854}
priority
/>
</div>
</div>
{/* Right Column: Refined Accordion list with letter-by-letter animation */}
<div className="ev-logistic-content-col">
{/* Character-by-character masked entrance wave reveal on scroll */}
<div className="ev-logistic-title-wrapper">
<h3 className="ev-logistic-title">
{headingWords.map((word, wordIndex) => (
<span key={wordIndex} className="ev-word-inline">
{word.split("").map((letter, letterIndex) => (
<span key={letterIndex} className="ev-char-wrapper">
<span className="ev-char">{letter}</span>
</span>
))}
<span className="ev-char-space">&nbsp;</span>
</span>
))}
</h3>
</div>
<div className="ev-logistic-accordion">
{ACCORDION_DATA.map((item) => (
<div
key={item.index}
className={`ev-logistic-accordion-item ${openIndex === item.index ? "active" : ""}`}
>
<button
className="ev-logistic-accordion-header"
onClick={() => toggleAccordion(item.index)}
aria-expanded={openIndex === item.index}
>
<span>{item.num}. {item.title}</span>
<span className="ev-logistic-accordion-arrow-container">
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
>
<line x1="7" y1="17" x2="17" y2="7"></line>
<polyline points="7 7 17 7 17 17"></polyline>
</svg>
</span>
</button>
<div className="ev-logistic-accordion-content">
<div className="ev-logistic-accordion-content-inner">
{/* Kinetic slide-up and fade subtitle reveal */}
<p style={{
transform: openIndex === item.index ? "translateY(0)" : "translateY(12px)",
opacity: openIndex === item.index ? 1 : 0,
transition: "transform 0.5s cubic-bezier(0.25, 1, 0.5, 1), opacity 0.5s ease",
transitionDelay: "0.08s",
margin: 0
}}>
{item.desc}
</p>
</div>
</div>
{/* Underline track & sweep animated red bar */}
<div className="ev-logistic-accordion-progress-track">
<div className="ev-logistic-accordion-progress-bar"></div>
</div> </div>
</div> </div>
))} <div className="ev-logistic-title-wrapper">
<div className="elementor-element elementor-element-1cc335a elementor-widget elementor-widget-logico_heading ev-logistic-title-widget" data-id="1cc335a" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
<div className="elementor-widget-container">
<div
className="logico-title"
style={{
WebkitTextStroke: "4px #c01227",
color: "#fff",
fontWeight: 800,
}}
>
Logistics Built for EV Vehicles
</div>
</div>
</div>
</div>
</div>
<div className="ev-logistic-body-grid">
{/* Left Column: Enlarged Floating Wrapper & Ambient glow */}
<div className="ev-logistic-image-col">
<div className="ev-logistic-image-glow"></div>
<div ref={imageWrapperRef} className="ev-logistic-image-wrapper">
<Image
ref={imageRef}
src="/images/ev.webp"
alt="EV Logistics"
width={1050}
height={854}
priority
/>
</div>
</div>
{/* Right Column: 2x2 Grid of Feature Cards */}
<div className="ev-logistic-content-col">
<div className="ev-feature-grid">
{CARDS_DATA.map((item) => (
<div
key={item.index}
className="ev-feature-card"
>
<div className="ev-feature-icon-wrapper">
{item.icon}
</div>
<h4 className="ev-feature-card-title">{item.title}</h4>
<p className="ev-feature-card-desc">{item.desc}</p>
</div>
))}
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </div>

File diff suppressed because it is too large Load Diff

View File

@@ -19,17 +19,20 @@ export default function HowItWorksHero() {
return ( return (
<> <>
<style dangerouslySetInnerHTML={{ __html: ` <link rel="preload" as="image" href="/images/home2-banner-1.webp" />
<style
dangerouslySetInnerHTML={{
__html: `
.howits-hero-custom-bg.elementor-repeater-item-3264830, .howits-hero-custom-bg.elementor-repeater-item-3264830,
.howits-hero-custom-bg.elementor-repeater-item-6867061 { .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; background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.1)), url('/images/home2-banner-1.webp') !important;
background-position: center !important; background-position: center !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
background-size: cover !important; background-size: cover !important;
} }
.howits-hero-custom-bg.elementor-repeater-item-6867061 { .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-2.png') !important; background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.1)), url('/images/home1-slide-2.webp') !important;
background-position: center !important; background-position: center !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
background-size: cover !important; background-size: cover !important;
@@ -44,7 +47,7 @@ export default function HowItWorksHero() {
text-align: center !important; text-align: center !important;
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
padding: 0 40px !important; padding: 0 !important;
box-sizing: border-box !important; box-sizing: border-box !important;
} }
@@ -70,6 +73,8 @@ export default function HowItWorksHero() {
width: 100% !important; width: 100% !important;
margin-left: auto !important; margin-left: auto !important;
margin-right: auto !important; margin-right: auto !important;
padding-left: 15px !important;
padding-right: 15px !important;
} }
.elementor-element.elementor-element-6c7cbcb .content-slider-item-text { .elementor-element.elementor-element-6c7cbcb .content-slider-item-text {
@@ -83,6 +88,8 @@ export default function HowItWorksHero() {
text-align: center !important; text-align: center !important;
max-width: 820px !important; max-width: 820px !important;
margin: 0 auto !important; margin: 0 auto !important;
padding-left: 15px !important;
padding-right: 15px !important;
} }
/* Larger, more readable hero subtitle on large/4K screens */ /* Larger, more readable hero subtitle on large/4K screens */
@@ -106,10 +113,13 @@ export default function HowItWorksHero() {
@media (max-width: 1024px) { @media (max-width: 1024px) {
.elementor-element.elementor-element-741f56c { .elementor-element.elementor-element-741f56c {
width: calc(100% - 40px) !important; width: 100% !important;
max-width: none !important; max-width: 100% !important;
margin-left: auto !important; margin-left: auto !important;
margin-right: auto !important; margin-right: auto !important;
--padding-top: 0px !important;
--padding-block-start: 0px !important;
padding: 0 10px 10px 10px !important;
overflow: hidden !important; overflow: hidden !important;
border-radius: 25px !important; border-radius: 25px !important;
} }
@@ -131,15 +141,19 @@ export default function HowItWorksHero() {
.elementor-element.elementor-element-6c7cbcb .slide-content { .elementor-element.elementor-element-6c7cbcb .slide-content {
width: 100% !important; width: 100% !important;
min-height: 620px !important; min-height: 620px !important;
padding-left: 48px !important; padding-left: 0 !important;
padding-right: 48px !important; padding-right: 0 !important;
box-sizing: border-box !important; box-sizing: border-box !important;
} }
} }
@media (max-width: 767px) { @media (max-width: 767px) {
.elementor-element.elementor-element-741f56c { .elementor-element.elementor-element-741f56c {
width: calc(100% - 24px) !important; width: 100% !important;
max-width: 100% !important;
--padding-top: 0px !important;
--padding-block-start: 0px !important;
padding: 0 10px 10px 10px !important;
border-radius: 22px !important; border-radius: 22px !important;
} }
@@ -159,8 +173,8 @@ export default function HowItWorksHero() {
asymmetric top margin). */ asymmetric top margin). */
.elementor .elementor-element.elementor-element-6c7cbcb .slide-content { .elementor .elementor-element.elementor-element-6c7cbcb .slide-content {
min-height: 560px !important; min-height: 560px !important;
padding-left: 22px !important; padding-left: 0 !important;
padding-right: 22px !important; padding-right: 0 !important;
align-items: center !important; align-items: center !important;
justify-content: center !important; justify-content: center !important;
text-align: center !important; text-align: center !important;
@@ -219,19 +233,44 @@ export default function HowItWorksHero() {
display: none !important; 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
<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"> 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="elementor-widget-container">
<div className="logico-content-slider-widget"> <div className="logico-content-slider-widget">
<div className="content-slider-wrapper"> <div className="content-slider-wrapper">
<div className="content-slider-container"> <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="content-slider owl-carousel owl-theme nav-view-vertical nav-h-position-right nav-v-position-bottom owl-loaded owl-drag">
<div
<div className="owl-stage-outer" style={{ position: "relative", overflow: "hidden", height: "800px" }}> className="owl-stage-outer"
<div className="owl-stage" style={{ position: "relative", width: "100%", height: "100%" }}> style={{
position: "relative",
overflow: "hidden",
height: "800px",
}}
>
<div
className="owl-stage"
style={{
position: "relative",
width: "100%",
height: "100%",
}}
>
{/* Slide 1 */} {/* Slide 1 */}
<div <div
className={`owl-item ${activeSlide === 0 ? "active" : ""}`} className={`owl-item ${activeSlide === 0 ? "active" : ""}`}
@@ -239,20 +278,30 @@ export default function HowItWorksHero() {
position: "relative", position: "relative",
width: "100%", width: "100%",
opacity: activeSlide === 0 ? 1 : 0, opacity: activeSlide === 0 ? 1 : 0,
visibility: activeSlide === 0 ? "visible" : "hidden", visibility:
transition: "opacity 0.8s ease-in-out, visibility 0.8s ease-in-out", activeSlide === 0 ? "visible" : "hidden",
zIndex: activeSlide === 0 ? 2 : 1 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="content-item slider-item elementor-repeater-item-3264830 slide-style-standard howits-hero-custom-bg">
<div className="slide-content"> <div className="slide-content">
<div className="slide-content-inner"> <div className="slide-content-inner">
<h1 className="content-slider-item-heading logico-content-wrapper-1"> <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> </h1>
<div className="content-slider-item-text logico-content-wrapper-2"> <div className="content-slider-item-text logico-content-wrapper-2">
<div className="text-content"> <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>
@@ -269,27 +318,36 @@ export default function HowItWorksHero() {
left: 0, left: 0,
width: "100%", width: "100%",
opacity: activeSlide === 1 ? 1 : 0, opacity: activeSlide === 1 ? 1 : 0,
visibility: activeSlide === 1 ? "visible" : "hidden", visibility:
transition: "opacity 0.8s ease-in-out, visibility 0.8s ease-in-out", activeSlide === 1 ? "visible" : "hidden",
zIndex: activeSlide === 1 ? 2 : 1 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="content-item slider-item elementor-repeater-item-6867061 slide-style-standard howits-hero-custom-bg">
<div className="slide-content"> <div className="slide-content">
<div className="slide-content-inner"> <div className="slide-content-inner">
<h1 className="content-slider-item-heading logico-content-wrapper-1"> <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> </h1>
<div className="content-slider-item-text logico-content-wrapper-2"> <div className="content-slider-item-text logico-content-wrapper-2">
<div className="text-content"> <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>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
@@ -298,42 +356,98 @@ export default function HowItWorksHero() {
<button <button
type="button" type="button"
className="owl-next" className="owl-next"
onClick={() => setActiveSlide((prev) => (prev === 0 ? 1 : 0))} onClick={() =>
setActiveSlide((prev) => (prev === 0 ? 1 : 0))
}
aria-label="Next" aria-label="Next"
style={{ cursor: "pointer", border: "none", outline: "none" }} style={{
cursor: "pointer",
border: "none",
outline: "none",
}}
/> />
<button <button
type="button" type="button"
className="owl-prev" className="owl-prev"
onClick={() => setActiveSlide((prev) => (prev === 0 ? 1 : 0))} onClick={() =>
setActiveSlide((prev) => (prev === 0 ? 1 : 0))
}
aria-label="Previous" aria-label="Previous"
style={{ cursor: "pointer", border: "none", outline: "none" }} style={{
cursor: "pointer",
border: "none",
outline: "none",
}}
/> />
</div> </div>
{/* Progress indicators */} {/* Progress indicators */}
<div className="slider-footer slider-footer-position-after slider-footer-width-full slider-footer-view-inside"> <div className="slider-footer slider-footer-position-after slider-footer-width-full slider-footer-view-inside">
<div className="slider-footer-content"> <div className="slider-footer-content">
<div className="slider-pagination" style={{ display: "flex", justifyContent: "flex-end", alignItems: "center", gap: "10px" }}> <div
<div className="slider-progress-wrapper" style={{ marginRight: "35px", display: "flex", flexDirection: "column", alignItems: "flex-start" }}> className="slider-pagination"
<div style={{ fontSize: "16px", fontWeight: 600, color: "#FFFFFF", marginBottom: "4px" }}> style={{
<span className="slider-progress-current">{activeSlide === 0 ? "01" : "02"}</span> 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> </div>
{/* Progress line — red bar slides to match the active slide (mirrors the home hero) */} {/* 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
<div style={{ style={{
position: "absolute", width: "80px",
left: activeSlide === 0 ? "0" : "50%", height: "2px",
width: "50%", background: "rgba(255, 255, 255, 0.2)",
height: "100%", position: "relative",
background: "#c01227", borderRadius: "1px",
transition: "left 0.3s ease" overflow: "hidden",
}} /> }}
>
<div
style={{
position: "absolute",
left: activeSlide === 0 ? "0" : "50%",
width: "50%",
height: "100%",
background: "#c01227",
transition: "left 0.3s ease",
}}
/>
</div> </div>
</div> </div>
<div className="owl-dots owl-dots-6c7cbcb" style={{ display: "none" }}> <div
className="owl-dots owl-dots-6c7cbcb"
style={{ display: "none" }}
>
<button <button
type="button" type="button"
role="button" role="button"
@@ -354,7 +468,6 @@ export default function HowItWorksHero() {
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -52,6 +52,7 @@ export default function IndexHero() {
return ( 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"> <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: ` <style dangerouslySetInnerHTML={{ __html: `
/* Fluid responsive font size override for hero headings */ /* Fluid responsive font size override for hero headings */
.logico-content-slider-widget .content-slider-item-heading { .logico-content-slider-widget .content-slider-item-heading {
@@ -177,7 +178,7 @@ export default function IndexHero() {
<div <div
className="content-item slider-item elementor-repeater-item-3264830 slide-style-standard" className="content-item slider-item elementor-repeater-item-3264830 slide-style-standard"
style={{ style={{
backgroundImage: "url('/images/home-bg-1.png')", backgroundImage: "url('/images/home-bg-1.webp')",
backgroundPosition: "center center", backgroundPosition: "center center",
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
backgroundSize: "cover" backgroundSize: "cover"
@@ -218,7 +219,7 @@ export default function IndexHero() {
<div <div
className="content-item slider-item elementor-repeater-item-6867061 slide-style-standard" className="content-item slider-item elementor-repeater-item-6867061 slide-style-standard"
style={{ style={{
backgroundImage: "url('/images/home-bg-1.png')", backgroundImage: "url('/images/home-bg-1.webp')",
backgroundPosition: "center center", backgroundPosition: "center center",
backgroundRepeat: "no-repeat", backgroundRepeat: "no-repeat",
backgroundSize: "cover" backgroundSize: "cover"

View File

@@ -5,6 +5,66 @@ import { ScrollReveal } from "@/animations/Reveal";
export default function IndustrySolutions() { export default function IndustrySolutions() {
return ( return (
<>
<style dangerouslySetInnerHTML={{ __html: `
@media (max-width: 768px) {
.elementor-element.elementor-element-3b4a7cc {
--padding-left: 10px !important;
--padding-right: 10px !important;
padding-left: 10px !important;
padding-right: 10px !important;
box-sizing: border-box !important;
}
.elementor-element.elementor-element-3b4a7cc > .e-con-inner {
width: 100% !important;
max-width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
.elementor-element.elementor-element-d602f7f,
.elementor-element.elementor-element-f64bd88,
.elementor-element.elementor-element-5ed2dbb,
.elementor-element.elementor-element-6829276 {
--padding-left: 0px !important;
--padding-right: 0px !important;
--margin-left: 0px !important;
--margin-right: 0px !important;
width: 100% !important;
max-width: 100% !important;
margin-left: 0 !important;
margin-right: 0 !important;
padding-left: 0 !important;
padding-right: 0 !important;
box-sizing: border-box !important;
}
.elementor-element.elementor-element-6829276 {
--e-con-grid-template-columns: minmax(0, 1fr) !important;
--grid-justify-content: stretch !important;
--justify-items: stretch !important;
grid-template-columns: minmax(0, 1fr) !important;
justify-content: stretch !important;
justify-items: stretch !important;
min-width: 0 !important;
}
.industry-solutions-grid,
.industry-card-link {
width: 100% !important;
max-width: 100% !important;
min-width: 0 !important;
}
.industry-solutions-grid {
margin-left: 0 !important;
margin-right: 0 !important;
}
}
`}} />
<div className="elementor-element elementor-element-3b4a7cc e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent" data-id="3b4a7cc" data-element_type="container" data-e-type="container"> <div className="elementor-element elementor-element-3b4a7cc e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent" data-id="3b4a7cc" data-element_type="container" data-e-type="container">
<div className="e-con-inner"> <div className="e-con-inner">
<div className="elementor-element elementor-element-d602f7f e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="d602f7f" data-element_type="container" data-e-type="container"> <div className="elementor-element elementor-element-d602f7f e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="d602f7f" data-element_type="container" data-e-type="container">
@@ -85,7 +145,7 @@ export default function IndustrySolutions() {
<Link href="/solutions" className="industry-card-link"> <Link href="/solutions" className="industry-card-link">
<div className="industry-card-bg"> <div className="industry-card-bg">
<Image <Image
src="/images/tab-pic-1.jpeg" src="/images/tab-pic-1.webp"
alt="FMCG Logistics" alt="FMCG Logistics"
fill fill
style={{ objectFit: "cover" }} style={{ objectFit: "cover" }}
@@ -149,7 +209,7 @@ export default function IndustrySolutions() {
<Link href="/solutions" className="industry-card-link"> <Link href="/solutions" className="industry-card-link">
<div className="industry-card-bg"> <div className="industry-card-bg">
<Image <Image
src="/images/tab-pic-2.jpeg" src="/images/tab-pic-2.webp"
alt="Pharma Logistics" alt="Pharma Logistics"
fill fill
style={{ objectFit: "cover" }} style={{ objectFit: "cover" }}
@@ -213,7 +273,7 @@ export default function IndustrySolutions() {
<Link href="/solutions" className="industry-card-link"> <Link href="/solutions" className="industry-card-link">
<div className="industry-card-bg"> <div className="industry-card-bg">
<Image <Image
src="/images/tab-pic-3.jpeg" src="/images/tab-pic-3.webp"
alt="Enterprise Logistics" alt="Enterprise Logistics"
fill fill
style={{ objectFit: "cover" }} style={{ objectFit: "cover" }}
@@ -278,6 +338,7 @@ export default function IndustrySolutions() {
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</>
); );
} }

View File

@@ -19,7 +19,7 @@ const SECTIONS: Section[] = [
{ {
id: 1, id: 1,
title: "FMCG", title: "FMCG",
image: "/images/tab-pic-1-solution.jpeg", image: "/images/tab-pic-1-solution.webp",
alt: "FMCG logistics", alt: "FMCG logistics",
desc: desc:
"FMCG logistics demands speed, precision, and continuous fulfillment across high-volume delivery networks. Businesses must balance tight delivery timelines, inventory movement, and operational efficiency without compromising product availability.", "FMCG logistics demands speed, precision, and continuous fulfillment across high-volume delivery networks. Businesses must balance tight delivery timelines, inventory movement, and operational efficiency without compromising product availability.",
@@ -39,7 +39,7 @@ const SECTIONS: Section[] = [
{ {
id: 2, id: 2,
title: "Pharma", title: "Pharma",
image: "/images/tab-pic-2-solution.jpeg", image: "/images/tab-pic-2-solution.webp",
alt: "Pharma logistics", alt: "Pharma logistics",
desc: desc:
"Pharma logistics requires precision, compliance, and real-time monitoring so every shipment arrives safely and on time — from temperature-sensitive medicines to urgent emergency deliveries.", "Pharma logistics requires precision, compliance, and real-time monitoring so every shipment arrives safely and on time — from temperature-sensitive medicines to urgent emergency deliveries.",
@@ -59,7 +59,7 @@ const SECTIONS: Section[] = [
{ {
id: 3, id: 3,
title: "Enterprise & B2B", title: "Enterprise & B2B",
image: "/images/tab-pic-3-solution.jpeg", image: "/images/tab-pic-3-solution.webp",
alt: "Enterprise and B2B logistics", alt: "Enterprise and B2B logistics",
desc: desc:
"Enterprise and B2B logistics require coordination and reliability to manage high-value shipments at scale — with appointment scheduling, white-glove standards, and strict SLA commitments.", "Enterprise and B2B logistics require coordination and reliability to manage high-value shipments at scale — with appointment scheduling, white-glove standards, and strict SLA commitments.",
@@ -306,7 +306,7 @@ const CSS = `
#ind-stack .istk__content { max-width: none; } #ind-stack .istk__content { max-width: none; }
} }
@media (max-width: 600px) { @media (max-width: 600px) {
#ind-stack .istk { padding: 10px 14px; } #ind-stack .istk { padding: 10px; }
#ind-stack .istk__card { padding: 22px 20px; border-radius: 24px; } #ind-stack .istk__card { padding: 22px 20px; border-radius: 24px; }
#ind-stack .istk__media { height: 240px; } #ind-stack .istk__media { height: 240px; }
#ind-stack .istk__title { margin-bottom: 16px !important; } #ind-stack .istk__title { margin-bottom: 16px !important; }

View File

@@ -60,10 +60,32 @@ function pointInPoly(x: number, y: number, poly: number[][]) {
return inside; return inside;
} }
export default function IndustryWorldMap() { /** Parse a #rrggbb hex into an [r,g,b] triple. Falls back to the section red. */
function hexToRgb(hex: string): [number, number, number] {
const m = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
if (!m) return [239, 68, 68];
const int = parseInt(m[1], 16);
return [(int >> 16) & 255, (int >> 8) & 255, int & 255];
}
/**
* @param accent Network accent colour (#rrggbb) for the hub nodes, pulse
* rings, travelling packets and dashed routes. The dotted continent
* silhouette stays neutral grey. Defaults to the section red so the Women
* Empowerment usage is unchanged; the MileTruth workflows pass their own
* accent (WF1 teal/cyan · WF2 crimson/red).
*/
export default function IndustryWorldMap({
accent = "#ef4444",
}: {
accent?: string;
}) {
const canvasRef = useRef<HTMLCanvasElement>(null); const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => { useEffect(() => {
const [ar, ag, ab] = hexToRgb(accent);
const rgba = (a: number) => `rgba(${ar},${ag},${ab},${a})`;
const solid = `rgb(${ar},${ag},${ab})`;
const canvas = canvasRef.current; const canvas = canvasRef.current;
const parent = canvas?.parentElement; const parent = canvas?.parentElement;
if (!canvas || !parent) return; if (!canvas || !parent) return;
@@ -135,7 +157,7 @@ export default function IndustryWorldMap() {
ctx.save(); ctx.save();
ctx.setLineDash([4, 7]); ctx.setLineDash([4, 7]);
ctx.lineWidth = 1; ctx.lineWidth = 1;
ctx.strokeStyle = "rgba(239,68,68,0.13)"; ctx.strokeStyle = rgba(0.13);
for (const [a, b] of ROUTES) { for (const [a, b] of ROUTES) {
const c = ctrl(cs[a], cs[b]); const c = ctrl(cs[a], cs[b]);
ctx.beginPath(); ctx.beginPath();
@@ -156,8 +178,8 @@ export default function IndustryWorldMap() {
const tt = Math.max(0, t - 0.04); const tt = Math.max(0, t - 0.04);
const pt = bezier(cs[a], c, cs[b], tt); const pt = bezier(cs[a], c, cs[b], tt);
const grad = ctx.createLinearGradient(pt.x, pt.y, p.x, p.y); const grad = ctx.createLinearGradient(pt.x, pt.y, p.x, p.y);
grad.addColorStop(0, "rgba(239,68,68,0)"); grad.addColorStop(0, rgba(0));
grad.addColorStop(1, "rgba(239,68,68,0.5)"); grad.addColorStop(1, rgba(0.5));
ctx.strokeStyle = grad; ctx.strokeStyle = grad;
ctx.lineWidth = 2; ctx.lineWidth = 2;
ctx.beginPath(); ctx.beginPath();
@@ -165,9 +187,9 @@ export default function IndustryWorldMap() {
ctx.lineTo(p.x, p.y); ctx.lineTo(p.x, p.y);
ctx.stroke(); ctx.stroke();
ctx.shadowColor = "#ef4444"; ctx.shadowColor = solid;
ctx.shadowBlur = 12; ctx.shadowBlur = 12;
ctx.fillStyle = "#ef4444"; ctx.fillStyle = solid;
ctx.beginPath(); ctx.beginPath();
ctx.arc(p.x, p.y, 2.6, 0, Math.PI * 2); ctx.arc(p.x, p.y, 2.6, 0, Math.PI * 2);
ctx.fill(); ctx.fill();
@@ -183,13 +205,13 @@ export default function IndustryWorldMap() {
const radius = 3 + phase * 24; const radius = 3 + phase * 24;
const alpha = (1 - phase) * 0.45; const alpha = (1 - phase) * 0.45;
ctx.beginPath(); ctx.beginPath();
ctx.strokeStyle = `rgba(239,68,68,${alpha})`; ctx.strokeStyle = rgba(alpha);
ctx.lineWidth = 1.5; ctx.lineWidth = 1.5;
ctx.arc(c.x, c.y, radius, 0, Math.PI * 2); ctx.arc(c.x, c.y, radius, 0, Math.PI * 2);
ctx.stroke(); ctx.stroke();
} }
ctx.fillStyle = "#ef4444"; ctx.fillStyle = solid;
ctx.shadowColor = "#ef4444"; ctx.shadowColor = solid;
ctx.shadowBlur = 8; ctx.shadowBlur = 8;
ctx.beginPath(); ctx.beginPath();
ctx.arc(c.x, c.y, 2.6, 0, Math.PI * 2); ctx.arc(c.x, c.y, 2.6, 0, Math.PI * 2);
@@ -221,7 +243,7 @@ export default function IndustryWorldMap() {
cancelAnimationFrame(raf); cancelAnimationFrame(raf);
ro.disconnect(); ro.disconnect();
}; };
}, []); }, [accent]);
return <canvas ref={canvasRef} className="ind__map" aria-hidden="true" />; return <canvas ref={canvasRef} className="ind__map" aria-hidden="true" />;
} }

View File

@@ -15,7 +15,7 @@ const ROADMAP_DATA = [
trackLeft: "12.5%", trackLeft: "12.5%",
phase: "Pilot Phase", phase: "Pilot Phase",
phaseClass: "yellow", phaseClass: "yellow",
title: "Hyderabad Pilot", title: "Pilot",
desc: "Launch operations in Hyderabad with dedicated EV hubs and MileTruth AI v1.0.", desc: "Launch operations in Hyderabad with dedicated EV hubs and MileTruth AI v1.0.",
icon: ( icon: (
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
@@ -25,7 +25,7 @@ const ROADMAP_DATA = [
</svg> </svg>
), ),
stats: [ stats: [
{ text: "50-80 orders/day", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> }, { text: "100+ orders/day", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> },
{ text: "1 city", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M3 21h18M19 21v-2a4 4 0 0 0-3-3.87M5 21v-2a4 4 0 0 1 3-3.87M9 21v-5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v5"></path></svg> }, { text: "1 city", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M3 21h18M19 21v-2a4 4 0 0 0-3-3.87M5 21v-2a4 4 0 0 1 3-3.87M9 21v-5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v5"></path></svg> },
{ text: "10+ women partners", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle></svg> } { text: "10+ women partners", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle></svg> }
] ]
@@ -36,7 +36,7 @@ const ROADMAP_DATA = [
trackLeft: "37.5%", trackLeft: "37.5%",
phase: "Multi-City", phase: "Multi-City",
phaseClass: "green", phaseClass: "green",
title: "Multi-City Scale", title: "Scale",
desc: "Expand to Bengaluru and Chennai, securing key B2B enterprise traction.", desc: "Expand to Bengaluru and Chennai, securing key B2B enterprise traction.",
icon: ( icon: (
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
@@ -46,7 +46,7 @@ const ROADMAP_DATA = [
</svg> </svg>
), ),
stats: [ stats: [
{ text: "300-500 orders/day", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> }, { text: "500+ orders/day", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> },
{ text: "3 cities", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M3 21h18M19 21v-2a4 4 0 0 0-3-3.87M5 21v-2a4 4 0 0 1 3-3.87M9 21v-5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v5"></path></svg> }, { text: "3 cities", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M3 21h18M19 21v-2a4 4 0 0 0-3-3.87M5 21v-2a4 4 0 0 1 3-3.87M9 21v-5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v5"></path></svg> },
{ text: "50+ EVs", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg> } { text: "50+ EVs", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg> }
] ]
@@ -57,8 +57,8 @@ const ROADMAP_DATA = [
trackLeft: "62.5%", trackLeft: "62.5%",
phase: "Platform", phase: "Platform",
phaseClass: "blue", phaseClass: "blue",
title: "Platform Expansion", title: "Expansion",
desc: "Scale to 5+ cities. Launch developer API marketplace and Series A readiness.", desc: "Scale to 5+ cities. Strengthen regional operations.",
icon: ( icon: (
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"> <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
<circle cx="12" cy="12" r="3"></circle> <circle cx="12" cy="12" r="3"></circle>
@@ -66,9 +66,9 @@ const ROADMAP_DATA = [
</svg> </svg>
), ),
stats: [ stats: [
{ text: "1,200+ orders/day", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> }, { text: "5000+ orders/day", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> },
{ text: "5+ cities", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M3 21h18M19 21v-2a4 4 0 0 0-3-3.87M5 21v-2a4 4 0 0 1 3-3.87M9 21v-5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v5"></path></svg> }, { text: "5+ cities", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M3 21h18M19 21v-2a4 4 0 0 0-3-3.87M5 21v-2a4 4 0 0 1 3-3.87M9 21v-5a2 2 0 0 1 2-2h2a2 2 0 0 1 2 2v5"></path></svg> },
{ text: "API marketplace", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg> } { text: "100+ women partners", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="16 18 22 12 16 6"></polyline><polyline points="8 6 2 12 8 18"></polyline></svg> }
] ]
}, },
{ {
@@ -86,8 +86,8 @@ const ROADMAP_DATA = [
</svg> </svg>
), ),
stats: [ stats: [
{ text: "5,000+ orders/day", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> }, { text: "50,000+ orders/day", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"></rect><line x1="16" y1="2" x2="16" y2="6"></line><line x1="8" y1="2" x2="8" y2="6"></line><line x1="3" y1="10" x2="21" y2="10"></line></svg> },
{ text: "Rs 65 Cr+ revenue", icon: <span className="currency-symbol" style={{ marginRight: "4px", fontSize: "11px", fontWeight: 800 }}>Rs</span> }, { text: "50+ cities", icon: <span className="currency-symbol" style={{ marginRight: "4px", fontSize: "11px", fontWeight: 800 }}>Rs</span> },
{ text: "2,000+ women partners", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg> } { text: "2,000+ women partners", icon: <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg> }
] ]
} }
@@ -234,7 +234,7 @@ export default function IntelligenceGrid() {
return ( return (
<div <div
ref={containerRef} ref={containerRef}
className="elementor-element elementor-element-bbc6760 e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent" className="elementor-element elementor-element-bbc6760 e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent intelligence-grid-section"
data-id="bbc6760" data-id="bbc6760"
data-element_type="container" data-element_type="container"
data-e-type="container" data-e-type="container"
@@ -710,11 +710,19 @@ export default function IntelligenceGrid() {
} }
} }
@media (max-width: 768px) { @media (max-width: 767px) {
.intelligence-grid-section {
--padding-left: 0px !important;
--padding-right: 0px !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
.roadmap-hero-section { .roadmap-hero-section {
width: calc(100% - 24px); /* tighter 12px gutter so the band reads edge-to-edge on phones */ width: calc(100% - 32px) !important;
max-width: calc(100% - 32px) !important;
padding: 52px 16px 44px; padding: 52px 16px 44px;
border-radius: 22px; border-radius: 22px !important;
} }
.vision-main-subtitle { margin-bottom: 40px !important; } .vision-main-subtitle { margin-bottom: 40px !important; }

View File

@@ -44,6 +44,7 @@ export default function MileTruthHero() {
return ( return (
<> <>
<link rel="preload" as="image" href="/images/miletruth-bg.webp" />
<style dangerouslySetInnerHTML={{ __html: ` <style dangerouslySetInnerHTML={{ __html: `
/* ── Hero wrapper: column layout, zero gap between hero + stats ── */ /* ── Hero wrapper: column layout, zero gap between hero + stats ── */
.miletruth-hero .elementor-element-86f3204 { .miletruth-hero .elementor-element-86f3204 {
@@ -56,7 +57,7 @@ export default function MileTruthHero() {
/* ── Hero slider card ── */ /* ── Hero slider card ── */
.miletruth-hero-container { .miletruth-hero-container {
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.05) 0%, rgba(0, 0, 0, 0.15) 55%, rgba(0, 0, 0, 0.3) 100%), url('/images/miletruth-bg.png') !important; background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.05) 0%, rgba(0, 0, 0, 0.15) 55%, rgba(0, 0, 0, 0.3) 100%), url('/images/miletruth-bg.webp') !important;
background-size: cover !important; background-size: cover !important;
background-position: center !important; background-position: center !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
@@ -68,7 +69,7 @@ export default function MileTruthHero() {
justify-content: center; justify-content: center;
border-radius: 25px 25px 0 0; border-radius: 25px 25px 0 0;
margin-top: 20px; margin-top: 20px;
padding: 100px 40px; padding: 100px 0;
} }
.miletruth-hero-container::before { .miletruth-hero-container::before {
display: none !important; display: none !important;
@@ -77,7 +78,7 @@ export default function MileTruthHero() {
position: relative; position: relative;
z-index: 2; z-index: 2;
width: 100%; width: 100%;
padding: 0 40px !important; padding: 0 !important;
box-sizing: border-box; box-sizing: border-box;
} }
.miletruth-hero .content-slider-item-heading, .miletruth-hero .content-slider-item-heading,
@@ -113,7 +114,7 @@ export default function MileTruthHero() {
text-align: center !important; text-align: center !important;
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
padding: 0 40px !important; padding: 0 15px !important;
box-sizing: border-box !important; box-sizing: border-box !important;
} }
.miletruth-hero .slide-content, .miletruth-hero .slide-content,
@@ -257,9 +258,12 @@ export default function MileTruthHero() {
} }
} }
@media (max-width: 1024px) { @media (max-width: 1024px) {
.miletruth-hero .elementor-element-86f3204 {
padding: 0 10px !important;
}
.miletruth-hero-container { .miletruth-hero-container {
min-height: 600px; min-height: 600px;
padding: 120px 0; padding: 80px 0 !important;
} }
.miletruth-hero .elementor-element-8e5c81e { .miletruth-hero .elementor-element-8e5c81e {
padding: 40px 0 !important; padding: 40px 0 !important;
@@ -287,19 +291,19 @@ export default function MileTruthHero() {
@media (max-width: 580px) { @media (max-width: 580px) {
.miletruth-hero-container { .miletruth-hero-container {
min-height: 500px; min-height: 500px;
padding: 100px 0; padding: 60px 0 !important;
} }
.miletruth-hero .elementor-element-86f3204 { .miletruth-hero .elementor-element-86f3204 {
padding: 0 12px; padding: 0 10px !important;
} }
/* Collapse the nested horizontal padding (was 40px + 40px on top of the /* Collapse the nested horizontal padding (was 40px + 40px on top of the
12px wrapper) — at 320px that left only ~136px for text and the long 12px wrapper) — at 320px that left only ~136px for text and the long
heading words overflowed, causing horizontal scroll + off-center look. */ heading words overflowed, causing horizontal scroll + off-center look. */
.miletruth-content { .miletruth-content {
padding: 0 14px !important; padding: 0 !important;
} }
.miletruth-hero .slide-content { .miletruth-hero .slide-content {
padding: 0 !important; padding: 0 15px !important;
} }
.miletruth-hero .elementor-element-8e5c81e { .miletruth-hero .elementor-element-8e5c81e {
padding: 30px 0 !important; padding: 30px 0 !important;

View File

@@ -6,28 +6,28 @@ export default function OurTeam() {
{ {
name: "Ratan Kumar", name: "Ratan Kumar",
position: "COO & Operational Specialist", position: "COO & Operational Specialist",
image: "/images/Investor.png", image: "/images/Investor.webp",
}, },
{ {
name: "Parthiban", name: "Parthiban",
position: "CGO & Growth Specialist", position: "CGO & Growth Specialist",
image: "/images/Parthi.png", image: "/images/Parthi.webp",
}, },
{ {
name: "Aravinth", name: "Aravinth",
position: "CFO & Finance Specialist", position: "CFO & Finance Specialist",
image: "/images/Aravinth.png", image: "/images/Aravinth.webp",
}, },
{ {
name: "Fazul Ilahi", name: "Fazul Ilahi",
position: "CTO & Technology Specialist", position: "CTO & Technology Specialist",
image: "/images/Fazul.png", image: "/images/Fazul.webp",
}, },
{ {
name: "Suriya Kumar", name: "Suriya Kumar",
position: "Engineering Head & AI Specialist", position: "Engineering Head & AI Specialist",
image: "/images/Suriya.png", image: "/images/Suriya.webp",
}, },
]; ];
@@ -87,8 +87,35 @@ export default function OurTeam() {
@media (max-width: 800px) { @media (max-width: 800px) {
.team-listing-wrapper.team-grid-listing .team-item-wrapper { width: 50%; } .team-listing-wrapper.team-grid-listing .team-item-wrapper { width: 50%; }
} }
@media (max-width: 767px) {
.elementor-86 .elementor-element-c2c601a > .e-con-inner {
padding-left: 0 !important;
padding-right: 0 !important;
padding-top: 32px !important;
padding-bottom: 32px !important;
}
.elementor-86 .elementor-element-c46350e > .elementor-widget-container {
margin-top: 24px !important;
margin-bottom: 24px !important;
}
.team-listing-wrapper.team-grid-listing {
margin: 0 0 -28px;
}
.team-listing-wrapper.team-grid-listing .team-item-wrapper {
width: 100%;
padding: 0;
margin-bottom: 28px;
}
}
@media (max-width: 480px) { @media (max-width: 480px) {
.team-listing-wrapper.team-grid-listing .team-item-wrapper { width: 100%; } .team-listing-wrapper.team-grid-listing {
margin: 0 0 -28px;
}
.team-listing-wrapper.team-grid-listing .team-item-wrapper {
width: 100%;
padding: 0;
margin-bottom: 28px;
}
} }
/* Card: photo on top, name/position at the bottom (down to image). */ /* Card: photo on top, name/position at the bottom (down to image). */
@@ -122,6 +149,10 @@ export default function OurTeam() {
width: 100%; width: 100%;
margin: 0; margin: 0;
} }
.team-listing-wrapper.team-grid-listing .team-item .post-title .team-member-name {
color: inherit;
text-decoration: none;
}
`}} /> `}} />
<div className="elementor-element elementor-element-c2c601a e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent" data-id="c2c601a" data-element_type="container" data-e-type="container"> <div className="elementor-element elementor-element-c2c601a e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent" data-id="c2c601a" data-element_type="container" data-e-type="container">
<div className="e-con-inner"> <div className="e-con-inner">
@@ -149,19 +180,17 @@ export default function OurTeam() {
<div className="team-item"> <div className="team-item">
<div className="team-item-media"> <div className="team-item-media">
<div className="post-media"> <div className="post-media">
<a href="#"> <Image
<Image src={member.image}
src={member.image} alt={member.name}
alt={member.name} width={300}
width={300} height={360}
height={360} style={{ objectFit: "cover", width: "100%", height: "100%" }}
style={{ objectFit: "cover", width: "100%", height: "100%" }} />
/>
</a>
</div> </div>
</div> </div>
<div className="team-item-content"> <div className="team-item-content">
<div className="post-title"><a href="#">{member.name}</a></div> <div className="post-title"><span className="team-member-name">{member.name}</span></div>
<div className="team-item-position" dangerouslySetInnerHTML={{ __html: member.position }}></div> <div className="team-item-position" dangerouslySetInnerHTML={{ __html: member.position }}></div>
<div className="team-item-socials"> <div className="team-item-socials">
<ul className="team-socials wrapper-socials"> <ul className="team-socials wrapper-socials">

View File

@@ -38,7 +38,7 @@ function ContentRenderer({ block }: { block: ContentBlock }) {
return ( return (
<blockquote className="dm-article-quote"> <blockquote className="dm-article-quote">
<p>{block.text}</p> <p>{block.text}</p>
{block.cite && <cite> {block.cite}</cite>} {block.cite && <cite>{block.cite}</cite>}
</blockquote> </blockquote>
); );
case "image": case "image":
@@ -74,6 +74,7 @@ export default function SingleBlog({ post }: { post: BlogPost }) {
return ( return (
<article className="dm-single-blog"> <article className="dm-single-blog">
<link rel="preload" as="image" href={post.image} />
<style dangerouslySetInnerHTML={{ __html: STYLES }} /> <style dangerouslySetInnerHTML={{ __html: STYLES }} />
{/* ── Full-width page banner (image + badge + title only) ──────── */} {/* ── Full-width page banner (image + badge + title only) ──────── */}
@@ -168,7 +169,7 @@ const STYLES = `
font-family: var(--font-manrope), sans-serif; font-family: var(--font-manrope), sans-serif;
} }
/* Heading normalization — beat the global theme's .elementor-kit-5 h1h6 /* Heading normalization. Beat the global theme's .elementor-kit-5 h1-h6
(120/80/60px UPPERCASE) rules with !important on our own classes. */ (120/80/60px UPPERCASE) rules with !important on our own classes. */
.dm-single-blog :where(h1, h2, h3, h4, h5, h6) { .dm-single-blog :where(h1, h2, h3, h4, h5, h6) {
font-family: var(--font-manrope), sans-serif !important; font-family: var(--font-manrope), sans-serif !important;
@@ -180,7 +181,7 @@ const STYLES = `
specificity 0-1-1) so blog links keep our colors and never get underlined. */ specificity 0-1-1) so blog links keep our colors and never get underlined. */
.dm-single-blog a { text-decoration: none !important; } .dm-single-blog a { text-decoration: none !important; }
/* ── Page banner tall (homepage-scale); only badge + title inside ── */ /* Page banner: tall homepage-scale frame with only badge + title inside */
/* Compound selector (specificity 20) + !important beats the global 800px /* Compound selector (specificity 20) + !important beats the global 800px
single-class height rules so the blog banner can use viewport heights. */ single-class height rules so the blog banner can use viewport heights. */
.custom-standard-hero-card.dm-banner-card { .custom-standard-hero-card.dm-banner-card {
@@ -216,7 +217,7 @@ const STYLES = `
@media (max-width: 1024px) { .dm-banner-title { font-size: clamp(32px, 6vw, 48px) !important; max-width: 90%; } } @media (max-width: 1024px) { .dm-banner-title { font-size: clamp(32px, 6vw, 48px) !important; max-width: 90%; } }
@media (max-width: 600px) { .dm-banner-title { font-size: clamp(28px, 8vw, 38px) !important; max-width: 90%; } } @media (max-width: 600px) { .dm-banner-title { font-size: clamp(28px, 8vw, 38px) !important; max-width: 90%; } }
/* ── Content wrap begins immediately below the banner ── */ /* Content wrap begins immediately below the banner */
/* Shared content container: the SAME max-width + horizontal padding is used /* Shared content container: the SAME max-width + horizontal padding is used
by BlogPostFooter (.dm-blog-footer-inner) so the article body, headings, by BlogPostFooter (.dm-blog-footer-inner) so the article body, headings,
images, Prev/Next, Related Articles and the CTA banner all align to one images, Prev/Next, Related Articles and the CTA banner all align to one
@@ -226,6 +227,12 @@ const STYLES = `
/* 20px mobile padding floor → 40px on desktop; matches the footer container. */ /* 20px mobile padding floor → 40px on desktop; matches the footer container. */
padding: clamp(14px, 2vw, 26px) clamp(20px, 4vw, 40px) 0; padding: clamp(14px, 2vw, 26px) clamp(20px, 4vw, 40px) 0;
} }
@media (max-width: 767px) {
.dm-blog-wrap {
padding-left: 16px;
padding-right: 16px;
}
}
/* ── Article meta bar (directly under the hero) ── */ /* ── Article meta bar (directly under the hero) ── */
.dm-meta-bar { .dm-meta-bar {
@@ -287,7 +294,7 @@ const STYLES = `
} }
/* Each article block is wrapped in its OWN ScrollReveal <div>, so a bare /* Each article block is wrapped in its OWN ScrollReveal <div>, so a bare
:first-child rule matched every heading (each is the only child of its :first-child rule matched every heading (each is the only child of its
wrapper) and zeroed its top margin collapsing the gap above every wrapper) and zeroed its top margin, collapsing the gap above every
section heading. Scope the reset to only the article body's first block. */ section heading. Scope the reset to only the article body's first block. */
.dm-article-body > :first-child :where(.dm-article-h2, .dm-article-h3), .dm-article-body > :first-child :where(.dm-article-h2, .dm-article-h3),
.dm-article-body > .dm-article-h2:first-child, .dm-article-body > .dm-article-h2:first-child,

View File

@@ -19,17 +19,18 @@ export default function SolutionsHero() {
return ( return (
<> <>
<link rel="preload" as="image" href="/images/home1-slide-1.webp" />
<style dangerouslySetInnerHTML={{ __html: ` <style dangerouslySetInnerHTML={{ __html: `
.howits-hero-custom-bg.elementor-repeater-item-3264830, .howits-hero-custom-bg.elementor-repeater-item-3264830,
.howits-hero-custom-bg.elementor-repeater-item-6867061 { .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; background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.1)), url('/images/home1-slide-1.webp') !important;
background-position: center !important; background-position: center !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
background-size: cover !important; background-size: cover !important;
} }
.howits-hero-custom-bg.elementor-repeater-item-6867061 { .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-2.png') !important; background-image: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.1)), url('/images/home1-slide-2.webp') !important;
background-position: center !important; background-position: center !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
background-size: cover !important; background-size: cover !important;
@@ -44,7 +45,7 @@ export default function SolutionsHero() {
text-align: center !important; text-align: center !important;
width: 100% !important; width: 100% !important;
height: 100% !important; height: 100% !important;
padding: 0 40px !important; padding: 0 !important;
box-sizing: border-box !important; box-sizing: border-box !important;
} }
@@ -70,6 +71,8 @@ export default function SolutionsHero() {
width: 100% !important; width: 100% !important;
margin-left: auto !important; margin-left: auto !important;
margin-right: auto !important; margin-right: auto !important;
padding-left: 15px !important;
padding-right: 15px !important;
} }
.elementor-element.elementor-element-6c7cbcb .content-slider-item-text { .elementor-element.elementor-element-6c7cbcb .content-slider-item-text {
@@ -83,6 +86,8 @@ export default function SolutionsHero() {
text-align: center !important; text-align: center !important;
max-width: 820px !important; max-width: 820px !important;
margin: 0 auto !important; margin: 0 auto !important;
padding-left: 15px !important;
padding-right: 15px !important;
} }
/* Larger, more readable hero subtitle on large/4K screens */ /* Larger, more readable hero subtitle on large/4K screens */
@@ -106,10 +111,13 @@ export default function SolutionsHero() {
@media (max-width: 1024px) { @media (max-width: 1024px) {
.elementor-element.elementor-element-741f56c { .elementor-element.elementor-element-741f56c {
width: calc(100% - 40px) !important; width: 100% !important;
max-width: none !important; max-width: 100% !important;
margin-left: auto !important; margin-left: auto !important;
margin-right: auto !important; margin-right: auto !important;
--padding-top: 0px !important;
--padding-block-start: 0px !important;
padding: 0 10px 10px 10px !important;
overflow: hidden !important; overflow: hidden !important;
border-radius: 25px !important; border-radius: 25px !important;
} }
@@ -131,15 +139,19 @@ export default function SolutionsHero() {
.elementor-element.elementor-element-6c7cbcb .slide-content { .elementor-element.elementor-element-6c7cbcb .slide-content {
width: 100% !important; width: 100% !important;
min-height: 620px !important; min-height: 620px !important;
padding-left: 48px !important; padding-left: 0 !important;
padding-right: 48px !important; padding-right: 0 !important;
box-sizing: border-box !important; box-sizing: border-box !important;
} }
} }
@media (max-width: 767px) { @media (max-width: 767px) {
.elementor-element.elementor-element-741f56c { .elementor-element.elementor-element-741f56c {
width: calc(100% - 24px) !important; width: 100% !important;
max-width: 100% !important;
--padding-top: 0px !important;
--padding-block-start: 0px !important;
padding: 0 10px 10px 10px !important;
border-radius: 22px !important; border-radius: 22px !important;
} }
@@ -155,8 +167,8 @@ export default function SolutionsHero() {
.elementor-element.elementor-element-6c7cbcb .slide-content { .elementor-element.elementor-element-6c7cbcb .slide-content {
min-height: 560px !important; min-height: 560px !important;
padding-left: 22px !important; padding-left: 0 !important;
padding-right: 22px !important; padding-right: 0 !important;
} }
.elementor-element.elementor-element-6c7cbcb .content-slider-item-heading { .elementor-element.elementor-element-6c7cbcb .content-slider-item-heading {
@@ -164,6 +176,8 @@ export default function SolutionsHero() {
font-size: clamp(38px, 11vw, 48px) !important; font-size: clamp(38px, 11vw, 48px) !important;
line-height: 1.05 !important; line-height: 1.05 !important;
overflow-wrap: break-word !important; overflow-wrap: break-word !important;
padding-left: 15px !important;
padding-right: 15px !important;
} }
/* Pin the slide counter ("01/02") cleanly bottom-RIGHT — matching the /* Pin the slide counter ("01/02") cleanly bottom-RIGHT — matching the

View File

@@ -2,35 +2,57 @@ import React from "react";
import Image from "next/image"; import Image from "next/image";
import { ScrollReveal } from "../../animations/Reveal"; import { ScrollReveal } from "../../animations/Reveal";
type Stat = {
value: string;
label: string;
};
type Stage = { type Stage = {
img: string; img: string;
label: string; label: string;
title: string; title: string;
subtitle?: string;
desc: string; desc: string;
points: string[]; points: string[];
stats?: Stat[];
}; };
const STAGES: Stage[] = [ const STAGES: Stage[] = [
{ {
img: "/images/first-mile-approach.jpg", img: "/images/first-mile-approach.webp",
label: "01 / First Mile", label: "Stage 01",
title: "Origin to Hub", title: "First Mile Warehouse",
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.", 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"], 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", img: "/images/mid-mile-approach.webp",
label: "02 / Mid Mile", label: "Stage 02",
title: "Hub to Hub Transit", title: "Mid Mile Transit",
desc: "Freight moves between hubs on optimised line-haul routes. Real-time tracking, cross-docking, and SLA monitoring keep every shipment on schedule.", 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"], 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", img: "/images/last-mile-approach.webp",
label: "03 / Last Mile", label: "Stage 03",
title: "Hub to Doorstep", title: "Last Mile Delivery",
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.", 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"], 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: ` <style dangerouslySetInnerHTML={{ __html: `
/* ===================================================================== /* =====================================================================
"Why Businesses Choose Doormile" — First / Mid / Last Mile stage cards. "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 { .wcd-section {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
width: auto; 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; background-color: #1F1F1F;
border-radius: 0 0 25px 25px; border-radius: 0 0 25px 25px;
padding: 50px 0 110px; padding: 50px 0 110px;
@@ -63,7 +79,6 @@ export default function WhyChooseDoormile() {
padding: 0 50px; padding: 0 50px;
box-sizing: border-box; box-sizing: border-box;
} }
/* Centered header block (eyebrow + heading) with a faint map backdrop */
.wcd-head { .wcd-head {
position: relative; position: relative;
text-align: center; text-align: center;
@@ -78,16 +93,16 @@ export default function WhyChooseDoormile() {
content: ""; content: "";
position: absolute; position: absolute;
left: 50%; 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%); transform: translateX(-50%);
width: min(1180px, 95%); width: min(1180px, 95%);
aspect-ratio: 2 / 1; aspect-ratio: 2 / 1;
background: url('/images/bg-map.png') center / contain no-repeat; background: url('/images/bg-map.png') center / contain no-repeat;
opacity: 0.06; /* Elegant faint visibility */ opacity: 0.06;
filter: invert(1); /* Invert dark map dots to white/light-gray to make them visible on the #1F1F1F background */ filter: invert(1);
z-index: 0; z-index: 0;
pointer-events: none; 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 { .wcd-card-wrapper {
display: flex; display: flex;
@@ -127,18 +142,23 @@ export default function WhyChooseDoormile() {
width: 100%; width: 100%;
} }
/* Premium Glassmorphism & Card Interaction */
.wcd-card { .wcd-card {
display: flex; display: flex;
flex-direction: column; 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: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 20px; border-radius: 20px;
overflow: hidden; 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 { .wcd-card:hover {
border-color: #c01227 !important; 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); transform: translateY(-8px);
} }
.wcd-card-media { .wcd-card-media {
@@ -152,75 +172,106 @@ export default function WhyChooseDoormile() {
transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1); transition: transform 0.6s cubic-bezier(0.165, 0.84, 0.44, 1);
} }
.wcd-card:hover .wcd-card-media img { .wcd-card:hover .wcd-card-media img {
transform: scale(1.06); transform: scale(1.05);
} }
/* Body Data Container (Unified Div) */
.wcd-card-body { .wcd-card-body {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
padding: 40px; padding: 40px;
} }
/* Modern Pill Badge for Label */
.wcd-card-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-family: var(--font-manrope), "Manrope", sans-serif;
font-size: 14px; font-size: 11px;
font-weight: 700; font-weight: 700;
letter-spacing: 1px; letter-spacing: 0.08em;
text-transform: uppercase; text-transform: uppercase;
color: #c01227; color: #ff3344;
margin: 0 0 20px; 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 { .logico-front-end .wcd-section h3.wcd-card-title {
font-family: var(--font-manrope), "Manrope", sans-serif; font-family: var(--font-manrope), "Manrope", sans-serif;
font-size: 32px; font-size: 26px;
font-weight: 700; font-weight: 700;
line-height: 1.1; line-height: 1.2;
letter-spacing: -0.02em; letter-spacing: -0.02em;
text-transform: uppercase; text-transform: uppercase;
color: #FFFFFF; color: #FFFFFF;
-webkit-text-fill-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 { .wcd-card-desc {
font-size: 17px; font-size: 15px;
font-weight: 400; font-weight: 400;
line-height: 1.6; line-height: 1.6;
color: rgba(255, 255, 255, 0.6); color: rgba(255, 255, 255, 0.65);
margin: 0 0 34px; margin: 0 0 24px;
} }
.wcd-card-points { .wcd-card-points {
list-style: none; list-style: none;
margin: auto 0 0; margin: 0 0 30px;
padding: 0; padding: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 16px; gap: 12px;
} }
.wcd-section .wcd-card-points li { .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; display: flex;
align-items: flex-start; align-items: flex-start;
gap: 12px; gap: 12px;
padding-left: 0; padding-left: 0;
font-size: 16px; font-size: 15px;
font-weight: 700; font-weight: 600;
line-height: 1.3; line-height: 1.3;
color: #FFFFFF; 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 { .wcd-section .wcd-card-points li::before {
content: none; content: none;
display: 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 { .wcd-card-points .wcd-check {
flex: 0 0 auto; flex: 0 0 auto;
width: 18px; width: 18px;
height: 18px; height: 18px;
margin-top: 0.12em; margin-top: 0.08em;
color: #c01227; color: #c01227;
transition: transform 0.3s ease; transition: transform 0.3s ease;
} }
@@ -228,20 +279,51 @@ export default function WhyChooseDoormile() {
transform: scale(1.1); 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) { @media (max-width: 1020px) {
/* No pull-up overlap on mobile/tablet: the Miles3 cards stack into a .wcd-section { margin: 0 10px 10px 10px; padding: 40px 0 80px; }
12 col layout, so a negative margin-top covers the last card's .wcd-inner { padding: 0; }
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-grid { grid-template-columns: 1fr; gap: 24px; }
.wcd-card-body { padding: 32px; } .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) { @media (max-width: 480px) {
.wcd-section { margin: 0 12px 12px 12px; border-radius: 0 0 20px 20px; padding: 30px 0 64px; } .wcd-section { margin: 0 10px 10px 10px; border-radius: 0 0 20px 20px; padding: 30px 0 64px; }
.wcd-inner { padding: 0 20px; } .wcd-inner { padding: 0; }
.wcd-card-body { padding: 24px; } .wcd-card-body { padding: 24px; }
} }
`}} /> `}} />
@@ -273,10 +355,20 @@ export default function WhyChooseDoormile() {
sizes="(max-width: 1020px) 100vw, 33vw" sizes="(max-width: 1020px) 100vw, 33vw"
/> />
</div> </div>
{/* Single Parent Div for All Data Content */}
<div className="wcd-card-body"> <div className="wcd-card-body">
<div className="wcd-card-label">{stage.label}</div> <div className="wcd-card-label">{stage.label}</div>
<h3 className="wcd-card-title">{stage.title}</h3> <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> <p className="wcd-card-desc">{stage.desc}</p>
<ul className="wcd-card-points"> <ul className="wcd-card-points">
{stage.points.map((point) => ( {stage.points.map((point) => (
<li key={point}> <li key={point}>
@@ -297,6 +389,18 @@ export default function WhyChooseDoormile() {
</li> </li>
))} ))}
</ul> </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> </div>
</article> </article>
</ScrollReveal> </ScrollReveal>

View File

@@ -12,7 +12,7 @@ const WS_STATS = [
const WS_CARDS = [ const WS_CARDS = [
{ {
title: "Women Leadership", title: "Women Leadership",
desc: "Women driving decisions across operations, routing, and last-mile delivery every day.", desc: "Women shaping strategy and driving excellence across logistics operations.",
icon: ( icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<circle cx="12" cy="9" r="5" /> <circle cx="12" cy="9" r="5" />
@@ -22,7 +22,7 @@ const WS_CARDS = [
}, },
{ {
title: "Entrepreneurship", title: "Entrepreneurship",
desc: "Enabling women to build, own, and scale their own delivery businesses.", desc: "Women building sustainable businesses within the logistics ecosystem.",
icon: ( icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"> <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" /> <rect x="3" y="7" width="18" height="13" rx="2" />
@@ -32,7 +32,7 @@ const WS_CARDS = [
}, },
{ {
title: "Innovation", title: "Innovation",
desc: "Fresh thinking that reshapes how first and last-mile logistics actually work.", desc: "Women advancing logistics through practical ideas and operational insight.",
icon: ( icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<path d="M9 18h6M10 21h4" /> <path d="M9 18h6M10 21h4" />
@@ -42,7 +42,7 @@ const WS_CARDS = [
}, },
{ {
title: "Community Growth", title: "Community Growth",
desc: "Local hiring and training that lifts entire neighbourhoods, not just routes.", desc: "Creating local opportunities through training, employment, and empowerment.",
icon: ( icon: (
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round"> <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
<circle cx="9" cy="8" r="3" /> <circle cx="9" cy="8" r="3" />
@@ -117,14 +117,18 @@ export default function WomenSection() {
display: grid; display: grid;
grid-template-columns: 1fr 1fr; grid-template-columns: 1fr 1fr;
gap: 14px; gap: 14px;
align-items: stretch;
} }
#ws-stories .ws__card { #ws-stories .ws__card {
position: relative; position: relative;
overflow: hidden; overflow: hidden;
display: flex;
flex-direction: column;
min-height: 168px;
background: rgba(255,255,255,0.035); background: rgba(255,255,255,0.035);
border: 1px solid rgba(255,255,255,0.08); border: 1px solid rgba(255,255,255,0.08);
border-radius: 16px; border-radius: 14px;
padding: 22px 20px; padding: 20px;
transition: transform 0.35s cubic-bezier(.25,1,.5,1), border-color 0.35s ease, background-color 0.35s ease, box-shadow 0.35s ease; transition: transform 0.35s cubic-bezier(.25,1,.5,1), border-color 0.35s ease, background-color 0.35s ease, box-shadow 0.35s ease;
} }
#ws-stories .ws__card::after { #ws-stories .ws__card::after {
@@ -148,31 +152,66 @@ export default function WomenSection() {
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
width: 44px; height: 44px; width: 40px; height: 40px;
border-radius: 12px; border-radius: 11px;
background: rgba(220,38,38,0.12); background: rgba(220,38,38,0.12);
border: 1px solid rgba(220,38,38,0.25); border: 1px solid rgba(220,38,38,0.25);
color: #ef4444; color: #ef4444;
margin-bottom: 14px; margin-bottom: 16px;
transition: background-color 0.3s ease, color 0.3s ease; transition: background-color 0.3s ease, color 0.3s ease;
} }
#ws-stories .ws__card:hover .ws__card-icon { background: #dc2626; color: #fff; } #ws-stories .ws__card:hover .ws__card-icon { background: #dc2626; color: #fff; }
#ws-stories .ws__card-icon svg { width: 22px; height: 22px; } #ws-stories .ws__card-icon svg { width: 21px; height: 21px; }
#ws-stories .ws__card-title { #ws-stories .ws__card-title {
color: #fff !important; color: #fff !important;
font-weight: 900; font-weight: 800;
font-size: 17px; font-size: 19px;
letter-spacing: -0.01em; line-height: 1.2;
margin-bottom: 8px; letter-spacing: 0;
margin-bottom: 9px;
} }
#ws-stories .ws__card-desc { #ws-stories .ws__card-desc {
color: rgba(255,255,255,0.6) !important; max-width: 28ch;
font-size: 13.5px; color: rgba(255,255,255,0.66) !important;
line-height: 1.6; font-size: 15px;
line-height: 1.52;
margin: 0; margin: 0;
font-weight: 900; font-weight: 500;
font-size: 16px; }
/* ---- Floating card sizing: content-driven, not container-driven ----
Matches the EV Logistics card height proportions exactly by trimming
red content padding and white footer min-height. */
.elementor-element.elementor-element-3b61435 {
--padding-top: 46px;
--padding-bottom: 38px;
}
.elementor-element.elementor-element-5aea22e {
--min-height: 38px;
--padding-top: 14px;
--padding-bottom: 14px;
}
@media (max-width: 1020px) {
.elementor-element.elementor-element-3b61435 {
--padding-top: 34px;
--padding-bottom: 28px;
}
.elementor-element.elementor-element-5aea22e {
--min-height: 32px;
--padding-top: 12px;
--padding-bottom: 12px;
}
}
@media (max-width: 660px) {
.elementor-element.elementor-element-3b61435 {
--padding-top: 26px;
--padding-bottom: 22px;
}
.elementor-element.elementor-element-5aea22e {
--min-height: 24px;
--padding-top: 10px;
--padding-bottom: 10px;
}
} }
.elementor-element-778840d .logico-title { .elementor-element-778840d .logico-title {
@@ -188,9 +227,9 @@ export default function WomenSection() {
background-position: 78% center !important; background-position: 78% center !important;
} }
} }
@media (max-width: 600px) { @media (max-width: 767px) {
.elementor-element.elementor-element-7da6646:not(.elementor-motion-effects-element-type-background) { .elementor-element.elementor-element-7da6646:not(.elementor-motion-effects-element-type-background) {
background-position: 82% center !important; background-position: 82% 25% !important;
} }
} }
@@ -220,7 +259,108 @@ export default function WomenSection() {
margin-right: 0 !important; margin-right: 0 !important;
} }
} }
@media (max-width: 600px) {
@media (max-width: 767px) {
#women-entrepreneurship {
width: calc(100% - 20px) !important;
margin-left: 10px !important;
margin-right: 10px !important;
border-radius: 22px !important;
padding-left: 0px !important;
padding-right: 0px !important;
}
#women-entrepreneurship .e-con-boxed > .e-con-inner {
padding-top: 32px !important;
padding-bottom: 32px !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
#women-entrepreneurship .elementor-element-8b5d6e6,
#women-entrepreneurship .elementor-element-b6e14bd,
#women-entrepreneurship .elementor-element-90cc867,
#women-entrepreneurship .elementor-element-36efec7,
#women-entrepreneurship .elementor-element-b2c956f {
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
}
#women-entrepreneurship .elementor-element-1f766ea {
width: 100% !important;
max-width: 100% !important;
margin-left: auto !important;
margin-right: auto !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
#women-entrepreneurship .elementor-element.elementor-element-2ed47f3 {
padding-left: 0 !important;
padding-right: 0 !important;
margin-left: 0 !important;
margin-right: 0 !important;
margin-top: 0 !important;
padding-top: 0 !important;
gap: 24px !important;
}
#women-entrepreneurship .elementor-element-36efec7 {
margin-top: 0px !important;
padding-top: 0px !important;
gap: 0px !important;
}
#women-entrepreneurship .elementor-element-24c0280 {
margin-bottom: 0px !important;
padding-left: 14px !important;
padding-right: 14px !important;
box-sizing: border-box !important;
}
#women-entrepreneurship .elementor-element-778840d {
margin-top: 14px !important;
margin-bottom: 14px !important;
padding-top: 0px !important;
padding-bottom: 0px !important;
padding-left: 14px !important;
padding-right: 14px !important;
box-sizing: border-box !important;
}
#women-entrepreneurship .elementor-element-778840d h3.logico-title,
#women-entrepreneurship .elementor-element-778840d > .elementor-widget-container {
margin: 0px !important;
padding: 0px !important;
}
#women-entrepreneurship .elementor-element-7da6646 {
width: calc(100% - 28px) !important;
margin-left: 14px !important;
margin-right: 14px !important;
border-radius: 18px !important;
min-height: 240px !important;
margin-top: 15px !important;
margin-bottom: -50px !important;
box-sizing: border-box !important;
}
#women-entrepreneurship .elementor-element-8b5d6e6 {
padding-left: 14px !important;
padding-right: 14px !important;
margin-left: 0px !important;
margin-right: 0px !important;
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box !important;
}
#women-entrepreneurship .elementor-element-bbfb67f img {
width: 100% !important;
}
#women-entrepreneurship .elementor-element-bbfb67f {
margin-top: 0px !important;
margin-bottom: 16px !important;
padding-left: 14px !important;
padding-right: 14px !important;
box-sizing: border-box !important;
}
#ws-stories {
padding-left: 14px !important;
padding-right: 14px !important;
box-sizing: border-box !important;
}
/* KPI stat cards stack one-per-row on mobile. Inside each card the big /* KPI stat cards stack one-per-row on mobile. Inside each card the big
number sits on the left and the label on the right, vertically number sits on the left and the label on the right, vertically
centered, so every stat reads as a clean full-width row instead of centered, so every stat reads as a clean full-width row instead of
@@ -231,7 +371,7 @@ export default function WomenSection() {
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
gap: 16px; gap: 16px;
padding: 20px 22px; padding: 16px 20px;
min-width: 0; min-width: 0;
} }
#ws-stories .ws__stat-num { font-size: clamp(30px, 9vw, 40px); } #ws-stories .ws__stat-num { font-size: clamp(30px, 9vw, 40px); }
@@ -242,7 +382,7 @@ export default function WomenSection() {
text-align: right; text-align: right;
min-height: 0; min-height: 0;
} }
#ws-stories .ws__cards { grid-template-columns: 1fr; } #ws-stories .ws__cards { grid-template-columns: 1fr; gap: 12px; }
} }
`}} /> `}} />
@@ -262,7 +402,7 @@ export default function WomenSection() {
</div> </div>
<div <div
style={{ backgroundImage: "url(/images/bg-header-women.png)" }} style={{ backgroundImage: "url(/images/bg-header-women.webp)" }}
className="elementor-element elementor-element-7da6646 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" className="elementor-element elementor-element-7da6646 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child"
data-id="7da6646" data-id="7da6646"
data-element_type="container" data-element_type="container"
@@ -284,7 +424,7 @@ export default function WomenSection() {
</div> </div>
<div className="elementor-element elementor-element-0d307dd elementor-widget elementor-widget-text-editor" data-id="0d307dd" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default"> <div className="elementor-element elementor-element-0d307dd elementor-widget elementor-widget-text-editor" data-id="0d307dd" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
<div className="elementor-widget-container"> <div className="elementor-widget-container">
<p>Doormile empowers women in last-mile delivery.</p> <p>Doormile empowers women to lead the future of logistics.</p>
</div> </div>
</div> </div>
</div> </div>
@@ -331,7 +471,7 @@ export default function WomenSection() {
<div className="elementor-element elementor-element-bbfb67f elementor-widget elementor-widget-image" data-id="bbfb67f" data-element_type="widget" data-e-type="widget" data-widget_type="image.default"> <div className="elementor-element elementor-element-bbfb67f elementor-widget elementor-widget-image" data-id="bbfb67f" data-element_type="widget" data-e-type="widget" data-widget_type="image.default">
<div className="elementor-widget-container"> <div className="elementor-widget-container">
<Image <Image
src="/images/bg-map-women.png" src="/images/bg-map-women.webp"
alt="Women Map" alt="Women Map"
width={626} width={626}
height={692} height={692}

View File

@@ -1,66 +1,80 @@
"use client"; "use client";
import React from "react"; import React from "react";
import EVSection, { EVStat, EVBadge, EVSlide, EVCardsTheme } from "./EVSection"; import EVSection, { EVStat, EVBadge, EVFeature, EVCardsTheme } from "./EVSection";
import WorkflowScene from "./WorkflowScene"; import WorkflowScene from "./WorkflowScene";
/* Cyan / electric-blue — matches the Optimization Engine scene palette. */ /* Cyan / Teal — matches the Optimization Engine scene palette. */
const THEME: EVCardsTheme = { const THEME: EVCardsTheme = {
accent: "#00E5FF", accent: "#00E5FF",
accent2: "#3B82F6", accent2: "#14B8A6",
glow: "rgba(0,229,255,0.22)", glow: "rgba(0,229,255,0.18)",
}; };
/** /**
* Workflow 1 — Performance (hybrid split-screen). * Workflow 1 — Performance (hybrid split-screen).
* *
* Keeps the premium EVSection chrome (banner → floating card → dark section → * • Left — the PRODUCTION Optimization Engine Three.js scene (depot, trucks,
* stat bar) but converts the body into a split layout: * route optimization, shaders, particles).
* • Left — the PRODUCTION Optimization Engine Three.js scene (the same * • Right — a COMPACT DASHBOARD: a quick KPI row over short feature cards,
* OptimizationCanvas used by OptimizationSection: depot, trucks, * sitting on the animated network backdrop (cyan/teal). Surfaces the
* route optimization, shaders, particles). One instance, mounted * key metrics fast, keeps copy short and reads well on mobile.
* compactly instead of as a multi-viewport pinned scroll.
* • Right — lightweight auto-rotating cards (4s / 600ms fade+slide).
*
* This preserves the 3D storytelling while dramatically cutting page height.
*/ */
const SLIDES: EVSlide[] = [ const METRICS: EVStat[] = [
{ value: 42, suffix: "%", label: "Distance Saved" },
{ value: 28, suffix: "%", label: "Faster Routes" },
{ value: 31, suffix: "%", label: "Lower Cost" },
{ value: 99.9, decimals: 1, suffix: "%", label: "On-Time" },
];
const ico = (path: React.ReactNode) => (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
{path}
</svg>
);
const FEATURES: EVFeature[] = [
{ {
status: "Optimization Running", icon: ico(<polygon points="3 11 22 2 13 21 11 13 3 11" />),
title: "Route Optimization", title: "Route Optimization",
value: 42, desc: "AI selects the most efficient path across every zone.",
suffix: "%",
metricLabel: "Distance Saved",
kpis: ["Route optimization active", "37% fewer vehicles required", "SLA compliance 99.9%"],
desc: "AI selects the most efficient delivery paths across every zone, cutting unnecessary travel and fuel and battery consumption.",
}, },
{ {
status: "Fleet Balancing", icon: ico(
<>
<polyline points="22 17 13.5 8.5 8.5 13.5 2 7" />
<polyline points="16 17 22 17 22 11" />
</>,
),
title: "Distance Reduction", title: "Distance Reduction",
value: 37, desc: "Same volume delivered with a leaner, better-used fleet.",
suffix: "%",
metricLabel: "Fewer Vehicles",
kpis: ["Load balancing engaged", "Same volume, leaner fleet", "Lower maintenance & staffing"],
desc: "Intelligent load balancing fulfils the same order volume with a leaner, better-utilised fleet — fewer miles, fewer vehicles.",
}, },
{ {
status: "Dispatch Active", icon: ico(
<>
<path d="M12 14l4-4" />
<path d="M3.34 19a10 10 0 1 1 17.32 0" />
</>,
),
title: "Fleet Efficiency", title: "Fleet Efficiency",
value: 31, desc: "Higher utilisation and lower operating cost.",
suffix: "%",
metricLabel: "Lower Operating Cost",
kpis: ["Higher fleet utilisation", "Predictable operations", "Reduced fuel & overhead"],
desc: "Smart grouping and dispatch keep operations smooth and predictable while reducing maintenance and staffing cost.",
}, },
{ {
status: "SLA Safe", icon: ico(
<>
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
<polyline points="9 12 11 14 15 10" />
</>,
),
title: "SLA Performance", title: "SLA Performance",
value: 99.9, desc: "Real-time correction keeps deliveries on time.",
decimals: 1,
suffix: "%",
metricLabel: "On-Time Delivery",
kpis: ["Real-time route correction", "Consistent delivery windows", "100% order fulfilment"],
desc: "Real-time routing keeps deliveries on time across all zones, sustaining high customer satisfaction and SLA performance.",
}, },
]; ];
@@ -69,31 +83,24 @@ const BADGES: EVBadge[] = [
{ value: "-37%", label: "FEWER VEHICLES" }, { value: "-37%", label: "FEWER VEHICLES" },
]; ];
const STATS: EVStat[] = [
{ value: 42, suffix: "%", label: "Distance Saved" },
{ value: 28, suffix: "%", label: "Faster Routes" },
{ value: 31, suffix: "%", label: "Lower Cost" },
{ value: 99.9, decimals: 1, suffix: "%", label: "On-Time" },
];
export default function Workflow1() { export default function Workflow1() {
return ( return (
<EVSection <EVSection
ariaLabel="Workflow 1 — Performance" ariaLabel="Workflow 1 — Performance"
gapTop gapTop
gapBottom gapBottom
bannerImage="/images/home3-slide-1.jpg" bannerImage="/images/mile-1.webp"
cardTitle="OPTIMIZE EVERY MILE" cardTitle="OPTIMIZE EVERY MILE"
cardSubtitle="Cut travel distance, reduce operating cost, and improve fleet productivity across every route." cardSubtitle="Cut travel distance, reduce operating cost, and improve fleet productivity across every route."
eyebrow="/ Performance /" eyebrow="/ Performance /"
titleLead="SMARTER ROUTES. " titleLead="SMARTER ROUTES. "
titleAccent="LOWER COSTS." titleAccent="LOWER COSTS."
mediaSlot={<WorkflowScene variant="optimization" ariaLabel="Live route optimization engine" />} mediaSlot={<WorkflowScene variant="optimization" ariaLabel="Live route optimization engine" />}
slides={SLIDES} metrics={METRICS}
features={FEATURES}
cardsHeading="Performance Insight" cardsHeading="Performance Insight"
cardsTheme={THEME} cardsTheme={THEME}
badges={BADGES} badges={BADGES}
stats={STATS}
/> />
); );
} }

View File

@@ -1,63 +1,81 @@
"use client"; "use client";
import React from "react"; import React from "react";
import EVSection, { EVStat, EVBadge, EVSlide, EVCardsTheme } from "./EVSection"; import EVSection, { EVStat, EVBadge, EVFeature, EVCardsTheme } from "./EVSection";
import WorkflowScene from "./WorkflowScene";
/* Red / crimson / orange — matches the Routing Engine (logistics brain) scene. */ /* Red / Crimson — matches the Routing Engine (logistics brain) scene. */
const THEME: EVCardsTheme = { const THEME: EVCardsTheme = {
accent: "#E2354A", accent: "#E2354A",
accent2: "#F59E0B", accent2: "#C01227",
glow: "rgba(226,53,74,0.24)", glow: "rgba(226,53,74,0.2)",
}; };
/** /**
* Workflow 2 — Innovation (hybrid split-screen). * Workflow 2 — Innovation (hybrid split-screen).
* *
* Keeps the premium EVSection chrome but converts the body into a split layout: * • Left — the PRODUCTION Routing Engine Three.js scene (city nodes,
* • Left — the PRODUCTION Routing Engine Three.js scene (the same * buildings, multi-route generation, constraint evaluation).
* LogisticsBrainCanvas used by LogisticsBrainSection: city nodes, * • Right — a COMPACT DASHBOARD: a quick KPI row over short feature cards,
* buildings, multi-route generation, constraint evaluation, * sitting on the animated network backdrop (red/crimson). Surfaces
* network/brain animation). One instance, mounted compactly. * the key metrics fast and keeps the section tight on mobile.
* • Right — lightweight auto-rotating cards (4s / 600ms fade+slide).
*
* Preserves the 3D storytelling while dramatically cutting page height.
*/ */
const SLIDES: EVSlide[] = [ const METRICS: EVStat[] = [
{ value: 45, suffix: "ms", label: "Inference" },
{ value: 12, suffix: "+", label: "Strategies" },
{ value: 99.9, decimals: 1, suffix: "%", label: "SLA Met" },
{ value: 24, suffix: "/7", label: "Adaptive" },
];
const ico = (path: React.ReactNode) => (
<svg
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
>
{path}
</svg>
);
const FEATURES: EVFeature[] = [
{ {
status: "Generating Routes", icon: ico(
<>
<polygon points="12 2 2 7 12 12 22 7 12 2" />
<polyline points="2 17 12 22 22 17" />
<polyline points="2 12 12 17 22 12" />
</>,
),
title: "Generate Routes", title: "Generate Routes",
value: 6, desc: "Many strategies explored per dispatch window.",
suffix: " plans",
metricLabel: "Route Plans Generated",
kpis: ["Parallel strategies explored", "59 orders in scope", "Real-time combinations"],
desc: "The Parallel Universe Engine evaluates many routing strategies at once for every dispatch window, exploring route combinations in real time.",
}, },
{ {
status: "Constraints Passed", icon: ico(
<>
<path d="M22 11.08V12a10 10 0 1 1-5.93-9.14" />
<polyline points="22 4 12 14.01 9 11.01" />
</>,
),
title: "Check Constraints", title: "Check Constraints",
value: 5, desc: "Battery, capacity, distance and time validated.",
metricLabel: "Constraints Evaluated",
kpis: ["Battery aware", "Capacity & distance checked", "Powered by Google OR-Tools"],
desc: "Battery, distance, capacity and time are first-class inputs — battery-aware simulation solves the EV routing challenge.",
}, },
{ {
status: "Scoring Routes", icon: ico(
<>
<line x1="6" y1="20" x2="6" y2="14" />
<line x1="12" y1="20" x2="12" y2="4" />
<line x1="18" y1="20" x2="18" y2="10" />
</>,
),
title: "Score & Compare", title: "Score & Compare",
value: 12, desc: "Plans ranked by total cost in parallel.",
suffix: "+",
metricLabel: "Strategies Compared",
kpis: ["Ranked by total cost", "SLA protected", "Real-time ETA validation"],
desc: "Every plan is benchmarked in parallel and ranked by total cost, with sub-45ms inference at production scale.",
}, },
{ {
status: "Delivery Ready", icon: ico(<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />),
title: "Select Best Plan", title: "Select Best Plan",
value: 45, desc: "SLA-first plan locked in and dispatched.",
suffix: "ms",
metricLabel: "Decision Latency",
kpis: ["Late plans rejected", "Best plan locked in", "Dispatched to the fleet"],
desc: "Late plans are rejected automatically and the highest-performing, SLA-first plan is locked in and dispatched.",
}, },
]; ];
@@ -66,30 +84,24 @@ const BADGES: EVBadge[] = [
{ value: "100%", label: "SLA-FIRST" }, { value: "100%", label: "SLA-FIRST" },
]; ];
const STATS: EVStat[] = [
{ value: 45, suffix: "ms", label: "Inference" },
{ value: 12, suffix: "+", label: "Strategies" },
{ value: 99.9, decimals: 1, suffix: "%", label: "SLA Met" },
{ value: 24, suffix: "/7", label: "Adaptive" },
];
export default function Workflow2() { export default function Workflow2() {
return ( return (
<EVSection <EVSection
ariaLabel="Workflow 2 — Innovation" ariaLabel="Workflow 2 — Innovation"
gapBottom gapBottom
bannerImage="/images/mid-mile-approach.jpg" bannerImage="/images/mid-mile-approach.webp"
cardTitle="CHOOSE THE BEST PLAN" cardTitle="CHOOSE THE BEST PLAN"
cardSubtitle="Analyze thousands of route possibilities and automatically select the most efficient delivery strategy." cardSubtitle="Analyze thousands of route possibilities and automatically select the most efficient delivery strategy."
eyebrow="/ Innovation /" eyebrow="/ Innovation /"
titleLead="MANY STRATEGIES. " titleLead="MANY STRATEGIES. "
titleAccent="ONE BEST PLAN." titleAccent="ONE BEST PLAN."
mediaSlot={<WorkflowScene variant="logistics" ariaLabel="Live multi-route logistics brain" />} image="/videos/workflow-2-routing.mp4"
slides={SLIDES} imageAlt="AI route-planning engine selecting the best delivery plan"
metrics={METRICS}
features={FEATURES}
cardsHeading="AI Decision Engine" cardsHeading="AI Decision Engine"
cardsTheme={THEME} cardsTheme={THEME}
badges={BADGES} badges={BADGES}
stats={STATS}
/> />
); );
} }

View File

@@ -1,282 +1,38 @@
"use client"; "use client";
import React, { useState, useEffect, useRef } from "react"; import React from "react";
import { motion, AnimatePresence } from "framer-motion";
import StrategySection from "../strategy/StrategySection"; import StrategySection from "../strategy/StrategySection";
/**
* Workflow 3 — the "Happier Riders. Higher Fulfillment." 3D scroll-storytelling
* experience (StrategySection).
*
* The old bottom "STRATEGY" explanation card (heading + description + pagination
* + chevron graphic) has been removed: the workflow narrative is already covered
* by the previous sections, so that card only repeated it and lengthened the
* page. StrategySection now renders standalone (non-connected) with its own
* rounded card. The wrapper only cancels the global `section` padding so the
* tall pinned section doesn't gain empty bands.
*/
export default function Workflow3() { export default function Workflow3() {
const [activeSlide, setActiveSlide] = useState(0);
const [paused, setPaused] = useState(false);
const [inView, setInView] = useState(false);
const cardRef = useRef<HTMLDivElement>(null);
const slides = [
{
title: "STRATEGY",
text: "Our grading engine continuously evaluates fulfillment performance, SLA compliance, and route efficiency before every dispatch. By comparing legacy routing methods with unified optimization, the system ensures smarter and more reliable delivery planning. This helps businesses maintain operational consistency while improving overall delivery performance."
},
{
title: "STRATEGY",
text: "Every EV route is pre-validated against real battery capacity and charging feasibility before a rider leaves the hub. This reduces the risk of delivery interruptions, charging failures, or delayed orders during operations. The platform ensures reliable route execution while maximizing EV fleet efficiency and rider confidence."
},
{
title: "STRATEGY",
text: "The system provides actionable fleet insights and optimized workload distribution to improve both rider experience and operational productivity. Balanced route allocation helps reduce rider fatigue, improve retention, and maintain consistent delivery quality across zones. Managers gain better visibility into fleet performance, enabling faster and more informed decision-making."
}
];
// Always begin on slide 1 (01/03) on mount. Scrolling away and back does NOT reset
// (the component stays mounted) — only a fresh page load / route change back to
// MileTruth re-mounts and restarts at slide 1.
useEffect(() => {
setActiveSlide(0);
}, []);
// Autoplay is gated on visibility: it starts only once the slider card scrolls into
// view (not on page load) and stops when it leaves — without touching activeSlide,
// so returning to the section resumes from wherever it was, never snapping to slide 1.
useEffect(() => {
const el = cardRef.current;
if (!el) return;
const io = new IntersectionObserver(
([entry]) => setInView(entry.isIntersecting),
{ threshold: 0.35 }
);
io.observe(el);
return () => io.disconnect();
}, []);
// Auto-advance every 10s, looping — but only while the card is in view and the user
// isn't hovering it. Keyed on activeSlide so a manual jump restarts the 10s dwell.
useEffect(() => {
if (!inView || paused) return;
const id = setTimeout(() => {
setActiveSlide((prev) => (prev + 1) % slides.length);
}, 10000);
return () => clearTimeout(id);
}, [activeSlide, inView, paused, slides.length]);
return ( return (
<section className="dm-wf3" aria-label="Workflow 3 — Happier Riders. Higher Fulfillment. & Strategy"> <section
className="dm-wf3"
aria-label="Workflow 3 — Happier Riders. Higher Fulfillment."
>
<StrategySection />
{/* ── Top sub-section: the full "Happier Riders. Higher Fulfillment." <style
3D scroll-storytelling experience ── */} dangerouslySetInnerHTML={{
<StrategySection connected /> __html: `
.dm-wf3 { position: relative; margin: 0 auto; }
{/* ── Bottom sub-section: Strategy content, flush + pulled up to butt against /* Cancel the global "section { padding: 6rem 0 }": both this wrapper
the 3D card's flat bottom so the whole workflow reads as one container — and the nested .dm-st are sections, so that padding would stack into
the same connected structure used in Workflow 1 & 2 ── */} large empty bands around the pinned 3D experience. */
<div className="dm-wf3-card" ref={cardRef} onMouseEnter={() => setPaused(true)} onMouseLeave={() => setPaused(false)}> .dm-wf3, .dm-wf3 .dm-st { padding-top: 0; padding-bottom: 0; }
{/* Left Column: Overlapping Chevron Graphic */} `,
<div className="dm-workflow-left"> }}
<svg viewBox="0 0 320 280" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-svg"> />
<path
d="M 30,20 C 22,20 16,26 16,34 L 78,85 C 81,88 81,92 78,95 L 16,146 C 16,154 22,160 30,160 L 130,160 C 138,160 145,154 148,146 L 204,95 C 207,92 207,88 204,85 L 148,34 C 145,26 138,20 130,20 Z"
stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.25"
/>
<path
d="M 110,100 C 102,100 96,106 96,114 L 158,165 C 161,168 161,172 158,175 L 96,226 C 96,234 102,240 110,240 L 210,240 C 218,240 225,234 228,226 L 284,175 C 287,172 287,168 284,165 L 228,114 C 225,106 218,100 210,100 Z"
stroke="white" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" opacity="0.85"
/>
</svg>
</div>
{/* Right Column: Quotes & Text Content */}
<div className="dm-workflow-right">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg" className="dm-workflow-quote">
<rect x="2" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
<rect x="16" y="2" width="9" height="20" rx="1.5" transform="skewX(-12)" fill="#C01227" />
</svg>
<h3 className="dm-workflow-title">{slides[activeSlide].title}</h3>
<div className="dm-workflow-text-container">
<AnimatePresence mode="wait">
<motion.p
key={activeSlide}
initial={{ opacity: 0, y: 12 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -12 }}
transition={{ duration: 0.7, ease: "easeInOut" }}
className="dm-workflow-text"
>
{slides[activeSlide].text}
</motion.p>
</AnimatePresence>
</div>
<div className="dm-workflow-nav">
<span className="dm-workflow-counter">0{activeSlide + 1}/03</span>
<div className="dm-workflow-bars">
{slides.map((_, index) => (
<button
key={index}
type="button"
aria-label={`Go to slide ${index + 1}`}
className={`dm-workflow-bar ${index === activeSlide ? "is-active" : ""}`}
onClick={() => setActiveSlide(index)}
/>
))}
</div>
</div>
</div>
</div>
<style dangerouslySetInnerHTML={{ __html: styles }} />
</section> </section>
); );
} }
const styles = `
/* ============================================================
Workflow 3 = ONE container:
├─ Happier Riders. Higher Fulfillment. (full StrategySection — 3D)
└─ Strategy (content card, flush, pulled up)
The Strategy card aligns to the 3D card's 20px side insets, butts against
its flat bottom and rounds the bottom corners, so the two read as a single
continuous container — same technique as Workflow 1 & 2.
============================================================ */
.dm-wf3 {
position: relative;
margin: 0 auto 0;
}
/* Cancel the global "section { padding: 6rem 0 }" (consolidated into /public/css/site.css): both
this wrapper and the nested .dm-st are sections, so that 96px top+bottom stacked
into large empty bands above / between the workflows. These are full-bleed pinned
experiences whose cards butt together via their own insets — no section padding. */
.dm-wf3, .dm-wf3 .dm-st { padding-top: 0; padding-bottom: 0; }
.dm-wf3-card {
position: relative;
z-index: 2;
margin: 0 20px 0;
background: #181818;
border: 1px solid rgba(255, 255, 255, 0.06);
border-top: none;
border-radius: 0 0 28px 28px;
/* No shadow: this card is flush under the strategy 3D card and merges with it as one
continuous container — a shadow here would re-introduce a dark band at the seam. */
box-shadow: none;
padding: 36px 60px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 40px;
overflow: hidden;
box-sizing: border-box;
}
.dm-workflow-left {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
max-width: 440px;
}
.dm-workflow-svg {
width: 100%;
height: auto;
filter: drop-shadow(0 8px 24px rgba(0,0,0,0.3));
}
.dm-workflow-right {
flex: 1.2;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 20px;
}
.dm-workflow-quote { margin-bottom: 5px; }
.dm-workflow-title {
font-family: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif;
font-size: 38px;
font-weight: 700;
color: #F8FAFC !important;
letter-spacing: -0.015em;
margin: 0 !important;
padding: 0 !important;
text-transform: uppercase;
}
.dm-workflow-text-container { min-height: 150px; width: 100%; }
.dm-workflow-text {
font-family: var(--font-manrope), system-ui, sans-serif;
font-size: 21px;
line-height: 1.75;
letter-spacing: 0.01em;
color: #A3A3A3;
margin: 0 !important;
padding: 0 !important;
}
.dm-workflow-nav {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 10px;
align-self: flex-end;
margin-top: 10px;
}
.dm-workflow-counter {
font-family: var(--font-space-grotesk), sans-serif;
font-size: 13px;
font-weight: 700;
color: #737373;
letter-spacing: 0.08em;
}
.dm-workflow-bars { display: flex; gap: 8px; }
.dm-workflow-bar {
width: 40px;
height: 3px;
border: none;
padding: 0;
background: rgba(255, 255, 255, 0.15);
border-radius: 999px;
cursor: pointer;
transition: all 0.3s ease;
}
.dm-workflow-bar.is-active { background: #C01227; }
.dm-workflow-bar:hover { background: rgba(255, 255, 255, 0.35); }
.dm-workflow-bar.is-active:hover { background: #C01227; }
/* ── Responsive — keep insets/radius aligned to the 3D card ── */
@media (max-width: 1024px) {
.dm-wf3-card { padding: 44px 44px; gap: 44px; }
.dm-workflow-title { font-size: 32px; }
.dm-workflow-text { font-size: 19px; }
}
@media (max-width: 767px) {
/* Mobile: compact card so it never exceeds ~500px (was ~850px from the full
desktop chevron + long paragraph). Smaller chevron, tighter spacing and a
line-clamped paragraph keep the workflow state readable without a long scroll. */
.dm-wf3-card {
/* Bottom gap separates this last workflow card from the contact section below. */
margin: 0 10px 16px;
border-radius: 0 0 20px 20px;
padding: 26px 22px;
gap: 16px;
flex-direction: column;
}
.dm-workflow-left { max-width: 128px; }
.dm-workflow-right { width: 100%; gap: 12px; }
.dm-workflow-quote { margin-bottom: 2px; }
.dm-workflow-title { font-size: 22px; }
.dm-workflow-text-container { min-height: auto; }
.dm-workflow-text {
font-size: 15px;
line-height: 1.5;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
overflow: hidden;
}
.dm-workflow-nav { margin-top: 4px; }
}
@media (max-width: 390px) {
.dm-workflow-left { max-width: 108px; }
.dm-workflow-title { font-size: 20px; }
.dm-workflow-text { font-size: 14px; -webkit-line-clamp: 4; }
}
`;

View File

@@ -2,7 +2,7 @@
import React, { Suspense, useEffect, useMemo, useRef, useState } from "react"; import React, { Suspense, useEffect, useMemo, useRef, useState } from "react";
import { Canvas, useFrame, useThree } from "@react-three/fiber"; import { Canvas, useFrame, useThree } from "@react-three/fiber";
import { RoundedBox, Line, Sparkles, Html, useGLTF, ContactShadows, Environment, Lightformer, Preload } from "@react-three/drei"; import { RoundedBox, Line, Sparkles, Html, useGLTF, useProgress, ContactShadows, Environment, Lightformer, Preload } from "@react-three/drei";
import { EffectComposer, Bloom } from "@react-three/postprocessing"; import { EffectComposer, Bloom } from "@react-three/postprocessing";
import { KernelSize } from "postprocessing"; import { KernelSize } from "postprocessing";
import * as THREE from "three"; import * as THREE from "three";
@@ -14,6 +14,8 @@ type Props = {
isMobile?: boolean; isMobile?: boolean;
active?: boolean; active?: boolean;
stage?: number; stage?: number;
ready?: boolean;
onReady?: () => void;
}; };
const BG = "#eef1f6"; const BG = "#eef1f6";
@@ -785,7 +787,50 @@ function Floor() {
); );
} }
function Scene({ progress, reduced, isMobile, stage, active, perf }: { progress: React.RefObject<number>; reduced: boolean; isMobile: boolean; stage: number; active: boolean; perf: boolean }) { function SceneReadySignal({ onReady }: { onReady?: () => void }) {
const { active, progress } = useProgress();
const gl = useThree((s) => s.gl);
const scene = useThree((s) => s.scene);
const camera = useThree((s) => s.camera);
const fired = useRef(false);
const compiling = useRef(false);
const compiled = useRef(false);
const meaningfulFrames = useRef(0);
useEffect(() => {
if (fired.current || compiling.current || compiled.current || active || progress < 100) return;
compiling.current = true;
const renderer = gl as THREE.WebGLRenderer & {
compileAsync?: (scene: THREE.Scene, camera: THREE.Camera) => Promise<void>;
};
const compile = renderer.compileAsync
? renderer.compileAsync(scene, camera)
: Promise.resolve(renderer.compile(scene, camera));
compile
.catch(() => undefined)
.finally(() => {
compiled.current = true;
});
}, [active, camera, gl, progress, scene]);
useFrame(() => {
if (fired.current || active || progress < 100 || !compiled.current) return;
const renderInfo = gl.info.render;
const hasMeaningfulScene = renderInfo.calls >= 12 && renderInfo.triangles >= 500;
meaningfulFrames.current = hasMeaningfulScene ? meaningfulFrames.current + 1 : 0;
if (meaningfulFrames.current < 4) return;
fired.current = true;
requestAnimationFrame(() => onReady?.());
});
return null;
}
function Scene({ progress, reduced, isMobile, stage, active, perf, onReady }: { progress: React.RefObject<number>; reduced: boolean; isMobile: boolean; stage: number; active: boolean; perf: boolean; onReady?: () => void }) {
// Districts stay MOUNTED for the whole scroll (GLB cloned once, shaders compiled // Districts stay MOUNTED for the whole scroll (GLB cloned once, shaders compiled
// once → no remount hitch, no blank frames). `near` only toggles per-district // once → no remount hitch, no blank frames). `near` only toggles per-district
// visibility + per-frame work + <Html> mounting, so off-screen districts render // visibility + per-frame work + <Html> mounting, so off-screen districts render
@@ -844,12 +889,13 @@ function Scene({ progress, reduced, isMobile, stage, active, perf }: { progress:
{/* Compile every material/texture upfront (districts are all mounted) so the {/* Compile every material/texture upfront (districts are all mounted) so the
first time a district becomes visible there's no shader-compile stall. */} first time a district becomes visible there's no shader-compile stall. */}
<Preload all /> <Preload all />
<SceneReadySignal onReady={onReady} />
{perf && <PerfHud />} {perf && <PerfHud />}
</> </>
); );
} }
export default function StrategyCanvas({ progress, reduced = false, isMobile = false, active = true, stage = 0 }: Props) { export default function StrategyCanvas({ progress, reduced = false, isMobile = false, active = true, stage = 0, ready = false, onReady }: Props) {
// Opt-in dev metrics overlay: append ?perf to the URL. // Opt-in dev metrics overlay: append ?perf to the URL.
const perf = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("perf"); const perf = typeof window !== "undefined" && new URLSearchParams(window.location.search).has("perf");
return ( return (
@@ -857,9 +903,9 @@ export default function StrategyCanvas({ progress, reduced = false, isMobile = f
dpr={[1, isMobile ? 1 : 1.25]} dpr={[1, isMobile ? 1 : 1.25]}
camera={{ position: [0, 4, 8.2], fov: 50, near: 0.1, far: 120 }} camera={{ position: [0, 4, 8.2], fov: 50, near: 0.1, far: 120 }}
gl={{ antialias: false, powerPreference: "high-performance", alpha: false }} gl={{ antialias: false, powerPreference: "high-performance", alpha: false }}
frameloop={active ? "always" : "never"} frameloop={active || !ready ? "always" : "never"}
> >
<Scene progress={progress} reduced={reduced} isMobile={isMobile} stage={stage} active={active} perf={perf} /> <Scene progress={progress} reduced={reduced} isMobile={isMobile} stage={stage} active={active} perf={perf} onReady={onReady} />
</Canvas> </Canvas>
); );
} }

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import React, { useEffect, useRef, useState, useSyncExternalStore } from "react"; import React, { useEffect, useRef, useState } from "react";
import dynamic from "next/dynamic"; import dynamic from "next/dynamic";
import { motion, useMotionValue, useTransform, type MotionValue } from "framer-motion"; import { motion, useMotionValue, useTransform, type MotionValue } from "framer-motion";
import gsap from "gsap"; import gsap from "gsap";
@@ -83,6 +83,8 @@ export default function StrategySection({ connected = false }: { connected?: boo
const [pinState, setPinState] = useState<"before" | "pinned" | "after">("before"); const [pinState, setPinState] = useState<"before" | "pinned" | "after">("before");
const [active, setActive] = useState(0); const [active, setActive] = useState(0);
const [mountScene, setMountScene] = useState(false); const [mountScene, setMountScene] = useState(false);
const [sceneReady, setSceneReady] = useState(false);
const [showLoader, setShowLoader] = useState(true);
const [sceneActive, setSceneActive] = useState(false); const [sceneActive, setSceneActive] = useState(false);
const [isMobile, setIsMobile] = useState(false); const [isMobile, setIsMobile] = useState(false);
const [reduced, setReduced] = useState(false); const [reduced, setReduced] = useState(false);
@@ -161,8 +163,30 @@ export default function StrategySection({ connected = false }: { connected?: boo
<div className="dm-st-sticky"> <div className="dm-st-sticky">
<div className="dm-st-card"> <div className="dm-st-card">
{mountScene && ( {mountScene && (
<div className="dm-st-canvas"> <div className={`dm-st-canvas${sceneReady ? " is-ready" : ""}`} aria-hidden={!sceneReady}>
<StrategyCanvas progress={progressRef} reduced={reduced} isMobile={isMobile} active={sceneActive && pinState === "pinned"} stage={active} /> <StrategyCanvas
progress={progressRef}
reduced={reduced}
isMobile={isMobile}
active={sceneActive && pinState === "pinned"}
stage={active}
ready={sceneReady}
onReady={() => setSceneReady(true)}
/>
</div>
)}
{showLoader && (
<div
className={`dm-st-loader${sceneReady ? " is-hiding" : ""}`}
role="status"
aria-live="polite"
aria-label="Loading MileTruth Strategy Engine"
onTransitionEnd={(e) => {
if (e.propertyName === "opacity" && sceneReady) setShowLoader(false);
}}
>
<span className="dm-st-loader__ring" />
</div> </div>
)} )}
@@ -170,7 +194,7 @@ export default function StrategySection({ connected = false }: { connected?: boo
can never be seen during the approach ("before"), where the sticky sits can never be seen during the approach ("before"), where the sticky sits
at the top of the tall section near the previous workflow's seam. */} at the top of the tall section near the previous workflow's seam. */}
{pinState !== "before" && ( {pinState !== "before" && (
<div className="dm-st-ui"> <div className={`dm-st-ui${sceneReady ? " is-ready" : ""}`} aria-hidden={!sceneReady}>
{/* Persistent header */} {/* Persistent header */}
<motion.div className="dm-st-top" style={{ opacity: headerOpacity }}> <motion.div className="dm-st-top" style={{ opacity: headerOpacity }}>
<div className="dm-st-eyebrow"><span className="dm-st-dot" /> MileTruth Strategy Engine</div> <div className="dm-st-eyebrow"><span className="dm-st-dot" /> MileTruth Strategy Engine</div>
@@ -285,11 +309,28 @@ const styles = `
} }
} }
.dm-st-canvas { position: absolute; inset: 0; z-index: 1; } .dm-st-canvas { position: absolute; inset: 0; z-index: 1; opacity: 0; visibility: hidden;
transition: opacity 0.42s cubic-bezier(0.22,1,0.36,1), visibility 0s linear 0.42s; }
.dm-st-canvas.is-ready { opacity: 1; visibility: visible; transition-delay: 0s; }
.dm-st-canvas canvas { display: block; } .dm-st-canvas canvas { display: block; }
.dm-st-ui { position: absolute; inset: 0; z-index: 4; pointer-events: none; .dm-st-ui { position: absolute; inset: 0; z-index: 4; pointer-events: none;
font-family: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif; color: #0f172a; } font-family: var(--font-space-grotesk), var(--font-manrope), system-ui, sans-serif; color: #0f172a;
opacity: 0; visibility: hidden;
transition: opacity 0.42s cubic-bezier(0.22,1,0.36,1), visibility 0s linear 0.42s; }
.dm-st-ui.is-ready { opacity: 1; visibility: visible; transition-delay: 0s; }
.dm-st-loader { position: absolute; inset: 0; z-index: 6; display: grid; place-items: center;
background: transparent; opacity: 1; pointer-events: none;
animation: dmStLoaderFadeIn 0.2s ease both;
transition: opacity 0.3s ease; }
.dm-st-loader.is-hiding { opacity: 0; pointer-events: none; }
.dm-st-loader__ring { width: 18px; height: 18px; border-radius: 50%;
border: 2px solid rgba(255,255,255,0.48); border-top-color: #ffffff;
box-shadow: 0 8px 22px rgba(15,23,42,0.14);
animation: dm-hiw-spin 0.8s linear infinite; }
@keyframes dmStLoaderFadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes dm-hiw-spin { to { transform: rotate(360deg); } }
/* ---- Persistent header: title + 5-stage rail ---- */ /* ---- Persistent header: title + 5-stage rail ---- */
.dm-st-top { position: absolute; top: clamp(96px, 13vh, 128px); left: 0; right: 0; z-index: 5; .dm-st-top { position: absolute; top: clamp(96px, 13vh, 128px); left: 0; right: 0; z-index: 5;

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