diff --git a/probe.mjs b/probe.mjs new file mode 100644 index 0000000..125807f --- /dev/null +++ b/probe.mjs @@ -0,0 +1,24 @@ +import { chromium } from 'playwright'; + +const browser = await chromium.launch(); +for (const w of [1600, 1440, 1366, 1280, 1180, 1024, 992, 900, 768, 640, 480, 390]) { + const page = await browser.newPage({ viewport: { width: w, height: 900 }, deviceScaleFactor: 1 }); + await page.goto('http://localhost:3000/miletruth', { waitUntil: 'load', timeout: 90000 }); + await page.waitForTimeout(1200); + const r = await page.evaluate(() => { + const copy = document.querySelector('#solution-miletruth-ai .sw-mt-copy'); + const flow = document.querySelector('#solution-miletruth-ai .sw-mt-flow'); + const labels = [...flow.querySelectorAll('.sw-mt-step-label')]; + return { + copyW: Math.round(copy.getBoundingClientRect().width), + // widest single unbreakable label vs the track it was given + worst: labels.map(e => { + const track = e.parentElement.getBoundingClientRect().width; + return { txt: e.textContent, need: Math.round(e.scrollWidth), track: Math.round(track) }; + }).filter(x => x.need > x.track + 1), + }; + }); + console.log(`${String(w).padStart(4)} copy=${String(r.copyW).padStart(3)} overflow=${r.worst.length ? JSON.stringify(r.worst) : 'none'}`); + await page.close(); +} +await browser.close(); diff --git a/public/images/miletruth-ai-core.png b/public/images/miletruth-ai-core.png new file mode 100644 index 0000000..a3c2bca Binary files /dev/null and b/public/images/miletruth-ai-core.png differ diff --git a/shot-miletruth.mjs b/shot-miletruth.mjs new file mode 100644 index 0000000..c90da2b --- /dev/null +++ b/shot-miletruth.mjs @@ -0,0 +1,49 @@ +import { chromium } from 'playwright'; + +const shots = [ + { name: 'desktop-1440', w: 1440, h: 1000 }, + { name: 'laptop-1280', w: 1280, h: 900 }, + { name: 'tablet-1024', w: 1024, h: 950 }, + { name: 'mobile-390', w: 390, h: 844 }, +]; + +for (const s of shots) { + // fresh browser each time: reusing one exhausts software WebGL contexts + const browser = await chromium.launch({ + args: ['--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'], + }); + const page = await browser.newPage({ viewport: { width: s.w, height: s.h }, deviceScaleFactor: 1 }); + const errs = []; + page.on('pageerror', e => errs.push('pageerror: ' + e.message)); + + await page.goto('http://localhost:3000/miletruth', { waitUntil: 'load', timeout: 90000 }); + const toSec = () => page.evaluate(() => { + const el = document.querySelector('#solution-miletruth-ai'); + if (el) window.scrollTo(0, window.scrollY + el.getBoundingClientRect().top - 8); + }); + await toSec(); + await page.waitForTimeout(18000); + await toSec(); + await page.waitForTimeout(2500); + + const info = await page.evaluate(() => { + const sec = document.querySelector('#solution-miletruth-ai'); + const scene = sec.querySelector('.mtn-scene'); + const sb = scene.getBoundingClientRect(); + return { + canvas: sec.querySelectorAll('canvas').length, + labels: [...scene.querySelectorAll('.mtn-lbl')].map(e => { + const r = e.getBoundingClientRect(); + const clip = r.left < sb.left || r.right > sb.right || r.top < sb.top || r.bottom > sb.bottom; + return e.querySelector('.mtn-lbl-n').textContent + (clip ? ' CLIPPED' : ''); + }), + stepRows: new Set([...sec.querySelectorAll('.sw-mt-step')].map(e => Math.round(e.getBoundingClientRect().top))).size, + overflowX: document.documentElement.scrollWidth > document.documentElement.clientWidth, + }; + }); + console.log(`${s.name}: ` + JSON.stringify(info)); + if (errs.length) console.log(' ERRORS:', errs.slice(0, 3)); + + await page.screenshot({ path: `/tmp/mt-${s.name}.png`, timeout: 90000 }); + await browser.close(); +} diff --git a/src/app/industries/page.tsx b/src/app/industries/page.tsx new file mode 100644 index 0000000..2c13f2f --- /dev/null +++ b/src/app/industries/page.tsx @@ -0,0 +1,35 @@ +import React from "react"; +import SolutionsHero from "@/components/sections/SolutionsHero"; +import IndustryStack from "@/components/sections/IndustryStack"; + +/** + * Industries — "Who Doormile serves". + * + * This is the original industry-focused content that used to live at /solutions + * (see commit 205924e): the industry hero slider plus the IndustryStack cards + * ported from doormile-site/includes/sections/solution/card-1.php. The FMCG, + * Pharma and Enterprise & B2B copy is unchanged from that original; Retail and + * Quick Commerce were added later in the same layout. + * + * /solutions now answers "what Doormile offers" instead. + */ +export const metadata = { + title: "Industries – Doormile", + description: + "Discover how Doormile's connected logistics platform serves diverse industries — FMCG, Pharma, Enterprise & B2B, Retail and Quick Commerce — with tailored solutions.", +}; + +export default function IndustriesPage() { + return ( +
+
+
+
+ + +
+
+
+
+ ); +} diff --git a/src/app/miletruth/page.tsx b/src/app/miletruth/page.tsx index 7cbb781..db69804 100644 --- a/src/app/miletruth/page.tsx +++ b/src/app/miletruth/page.tsx @@ -5,6 +5,14 @@ import Workflow1 from "../../components/sections/Workflow1"; import Workflow2 from "../../components/sections/Workflow2"; import Workflow4 from "../../components/sections/Workflow4"; import Workflow3Lazy from "../../components/sections/Workflow3Lazy"; +import { + SolutionAIVehicleSelection, + SolutionSmartRouteIntelligence, + SolutionQuantumDecisionEngine, + SolutionLiveLogisticsIntelligence, + SolutionMileTruthAI, + SolutionOperationsDashboard, +} from "../../components/sections/SolutionsWorkflow"; export const metadata = { title: "MileTruth – Doormile", @@ -19,9 +27,15 @@ export default function MileTruthPage() {
+ + + + + +
diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index 219c488..8c6c1e6 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -10,11 +10,8 @@ export default function sitemap(): MetadataRoute.Sitemap { "/contact", "/blog", "/air-freight", - "/first-mile-delivery", - "/mid-mile-delivery", - "/last-mile-delivery", - "/ev-logistics", - "/strategy-optimization", + "/solutions", + "/industries", "/how-it-works", ].map((route) => ({ url: `${SITE_URL}${route}`, diff --git a/src/app/solutions/page.tsx b/src/app/solutions/page.tsx index 7443256..39c8b3b 100644 --- a/src/app/solutions/page.tsx +++ b/src/app/solutions/page.tsx @@ -1,65 +1,119 @@ import React from "react"; -import SolutionsHero from "@/components/sections/SolutionsHero"; -import SolutionsWorkflow from "@/components/sections/SolutionsWorkflow"; +import SolutionsHero, { type HeroSlide } from "@/components/sections/SolutionsHero"; +import Miles3 from "@/components/sections/Miles3"; +import EVSection, { type EVFeature } from "@/components/sections/EVSection"; +import { + SolutionsSectionsShell, + SolOfferings, + SolWingsSpotlight, + SolMileTruthBand, + SolutionsWhyDoormile, + SolutionsResults, +} from "@/components/sections/SolutionsSections"; +/** + * Solutions — "What Doormile offers". + * + * Services only: first mile, mid mile, last mile, Doormile Wings, EV fleet, and + * the fact that all of it is powered by MileTruth™. The technology story (AI + * architecture, routing, the decision engine) belongs to /miletruth and is + * deliberately not repeated here; industry detail belongs to /industries. + */ export const metadata = { title: "Solutions – Doormile", - description: "See how Doormile's AI-powered Logistics Intelligence Platform works end-to-end — from pickup and AI vehicle selection to Quantum routing, live shipment tracking, door-to-door movement, MileTruth AI and real-time operations monitoring.", + description: + "What Doormile offers: first mile pickup, mid mile line haul, last mile delivery, same-day air with Doormile Wings, and an electric fleet — every mile powered by MileTruth™.", }; +const SOLUTIONS_SLIDES: HeroSlide[] = [ + { + heading: "One Partner for Every Mile", + text: "First mile, mid mile and last mile — on road and in the air, with an electric fleet and one tracking link end to end.", + }, + { + heading: "Everything Doormile Delivers", + text: "Take one service or the whole chain. Either way it is one contract, one team, and one place to see where your shipment is.", + }, +]; + +/* Customer-facing framing of the EV fleet — the homepage section's own copy is + written around the EV-native platform design, which belongs on MileTruth. */ +const EV_FLEET_FEATURES: EVFeature[] = [ + { + icon: ( + + + + ), + title: "Lower Cost Per Kilometre", + desc: "Electric running costs are a fraction of diesel, and that saving shows up on the miles you move with us.", + }, + { + icon: ( + + + + + ), + title: "No Range Surprises", + desc: "Charging is planned into the day before the vehicle leaves the depot, so an EV run arrives when a diesel run would.", + }, + { + icon: ( + + + + + ), + title: "Zero Tailpipe Emissions", + desc: "Clean deliveries into cities with low-emission zones, and a number you can put in your own sustainability reporting.", + }, + { + icon: ( + + + + + + + ), + title: "Built For Your Routes", + desc: "Urban and regional runs served by a fleet sized to the job, from small city vehicles to larger regional loads.", + }, +]; + export default function SolutionsPage() { return ( - <> -