Compare commits
30 Commits
b2d64bd335
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 09082a12a5 | |||
| 1286045a21 | |||
| 23d9108436 | |||
| ddbf352b33 | |||
| 635bd4e7ba | |||
| 0560b86b87 | |||
| 205924e057 | |||
| 2bc01b5952 | |||
| ba34c80761 | |||
| f412b9f71e | |||
| 1e6653de96 | |||
| 10d73b6d31 | |||
| d56e710e28 | |||
| c4722a6c99 | |||
| 86207fee86 | |||
| 8c85a11698 | |||
|
|
45b4e7a109 | ||
| 0ef51540e9 | |||
| 3d53f82e7b | |||
| e93785f2b6 | |||
| 263e03937f | |||
| bbe8f0d92b | |||
| 8862ad2cb3 | |||
| a16d51f2fa | |||
| ab67fec091 | |||
| 91841ba3f4 | |||
| 2f23f16634 | |||
| 7fb97a9ca6 | |||
| 3a16bf9267 | |||
| d5987b5dd1 |
0
3d_scene_final.jsx
Normal file
@@ -14,6 +14,9 @@ const eslintConfig = defineConfig([
|
||||
"next-env.d.ts",
|
||||
// Vendored third-party JS shipped to /public is not ours to lint.
|
||||
"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/**",
|
||||
]),
|
||||
]);
|
||||
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
import type { NextConfig } from "next";
|
||||
import path from "path";
|
||||
|
||||
const nextConfig: NextConfig = {
|
||||
turbopack: {
|
||||
root: path.resolve(__dirname),
|
||||
},
|
||||
output: "export",
|
||||
// Required by the How It Works 3D experience. React StrictMode double-invokes
|
||||
// mount/effects in dev, which tears down and re-creates the WebGL context of
|
||||
// the heavy 32MB scene mid-initialization — the context is lost ("THREE.
|
||||
// WebGLRenderer: Context Lost") and the canvas stays blank. This is a known
|
||||
// React-Three-Fiber + StrictMode incompatibility. Disabling it is a DEV-ONLY
|
||||
// change (production never runs StrictMode's double-mount) and does not affect
|
||||
// any other page's runtime behavior.
|
||||
reactStrictMode: false,
|
||||
images: {
|
||||
unoptimized: true,
|
||||
formats: ["image/avif", "image/webp"],
|
||||
},
|
||||
};
|
||||
|
||||
118
package-lock.json
generated
@@ -8,26 +8,34 @@
|
||||
"name": "doormile-next",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@emailjs/browser": "^4.4.1",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.6.1",
|
||||
"@react-three/postprocessing": "^3.0.4",
|
||||
"framer-motion": "^12.40.0",
|
||||
"gsap": "^3.15.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"lenis": "^1.3.23",
|
||||
"maath": "^0.10.8",
|
||||
"next": "16.2.6",
|
||||
"nodemailer": "^9.0.0",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"three": "^0.171.0"
|
||||
"react-leaflet": "^5.0.0",
|
||||
"three": "0.171.0",
|
||||
"zustand": "^5.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/node": "^20",
|
||||
"@types/nodemailer": "^8.0.1",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/three": "^0.171.0",
|
||||
"@types/three": "^0.184.0",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.2.6",
|
||||
"jest": "^30.4.2",
|
||||
@@ -722,6 +730,21 @@
|
||||
"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": {
|
||||
"version": "4.4.1",
|
||||
"resolved": "https://registry.npmjs.org/@emailjs/browser/-/browser-4.4.1.tgz",
|
||||
"integrity": "sha512-DGSlP9sPvyFba3to2A50kDtZ+pXVp/0rhmqs2LmbMS3I5J8FSOgLwzY2Xb4qfKlOVHh29EAutLYwe5yuEZmEFg==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emnapi/core": {
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz",
|
||||
@@ -2267,6 +2290,17 @@
|
||||
"url": "https://opencollective.com/pkgr"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-leaflet/core": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-3.0.0.tgz",
|
||||
"integrity": "sha512-3EWmekh4Nz+pGcr+xjf0KNyYfC3U2JjnkWsh0zcqaexYqmmB5ZhH37kz41JXGmKzpaMZCnPofBBm64i+YrEvGQ==",
|
||||
"license": "Hippocratic-2.1",
|
||||
"peerDependencies": {
|
||||
"leaflet": "^1.9.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-three/drei": {
|
||||
"version": "10.7.7",
|
||||
"resolved": "https://registry.npmjs.org/@react-three/drei/-/drei-10.7.7.tgz",
|
||||
@@ -2893,6 +2927,13 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/geojson": {
|
||||
"version": "7946.0.16",
|
||||
"resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz",
|
||||
"integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
|
||||
@@ -2986,6 +3027,16 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/leaflet": {
|
||||
"version": "1.9.21",
|
||||
"resolved": "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.9.21.tgz",
|
||||
"integrity": "sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/geojson": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.19.41",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.41.tgz",
|
||||
@@ -2996,6 +3047,16 @@
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/nodemailer": {
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-8.0.1.tgz",
|
||||
"integrity": "sha512-PxpaInm8V1JQDd4j0ds5HfvWQk8JupS1C0Picb96QJsrrRDjBH+DlK7L4ZdNSqNULhiZRQHc40nLVShaGxXAMw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/offscreencanvas": {
|
||||
"version": "2019.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/offscreencanvas/-/offscreencanvas-2019.7.3.tgz",
|
||||
@@ -3044,17 +3105,17 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/three": {
|
||||
"version": "0.171.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.171.0.tgz",
|
||||
"integrity": "sha512-oLuT1SAsT+CUg/wxUTFHo0K3NtJLnx9sJhZWQJp/0uXqFpzSk1hRHmvWvpaAWSfvx2db0lVKZ5/wV0I0isD2mQ==",
|
||||
"version": "0.184.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/three/-/three-0.184.1.tgz",
|
||||
"integrity": "sha512-6q4VdiqVsrTRqmk62/BnlcAvIrnDM0zf2ZDVKI5kZiniWrSaOHaQzmbp+BNzoggc/8tgW412pL//wZIxu2PPTA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@dimforge/rapier3d-compat": "~0.12.0",
|
||||
"@tweenjs/tween.js": "~23.1.3",
|
||||
"@types/stats.js": "*",
|
||||
"@types/webxr": "*",
|
||||
"@webgpu/types": "*",
|
||||
"@types/webxr": ">=0.5.17",
|
||||
"fflate": "~0.8.2",
|
||||
"meshoptimizer": "~0.18.1"
|
||||
"meshoptimizer": "~1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/tough-cookie": {
|
||||
@@ -3720,12 +3781,6 @@
|
||||
"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": {
|
||||
"version": "8.16.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
|
||||
@@ -8215,6 +8270,12 @@
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/leaflet": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz",
|
||||
"integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==",
|
||||
"license": "BSD-2-Clause"
|
||||
},
|
||||
"node_modules/lenis": {
|
||||
"version": "1.3.23",
|
||||
"resolved": "https://registry.npmjs.org/lenis/-/lenis-1.3.23.tgz",
|
||||
@@ -8707,9 +8768,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/meshoptimizer": {
|
||||
"version": "0.18.1",
|
||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz",
|
||||
"integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-1.1.1.tgz",
|
||||
"integrity": "sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/micromatch": {
|
||||
@@ -8969,6 +9030,15 @@
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/nodemailer": {
|
||||
"version": "9.0.0",
|
||||
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-9.0.0.tgz",
|
||||
"integrity": "sha512-tbPTid7d/p9jAA8CRZ3iomvrMaST0o6NYuY7v6JQZHpPRZ61mLFSPKYd7342NtOFuej9/+L48SOIxwfu2uDvtw==",
|
||||
"license": "MIT-0",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-path": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||
@@ -9652,6 +9722,20 @@
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/react-leaflet": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-5.0.0.tgz",
|
||||
"integrity": "sha512-CWbTpr5vcHw5bt9i4zSlPEVQdTVcML390TjeDG0cK59z1ylexpqC6M1PJFjV8jD7CF+ACBFsLIDs6DRMoLEofw==",
|
||||
"license": "Hippocratic-2.1",
|
||||
"dependencies": {
|
||||
"@react-leaflet/core": "^3.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"leaflet": "^1.9.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-use-measure": {
|
||||
"version": "2.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-use-measure/-/react-use-measure-2.1.7.tgz",
|
||||
|
||||
13
package.json
@@ -5,6 +5,7 @@
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
"build:css": "bash scripts/build-css.sh",
|
||||
"start": "next start",
|
||||
"lint": "eslint",
|
||||
"test": "jest",
|
||||
@@ -12,26 +13,34 @@
|
||||
"test:coverage": "jest --coverage"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emailjs/browser": "^4.4.1",
|
||||
"@react-three/drei": "^10.7.7",
|
||||
"@react-three/fiber": "^9.6.1",
|
||||
"@react-three/postprocessing": "^3.0.4",
|
||||
"framer-motion": "^12.40.0",
|
||||
"gsap": "^3.15.0",
|
||||
"leaflet": "^1.9.4",
|
||||
"lenis": "^1.3.23",
|
||||
"maath": "^0.10.8",
|
||||
"next": "16.2.6",
|
||||
"nodemailer": "^9.0.0",
|
||||
"react": "19.2.4",
|
||||
"react-dom": "19.2.4",
|
||||
"three": "^0.171.0"
|
||||
"react-leaflet": "^5.0.0",
|
||||
"three": "0.171.0",
|
||||
"zustand": "^5.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@types/jest": "^30.0.0",
|
||||
"@types/leaflet": "^1.9.21",
|
||||
"@types/node": "^20",
|
||||
"@types/nodemailer": "^8.0.1",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@types/three": "^0.171.0",
|
||||
"@types/three": "^0.184.0",
|
||||
"eslint": "^9",
|
||||
"eslint-config-next": "16.2.6",
|
||||
"jest": "^30.4.2",
|
||||
|
||||
4622
public/css/custom-frontend.min.css
vendored
@@ -1,188 +0,0 @@
|
||||
/* ── Blog page hero ── */
|
||||
.blog-hero {
|
||||
background: linear-gradient(160deg, #0c0c14 0%, #18050a 100%);
|
||||
padding: 120px 0 60px;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.blog-hero::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(ellipse 60% 50% at 50% 60%, rgba(192,18,39,0.14) 0%, transparent 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.blog-hero-inner {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 32px;
|
||||
}
|
||||
.blog-hero-eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 3.5px;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255,255,255,0.35);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.blog-hero-eyebrow::before,
|
||||
.blog-hero-eyebrow::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 28px;
|
||||
height: 1px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
}
|
||||
.blog-hero h1 {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: clamp(28px, 4.5vw, 52px);
|
||||
font-weight: 900;
|
||||
color: #ffffff !important;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -1.5px;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
.blog-hero h1 span { color: #c01227; }
|
||||
.blog-hero p {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 15px;
|
||||
color: rgba(255,255,255,0.48);
|
||||
line-height: 1.7;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ── Blog grid section ── */
|
||||
.blog-section {
|
||||
background: #f8fafc;
|
||||
padding: 80px 0 100px;
|
||||
}
|
||||
.blog-container {
|
||||
max-width: 1280px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
/* ── Blog grid ── */
|
||||
.dm-blog-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 32px;
|
||||
}
|
||||
|
||||
/* ── Blog card ── */
|
||||
.dm-blog-card {
|
||||
background: #fff;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
border: 1px solid rgba(0,0,0,0.07);
|
||||
box-shadow: 0 4px 24px rgba(0,0,0,0.05);
|
||||
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.4s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.dm-blog-card:hover {
|
||||
transform: translateY(-12px) scale(1.02);
|
||||
box-shadow: 0 20px 50px rgba(0,0,0,0.15), 0 0 20px rgba(192, 18, 39, 0.1);
|
||||
}
|
||||
.dm-blog-card-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 4/3;
|
||||
overflow: hidden;
|
||||
background: #eee;
|
||||
}
|
||||
.dm-blog-card-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
transition: transform 0.6s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
}
|
||||
.dm-blog-card:hover .dm-blog-card-image img {
|
||||
transform: scale(1.04);
|
||||
}
|
||||
.dm-blog-card-body {
|
||||
padding: 20px 22px 24px;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.dm-blog-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.dm-blog-category {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1.8px;
|
||||
color: #c01227;
|
||||
background: rgba(192,18,39,0.07);
|
||||
border: 1px solid rgba(192,18,39,0.15);
|
||||
border-radius: 100px;
|
||||
padding: 3px 9px;
|
||||
text-decoration: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.dm-blog-date {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
color: #94a3b8;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
.dm-blog-card h3 {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
color: #111827;
|
||||
line-height: 1.4;
|
||||
letter-spacing: -0.2px;
|
||||
margin: 0 0 8px;
|
||||
}
|
||||
.dm-blog-card p {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 12.5px;
|
||||
color: #64748b;
|
||||
line-height: 1.65;
|
||||
margin: 0 0 18px;
|
||||
flex: 1;
|
||||
}
|
||||
.dm-blog-read-more {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #c01227;
|
||||
text-decoration: none;
|
||||
letter-spacing: 0.2px;
|
||||
margin-top: auto;
|
||||
transition: gap 0.2s ease;
|
||||
}
|
||||
.dm-blog-read-more:hover { gap: 9px; }
|
||||
.dm-blog-read-more svg { flex-shrink: 0; }
|
||||
|
||||
/* ── Responsive ── */
|
||||
@media (max-width: 1024px) {
|
||||
.dm-blog-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
}
|
||||
@media (max-width: 640px) {
|
||||
.dm-blog-grid { grid-template-columns: 1fr; }
|
||||
.blog-hero { padding: 110px 0 60px; }
|
||||
.blog-container { padding: 0 20px; }
|
||||
.blog-section { padding: 60px 0 80px; }
|
||||
}
|
||||
@@ -1,171 +0,0 @@
|
||||
.tax-product_brand .brand-description {
|
||||
overflow: hidden;
|
||||
zoom:1}
|
||||
|
||||
.tax-product_brand .brand-description img.brand-thumbnail {
|
||||
width: 25%;
|
||||
float: right
|
||||
}
|
||||
|
||||
.tax-product_brand .brand-description .text {
|
||||
width: 72%;
|
||||
float: left
|
||||
}
|
||||
|
||||
.widget_brand_description img {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: auto;
|
||||
margin: 0 0 1em
|
||||
}
|
||||
|
||||
ul.brand-thumbnails {
|
||||
margin-left: 0;
|
||||
margin-bottom: 0;
|
||||
clear: both;
|
||||
list-style: none
|
||||
}
|
||||
|
||||
ul.brand-thumbnails:before {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table
|
||||
}
|
||||
|
||||
ul.brand-thumbnails:after {
|
||||
clear: both;
|
||||
content: "";
|
||||
display: table
|
||||
}
|
||||
|
||||
ul.brand-thumbnails li {
|
||||
float: left;
|
||||
margin: 0 3.8% 1em 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
width: 22.05%
|
||||
}
|
||||
|
||||
ul.brand-thumbnails.fluid-columns li {
|
||||
width: auto
|
||||
}
|
||||
|
||||
ul.brand-thumbnails:not(.fluid-columns) li.first {
|
||||
clear: both
|
||||
}
|
||||
|
||||
ul.brand-thumbnails:not(.fluid-columns) li.last {
|
||||
margin-right: 0
|
||||
}
|
||||
|
||||
ul.brand-thumbnails.columns-1 li {
|
||||
width: 100%;
|
||||
margin-right: 0
|
||||
}
|
||||
|
||||
ul.brand-thumbnails.columns-2 li {
|
||||
width: 48%
|
||||
}
|
||||
|
||||
ul.brand-thumbnails.columns-3 li {
|
||||
width: 30.75%
|
||||
}
|
||||
|
||||
ul.brand-thumbnails.columns-5 li {
|
||||
width: 16.95%
|
||||
}
|
||||
|
||||
ul.brand-thumbnails.columns-6 li {
|
||||
width: 13.5%
|
||||
}
|
||||
|
||||
.brand-thumbnails li img {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
height: auto;
|
||||
margin: 0
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
ul.brand-thumbnails:not(.fluid-columns) li {
|
||||
width:48%!important
|
||||
}
|
||||
|
||||
ul.brand-thumbnails:not(.fluid-columns) li.first {
|
||||
clear: none
|
||||
}
|
||||
|
||||
ul.brand-thumbnails:not(.fluid-columns) li.last {
|
||||
margin-right: 3.8%
|
||||
}
|
||||
|
||||
ul.brand-thumbnails:not(.fluid-columns) li:nth-of-type(odd) {
|
||||
clear: both
|
||||
}
|
||||
|
||||
ul.brand-thumbnails:not(.fluid-columns) li:nth-of-type(even) {
|
||||
margin-right: 0
|
||||
}
|
||||
}
|
||||
|
||||
.brand-thumbnails-description li {
|
||||
text-align: center
|
||||
}
|
||||
|
||||
.brand-thumbnails-description li .term-thumbnail img {
|
||||
display: inline
|
||||
}
|
||||
|
||||
.brand-thumbnails-description li .term-description {
|
||||
margin-top: 1em;
|
||||
text-align: left
|
||||
}
|
||||
|
||||
#brands_a_z h3:target {
|
||||
text-decoration: underline
|
||||
}
|
||||
|
||||
ul.brands_index {
|
||||
list-style: none outside;
|
||||
overflow: hidden;
|
||||
zoom:1}
|
||||
|
||||
ul.brands_index li {
|
||||
float: left;
|
||||
margin: 0 2px 2px 0
|
||||
}
|
||||
|
||||
ul.brands_index li a,ul.brands_index li span {
|
||||
border: 1px solid #ccc;
|
||||
padding: 6px;
|
||||
line-height: 1em;
|
||||
float: left;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
ul.brands_index li span {
|
||||
border-color: #eee;
|
||||
color: #ddd
|
||||
}
|
||||
|
||||
ul.brands_index li a:hover {
|
||||
border-width: 2px;
|
||||
padding: 5px;
|
||||
text-decoration: none
|
||||
}
|
||||
|
||||
ul.brands_index li a.active {
|
||||
border-width: 2px;
|
||||
padding: 5px
|
||||
}
|
||||
|
||||
div#brands_a_z a.top {
|
||||
border: 1px solid #ccc;
|
||||
padding: 4px;
|
||||
line-height: 1em;
|
||||
float: right;
|
||||
text-decoration: none;
|
||||
font-size: .8em
|
||||
}
|
||||
@@ -1,737 +0,0 @@
|
||||
/* EV Section Styles */
|
||||
:root {
|
||||
--ev-primary: #111111;
|
||||
--ev-accent: #E31E24;
|
||||
--ev-accent-soft: rgba(227, 30, 36, 0.1);
|
||||
--ev-text-muted: #64748B;
|
||||
--ev-bg-light: #F8FAFC;
|
||||
--ev-card-bg: rgba(255, 255, 255, 0.8);
|
||||
--ev-radius-lg: 32px;
|
||||
--ev-radius-md: 20px;
|
||||
--ev-radius-sm: 12px;
|
||||
--ev-shadow-premium: 0 20px 50px rgba(0, 0, 0, 0.06);
|
||||
--ev-glass-border: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.ev-section {
|
||||
padding: 120px 0;
|
||||
background-color: #FDFDFD;
|
||||
font-family: 'Inter', system-ui, -apple-system, sans-serif;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Background Blobs */
|
||||
.ev-section::before,
|
||||
.ev-section::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
border-radius: 50%;
|
||||
filter: blur(120px);
|
||||
z-index: 0;
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.ev-section::before {
|
||||
background: radial-gradient(circle, #E31E24 0%, transparent 70%);
|
||||
top: -200px;
|
||||
right: -200px;
|
||||
}
|
||||
|
||||
.ev-section::after {
|
||||
background: radial-gradient(circle, #3B82F6 0%, transparent 70%);
|
||||
bottom: -200px;
|
||||
left: -200px;
|
||||
}
|
||||
|
||||
.ev-container {
|
||||
max-width: 1300px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ev-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 1.2fr;
|
||||
gap: 80px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Left Content */
|
||||
.ev-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 8px 18px;
|
||||
border-radius: 100px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 32px;
|
||||
background: #FFFFFF;
|
||||
border: 1px solid #E2E8F0;
|
||||
color: #1E293B;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.03);
|
||||
}
|
||||
|
||||
.ev-title {
|
||||
font-size: clamp(36px, 5vw, 64px);
|
||||
font-weight: 900;
|
||||
line-height: 1.05;
|
||||
color: var(--ev-primary);
|
||||
margin-bottom: 28px;
|
||||
letter-spacing: -1.5px;
|
||||
}
|
||||
|
||||
.ev-title .accent {
|
||||
color: var(--ev-accent);
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ev-desc {
|
||||
font-size: 18px;
|
||||
color: var(--ev-text-muted);
|
||||
line-height: 1.7;
|
||||
max-width: 520px;
|
||||
margin-bottom: 56px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Feature Grid */
|
||||
.feature-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
background: var(--ev-card-bg);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
padding: 24px;
|
||||
border-radius: var(--ev-radius-md);
|
||||
border: 1px solid var(--ev-glass-border);
|
||||
box-shadow: var(--ev-shadow-premium);
|
||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-12px) scale(1.02);
|
||||
border-color: rgba(227, 30, 36, 0.3);
|
||||
box-shadow: 0 30px 70px rgba(227, 30, 36, 0.2), 0 0 30px rgba(227, 30, 36, 0.1);
|
||||
}
|
||||
|
||||
.feature-icon-box {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 8px 16px rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
font-size: 15px;
|
||||
font-weight: 800;
|
||||
margin-bottom: 8px;
|
||||
color: var(--ev-primary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
font-size: 13px;
|
||||
color: var(--ev-text-muted);
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* Right Dashboard */
|
||||
.ev-dashboard {
|
||||
position: relative;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.dashboard-card {
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-radius: var(--ev-radius-lg);
|
||||
padding: 40px;
|
||||
box-shadow: 0 40px 100px rgba(0, 0, 0, 0.08);
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
position: relative;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.dashboard-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.dashboard-title {
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
margin: 0;
|
||||
color: var(--ev-primary);
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.live-indicator {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #059669;
|
||||
padding: 6px 14px;
|
||||
border-radius: 100px;
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.live-indicator::before {
|
||||
content: '';
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #10B981;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px rgba(16, 185, 129, 0.5);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(0.95); opacity: 1; }
|
||||
50% { transform: scale(1.1); opacity: 0.6; }
|
||||
100% { transform: scale(0.95); opacity: 1; }
|
||||
}
|
||||
|
||||
.van-display {
|
||||
position: relative;
|
||||
margin: 60px 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.van-image {
|
||||
width: 80%;
|
||||
height: auto;
|
||||
filter: drop-shadow(0 30px 40px rgba(0,0,0,0.1));
|
||||
transition: transform 0.5s ease;
|
||||
}
|
||||
|
||||
.van-display:hover .van-image {
|
||||
transform: scale(1.05) rotate(-2deg);
|
||||
}
|
||||
|
||||
/* Dashboard Overlays */
|
||||
.overlay-card {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
backdrop-filter: blur(8px);
|
||||
padding: 12px 18px;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.08);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
z-index: 2;
|
||||
border: 1px solid rgba(255, 255, 255, 1);
|
||||
animation: float 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
0% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-10px); }
|
||||
100% { transform: translateY(0px); }
|
||||
}
|
||||
|
||||
.overlay-card.v-status-1 { top: 0%; left: -10%; animation-delay: 0s; }
|
||||
.overlay-card.v-status-2 { top: -15%; right: 0%; animation-delay: 1s; }
|
||||
.overlay-card.v-status-3 { bottom: 10%; left: -5%; animation-delay: 2s; }
|
||||
.overlay-card.v-status-4 { bottom: -5%; right: -10%; animation-delay: 1.5s; }
|
||||
|
||||
.status-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 10px;
|
||||
background: #F1F5F9;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.overlay-card .info h4 {
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
margin: 0;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.overlay-card .info p {
|
||||
font-size: 11px;
|
||||
margin: 0;
|
||||
color: var(--ev-text-muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.progress-ring {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
border-radius: 50%;
|
||||
border: 3px solid #E2E8F0;
|
||||
color: #111;
|
||||
}
|
||||
|
||||
.progress-ring.success { border-color: #10B981; color: #10B981; }
|
||||
.progress-ring.warning { border-color: #F59E0B; color: #F59E0B; }
|
||||
|
||||
/* Stats Row */
|
||||
.stats-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.stat-metric {
|
||||
background: rgba(255, 255, 255, 1);
|
||||
padding: 20px 10px;
|
||||
border-radius: 20px;
|
||||
text-align: center;
|
||||
border: 1px solid #F1F5F9;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.03);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-metric:hover {
|
||||
transform: translateY(-5px);
|
||||
border-color: var(--ev-accent);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
display: block;
|
||||
font-size: 22px;
|
||||
font-weight: 900;
|
||||
color: var(--ev-primary);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
display: block;
|
||||
font-size: 10px;
|
||||
color: var(--ev-text-muted);
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* Responsiveness */
|
||||
@media (max-width: 1280px) {
|
||||
.ev-grid { gap: 40px; }
|
||||
.overlay-card.v-status-1 { left: 0; }
|
||||
.overlay-card.v-status-2 { right: 0; }
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.ev-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 80px;
|
||||
}
|
||||
|
||||
.ev-content {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ev-desc {
|
||||
max-width: 600px;
|
||||
}
|
||||
|
||||
.feature-grid {
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ev-dashboard {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.ev-section { padding: 80px 0; }
|
||||
.feature-grid { grid-template-columns: 1fr; }
|
||||
.stats-row { grid-template-columns: 1fr 1fr; }
|
||||
.dashboard-card { padding: 30px 20px; }
|
||||
.ev-title { font-size: 40px; }
|
||||
.overlay-card { transform: scale(0.8); }
|
||||
.v-status-1, .v-status-2, .v-status-3, .v-status-4 { position: static; margin-bottom: 10px; }
|
||||
.van-display { margin: 20px 0; }
|
||||
}
|
||||
/* EV2 Section - Premium Redesign */
|
||||
:root {
|
||||
--ev2-primary: #0A0A0B;
|
||||
--ev2-accent: #E31E24;
|
||||
--ev2-accent-glow: rgba(227, 30, 36, 0.4);
|
||||
--ev2-text: #FFFFFF;
|
||||
--ev2-text-muted: #A1A1AA;
|
||||
--ev2-card-bg: rgba(23, 23, 23, 0.7);
|
||||
--ev2-border: rgba(255, 255, 255, 0.1);
|
||||
--ev2-radius: 24px;
|
||||
}
|
||||
|
||||
.ev2-section {
|
||||
padding: 120px 0;
|
||||
background-color: var(--ev2-primary);
|
||||
color: var(--ev2-text);
|
||||
font-family: 'Outfit', 'Manrope', sans-serif;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Abstract Background Elements */
|
||||
.ev2-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -10%;
|
||||
right: -10%;
|
||||
width: 40%;
|
||||
height: 40%;
|
||||
background: radial-gradient(circle, var(--ev2-accent-glow) 0%, transparent 70%);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ev2-container {
|
||||
max-width: 1300px;
|
||||
margin: 0 auto;
|
||||
padding: 0 30px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ev2-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 80px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Left Content */
|
||||
.ev2-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 100px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 24px;
|
||||
background: rgba(227, 30, 36, 0.1);
|
||||
color: var(--ev2-accent);
|
||||
border: 1px solid rgba(227, 30, 36, 0.2);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.ev2-badge::before {
|
||||
content: '';
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
background: var(--ev2-accent);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px var(--ev2-accent);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.5); opacity: 0.5; }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
.ev2-title {
|
||||
font-size: clamp(40px, 6vw, 72px);
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
margin-bottom: 30px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ev2-title .glow {
|
||||
color: var(--ev2-accent);
|
||||
display: block;
|
||||
text-shadow: 0 0 30px var(--ev2-accent-glow);
|
||||
}
|
||||
|
||||
.ev2-desc {
|
||||
font-size: 20px;
|
||||
color: var(--ev2-text-muted);
|
||||
line-height: 1.6;
|
||||
max-width: 580px;
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
/* Feature Stack */
|
||||
.ev2-features {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.ev2-feature-item {
|
||||
padding: 24px;
|
||||
background: var(--ev2-card-bg);
|
||||
backdrop-filter: blur(10px);
|
||||
border: 1px solid var(--ev2-border);
|
||||
border-radius: 20px;
|
||||
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.ev2-feature-item:hover {
|
||||
transform: translateY(-8px);
|
||||
border-color: rgba(227, 30, 36, 0.4);
|
||||
background: rgba(23, 23, 23, 0.9);
|
||||
}
|
||||
|
||||
.ev2-icon-wrap {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
color: var(--ev2-accent);
|
||||
}
|
||||
|
||||
.ev2-feature-item h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.ev2-feature-item p {
|
||||
font-size: 14px;
|
||||
color: var(--ev2-text-muted);
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Right Side - Visual Interface */
|
||||
.ev2-visual-wrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ev2-main-card {
|
||||
background: linear-gradient(135deg, #171719 0%, #0A0A0B 100%);
|
||||
border-radius: 32px;
|
||||
padding: 40px;
|
||||
border: 1px solid var(--ev2-border);
|
||||
box-shadow: 0 40px 100px rgba(0, 0, 0, 0.5);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ev2-main-card::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(180deg, transparent 0%, rgba(227, 30, 36, 0.05) 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ev2-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.ev2-tagline {
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
color: var(--ev2-accent);
|
||||
letter-spacing: 2px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.ev2-id {
|
||||
font-size: 28px;
|
||||
font-weight: 900;
|
||||
color: #fff;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.ev2-status-pill {
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
color: #10B981;
|
||||
padding: 6px 14px;
|
||||
border-radius: 100px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.ev2-image-box {
|
||||
position: relative;
|
||||
margin: 30px 0;
|
||||
border-radius: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ev2-image-box img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
transform: scale(1.05);
|
||||
transition: transform 0.8s ease;
|
||||
}
|
||||
|
||||
.ev2-main-card:hover .ev2-image-box img {
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
/* Floating HUD elements */
|
||||
.ev2-hud {
|
||||
position: absolute;
|
||||
padding: 15px;
|
||||
background: rgba(10, 10, 11, 0.8);
|
||||
backdrop-filter: blur(12px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 16px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.hud-1 {
|
||||
top: 30%;
|
||||
right: -20px;
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
.hud-2 {
|
||||
bottom: 20%;
|
||||
left: -20px;
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.hud-label {
|
||||
font-size: 10px;
|
||||
color: var(--ev2-text-muted);
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.hud-value {
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.hud-progress {
|
||||
height: 4px;
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 2px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.hud-bar {
|
||||
height: 100%;
|
||||
background: var(--ev2-accent);
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 10px var(--ev2-accent);
|
||||
}
|
||||
|
||||
/* Bottom Metrics */
|
||||
.ev2-metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 20px;
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.metric-box {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.m-val {
|
||||
font-size: 24px;
|
||||
font-weight: 900;
|
||||
display: block;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.m-label {
|
||||
font-size: 11px;
|
||||
color: var(--ev2-text-muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Responsiveness */
|
||||
@media (max-width: 1024px) {
|
||||
.ev2-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 60px;
|
||||
}
|
||||
|
||||
.ev2-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.ev2-desc {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.ev2-features {
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.ev2-visual-wrap {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.hud-1, .hud-2 {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.ev2-features {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.ev2-title {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.ev2-metrics {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
}
|
||||
@@ -1,464 +0,0 @@
|
||||
/* EV Premium Section - High-End SaaS UI */
|
||||
:root {
|
||||
--evp-bg: #030303;
|
||||
--evp-card-bg: rgba(20, 20, 22, 0.6);
|
||||
--evp-accent: #E31E24;
|
||||
--evp-accent-glow: rgba(227, 30, 36, 0.4);
|
||||
--evp-success: #10B981;
|
||||
--evp-info: #3B82F6;
|
||||
--evp-text: #FFFFFF;
|
||||
--evp-text-dim: #A1A1AA;
|
||||
--evp-border: rgba(255, 255, 255, 0.08);
|
||||
--evp-glass-border: rgba(255, 255, 255, 0.12);
|
||||
--evp-radius-lg: 32px;
|
||||
--evp-radius-md: 20px;
|
||||
--evp-font: 'Manrope', -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
.evp-section {
|
||||
padding: 140px 0;
|
||||
background-color: #1f1f1f;
|
||||
color: var(--evp-text);
|
||||
font-family: var(--evp-font);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* ─── BACKGROUND EFFECTS ─── */
|
||||
.evp-bg-aura {
|
||||
position: absolute;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
border-radius: 50%;
|
||||
filter: blur(150px);
|
||||
z-index: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.aura-red {
|
||||
background: radial-gradient(circle, var(--evp-accent) 0%, transparent 70%);
|
||||
top: -100px;
|
||||
right: -100px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.logico-front-end h4:not([class*=logico-title-h]){
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
|
||||
.aura-blue {
|
||||
background: radial-gradient(circle, var(--evp-info) 0%, transparent 70%);
|
||||
bottom: -100px;
|
||||
left: -100px;
|
||||
}
|
||||
|
||||
.evp-grid-overlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
|
||||
background-size: 50px 50px;
|
||||
mask-image: radial-gradient(circle at center, black, transparent 80%);
|
||||
pointer-events: none;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.evp-container {
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.evp-layout {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 80px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ─── LEFT: CONTENT & HERO ─── */
|
||||
.evp-content {
|
||||
flex: 1;
|
||||
max-width: 650px;
|
||||
}
|
||||
|
||||
.evp-tag {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
background: rgba(227, 30, 36, 0.1);
|
||||
border: 1px solid rgba(227, 30, 36, 0.2);
|
||||
border-radius: 100px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--evp-accent);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.evp-tag .status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: var(--evp-accent);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 10px var(--evp-accent);
|
||||
animation: evp-pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes evp-pulse {
|
||||
0% { transform: scale(0.95); opacity: 1; }
|
||||
50% { transform: scale(1.3); opacity: 0.5; }
|
||||
100% { transform: scale(0.95); opacity: 1; }
|
||||
}
|
||||
|
||||
.evp-title {
|
||||
font-size: clamp(40px, 5vw, 68px);
|
||||
line-height: 1.05;
|
||||
font-weight: 800;
|
||||
letter-spacing: -2px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.evp-title span {
|
||||
display: block;
|
||||
color: var(--evp-accent);
|
||||
filter: drop-shadow(0 0 20px var(--evp-accent-glow));
|
||||
}
|
||||
|
||||
.evp-desc {
|
||||
font-size: 20px;
|
||||
color: var(--evp-text-dim);
|
||||
line-height: 1.6;
|
||||
margin-bottom: 50px;
|
||||
max-width: 580px;
|
||||
}
|
||||
|
||||
/* Feature Cards Stack */
|
||||
.evp-features {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.evp-feature-card {
|
||||
padding: 30px;
|
||||
background: var(--evp-card-bg);
|
||||
backdrop-filter: blur(20px);
|
||||
border: 1px solid var(--evp-glass-border);
|
||||
border-radius: var(--evp-radius-md);
|
||||
transition: all 0.4s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.evp-feature-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; width: 100%; height: 100%;
|
||||
background: linear-gradient(135deg, rgba(255,255,255,0.05) 0%, transparent 100%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.4s;
|
||||
}
|
||||
|
||||
.evp-feature-card:hover {
|
||||
transform: translateY(-5px) scale(1.02);
|
||||
border-color: rgba(227, 30, 36, 0.4);
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.4);
|
||||
}
|
||||
|
||||
.evp-feature-card:hover::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.evp-icon-box {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 14px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 20px;
|
||||
color: var(--evp-accent);
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.evp-feature-card h3 {
|
||||
font-size: 17px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.evp-feature-card p {
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
line-height: 1.5;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ─── RIGHT: VISUAL DASHBOARD ─── */
|
||||
.evp-visual {
|
||||
flex: 1;
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.evp-dashboard {
|
||||
width: 100%;
|
||||
max-width: 600px;
|
||||
background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, transparent 100%);
|
||||
border: 1px solid var(--evp-border);
|
||||
border-radius: 40px;
|
||||
padding: 50px;
|
||||
position: relative;
|
||||
box-shadow: 0 50px 100px rgba(0,0,0,0.8);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.evp-dashboard::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 40px;
|
||||
padding: 1px;
|
||||
background: linear-gradient(to bottom right, rgba(255,255,255,0.1), transparent, rgba(255,255,255,0.05));
|
||||
-webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
|
||||
-webkit-mask-composite: xor;
|
||||
mask-composite: exclude;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.evp-db-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.evp-db-title {
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
color: var(--evp-text-dim);
|
||||
}
|
||||
|
||||
.evp-live-tag {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--evp-success);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: rgba(16, 185, 129, 0.1);
|
||||
padding: 4px 10px;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
.evp-db-id {
|
||||
font-size: 32px;
|
||||
font-weight: 900;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.evp-van-stage {
|
||||
position: relative;
|
||||
margin: 40px 0;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.evp-van-image {
|
||||
width: 110%;
|
||||
height: auto;
|
||||
/* margin-left: -5%; */
|
||||
filter: drop-shadow(0 20px 30px rgba(0,0,0,0.5));
|
||||
animation: evp-float 6s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes evp-float {
|
||||
0%, 100% { transform: translateY(0); }
|
||||
50% { transform: translateY(-15px); }
|
||||
}
|
||||
|
||||
/* Floating HUD Cards */
|
||||
.evp-hud-card {
|
||||
position: absolute;
|
||||
background: rgba(15, 15, 18, 0.85);
|
||||
backdrop-filter: blur(15px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 18px;
|
||||
padding: 16px;
|
||||
z-index: 10;
|
||||
box-shadow: 0 20px 40px rgba(0,0,0,0.4);
|
||||
pointer-events: all;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.evp-hud-card:hover {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
|
||||
.hud-battery {
|
||||
top: 55%;
|
||||
left: -40px;
|
||||
min-width: 160px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
}
|
||||
|
||||
.hud-location {
|
||||
top: 15%;
|
||||
right: -30px;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.hud-route {
|
||||
bottom: 30%;
|
||||
right: -50px;
|
||||
min-width: 170px;
|
||||
background: linear-gradient(135deg, rgba(20, 20, 24, 0.9), rgba(10, 10, 12, 0.9));
|
||||
}
|
||||
|
||||
.evp-progress-svg {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.evp-progress-circle-bg {
|
||||
fill: none;
|
||||
stroke: rgba(255,255,255,0.05);
|
||||
stroke-width: 4;
|
||||
}
|
||||
|
||||
.evp-progress-circle {
|
||||
fill: none;
|
||||
stroke: var(--evp-success);
|
||||
stroke-width: 4;
|
||||
stroke-linecap: round;
|
||||
stroke-dasharray: 113;
|
||||
stroke-dashoffset: 20; /* Example for 82% */
|
||||
transition: stroke-dashoffset 1s ease-out;
|
||||
}
|
||||
|
||||
.hud-val-large {
|
||||
font-size: 20px;
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.hud-label-small {
|
||||
font-size: 10px;
|
||||
color: var(--evp-text-dim);
|
||||
text-transform: uppercase;
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
/* Route Visual in HUD */
|
||||
.hud-route-path {
|
||||
margin-top: 10px;
|
||||
height: 30px;
|
||||
background-image: radial-gradient(circle, rgba(255,255,255,0.2) 1px, transparent 1px);
|
||||
background-size: 8px 8px;
|
||||
border-radius: 8px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hud-route-line {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 10%;
|
||||
width: 60%;
|
||||
height: 2px;
|
||||
background: var(--evp-accent);
|
||||
box-shadow: 0 0 10px var(--evp-accent);
|
||||
}
|
||||
|
||||
/* Dashboard Bottom Metrics */
|
||||
.evp-metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 15px;
|
||||
border-top: 1px solid var(--evp-border);
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
||||
.m-item {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.m-label {
|
||||
font-size: 9px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 800;
|
||||
color: var(--evp-text-dim);
|
||||
letter-spacing: 0.5px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.m-value {
|
||||
font-size: 18px;
|
||||
font-weight: 900;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ─── RESPONSIVENESS ─── */
|
||||
@media (max-width: 1300px) {
|
||||
.evp-layout { gap: 40px; }
|
||||
.hud-battery { left: 0; }
|
||||
.hud-location { right: 0; }
|
||||
.hud-route { right: 0; }
|
||||
}
|
||||
|
||||
@media (max-width: 1100px) {
|
||||
.evp-layout {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.evp-content {
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.evp-desc {
|
||||
max-width: 700px;
|
||||
}
|
||||
|
||||
.evp-features {
|
||||
max-width: 800px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.evp-visual {
|
||||
margin-top: 60px;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.evp-section { padding: 80px 0; }
|
||||
.evp-features { grid-template-columns: 1fr; }
|
||||
.evp-metrics { grid-template-columns: 1fr 1fr; gap: 20px; }
|
||||
.evp-dashboard { padding: 30px 20px; }
|
||||
.evp-hud-card { display: contents; }
|
||||
.evp-title { font-size: 38px; }
|
||||
.evp-db-id { font-size: 24px; }
|
||||
}
|
||||
@@ -1,562 +0,0 @@
|
||||
/*
|
||||
* section-miletruth.css
|
||||
* Extends the existing ev-section & evp-section patterns.
|
||||
* Only adds what the existing CSS files don't already provide.
|
||||
*/
|
||||
|
||||
/* ── Override ev-section font to match site Manrope ── */
|
||||
.ev-section,
|
||||
.ev-section .ev-title,
|
||||
.ev-section .ev-desc,
|
||||
.ev-section .ev-badge,
|
||||
.ev-section .feature-card h3,
|
||||
.ev-section .feature-card p {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
}
|
||||
|
||||
/* ── Remove the decorative blobs (they show as colored arcs at section edges) ── */
|
||||
.ev-section::before,
|
||||
.ev-section::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* ── Column widths: image always gets the large column ──
|
||||
Edge (non-rev): content first → col1 small, image → col2 large
|
||||
Impact/Fulfillment (rev): image first → col1 large, content → col2 small ── */
|
||||
.ev-section .ev-grid {
|
||||
grid-template-columns: 0.42fr 2.2fr;
|
||||
}
|
||||
.ev-section .ev-grid.ev-grid--rev {
|
||||
grid-template-columns: 2.2fr 0.42fr;
|
||||
}
|
||||
.ev-section .ev-grid,
|
||||
.ev-section .ev-grid.ev-grid--rev {
|
||||
gap: 40px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* ── Widen the container on this page to give images more room ── */
|
||||
.ev-section .ev-container {
|
||||
max-width: 1440px;
|
||||
padding: 0 32px;
|
||||
}
|
||||
|
||||
/* ── Slightly smaller body text so images feel dominant ── */
|
||||
.ev-section .ev-desc {
|
||||
font-size: 16px !important;
|
||||
line-height: 1.65;
|
||||
margin-bottom: 36px;
|
||||
}
|
||||
|
||||
/* ── Hero slider dark background (no Elementor CSS on this page) ── */
|
||||
/* .miletruth-hero .content-slider-wrapper {
|
||||
background: linear-gradient(160deg, #0c0c14 0%, #18050a 100%);
|
||||
} */
|
||||
|
||||
/* ── Remove the default 90px top margin so hero starts at y:0, covering the
|
||||
white gap that would otherwise show behind the transparent header ── */
|
||||
.miletruth-hero .content-slider .slide-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.miletruth-hero .content-slider .slide-content-inner {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 980px;
|
||||
padding: 0 32px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.miletruth-hero .content-slider-item-heading,
|
||||
.miletruth-hero .content-slider-item-heading .heading-content {
|
||||
color: rgba(255, 255, 255, 0.92) !important;
|
||||
font-size: 72px;
|
||||
line-height: 1.05;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.miletruth-hero .content-slider-item-text,
|
||||
.miletruth-hero .content-slider-item-text .text-content,
|
||||
.miletruth-hero .content-slider-item-text p {
|
||||
color: rgba(255, 255, 255, 0.72) !important;
|
||||
font-size: 20px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.content-wrapper.miletruth-hero {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
/* ── Image-left layouts: image is first child in HTML, large column is first ── */
|
||||
.ev-grid--rev {
|
||||
grid-template-columns: 2.2fr 0.42fr;
|
||||
}
|
||||
/* No order override needed — HTML source order already places image first (left, large) */
|
||||
|
||||
|
||||
/* ── Dark section override on ev-section ── */
|
||||
.ev-section--dark {
|
||||
background: #1a1a1f !important;
|
||||
}
|
||||
.ev-section--dark .ev-title { color: #ffffff; }
|
||||
.ev-section--dark .ev-desc { color: rgba(255, 255, 255, 0.55); }
|
||||
.ev-section--dark .ev-badge {
|
||||
background: rgba(255,255,255,0.06);
|
||||
border-color: rgba(255,255,255,0.12);
|
||||
color: rgba(255,255,255,0.7);
|
||||
box-shadow: none;
|
||||
}
|
||||
.ev-section--dark .feature-card {
|
||||
background: rgba(255,255,255,0.05);
|
||||
border-color: rgba(255,255,255,0.08);
|
||||
}
|
||||
.ev-section--dark .feature-card:hover {
|
||||
background: rgba(255,255,255,0.08);
|
||||
border-color: rgba(227,30,36,0.3);
|
||||
}
|
||||
.ev-section--dark .feature-card h3 { color: #ffffff; }
|
||||
.ev-section--dark .feature-card p { color: rgba(255,255,255,0.5); }
|
||||
.ev-section--dark .feature-icon-box {
|
||||
background: rgba(255,255,255,0.07);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/* ── Picture card (pitch image container) ── */
|
||||
.mr-pic-card {
|
||||
border-radius: 32px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 40px 100px rgba(0, 0, 0, 0.09);
|
||||
border: 1px solid rgba(255, 255, 255, 0.6);
|
||||
background: #fff;
|
||||
}
|
||||
.mr-pic-card--dark {
|
||||
border-color: rgba(255,255,255,0.08);
|
||||
box-shadow: 0 40px 80px rgba(0,0,0,0.45);
|
||||
}
|
||||
.mr-pic-card img {
|
||||
width: 100%; height: auto; display: block;
|
||||
transition: transform 0.6s cubic-bezier(0.19, 1, 0.22, 1);
|
||||
}
|
||||
.mr-pic-card:hover img { transform: scale(1.025); }
|
||||
|
||||
/* ── Stats strip ── */
|
||||
.mr-stats-strip {
|
||||
padding: 72px 0;
|
||||
background: #F8FAFC;
|
||||
border-top: 1px solid rgba(0,0,0,0.05);
|
||||
border-bottom: 1px solid rgba(0,0,0,0.05);
|
||||
}
|
||||
.mr-stats-grid {
|
||||
max-width: 1300px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0;
|
||||
background: #fff;
|
||||
border: 1px solid rgba(0,0,0,0.07);
|
||||
border-radius: 24px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 8px 40px rgba(0,0,0,0.05);
|
||||
}
|
||||
.mr-stat-item {
|
||||
padding: 44px 32px;
|
||||
text-align: center;
|
||||
border-right: 1px solid rgba(0,0,0,0.07);
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
.mr-stat-item:last-child { border-right: none; }
|
||||
.mr-stat-item:hover { background: rgba(192,18,39,0.03); }
|
||||
.mr-stat-num {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: clamp(36px, 4.5vw, 58px);
|
||||
font-weight: 900;
|
||||
color: #111;
|
||||
letter-spacing: -2px;
|
||||
line-height: 1;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.mr-stat-num span { color: #c01227; }
|
||||
.mr-stat-lbl {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 2px;
|
||||
color: #64748B;
|
||||
}
|
||||
|
||||
/* ── CTA section ── */
|
||||
.mr-cta-section {
|
||||
padding: 130px 0;
|
||||
background: #0d0304;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
text-align: center;
|
||||
}
|
||||
.mr-cta-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(ellipse 65% 55% at 50% 50%, rgba(192,18,39,0.16) 0%, transparent 100%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.mr-cta-inner {
|
||||
max-width: 720px;
|
||||
margin: 0 auto;
|
||||
padding: 0 40px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.mr-cta-eyebrow {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 3.5px;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255,255,255,0.35);
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
.mr-cta-eyebrow::before,
|
||||
.mr-cta-eyebrow::after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 28px; height: 1px;
|
||||
background: rgba(255,255,255,0.2);
|
||||
}
|
||||
.mr-cta-inner h2 {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: clamp(34px, 5vw, 64px);
|
||||
font-weight: 900;
|
||||
color: #fff;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -2px;
|
||||
text-transform: uppercase;
|
||||
margin: 0 0 22px;
|
||||
}
|
||||
.mr-cta-inner h2 span { color: #c01227; }
|
||||
.mr-cta-inner p {
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 18px;
|
||||
color: rgba(255,255,255,0.48);
|
||||
line-height: 1.75;
|
||||
margin: 0 0 52px;
|
||||
}
|
||||
.mr-cta-btns {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
/* Primary CTA button */
|
||||
.mr-cta-btns .logico-button {
|
||||
border-radius: 100px !important;
|
||||
}
|
||||
|
||||
/* ── List style matching ev-section checklist ── */
|
||||
.ev-checklist {
|
||||
list-style: none !important;
|
||||
margin: 0 0 48px;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 14px;
|
||||
}
|
||||
.ev-checklist li {
|
||||
list-style: none !important;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 12px;
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #2d3748;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.ev-checklist li::marker {
|
||||
content: '' !important;
|
||||
display: none !important;
|
||||
}
|
||||
.ev-checklist--dark li { color: rgba(255,255,255,0.75); }
|
||||
|
||||
/* .logico-front-end ul li:before (vendor-theme-core.css) injects a fontello icon on
|
||||
every li — override it with higher specificity + !important so only our SVG shows */
|
||||
.logico-front-end .ev-checklist li::before,
|
||||
.ev-checklist li::before {
|
||||
content: '' !important;
|
||||
font: unset !important;
|
||||
position: relative !important;
|
||||
display: block !important;
|
||||
width: 20px; height: 20px; min-width: 20px;
|
||||
border-radius: 6px;
|
||||
background: rgba(192,18,39,0.08) !important;
|
||||
border: 1.5px solid rgba(192,18,39,0.2);
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='8' viewBox='0 0 10 8' fill='none'%3E%3Cpath d='M1 4l2.5 2.5L9 1' stroke='%23c01227' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") !important;
|
||||
background-size: 10px; background-repeat: no-repeat; background-position: center;
|
||||
margin-top: 2px; flex-shrink: 0;
|
||||
top: 0 !important; left: 0 !important;
|
||||
}
|
||||
.ev-checklist--dark li::before {
|
||||
background-color: rgba(192,18,39,0.08) !important;
|
||||
border-color: rgba(192,18,39,0.2) !important;
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23c01227' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M18 6 7 17l-5-5'/%3E%3Cpath d='m22 10-7.5 7.5L13 16'/%3E%3C/svg%3E") !important;
|
||||
background-size: 14px;
|
||||
}
|
||||
|
||||
/* ── Responsive extras ── */
|
||||
@media (max-width: 1024px) {
|
||||
.mr-stats-grid { grid-template-columns: repeat(2, 1fr); }
|
||||
.mr-stat-item:nth-child(even) { border-right: none; }
|
||||
.mr-stat-item:nth-child(n+3) { border-top: 1px solid rgba(0,0,0,0.07); }
|
||||
}
|
||||
@media (max-width: 680px) {
|
||||
.mr-stats-strip { padding: 52px 0; }
|
||||
.mr-stats-grid { padding: 0 20px; grid-template-columns: 1fr 1fr; border-radius: 16px; }
|
||||
.mr-stat-item { padding: 28px 16px; }
|
||||
.mr-stat-item:nth-child(odd) { border-right: 1px solid rgba(0,0,0,0.07); }
|
||||
.mr-cta-section { padding: 80px 0; }
|
||||
.mr-cta-inner { padding: 0 20px; }
|
||||
.mr-cta-btns { flex-direction: column; align-items: center; }
|
||||
}
|
||||
|
||||
/* Additional responsive image fixes for MileTruth page */
|
||||
@media (max-width: 1200px) {
|
||||
.mr-pic-card img {
|
||||
max-height: 520px;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
object-fit: cover;
|
||||
}
|
||||
.miletruth-hero .content-slider .slide-content-inner {
|
||||
max-width: 860px;
|
||||
padding: 0 28px;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-heading,
|
||||
.miletruth-hero .content-slider-item-heading .heading-content {
|
||||
font-size: 60px;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-text,
|
||||
.miletruth-hero .content-slider-item-text .text-content,
|
||||
.miletruth-hero .content-slider-item-text p {
|
||||
font-size: 19px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.mr-pic-card {
|
||||
border-radius: 24px;
|
||||
}
|
||||
.mr-pic-card img {
|
||||
max-height: 480px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.miletruth-hero .content-slider .slide-content-inner {
|
||||
max-width: 760px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-heading,
|
||||
.miletruth-hero .content-slider-item-heading .heading-content {
|
||||
font-size: 50px;
|
||||
line-height: 1.1;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-text,
|
||||
.miletruth-hero .content-slider-item-text .text-content,
|
||||
.miletruth-hero .content-slider-item-text p {
|
||||
font-size: 18px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.ev-section .ev-grid,
|
||||
.ev-grid--rev {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.mr-pic-card {
|
||||
width: 100%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
.mr-pic-card img {
|
||||
max-height: 360px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.miletruth-hero .content-slider .slide-content-inner {
|
||||
max-width: 620px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding: 0 22px;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-heading,
|
||||
.miletruth-hero .content-slider-item-heading .heading-content {
|
||||
font-size: 40px;
|
||||
line-height: 1.12;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-text,
|
||||
.miletruth-hero .content-slider-item-text .text-content,
|
||||
.miletruth-hero .content-slider-item-text p {
|
||||
font-size: 16px;
|
||||
line-height: 1.55;
|
||||
}
|
||||
.miletruth-hero .content-slider.nav-h-position-right .owl-nav,
|
||||
.miletruth-hero .content-slider.nav-h-position-right .slider-footer {
|
||||
right: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.mr-pic-card img {
|
||||
max-height: 240px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.mr-stats-grid {
|
||||
grid-template-columns: 1fr;
|
||||
padding: 0 14px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
.mr-stat-item {
|
||||
padding: 20px;
|
||||
border-right: none !important;
|
||||
border-top: 1px solid rgba(0,0,0,0.07);
|
||||
}
|
||||
.mr-stat-num {
|
||||
font-size: clamp(28px, 8vw, 40px);
|
||||
}
|
||||
.miletruth-hero .content-slider .slide-content-inner {
|
||||
max-width: 100%;
|
||||
padding: 0 18px;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-heading,
|
||||
.miletruth-hero .content-slider-item-heading .heading-content {
|
||||
font-size: 32px;
|
||||
line-height: 1.14;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-text,
|
||||
.miletruth-hero .content-slider-item-text .text-content,
|
||||
.miletruth-hero .content-slider-item-text p {
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.miletruth-hero .content-slider-item-text {
|
||||
margin-top: 16px !important;
|
||||
}
|
||||
.miletruth-hero .content-slider.nav-h-position-right .owl-nav,
|
||||
.miletruth-hero .content-slider.nav-h-position-right .slider-footer {
|
||||
right: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Responsive tweaks specifically for dark variant to ensure readable layout */
|
||||
@media (max-width: 1024px) {
|
||||
.ev-section--dark {
|
||||
padding: 80px 0;
|
||||
}
|
||||
.ev-section--dark .ev-container {
|
||||
padding: 0 20px;
|
||||
}
|
||||
.ev-section--dark .ev-title,
|
||||
.ev-section--dark .ev-desc {
|
||||
text-align: center;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 100%;
|
||||
}
|
||||
.ev-section--dark .feature-grid {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.ev-section--dark {
|
||||
padding: 60px 0;
|
||||
}
|
||||
.ev-section--dark .ev-title { font-size: clamp(28px, 7vw, 40px); }
|
||||
.ev-section--dark .ev-badge { margin-left: auto; margin-right: auto; }
|
||||
.ev-section--dark .feature-card {
|
||||
background: rgba(255,255,255,0.03);
|
||||
border-color: rgba(255,255,255,0.06);
|
||||
}
|
||||
}
|
||||
|
||||
/* MileTruth workflow carousel pagination */
|
||||
.miletruth-workflow-heading {
|
||||
color: #ffffff;
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: clamp(26px, 3vw, 44px);
|
||||
font-weight: 800;
|
||||
line-height: 1.12;
|
||||
letter-spacing: 0;
|
||||
margin: 0 0 24px;
|
||||
}
|
||||
|
||||
.elementor-63 .elementor-element.elementor-element-0a76e77 .testimonial-text p {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.elementor-63 .elementor-element.elementor-element-0a76e77 .miletruth-workflow-progress,
|
||||
.miletruth-workflow-progress.slider-footer.slider-footer-position-after {
|
||||
margin-top: 18px !important;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.miletruth-workflow-progress .slider-footer-content {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.miletruth-workflow-progress .slider-pagination {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.miletruth-workflow-progress .slider-progress-wrapper {
|
||||
color: #ffffff !important;
|
||||
font-family: 'Manrope', sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: 800;
|
||||
line-height: 1;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.miletruth-workflow-progress .slider-progress-current,
|
||||
.miletruth-workflow-progress .slider-progress-all {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
.miletruth-workflow-progress .owl-dots {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.miletruth-workflow-progress .owl-dots .owl-dot,
|
||||
.miletruth-workflow-progress .owl-dots button.owl-dot {
|
||||
width: 43px;
|
||||
height: 2px;
|
||||
padding: 0 !important;
|
||||
border: 0 !important;
|
||||
border-radius: 0;
|
||||
background: rgba(255,255,255,0.9) !important;
|
||||
opacity: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.miletruth-workflow-progress .owl-dots .owl-dot span {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.miletruth-workflow-progress .owl-dots .owl-dot.active,
|
||||
.miletruth-workflow-progress .owl-dots button.owl-dot.active {
|
||||
height: 3px;
|
||||
background: #C01227 !important;
|
||||
}
|
||||
18239
public/css/site.css
Normal file
2000
public/css/vendor/vendor-elementor-base.css
vendored
2282
public/css/vendor/vendor-elementor-custom.min.css
vendored
7724
public/css/vendor/vendor-elementor-generated-globals.css
vendored
25
public/css/vendor/vendor-elementor-hfe.css
vendored
@@ -1,25 +0,0 @@
|
||||
.footer-width-fixer {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
.ehf-template-genesis.ehf-header .site-header .wrap,.ehf-template-genesis.ehf-footer .site-footer .wrap,.ehf-template-generatepress.ehf-header .site-header .inside-header {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
max-width: 100%
|
||||
}
|
||||
|
||||
.ehf-template-generatepress.ehf-header .site-header,.ehf-template-generatepress.ehf-footer .site-footer {
|
||||
width: 100%;
|
||||
padding: 0;
|
||||
max-width: 100%;
|
||||
background-color: transparent!important
|
||||
}
|
||||
|
||||
.bhf-hidden {
|
||||
display: none
|
||||
}
|
||||
|
||||
.ehf-header #masthead {
|
||||
z-index: 99;
|
||||
position: relative
|
||||
}
|
||||
979
public/css/vendor/vendor-global-overrides.css
vendored
@@ -1,979 +0,0 @@
|
||||
/*
|
||||
Theme Name: Logico Child
|
||||
Description: Logico Child Theme
|
||||
Theme URI: https://example.com/themes/logico
|
||||
Author: Artureanec
|
||||
Author URI: https://example.com
|
||||
Template: logico
|
||||
Version: 1.0.0
|
||||
License: GNU General Public License version 3.0
|
||||
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
Text Domain: logico-child
|
||||
*/
|
||||
|
||||
/* =Theme customization starts here
|
||||
------------------------------------------------------- */
|
||||
|
||||
/* Hide mobile-specific logo by default */
|
||||
.logo-mobile {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
MOBILE & TABLET HEADER REFINEMENT (< 1024px)
|
||||
Transition to mobile pill-shape earlier to prevent desktop overlap/wrapping
|
||||
============================================================ */
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
|
||||
/* Logo toggling */
|
||||
.logo-desktop {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.logo-mobile {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* 1. Header Wrapper (Outer): Position & Spacing */
|
||||
.elementor-5180 .elementor-element.elementor-element-466de1b {
|
||||
position: fixed !important;
|
||||
top: 30px !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
width: 100% !important;
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
height: auto !important;
|
||||
z-index: 10000 !important;
|
||||
background: transparent !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 2. The Header Box / Card (Inner) */
|
||||
.elementor-5180 .elementor-element.elementor-element-e052838 {
|
||||
margin: 0 10px auto !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
background-color: #ffffff !important;
|
||||
border-radius: 25px !important;
|
||||
overflow: hidden !important;
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.08) !important;
|
||||
pointer-events: all;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
/* 3. Main Row Layout Container */
|
||||
.elementor-5180 .elementor-element.elementor-element-d681ece {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
flex-wrap: nowrap !important;
|
||||
align-items: center !important;
|
||||
justify-content: space-between !important;
|
||||
width: 100% !important;
|
||||
padding: 12px 25px !important;
|
||||
min-height: 70px !important;
|
||||
box-sizing: border-box !important;
|
||||
gap: 0 !important;
|
||||
}
|
||||
|
||||
/* 4. Logo Container (Left) */
|
||||
.elementor-5180 .elementor-element.elementor-element-472172e {
|
||||
flex: 1 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-start !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* 5. Menu Container (Right / Hamburger) */
|
||||
.elementor-5180 .elementor-element.elementor-element-e44ee7e {
|
||||
flex: 0 0 auto !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-end !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* 6. Logo Widget Adjustments */
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d {
|
||||
width: auto !important;
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .hfe-site-logo .hfe-site-logo-container img {
|
||||
margin: 0 !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .logo-mobile {
|
||||
width: 180px !important;
|
||||
height: auto !important;
|
||||
min-width: 100px !important;
|
||||
}
|
||||
|
||||
/* 7. Menu Trigger (Hamburger) alignment */
|
||||
.elementor-5180 .elementor-element.elementor-element-0b7bf6f .menu-trigger {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-end !important;
|
||||
padding: 5px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* Hamburger icon customization */
|
||||
.elementor-5180 .elementor-element.elementor-element-0b7bf6f .hamburger {
|
||||
width: 30px !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .elementor-element.elementor-element-0b7bf6f .hamburger span {
|
||||
background-color: #1f1f1f !important;
|
||||
height: 2px !important;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
/* Hide desktop-only elements */
|
||||
.elementor-5180 .elementor-element.elementor-element-0b7bf6f .header-menu-container,
|
||||
.elementor-5180 .elementor-element.elementor-element-2f31137,
|
||||
.elementor-5180 .elementor-element.elementor-element-f961133 {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
MEDIUM DESKTOP OPTIMIZATION (1025px - 1480px)
|
||||
Prevents logo/menu overlap on medium screens
|
||||
============================================================ */
|
||||
@media (min-width: 1025px) and (max-width: 1480px) {
|
||||
/* Reduce logo size slightly */
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .logo-desktop {
|
||||
width: 130px !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Force single line and tighten spacing */
|
||||
.elementor-5180 .main-menu {
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .main-menu > li > a {
|
||||
padding-left: 8px !important;
|
||||
padding-right: 8px !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
/* Reduce container spacing */
|
||||
.elementor-5180 .elementor-element.elementor-element-d681ece {
|
||||
padding-left: 15px !important;
|
||||
padding-right: 15px !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
/* Ensure the menu pill container stays compact */
|
||||
.header-menu-container {
|
||||
padding-left: 8px !important;
|
||||
padding-right: 8px !important;
|
||||
max-width: 750px !important;
|
||||
}
|
||||
|
||||
/* Scale down the CTA button */
|
||||
.elementor-5180 .logico-small-button {
|
||||
padding: 10px 20px !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet Scaling (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .logo-mobile {
|
||||
width: 210px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Standard Mobile Scaling */
|
||||
@media (max-width: 767px) {
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .logo-mobile {
|
||||
width: 175px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extra Small Devise Optimization */
|
||||
@media (max-width: 480px) {
|
||||
.elementor-5180 .elementor-element.elementor-element-466de1b {
|
||||
top: 15px !important;
|
||||
padding-left: 15px !important;
|
||||
padding-right: 15px !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .elementor-element.elementor-element-d681ece {
|
||||
padding: 6px 20px !important;
|
||||
min-height: 55px !important; /* Smaller height for small screens */
|
||||
}
|
||||
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .logo-mobile {
|
||||
width: 155px !important; /* Scaled down for tiny screens */
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
HOME SECTION IMAGE RESPONSIVENESS (wp-image-4481)
|
||||
============================================================ */
|
||||
|
||||
/* Fix for Tablet and Mobile */
|
||||
@media (max-width: 1024px) {
|
||||
.elementor-element.elementor-element-99768ba .elementor-widget-container img.wp-image-4481 {
|
||||
width: 100% !important;
|
||||
max-width: 450px !important;
|
||||
/* Prevent oversized/zoomed appearance on tablets */
|
||||
height: auto !important;
|
||||
aspect-ratio: auto !important;
|
||||
/* Maintain original proportions */
|
||||
object-fit: contain !important;
|
||||
/* Ensure no cropping of important content */
|
||||
margin: 0 auto !important;
|
||||
/* Centering */
|
||||
display: block !important;
|
||||
padding-bottom: 0px !important;
|
||||
/* Spacing below the image */
|
||||
}
|
||||
|
||||
/* Ensure parent container centers the image */
|
||||
.elementor-element.elementor-element-99768ba {
|
||||
text-align: center !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Specific scaling for small mobile phones */
|
||||
@media (max-width: 767px) {
|
||||
.elementor-element.elementor-element-99768ba .elementor-widget-container img.wp-image-4481 {
|
||||
max-width: 90% !important;
|
||||
/* Reduce scale on small screens */
|
||||
margin: 0 auto !important;
|
||||
border-radius: 18px !important;
|
||||
/* Consistent rounded edges */
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
WHY CHOOSE DOORMILE SECTION RESPONSIVENESS (< 1020px)
|
||||
============================================================ */
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ============================================================
|
||||
PROBLEM SECTION - Responsive Layout Fix
|
||||
Targets: "Fragmented Logistics is Broken" section only
|
||||
No button styles included.
|
||||
============================================================ */
|
||||
|
||||
/* Prevent horizontal overflow on the outer container */
|
||||
.elementor-element.section-shrink-custom[data-id="30fd9d1"],
|
||||
.elementor-element.section-shrink-custom[data-id="30fd9d1"] > .e-con-inner {
|
||||
overflow-x: hidden !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
/* Full width with padding so content doesn't touch screen edges */
|
||||
.elementor-element.section-shrink-custom[data-id="30fd9d1"] > .e-con-inner {
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
}
|
||||
|
||||
/* The 4-icon stat grid: wrap into 2 columns on tablet */
|
||||
.elementor-element.elementor-element-e09c20e {
|
||||
grid-template-columns: repeat(2, 1fr) !important;
|
||||
gap: 24px !important;
|
||||
}
|
||||
|
||||
/* Description text: prevent overflow */
|
||||
.elementor-element.elementor-element-5a5c397,
|
||||
.elementor-element.elementor-element-07cd509 {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-07cd509 p {
|
||||
width: 100% !important;
|
||||
overflow-wrap: break-word !important;
|
||||
word-break: break-word !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
/* Single column on small mobile */
|
||||
.elementor-element.elementor-element-e09c20e {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
HEADER SCROLL VISIBILITY
|
||||
Home page: hidden until scroll. All other pages: always visible.
|
||||
============================================================ */
|
||||
|
||||
/* Home page: header fades in after scroll (JS adds .header-visible-scrolled) */
|
||||
.is-home-page .header-hide-until-scroll {
|
||||
opacity: 0 !important;
|
||||
visibility: hidden !important;
|
||||
transform: translateY(-20px);
|
||||
transition: opacity 0.5s ease, visibility 0.5s ease, transform 0.5s ease !important;
|
||||
}
|
||||
|
||||
.is-home-page .header-hide-until-scroll.header-visible-scrolled {
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
|
||||
/* Non-home pages: always show header */
|
||||
body:not(.is-home-page) .header-hide-until-scroll {
|
||||
opacity: 1 !important;
|
||||
visibility: visible !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
PROBLEM SECTION - Responsive Text & Layout Fix
|
||||
============================================================ */
|
||||
|
||||
/* Prevent overflow at the section container level */
|
||||
.elementor-element.section-shrink-custom[data-id="30fd9d1"] {
|
||||
overflow-x: hidden !important;
|
||||
max-width: 100vw !important;
|
||||
}
|
||||
|
||||
.elementor-element.section-shrink-custom[data-id="30fd9d1"] > .e-con-inner {
|
||||
max-width: 1480px !important;
|
||||
width: 100% !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* @media (min-width: 1300px) {
|
||||
|
||||
.elementor-element.section-shrink-custom[data-id="30fd9d1"] > .e-con-inner {
|
||||
max-width: 1300px !important;
|
||||
padding: 0px 50px 10px !important;
|
||||
}
|
||||
} */
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.elementor-element.section-shrink-custom[data-id="30fd9d1"] > .e-con-inner {
|
||||
max-width: 100% !important;
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
}
|
||||
|
||||
/* Left column (heading + stats + text): full width */
|
||||
.elementor-element.elementor-element-03db5d7 {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* Stats grid: 2 columns on tablet */
|
||||
.elementor-element.elementor-element-e09c20e {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(2, 1fr) !important;
|
||||
gap: 20px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* Description text block: prevent overflow */
|
||||
.elementor-element.elementor-element-5a5c397 {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-07cd509 {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
/* Force all text to wrap properly */
|
||||
.section-shrink-custom p,
|
||||
.section-shrink-custom h3,
|
||||
.section-shrink-custom span {
|
||||
max-width: 100% !important;
|
||||
word-break: break-word !important;
|
||||
overflow-wrap: break-word !important;
|
||||
white-space: normal !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
/* Single column stats on small phones */
|
||||
.elementor-element.elementor-element-e09c20e {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
PAGE LOADER - Must cover header on all viewports
|
||||
Header uses z-index: 10000 so loader needs higher
|
||||
============================================================ */
|
||||
.page-loader-container {
|
||||
z-index: 100000 !important;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
CONNECTED LOGISTICS - India Map Image Sizing
|
||||
Widen the map column to fill the gap on the right side
|
||||
============================================================ */
|
||||
@media (min-width: 1021px) {
|
||||
/* Image column: force wider */
|
||||
|
||||
|
||||
/* Text column: take the remaining space */
|
||||
|
||||
|
||||
/* Map image: fill the wider container */
|
||||
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
INDUSTRY SOLUTIONS - Hover Effect Override
|
||||
Image fills box, content shows on hover with #c01227 shade.
|
||||
============================================================ */
|
||||
|
||||
.elementor-element.elementor-element-b891c78,
|
||||
.elementor-element.elementor-element-9b933db,
|
||||
.elementor-element.elementor-element-280b0dc {
|
||||
position: relative !important;
|
||||
overflow: hidden !important;
|
||||
height: 620px !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
justify-content: flex-end !important;
|
||||
align-items: flex-start !important;
|
||||
text-align: left !important;
|
||||
padding: 40px 40px 80px 40px !important;
|
||||
|
||||
|
||||
border-radius: 20px !important;
|
||||
background-color: #f7f7f7 !important;
|
||||
transition: all 0.4s ease !important;
|
||||
}
|
||||
|
||||
|
||||
/* Make image fill the entire card box */
|
||||
.elementor-element.elementor-element-b891c78 .elementor-widget-image,
|
||||
.elementor-element.elementor-element-9b933db .elementor-widget-image,
|
||||
.elementor-element.elementor-element-280b0dc .elementor-widget-image {
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-b891c78 .elementor-widget-image img,
|
||||
.elementor-element.elementor-element-9b933db .elementor-widget-image img,
|
||||
.elementor-element.elementor-element-280b0dc .elementor-widget-image img {
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
object-fit: cover !important;
|
||||
border-radius: 20px !important;
|
||||
transition: transform 0.6s ease !important;
|
||||
}
|
||||
|
||||
/* Persistent bottom overlay before hover #c01227 */
|
||||
.elementor-element.elementor-element-b891c78::after,
|
||||
.elementor-element.elementor-element-9b933db::after,
|
||||
.elementor-element.elementor-element-280b0dc::after {
|
||||
content: "" !important;
|
||||
position: absolute !important;
|
||||
bottom: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 120px !important;
|
||||
background-image: linear-gradient(to top, rgba(34, 34, 34, 0.9), rgba(192, 18, 39, 0)) !important;
|
||||
z-index: 1 !important;
|
||||
opacity: 1 !important;
|
||||
border-radius: 0 0 20px 20px !important;
|
||||
transition: opacity 0.4s ease !important;
|
||||
pointer-events: none !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-b891c78:hover::after,
|
||||
.elementor-element.elementor-element-9b933db:hover::after,
|
||||
.elementor-element.elementor-element-280b0dc:hover::after {
|
||||
opacity: 0 !important;
|
||||
}
|
||||
|
||||
/* Hover background shade overlay #c01227 */
|
||||
|
||||
.elementor-element.elementor-element-b891c78::before,
|
||||
.elementor-element.elementor-element-9b933db::before,
|
||||
.elementor-element.elementor-element-280b0dc::before {
|
||||
content: "" !important;
|
||||
position: absolute !important;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
background-image: linear-gradient(to top, rgba(192, 18, 39, 0.95), rgba(192, 18, 39, 0.2)) !important;
|
||||
|
||||
z-index: 2 !important;
|
||||
opacity: 0 !important;
|
||||
transition: opacity 0.4s ease !important;
|
||||
border-radius: 20px !important;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-b891c78:hover::before,
|
||||
.elementor-element.elementor-element-9b933db:hover::before,
|
||||
.elementor-element.elementor-element-280b0dc:hover::before {
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* Hide content until hover (explicitly target text and lists) */
|
||||
.elementor-element.elementor-element-b891c78 .elementor-widget-text-editor,
|
||||
.elementor-element.elementor-element-9b933db .elementor-widget-text-editor,
|
||||
.elementor-element.elementor-element-280b0dc .elementor-widget-text-editor,
|
||||
.elementor-element.elementor-element-b891c78 .elementor-widget-divider,
|
||||
.elementor-element.elementor-element-9b933db .elementor-widget-divider,
|
||||
.elementor-element.elementor-element-280b0dc .elementor-widget-divider,
|
||||
.elementor-element.elementor-element-b891c78 .hover-list-content,
|
||||
.elementor-element.elementor-element-9b933db .hover-list-content,
|
||||
.elementor-element.elementor-element-280b0dc .hover-list-content {
|
||||
position: relative !important;
|
||||
z-index: 3 !important;
|
||||
opacity: 0 !important;
|
||||
transform: translateY(20px) !important;
|
||||
transition: opacity 0.4s ease, transform 0.4s ease !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-b891c78:hover .elementor-widget-text-editor,
|
||||
.elementor-element.elementor-element-9b933db:hover .elementor-widget-text-editor,
|
||||
.elementor-element.elementor-element-280b0dc:hover .elementor-widget-text-editor,
|
||||
.elementor-element.elementor-element-b891c78:hover .elementor-widget-divider,
|
||||
.elementor-element.elementor-element-9b933db:hover .elementor-widget-divider,
|
||||
.elementor-element.elementor-element-280b0dc:hover .elementor-widget-divider,
|
||||
.elementor-element.elementor-element-b891c78:hover .hover-list-content,
|
||||
.elementor-element.elementor-element-9b933db:hover .hover-list-content,
|
||||
.elementor-element.elementor-element-280b0dc:hover .hover-list-content {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
|
||||
|
||||
/* Force text to be white and clean */
|
||||
.elementor-element.elementor-element-b891c78 .logico-title,
|
||||
.elementor-element.elementor-element-9b933db .logico-title,
|
||||
.elementor-element.elementor-element-280b0dc .logico-title,
|
||||
.elementor-element.elementor-element-b891c78 p,
|
||||
.elementor-element.elementor-element-9b933db p,
|
||||
.elementor-element.elementor-element-280b0dc p {
|
||||
position: relative !important;
|
||||
z-index: 5 !important;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-b891c78 .logico-title,
|
||||
.elementor-element.elementor-element-9b933db .logico-title,
|
||||
.elementor-element.elementor-element-280b0dc .logico-title {
|
||||
color: #fff !important;
|
||||
text-shadow: 0px 2px 10px rgba(255, 255, 255, 0.4) !important;
|
||||
font-size: 24px !important;
|
||||
font-weight: 700 !important;
|
||||
margin-bottom: 15px !important;
|
||||
text-align: left !important;
|
||||
transition: all 0.3s ease !important;
|
||||
position: relative !important;
|
||||
padding-left: 0px !important;
|
||||
cursor: pointer !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-b891c78:hover .logico-title,
|
||||
.elementor-element.elementor-element-9b933db:hover .logico-title,
|
||||
.elementor-element.elementor-element-280b0dc:hover .logico-title {
|
||||
padding-left: 45px !important;
|
||||
}
|
||||
|
||||
|
||||
/* White Circle Arrow Icon Wrapper */
|
||||
.title-icon {
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
position: absolute !important;
|
||||
left: 0 !important;
|
||||
top: 50% !important;
|
||||
transform: translateY(-50%) scale(0.6) !important;
|
||||
width: 32px !important;
|
||||
height: 32px !important;
|
||||
background-color: #ffffff !important;
|
||||
border-radius: 50% !important;
|
||||
color: #000000 !important;
|
||||
opacity: 0 !important;
|
||||
transition: opacity 0.3s ease, transform 0.3s ease !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-b891c78:hover .title-icon,
|
||||
.elementor-element.elementor-element-9b933db:hover .title-icon,
|
||||
.elementor-element.elementor-element-280b0dc:hover .title-icon {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(-50%) scale(1) !important;
|
||||
}
|
||||
|
||||
|
||||
.title-icon svg {
|
||||
width: 18px !important;
|
||||
height: 18px !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
.elementor-element.elementor-element-b891c78:hover .logico-title,
|
||||
.elementor-element.elementor-element-9b933db:hover .logico-title,
|
||||
.elementor-element.elementor-element-280b0dc:hover .logico-title,
|
||||
.elementor-element.elementor-element-b891c78:hover p,
|
||||
.elementor-element.elementor-element-9b933db:hover p,
|
||||
.elementor-element.elementor-element-280b0dc:hover p {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
|
||||
.elementor-element.elementor-element-b891c78:hover .elementor-divider-separator,
|
||||
.elementor-element.elementor-element-9b933db:hover .elementor-divider-separator,
|
||||
.elementor-element.elementor-element-280b0dc:hover .elementor-divider-separator {
|
||||
border-top-color: rgba(255, 255, 255, 0.4) !important;
|
||||
}
|
||||
|
||||
/* Lists styling on Hover */
|
||||
.hover-list-content {
|
||||
position: relative !important;
|
||||
z-index: 5 !important;
|
||||
opacity: 0 !important;
|
||||
transform: translateY(20px) !important;
|
||||
transition: opacity 0.4s ease 0.1s, transform 0.4s ease 0.1s !important; /* staggered entry */
|
||||
|
||||
width: 100% !important;
|
||||
margin-top: 25px !important;
|
||||
margin-bottom: 20px !important;
|
||||
|
||||
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-b891c78:hover .hover-list-content,
|
||||
.elementor-element.elementor-element-9b933db:hover .hover-list-content,
|
||||
.elementor-element.elementor-element-280b0dc:hover .hover-list-content {
|
||||
opacity: 1 !important;
|
||||
transform: translateY(0) !important;
|
||||
}
|
||||
|
||||
.list-section {
|
||||
margin-bottom: 25px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
|
||||
.list-section:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.list-section-title {
|
||||
color: #e2e8f0 !important;
|
||||
font-size: 13px !important;
|
||||
font-weight: 700 !important;
|
||||
letter-spacing: 1.5px !important;
|
||||
margin-bottom: 12px !important;
|
||||
text-transform: uppercase !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.hover-items-list {
|
||||
list-style: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
.hover-items-list li {
|
||||
display: flex !important;
|
||||
justify-content: flex-start !important;
|
||||
align-items: flex-start !important;
|
||||
gap: 12px !important;
|
||||
margin-bottom: 12px !important;
|
||||
color: #ffffff !important;
|
||||
font-size: 16px !important;
|
||||
line-height: 1.4 !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.hover-items-list li::before {
|
||||
content: "•" !important;
|
||||
color: #ffffff !important;
|
||||
font-size: 20px !important;
|
||||
line-height: 1 !important;
|
||||
margin-top: -3px !important;
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
.hover-items-list li:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.hover-items-list li span {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* Push headers to bottom */
|
||||
.elementor-element.elementor-element-b891c78 .elementor-widget-logico_heading,
|
||||
.elementor-element.elementor-element-9b933db .elementor-widget-logico_heading,
|
||||
.elementor-element.elementor-element-280b0dc .elementor-widget-logico_heading {
|
||||
position: absolute !important;
|
||||
bottom: 30px !important;
|
||||
left: 30px !important;
|
||||
z-index: 10 !important;
|
||||
margin: 0 !important;
|
||||
text-align: left !important;
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
FOOTER CONTACT LINKS - Contrast Fix
|
||||
============================================================ */
|
||||
.elementor-6585 a,
|
||||
.elementor-6585 a::before,
|
||||
.elementor-6585 a::after {
|
||||
color: #FFFFFF !important;
|
||||
text-decoration: none !important;
|
||||
transition: all 0.4s ease-in-out !important;
|
||||
}
|
||||
|
||||
/* Apply red hover color only to simple links, avoiding buttons */
|
||||
.elementor-6585 a:not(.logico-alter-button):hover {
|
||||
color: #c01227 !important;
|
||||
}
|
||||
|
||||
.elementor-6585 a.logico-alter-button:hover {
|
||||
opacity: 0.9 !important;
|
||||
}
|
||||
|
||||
/* Specific fix for phone and email links to ensure no default theme underlines */
|
||||
.elementor-element-87be926 a,
|
||||
.elementor-element-ba67644 a {
|
||||
border-bottom: none !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
SOCIAL ICONS VISIBILITY FIX
|
||||
Ensures both font-based and SVG icons are visible and properly sized.
|
||||
Targeting the common .wrapper-socials class used across the site.
|
||||
============================================================ */
|
||||
.e-font-icon-svg {
|
||||
display: inline-block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
fill: currentColor;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.elementor-social-icon svg {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.fab, .fas, .far {
|
||||
display: inline-block !important;
|
||||
}
|
||||
|
||||
.wrapper-socials {
|
||||
list-style: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.wrapper-socials li {
|
||||
display: inline-block !important;
|
||||
margin: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.wrapper-socials li:before {
|
||||
content: none !important;
|
||||
}
|
||||
|
||||
.wrapper-socials a {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
color: inherit;
|
||||
text-decoration: none !important;
|
||||
transition: all 0.3s ease;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.wrapper-socials a:hover {
|
||||
background-color: var(--logico-accent-color, #c01227);
|
||||
color: #fff !important;
|
||||
}
|
||||
|
||||
.wrapper-socials a svg {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
/* Global visibility enforcement for Elementor and other social icons */
|
||||
.elementor-social-icon,
|
||||
.e-font-icon-svg,
|
||||
.wrapper-socials i,
|
||||
.wrapper-socials svg {
|
||||
display: inline-block !important;
|
||||
visibility: visible !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
/* Ensure SVG icons have at least some size if not specified */
|
||||
svg.e-font-icon-svg,
|
||||
.elementor-social-icon svg {
|
||||
min-width: 16px;
|
||||
min-height: 16px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* ============================================================
|
||||
REDESIGNED STEP CARDS (How It Works) - BLACK TEMPLATE
|
||||
============================================================ */
|
||||
.dm-step-card {
|
||||
background: #111111;
|
||||
border: 1px solid #222222;
|
||||
border-radius: 25px;
|
||||
padding: 35px;
|
||||
height: 100%;
|
||||
transition: all 0.4s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2);
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.dm-step-card:hover {
|
||||
border-color: #c01227;
|
||||
background: #161616;
|
||||
box-shadow: 0 30px 60px rgba(192, 18, 39, 0.3), 0 0 30px rgba(192, 18, 39, 0.15), 0 10px 25px rgba(0, 0, 0, 0.5);
|
||||
transform: translateY(-10px) scale(1.02);
|
||||
}
|
||||
|
||||
.dm-step-card__image {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dm-step-card__image img {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border-radius: 20px;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
opacity: 0.9;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.dm-step-card:hover .dm-step-card__image img {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dm-step-card__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.dm-step-card__num {
|
||||
color: #c01227;
|
||||
font-weight: 800;
|
||||
text-transform: uppercase;
|
||||
font-size: 13px;
|
||||
letter-spacing: 1.5px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dm-step-card__title {
|
||||
font-size: 26px;
|
||||
font-weight: 800;
|
||||
color: #ffffff;
|
||||
margin: 0;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.dm-step-card__text {
|
||||
font-size: 15px;
|
||||
line-height: 1.6;
|
||||
color: #aaaaaa;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.dm-step-card__list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 10px 0 0 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.dm-step-card__list li {
|
||||
position: relative;
|
||||
padding-left: 28px;
|
||||
font-size: 14px;
|
||||
color: #dddddd;
|
||||
font-weight: 700;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.dm-step-card__list li::before {
|
||||
content: "\f061"; /* FontAwesome arrow-right */
|
||||
font-family: "Font Awesome 6 Free";
|
||||
font-weight: 900;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 1px;
|
||||
color: #c01227;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
@font-face {
|
||||
font-display: swap;
|
||||
font-family: 'fontello';
|
||||
src: url(/fonts/fontello.woff2?98791691) format("woff2");
|
||||
font-weight: 400;
|
||||
font-style: normal
|
||||
}
|
||||
|
||||
523
public/css/vendor/vendor-icons-fontello.css
vendored
@@ -1,523 +0,0 @@
|
||||
[class^="icon-"]:before,[class*=" icon-"]:before {
|
||||
font-family: 'fontello';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
speak: never;
|
||||
display: inline-block;
|
||||
text-decoration: inherit;
|
||||
width: 1em;
|
||||
text-align: center;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1em;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
}
|
||||
|
||||
.icon-arrow-down:before {
|
||||
content: '\e800'
|
||||
}
|
||||
|
||||
.icon-arrow-right:before {
|
||||
content: '\e801'
|
||||
}
|
||||
|
||||
.icon-arrow-left:before {
|
||||
content: '\e802'
|
||||
}
|
||||
|
||||
.icon-arrow-top:before {
|
||||
content: '\e803'
|
||||
}
|
||||
|
||||
.icon-arrow-x-l-down:before {
|
||||
content: '\e804'
|
||||
}
|
||||
|
||||
.icon-arrow-x-r-down:before {
|
||||
content: '\e805'
|
||||
}
|
||||
|
||||
.icon-arrow-x-l-top:before {
|
||||
content: '\e806'
|
||||
}
|
||||
|
||||
.icon-button-arrow-x-l-down:before {
|
||||
content: '\e807'
|
||||
}
|
||||
|
||||
.icon-button-arrow-x-r-down:before {
|
||||
content: '\e808'
|
||||
}
|
||||
|
||||
.icon-button-arrow-x-l-top:before {
|
||||
content: '\e809'
|
||||
}
|
||||
|
||||
.icon-arrow-x-r-top:before {
|
||||
content: '\e80a'
|
||||
}
|
||||
|
||||
.icon-button-arrow-x-r-top:before {
|
||||
content: '\e80b'
|
||||
}
|
||||
|
||||
.icon-chevron-large-down:before {
|
||||
content: '\e80c'
|
||||
}
|
||||
|
||||
.icon-chevron-large-left:before {
|
||||
content: '\e80d'
|
||||
}
|
||||
|
||||
.icon-chevron-large-top:before {
|
||||
content: '\e80e'
|
||||
}
|
||||
|
||||
.icon-chevron-large-right:before {
|
||||
content: '\e80f'
|
||||
}
|
||||
|
||||
.icon-chevron-medium-down:before {
|
||||
content: '\e810'
|
||||
}
|
||||
|
||||
.icon-chevron-medium-left:before {
|
||||
content: '\e811'
|
||||
}
|
||||
|
||||
.icon-chevron-medium-right:before {
|
||||
content: '\e812'
|
||||
}
|
||||
|
||||
.icon-chevron-medium-top:before {
|
||||
content: '\e813'
|
||||
}
|
||||
|
||||
.icon-chevron-small-left:before {
|
||||
content: '\e814'
|
||||
}
|
||||
|
||||
.icon-chevron-small-top:before {
|
||||
content: '\e815'
|
||||
}
|
||||
|
||||
.icon-chevron-small-down:before {
|
||||
content: '\e816'
|
||||
}
|
||||
|
||||
.icon-chevron-small-right:before {
|
||||
content: '\e817'
|
||||
}
|
||||
|
||||
.icon-goods-export-1:before {
|
||||
content: '\e819'
|
||||
}
|
||||
|
||||
.icon-goods-export-2:before {
|
||||
content: '\e81a'
|
||||
}
|
||||
|
||||
.icon-goods-export-3:before {
|
||||
content: '\e81b'
|
||||
}
|
||||
|
||||
.icon-goods-export-4:before {
|
||||
content: '\e81c'
|
||||
}
|
||||
|
||||
.icon-goods-export-5:before {
|
||||
content: '\e81d'
|
||||
}
|
||||
|
||||
.icon-goods-export-6:before {
|
||||
content: '\e81e'
|
||||
}
|
||||
|
||||
.icon-goods-export-7:before {
|
||||
content: '\e81f'
|
||||
}
|
||||
|
||||
.icon-goods-export-8:before {
|
||||
content: '\e820'
|
||||
}
|
||||
|
||||
.icon-goods-export-9:before {
|
||||
content: '\e821'
|
||||
}
|
||||
|
||||
.icon-goods-export-10:before {
|
||||
content: '\e822'
|
||||
}
|
||||
|
||||
.icon-goods-export-11:before {
|
||||
content: '\e823'
|
||||
}
|
||||
|
||||
.icon-goods-export-12:before {
|
||||
content: '\e824'
|
||||
}
|
||||
|
||||
.icon-goods-export-13:before {
|
||||
content: '\e825'
|
||||
}
|
||||
|
||||
.icon-goods-export-14:before {
|
||||
content: '\e826'
|
||||
}
|
||||
|
||||
.icon-goods-export-15:before {
|
||||
content: '\e827'
|
||||
}
|
||||
|
||||
.icon-goods-export-16:before {
|
||||
content: '\e828'
|
||||
}
|
||||
|
||||
.icon-goods-export-17:before {
|
||||
content: '\e829'
|
||||
}
|
||||
|
||||
.icon-goods-export-18:before {
|
||||
content: '\e82a'
|
||||
}
|
||||
|
||||
.icon-goods-export-19:before {
|
||||
content: '\e82b'
|
||||
}
|
||||
|
||||
.icon-goods-export-20:before {
|
||||
content: '\e82c'
|
||||
}
|
||||
|
||||
.icon-goods-export-21:before {
|
||||
content: '\e82d'
|
||||
}
|
||||
|
||||
.icon-goods-export-22:before {
|
||||
content: '\e82e'
|
||||
}
|
||||
|
||||
.icon-goods-export-23:before {
|
||||
content: '\e82f'
|
||||
}
|
||||
|
||||
.icon-goods-export-24:before {
|
||||
content: '\e830'
|
||||
}
|
||||
|
||||
.icon-goods-export-25:before {
|
||||
content: '\e831'
|
||||
}
|
||||
|
||||
.icon-goods-export-26:before {
|
||||
content: '\e832'
|
||||
}
|
||||
|
||||
.icon-goods-export-27:before {
|
||||
content: '\e833'
|
||||
}
|
||||
|
||||
.icon-goods-export-28:before {
|
||||
content: '\e834'
|
||||
}
|
||||
|
||||
.icon-goods-export-29:before {
|
||||
content: '\e835'
|
||||
}
|
||||
|
||||
.icon-goods-export-30:before {
|
||||
content: '\e836'
|
||||
}
|
||||
|
||||
.icon-goods-export-31:before {
|
||||
content: '\e837'
|
||||
}
|
||||
|
||||
.icon-goods-export-32:before {
|
||||
content: '\e838'
|
||||
}
|
||||
|
||||
.icon-light-container:before {
|
||||
content: '\e839'
|
||||
}
|
||||
|
||||
.icon-light-delivery:before {
|
||||
content: '\e83a'
|
||||
}
|
||||
|
||||
.icon-light-directions:before {
|
||||
content: '\e83b'
|
||||
}
|
||||
|
||||
.icon-light-storage:before {
|
||||
content: '\e83c'
|
||||
}
|
||||
|
||||
.icon-triangle-arrow-down:before {
|
||||
content: '\e83d'
|
||||
}
|
||||
|
||||
.icon-triangle-arrow-left:before {
|
||||
content: '\e83e'
|
||||
}
|
||||
|
||||
.icon-triangle-arrow-right:before {
|
||||
content: '\e83f'
|
||||
}
|
||||
|
||||
.icon-triangle-arrow-top:before {
|
||||
content: '\e840'
|
||||
}
|
||||
|
||||
.icon-air-delivery:before {
|
||||
content: '\e841'
|
||||
}
|
||||
|
||||
.icon-call:before {
|
||||
content: '\e842'
|
||||
}
|
||||
|
||||
.icon-cart:before {
|
||||
content: '\e843'
|
||||
}
|
||||
|
||||
.icon-calendar:before {
|
||||
content: '\e844'
|
||||
}
|
||||
|
||||
.icon-close:before {
|
||||
content: '\e845'
|
||||
}
|
||||
|
||||
.icon-container:before {
|
||||
content: '\e846'
|
||||
}
|
||||
|
||||
.icon-coupon:before {
|
||||
content: '\e847'
|
||||
}
|
||||
|
||||
.icon-crop:before {
|
||||
content: '\e848'
|
||||
}
|
||||
|
||||
.icon-download:before {
|
||||
content: '\e849'
|
||||
}
|
||||
|
||||
.icon-download-alter:before {
|
||||
content: '\e84a'
|
||||
}
|
||||
|
||||
.icon-eye:before {
|
||||
content: '\e84b'
|
||||
}
|
||||
|
||||
.icon-file-doc:before {
|
||||
content: '\e84c'
|
||||
}
|
||||
|
||||
.icon-file-file:before {
|
||||
content: '\e84d'
|
||||
}
|
||||
|
||||
.icon-file-pdf:before {
|
||||
content: '\e84e'
|
||||
}
|
||||
|
||||
.icon-garland:before {
|
||||
content: '\e84f'
|
||||
}
|
||||
|
||||
.icon-font:before {
|
||||
content: '\e850'
|
||||
}
|
||||
|
||||
.icon-half-logo:before {
|
||||
content: '\e851'
|
||||
}
|
||||
|
||||
.icon-logo:before {
|
||||
content: '\e852'
|
||||
}
|
||||
|
||||
.icon-location:before {
|
||||
content: '\e853'
|
||||
}
|
||||
|
||||
.icon-hiring:before {
|
||||
content: '\e854'
|
||||
}
|
||||
|
||||
.icon-mail:before {
|
||||
content: '\e855'
|
||||
}
|
||||
|
||||
.icon-package-delivery:before {
|
||||
content: '\e856'
|
||||
}
|
||||
|
||||
.icon-paint:before {
|
||||
content: '\e857'
|
||||
}
|
||||
|
||||
.icon-print:before {
|
||||
content: '\e858'
|
||||
}
|
||||
|
||||
.icon-resize:before {
|
||||
content: '\e85a'
|
||||
}
|
||||
|
||||
.icon-sidebar:before {
|
||||
content: '\e85b'
|
||||
}
|
||||
|
||||
.icon-search:before {
|
||||
content: '\e85c'
|
||||
}
|
||||
|
||||
.icon-side-menu-black:before {
|
||||
content: '\e85d'
|
||||
}
|
||||
|
||||
.icon-side-menu-light:before {
|
||||
content: '\e85e'
|
||||
}
|
||||
|
||||
.icon-star-light:before {
|
||||
content: '\e861'
|
||||
}
|
||||
|
||||
.icon-star:before {
|
||||
content: '\e862'
|
||||
}
|
||||
|
||||
.icon-supply-chain:before {
|
||||
content: '\e863'
|
||||
}
|
||||
|
||||
.icon-tap:before {
|
||||
content: '\e864'
|
||||
}
|
||||
|
||||
.icon-terms:before {
|
||||
content: '\e865'
|
||||
}
|
||||
|
||||
.icon-translate:before {
|
||||
content: '\e866'
|
||||
}
|
||||
|
||||
.icon-trolley:before {
|
||||
content: '\e867'
|
||||
}
|
||||
|
||||
.icon-update:before {
|
||||
content: '\e868'
|
||||
}
|
||||
|
||||
.icon-user:before {
|
||||
content: '\e869'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-container:before {
|
||||
content: '\e86a'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-conveyor:before {
|
||||
content: '\e86b'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-delivery:before {
|
||||
content: '\e86c'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-hand-loader:before {
|
||||
content: '\e86d'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-loader:before {
|
||||
content: '\e86e'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-loader-in-work:before {
|
||||
content: '\e86f'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-manual-loader-box:before {
|
||||
content: '\e870'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-manual-transportation:before {
|
||||
content: '\e871'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-scales:before {
|
||||
content: '\e872'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-scanner:before {
|
||||
content: '\e873'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-palett:before {
|
||||
content: '\e874'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-security:before {
|
||||
content: '\e875'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-storage:before {
|
||||
content: '\e876'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-truck:before {
|
||||
content: '\e877'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-warehouse:before {
|
||||
content: '\e878'
|
||||
}
|
||||
|
||||
.icon-solid-warehousing-autoloader:before {
|
||||
content: '\e879'
|
||||
}
|
||||
|
||||
.icon-check:before {
|
||||
content: '\e87a'
|
||||
}
|
||||
|
||||
.icon-check-alter:before {
|
||||
content: '\e87b'
|
||||
}
|
||||
|
||||
.icon-quote-left:before {
|
||||
content: '\e87c'
|
||||
}
|
||||
|
||||
.icon-quote-right:before {
|
||||
content: '\e87d'
|
||||
}
|
||||
|
||||
.icon-quote-right-light:before {
|
||||
content: '\e87e'
|
||||
}
|
||||
|
||||
.icon-play:before {
|
||||
content: '\e8a2'
|
||||
}
|
||||
|
||||
.icon-play-active:before {
|
||||
content: '\e8a3'
|
||||
}
|
||||
|
||||
.icon-icon-rounded:before {
|
||||
content: '\e8f9'
|
||||
}
|
||||
|
||||
.icon-icon-angular:before {
|
||||
content: '\e8fa'
|
||||
}
|
||||
200
public/css/vendor/vendor-layout-main.css
vendored
@@ -1,200 +0,0 @@
|
||||
/*
|
||||
Theme Name: Logico Child
|
||||
Description: Logico Child Theme
|
||||
Theme URI: https://example.com/themes/logico
|
||||
Author: Artureanec
|
||||
Author URI: https://example.com
|
||||
Template: logico
|
||||
Version: 1.0.0
|
||||
License: GNU General Public License version 3.0
|
||||
License URI: http://www.gnu.org/licenses/gpl-3.0.html
|
||||
Text Domain: logico-child
|
||||
*/
|
||||
|
||||
/* =Theme customization starts here
|
||||
------------------------------------------------------- */
|
||||
|
||||
/* Hide mobile-specific logo by default */
|
||||
.logo-mobile {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
MOBILE & TABLET HEADER REFINEMENT (< 1024px)
|
||||
Transition to mobile pill-shape earlier to prevent desktop overlap/wrapping
|
||||
============================================================ */
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
|
||||
/* Logo toggling */
|
||||
.logo-desktop {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.logo-mobile {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* 1. Header Wrapper (Outer): Position & Spacing */
|
||||
.elementor-5180 .elementor-element.elementor-element-466de1b {
|
||||
position: fixed !important;
|
||||
top: 30px !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
width: 100% !important;
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
height: auto !important;
|
||||
z-index: 10000 !important;
|
||||
background: transparent !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 2. The Header Box / Card (Inner) */
|
||||
.elementor-5180 .elementor-element.elementor-element-e052838 {
|
||||
margin: 0 10px auto !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
background-color: #ffffff !important;
|
||||
border-radius: 25px !important;
|
||||
overflow: hidden !important;
|
||||
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.08) !important;
|
||||
pointer-events: all;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
/* 3. Main Row Layout Container */
|
||||
.elementor-5180 .elementor-element.elementor-element-d681ece {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
flex-wrap: nowrap !important;
|
||||
align-items: center !important;
|
||||
justify-content: space-between !important;
|
||||
width: 100% !important;
|
||||
padding: 12px 25px !important;
|
||||
min-height: 70px !important;
|
||||
box-sizing: border-box !important;
|
||||
gap: 0 !important;
|
||||
}
|
||||
|
||||
/* 4. Logo Container (Left) */
|
||||
.elementor-5180 .elementor-element.elementor-element-472172e {
|
||||
flex: 1 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-start !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* 5. Menu Container (Right / Hamburger) */
|
||||
.elementor-5180 .elementor-element.elementor-element-e44ee7e {
|
||||
flex: 0 0 auto !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-end !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* 6. Logo Widget Adjustments */
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d {
|
||||
width: auto !important;
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .hfe-site-logo .hfe-site-logo-container img {
|
||||
margin: 0 !important;
|
||||
max-width: none !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .logo-mobile {
|
||||
width: 180px !important;
|
||||
height: auto !important;
|
||||
min-width: 100px !important;
|
||||
}
|
||||
|
||||
/* 7. Menu Trigger (Hamburger) alignment */
|
||||
.elementor-5180 .elementor-element.elementor-element-0b7bf6f .menu-trigger {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: flex-end !important;
|
||||
padding: 5px !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
||||
/* Hamburger icon customization */
|
||||
.elementor-5180 .elementor-element.elementor-element-0b7bf6f .hamburger {
|
||||
width: 30px !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .elementor-element.elementor-element-0b7bf6f .hamburger span {
|
||||
background-color: #1f1f1f !important;
|
||||
height: 2px !important;
|
||||
margin-bottom: 5px !important;
|
||||
}
|
||||
|
||||
/* Hide desktop-only elements */
|
||||
.elementor-5180 .elementor-element.elementor-element-0b7bf6f .header-menu-container,
|
||||
.elementor-5180 .elementor-element.elementor-element-2f31137,
|
||||
.elementor-5180 .elementor-element.elementor-element-f961133 {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
MEDIUM DESKTOP OPTIMIZATION (1025px - 1480px)
|
||||
Prevents logo/menu overlap on medium screens
|
||||
============================================================ */
|
||||
@media (min-width: 1025px) and (max-width: 1480px) {
|
||||
/* Reduce logo size slightly */
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .logo-desktop {
|
||||
width: 130px !important;
|
||||
height: auto !important;
|
||||
}
|
||||
|
||||
/* Force single line and tighten spacing */
|
||||
.elementor-5180 .main-menu {
|
||||
display: flex !important;
|
||||
flex-wrap: nowrap !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
.elementor-5180 .main-menu > li > a {
|
||||
padding-left: 8px !important;
|
||||
padding-right: 8px !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
/* Reduce container spacing */
|
||||
.elementor-5180 .elementor-element.elementor-element-d681ece {
|
||||
padding-left: 15px !important;
|
||||
padding-right: 15px !important;
|
||||
gap: 10px !important;
|
||||
}
|
||||
|
||||
/* Ensure the menu pill container stays compact */
|
||||
.header-menu-container {
|
||||
padding-left: 8px !important;
|
||||
padding-right: 8px !important;
|
||||
max-width: 750px !important;
|
||||
}
|
||||
|
||||
/* Scale down the CTA button */
|
||||
.elementor-5180 .logico-small-button {
|
||||
padding: 10px 20px !important;
|
||||
font-size: 13px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Tablet Scaling (768px - 1024px) */
|
||||
@media (max-width: 1024px) and (min-width: 768px) {
|
||||
.elementor-5180 .elementor-element.elementor-element-846e53d .logo-mobile {
|
||||
width: 210px !important;
|
||||
}
|
||||
}
|
||||
|
||||
73
public/css/vendor/vendor-responsive-laptops.css
vendored
@@ -1,73 +0,0 @@
|
||||
/* ============================================================
|
||||
LAPTOP RESPONSIVENESS FIX (1025px – 1520px)
|
||||
Enforces desktop layouts strictly within laptop constraints.
|
||||
Leaves Mobile (<1024px) and Ultrawide (>1520px) UNTOUCHED.
|
||||
============================================================ */
|
||||
|
||||
@media (min-width: 1025px) and (max-width: 1520px) {
|
||||
|
||||
/* Hero Slider - Force Desktop Alignment */
|
||||
.logico-content-slider-widget .slide-content-inner {
|
||||
text-align: left !important;
|
||||
align-items: flex-start !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.logico-content-slider-widget .content-slider-item-heading,
|
||||
.logico-content-slider-widget .content-slider-item-text {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
/* Restore Flex & Grid container directions to desktop state */
|
||||
/* .e-con.e-parent,
|
||||
.e-con.e-child {
|
||||
--flex-direction: var(--flex-direction) !important;
|
||||
--container-widget-width: var(--container-widget-width) !important;
|
||||
--container-widget-height: var(--container-widget-height) !important;
|
||||
} */
|
||||
|
||||
/* Keep the .e-con-inner container widths at desktop-equivalent max widths */
|
||||
.e-con-boxed>.e-con-inner {
|
||||
max-width: min(var(--container-max-width, 1480px), 100%) !important;
|
||||
margin-left: auto !important;
|
||||
margin-right: auto !important;
|
||||
}
|
||||
|
||||
/* ─── Problem Section Specific Fixes ─── */
|
||||
.elementor-element.section-shrink-custom[data-id="30fd9d1"]>.e-con-inner {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
max-width: 1480px !important;
|
||||
width: 100% !important;
|
||||
gap: 40px !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-03db5d7 {
|
||||
flex: 1 1 0% !important;
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-e09c20e {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(4, 1fr) !important;
|
||||
gap: 24px !important;
|
||||
}
|
||||
|
||||
.elementor-element.elementor-element-5a5c397 {
|
||||
flex: 0 0 auto !important;
|
||||
width: auto !important;
|
||||
}
|
||||
|
||||
/* ─── Connected Logistics Section Fixes ─── */
|
||||
.elementor-element.elementor-element-9ffed33 {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
flex-wrap: nowrap !important;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
28884
public/css/vendor/vendor-theme-core.css
vendored
33
public/draco/draco_decoder.js
Normal file
BIN
public/draco/draco_decoder.wasm
Normal file
116
public/draco/draco_wasm_wrapper.js
Normal 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
|
After Width: | Height: | Size: 26 KiB |
BIN
public/images/Fazul.webp
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
public/images/Investor.webp
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
public/images/Parthi.webp
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
public/images/Suriya.webp
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
public/images/about-bg.webp
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
public/images/b5b560fe-aab0-4fe6-9f8c-4b187c2f0e99.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
public/images/b5b560fe-aab0-4fe6-9f8c-4b187c2f0e99.webp
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
public/images/bg-header-5.webp
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
public/images/bg-header-women.webp
Normal file
|
After Width: | Height: | Size: 163 KiB |
BIN
public/images/bg-map-women.webp
Normal file
|
After Width: | Height: | Size: 45 KiB |
BIN
public/images/bg-slide-sidebar.webp
Normal file
|
After Width: | Height: | Size: 5.3 KiB |
BIN
public/images/blog-post-pic-14.webp
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
public/images/blog-post-pic-15.webp
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
public/images/blog-post-pic-17.webp
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
public/images/blog-post-pic-3.webp
Normal file
|
After Width: | Height: | Size: 49 KiB |
BIN
public/images/blog-post-pic-31.webp
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
public/images/blog-post-pic-6.webp
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
public/images/blog-post-pic-8.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
public/images/ev-paradox.png
Normal file
|
After Width: | Height: | Size: 999 KiB |
BIN
public/images/ev-paradox.webp
Normal file
|
After Width: | Height: | Size: 176 KiB |
BIN
public/images/ev.webp
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
public/images/first-mile-approach.webp
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/images/home-bg-1.webp
Normal file
|
After Width: | Height: | Size: 236 KiB |
BIN
public/images/home1-slide-1.webp
Normal file
|
After Width: | Height: | Size: 115 KiB |
BIN
public/images/home1-slide-2.webp
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
public/images/home2-banner-1.webp
Normal file
|
After Width: | Height: | Size: 118 KiB |
BIN
public/images/home2-banner-3.webp
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
public/images/home2-pic-3.webp
Normal file
|
After Width: | Height: | Size: 141 KiB |
BIN
public/images/home2-slide-1.webp
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
public/images/home2-slide-2.webp
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
public/images/last-mile-approach.webp
Normal file
|
After Width: | Height: | Size: 121 KiB |
BIN
public/images/mid-mile-approach.webp
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
public/images/mile-1.png
Normal file
|
After Width: | Height: | Size: 8.0 MiB |
BIN
public/images/mile-1.webp
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
public/images/miletruth-bg.webp
Normal file
|
After Width: | Height: | Size: 187 KiB |
BIN
public/images/premium-ev-van.webp
Normal file
|
After Width: | Height: | Size: 94 KiB |
BIN
public/images/tab-pic-1-solution.webp
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
public/images/tab-pic-1.webp
Normal file
|
After Width: | Height: | Size: 106 KiB |
BIN
public/images/tab-pic-2-solution.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/images/tab-pic-2.webp
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
public/images/tab-pic-3-solution.webp
Normal file
|
After Width: | Height: | Size: 157 KiB |
BIN
public/images/tab-pic-3.webp
Normal file
|
After Width: | Height: | Size: 155 KiB |
BIN
public/models/3d_scene_final.glb
Normal file
BIN
public/videos/workflow-2-routing.mp4
Normal file
39
purgecss.config.cjs
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
* PurgeCSS config for consolidating the migrated WordPress/Elementor CSS.
|
||||
* Content = rendered static HTML (authoritative class list) + component source
|
||||
* (catches runtime-toggled classes that appear as string literals, e.g. "active",
|
||||
* "is-active", "animated", and the body-class strings in src/lib/bodyClasses.ts).
|
||||
*
|
||||
* Conservative by design: keyframes / @font-face / CSS variables are kept (PurgeCSS
|
||||
* defaults), and a safelist guards classes added by JS at runtime.
|
||||
*/
|
||||
module.exports = {
|
||||
css: ["public/css/site.css"],
|
||||
content: [
|
||||
"out/**/*.html",
|
||||
"src/**/*.{tsx,ts,jsx,js}",
|
||||
],
|
||||
// Keep classes toggled/added at runtime by GSAP/React state and header/menu logic.
|
||||
safelist: {
|
||||
standard: [
|
||||
"active",
|
||||
"is-active",
|
||||
"is-hiding",
|
||||
"animated",
|
||||
"header-visible-scrolled",
|
||||
"dm-header-scrolled",
|
||||
"header-visible",
|
||||
"current-menu-item",
|
||||
"current_page_item",
|
||||
"current-menu-ancestor",
|
||||
"menu-open",
|
||||
"loaded",
|
||||
],
|
||||
greedy: [
|
||||
/^elementor-.*-animation/, // animation utility classes applied on scroll
|
||||
/animated$/,
|
||||
/^swiper-/, // carousel runtime classes
|
||||
/^owl-/, // carousel runtime classes
|
||||
],
|
||||
},
|
||||
};
|
||||
48
scripts/build-css.sh
Executable file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Consolidate the formerly-WordPress/Elementor vendor CSS into one purged, renamed file.
|
||||
# - Concatenates the load-bearing CSS in the SAME order they were <link>-ed (cascade-preserving).
|
||||
# - Normalizes relative ../../fonts and ../../images url() refs to absolute (location-independent).
|
||||
# - PurgeCSS strips selectors not present in the rendered HTML or component source.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
OUT="public/css/site.css"
|
||||
PURGED_DIR="public/css/.purged"
|
||||
|
||||
# Exact <link> order from the old layout.tsx <head> (cascade matters).
|
||||
FILES=(
|
||||
"public/css/vendor/vendor-elementor-generated-globals.css"
|
||||
"public/css/vendor/vendor-elementor-base.css"
|
||||
"public/css/vendor/vendor-elementor-custom.min.css"
|
||||
"public/css/vendor/vendor-theme-core.css"
|
||||
"public/css/vendor/vendor-global-overrides.css"
|
||||
"public/css/vendor/vendor-layout-main.css"
|
||||
"public/css/vendor/vendor-responsive-laptops.css"
|
||||
"public/css/vendor/vendor-elementor-hfe.css"
|
||||
"public/css/vendor/vendor-icons-fontello-load.css"
|
||||
"public/css/vendor/vendor-icons-fontello.css"
|
||||
"public/css/custom-frontend.min.css"
|
||||
"public/css/all-inlined-head-styles.css"
|
||||
)
|
||||
|
||||
: > "$OUT"
|
||||
for f in "${FILES[@]}"; do
|
||||
printf '\n/* === %s === */\n' "$f" >> "$OUT"
|
||||
cat "$f" >> "$OUT"
|
||||
done
|
||||
|
||||
# Normalize relative asset paths so the combined file works from any location.
|
||||
sed -i '' -E 's#\.\./\.\./fonts/#/fonts/#g; s#\.\./\.\./images/#/images/#g' "$OUT"
|
||||
|
||||
RAW_BYTES=$(wc -c < "$OUT" | tr -d ' ')
|
||||
|
||||
# Purge (config points css: ['public/css/site.css']); output keeps the basename.
|
||||
rm -rf "$PURGED_DIR"
|
||||
npx purgecss --config purgecss.config.cjs --output "$PURGED_DIR"
|
||||
mv "$PURGED_DIR/site.css" "$OUT"
|
||||
rm -rf "$PURGED_DIR"
|
||||
|
||||
PURGED_BYTES=$(wc -c < "$OUT" | tr -d ' ')
|
||||
echo "---"
|
||||
echo "Raw combined: $RAW_BYTES bytes"
|
||||
echo "After purge: $PURGED_BYTES bytes -> $OUT"
|
||||
53
src/actions/sendEmail.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
"use server";
|
||||
|
||||
import nodemailer from "nodemailer";
|
||||
|
||||
export async function sendContactEmail(data: {
|
||||
fullName: string;
|
||||
email: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
}) {
|
||||
const { fullName, email, subject, message } = data;
|
||||
|
||||
if (!fullName || !email || !subject || !message) {
|
||||
return { success: false, error: "All fields are required." };
|
||||
}
|
||||
|
||||
const transporter = nodemailer.createTransport({
|
||||
host: process.env.SMTP_HOST,
|
||||
port: Number(process.env.SMTP_PORT) || 465,
|
||||
secure: true,
|
||||
auth: {
|
||||
user: process.env.SMTP_USER,
|
||||
pass: process.env.SMTP_PASSWORD,
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
await transporter.sendMail({
|
||||
from: `"${fullName}" <${process.env.SMTP_USER}>`,
|
||||
to: process.env.CONTACT_RECEIVER,
|
||||
replyTo: email,
|
||||
subject: `New Contact Form Submission: ${subject}`,
|
||||
html: `
|
||||
<div style="font-family: sans-serif; color: #333; max-width: 600px; margin: 0 auto; border: 1px solid #eee; padding: 20px; border-radius: 8px;">
|
||||
<h2 style="color: #c01227; border-bottom: 2px solid #c01227; padding-bottom: 10px; margin-top: 0;">New Contact Form Submission</h2>
|
||||
<p><strong>Name:</strong> ${fullName}</p>
|
||||
<p><strong>Email:</strong> ${email}</p>
|
||||
<p><strong>Subject:</strong> ${subject}</p>
|
||||
<p><strong>Message:</strong></p>
|
||||
<blockquote style="background: #f9f9f9; padding: 15px; border-left: 4px solid #c01227; margin: 0; font-style: italic;">
|
||||
${message.replace(/\n/g, "<br />")}
|
||||
</blockquote>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
return { success: true };
|
||||
} catch (error) {
|
||||
console.error("Email send error:", error);
|
||||
const errorMessage = error instanceof Error ? error.message : "Failed to send the email. Please try again later.";
|
||||
return { success: false, error: errorMessage };
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,10 @@ import Lenis from "lenis";
|
||||
* Re-evaluates on every route change: the effect cleanup destroys the previous
|
||||
* 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() {
|
||||
const pathname = usePathname();
|
||||
|
||||
@@ -15,7 +15,7 @@ export const metadata = {
|
||||
|
||||
export default function AboutUsPage() {
|
||||
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-inner">
|
||||
<div data-elementor-type="wp-page" data-elementor-id="86" className="elementor elementor-86 elementor-59">
|
||||
|
||||
116
src/app/blog/[slug]/page.tsx
Normal file
@@ -0,0 +1,116 @@
|
||||
import React from "react";
|
||||
import type { Metadata } from "next";
|
||||
import { notFound } from "next/navigation";
|
||||
import SingleBlog from "@/components/sections/SingleBlog";
|
||||
import BlogPostFooter from "@/components/sections/BlogPostFooter";
|
||||
import { getPostBySlug, getAllSlugs, SITE_URL } from "@/data/blog";
|
||||
|
||||
type Params = { slug: string };
|
||||
|
||||
// Required for `output: "export"`: prerender every post at build time.
|
||||
export function generateStaticParams(): Params[] {
|
||||
return getAllSlugs().map((slug) => ({ slug }));
|
||||
}
|
||||
|
||||
export async function generateMetadata({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<Params>;
|
||||
}): Promise<Metadata> {
|
||||
const { slug } = await params;
|
||||
const post = getPostBySlug(slug);
|
||||
|
||||
if (!post) {
|
||||
return { title: "Article Not Found – Doormile" };
|
||||
}
|
||||
|
||||
const url = `${SITE_URL}/blog/${post.slug}`;
|
||||
const image = `${SITE_URL}${post.image}`;
|
||||
|
||||
return {
|
||||
title: `${post.title} – Doormile`,
|
||||
description: post.excerpt,
|
||||
keywords: [post.category, "last-mile logistics", "EV fleet", "MileTruth", "route optimisation"],
|
||||
authors: [{ name: post.author }],
|
||||
alternates: { canonical: url },
|
||||
openGraph: {
|
||||
type: "article",
|
||||
title: post.title,
|
||||
description: post.excerpt,
|
||||
url,
|
||||
siteName: "Doormile",
|
||||
images: [{ url: image, alt: post.title }],
|
||||
publishedTime: new Date(`${post.date}T00:00:00Z`).toISOString(),
|
||||
authors: [post.author],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
title: post.title,
|
||||
description: post.excerpt,
|
||||
images: [image],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default async function BlogPostPage({
|
||||
params,
|
||||
}: {
|
||||
params: Promise<Params>;
|
||||
}) {
|
||||
const { slug } = await params;
|
||||
const post = getPostBySlug(slug);
|
||||
|
||||
if (!post) notFound();
|
||||
|
||||
const url = `${SITE_URL}/blog/${post.slug}`;
|
||||
|
||||
const articleSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Article",
|
||||
headline: post.title,
|
||||
description: post.excerpt,
|
||||
image: [`${SITE_URL}${post.image}`],
|
||||
datePublished: new Date(`${post.date}T00:00:00Z`).toISOString(),
|
||||
dateModified: new Date(`${post.date}T00:00:00Z`).toISOString(),
|
||||
author: { "@type": "Organization", name: post.author, url: SITE_URL },
|
||||
publisher: {
|
||||
"@type": "Organization",
|
||||
name: "Doormile",
|
||||
logo: {
|
||||
"@type": "ImageObject",
|
||||
url: `${SITE_URL}/images/cropped-image-2.png`,
|
||||
},
|
||||
},
|
||||
mainEntityOfPage: { "@type": "WebPage", "@id": url },
|
||||
articleSection: post.category,
|
||||
};
|
||||
|
||||
const breadcrumbSchema = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "BreadcrumbList",
|
||||
itemListElement: [
|
||||
{ "@type": "ListItem", position: 1, name: "Home", item: SITE_URL },
|
||||
{ "@type": "ListItem", position: 2, name: "Blog", item: `${SITE_URL}/blog` },
|
||||
{ "@type": "ListItem", position: 3, name: post.title, item: url },
|
||||
],
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(articleSchema) }}
|
||||
/>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
|
||||
/>
|
||||
<div className="content">
|
||||
<div className="content-inner">
|
||||
<SingleBlog post={post} />
|
||||
<BlogPostFooter slug={post.slug} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -4,12 +4,12 @@ import BlogGrid from "@/components/sections/BlogGrid";
|
||||
|
||||
export const metadata = {
|
||||
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() {
|
||||
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-inner">
|
||||
<div data-elementor-type="wp-page" data-elementor-id="104" className="elementor elementor-104">
|
||||
|
||||
121
src/app/cookie-policy/page.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import React from "react";
|
||||
import type { Metadata } from "next";
|
||||
import LegalDocument, { ContactLink, type LegalSection } from "@/components/sections/LegalDocument";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Cookie Policy – Doormile",
|
||||
description:
|
||||
"How Doormile uses cookies and similar technologies when you visit our website, and how you can manage them.",
|
||||
};
|
||||
|
||||
const sections: LegalSection[] = [
|
||||
{
|
||||
heading: "What Are Cookies?",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "Cookies are small text files stored on your device that help websites remember information about your visit and improve the browsing experience.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Why We Use Cookies",
|
||||
blocks: [
|
||||
{ type: "p", text: "We use cookies to:" },
|
||||
{
|
||||
type: "ul",
|
||||
items: [
|
||||
"Enable essential website functionality",
|
||||
"Improve website performance",
|
||||
"Analyze visitor behavior and traffic patterns",
|
||||
"Remember user preferences",
|
||||
"Enhance overall user experience",
|
||||
"Support website security",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Types of Cookies We Use",
|
||||
blocks: [
|
||||
{ type: "h3", text: "Essential Cookies" },
|
||||
{
|
||||
type: "p",
|
||||
text: "These cookies are required for core website functionality and security features. The website may not function correctly without them.",
|
||||
},
|
||||
{ type: "h3", text: "Performance Cookies" },
|
||||
{
|
||||
type: "p",
|
||||
text: "These cookies collect information about how visitors interact with the website and help us improve performance and usability.",
|
||||
},
|
||||
{ type: "h3", text: "Analytics Cookies" },
|
||||
{
|
||||
type: "p",
|
||||
text: "Analytics cookies help us understand website traffic, visitor engagement, popular content, and user journeys.",
|
||||
},
|
||||
{ type: "h3", text: "Functional Cookies" },
|
||||
{
|
||||
type: "p",
|
||||
text: "These cookies remember user preferences such as language, region, and other customization settings.",
|
||||
},
|
||||
{ type: "h3", text: "Third-Party Cookies" },
|
||||
{
|
||||
type: "p",
|
||||
text: "Some third-party services integrated into our website, including analytics and performance monitoring tools, may place cookies on your device.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Managing Cookies",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "Most web browsers allow users to control, block, or delete cookies through browser settings. Please note that disabling cookies may impact certain website features and functionality.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Cookie Consent",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "Where required by applicable law, visitors may be presented with cookie consent options when accessing the website.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Policy Updates",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "We may revise this Cookie Policy periodically to reflect changes in technology, regulations, or business practices. Updated versions will be published on this page.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Contact Us",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: <>If you have questions regarding this Cookie Policy or our use of cookies, please contact us through our <ContactLink />.</>,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function CookiePolicyPage() {
|
||||
return (
|
||||
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
|
||||
<div className="content">
|
||||
<div className="content-inner">
|
||||
<LegalDocument
|
||||
title="Cookie Policy"
|
||||
lastUpdated="June 2026"
|
||||
intro="This Cookie Policy explains how Doormile uses cookies and similar technologies when you visit our website."
|
||||
sections={sections}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -10,6 +10,77 @@ html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
GLOBAL SPACING SYSTEM
|
||||
A single shared vertical-rhythm scale so every section uses
|
||||
consistent top/bottom spacing instead of ad-hoc values.
|
||||
Fluid (clamp) so it scales down gracefully on small screens.
|
||||
--space-section → standard section (≈80px)
|
||||
--space-section-lg → large/feature section (≈100px)
|
||||
--space-hero-gap → gap from hero to the first section below
|
||||
Apply via .dm-section / .dm-section-lg, or reference the vars
|
||||
directly in component styles.
|
||||
============================================================ */
|
||||
:root {
|
||||
--space-section: clamp(40px, 5vw, 64px);
|
||||
--space-section-lg: clamp(52px, 6vw, 80px);
|
||||
--space-hero-gap: clamp(36px, 4.5vw, 64px);
|
||||
--dm-mobile-gutter: 10px;
|
||||
}
|
||||
|
||||
.dm-section {
|
||||
padding-top: var(--space-section) !important;
|
||||
padding-bottom: var(--space-section) !important;
|
||||
}
|
||||
|
||||
.dm-section-lg {
|
||||
padding-top: var(--space-section-lg) !important;
|
||||
padding-bottom: var(--space-section-lg) !important;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
Hero → first-section gap fixes
|
||||
The "Doormile Way" container (.elementor-element-88745f4) carries a
|
||||
150px top margin from the shared Elementor kit (intended as mid-page
|
||||
spacing on Home, where it sits deep in the stack). On the About page
|
||||
it is the FIRST section under the hero, so that 150px reads as an
|
||||
oversized empty gap. Scope the reduction to the About page only
|
||||
(.elementor-86) so Home's mid-page rhythm is untouched.
|
||||
------------------------------------------------------------ */
|
||||
.elementor-86 .elementor-element.elementor-element-88745f4 {
|
||||
margin-top: var(--space-hero-gap) !important;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------
|
||||
HOME PAGE — unified section rhythm
|
||||
The shared Elementor kit stamps several stacked sections with a
|
||||
150px top margin. On Home that makes "The Problem", the marquee,
|
||||
"Connected Logistics" and "The Doormile Way" float far below the
|
||||
section above them, while Stats and the EV card sit tight — an
|
||||
uneven, broken rhythm. Normalize EVERY post-hero section to one
|
||||
consistent gap so the page reads Hero ↓ Section ↓ Section evenly.
|
||||
|
||||
Scoped to `.elementor.elementor-61` — the Home page root uniquely
|
||||
carries BOTH classes, so this never leaks onto other pages that
|
||||
reuse these components (e.g. The Doormile Way on About-us, whose
|
||||
root is `.elementor.elementor-86`).
|
||||
|
||||
Hero (741f56c) and the Stats band (9b26234) directly beneath it are
|
||||
intentionally left tight and untouched.
|
||||
------------------------------------------------------------ */
|
||||
/* Sections, in render order: 30fd9d1 The Problem · b62c0b3 Marquee ·
|
||||
89a0ca1 Connected Logistics · 88745f4 The Doormile Way · bbc6760 EV ·
|
||||
3b4a7cc Industry Solutions. */
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-30fd9d1,
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-b62c0b3,
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-89a0ca1,
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-88745f4,
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-bbc6760,
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc {
|
||||
margin-top: var(--space-section) !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
/* Lenis global smooth scroll (src/animations/SmoothScroll.tsx). These classes are
|
||||
only present on routes/devices where Lenis is active; on touch devices and with
|
||||
prefers-reduced-motion Lenis is off and native scroll-behavior:smooth (above) applies. */
|
||||
@@ -87,12 +158,15 @@ body {
|
||||
#masthead .elementor-element.elementor-element-0b7bf6f .header-menu-container .main-menu > li > a {
|
||||
padding-top: 15px !important;
|
||||
padding-bottom: 16px !important;
|
||||
padding-left: 14px !important;
|
||||
padding-right: 14px !important;
|
||||
font-size: 15px !important;
|
||||
line-height: 1.2 !important;
|
||||
white-space: nowrap !important;
|
||||
}
|
||||
|
||||
#masthead .header-menu-container .main-menu {
|
||||
gap: 18px !important;
|
||||
gap: 8px !important;
|
||||
}
|
||||
|
||||
#masthead .elementor-element.elementor-element-cabdb09 a.header-button {
|
||||
@@ -387,7 +461,7 @@ body {
|
||||
@media (max-width: 720px) {
|
||||
.dm-contact-left {
|
||||
min-height: auto;
|
||||
padding: 42px 22px 58px;
|
||||
padding: 42px 20px 58px;
|
||||
}
|
||||
|
||||
.dm-contact-title {
|
||||
@@ -405,43 +479,43 @@ body {
|
||||
}
|
||||
|
||||
.dm-contact-card {
|
||||
margin: 0 16px 32px;
|
||||
padding: 44px 22px 34px;
|
||||
border-radius: 24px;
|
||||
margin: 0 20px 28px;
|
||||
padding: 28px 20px 24px;
|
||||
border-radius: 28px;
|
||||
}
|
||||
|
||||
.dm-contact-card h3 {
|
||||
margin-bottom: 34px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.dm-contact-form {
|
||||
gap: 20px;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.dm-contact-form input {
|
||||
height: 60px;
|
||||
padding: 0 20px;
|
||||
height: 56px;
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
.dm-contact-form textarea {
|
||||
min-height: 190px;
|
||||
padding: 20px;
|
||||
min-height: 170px;
|
||||
padding: 18px;
|
||||
}
|
||||
|
||||
.dm-contact-submit {
|
||||
width: 100%;
|
||||
height: 64px;
|
||||
height: 60px;
|
||||
}
|
||||
|
||||
.dm-contact-submit span:first-child {
|
||||
flex: 1 1 auto;
|
||||
min-width: 0;
|
||||
padding: 0 22px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
.dm-contact-submit-icon {
|
||||
width: 64px;
|
||||
min-width: 64px;
|
||||
width: 60px;
|
||||
min-width: 60px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,8 +524,8 @@ body {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 30px;
|
||||
width: 90%;
|
||||
margin: 30px auto;
|
||||
width: 100%;
|
||||
margin: 30px 0 0;
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
@@ -464,7 +538,139 @@ body {
|
||||
.industry-solutions-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}.industry-card-link {
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc {
|
||||
--padding-left: 0px !important;
|
||||
--padding-right: 0px !important;
|
||||
padding-left: 0px !important;
|
||||
padding-right: 0px !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 {
|
||||
--padding-left: 20px !important;
|
||||
--padding-right: 20px !important;
|
||||
padding-left: 20px !important;
|
||||
padding-right: 20px !important;
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.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-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-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-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;
|
||||
height: 600px;
|
||||
border-radius: 30px;
|
||||
@@ -579,12 +785,17 @@ body {
|
||||
transform: translateY(10px);
|
||||
}
|
||||
|
||||
/* Default card title — single typography token shared by every card (FMCG,
|
||||
Pharmaceutical, Enterprise & B2B). FMCG is the reference: small, clean,
|
||||
bottom-left, consistent weight. Kept compact so longer labels read at the
|
||||
same size/weight as FMCG instead of looking heavier. */
|
||||
.industry-card-default-title {
|
||||
color: #ffffff !important;
|
||||
font-size: 28px !important;
|
||||
font-size: 22px !important;
|
||||
font-weight: 700 !important;
|
||||
line-height: 1.2 !important;
|
||||
margin: 0 !important;
|
||||
letter-spacing: -0.5px !important;
|
||||
letter-spacing: -0.3px !important;
|
||||
text-transform: none !important;
|
||||
font-family: var(--font-manrope), 'Manrope', system-ui, -apple-system, sans-serif !important;
|
||||
}
|
||||
@@ -730,9 +941,27 @@ body {
|
||||
letter-spacing: -0.5px !important;
|
||||
}
|
||||
|
||||
.elementor-element-3b4a7cc > .e-con-inner {
|
||||
/* Industry Solutions: match the Meet Crew (OurTeam) container EXACTLY. The
|
||||
e-con-boxed inner otherwise spans edge-to-edge because the vendor
|
||||
"display: var(--display)" resolves to the invalid `inline` fallback, so its
|
||||
max-width is ignored. The previous rule here only set padding (never
|
||||
max-width / margin / display), which is why the divider line stretched almost
|
||||
full-viewport and the heading hugged the left edge. Force flex and pin the
|
||||
inner to the shared 1480px inset so the label divider, heading and card grid
|
||||
all share the same left/right edges as the rest of the page. */
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc {
|
||||
display: flex;
|
||||
}
|
||||
.elementor.elementor-61 .elementor-element.elementor-element-3b4a7cc > .e-con-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: 1480px;
|
||||
width: 100%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
padding-left: clamp(20px, 4vw, 50px) !important;
|
||||
padding-right: clamp(20px, 4vw, 50px) !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* Custom premium animations keyframes */
|
||||
@@ -803,10 +1032,14 @@ body {
|
||||
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
|
||||
width falls below 1536px). Match the home breakpoint instead. */
|
||||
@media (max-width: 840px) {
|
||||
@media (max-width: 1024px) {
|
||||
.custom-standard-hero-container {
|
||||
padding: 10px 10px 10px 10px !important;
|
||||
/* 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. */
|
||||
padding: 0 10px 10px 10px !important;
|
||||
}
|
||||
}
|
||||
@media (max-width: 840px) {
|
||||
.custom-standard-hero-card {
|
||||
height: 600px !important;
|
||||
min-height: 600px !important;
|
||||
@@ -814,3 +1047,122 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
/* Mobile hero/header normalization
|
||||
Home is the reference: at phone width the hero wrapper is full-width with
|
||||
10px side gutters, the visible hero card is 370px wide, starts 7px from the
|
||||
viewport top, and the first section begins immediately after the hero wrapper.
|
||||
Apply those same computed values to every hero template instead of carrying
|
||||
separate page-level mobile overrides. */
|
||||
@media (max-width: 840px) {
|
||||
body .elementor .elementor-element.elementor-element-741f56c,
|
||||
body .custom-standard-hero-container,
|
||||
body .miletruth-hero .elementor-element.elementor-element-86f3204 {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
padding: 0 var(--dm-mobile-gutter) var(--dm-mobile-gutter) var(--dm-mobile-gutter) !important;
|
||||
--padding-top: 0px !important;
|
||||
--padding-right: var(--dm-mobile-gutter) !important;
|
||||
--padding-bottom: var(--dm-mobile-gutter) !important;
|
||||
--padding-left: var(--dm-mobile-gutter) !important;
|
||||
--padding-block-start: 0px !important;
|
||||
box-sizing: border-box !important;
|
||||
border-radius: 0 !important;
|
||||
overflow: visible !important;
|
||||
}
|
||||
|
||||
body .elementor .elementor-element.elementor-element-6c7cbcb .owl-stage-outer,
|
||||
body .custom-standard-hero-card,
|
||||
body .miletruth-hero .miletruth-hero-container {
|
||||
width: 100% !important;
|
||||
height: 600px !important;
|
||||
min-height: 600px !important;
|
||||
margin-top: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
border-radius: 25px !important;
|
||||
overflow: hidden !important;
|
||||
box-sizing: border-box !important;
|
||||
}
|
||||
|
||||
body .elementor .elementor-element.elementor-element-6c7cbcb .owl-carousel.owl-theme .content-item,
|
||||
body .elementor .elementor-element.elementor-element-6c7cbcb .content-item,
|
||||
body .miletruth-hero .slide-content {
|
||||
height: 600px !important;
|
||||
min-height: 600px !important;
|
||||
}
|
||||
|
||||
body .elementor .elementor-element.elementor-element-741f56c + *,
|
||||
body .custom-standard-hero-container + *,
|
||||
body .elementor-63.miletruth-hero + * {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
body .elementor .elementor-element.elementor-element-6c7cbcb .owl-stage-outer,
|
||||
body .custom-standard-hero-card,
|
||||
body .miletruth-hero .miletruth-hero-container {
|
||||
height: 520px !important;
|
||||
min-height: 520px !important;
|
||||
margin-top: 7px !important;
|
||||
border-radius: 25px !important;
|
||||
}
|
||||
|
||||
body .elementor .elementor-element.elementor-element-6c7cbcb .owl-carousel.owl-theme .content-item,
|
||||
body .elementor .elementor-element.elementor-element-6c7cbcb .content-item,
|
||||
body .miletruth-hero .slide-content {
|
||||
height: 520px !important;
|
||||
min-height: 520px !important;
|
||||
}
|
||||
|
||||
body .miletruth-hero .miletruth-hero-container {
|
||||
padding-top: 60px !important;
|
||||
padding-bottom: 60px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* Consistent flush hero across every page on mobile/tablet — the floating navbar
|
||||
(position:absolute; top:5px) should rest directly on the hero with no band
|
||||
above it. Home / How It Works / Solutions share .elementor-element-741f56c;
|
||||
MileTruth uses .miletruth-hero-container. (About / Contact / Blog already use
|
||||
.custom-standard-hero-container, handled above.) */
|
||||
@media (max-width: 1024px) {
|
||||
/* The e-con applies top padding via the --padding-top / --padding-block-start
|
||||
vars (site.css sets them to 10px at a higher specificity), so override the
|
||||
vars — not just padding-top — to actually zero the gap. */
|
||||
.elementor-element.elementor-element-741f56c {
|
||||
--padding-top: 0px !important;
|
||||
--padding-block-start: 0px !important;
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
.miletruth-hero-container {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React from "react";
|
||||
import HowItWorksHero from "../../components/sections/HowItWorksHero";
|
||||
import Miles3 from "../../components/sections/Miles3";
|
||||
import WhyChooseDoormile from "../../components/sections/WhyChooseDoormile";
|
||||
import TheDoormileWay from "../../components/sections/TheDoormileWay";
|
||||
import Experience3DLoader from "@/modules/how-it-works-3d/Experience3DLoader";
|
||||
|
||||
export const metadata = {
|
||||
title: "How It Works – Doormile",
|
||||
@@ -11,14 +9,15 @@ export const metadata = {
|
||||
|
||||
export default function HowItWorksPage() {
|
||||
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-inner">
|
||||
<div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59">
|
||||
<HowItWorksHero />
|
||||
<Miles3 />
|
||||
<WhyChooseDoormile />
|
||||
<TheDoormileWay />
|
||||
{/* The first/mid/last-mile story is now told by the scroll-driven 3D
|
||||
experience, which replaces the former Miles3 / WhyChooseDoormile /
|
||||
TheDoormileWay content sections on this page. */}
|
||||
<Experience3DLoader />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -45,7 +45,7 @@ const inter = Inter({
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Doormile — Last-Mile Logistics Intelligence",
|
||||
title: "Doormile — Delivering Trust. Beyond Boundaries",
|
||||
description: "Doormile powers last-mile logistics with MileTruth™ AI, providing connected miles, SLA protection, and carrier management.",
|
||||
icons: {
|
||||
icon: "/images/cropped-image-2.png",
|
||||
@@ -71,19 +71,13 @@ export default function RootLayout({
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css"
|
||||
/>
|
||||
{/* Load WordPress & Elementor compiled styles from public folder */}
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-elementor-generated-globals.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-elementor-base.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-elementor-custom.min.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-theme-core.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-global-overrides.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-layout-main.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-responsive-laptops.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-elementor-hfe.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-icons-fontello-load.css" />
|
||||
<link rel="stylesheet" href="/css/vendor/vendor-icons-fontello.css" />
|
||||
<link rel="stylesheet" href="/css/custom-frontend.min.css" />
|
||||
<link rel="stylesheet" href="/css/all-inlined-head-styles.css" />
|
||||
{/*
|
||||
Consolidated site styles. Generated by `npm run build:css`
|
||||
(scripts/build-css.sh): the legacy WordPress/Elementor vendor CSS is
|
||||
concatenated in cascade order and purged of unused selectors via
|
||||
purgecss.config.cjs. ~2.86 MB of vendor CSS -> ~560 KB, one request.
|
||||
*/}
|
||||
<link rel="stylesheet" href="/css/site.css" />
|
||||
</head>
|
||||
{/*
|
||||
Production DOM (index.php + header.php):
|
||||
@@ -96,7 +90,7 @@ export default function RootLayout({
|
||||
│ └─ footer
|
||||
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 />
|
||||
<LoadingScreen />
|
||||
<AnimationProvider>
|
||||
|
||||
@@ -11,7 +11,7 @@ export const metadata = {
|
||||
|
||||
export default function MileTruthPage() {
|
||||
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-inner">
|
||||
<div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59">
|
||||
|
||||
@@ -10,14 +10,14 @@ import EVSection from "@/components/sections/EVSection";
|
||||
import IndustrySolutions from "@/components/sections/IndustrySolutions";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Doormile — Last-Mile Logistics Intelligence",
|
||||
title: "Doormile — Delivering Trust. Beyond Boundaries",
|
||||
description:
|
||||
"Doormile helps logistics companies track every mile with MileTruth™ AI. Real-time SLA protection and connected miles visibility.",
|
||||
};
|
||||
|
||||
export default function Home() {
|
||||
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-inner">
|
||||
<div data-elementor-type="wp-page" data-elementor-id="61" className="elementor elementor-61">
|
||||
|
||||
146
src/app/privacy-policy/page.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import React from "react";
|
||||
import type { Metadata } from "next";
|
||||
import LegalDocument, { ContactLink, type LegalSection } from "@/components/sections/LegalDocument";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Privacy Policy – Doormile",
|
||||
description:
|
||||
"How Doormile collects, uses, stores, and protects your information when you visit our website, interact with our services, or communicate with us.",
|
||||
};
|
||||
|
||||
const sections: LegalSection[] = [
|
||||
{
|
||||
heading: "Information We Collect",
|
||||
blocks: [
|
||||
{ type: "p", text: "We may collect the following information:" },
|
||||
{
|
||||
type: "ul",
|
||||
items: [
|
||||
"Full name",
|
||||
"Email address",
|
||||
"Phone number",
|
||||
"Company name",
|
||||
"Job title",
|
||||
"Information submitted through contact forms",
|
||||
"Service inquiry details",
|
||||
"Website usage data and analytics",
|
||||
"Browser, device, and IP information",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "How We Use Your Information",
|
||||
blocks: [
|
||||
{ type: "p", text: "We use your information to:" },
|
||||
{
|
||||
type: "ul",
|
||||
items: [
|
||||
"Respond to inquiries and support requests",
|
||||
"Provide information about our services",
|
||||
"Improve website performance and user experience",
|
||||
"Analyze usage trends and platform effectiveness",
|
||||
"Maintain security and prevent unauthorized access",
|
||||
"Communicate service updates and business information",
|
||||
"Comply with legal and regulatory requirements",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Information Sharing",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "Doormile does not sell, rent, or trade personal information. Information may be shared with trusted service providers that assist with website hosting, analytics, communications, and operational support, subject to appropriate confidentiality and security obligations.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Data Security",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "We implement industry-standard administrative, technical, and organizational safeguards designed to protect personal information from unauthorized access, disclosure, alteration, or destruction.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Data Retention",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "We retain information only for as long as necessary to fulfill the purposes described in this policy, comply with legal obligations, resolve disputes, and enforce agreements.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Cookies and Tracking Technologies",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "We use cookies and similar technologies to improve website functionality, measure performance, understand user behavior, and enhance user experience.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Your Rights",
|
||||
blocks: [
|
||||
{ type: "p", text: "Depending on applicable laws, you may have the right to:" },
|
||||
{
|
||||
type: "ul",
|
||||
items: [
|
||||
"Access your personal information",
|
||||
"Request correction of inaccurate data",
|
||||
"Request deletion of personal information",
|
||||
"Restrict or object to certain processing activities",
|
||||
"Withdraw consent where applicable",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Third-Party Links",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "Our website may contain links to third-party websites. We are not responsible for the privacy practices or content of external websites.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Policy Updates",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "We may update this Privacy Policy periodically. Any changes will be posted on this page with a revised effective date.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Contact Us",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: <>For privacy-related questions or requests, please contact us through our <ContactLink />.</>,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function PrivacyPolicyPage() {
|
||||
return (
|
||||
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
|
||||
<div className="content">
|
||||
<div className="content-inner">
|
||||
<LegalDocument
|
||||
title="Privacy Policy"
|
||||
lastUpdated="June 2026"
|
||||
intro="At Doormile, we are committed to protecting your privacy and maintaining the security of the information you share with us. This Privacy Policy outlines how we collect, use, store, and protect your information when you visit our website, interact with our services, or communicate with us."
|
||||
sections={sections}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -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-inner">
|
||||
<div data-elementor-type="wp-page" data-elementor-id="59" className="elementor elementor-59">
|
||||
|
||||
140
src/app/terms-of-service/page.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import React from "react";
|
||||
import type { Metadata } from "next";
|
||||
import LegalDocument, { ContactLink, type LegalSection } from "@/components/sections/LegalDocument";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Terms of Service – Doormile",
|
||||
description:
|
||||
"The Terms of Service governing your access to and use of the Doormile website and related services.",
|
||||
};
|
||||
|
||||
const sections: LegalSection[] = [
|
||||
{
|
||||
heading: "Acceptance of Terms",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "By accessing this website, you acknowledge that you have read, understood, and agreed to these Terms of Service and all applicable laws and regulations.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Permitted Use",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "You agree to use the website and services only for lawful purposes and in accordance with these terms.",
|
||||
},
|
||||
{ type: "p", text: "You agree not to:" },
|
||||
{
|
||||
type: "ul",
|
||||
items: [
|
||||
"Violate applicable laws or regulations",
|
||||
"Attempt unauthorized access to systems or networks",
|
||||
"Interfere with website functionality or security",
|
||||
"Distribute malicious software or harmful code",
|
||||
"Misrepresent your identity or organization",
|
||||
"Use website content without authorization",
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Intellectual Property Rights",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "All content, technology, software, graphics, trademarks, logos, text, designs, and other materials available on this website are the property of Doormile or its licensors and are protected by applicable intellectual property laws.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "User Submissions",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "Any information submitted through forms, inquiries, or communications must be accurate and lawful. Users are responsible for the content they submit.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Service Availability",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "While we strive to maintain uninterrupted access, we do not guarantee that the website or services will always be available, secure, or error-free.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Disclaimer of Warranties",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: 'The website and services are provided on an "as is" and "as available" basis without warranties of any kind, whether express or implied.',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Limitation of Liability",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "To the maximum extent permitted by law, Doormile shall not be liable for any indirect, incidental, special, consequential, or punitive damages arising from or related to the use of the website or services.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Indemnification",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "Users agree to indemnify and hold harmless Doormile, its employees, partners, and affiliates from claims arising from misuse of the website or violation of these terms.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Modifications",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "We reserve the right to update or modify these Terms of Service at any time. Continued use of the website following updates constitutes acceptance of the revised terms.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Governing Law",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: "These Terms shall be governed by and interpreted in accordance with applicable laws and regulations in the jurisdictions where Doormile conducts business.",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
heading: "Contact Us",
|
||||
blocks: [
|
||||
{
|
||||
type: "p",
|
||||
text: <>Questions regarding these Terms of Service may be submitted through our <ContactLink />.</>,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function TermsOfServicePage() {
|
||||
return (
|
||||
<div className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
|
||||
<div className="content">
|
||||
<div className="content-inner">
|
||||
<LegalDocument
|
||||
title="Terms of Service"
|
||||
lastUpdated="June 2026"
|
||||
intro="These Terms of Service govern your access to and use of the Doormile website and related services. By accessing or using our website, you agree to comply with these terms."
|
||||
sections={sections}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
122
src/components/blog/BlogSearch.tsx
Normal file
@@ -0,0 +1,122 @@
|
||||
"use client";
|
||||
|
||||
import React, { useMemo, useState, useRef, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import { blogPosts } from "@/data/blog";
|
||||
|
||||
/**
|
||||
* 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
|
||||
* matching /blog/[slug] routes.
|
||||
*/
|
||||
export default function BlogSearch() {
|
||||
const [query, setQuery] = useState("");
|
||||
const [open, setOpen] = useState(false);
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const results = useMemo(() => {
|
||||
const q = query.trim().toLowerCase();
|
||||
if (!q) return [];
|
||||
return blogPosts
|
||||
.filter(
|
||||
(p) =>
|
||||
p.title.toLowerCase().includes(q) ||
|
||||
p.category.toLowerCase().includes(q) ||
|
||||
p.excerpt.toLowerCase().includes(q)
|
||||
)
|
||||
.slice(0, 6);
|
||||
}, [query]);
|
||||
|
||||
// Close the results panel on outside click.
|
||||
useEffect(() => {
|
||||
function onDocClick(e: MouseEvent) {
|
||||
if (
|
||||
containerRef.current &&
|
||||
!containerRef.current.contains(e.target as Node)
|
||||
) {
|
||||
setOpen(false);
|
||||
}
|
||||
}
|
||||
document.addEventListener("mousedown", onDocClick);
|
||||
return () => document.removeEventListener("mousedown", onDocClick);
|
||||
}, []);
|
||||
|
||||
const showPanel = open && query.trim().length > 0;
|
||||
|
||||
return (
|
||||
<div className="dm-blog-search" ref={containerRef}>
|
||||
<form
|
||||
role="search"
|
||||
className="dm-blog-search-form"
|
||||
onSubmit={(e) => e.preventDefault()}
|
||||
>
|
||||
<label htmlFor="dm-blog-search-input" className="dm-sr-only">
|
||||
Search articles
|
||||
</label>
|
||||
<input
|
||||
id="dm-blog-search-input"
|
||||
type="search"
|
||||
className="dm-blog-search-input"
|
||||
placeholder="Search articles…"
|
||||
value={query}
|
||||
autoComplete="off"
|
||||
onChange={(e) => {
|
||||
setQuery(e.target.value);
|
||||
setOpen(true);
|
||||
}}
|
||||
onFocus={() => setOpen(true)}
|
||||
aria-expanded={showPanel}
|
||||
aria-controls="dm-blog-search-results"
|
||||
/>
|
||||
<span className="dm-blog-search-icon" aria-hidden="true">
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.2"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
>
|
||||
<circle cx="11" cy="11" r="7" />
|
||||
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
||||
</svg>
|
||||
</span>
|
||||
</form>
|
||||
|
||||
{showPanel && (
|
||||
<div
|
||||
id="dm-blog-search-results"
|
||||
className="dm-blog-search-results"
|
||||
role="listbox"
|
||||
>
|
||||
{results.length === 0 ? (
|
||||
<p className="dm-blog-search-empty">
|
||||
No articles match “{query.trim()}”.
|
||||
</p>
|
||||
) : (
|
||||
<ul>
|
||||
{results.map((p) => (
|
||||
<li key={p.slug} role="option" aria-selected="false">
|
||||
<Link
|
||||
href={`/blog/${p.slug}`}
|
||||
className="dm-blog-search-result"
|
||||
onClick={() => setOpen(false)}
|
||||
>
|
||||
<span className="dm-blog-search-result-cat">
|
||||
{p.category}
|
||||
</span>
|
||||
<span className="dm-blog-search-result-title">
|
||||
{p.title}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
100
src/components/blog/BlogSidebar.tsx
Normal file
@@ -0,0 +1,100 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import {
|
||||
getRecentPosts,
|
||||
getCategories,
|
||||
formatDate,
|
||||
type BlogPost,
|
||||
} from "@/data/blog";
|
||||
import BlogSearch from "./BlogSearch";
|
||||
|
||||
/**
|
||||
* Sticky single-post sidebar: Search, Recent Posts, Categories and a CTA card.
|
||||
* Styling lives in SingleBlog's scoped <style> block (dm-blog-* classes).
|
||||
*/
|
||||
export default function BlogSidebar({ current }: { current?: BlogPost }) {
|
||||
const recent = getRecentPosts(5)
|
||||
.filter((p) => p.slug !== current?.slug)
|
||||
.slice(0, 4);
|
||||
const categories = getCategories();
|
||||
|
||||
return (
|
||||
<aside className="dm-blog-sidebar" aria-label="Blog sidebar">
|
||||
{/* Search */}
|
||||
<section className="dm-blog-widget">
|
||||
<h2 className="dm-blog-widget-title">Search</h2>
|
||||
<BlogSearch />
|
||||
</section>
|
||||
|
||||
{/* Recent Posts */}
|
||||
<section className="dm-blog-widget">
|
||||
<h2 className="dm-blog-widget-title">Recent Posts</h2>
|
||||
<ul className="dm-blog-recent">
|
||||
{recent.map((p) => (
|
||||
<li key={p.slug}>
|
||||
<Link href={`/blog/${p.slug}`} className="dm-blog-recent-item">
|
||||
<span className="dm-blog-recent-thumb">
|
||||
<Image
|
||||
src={p.image}
|
||||
alt={p.title}
|
||||
fill
|
||||
sizes="62px"
|
||||
style={{ objectFit: "cover" }}
|
||||
/>
|
||||
</span>
|
||||
<span className="dm-blog-recent-meta">
|
||||
<span className="dm-blog-recent-title">{p.title}</span>
|
||||
<time dateTime={p.date} className="dm-blog-recent-date">
|
||||
{formatDate(p.date)}
|
||||
</time>
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* Categories */}
|
||||
<section className="dm-blog-widget">
|
||||
<h2 className="dm-blog-widget-title">Categories</h2>
|
||||
<ul className="dm-blog-categories">
|
||||
{categories.map((c) => (
|
||||
<li key={c.name}>
|
||||
<Link href="/blog" className="dm-blog-category-item">
|
||||
<span>{c.name}</span>
|
||||
<span className="dm-blog-category-count">{c.count}</span>
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
{/* CTA Card */}
|
||||
<section className="dm-blog-widget dm-blog-cta-card">
|
||||
<h2 className="dm-blog-cta-title">Planning delivery routes?</h2>
|
||||
<p className="dm-blog-cta-text">
|
||||
Talk to us about reducing wasted distance, missed windows, and avoidable
|
||||
vehicle time.
|
||||
</p>
|
||||
<Link href="/contact" className="dm-blog-cta-btn">
|
||||
Contact Us
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth="2.5"
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<line x1="5" y1="12" x2="19" y2="12" />
|
||||
<polyline points="12 5 19 12 12 19" />
|
||||
</svg>
|
||||
</Link>
|
||||
</section>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { useHeaderUI } from "./HeaderUIProvider";
|
||||
/**
|
||||
* Production: `<div class="body-overlay"></div>` is a direct child of body
|
||||
* (rendered by both index.php line 6 and header.php line 5 — production has two; we render one).
|
||||
* CSS (in vendor-theme-core.css and elementor-frontend-inline-css.css) styles it as fixed-position fullscreen overlay.
|
||||
* CSS (consolidated into /public/css/site.css) styles it as fixed-position fullscreen overlay.
|
||||
*/
|
||||
export default function BodyOverlay() {
|
||||
const { isMenuOpen, isSidebarOpen, closeAll } = useHeaderUI();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { submitContactForm } from "@/lib/contactForm";
|
||||
import { ScrollReveal } from "@/animations/Reveal";
|
||||
|
||||
export default function Footer() {
|
||||
@@ -45,25 +46,51 @@ export default function Footer() {
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData((prev) => ({ ...prev, [name]: value }));
|
||||
// Clear any stale success/error message once the user resumes editing.
|
||||
if (formStatus === "success" || formStatus === "error") setFormStatus("idle");
|
||||
};
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
setFormStatus("submitting");
|
||||
try {
|
||||
// Simulate API submission
|
||||
console.log("Footer contact form submitted:", formData);
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
const res = await submitContactForm(formData);
|
||||
|
||||
if (res.success) {
|
||||
setFormStatus("success");
|
||||
setFormData({ fullName: "", email: "", subject: "", message: "" });
|
||||
} else {
|
||||
console.error("Failed to submit contact form");
|
||||
setFormStatus("error");
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error("Error submitting contact form:", err);
|
||||
setFormStatus("error");
|
||||
}
|
||||
};
|
||||
|
||||
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
|
||||
data-rocket-location-hash="1eeca93394c4fc14089e9d12a2a92e22"
|
||||
itemScope
|
||||
@@ -107,7 +134,12 @@ export default function Footer() {
|
||||
<div className="elementor-element elementor-element-df89993 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="df89993" data-element_type="container" data-e-type="container">
|
||||
<div className="elementor-element elementor-element-69b6892 elementor-widget elementor-widget-logico_heading" data-id="69b6892" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
||||
<div className="elementor-widget-container">
|
||||
<div className="logico-title">Call Center</div>
|
||||
<div className="logico-title dm-foot-label">
|
||||
<svg className="dm-foot-ic" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
||||
<path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.13.96.36 1.9.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.91.34 1.85.57 2.81.7A2 2 0 0 1 22 16.92z" />
|
||||
</svg>
|
||||
<span>Contect</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="elementor-element elementor-element-87be926 elementor-widget elementor-widget-text-editor" data-id="87be926" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
|
||||
@@ -119,7 +151,13 @@ export default function Footer() {
|
||||
<div className="elementor-element elementor-element-f5d8e63 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="f5d8e63" data-element_type="container" data-e-type="container">
|
||||
<div className="elementor-element elementor-element-774e540 elementor-widget elementor-widget-logico_heading" data-id="774e540" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
||||
<div className="elementor-widget-container">
|
||||
<div className="logico-title">Our Location</div>
|
||||
<div className="logico-title dm-foot-label">
|
||||
<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="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
|
||||
<circle cx="12" cy="10" r="3" />
|
||||
</svg>
|
||||
<span>Address</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="elementor-element elementor-element-9c1cf03 elementor-widget elementor-widget-text-editor" data-id="9c1cf03" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
|
||||
@@ -135,7 +173,13 @@ export default function Footer() {
|
||||
<div className="elementor-element elementor-element-a96d151 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="a96d151" data-element_type="container" data-e-type="container">
|
||||
<div className="elementor-element elementor-element-37e647f elementor-widget elementor-widget-logico_heading" data-id="37e647f" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
||||
<div className="elementor-widget-container">
|
||||
<div className="logico-title">Email</div>
|
||||
<div className="logico-title dm-foot-label">
|
||||
<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">
|
||||
<rect x="2" y="4" width="20" height="16" rx="2" />
|
||||
<path d="m22 7-10 5L2 7" />
|
||||
</svg>
|
||||
<span>Email</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="elementor-element elementor-element-ba67644 elementor-widget elementor-widget-text-editor" data-id="ba67644" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
|
||||
@@ -149,13 +193,22 @@ export default function Footer() {
|
||||
<div className="elementor-element elementor-element-9ba4b82 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="9ba4b82" data-element_type="container" data-e-type="container">
|
||||
<div className="elementor-element elementor-element-e9a5d79 elementor-widget elementor-widget-logico_heading" data-id="e9a5d79" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
||||
<div className="elementor-widget-container">
|
||||
<div className="logico-title">Social network</div>
|
||||
<div className="logico-title dm-foot-label">
|
||||
<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">
|
||||
<circle cx="18" cy="5" r="3" />
|
||||
<circle cx="6" cy="12" r="3" />
|
||||
<circle cx="18" cy="19" r="3" />
|
||||
<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" />
|
||||
</svg>
|
||||
<span>Social</span>
|
||||
</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-widget-container">
|
||||
<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">
|
||||
<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">
|
||||
@@ -170,7 +223,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>
|
||||
</svg>
|
||||
</a>
|
||||
</span>
|
||||
</span> */}
|
||||
<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">
|
||||
<span className="elementor-screen-only">LinkedIn</span>
|
||||
@@ -206,7 +259,7 @@ export default function Footer() {
|
||||
<div className="wpforms-container wpforms-render-modern" id="wpforms-369">
|
||||
<form id="wpforms-form-369" className="wpforms-validate wpforms-form" onSubmit={handleSubmit}>
|
||||
<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-placeholder">
|
||||
<div className="logico-label-placeholder-text">Full name</div>
|
||||
@@ -223,7 +276,7 @@ export default function Footer() {
|
||||
required
|
||||
/>
|
||||
</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-placeholder">
|
||||
<div className="logico-label-placeholder-text">Email</div>
|
||||
@@ -240,7 +293,7 @@ export default function Footer() {
|
||||
required
|
||||
/>
|
||||
</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-placeholder">
|
||||
<div className="logico-label-placeholder-text">Subject</div>
|
||||
@@ -257,7 +310,7 @@ export default function Footer() {
|
||||
required
|
||||
/>
|
||||
</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-placeholder">
|
||||
<div className="logico-label-placeholder-text">Message</div>
|
||||
@@ -285,12 +338,12 @@ export default function Footer() {
|
||||
</button>
|
||||
{formStatus === "success" && (
|
||||
<div style={{ color: "#4caf50", marginTop: "10px", fontSize: "14px" }}>
|
||||
Message sent successfully!
|
||||
Message sent successfully.
|
||||
</div>
|
||||
)}
|
||||
{formStatus === "error" && (
|
||||
<div style={{ color: "#f44336", marginTop: "10px", fontSize: "14px" }}>
|
||||
Something went wrong. Please try again.
|
||||
Failed to send message. Please try again.
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -335,7 +388,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-widget-container">
|
||||
<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">
|
||||
<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">
|
||||
@@ -350,7 +403,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>
|
||||
</svg>
|
||||
</a>
|
||||
</span>
|
||||
</span> */}
|
||||
<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">
|
||||
<span className="elementor-screen-only">LinkedIn</span>
|
||||
@@ -498,6 +551,97 @@ export default function Footer() {
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
/* Compact footer link columns on phones (<=480px). The kit only sets
|
||||
column --width at min-width:481px, so below that all four blocks
|
||||
fall back to 100% and stack into one very tall column. Put the
|
||||
logo+social full-width on top, then lay the three link groups out
|
||||
in a 2-up grid (matching the tablet 47% intent) so the footer is
|
||||
roughly half as tall. */
|
||||
/* Inline icon beside each contact label (Call Center / Our Location /
|
||||
Email / Social network). */
|
||||
.elementor-6585 .dm-foot-label {
|
||||
display: inline-flex !important;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.elementor-6585 .dm-foot-label .dm-foot-ic {
|
||||
flex: 0 0 auto;
|
||||
color: #C01227;
|
||||
}
|
||||
|
||||
/* ── Compact mobile contact footer (<=600px) ──
|
||||
On phones the upper contact section stacked into one very tall
|
||||
column with 50px margins between every block. Tighten the heading,
|
||||
collapse those stack margins, and pair Email + Social side-by-side
|
||||
(both are short) so the footer is far shorter without losing the
|
||||
full-width address readability. */
|
||||
@media (max-width: 600px) {
|
||||
/* Trim the band's oversized top/bottom padding on phones. */
|
||||
.elementor-6585 .elementor-element.elementor-element-b29b8fc > .e-con-inner {
|
||||
padding-top: 40px !important;
|
||||
padding-bottom: 40px !important;
|
||||
}
|
||||
.elementor-6585 .elementor-element.elementor-element-687d944 .logico-title {
|
||||
font-size: clamp(24px, 6.6vw, 32px) !important;
|
||||
line-height: 1.2 !important;
|
||||
}
|
||||
.elementor-6585 .dm-foot-label .dm-foot-ic { width: 14px; height: 14px; }
|
||||
/* The Call Center / Our Location group carries a 10px side padding
|
||||
the Email / Social group doesn't, leaving its labels indented
|
||||
10px out of line with everything else — zero it so all four
|
||||
contact blocks share the same left edge as the heading. */
|
||||
.elementor-6585 .elementor-element.elementor-element-2631b42 {
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
/* All four contact blocks stack in one left-aligned column with a
|
||||
consistent gap (Social network sits below Email). */
|
||||
.elementor-6585 .elementor-element.elementor-element-2631b42,
|
||||
.elementor-6585 .elementor-element.elementor-element-f5d8e63,
|
||||
.elementor-6585 .elementor-element.elementor-element-645be8d,
|
||||
.elementor-6585 .elementor-element.elementor-element-9ba4b82 {
|
||||
margin-top: 22px !important;
|
||||
}
|
||||
.elementor-6585 .elementor-element.elementor-element-645be8d > .e-con-inner {
|
||||
flex-direction: column !important;
|
||||
gap: 0 !important;
|
||||
align-items: stretch !important;
|
||||
}
|
||||
.elementor-6585 .elementor-element.elementor-element-a96d151,
|
||||
.elementor-6585 .elementor-element.elementor-element-9ba4b82 {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.elementor-6585 .elementor-element.elementor-element-3f1ba7a {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
flex-wrap: wrap !important;
|
||||
gap: 36px 16px !important;
|
||||
}
|
||||
.elementor-6585 .elementor-element.elementor-element-64e2e81 {
|
||||
flex: 0 0 100% !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
.elementor-6585 .elementor-element.elementor-element-5b73dd3,
|
||||
.elementor-6585 .elementor-element.elementor-element-451f15c,
|
||||
.elementor-6585 .elementor-element.elementor-element-44a1f5d {
|
||||
flex: 1 1 calc(50% - 8px) !important;
|
||||
width: calc(50% - 8px) !important;
|
||||
min-width: 0 !important;
|
||||
}
|
||||
/* Allow long links (e.g. "Women entrepreneurship") to wrap inside
|
||||
the narrow columns instead of overflowing. */
|
||||
.elementor-6585 .logico-custom-menu-widget li a {
|
||||
white-space: normal !important;
|
||||
}
|
||||
/* Trim the large social-icons top gap so the brand block stays tight. */
|
||||
.elementor-6585 .elementor-element.elementor-element-e4e6486 > .elementor-widget-container {
|
||||
margin-top: 24px !important;
|
||||
}
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* 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 Image from "next/image";
|
||||
import { usePathname } from "next/navigation";
|
||||
@@ -44,6 +44,34 @@ export default function Header() {
|
||||
const dmHeaderActive = (key: string) =>
|
||||
(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):
|
||||
// - on doc.ready: $('.header-hide-until-scroll').addClass('header-visible-scrolled')
|
||||
// - on scroll: toggleClass('dm-header-scrolled', scrollTop > 50)
|
||||
@@ -142,10 +170,8 @@ export default function Header() {
|
||||
>
|
||||
<div className="slide-sidebar-close" onClick={closeAll}></div>
|
||||
<div className="slide-sidebar">
|
||||
<div className="slide-sidebar-content">
|
||||
<div id="block-37" className="widget widget_block">
|
||||
<div className="widget-wrapper">
|
||||
<div className="wp-block-group is-layout-constrained wp-block-group-is-layout-constrained">
|
||||
{/* Header — does not scroll. */}
|
||||
<div className="slide-sidebar-header">
|
||||
<figure className="wp-block-image size-full is-resized">
|
||||
<Image
|
||||
width={305}
|
||||
@@ -153,12 +179,17 @@ export default function Header() {
|
||||
src="/images/doormile-logo.png"
|
||||
alt="Doormile logo"
|
||||
className="wp-image-5851"
|
||||
style={{ width: "150px", height: "auto" }}
|
||||
style={{ width: "210px", height: "auto" }}
|
||||
sizes="(max-width: 305px) 100vw, 305px"
|
||||
/>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<div style={{ height: "46px" }} aria-hidden="true" className="wp-block-spacer"></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 className="widget-wrapper">
|
||||
<div className="dm-block-group is-layout-constrained dm-block-group-is-layout-constrained">
|
||||
|
||||
<div className="wp-block-title">
|
||||
<h6
|
||||
@@ -171,11 +202,87 @@ export default function Header() {
|
||||
textTransform: "none",
|
||||
}}
|
||||
>
|
||||
Our Location
|
||||
Address
|
||||
</h6>
|
||||
</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>
|
||||
|
||||
@@ -272,8 +379,13 @@ export default function Header() {
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div style={{ height: "137px" }} aria-hidden="true" className="wp-block-spacer"></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" }}>
|
||||
@@ -286,9 +398,6 @@ export default function Header() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className="elementor-element elementor-element-846e53d elementor-widget elementor-widget-site-logo"
|
||||
data-id="846e53d"
|
||||
@@ -325,22 +434,22 @@ export default function Header() {
|
||||
<nav>
|
||||
<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")}`}>
|
||||
<Link href="/">Home</Link>
|
||||
<Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home")}>Home</Link>
|
||||
</li>
|
||||
<li id="menu-item-10509" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("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 id="menu-item-10510" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
|
||||
<Link href="/miletruth">MileTruth™ AI</Link>
|
||||
<Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth")}>MileTruth™ AI</Link>
|
||||
</li>
|
||||
<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 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 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>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -362,14 +471,6 @@ export default function Header() {
|
||||
>
|
||||
<div className="mobile-header-row">
|
||||
<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">
|
||||
<a href="#" title="Login/Register" className="link-login"></a>
|
||||
</div>
|
||||
@@ -381,22 +482,22 @@ export default function Header() {
|
||||
<nav>
|
||||
<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")}`}>
|
||||
<Link href="/" onClick={closeAll}>Home</Link>
|
||||
<Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home", true)}>Home</Link>
|
||||
</li>
|
||||
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("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 className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
|
||||
<Link href="/miletruth" onClick={closeAll}>MileTruth™ AI</Link>
|
||||
<Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth", true)}>MileTruth™ AI</Link>
|
||||
</li>
|
||||
<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 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 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>
|
||||
</ul>
|
||||
</nav>
|
||||
@@ -449,7 +550,7 @@ export default function Header() {
|
||||
</ul>
|
||||
</div>
|
||||
<div className="header-mobile-button">
|
||||
<Link className="logico-alter-button" href="/contact" target="_blank">Get in Touch</Link>
|
||||
<Link className="logico-alter-button" href="/contact">Get in Touch</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -470,7 +571,7 @@ export default function Header() {
|
||||
>
|
||||
<div className="elementor-widget-container">
|
||||
<div className="header-button-container">
|
||||
<Link href="/contact" target="_blank" className="header-button header-button-animation-fade">
|
||||
<Link href="/contact" className="header-button header-button-animation-fade">
|
||||
Contact Us
|
||||
</Link>
|
||||
</div>
|
||||
@@ -480,10 +581,80 @@ export default function Header() {
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
{/* Inline <style> block — 1:1 translation of header.php lines 600-627 */}
|
||||
{/* Inline <style> block — 1:1 translation of header.php lines 600-627.
|
||||
suppressHydrationWarning: this is a static, deterministic CSS string,
|
||||
but as a client component its dangerouslySetInnerHTML is diffed during
|
||||
hydration. A stale prebuilt out/ or a CSS-injecting browser extension
|
||||
can make the server HTML differ from the client bundle, which React
|
||||
refuses to patch — suppressing avoids a false console error for a node
|
||||
whose content never depends on render-time state. */}
|
||||
<style
|
||||
suppressHydrationWarning
|
||||
dangerouslySetInnerHTML={{
|
||||
__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 {
|
||||
position: absolute !important;
|
||||
top: 5px !important;
|
||||
@@ -512,8 +683,10 @@ export default function Header() {
|
||||
Force position:fixed once scrolled past 50px so the header stays in viewport. */
|
||||
#masthead .elementor-element.elementor-element-466de1b.dm-header-scrolled {
|
||||
position: fixed !important;
|
||||
background: #4b4b4baa !important;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18) !important;
|
||||
background: rgba(26, 26, 26, 0.92) !important;
|
||||
-webkit-backdrop-filter: blur(14px) !important;
|
||||
backdrop-filter: blur(14px) !important;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.22) !important;
|
||||
top: 0 !important;
|
||||
}
|
||||
|
||||
@@ -522,11 +695,126 @@ export default function Header() {
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Desktop navbar layout (3-section balanced grid) ──
|
||||
The earlier Mac-M1 fix used justify-content:space-between, which
|
||||
on wide screens flung the logo and nav to opposite edges and
|
||||
left a large asymmetric gap between them. Replace it with a
|
||||
responsive three-column grid so the nav stays optically centred
|
||||
with equal space on both sides, the logo group sits left and the
|
||||
Contact button right — proportional at every desktop width.
|
||||
|
||||
The logo group (472172e) and nav (e44ee7e) are nested inside
|
||||
e052838 › d681ece, while the Contact button (f961133) is their
|
||||
uncle. Those two wrappers carry no visual styling on desktop
|
||||
(their background/padding live only in the <1024px mobile block),
|
||||
so display:contents flattens them and promotes all three logical
|
||||
sections to be direct items of one header grid. The grid only
|
||||
applies >=1025px, leaving the mobile pill layout untouched. */
|
||||
@media (min-width: 1025px) {
|
||||
#masthead .elementor-element.elementor-element-466de1b {
|
||||
display: grid !important;
|
||||
grid-template-columns: 1fr auto 1fr !important;
|
||||
align-items: center !important;
|
||||
column-gap: clamp(16px, 2vw, 32px) !important;
|
||||
/* Equal left/right safe areas pull the logo and Contact
|
||||
button inward off the viewport edges. Because the inset
|
||||
is symmetric, the 1fr side columns shrink equally and the
|
||||
centred nav does not shift. */
|
||||
padding-inline: clamp(24px, 3vw, 48px) !important;
|
||||
/* Consistent top/bottom breathing room inside the bar; with
|
||||
align-items:center every section stays vertically centred. */
|
||||
padding-block: clamp(8px, 1vw, 16px) !important;
|
||||
}
|
||||
|
||||
#masthead .elementor-element.elementor-element-e052838,
|
||||
#masthead .elementor-element.elementor-element-d681ece {
|
||||
display: contents !important;
|
||||
}
|
||||
|
||||
/* navbar-left: menu/grid icon + logo */
|
||||
#masthead .elementor-element.elementor-element-472172e {
|
||||
justify-self: start !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
gap: clamp(16px, 1.5vw, 28px) !important;
|
||||
flex: 0 0 auto !important;
|
||||
}
|
||||
|
||||
/* Logo never shrinks — keeps a clean edge against the nav. */
|
||||
#masthead .elementor-element.elementor-element-846e53d {
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
/* navbar-center: navigation menu, optically centred. */
|
||||
#masthead .elementor-element.elementor-element-e44ee7e {
|
||||
justify-self: center !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
/* navbar-right: Contact button, aligned to the right edge. */
|
||||
#masthead .elementor-element.elementor-element-f961133 {
|
||||
justify-self: end !important;
|
||||
}
|
||||
}
|
||||
|
||||
#masthead .header-menu-container .main-menu > li.active > a:before {
|
||||
background-color: #ffffff !important;
|
||||
opacity: 1 !important;
|
||||
}
|
||||
|
||||
#masthead .header-menu-container .main-menu > li.active > a {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
#masthead .header-menu-container .main-menu > li > a:focus,
|
||||
#masthead .header-menu-container .main-menu > li > a:focus-visible {
|
||||
outline: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
/* Responsive logo adjustment on mobile/tablet */
|
||||
@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 {
|
||||
width: 150px !important;
|
||||
height: auto !important;
|
||||
margin-left: 20px !important;
|
||||
}
|
||||
#masthead .header-menu-container {
|
||||
display: none !important;
|
||||
}
|
||||
#masthead .menu-trigger {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
/* Sticky header on scroll for mobile/tablet too — the desktop
|
||||
rule lived only in the min-width:1025px block, so on phones the
|
||||
absolutely-positioned navbar scrolled away with the page. Pin it
|
||||
to the top once scrolled past 50px, matching desktop behaviour. */
|
||||
#masthead .elementor-element.elementor-element-466de1b.dm-header-scrolled {
|
||||
position: fixed !important;
|
||||
top: 18px !important;
|
||||
left: 0 !important;
|
||||
right: 0 !important;
|
||||
}
|
||||
|
||||
#masthead .elementor-element.elementor-element-e052838 {
|
||||
margin-left: 0px !important;
|
||||
margin-right: 0px !important;
|
||||
border-radius: 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
#masthead .elementor-element.elementor-element-846e53d .hfe-site-logo .hfe-site-logo-container img {
|
||||
width: 150px !important;
|
||||
margin-left: 8px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* The theme reveals the mobile slide-in menu via Elementor's
|
||||
body[data-elementor-device-mode="mobile"] rules, which are set by
|
||||
Elementor's frontend JS — that JS isn't shipped in this Next port,
|
||||
@@ -539,6 +827,34 @@ export default function Header() {
|
||||
display: block !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* The bundled 'fontello' subset is missing the search (\\e85c) and
|
||||
close (\\e845) glyphs, so those two icons render as empty tofu
|
||||
boxes. Replace them with inline SVGs via CSS masks so they paint
|
||||
in the current text colour (matching the working cart/user icons)
|
||||
and respond to hover. */
|
||||
#masthead .header-icon.search-link .search-trigger-icon::before,
|
||||
#masthead .header-icon.menu-close .menu-close-icon::before {
|
||||
content: "" !important;
|
||||
display: block;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
background-color: currentColor;
|
||||
-webkit-mask-repeat: no-repeat;
|
||||
mask-repeat: no-repeat;
|
||||
-webkit-mask-position: center;
|
||||
mask-position: center;
|
||||
-webkit-mask-size: contain;
|
||||
mask-size: contain;
|
||||
}
|
||||
#masthead .header-icon.search-link .search-trigger-icon::before {
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='16.5' y1='16.5' x2='21' y2='21'/%3E%3C/svg%3E");
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Ccircle cx='11' cy='11' r='7'/%3E%3Cline x1='16.5' y1='16.5' x2='21' y2='21'/%3E%3C/svg%3E");
|
||||
}
|
||||
#masthead .header-icon.menu-close .menu-close-icon::before {
|
||||
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='5' y1='5' x2='19' y2='19'/%3E%3Cline x1='19' y1='5' x2='5' y2='19'/%3E%3C/svg%3E");
|
||||
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cline x1='5' y1='5' x2='19' y2='19'/%3E%3Cline x1='19' y1='5' x2='5' y2='19'/%3E%3C/svg%3E");
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -1,69 +1,170 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useEffect, useRef, useState, type MutableRefObject } from "react";
|
||||
import Image from "next/image";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
/**
|
||||
* LoadingScreen
|
||||
* ---------------------------------------------------------------------------
|
||||
* Native reimplementation of the legacy WordPress page-loader: a black
|
||||
* full-screen overlay with a centered, pulsing Doormile logo that fades out.
|
||||
* Route-transition loader only: a black full-screen overlay with a centered,
|
||||
* 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
|
||||
* a flash, capped at 2.5s so it never blocks) and again briefly on each route
|
||||
* navigation. CWV-safe: fixed/out-of-flow (no layout shift), logo is priority,
|
||||
* and it never delays hydration.
|
||||
* The App Router does not expose the old `next/router` routeChangeStart /
|
||||
* routeChangeComplete event API. This component provides the same behavior by
|
||||
* detecting internal route-link starts and completing when `usePathname()`
|
||||
* reports the committed route.
|
||||
*/
|
||||
type Phase = "visible" | "hiding" | "gone";
|
||||
type Phase = "hidden" | "visible" | "hiding";
|
||||
|
||||
const MIN_SHOW_MS = 450;
|
||||
const MAX_SHOW_MS = 2500;
|
||||
const NAV_SHOW_MS = 520;
|
||||
const MIN_VISIBLE_MS = 420;
|
||||
const MAX_VISIBLE_MS = 800;
|
||||
|
||||
function getRoutePath(url: URL) {
|
||||
return `${url.pathname}${url.search}`;
|
||||
}
|
||||
|
||||
export default function LoadingScreen() {
|
||||
const pathname = usePathname();
|
||||
const [phase, setPhase] = useState<Phase>("visible");
|
||||
const isFirstRender = useRef(true);
|
||||
const [phase, setPhase] = useState<Phase>("hidden");
|
||||
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);
|
||||
|
||||
// Initial load: hide once the page is ready.
|
||||
useEffect(() => {
|
||||
const start = performance.now();
|
||||
let began = false;
|
||||
let fadeTimer: ReturnType<typeof setTimeout>;
|
||||
|
||||
const begin = () => {
|
||||
if (began) return;
|
||||
began = true;
|
||||
const wait = Math.max(0, MIN_SHOW_MS - (performance.now() - start));
|
||||
fadeTimer = setTimeout(() => setPhase("hiding"), wait);
|
||||
const setLoaderPhase = (nextPhase: Phase) => {
|
||||
phaseRef.current = nextPhase;
|
||||
setPhase(nextPhase);
|
||||
};
|
||||
|
||||
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);
|
||||
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);
|
||||
};
|
||||
}, []);
|
||||
|
||||
// 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);
|
||||
currentRoutePath.current = `${pathname}${window.location.search}`;
|
||||
completeTransition();
|
||||
// `phase` intentionally stays out of this dependency list. The route commit
|
||||
// is the completion signal; phase changes should not repeatedly restart hide.
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [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 (
|
||||
<div
|
||||
@@ -72,18 +173,20 @@ export default function LoadingScreen() {
|
||||
aria-live="polite"
|
||||
aria-label="Loading"
|
||||
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">
|
||||
<Image
|
||||
src="/images/preloader.png"
|
||||
alt="Doormile"
|
||||
width={200}
|
||||
height={38}
|
||||
width={325}
|
||||
height={239}
|
||||
priority
|
||||
className="dm-loader__logo"
|
||||
style={{ width: "auto", height: "auto" }}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -100,8 +203,8 @@ export default function LoadingScreen() {
|
||||
will-change: opacity;
|
||||
}
|
||||
.dm-loader.is-hiding { opacity: 0; pointer-events: none; }
|
||||
.dm-loader__pulse { animation: dmLoaderPulse 1.5s linear infinite; }
|
||||
.dm-loader__logo { width: clamp(140px, 18vw, 200px); height: auto; }
|
||||
.dm-loader__pulse { animation: dmLoaderPulse 1.5s linear infinite; display: grid; place-items: center; }
|
||||
.dm-loader__logo { display: block; margin: 0 auto; width: clamp(120px, 32vw, 180px); height: auto; }
|
||||
@keyframes dmLoaderPulse {
|
||||
50% { transform: scale(0.85); }
|
||||
100% { transform: scale(1); }
|
||||
|
||||
@@ -5,7 +5,7 @@ import dynamic from "next/dynamic";
|
||||
import { motion, useMotionValue, useTransform, type MotionValue } from "framer-motion";
|
||||
import gsap from "gsap";
|
||||
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||
import { P, STRATEGIES, ENGINE_STEPS, CONSTRAINT_LIST, STRATEGY_SCORES } from "./theme";
|
||||
import { P, STRATEGIES, WINNER_INDEX, ENGINE_STEPS, CONSTRAINT_LIST, STRATEGY_SCORES } from "./theme";
|
||||
|
||||
const LogisticsBrainCanvas = dynamic(() => import("./LogisticsBrainCanvas"), { ssr: false });
|
||||
|
||||
@@ -24,23 +24,7 @@ function Counter({ mv }: { mv: MotionValue<number> }) {
|
||||
return <span ref={ref}>{Math.round(mv.get())}</span>;
|
||||
}
|
||||
|
||||
/** True only while a card's own opacity window is open (with a tiny buffer).
|
||||
* Lets us keep future/past story cards out of the DOM — and off the compositor
|
||||
* (each has `will-change`) — until their beat is actually on screen, so no
|
||||
* workflow state is rendered before activation. Visually identical, since a
|
||||
* card outside its window is opacity:0 anyway. */
|
||||
function useInWindow(mv: MotionValue<number>, threshold = 0.01): boolean {
|
||||
// `mv` is an external mutable store (a MotionValue). useTransform `.set()`s its
|
||||
// output synchronously while the PARENT renders, so a plain `.on("change") -> setState`
|
||||
// updates this component during the parent's render (React warns). useSyncExternalStore
|
||||
// is built for exactly this: it reads a snapshot and reconciles store-changes-during-
|
||||
// render safely. The snapshot is a primitive boolean, so it never re-renders needlessly.
|
||||
return useSyncExternalStore(
|
||||
(onStoreChange) => mv.on("change", onStoreChange),
|
||||
() => mv.get() > threshold,
|
||||
() => mv.get() > threshold,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/** Active step index from scroll progress (−1 before the engine starts). */
|
||||
function stepFromProgress(p: number): number {
|
||||
@@ -69,26 +53,43 @@ function StepRail({ active }: { active: number }) {
|
||||
);
|
||||
}
|
||||
|
||||
/** One cross-fading workflow card pinned to the lower-left. */
|
||||
/**
|
||||
* One workflow card that travels through the story. The outer anchor pins it to
|
||||
* its stage position (Left / Center / Right / Center-Hero); the inner motion card
|
||||
* slides + scales into that anchor in lockstep with scroll, so as the camera moves
|
||||
* through the stages the card visibly moves with the narrative instead of sitting
|
||||
* fixed in one corner.
|
||||
*/
|
||||
function StoryCard({
|
||||
step,
|
||||
index,
|
||||
pos,
|
||||
opacity,
|
||||
y,
|
||||
x,
|
||||
scale,
|
||||
num,
|
||||
kicker,
|
||||
title,
|
||||
children,
|
||||
}: {
|
||||
step: number;
|
||||
index: number;
|
||||
pos: "left" | "center" | "right" | "hero";
|
||||
opacity: MotionValue<number>;
|
||||
y: MotionValue<number>;
|
||||
x: MotionValue<number>;
|
||||
scale: MotionValue<number>;
|
||||
num: string;
|
||||
kicker: string;
|
||||
title: string;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
// Don't mount this beat's card until its cross-fade window opens.
|
||||
if (!useInWindow(opacity)) return null;
|
||||
// Don't mount this beat's card until its step is active.
|
||||
if (step !== index) return null;
|
||||
return (
|
||||
<motion.div className="dm-lb-card-story" style={{ opacity, y }}>
|
||||
<div className={`dm-lb-card-anchor is-${pos}`}>
|
||||
<motion.div className="dm-lb-card-story" style={{ opacity, y, x, scale }}>
|
||||
<div className="dm-lb-card-story__head">
|
||||
<span className="dm-lb-pillar__num">{num}</span>
|
||||
<span className="dm-lb-pillar__kicker">{kicker}</span>
|
||||
@@ -96,6 +97,7 @@ function StoryCard({
|
||||
<h3 className="dm-lb-pillar__title">{title}</h3>
|
||||
{children}
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -194,6 +196,22 @@ export default function LogisticsBrainSection({ connected = false }: { connected
|
||||
const p5o = useTransform(scroll, [0.75, 0.78, 0.855, 0.875], [0, 1, 1, 0]);
|
||||
const p5y = useTransform(scroll, [0.75, 0.79], [26, 0]);
|
||||
|
||||
// Horizontal slide + scale per beat — same windows as the opacity above, so the
|
||||
// card glides between its stage anchors (Left → Center → Right → Left → Center-Hero)
|
||||
// in lockstep with the camera. Each card enters from the direction of the previous
|
||||
// stage and drifts toward the next as it leaves, reading as one continuous travel.
|
||||
const p1x = useTransform(scroll, [0.135, 0.165, 0.255, 0.275], [-52, 0, 0, 52]);
|
||||
const p1s = useTransform(scroll, [0.135, 0.165, 0.255, 0.275], [0.965, 1, 1, 0.965]);
|
||||
const p2x = useTransform(scroll, [0.29, 0.32, 0.415, 0.435], [-52, 0, 0, 52]);
|
||||
const p2s = useTransform(scroll, [0.29, 0.32, 0.415, 0.435], [0.965, 1, 1, 0.965]);
|
||||
const p3x = useTransform(scroll, [0.45, 0.48, 0.575, 0.595], [-52, 0, 0, -52]);
|
||||
const p3s = useTransform(scroll, [0.45, 0.48, 0.575, 0.595], [0.965, 1, 1, 0.965]);
|
||||
const p4x = useTransform(scroll, [0.61, 0.64, 0.715, 0.735], [52, 0, 0, 52]);
|
||||
const p4s = useTransform(scroll, [0.61, 0.64, 0.715, 0.735], [0.965, 1, 1, 0.965]);
|
||||
// Hero (final selection): scales up a touch and holds center as it settles.
|
||||
const p5x = useTransform(scroll, [0.75, 0.78, 0.855, 0.875], [-52, 0, 0, 0]);
|
||||
const p5s = useTransform(scroll, [0.75, 0.78, 0.855, 0.875], [0.97, 1.05, 1.05, 1.0]);
|
||||
|
||||
const finaleOpacity = useTransform(scroll, [P.finale - 0.02, P.finale + 0.04], [0, 1]);
|
||||
const finaleY = useTransform(scroll, [P.finale - 0.02, P.finale + 0.06], [40, 0]);
|
||||
const taglineOpacity = useTransform(scroll, [P.finale + 0.04, P.finale + 0.1], [0, 1]);
|
||||
@@ -229,18 +247,18 @@ export default function LogisticsBrainSection({ connected = false }: { connected
|
||||
<span className="dm-lb-arrow">↓</span>
|
||||
</motion.div>
|
||||
|
||||
{/* STEP 01 — Generate Routes */}
|
||||
<StoryCard opacity={p1o} y={p1y} num="01" kicker="Generate Routes" title="We create many delivery plans at once">
|
||||
{/* STEP 01 — Generate Routes (card anchored LEFT) */}
|
||||
<StoryCard step={step} index={0} pos="left" opacity={p1o} y={p1y} x={p1x} scale={p1s} num="01" kicker="Generate Routes" title="We create many delivery plans at once">
|
||||
<div className="dm-lb-chips">
|
||||
{STRATEGIES.map((s) => (
|
||||
<span key={s} className="dm-lb-chip">{s}</span>
|
||||
{STRATEGIES.map((s, i) => (
|
||||
<span key={s} className={`dm-lb-chip${i === WINNER_INDEX ? " dm-lb-chip--active" : ""}`}>{s}</span>
|
||||
))}
|
||||
</div>
|
||||
<p className="dm-lb-pillar__foot">6 different ways to deliver all 59 orders — generated in milliseconds.</p>
|
||||
</StoryCard>
|
||||
|
||||
{/* STEP 02 — Check Constraints (the EV paradox) */}
|
||||
<StoryCard opacity={p2o} y={p2y} num="02" kicker="Check Constraints" title="Every plan must respect real-world limits">
|
||||
{/* STEP 02 — Check Constraints (card anchored CENTER) */}
|
||||
<StoryCard step={step} index={1} pos="center" opacity={p2o} y={p2y} x={p2x} scale={p2s} num="02" kicker="Check Constraints" title="Every plan must respect real-world limits">
|
||||
<ul className="dm-lb-constraints">
|
||||
{CONSTRAINT_LIST.map((c) => (
|
||||
<li key={c.label}>
|
||||
@@ -253,8 +271,8 @@ export default function LogisticsBrainSection({ connected = false }: { connected
|
||||
<p className="dm-lb-pillar__stat"><strong>59/59</strong> delivered <em>vs 34/59 when battery limits are ignored</em></p>
|
||||
</StoryCard>
|
||||
|
||||
{/* STEP 03 — Score & Compare (the leaderboard) */}
|
||||
<StoryCard opacity={p3o} y={p3y} num="03" kicker="Score & Compare" title="Each plan is scored by total delivery cost">
|
||||
{/* STEP 03 — Score & Compare (card anchored RIGHT) */}
|
||||
<StoryCard step={step} index={2} pos="right" opacity={p3o} y={p3y} x={p3x} scale={p3s} num="03" kicker="Score & Compare" title="Each plan is scored by total delivery cost">
|
||||
<ul className="dm-lb-board">
|
||||
{STRATEGY_SCORES.map((s) => (
|
||||
<li key={s.name} className={s.win ? "is-win" : ""}>
|
||||
@@ -266,8 +284,8 @@ export default function LogisticsBrainSection({ connected = false }: { connected
|
||||
</ul>
|
||||
</StoryCard>
|
||||
|
||||
{/* STEP 04 — Guarantee On-Time */}
|
||||
<StoryCard opacity={p4o} y={p4y} num="04" kicker="Guarantee On-Time" title="Any plan even 1 minute late is rejected">
|
||||
{/* STEP 04 — Guarantee On-Time (card anchored LEFT) */}
|
||||
<StoryCard step={step} index={3} pos="left" opacity={p4o} y={p4y} x={p4x} scale={p4s} num="04" kicker="Guarantee On-Time" title="Any plan even 1 minute late is rejected">
|
||||
<div className="dm-lb-sla">
|
||||
<span className="dm-lb-sla__badge">⏱️ On-time only</span>
|
||||
<span className="dm-lb-sla__x">✕ Late plan → dropped</span>
|
||||
@@ -275,8 +293,8 @@ export default function LogisticsBrainSection({ connected = false }: { connected
|
||||
<p className="dm-lb-pillar__foot">We only keep plans that hit every promised delivery window.</p>
|
||||
</StoryCard>
|
||||
|
||||
{/* STEP 05 — Pick & Dispatch */}
|
||||
<StoryCard opacity={p5o} y={p5y} num="05" kicker="Pick & Dispatch" title="The winning plan is sent to the fleet">
|
||||
{/* STEP 05 — Pick & Dispatch (card anchored CENTER, hero) */}
|
||||
<StoryCard step={step} index={4} pos="hero" opacity={p5o} y={p5y} x={p5x} scale={p5s} num="05" kicker="Pick & Dispatch" title="The winning plan is sent to the fleet">
|
||||
<div className="dm-lb-winner">✓ Multi-Trip selected — lowest cost, zero delays</div>
|
||||
<div className="dm-lb-chips">
|
||||
<span className="dm-lb-chip">EV Bikes</span>
|
||||
@@ -393,62 +411,76 @@ const styles = `
|
||||
.dm-lb-arrow { font-size: 18px; animation: dmLbBob 1.8s ease-in-out infinite; }
|
||||
@keyframes dmLbBob { 0%,100% { transform: translateY(0); opacity: 0.5; } 50% { transform: translateY(6px); opacity: 1; } }
|
||||
|
||||
/* ---- Lower-left workflow card (glass panel, cross-fades per step) ---- */
|
||||
.dm-lb-card-story { position: absolute; left: clamp(18px, 4vw, 56px); bottom: clamp(26px, 7vh, 64px);
|
||||
width: min(440px, 84vw); pointer-events: auto; will-change: opacity, transform;
|
||||
/* ---- Story card: a premium light-glass panel that TRAVELS between stage
|
||||
anchors. The anchor pins the stage position; the inner card slides/scales into
|
||||
it (Left → Center → Right → Left → Center-Hero) in lockstep with scroll. ---- */
|
||||
.dm-lb-card-anchor { position: absolute; bottom: clamp(26px, 7vh, 64px); z-index: 6; pointer-events: none; }
|
||||
.dm-lb-card-anchor.is-left { left: clamp(18px, 4vw, 56px); }
|
||||
.dm-lb-card-anchor.is-right { right: clamp(18px, 4vw, 56px); }
|
||||
.dm-lb-card-anchor.is-center,
|
||||
.dm-lb-card-anchor.is-hero { left: 50%; transform: translateX(-50%); }
|
||||
/* Hero (final selection) sits a little higher + centred so it reads as the payoff. */
|
||||
.dm-lb-card-anchor.is-hero { bottom: clamp(40px, 9vh, 92px); }
|
||||
|
||||
.dm-lb-card-story { position: relative; width: min(440px, 84vw); pointer-events: auto;
|
||||
will-change: opacity, transform; transform-origin: bottom center;
|
||||
padding: 18px 20px; border-radius: 18px;
|
||||
background: rgba(14,8,10,0.9); border: 1px solid rgba(226,53,66,0.22);
|
||||
/* backdrop blur removed — this card cross-fades/translates on scroll, so the blur
|
||||
was recomputed every frame; a near-opaque fill keeps the look at no per-frame cost. */
|
||||
box-shadow: 0 24px 64px -30px rgba(0,0,0,0.92); }
|
||||
/* Premium light glass — clean SaaS surface, brand red used only as a top accent. */
|
||||
background: rgba(255,255,255,0.94);
|
||||
border: 1px solid rgba(15,23,42,0.08); border-top: 3px solid #C01227;
|
||||
box-shadow: 0 28px 70px -34px rgba(15,23,42,0.45); }
|
||||
.dm-lb-card-anchor.is-hero .dm-lb-card-story { width: min(480px, 88vw);
|
||||
box-shadow: 0 38px 92px -34px rgba(192,18,39,0.4); }
|
||||
.dm-lb-card-story__head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
|
||||
.dm-lb-pillar__num { font-size: 12px; font-weight: 700; letter-spacing: 0.1em; color: #ffffff;
|
||||
background: linear-gradient(135deg, #E2354A, #C01227); border-radius: 7px; padding: 3px 8px; }
|
||||
.dm-lb-pillar__kicker { font-size: clamp(11px, 1.1vw, 13px); font-weight: 700; letter-spacing: 0.18em;
|
||||
text-transform: uppercase; color: #F2667A; }
|
||||
.dm-lb .dm-lb-pillar__title { margin: 0 0 12px !important; padding: 0 !important; color: #fbf5f6 !important;
|
||||
text-transform: uppercase; color: #C01227; }
|
||||
.dm-lb .dm-lb-pillar__title { margin: 0 0 12px !important; padding: 0 !important; color: #0f172a !important;
|
||||
font-weight: 700 !important; text-transform: none !important; letter-spacing: -0.015em !important;
|
||||
font-size: clamp(17px, 1.9vw, 24px) !important; line-height: 1.18 !important;
|
||||
text-shadow: 0 0 30px rgba(192,18,39,0.3) !important; }
|
||||
.dm-lb-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
|
||||
.dm-lb-chip { font-size: 11.5px; font-weight: 600; letter-spacing: 0.02em; color: #f1dadd;
|
||||
padding: 4px 11px; border-radius: 999px; background: rgba(192,18,39,0.12);
|
||||
border: 1px solid rgba(226,53,66,0.30); white-space: nowrap; }
|
||||
.dm-lb-pillar__foot { margin: 0; font-size: clamp(12px, 1.1vw, 13.5px); line-height: 1.45; color: rgba(236,224,226,0.72); }
|
||||
.dm-lb-pillar__stat { margin: 6px 0 0; font-size: clamp(12.5px, 1.2vw, 15px); color: rgba(236,224,226,0.78); }
|
||||
.dm-lb-pillar__stat strong { color: #4ade80; font-weight: 800; font-size: 1.25em; text-shadow: 0 0 20px rgba(34,197,94,0.5); }
|
||||
.dm-lb-pillar__stat em { font-style: normal; color: rgba(230,218,220,0.55); }
|
||||
font-size: clamp(17px, 1.9vw, 24px) !important; line-height: 1.18 !important; }
|
||||
.dm-lb-chips { display: flex; flex-wrap: wrap; gap: 7px; margin-bottom: 10px; }
|
||||
/* Strategy pills — white pills, soft border + light shadow, brand-red active state. */
|
||||
.dm-lb-chip { font-size: 11.5px; font-weight: 600; letter-spacing: 0.02em; color: #334155;
|
||||
padding: 5px 12px; border-radius: 999px; background: #ffffff;
|
||||
border: 1px solid rgba(15,23,42,0.1); box-shadow: 0 1px 2px rgba(15,23,42,0.06); white-space: nowrap; }
|
||||
.dm-lb-chip--active { color: #ffffff; background: linear-gradient(135deg, #E2354A, #C01227);
|
||||
border-color: transparent; box-shadow: 0 6px 16px -6px rgba(192,18,39,0.5); }
|
||||
.dm-lb-pillar__foot { margin: 0; font-size: clamp(12px, 1.1vw, 13.5px); line-height: 1.45; color: #475569; }
|
||||
.dm-lb-pillar__stat { margin: 6px 0 0; font-size: clamp(12.5px, 1.2vw, 15px); color: #475569; }
|
||||
.dm-lb-pillar__stat strong { color: #16a34a; font-weight: 800; font-size: 1.25em; }
|
||||
.dm-lb-pillar__stat em { font-style: normal; color: #94a3b8; }
|
||||
|
||||
/* Constraints checklist (step 02) */
|
||||
.dm-lb-constraints { list-style: none; margin: 0 0 10px; padding: 0; display: grid; gap: 7px; }
|
||||
.dm-lb-constraints li { display: flex; align-items: center; gap: 9px; }
|
||||
.dm-lb-constraints__icon { font-size: 14px; width: 20px; text-align: center; }
|
||||
.dm-lb-constraints__label { font-size: 13px; font-weight: 700; color: #fbeff0; min-width: 84px; }
|
||||
.dm-lb-constraints__note { font-size: 12px; color: rgba(232,222,224,0.6); }
|
||||
.dm-lb-constraints__label { font-size: 13px; font-weight: 700; color: #0f172a; min-width: 84px; }
|
||||
.dm-lb-constraints__note { font-size: 12px; color: #64748b; }
|
||||
|
||||
/* Scored leaderboard (step 03) */
|
||||
.dm-lb-board { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
|
||||
.dm-lb-board li { display: grid; grid-template-columns: 104px 1fr 26px; align-items: center; gap: 9px; }
|
||||
.dm-lb-board__name { font-size: 11.5px; font-weight: 600; color: rgba(234,226,228,0.68); display: flex; align-items: center; gap: 6px; white-space: nowrap; }
|
||||
.dm-lb-board li.is-win .dm-lb-board__name { color: #fff; font-weight: 800; }
|
||||
.dm-lb-board__name { font-size: 11.5px; font-weight: 600; color: #64748b; display: flex; align-items: center; gap: 6px; white-space: nowrap; }
|
||||
.dm-lb-board li.is-win .dm-lb-board__name { color: #0f172a; font-weight: 800; }
|
||||
.dm-lb-board__tag { font-size: 8px; font-weight: 800; letter-spacing: 0.08em; color: #fff;
|
||||
background: linear-gradient(135deg,#E2354A,#C01227); padding: 2px 5px; border-radius: 5px; }
|
||||
.dm-lb-board__track { height: 7px; border-radius: 999px; background: rgba(255,255,255,0.08); overflow: hidden; }
|
||||
.dm-lb-board__fill { display: block; height: 100%; border-radius: 999px; background: rgba(150,150,165,0.5); }
|
||||
.dm-lb-board li.is-win .dm-lb-board__fill { background: linear-gradient(90deg,#E2354A,#C01227); box-shadow: 0 0 12px rgba(226,53,66,0.6); }
|
||||
.dm-lb-board__score { font-size: 12px; font-weight: 700; color: rgba(234,226,228,0.68); text-align: right; }
|
||||
.dm-lb-board li.is-win .dm-lb-board__score { color: #fff; }
|
||||
.dm-lb-board__track { height: 7px; border-radius: 999px; background: rgba(15,23,42,0.08); overflow: hidden; }
|
||||
.dm-lb-board__fill { display: block; height: 100%; border-radius: 999px; background: rgba(100,116,139,0.45); }
|
||||
.dm-lb-board li.is-win .dm-lb-board__fill { background: linear-gradient(90deg,#E2354A,#C01227); box-shadow: 0 0 12px rgba(226,53,66,0.4); }
|
||||
.dm-lb-board__score { font-size: 12px; font-weight: 700; color: #64748b; text-align: right; }
|
||||
.dm-lb-board li.is-win .dm-lb-board__score { color: #0f172a; }
|
||||
|
||||
/* SLA badges (step 04) */
|
||||
.dm-lb-sla { display: flex; gap: 8px; margin-bottom: 10px; flex-wrap: wrap; }
|
||||
.dm-lb-sla__badge { font-size: 12px; font-weight: 700; color: #86efac; background: rgba(34,197,94,0.1);
|
||||
border: 1px solid rgba(34,197,94,0.32); padding: 6px 12px; border-radius: 999px; }
|
||||
.dm-lb-sla__x { font-size: 12px; font-weight: 700; color: #fca5a5; background: rgba(239,68,68,0.1);
|
||||
border: 1px solid rgba(239,68,68,0.32); padding: 6px 12px; border-radius: 999px; }
|
||||
.dm-lb-sla__badge { font-size: 12px; font-weight: 700; color: #15803d; background: rgba(34,197,94,0.1);
|
||||
border: 1px solid rgba(34,197,94,0.3); padding: 6px 12px; border-radius: 999px; }
|
||||
.dm-lb-sla__x { font-size: 12px; font-weight: 700; color: #b91c1c; background: rgba(239,68,68,0.08);
|
||||
border: 1px solid rgba(239,68,68,0.28); padding: 6px 12px; border-radius: 999px; }
|
||||
|
||||
/* Winner banner (step 05) */
|
||||
.dm-lb-winner { font-size: 13.5px; font-weight: 700; color: #fff; margin-bottom: 10px; padding: 9px 13px; border-radius: 12px;
|
||||
background: linear-gradient(135deg, rgba(192,18,39,0.24), rgba(34,197,94,0.16)); border: 1px solid rgba(226,53,66,0.4); }
|
||||
.dm-lb-winner { font-size: 13.5px; font-weight: 700; color: #0f172a; margin-bottom: 10px; padding: 9px 13px; border-radius: 12px;
|
||||
background: linear-gradient(135deg, rgba(192,18,39,0.08), rgba(34,197,94,0.08)); border: 1px solid rgba(226,53,66,0.32); }
|
||||
|
||||
/* ---- Finale: KPI cards ---- */
|
||||
.dm-lb-finale { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; padding: 0 20px; }
|
||||
@@ -478,7 +510,15 @@ const styles = `
|
||||
.dm-lb { height: 400vh; }
|
||||
.dm-lb-kpis { gap: 12px; }
|
||||
.dm-lb-kpi { min-width: 96px; padding: 14px 14px; }
|
||||
.dm-lb-card-story { left: 0; right: 0; margin: 0 auto; width: calc(100% - 28px); bottom: clamp(20px, 5vh, 44px); padding: 14px 16px; }
|
||||
/* On phones every stage collapses to one centred, full-width position — the
|
||||
horizontal travel only reads on wider screens. */
|
||||
.dm-lb-card-anchor,
|
||||
.dm-lb-card-anchor.is-left,
|
||||
.dm-lb-card-anchor.is-right,
|
||||
.dm-lb-card-anchor.is-center,
|
||||
.dm-lb-card-anchor.is-hero { left: 50%; right: auto; transform: translateX(-50%); bottom: clamp(20px, 5vh, 44px); }
|
||||
.dm-lb-card-story,
|
||||
.dm-lb-card-anchor.is-hero .dm-lb-card-story { width: calc(100vw - 28px); padding: 14px 16px; }
|
||||
.dm-lb-board li { grid-template-columns: 88px 1fr 24px; }
|
||||
.dm-lb-constraints__note { display: none; }
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ function makeRouteCurve(i: number): THREE.CatmullRomCurve3 {
|
||||
* route is invalid, the brain recalculates, a charging station rises and a new
|
||||
* green optimized route lights up.
|
||||
*/
|
||||
function Routes({ progress }: Props) {
|
||||
function Routes({ progress, isMobile = false }: Props) {
|
||||
const eased = useRef(0);
|
||||
|
||||
const tubeMats = useRef<(THREE.MeshBasicMaterial | null)[]>([]);
|
||||
@@ -295,6 +295,10 @@ function Routes({ progress }: Props) {
|
||||
{labelPos.map((pos, i) => {
|
||||
const isWinner = i === WINNER;
|
||||
const isReject = i === REJECT_INDEX;
|
||||
// Mobile clarity: hide ALL floating strategy labels (Multi-Trip, EV-Aware,
|
||||
// Balanced, Best, Time-Aware …) so the small canvas shows only the routing
|
||||
// nodes/network — these screen-space labels overlap badly at 320–390px.
|
||||
if (isMobile) return null;
|
||||
const dotColor = isWinner || isReject ? C.red : ROUTE_COLORS[i];
|
||||
return (
|
||||
<Html key={`lbl${i}`} position={[pos.x, pos.y, pos.z]} center zIndexRange={[30, 0]} style={{ pointerEvents: "none" }}>
|
||||
@@ -342,13 +346,16 @@ function Routes({ progress }: Props) {
|
||||
</mesh>
|
||||
</group>
|
||||
|
||||
{/* Recharge hub label (the "Kitchen / Recharge" in the EV paradox) */}
|
||||
{/* Recharge hub label (the "Kitchen / Recharge" in the EV paradox).
|
||||
Hidden on mobile to keep the small canvas free of overlapping overlays. */}
|
||||
{!isMobile && (
|
||||
<Html position={[stationPos.x, 1.7, stationPos.z]} center zIndexRange={[30, 0]} style={{ pointerEvents: "none" }}>
|
||||
<div ref={stationLabelRef} style={{ ...labelBase, border: "1px solid rgba(34,197,94,0.65)", boxShadow: "0 0 18px rgba(34,197,94,0.45)" }}>
|
||||
<span style={{ width: 7, height: 7, borderRadius: "50%", background: C.green, boxShadow: `0 0 8px ${C.green}` }} />
|
||||
Recharge Hub
|
||||
</div>
|
||||
</Html>
|
||||
)}
|
||||
|
||||
{/* EV scooter */}
|
||||
<group ref={scooter} visible={false}>
|
||||
|
||||
22
src/components/map/ContactMapEmbed.tsx
Normal file
@@ -0,0 +1,22 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* ContactMapEmbed
|
||||
* ---------------------------------------------------------------------------
|
||||
* Client boundary that lazy-loads the Leaflet map. `ssr: false` keeps Leaflet
|
||||
* out of the server bundle and off the critical render path. The host container
|
||||
* already owns the fixed height, so the loading state stays invisible before
|
||||
* the interactive map is ready.
|
||||
*/
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import styles from "./OfficeMap.module.css";
|
||||
|
||||
const OfficeMap = dynamic(() => import("./OfficeMap"), {
|
||||
ssr: false,
|
||||
loading: () => <div className={styles.mapMountReserve} role="presentation" aria-hidden="true" />,
|
||||
});
|
||||
|
||||
export default function ContactMapEmbed() {
|
||||
return <OfficeMap />;
|
||||
}
|
||||
401
src/components/map/OfficeMap.module.css
Normal file
@@ -0,0 +1,401 @@
|
||||
/* ===========================================================================
|
||||
Office satellite map — scoped styles.
|
||||
=========================================================================== */
|
||||
|
||||
.root {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
overflow: hidden;
|
||||
background: #0b0b0b;
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
.map {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #0b0b0b;
|
||||
font-family: var(--font-manrope), system-ui, -apple-system, sans-serif;
|
||||
}
|
||||
|
||||
/* Visible keyboard focus ring on the map viewport */
|
||||
.map:focus-visible {
|
||||
outline: 2px solid #c01227;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
|
||||
/* ---- Office navigation buttons (fly-to controls) ---- */
|
||||
.controls {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
left: 12px;
|
||||
right: 12px;
|
||||
z-index: 600; /* above tiles + markers; popups open lower so they never collide */
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
pointer-events: none; /* let the row be transparent to drags; buttons re-enable */
|
||||
}
|
||||
|
||||
.controlBtn {
|
||||
pointer-events: auto;
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
border: 1px solid rgba(255, 255, 255, 0.18);
|
||||
background: rgba(15, 15, 17, 0.82);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
backdrop-filter: blur(8px);
|
||||
color: #f5f5f5;
|
||||
font-family: var(--font-manrope), system-ui, -apple-system, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.01em;
|
||||
line-height: 1;
|
||||
padding: 9px 16px;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
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;
|
||||
}
|
||||
|
||||
.controlBtn:hover {
|
||||
background: rgba(192, 18, 39, 0.9);
|
||||
border-color: #c01227;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.controlBtn:focus-visible {
|
||||
outline: 2px solid #ffffff;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.controlBtnActive,
|
||||
.controlBtnActive:hover {
|
||||
background: #c01227;
|
||||
border-color: #c01227;
|
||||
color: #ffffff;
|
||||
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) {
|
||||
.controls {
|
||||
top: 12px;
|
||||
gap: 5px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
.controlBtn {
|
||||
font-size: 11px;
|
||||
padding: 7px 9px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- Branded marker pin ---- */
|
||||
.markerIcon {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
filter: drop-shadow(0 4px 6px rgba(0, 0, 0, 0.55));
|
||||
transition: transform 0.18s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.markerIcon:hover,
|
||||
.markerIcon:focus-visible {
|
||||
transform: translateY(-3px) scale(1.06);
|
||||
}
|
||||
|
||||
/* ---- 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) {
|
||||
background: #0b0b0b;
|
||||
}
|
||||
|
||||
.root :global(.leaflet-bar) {
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
box-shadow: 0 6px 18px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
.root :global(.leaflet-bar a) {
|
||||
background: rgba(15, 15, 17, 0.92);
|
||||
color: #f5f5f5;
|
||||
border-bottom-color: rgba(255, 255, 255, 0.12);
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
|
||||
.root :global(.leaflet-bar a:hover) {
|
||||
background: #c01227;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.root :global(.leaflet-bar a:focus-visible) {
|
||||
outline: 2px solid #c01227;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
.root :global(.leaflet-control-attribution) {
|
||||
background: rgba(10, 10, 10, 0.72);
|
||||
color: rgba(255, 255, 255, 0.65);
|
||||
backdrop-filter: blur(6px);
|
||||
-webkit-backdrop-filter: blur(6px);
|
||||
font-size: 11px;
|
||||
border-radius: 6px 0 0 0;
|
||||
padding: 2px 8px;
|
||||
}
|
||||
|
||||
.root :global(.leaflet-control-attribution a) {
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
/* ---- Floating Popup overlays ---- */
|
||||
.root :global(.leaflet-popup) {
|
||||
animation: popupFade 0.2s ease-out forwards;
|
||||
}
|
||||
|
||||
.root :global(.leaflet-popup-content-wrapper) {
|
||||
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%;
|
||||
height: 100%;
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.markerIcon,
|
||||
.markerIconHq { transition: none; }
|
||||
.pinPulse { animation: none; opacity: 0.45; }
|
||||
}
|
||||
|
||||
/* ---- Graceful error fallback ---- */
|
||||
.errorOverlay {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
z-index: 500;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 14px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
background: #101012;
|
||||
color: rgba(255, 255, 255, 0.82);
|
||||
font-family: var(--font-manrope), system-ui, sans-serif;
|
||||
}
|
||||
|
||||
.errorTitle {
|
||||
font-size: clamp(15px, 2.4vw, 18px);
|
||||
font-weight: 800;
|
||||
color: #ffffff;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.errorText {
|
||||
font-size: 13px;
|
||||
max-width: 38ch;
|
||||
margin: 0;
|
||||
line-height: 1.55;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
.errorList {
|
||||
list-style: none;
|
||||
margin: 4px 0 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px 12px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.errorList li {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 7px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #f1f1f1;
|
||||
}
|
||||
|
||||
.errorList li::before {
|
||||
content: "";
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: #c01227;
|
||||
}
|
||||
|
||||
/* ---- Screen-reader-only office list ---- */
|
||||
.srOnly {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
padding: 0;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
clip: rect(0, 0, 0, 0);
|
||||
white-space: nowrap;
|
||||
border: 0;
|
||||
}
|
||||
303
src/components/map/OfficeMap.tsx
Normal file
@@ -0,0 +1,303 @@
|
||||
"use client";
|
||||
|
||||
/**
|
||||
* OfficeMap
|
||||
* ---------------------------------------------------------------------------
|
||||
* Client-only Leaflet satellite map (Esri World Imagery) rendering the three
|
||||
* 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.
|
||||
*
|
||||
* The map centers with a North latitude offset to keep markers lower in the viewport,
|
||||
* avoiding collisions with the navigation tabs. Popups are compact and styled like
|
||||
* clean business cards.
|
||||
*/
|
||||
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import styles from "./OfficeMap.module.css";
|
||||
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import L from "leaflet";
|
||||
import { MapContainer, Marker, Popup, TileLayer, ZoomControl, useMap } from "react-leaflet";
|
||||
|
||||
import {
|
||||
LatLng,
|
||||
ESRI_WORLD_IMAGERY,
|
||||
HQ_OFFICE,
|
||||
MAP_FOCUS_ZOOM,
|
||||
MAP_INITIAL_CENTER,
|
||||
MAP_INITIAL_ZOOM,
|
||||
OFFICE_LOCATIONS,
|
||||
} from "./offices";
|
||||
|
||||
type MapStatus = "loading" | "ready" | "error";
|
||||
|
||||
/** A request to focus a specific office. `nonce` lets the same office be re-selected. */
|
||||
type FocusTarget = { id: string; nonce: number };
|
||||
|
||||
/**
|
||||
* 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({
|
||||
className: styles.markerIcon,
|
||||
html: `
|
||||
<svg width="30" height="40" 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.85"/>
|
||||
<circle cx="15" cy="15" r="5.4" fill="#ffffff"/>
|
||||
</svg>
|
||||
`,
|
||||
iconSize: [30, 40],
|
||||
iconAnchor: [15, 40],
|
||||
popupAnchor: [0, -40], // Anchored to top tip of pin
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Imperative map effects that need the Leaflet instance:
|
||||
* - keep the viewport sized correctly across resizes / lazy reveals
|
||||
* - snap to the offset HQ on first paint, fly thereafter and open popups.
|
||||
*/
|
||||
function MapController({
|
||||
focus,
|
||||
markerRefs,
|
||||
}: {
|
||||
focus: FocusTarget | null;
|
||||
markerRefs: React.RefObject<Record<string, L.Marker>>;
|
||||
}) {
|
||||
const map = useMap();
|
||||
const didInit = useRef(false);
|
||||
|
||||
// Keep the map correctly sized on container resize / lazy reveal.
|
||||
useEffect(() => {
|
||||
const container = map.getContainer();
|
||||
let raf = 0;
|
||||
const observer = new ResizeObserver(() => {
|
||||
cancelAnimationFrame(raf);
|
||||
raf = requestAnimationFrame(() => map.invalidateSize());
|
||||
});
|
||||
observer.observe(container);
|
||||
return () => {
|
||||
cancelAnimationFrame(raf);
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [map]);
|
||||
|
||||
// React to the focused office: snap on first paint, fly thereafter.
|
||||
useEffect(() => {
|
||||
if (!focus) return;
|
||||
const office = OFFICE_LOCATIONS.find((item) => item.id === focus.id);
|
||||
if (!office) return;
|
||||
|
||||
const openPopup = () => markerRefs.current[office.id]?.openPopup();
|
||||
|
||||
// Offset the center slightly North so the marker sits lower in the viewport,
|
||||
// preventing the popup card from colliding with the top controls.
|
||||
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);
|
||||
return () => {
|
||||
map.off("moveend", openPopup);
|
||||
};
|
||||
}, [map, focus, markerRefs]);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
export default function OfficeMap() {
|
||||
const icon = useMemo(() => createMarkerIcon(false), []);
|
||||
const hqIcon = useMemo(() => createMarkerIcon(true), []);
|
||||
const markerRefs = useRef<Record<string, L.Marker>>({});
|
||||
|
||||
// Default to the headquarters.
|
||||
const [focus, setFocus] = useState<FocusTarget | null>({ id: HQ_OFFICE.id, nonce: 0 });
|
||||
const focusOffice = useCallback((id: string) => {
|
||||
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 loadedRef = useRef(false);
|
||||
const errorCountRef = useRef(0);
|
||||
|
||||
const handleTileLoad = useCallback(() => {
|
||||
loadedRef.current = true;
|
||||
setStatus("ready");
|
||||
}, []);
|
||||
|
||||
const handleTileError = useCallback(() => {
|
||||
errorCountRef.current += 1;
|
||||
if (!loadedRef.current && errorCountRef.current >= 6) setStatus("error");
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const timeout = window.setTimeout(() => {
|
||||
if (!loadedRef.current) setStatus("error");
|
||||
}, 12_000);
|
||||
return () => window.clearTimeout(timeout);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={styles.root} role="region" aria-label="Map of Doormile office locations">
|
||||
{/* Semantic, always-available fallback for assistive tech + no-JS/SEO. */}
|
||||
<ul className={styles.srOnly}>
|
||||
{OFFICE_LOCATIONS.map((office) => (
|
||||
<li key={office.id}>
|
||||
{office.name} — latitude {office.position[0]}, longitude {office.position[1]}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
{/* Jump-to-office navigation buttons. */}
|
||||
<div className={styles.controls} role="group" aria-label="Jump to an office location">
|
||||
{OFFICE_LOCATIONS.map((office) => {
|
||||
const isActive = focus?.id === office.id;
|
||||
return (
|
||||
<button
|
||||
key={office.id}
|
||||
type="button"
|
||||
className={`${styles.controlBtn} ${isActive ? styles.controlBtnActive : ""} ${
|
||||
office.isHeadquarters ? styles.controlBtnHq : ""
|
||||
}`}
|
||||
aria-pressed={isActive}
|
||||
aria-label={`Show ${office.name} on the map`}
|
||||
onClick={() => focusOffice(office.id)}
|
||||
>
|
||||
{office.city}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<MapContainer
|
||||
className={styles.map}
|
||||
center={MAP_INITIAL_CENTER}
|
||||
zoom={MAP_INITIAL_ZOOM}
|
||||
scrollWheelZoom={false}
|
||||
zoomControl={false}
|
||||
attributionControl={false}
|
||||
worldCopyJump
|
||||
>
|
||||
{/* Keep zoom controls clear of the top-row buttons. */}
|
||||
<ZoomControl position="bottomleft" />
|
||||
|
||||
<TileLayer
|
||||
url={ESRI_WORLD_IMAGERY.url}
|
||||
attribution={ESRI_WORLD_IMAGERY.attribution}
|
||||
maxZoom={ESRI_WORLD_IMAGERY.maxZoom}
|
||||
updateWhenIdle
|
||||
keepBuffer={2}
|
||||
eventHandlers={{ load: handleTileLoad, tileerror: handleTileError }}
|
||||
/>
|
||||
|
||||
{OFFICE_LOCATIONS.map((office) => {
|
||||
const isFocused = focus?.id === office.id;
|
||||
const isHovered = hoveredOfficeId === office.id;
|
||||
|
||||
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>
|
||||
|
||||
{status === "error" && (
|
||||
<div className={styles.errorOverlay} role="alert">
|
||||
<p className={styles.errorTitle}>Map could not be loaded</p>
|
||||
<p className={styles.errorText}>
|
||||
Please check your connection. Our offices are located in:
|
||||
</p>
|
||||
<ul className={styles.errorList}>
|
||||
{OFFICE_LOCATIONS.map((office) => (
|
||||
<li key={office.id}>{office.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||