fix the mobile responsive

This commit is contained in:
2026-06-04 20:06:39 +05:30
parent d5987b5dd1
commit 3a16bf9267
6 changed files with 192 additions and 52 deletions

View File

@@ -527,6 +527,28 @@ export default function Header() {
opacity: 1 !important; opacity: 1 !important;
} }
/* Responsive logo adjustment on mobile/tablet */
@media (max-width: 1024px) {
#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;
}
}
@media (max-width: 480px) {
#masthead .elementor-element.elementor-element-846e53d .hfe-site-logo .hfe-site-logo-container img {
width: 130px !important;
margin-left: 10px !important;
}
}
/* The theme reveals the mobile slide-in menu via Elementor's /* The theme reveals the mobile slide-in menu via Elementor's
body[data-elementor-device-mode="mobile"] rules, which are set by body[data-elementor-device-mode="mobile"] rules, which are set by
Elementor's frontend JS — that JS isn't shipped in this Next port, Elementor's frontend JS — that JS isn't shipped in this Next port,

View File

@@ -24,23 +24,7 @@ function Counter({ mv }: { mv: MotionValue<number> }) {
return <span ref={ref}>{Math.round(mv.get())}</span>; 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). */ /** Active step index from scroll progress (1 before the engine starts). */
function stepFromProgress(p: number): number { function stepFromProgress(p: number): number {
@@ -71,6 +55,8 @@ function StepRail({ active }: { active: number }) {
/** One cross-fading workflow card pinned to the lower-left. */ /** One cross-fading workflow card pinned to the lower-left. */
function StoryCard({ function StoryCard({
step,
index,
opacity, opacity,
y, y,
num, num,
@@ -78,6 +64,8 @@ function StoryCard({
title, title,
children, children,
}: { }: {
step: number;
index: number;
opacity: MotionValue<number>; opacity: MotionValue<number>;
y: MotionValue<number>; y: MotionValue<number>;
num: string; num: string;
@@ -85,8 +73,8 @@ function StoryCard({
title: string; title: string;
children?: React.ReactNode; children?: React.ReactNode;
}) { }) {
// Don't mount this beat's card until its cross-fade window opens. // Don't mount this beat's card until its step is active.
if (!useInWindow(opacity)) return null; if (step !== index) return null;
return ( return (
<motion.div className="dm-lb-card-story" style={{ opacity, y }}> <motion.div className="dm-lb-card-story" style={{ opacity, y }}>
<div className="dm-lb-card-story__head"> <div className="dm-lb-card-story__head">
@@ -230,7 +218,7 @@ export default function LogisticsBrainSection({ connected = false }: { connected
</motion.div> </motion.div>
{/* STEP 01 — Generate Routes */} {/* STEP 01 — Generate Routes */}
<StoryCard opacity={p1o} y={p1y} num="01" kicker="Generate Routes" title="We create many delivery plans at once"> <StoryCard step={step} index={0} opacity={p1o} y={p1y} num="01" kicker="Generate Routes" title="We create many delivery plans at once">
<div className="dm-lb-chips"> <div className="dm-lb-chips">
{STRATEGIES.map((s) => ( {STRATEGIES.map((s) => (
<span key={s} className="dm-lb-chip">{s}</span> <span key={s} className="dm-lb-chip">{s}</span>
@@ -240,7 +228,7 @@ export default function LogisticsBrainSection({ connected = false }: { connected
</StoryCard> </StoryCard>
{/* STEP 02 — Check Constraints (the EV paradox) */} {/* 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"> <StoryCard step={step} index={1} opacity={p2o} y={p2y} num="02" kicker="Check Constraints" title="Every plan must respect real-world limits">
<ul className="dm-lb-constraints"> <ul className="dm-lb-constraints">
{CONSTRAINT_LIST.map((c) => ( {CONSTRAINT_LIST.map((c) => (
<li key={c.label}> <li key={c.label}>
@@ -254,7 +242,7 @@ export default function LogisticsBrainSection({ connected = false }: { connected
</StoryCard> </StoryCard>
{/* STEP 03 — Score & Compare (the leaderboard) */} {/* STEP 03 — Score & Compare (the leaderboard) */}
<StoryCard opacity={p3o} y={p3y} num="03" kicker="Score &amp; Compare" title="Each plan is scored by total delivery cost"> <StoryCard step={step} index={2} opacity={p3o} y={p3y} num="03" kicker="Score &amp; Compare" title="Each plan is scored by total delivery cost">
<ul className="dm-lb-board"> <ul className="dm-lb-board">
{STRATEGY_SCORES.map((s) => ( {STRATEGY_SCORES.map((s) => (
<li key={s.name} className={s.win ? "is-win" : ""}> <li key={s.name} className={s.win ? "is-win" : ""}>
@@ -267,7 +255,7 @@ export default function LogisticsBrainSection({ connected = false }: { connected
</StoryCard> </StoryCard>
{/* STEP 04 — Guarantee On-Time */} {/* 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"> <StoryCard step={step} index={3} opacity={p4o} y={p4y} num="04" kicker="Guarantee On-Time" title="Any plan even 1 minute late is rejected">
<div className="dm-lb-sla"> <div className="dm-lb-sla">
<span className="dm-lb-sla__badge"> On-time only</span> <span className="dm-lb-sla__badge"> On-time only</span>
<span className="dm-lb-sla__x"> Late plan dropped</span> <span className="dm-lb-sla__x"> Late plan dropped</span>
@@ -276,7 +264,7 @@ export default function LogisticsBrainSection({ connected = false }: { connected
</StoryCard> </StoryCard>
{/* STEP 05 — Pick & Dispatch */} {/* STEP 05 — Pick & Dispatch */}
<StoryCard opacity={p5o} y={p5y} num="05" kicker="Pick &amp; Dispatch" title="The winning plan is sent to the fleet"> <StoryCard step={step} index={4} opacity={p5o} y={p5y} num="05" kicker="Pick &amp; 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-winner"> Multi-Trip selected lowest cost, zero delays</div>
<div className="dm-lb-chips"> <div className="dm-lb-chips">
<span className="dm-lb-chip">EV Bikes</span> <span className="dm-lb-chip">EV Bikes</span>

View File

@@ -8,6 +8,78 @@ import { ScrollReveal, Magnetic } from "@/animations/Reveal";
export default function ConnectedLogistics() { export default function ConnectedLogistics() {
return ( return (
<div className="elementor-element elementor-element-89a0ca1 e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent" data-id="89a0ca1" data-element_type="container" data-e-type="container"> <div className="elementor-element elementor-element-89a0ca1 e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent" data-id="89a0ca1" data-element_type="container" data-e-type="container">
<style dangerouslySetInnerHTML={{ __html: `
/* Ensure the inner content wrapper is responsive and never overflows */
.elementor-element-fdb2e58 {
width: 100% !important;
max-width: 650px !important;
}
/* Make heading font size fluid and responsive */
.elementor-element-7500280 .logico-title {
font-size: clamp(26px, 3.5vw, 54px) !important;
line-height: 1.25em !important;
word-wrap: break-word !important;
overflow-wrap: break-word !important;
}
/* Make sure paragraphs are responsive on all screen widths */
.elementor-element-165dfa5 {
width: 100% !important;
max-width: min(619px, 100%) !important;
}
.elementor-element-3888a1e {
width: 100% !important;
max-width: min(526px, 100%) !important;
}
/* Desktop/Laptop (min-width: 1025px) column width and flex rules */
@media (min-width: 1025px) {
.elementor-element-9ffed33 {
display: flex !important;
flex-direction: row !important;
flex-wrap: nowrap !important;
align-items: center !important;
}
.elementor-element-96343ba,
.elementor-element-71c3e1d {
width: 50% !important;
max-width: 50% !important;
flex: 1 1 50% !important;
box-sizing: border-box !important;
}
}
@media (max-width: 1024px) {
/* Force columns to stack vertically on mobile/tablet */
.elementor-element-9ffed33 {
flex-direction: column !important;
align-items: stretch !important;
}
/* Force both children to be full width */
.elementor-element-96343ba,
.elementor-element-71c3e1d {
width: 100% !important;
max-width: 100% !important;
padding-left: 0 !important;
padding-right: 0 !important;
}
/* Show image at the bottom or top depending on order */
.elementor-element-96343ba {
margin-top: 30px !important;
}
/* Override desktop fixed widths on mobile/tablet */
.elementor-element-165dfa5,
.elementor-element-3888a1e {
width: 100% !important;
max-width: 100% !important;
}
}
`}} />
<div className="elementor-element elementor-element-9ffed33 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="9ffed33" data-element_type="container" data-e-type="container" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}"> <div className="elementor-element elementor-element-9ffed33 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="9ffed33" data-element_type="container" data-e-type="container" data-settings="{&quot;background_background&quot;:&quot;classic&quot;}">
{/* Image side */} {/* Image side */}

View File

@@ -52,13 +52,84 @@ export default function IndexHero() {
return ( return (
<div className="elementor-element elementor-element-741f56c e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent" data-id="741f56c" data-element_type="container" data-e-type="container"> <div className="elementor-element elementor-element-741f56c e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent" data-id="741f56c" data-element_type="container" data-e-type="container">
<style dangerouslySetInnerHTML={{ __html: `
/* Fluid responsive font size override for hero headings */
.logico-content-slider-widget .content-slider-item-heading {
font-size: clamp(30px, 5.5vw, 80px) !important;
word-wrap: break-word !important;
overflow-wrap: break-word !important;
white-space: normal !important;
}
/* Prevent horizontal overflow on slider and stage containers */
.logico-content-slider-widget,
.content-slider-wrapper,
.content-slider-container,
.content-slider,
.owl-stage-outer,
.owl-stage,
.owl-item,
.slider-item,
.slide-content,
.slide-content-inner {
width: 100% !important;
max-width: 100% !important;
box-sizing: border-box !important;
}
/* Force word wrapping and responsive spacing for heading and text */
.logico-content-slider-widget .content-slider-item-heading,
.logico-content-slider-widget .content-slider-item-heading span,
.logico-content-slider-widget .content-slider-item-heading .heading-content {
white-space: normal !important;
word-wrap: break-word !important;
overflow-wrap: break-word !important;
max-width: 100% !important;
}
.logico-content-slider-widget .content-slider-item-heading {
padding-left: 15px !important;
padding-right: 15px !important;
}
.logico-content-slider-widget .text-content {
width: 100% !important;
max-width: min(680px, 100%) !important;
box-sizing: border-box !important;
padding-left: 15px !important;
padding-right: 15px !important;
}
/* Responsive slider heights */
.logico-content-slider-widget .owl-stage-outer {
height: 800px !important;
}
@media (max-width: 840px) {
.logico-content-slider-widget .owl-stage-outer {
height: 600px !important;
}
}
@media (max-width: 480px) {
.logico-content-slider-widget .owl-stage-outer {
height: 520px !important;
}
}
@media (max-width: 660px) {
.logico-content-slider-widget .content-slider-item-heading {
font-size: clamp(20px, 7vw, 32px) !important;
}
}
`}} />
<div className="elementor-element elementor-element-6c7cbcb elementor-widget elementor-widget-logico_content_slider" data-id="6c7cbcb" data-element_type="widget" data-e-type="widget" data-widget_type="logico_content_slider.default"> <div className="elementor-element elementor-element-6c7cbcb elementor-widget elementor-widget-logico_content_slider" data-id="6c7cbcb" data-element_type="widget" data-e-type="widget" data-widget_type="logico_content_slider.default">
<div className="elementor-widget-container"> <div className="elementor-widget-container">
<div className="logico-content-slider-widget"> <div className="logico-content-slider-widget">
<div className="content-slider-wrapper"> <div className="content-slider-wrapper">
<div className="content-slider-container"> <div className="content-slider-container">
<div className="content-slider owl-carousel owl-theme nav-view-vertical nav-h-position-right nav-v-position-bottom owl-loaded owl-drag" ref={containerRef}> <div className="content-slider owl-carousel owl-theme nav-view-vertical nav-h-position-right nav-v-position-bottom owl-loaded owl-drag" ref={containerRef}>
<div className="owl-stage-outer" style={{ position: "relative", overflow: "hidden", height: "800px" }}> <div className="owl-stage-outer" style={{ position: "relative", overflow: "hidden" }}>
<div className="owl-stage" style={{ position: "relative", width: "100%", height: "100%" }}> <div className="owl-stage" style={{ position: "relative", width: "100%", height: "100%" }}>
{/* Slide 1 */} {/* Slide 1 */}

View File

@@ -118,12 +118,14 @@ export default function Miles3() {
} }
.elementor-element-4add972 .elementor-icon svg { .elementor-element-4add972 .elementor-icon svg {
width: auto; width: auto;
max-width: 100%;
height: auto;
} }
.elementor-element-74687fb > .elementor-widget-container { padding: 22px 0 0 0; } .elementor-element-74687fb > .elementor-widget-container { padding: 22px 0 0 0; }
.elementor-element-74687fb .elementor-icon svg { height: 139px; } .elementor-element-74687fb .elementor-icon svg { max-height: 139px; }
.elementor-element-fd9c57e .elementor-icon svg { height: 158px; } .elementor-element-fd9c57e .elementor-icon svg { max-height: 158px; }
.elementor-element-fbb1628 > .elementor-widget-container { padding: 25px 0 0 0; } .elementor-element-fbb1628 > .elementor-widget-container { padding: 25px 0 0 0; }
.elementor-element-fbb1628 .elementor-icon svg { height: 128px; } .elementor-element-fbb1628 .elementor-icon svg { max-height: 128px; }
/* Card titles (First / Mid / Last Mile) */ /* Card titles (First / Mid / Last Mile) */
.elementor-element-d310968 > .elementor-widget-container, .elementor-element-d310968 > .elementor-widget-container,

View File

@@ -12,23 +12,6 @@ const StrategyCanvas = dynamic(() => import("./StrategyCanvas"), { ssr: false })
/** Center of each stage's scroll window (0…1). */ /** Center of each stage's scroll window (0…1). */
const CENTER = (i: number) => i / (N - 1); const CENTER = (i: number) => i / (N - 1);
/** True only while a card's own opacity window is open (tiny buffer). Keeps
* not-yet-reached stage cards out of the DOM / off the compositor until their
* stage is on screen — no future workflow state is rendered before activation.
* Visually identical: a card outside its window is opacity:0 regardless. */
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,
);
}
/** Persistent top rail: the 5 stages, current one highlighted. */ /** Persistent top rail: the 5 stages, current one highlighted. */
function StageRail({ active }: { active: number }) { function StageRail({ active }: { active: number }) {
return ( return (
@@ -54,11 +37,13 @@ function StageCard({
i, i,
scroll, scroll,
side, side,
active,
children, children,
}: { }: {
i: number; i: number;
scroll: MotionValue<number>; scroll: MotionValue<number>;
side: "left" | "right"; side: "left" | "right";
active: number;
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const c = CENTER(i); const c = CENTER(i);
@@ -67,8 +52,8 @@ function StageCard({
const opacity = useTransform(scroll, [c - 0.1, c - 0.05, c + 0.05, c + 0.1], [0, 1, 1, 0]); const opacity = useTransform(scroll, [c - 0.1, c - 0.05, c + 0.05, c + 0.1], [0, 1, 1, 0]);
const y = useTransform(scroll, [c - 0.1, c - 0.05], [34, 0]); const y = useTransform(scroll, [c - 0.1, c - 0.05], [34, 0]);
const s = STAGES[i]; const s = STAGES[i];
// Only mount the card while its stage's cross-fade window is open. // Only mount the card while its stage is active.
if (!useInWindow(opacity)) return null; if (active !== i) return null;
return ( return (
<motion.div <motion.div
className={`dm-st-card-story is-${side}`} className={`dm-st-card-story is-${side}`}
@@ -202,7 +187,7 @@ export default function StrategySection({ connected = false }: { connected?: boo
detail. Each: short title + one lead line + a couple of key chips. */} detail. Each: short title + one lead line + a couple of key chips. */}
{/* STAGE 01 — INPUT (green) */} {/* STAGE 01 — INPUT (green) */}
<StageCard i={0} scroll={scroll} side="left"> <StageCard i={0} scroll={scroll} side="left" active={active}>
<h3 className="dm-st-pillar__title">Orders &amp; riders enter the system</h3> <h3 className="dm-st-pillar__title">Orders &amp; riders enter the system</h3>
<p className="dm-st-anchor__lead">Orders are uploaded and matched against the available fleet, ready for assignment.</p> <p className="dm-st-anchor__lead">Orders are uploaded and matched against the available fleet, ready for assignment.</p>
<div className="dm-st-anchor__chips"> <div className="dm-st-anchor__chips">
@@ -213,7 +198,7 @@ export default function StrategySection({ connected = false }: { connected?: boo
</StageCard> </StageCard>
{/* STAGE 02 — PARALLEL EXECUTION (purple) */} {/* STAGE 02 — PARALLEL EXECUTION (purple) */}
<StageCard i={1} scroll={scroll} side="right"> <StageCard i={1} scroll={scroll} side="right" active={active}>
<h3 className="dm-st-pillar__title">Six strategies, evaluated in parallel</h3> <h3 className="dm-st-pillar__title">Six strategies, evaluated in parallel</h3>
<p className="dm-st-anchor__lead">The AI runs every routing strategy at the same time legacy baselines and MileTruth&apos;s unified engine.</p> <p className="dm-st-anchor__lead">The AI runs every routing strategy at the same time legacy baselines and MileTruth&apos;s unified engine.</p>
<div className="dm-st-anchor__chips"> <div className="dm-st-anchor__chips">
@@ -224,7 +209,7 @@ export default function StrategySection({ connected = false }: { connected?: boo
</StageCard> </StageCard>
{/* STAGE 03 — SMART OPTIMIZATION (blue) */} {/* STAGE 03 — SMART OPTIMIZATION (blue) */}
<StageCard i={2} scroll={scroll} side="left"> <StageCard i={2} scroll={scroll} side="left" active={active}>
<h3 className="dm-st-pillar__title">Routes optimized &amp; validated</h3> <h3 className="dm-st-pillar__title">Routes optimized &amp; validated</h3>
<p className="dm-st-anchor__lead">Every route is solved for distance, then checked against battery range and delivery SLAs.</p> <p className="dm-st-anchor__lead">Every route is solved for distance, then checked against battery range and delivery SLAs.</p>
<div className="dm-st-anchor__chips"> <div className="dm-st-anchor__chips">
@@ -235,7 +220,7 @@ export default function StrategySection({ connected = false }: { connected?: boo
</StageCard> </StageCard>
{/* STAGE 04 — PERFORMANCE GRADING (orange) */} {/* STAGE 04 — PERFORMANCE GRADING (orange) */}
<StageCard i={3} scroll={scroll} side="right"> <StageCard i={3} scroll={scroll} side="right" active={active}>
<h3 className="dm-st-pillar__title">Every strategy is scored</h3> <h3 className="dm-st-pillar__title">Every strategy is scored</h3>
<p className="dm-st-anchor__lead">Each strategy is graded live on fulfillment, SLA compliance, efficiency and battery feasibility.</p> <p className="dm-st-anchor__lead">Each strategy is graded live on fulfillment, SLA compliance, efficiency and battery feasibility.</p>
<div className="dm-st-anchor__chips"> <div className="dm-st-anchor__chips">
@@ -246,7 +231,7 @@ export default function StrategySection({ connected = false }: { connected?: boo
</StageCard> </StageCard>
{/* STAGE 05 — STRATEGY COMPARISON (red, hero) */} {/* STAGE 05 — STRATEGY COMPARISON (red, hero) */}
<StageCard i={4} scroll={scroll} side="right"> <StageCard i={4} scroll={scroll} side="right" active={active}>
<h3 className="dm-st-pillar__title">Happier riders. Higher fulfillment.</h3> <h3 className="dm-st-pillar__title">Happier riders. Higher fulfillment.</h3>
<p className="dm-st-anchor__lead">EV Aware wins the best fulfillment with feasible, battery-safe routes for every rider.</p> <p className="dm-st-anchor__lead">EV Aware wins the best fulfillment with feasible, battery-safe routes for every rider.</p>
<div className="dm-st-anchor__chips"> <div className="dm-st-anchor__chips">