Merge branch 'cursor/air-freight-page'
@@ -2,6 +2,15 @@ import type { NextConfig } from "next";
|
|||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
|
async redirects() {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
source: "/air-freight",
|
||||||
|
destination: "/doormile-wings",
|
||||||
|
permanent: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
turbopack: {
|
turbopack: {
|
||||||
root: path.resolve(__dirname),
|
root: path.resolve(__dirname),
|
||||||
},
|
},
|
||||||
|
|||||||
BIN
public/images/air-freight-pic-1.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/images/air-freight-slide-1.jpg
Normal file
|
After Width: | Height: | Size: 65 KiB |
BIN
public/images/air-freight-slide-2.jpg
Normal file
|
After Width: | Height: | Size: 143 KiB |
BIN
public/images/card 1.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/images/card 2.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/images/card 3.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/images/card4.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
public/images/card5.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
public/images/card6.png
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
public/images/pahse1.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/wings-heroslide2.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
public/images/wings-heroslider3.png
Normal file
|
After Width: | Height: | Size: 1.5 MiB |
BIN
public/images/wings-heroslider4.png
Normal file
|
After Width: | Height: | Size: 1.8 MiB |
BIN
public/images/women-hero-silder-1.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/images/women-hero-slider-2.png
Normal file
|
After Width: | Height: | Size: 2.0 MiB |
BIN
public/images/women-hero-slider-3.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
public/videos/Doormile-quantum-video3.mp4
Normal file
BIN
public/videos/fleet.mp4
Normal file
@@ -22,7 +22,7 @@ export default function AboutUsPage() {
|
|||||||
<AboutHero />
|
<AboutHero />
|
||||||
<TheDoormileWay />
|
<TheDoormileWay />
|
||||||
<EVLogisticSection />
|
<EVLogisticSection />
|
||||||
<WomenSection />
|
{/* <WomenSection /> */}
|
||||||
<CompetitiveEdge />
|
<CompetitiveEdge />
|
||||||
<IntelligenceGrid />
|
<IntelligenceGrid />
|
||||||
<OurTeam />
|
<OurTeam />
|
||||||
|
|||||||
15
src/app/air-freight/page.tsx
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { permanentRedirect } from "next/navigation";
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
alternates: {
|
||||||
|
canonical: "https://doormile.com/doormile-wings",
|
||||||
|
},
|
||||||
|
robots: {
|
||||||
|
index: false,
|
||||||
|
follow: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function AirFreightRedirectPage() {
|
||||||
|
permanentRedirect("/doormile-wings");
|
||||||
|
}
|
||||||
62
src/app/doormile-wings/page.tsx
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import WingsHero from "@/components/sections/WingsHero";
|
||||||
|
import WingsWhyGrid from "@/components/sections/WingsWhyGrid";
|
||||||
|
import WingsSpeedAdvantage from "@/components/sections/WingsSpeedAdvantage";
|
||||||
|
import WingsFeatureGrid from "@/components/sections/WingsFeatureGrid";
|
||||||
|
import WingsIndustries from "@/components/sections/WingsIndustries";
|
||||||
|
import WingsHowItWorks from "@/components/sections/WingsHowItWorks";
|
||||||
|
import WingsFleetStrategy from "@/components/sections/WingsFleetStrategy";
|
||||||
|
import WingsInfrastructure from "@/components/sections/WingsInfrastructure";
|
||||||
|
import WingsMileTruth from "@/components/sections/WingsMileTruth";
|
||||||
|
import WingsRoadmap from "@/components/sections/WingsRoadmap";
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: "Doormile Wings – Redefining Domestic Air Logistics Through AI",
|
||||||
|
description: "Doormile Wings links India's major regional capitals with AI-powered, same-day air logistics and zero-emission EV last-mile delivery — where quick commerce meets quick logistics.",
|
||||||
|
alternates: {
|
||||||
|
canonical: "https://doormile.com/doormile-wings",
|
||||||
|
},
|
||||||
|
openGraph: {
|
||||||
|
title: "Doormile Wings – Redefining Domestic Air Logistics Through AI",
|
||||||
|
description: "Same-day air logistics linking India's major regional capitals, powered by MileTruth™ AI and zero-emission EV last-mile delivery.",
|
||||||
|
url: "https://doormile.com/doormile-wings",
|
||||||
|
siteName: "Doormile",
|
||||||
|
type: "website",
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: "https://doormile.com/images/wings-logo.png",
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: "Doormile Wings",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary_large_image",
|
||||||
|
title: "Doormile Wings – Redefining Domestic Air Logistics Through AI",
|
||||||
|
description: "Same-day air logistics linking India's major regional capitals, powered by MileTruth™ AI and zero-emission EV last-mile delivery.",
|
||||||
|
images: ["https://doormile.com/images/wings-logo.png"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function DoormileWingsPage() {
|
||||||
|
return (
|
||||||
|
<div id="doormile-wings" 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="95" className="elementor elementor-95 elementor-59">
|
||||||
|
<WingsHero />
|
||||||
|
<WingsWhyGrid />
|
||||||
|
<WingsSpeedAdvantage />
|
||||||
|
<WingsFeatureGrid />
|
||||||
|
<WingsIndustries />
|
||||||
|
<WingsHowItWorks />
|
||||||
|
<WingsFleetStrategy />
|
||||||
|
<WingsInfrastructure />
|
||||||
|
<WingsMileTruth />
|
||||||
|
<WingsRoadmap />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
123
src/app/empowerment/page.tsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
import EmpowermentHero from "@/components/sections/EmpowermentHero";
|
||||||
|
import EmpowermentVision from "@/components/sections/EmpowermentVision";
|
||||||
|
import EmpowermentStory from "@/components/sections/EmpowermentStory";
|
||||||
|
import WomenSection from "@/components/sections/WomenSection";
|
||||||
|
import EmpowermentPrograms from "@/components/sections/EmpowermentPrograms";
|
||||||
|
import EmpowermentImpact from "@/components/sections/EmpowermentImpact";
|
||||||
|
import EmpowermentHowItWorks from "@/components/sections/EmpowermentHowItWorks";
|
||||||
|
import EmpowermentStories from "@/components/sections/EmpowermentStories";
|
||||||
|
import EmpowermentPartners from "@/components/sections/EmpowermentPartners";
|
||||||
|
import EmpowermentCTA from "@/components/sections/EmpowermentCTA";
|
||||||
|
|
||||||
|
const WINGS_WOMEN_STATS = [
|
||||||
|
{ value: "500", plus: "+", label: "Women Founders" },
|
||||||
|
{ value: "35", plus: "+", label: "Cities Active" },
|
||||||
|
{ value: "100", plus: "+", label: "Enterprises Launched" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const WINGS_WOMEN_CARDS = [
|
||||||
|
{
|
||||||
|
title: "Venture Building",
|
||||||
|
desc: "End-to-end support for women launching logistics enterprises — from ideation to market entry.",
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M4 19V5" />
|
||||||
|
<path d="M4 7h12l-2 4 2 4H4" />
|
||||||
|
<path d="M18 19h2" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Entrepreneurship",
|
||||||
|
desc: "Women building, owning, and scaling sustainable businesses within the logistics ecosystem.",
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<rect x="3" y="7" width="18" height="13" rx="2" />
|
||||||
|
<path d="M8 7V5a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M3 12h18" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Founder Networks",
|
||||||
|
desc: "Peer communities and strategic connections that accelerate growth and open new markets.",
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="8" cy="8" r="3" />
|
||||||
|
<circle cx="16" cy="8" r="3" />
|
||||||
|
<path d="M2.5 20c.7-3.1 2.8-5 5.5-5s4.8 1.9 5.5 5" />
|
||||||
|
<path d="M10.5 20c.7-3.1 2.8-5 5.5-5s4.8 1.9 5.5 5" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Enterprise Leadership",
|
||||||
|
desc: "Strategic decision-making, operational excellence, and vision-driven leadership for women founders.",
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="12" cy="9" r="5" />
|
||||||
|
<path d="M9 13.5 7.5 21 12 18l4.5 3-1.5-7.5" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const metadata = {
|
||||||
|
title: "Doormile Wings — Women Entrepreneurship Initiative",
|
||||||
|
description: "Doormile Wings empowers women to build, own, and lead enterprises within the logistics ecosystem through strategic partnerships, founder networks, and entrepreneurial infrastructure.",
|
||||||
|
alternates: {
|
||||||
|
canonical: "https://doormile.com/empowerment",
|
||||||
|
},
|
||||||
|
openGraph: {
|
||||||
|
title: "Doormile Wings — Women Entrepreneurship Initiative",
|
||||||
|
description: "An entrepreneurial ecosystem empowering women to launch, grow, and lead logistics enterprises with strategic partnerships and industry collaboration.",
|
||||||
|
url: "https://doormile.com/empowerment",
|
||||||
|
siteName: "Doormile",
|
||||||
|
type: "website",
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: "https://doormile.com/images/wings-logo.png",
|
||||||
|
width: 1200,
|
||||||
|
height: 630,
|
||||||
|
alt: "Doormile Wings — Women Entrepreneurship Initiative",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
card: "summary_large_image",
|
||||||
|
title: "Doormile Wings — Women Entrepreneurship Initiative",
|
||||||
|
description: "An entrepreneurial ecosystem empowering women to launch, grow, and lead logistics enterprises with strategic partnerships and industry collaboration.",
|
||||||
|
images: ["https://doormile.com/images/wings-logo.png"],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function EmpowermentPage() {
|
||||||
|
return (
|
||||||
|
<div id="empowerment" 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 elementor-empowerment">
|
||||||
|
<EmpowermentHero />
|
||||||
|
<EmpowermentVision />
|
||||||
|
<EmpowermentStory />
|
||||||
|
<WomenSection
|
||||||
|
eyebrow="/ Doormile Wings /"
|
||||||
|
bannerLabel="Women Entrepreneurship"
|
||||||
|
bannerCopy="An ecosystem where women build, own, and scale enterprises across logistics."
|
||||||
|
ctaLabel="Explore the ecosystem"
|
||||||
|
ctaHref="#wings-empowerment-programs"
|
||||||
|
heading="Women Building The Next Mile"
|
||||||
|
stats={WINGS_WOMEN_STATS}
|
||||||
|
cards={WINGS_WOMEN_CARDS}
|
||||||
|
/>
|
||||||
|
<EmpowermentPrograms />
|
||||||
|
<EmpowermentImpact />
|
||||||
|
<EmpowermentHowItWorks />
|
||||||
|
<EmpowermentStories />
|
||||||
|
<EmpowermentPartners />
|
||||||
|
<EmpowermentCTA />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
133
src/app/express/page.tsx
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import type { Metadata } from "next";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: "Express – Doormile",
|
||||||
|
description: "Doormile Express — a fast-lane logistics service for time-critical shipments. Coming soon.",
|
||||||
|
alternates: {
|
||||||
|
canonical: "https://doormile.com/express",
|
||||||
|
},
|
||||||
|
robots: {
|
||||||
|
index: false,
|
||||||
|
follow: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ExpressPage() {
|
||||||
|
return (
|
||||||
|
<div id="express" className="content-wrapper content-wrapper-may-contain-elementor-code content-wrapper-sidebar-position-none">
|
||||||
|
<div className="content">
|
||||||
|
<div className="content-inner">
|
||||||
|
<section className="express-coming-soon">
|
||||||
|
<div className="express-coming-soon-inner">
|
||||||
|
<span className="express-coming-soon-eyebrow">Solutions / Express</span>
|
||||||
|
<h1 className="express-coming-soon-heading">
|
||||||
|
Doormile Express is <span className="express-coming-soon-accent">on its way</span>
|
||||||
|
</h1>
|
||||||
|
<p className="express-coming-soon-text">
|
||||||
|
We're building a fast-lane logistics service for time-critical shipments.
|
||||||
|
Check back soon, or explore what Doormile already delivers today.
|
||||||
|
</p>
|
||||||
|
<div className="express-coming-soon-actions">
|
||||||
|
<Link href="/doormile-wings" prefetch className="logico-alter-button">
|
||||||
|
Explore Doormile Wings
|
||||||
|
</Link>
|
||||||
|
<Link href="/contact" className="express-coming-soon-link">
|
||||||
|
Get notified when we launch →
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Self-contained placeholder styling, scoped to .express-coming-soon.
|
||||||
|
Re-declares --logico-dark-text-color/--logico-light-text-color/
|
||||||
|
--logico-accent-color locally: the sitewide :root fallback is the
|
||||||
|
light-theme value (dark text on white), which would render near-
|
||||||
|
black text on this dark section without the override. */}
|
||||||
|
<style
|
||||||
|
suppressHydrationWarning
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.express-coming-soon {
|
||||||
|
--logico-dark-text-color: #ffffff;
|
||||||
|
--logico-light-text-color: #bebebe;
|
||||||
|
--logico-accent-color: #C01227;
|
||||||
|
min-height: calc(100vh - 160px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: clamp(48px, 8vw, 120px) 24px;
|
||||||
|
background: radial-gradient(120% 120% at 50% 0%, rgba(192, 18, 39, 0.18) 0%, rgba(26, 26, 26, 0.98) 55%, #111111 100%);
|
||||||
|
}
|
||||||
|
.express-coming-soon-inner {
|
||||||
|
max-width: 720px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.express-coming-soon-eyebrow {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.14em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: var(--logico-accent-color);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.express-coming-soon-heading {
|
||||||
|
font-family: 'Manrope', sans-serif;
|
||||||
|
font-size: clamp(32px, 5vw, 56px);
|
||||||
|
line-height: 1.15em;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--logico-dark-text-color);
|
||||||
|
margin: 0 0 20px;
|
||||||
|
}
|
||||||
|
.express-coming-soon-accent {
|
||||||
|
color: var(--logico-accent-color);
|
||||||
|
}
|
||||||
|
.express-coming-soon-text {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.75em;
|
||||||
|
color: var(--logico-light-text-color);
|
||||||
|
margin: 0 auto 36px;
|
||||||
|
max-width: 520px;
|
||||||
|
}
|
||||||
|
.express-coming-soon-actions {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
.express-coming-soon .logico-alter-button {
|
||||||
|
color: #ffffff !important;
|
||||||
|
border-color: rgba(255, 255, 255, 0.45) !important;
|
||||||
|
background: transparent !important;
|
||||||
|
}
|
||||||
|
.express-coming-soon .logico-alter-button:hover {
|
||||||
|
background: #ffffff !important;
|
||||||
|
color: #1a1a1a !important;
|
||||||
|
border-color: #ffffff !important;
|
||||||
|
}
|
||||||
|
.express-coming-soon-link {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--logico-light-text-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
text-underline-offset: 4px;
|
||||||
|
}
|
||||||
|
.express-coming-soon-link:hover {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.express-coming-soon-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -10,6 +10,27 @@ html {
|
|||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
SITE-WIDE STICKY FIX
|
||||||
|
The legacy Elementor build sets `.body-container { overflow: hidden }`
|
||||||
|
(public/css/site.css, generated — do not edit there). Any ancestor
|
||||||
|
with non-visible overflow becomes the containing block for
|
||||||
|
`position: sticky` descendants; since `.body-container` itself never
|
||||||
|
actually scrolls (the real scrollbar lives on <body>), that made every
|
||||||
|
`position: sticky` element on the site inert — it just scrolled away
|
||||||
|
in normal flow instead of pinning.
|
||||||
|
Note: setting overflow-x:hidden paired with overflow-y:visible gets
|
||||||
|
silently coerced to overflow-y:auto by the CSS spec (an "auto" value on
|
||||||
|
one axis when the other is non-visible) — which ALSO still counts as a
|
||||||
|
scroll container and breaks sticky the same way. So this must be fully
|
||||||
|
visible on both axes. <body> already has overflow-x: hidden site-wide
|
||||||
|
(from the same legacy stylesheet), so horizontal bleed is still safely
|
||||||
|
clipped there — this rule doesn't need to repeat it.
|
||||||
|
============================================================ */
|
||||||
|
.body-container {
|
||||||
|
overflow: visible !important;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
GLOBAL SPACING SYSTEM
|
GLOBAL SPACING SYSTEM
|
||||||
A single shared vertical-rhythm scale so every section uses
|
A single shared vertical-rhythm scale so every section uses
|
||||||
@@ -153,16 +174,21 @@ body {
|
|||||||
#masthead .elementor-element.elementor-element-0b7bf6f > .elementor-widget-container {
|
#masthead .elementor-element.elementor-element-0b7bf6f > .elementor-widget-container {
|
||||||
padding: 0 42px !important;
|
padding: 0 42px !important;
|
||||||
border-radius: 22px !important;
|
border-radius: 22px !important;
|
||||||
|
height: 58px !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#masthead .elementor-element.elementor-element-0b7bf6f .header-menu-container .main-menu > li > a {
|
#masthead .elementor-element.elementor-element-0b7bf6f .header-menu-container .main-menu > li > a {
|
||||||
padding-top: 15px !important;
|
padding-top: 6px !important;
|
||||||
padding-bottom: 16px !important;
|
padding-bottom: 6px !important;
|
||||||
padding-left: 14px !important;
|
padding-left: 14px !important;
|
||||||
padding-right: 14px !important;
|
padding-right: 14px !important;
|
||||||
font-size: 15px !important;
|
font-size: 15px !important;
|
||||||
line-height: 1.2 !important;
|
line-height: 1.2 !important;
|
||||||
white-space: nowrap !important;
|
white-space: nowrap !important;
|
||||||
|
min-height: auto !important;
|
||||||
|
height: auto !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
#masthead .header-menu-container .main-menu {
|
#masthead .header-menu-container .main-menu {
|
||||||
@@ -179,7 +205,11 @@ body {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#masthead .elementor-element.elementor-element-466de1b.dm-header-scrolled {
|
#masthead .elementor-element.elementor-element-466de1b.dm-header-scrolled {
|
||||||
top: 0 !important;
|
top: 15px !important;
|
||||||
|
left: 20px !important;
|
||||||
|
right: 20px !important;
|
||||||
|
border-radius: 24px !important;
|
||||||
|
width: auto !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -964,6 +994,49 @@ body {
|
|||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ============================================================
|
||||||
|
SHARED PAGE CONTENT CONTAINER
|
||||||
|
Matches the Hero/Header/Footer's real rendered OUTER edge, not the
|
||||||
|
Footer's narrower inner text column. The Hero card
|
||||||
|
(.owl-stage-outer, via .wings-hero-page.elementor-element-741f56c)
|
||||||
|
and the Footer's outer band (.elementor-element-b29b8fc) both sit a
|
||||||
|
fixed, uncapped 20px in from the viewport edge at every desktop
|
||||||
|
width (verified 1280px-2400px — no max-width ever kicks in). Apply
|
||||||
|
this class to any Wings-page section's content wrapper to guarantee
|
||||||
|
its cards/timeline/grid hug those same edges instead of sitting
|
||||||
|
narrower and more centered.
|
||||||
|
============================================================ */
|
||||||
|
.dm-section-container {
|
||||||
|
width: 100%;
|
||||||
|
margin-left: auto;
|
||||||
|
margin-right: auto;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.dm-section-container {
|
||||||
|
padding-left: var(--dm-mobile-gutter, 10px);
|
||||||
|
padding-right: var(--dm-mobile-gutter, 10px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Doormile Wings hero frame: the generic vendor CSS assigns 32px padding to
|
||||||
|
.elementor-element-741f56c on all pages; Home's frame is 20px (scoped
|
||||||
|
to .elementor-61). Scope the same 20px frame to the Doormile Wings hero so
|
||||||
|
the card alignment matches Home and Solutions exactly. */
|
||||||
|
@media (min-width: 1025px) {
|
||||||
|
.wings-hero-page.elementor-element-741f56c,
|
||||||
|
.af-hero-page.elementor-element-741f56c {
|
||||||
|
--padding-top: 20px;
|
||||||
|
--padding-right: 20px;
|
||||||
|
--padding-bottom: 20px;
|
||||||
|
--padding-left: 20px;
|
||||||
|
padding: 20px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Custom premium animations keyframes */
|
/* Custom premium animations keyframes */
|
||||||
@keyframes shimmer-sweep {
|
@keyframes shimmer-sweep {
|
||||||
0% {
|
0% {
|
||||||
@@ -1166,3 +1239,4 @@ body {
|
|||||||
margin-top: var(--space-section) !important;
|
margin-top: var(--space-section) !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from "react";
|
|||||||
import MileTruthHero from "../../components/sections/MileTruthHero";
|
import MileTruthHero from "../../components/sections/MileTruthHero";
|
||||||
import Workflow1 from "../../components/sections/Workflow1";
|
import Workflow1 from "../../components/sections/Workflow1";
|
||||||
import Workflow2 from "../../components/sections/Workflow2";
|
import Workflow2 from "../../components/sections/Workflow2";
|
||||||
|
import Workflow4 from "../../components/sections/Workflow4";
|
||||||
import Workflow3Lazy from "../../components/sections/Workflow3Lazy";
|
import Workflow3Lazy from "../../components/sections/Workflow3Lazy";
|
||||||
|
|
||||||
export const metadata = {
|
export const metadata = {
|
||||||
@@ -18,6 +19,7 @@ export default function MileTruthPage() {
|
|||||||
<MileTruthHero />
|
<MileTruthHero />
|
||||||
<Workflow1 />
|
<Workflow1 />
|
||||||
<Workflow2 />
|
<Workflow2 />
|
||||||
|
<Workflow4 />
|
||||||
<Workflow3Lazy />
|
<Workflow3Lazy />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -458,6 +458,9 @@ export default function Footer() {
|
|||||||
<div className="elementor-element elementor-element-a219b7d elementor-widget elementor-widget-logico_custom_navigation_menu" data-id="a219b7d" data-element_type="widget" data-e-type="widget" data-widget_type="logico_custom_navigation_menu.default">
|
<div className="elementor-element elementor-element-a219b7d elementor-widget elementor-widget-logico_custom_navigation_menu" data-id="a219b7d" data-element_type="widget" data-e-type="widget" data-widget_type="logico_custom_navigation_menu.default">
|
||||||
<div className="elementor-widget-container">
|
<div className="elementor-widget-container">
|
||||||
<ul className="logico-custom-menu-widget">
|
<ul className="logico-custom-menu-widget">
|
||||||
|
<li>
|
||||||
|
<Link href="/doormile-wings">Doormile Wings</Link>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<Link href="/about-us">About Us</Link>
|
<Link href="/about-us">About Us</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
* Menu open/close + sidebar state is read from HeaderUIProvider so BodyOverlay (sibling at body level) can react.
|
* Menu open/close + sidebar state is read from HeaderUIProvider so BodyOverlay (sibling at body level) can react.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { MouseEvent, useEffect, useState } from "react";
|
import { KeyboardEvent, FocusEvent, MouseEvent, useEffect, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { usePathname } from "next/navigation";
|
import { usePathname } from "next/navigation";
|
||||||
@@ -20,6 +20,9 @@ const CURRENT_PAGE_MAP: Record<string, string> = {
|
|||||||
"/miletruth": "miletruth",
|
"/miletruth": "miletruth",
|
||||||
"/solutions": "solutions",
|
"/solutions": "solutions",
|
||||||
"/about": "about",
|
"/about": "about",
|
||||||
|
"/doormile-wings": "doormile-wings",
|
||||||
|
"/empowerment": "entarpreneurship",
|
||||||
|
"/express": "express",
|
||||||
"/about-us": "about",
|
"/about-us": "about",
|
||||||
"/blog": "blogs",
|
"/blog": "blogs",
|
||||||
"/contact": "contact",
|
"/contact": "contact",
|
||||||
@@ -33,13 +36,17 @@ const CURRENT_PAGE_ALIASES: Record<string, string[]> = {
|
|||||||
home: ["home"],
|
home: ["home"],
|
||||||
"how-it-works": ["how-it-works", "how-its-works"],
|
"how-it-works": ["how-it-works", "how-its-works"],
|
||||||
miletruth: ["miletruth"],
|
miletruth: ["miletruth"],
|
||||||
|
// Solutions is now a plain top-level link — no child pages own its active state.
|
||||||
solutions: ["solutions"],
|
solutions: ["solutions"],
|
||||||
about: ["about", "about-us", "women"],
|
"doormile-wings": ["doormile-wings"],
|
||||||
|
entarpreneurship: ["entarpreneurship"],
|
||||||
blogs: ["blogs"],
|
blogs: ["blogs"],
|
||||||
contact: ["contact"],
|
contact: ["contact"],
|
||||||
"privacy-policy": ["privacy-policy"],
|
"privacy-policy": ["privacy-policy"],
|
||||||
"terms-of-service": ["terms-of-service"],
|
"terms-of-service": ["terms-of-service"],
|
||||||
"cookie-policy": ["cookie-policy"],
|
"cookie-policy": ["cookie-policy"],
|
||||||
|
// Company is the dropdown parent for About + Blogs + How It Works — active if any is.
|
||||||
|
company: ["blogs", "how-it-works", "about"],
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Header() {
|
export default function Header() {
|
||||||
@@ -72,7 +79,7 @@ export default function Header() {
|
|||||||
targetId: string,
|
targetId: string,
|
||||||
shouldCloseMenu = false,
|
shouldCloseMenu = false,
|
||||||
) => {
|
) => {
|
||||||
if (shouldCloseMenu) closeAll();
|
if (shouldCloseMenu) closeMobileMenu();
|
||||||
|
|
||||||
if (pathname !== targetPath) return;
|
if (pathname !== targetPath) return;
|
||||||
|
|
||||||
@@ -90,6 +97,23 @@ export default function Header() {
|
|||||||
const [visibleScrolled, setVisibleScrolled] = useState(false);
|
const [visibleScrolled, setVisibleScrolled] = useState(false);
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
|
|
||||||
|
// Company dropdown (desktop) / accordion (mobile). Local to Header —
|
||||||
|
// unlike isMenuOpen/isSidebarOpen (HeaderUIProvider), no sibling component
|
||||||
|
// needs to read this.
|
||||||
|
const [isDesktopCompanyOpen, setDesktopCompanyOpen] = useState(false);
|
||||||
|
const [isMobileCompanyOpen, setMobileCompanyOpen] = useState(false);
|
||||||
|
|
||||||
|
// Collapse the accordion whenever the off-canvas menu closes, so it isn't
|
||||||
|
// left expanded the next time the menu opens.
|
||||||
|
const closeMobileMenu = () => {
|
||||||
|
setMobileCompanyOpen(false);
|
||||||
|
closeAll();
|
||||||
|
};
|
||||||
|
const toggleMobileMenu = () => {
|
||||||
|
setMobileCompanyOpen(false);
|
||||||
|
toggleMenu();
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handle = requestAnimationFrame(() => {
|
const handle = requestAnimationFrame(() => {
|
||||||
setVisibleScrolled(true);
|
setVisibleScrolled(true);
|
||||||
@@ -448,25 +472,86 @@ export default function Header() {
|
|||||||
<li id="menu-item-10508" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}>
|
<li id="menu-item-10508" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}>
|
||||||
<Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home")}>Home</Link>
|
<Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home")}>Home</Link>
|
||||||
</li>
|
</li>
|
||||||
<li id="menu-item-10509" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("how-it-works")}`}>
|
<li id="menu-item-10509" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("doormile-wings")}`}>
|
||||||
<Link href="/how-it-works#how-it-works" onClick={(event) => handleNavClick(event, "/how-it-works", "how-it-works")}>How It Works</Link>
|
<Link href="/doormile-wings" prefetch>Wings</Link>
|
||||||
</li>
|
</li>
|
||||||
<li id="menu-item-10510" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
|
<li id="menu-item-10510" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
|
||||||
<Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth")}>MileTruth™ AI</Link>
|
<Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth")}>MileTruth™ AI</Link>
|
||||||
</li>
|
</li>
|
||||||
<li id="menu-item-10511" className={`menu-item menu-item-type-custom menu-item-10511${dmHeaderActive("solutions")}`}>
|
<li id="menu-item-10511" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10511${dmHeaderActive("solutions")}`}>
|
||||||
<Link href="/solutions#solutions" onClick={(event) => handleNavClick(event, "/solutions", "solutions")}>Solutions</Link>
|
<Link href="/solutions" prefetch>Solutions</Link>
|
||||||
</li>
|
</li>
|
||||||
<li id="menu-item-10512" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10512${dmHeaderActive("about")}`}>
|
<li id="menu-item-10513" className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10513${dmHeaderActive("entarpreneurship")}`}>
|
||||||
<Link href="/about-us#about" onClick={(event) => handleNavClick(event, "/about-us", "about")}>About</Link>
|
<Link href="/empowerment" prefetch>Entrepreneurship</Link>
|
||||||
</li>
|
</li>
|
||||||
<li id="menu-item-10535" className={`menu-item menu-item-type-post_type menu-item-object-page menu-item-10535${dmHeaderActive("blogs")}`}>
|
<li
|
||||||
<Link href="/blog#blogs" onClick={(event) => handleNavClick(event, "/blog", "blogs")}>Blogs</Link>
|
id="menu-item-10535"
|
||||||
|
className={`menu-item menu-item-type-custom menu-item-has-children menu-item-10535${dmHeaderActive("company")}`}
|
||||||
|
onMouseEnter={() => setDesktopCompanyOpen(true)}
|
||||||
|
onMouseLeave={() => setDesktopCompanyOpen(false)}
|
||||||
|
onKeyDown={(event: KeyboardEvent<HTMLLIElement>) => {
|
||||||
|
if (event.key !== "Escape") return;
|
||||||
|
setDesktopCompanyOpen(false);
|
||||||
|
event.currentTarget.querySelector<HTMLAnchorElement>(":scope > a")?.focus();
|
||||||
|
}}
|
||||||
|
onBlur={(event: FocusEvent<HTMLLIElement>) => {
|
||||||
|
if (!event.currentTarget.contains(event.relatedTarget as Node)) {
|
||||||
|
setDesktopCompanyOpen(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={isDesktopCompanyOpen}
|
||||||
|
onFocus={() => setDesktopCompanyOpen(true)}
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setDesktopCompanyOpen((open) => !open);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Company
|
||||||
|
</a>
|
||||||
|
<ul className="dm-solutions-dropdown">
|
||||||
|
<li className={currentPage === "about" ? "dm-child-active" : ""}>
|
||||||
|
<Link
|
||||||
|
href="/about-us#about"
|
||||||
|
onClick={(event) => {
|
||||||
|
setDesktopCompanyOpen(false);
|
||||||
|
handleNavClick(event, "/about-us", "about");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
About
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li className={currentPage === "blogs" ? "dm-child-active" : ""}>
|
||||||
|
<Link
|
||||||
|
href="/blog#blogs"
|
||||||
|
onClick={(event) => {
|
||||||
|
setDesktopCompanyOpen(false);
|
||||||
|
handleNavClick(event, "/blog", "blogs");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Blogs
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
<li className={currentPage === "how-it-works" ? "dm-child-active" : ""}>
|
||||||
|
<Link
|
||||||
|
href="/how-it-works#how-it-works"
|
||||||
|
onClick={(event) => {
|
||||||
|
setDesktopCompanyOpen(false);
|
||||||
|
handleNavClick(event, "/how-it-works", "how-it-works");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
How It Works
|
||||||
|
</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
<div className="menu-trigger" data-id="0b7bf6f" onClick={toggleMenu}>
|
<div className="menu-trigger" data-id="0b7bf6f" onClick={toggleMobileMenu}>
|
||||||
<span className="menu-trigger-icon">
|
<span className="menu-trigger-icon">
|
||||||
<span className="hamburger">
|
<span className="hamburger">
|
||||||
<span></span>
|
<span></span>
|
||||||
@@ -486,7 +571,7 @@ export default function Header() {
|
|||||||
<div className="header-icon login-logout">
|
<div className="header-icon login-logout">
|
||||||
<a href="#" title="Login/Register" className="link-login"></a>
|
<a href="#" title="Login/Register" className="link-login"></a>
|
||||||
</div>
|
</div>
|
||||||
<div className="header-icon menu-close" onClick={closeAll}>
|
<div className="header-icon menu-close" onClick={closeMobileMenu}>
|
||||||
<span className="menu-close-icon"></span>
|
<span className="menu-close-icon"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -496,20 +581,53 @@ export default function Header() {
|
|||||||
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}>
|
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10508${dmHeaderActive("home")}`}>
|
||||||
<Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home", true)}>Home</Link>
|
<Link href="/#home" onClick={(event) => handleNavClick(event, "/", "home", true)}>Home</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("how-it-works")}`}>
|
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10509${dmHeaderActive("doormile-wings")}`}>
|
||||||
<Link href="/how-it-works#how-it-works" onClick={(event) => handleNavClick(event, "/how-it-works", "how-it-works", true)}>How It Works</Link>
|
<Link href="/doormile-wings" prefetch onClick={closeMobileMenu}>Wings</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
|
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10510${dmHeaderActive("miletruth")}`}>
|
||||||
<Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth", true)}>MileTruth™ AI</Link>
|
<Link href="/miletruth#miletruth" onClick={(event) => handleNavClick(event, "/miletruth", "miletruth", true)}>MileTruth™ AI</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={`menu-item menu-item-type-custom menu-item-10511${dmHeaderActive("solutions")}`}>
|
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10511${dmHeaderActive("solutions")}`}>
|
||||||
<Link href="/solutions#solutions" onClick={(event) => handleNavClick(event, "/solutions", "solutions", true)}>Solutions</Link>
|
<Link href="/solutions" prefetch onClick={closeMobileMenu}>Solutions</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-has-children menu-item-10512${dmHeaderActive("about")}`}>
|
<li className={`menu-item menu-item-type-custom menu-item-object-custom menu-item-10513${dmHeaderActive("entarpreneurship")}`}>
|
||||||
<Link href="/about-us#about" onClick={(event) => handleNavClick(event, "/about-us", "about", true)}>About</Link>
|
<Link href="/empowerment" prefetch onClick={closeMobileMenu}>Entrepreneurship</Link>
|
||||||
</li>
|
</li>
|
||||||
<li className={`menu-item menu-item-type-post_type menu-item-object-page menu-item-10535${dmHeaderActive("blogs")}`}>
|
<li
|
||||||
<Link href="/blog#blogs" onClick={(event) => handleNavClick(event, "/blog", "blogs", true)}>Blogs</Link>
|
className={`menu-item menu-item-type-custom menu-item-10535 dm-mobile-solutions${dmHeaderActive("company")}${isMobileCompanyOpen ? " dm-accordion-open" : ""}`}
|
||||||
|
>
|
||||||
|
<div className="dm-mobile-solutions-row">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
onClick={(event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
setMobileCompanyOpen((open) => !open);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Company
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="dm-mobile-solutions-toggle"
|
||||||
|
aria-label={isMobileCompanyOpen ? "Close Company menu" : "Open Company menu"}
|
||||||
|
aria-haspopup="true"
|
||||||
|
aria-expanded={isMobileCompanyOpen}
|
||||||
|
onClick={() => setMobileCompanyOpen((open) => !open)}
|
||||||
|
>
|
||||||
|
<span className="dm-accordion-chevron" aria-hidden="true"></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<ul className="dm-mobile-solutions-panel">
|
||||||
|
<li className={currentPage === "about" ? "dm-child-active" : ""}>
|
||||||
|
<Link href="/about-us#about" onClick={(event) => handleNavClick(event, "/about-us", "about", true)}>About</Link>
|
||||||
|
</li>
|
||||||
|
<li className={currentPage === "blogs" ? "dm-child-active" : ""}>
|
||||||
|
<Link href="/blog#blogs" onClick={(event) => handleNavClick(event, "/blog", "blogs", true)}>Blogs</Link>
|
||||||
|
</li>
|
||||||
|
<li className={currentPage === "how-it-works" ? "dm-child-active" : ""}>
|
||||||
|
<Link href="/how-it-works#how-it-works" onClick={(event) => handleNavClick(event, "/how-it-works", "how-it-works", true)}>How It Works</Link>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
@@ -867,6 +985,281 @@ export default function Header() {
|
|||||||
-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");
|
-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");
|
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");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ── Company dropdown (desktop) / accordion (mobile) ──
|
||||||
|
Company (Blogs + How It Works) is a non-navigating parent —
|
||||||
|
its trigger is a plain anchor with no href target, only a
|
||||||
|
dropdown toggle. Class names below (.dm-solutions-dropdown,
|
||||||
|
.dm-mobile-solutions*) are unchanged from when this same
|
||||||
|
component/markup belonged to Solutions, reusing site.css's dead
|
||||||
|
.menu-item-has-children chevron (fontello glyph, already
|
||||||
|
coloured white for this exact header widget) instead of
|
||||||
|
inventing a new arrow. Desktop opens on hover/focus; mobile is a
|
||||||
|
height-collapsing accordion controlled by the chevron button. */
|
||||||
|
@media (min-width: 1025px) {
|
||||||
|
#masthead .header-menu-container nav {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li > a {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 0 !important;
|
||||||
|
padding-bottom: 0 !important;
|
||||||
|
min-height: 48px;
|
||||||
|
line-height: 1.2 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children:after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
width: 100%;
|
||||||
|
min-width: 360px;
|
||||||
|
height: 24px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, 34px);
|
||||||
|
width: 240px;
|
||||||
|
min-width: 240px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px;
|
||||||
|
list-style: none;
|
||||||
|
background: linear-gradient(180deg, rgba(31, 31, 31, 0.98) 0%, rgba(18, 18, 18, 0.98) 100%);
|
||||||
|
-webkit-backdrop-filter: blur(20px);
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.38), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.09);
|
||||||
|
border-radius: 18px;
|
||||||
|
opacity: 0;
|
||||||
|
visibility: hidden;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: opacity 0.25s ease, transform 0.25s ease, visibility 0.25s;
|
||||||
|
z-index: 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .main-menu li:not(:last-of-type) (site.css) is a descendant
|
||||||
|
selector that also matches these nested <li>s, pushing every
|
||||||
|
row but the last 12px to the right. Cancel it here. */
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li {
|
||||||
|
margin-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li + li {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
height: 48px;
|
||||||
|
padding: 0 16px;
|
||||||
|
border-radius: 10px;
|
||||||
|
/* Hardcoded rather than var(--logico-dark-text-color): that
|
||||||
|
variable is only overridden to white inside a few unrelated
|
||||||
|
scoped contexts (e.g. #side-panel-2f31137) and otherwise
|
||||||
|
falls back to the sitewide light-theme value (#111111),
|
||||||
|
which would be invisible on this dark glass panel. */
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-family: var(--font-manrope), "Manrope", sans-serif;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.25;
|
||||||
|
letter-spacing: normal;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: background-color 0.25s ease, color 0.25s ease, padding-left 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:before {
|
||||||
|
content: "";
|
||||||
|
width: 11px;
|
||||||
|
height: 11px;
|
||||||
|
margin-left: -16px;
|
||||||
|
flex: 0 0 11px;
|
||||||
|
background-color: #c01227;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(-10px);
|
||||||
|
-webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12 12 4'/%3E%3Cpath d='M5 4h7v7'/%3E%3C/svg%3E");
|
||||||
|
mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 12 12 4'/%3E%3Cpath d='M5 4h7v7'/%3E%3C/svg%3E");
|
||||||
|
-webkit-mask-repeat: no-repeat;
|
||||||
|
mask-repeat: no-repeat;
|
||||||
|
-webkit-mask-position: center;
|
||||||
|
mask-position: center;
|
||||||
|
-webkit-mask-size: contain;
|
||||||
|
mask-size: contain;
|
||||||
|
transition: opacity 0.25s ease-in-out, transform 0.25s ease-in-out, background-color 0.25s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:hover,
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:focus-visible {
|
||||||
|
background-color: rgba(255, 255, 255, 0.08);
|
||||||
|
color: #ffffff;
|
||||||
|
padding-left: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:hover:before,
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li > a:focus-visible:before {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
background-color: #c01227;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children .dm-solutions-dropdown li.dm-child-active > a {
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children > a[aria-expanded="true"] ~ .dm-solutions-dropdown {
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
pointer-events: auto;
|
||||||
|
transform: translate(-50%, 24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children > a[aria-expanded="true"]:after {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Outlines are globally suppressed for .main-menu>li>a above;
|
||||||
|
restore a visible focus ring scoped only to this trigger. */
|
||||||
|
#masthead .header-menu-container .main-menu > li.menu-item-has-children > a:focus-visible {
|
||||||
|
outline: 2px solid var(--logico-dark-text-color, #ffffff);
|
||||||
|
outline-offset: 6px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-row {
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-row > a {
|
||||||
|
display: block;
|
||||||
|
flex: 1 1 auto;
|
||||||
|
padding: 0.75em 0;
|
||||||
|
color: var(--logico-default-text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-toggle {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
flex: 0 0 44px;
|
||||||
|
border: 0;
|
||||||
|
background: transparent;
|
||||||
|
color: var(--logico-default-text-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-toggle:focus-visible {
|
||||||
|
outline: 2px solid currentColor;
|
||||||
|
outline-offset: 2px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-accordion-chevron {
|
||||||
|
display: inline-block;
|
||||||
|
width: 0.7em;
|
||||||
|
height: 0.7em;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
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;
|
||||||
|
-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' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%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' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions.dm-accordion-open .dm-accordion-chevron {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel {
|
||||||
|
max-height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
padding-left: 18px;
|
||||||
|
transition: max-height 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions.dm-accordion-open .dm-mobile-solutions-panel {
|
||||||
|
max-height: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li {
|
||||||
|
font-size: 0.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* .mobile-header-menu-container ul.main-menu>li>a (site.css) only
|
||||||
|
matches direct children of .main-menu, so these nested links
|
||||||
|
fall through to the sitewide generic underlined-anchor default.
|
||||||
|
Restore the plain sibling-item look explicitly. */
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li > a {
|
||||||
|
display: block;
|
||||||
|
padding: 0.35em 0;
|
||||||
|
color: var(--logico-default-text-color);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A vendor li:before rule (checkmark bullet, likely intended for a
|
||||||
|
feature-list style elsewhere) leaks through here since the reset
|
||||||
|
at .mobile-header-menu-container ul.main-menu>li:before only
|
||||||
|
matches direct children of .main-menu, not this nested li.
|
||||||
|
Suppress it on both the <li> and <a> — these child rows aren't
|
||||||
|
top-level nav items and shouldn't carry any bullet/indicator. */
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li:before,
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li > a:before {
|
||||||
|
content: none !important;
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#masthead .mobile-header-menu-container .dm-mobile-solutions-panel > li.dm-child-active > a {
|
||||||
|
color: var(--logico-light-text-color);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -42,6 +42,9 @@ export interface EVStat {
|
|||||||
decimals?: number;
|
decimals?: number;
|
||||||
suffix: string;
|
suffix: string;
|
||||||
label: string;
|
label: string;
|
||||||
|
/** Optional static text override — when set, renders this text instead of
|
||||||
|
* an animated CountUp (for non-numeric KPI concepts, e.g. "QUBO"). */
|
||||||
|
text?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Accent theming for the dashboard column so it matches the scene on the left
|
/** Accent theming for the dashboard column so it matches the scene on the left
|
||||||
@@ -83,10 +86,19 @@ interface EVSectionProps {
|
|||||||
* static homepage feature list. The bottom stat bar is dropped in this mode
|
* static homepage feature list. The bottom stat bar is dropped in this mode
|
||||||
* to avoid repeating the metrics. */
|
* to avoid repeating the metrics. */
|
||||||
metrics?: EVStat[];
|
metrics?: EVStat[];
|
||||||
|
/** Optional distinct copy for the bottom `.evnd__bar` strip in dashboard
|
||||||
|
* mode. Defaults to reusing `metrics` (WF1/WF2 behaviour) when omitted. */
|
||||||
|
barStats?: EVStat[];
|
||||||
/** Heading above the dashboard, e.g. "PERFORMANCE INSIGHT". */
|
/** Heading above the dashboard, e.g. "PERFORMANCE INSIGHT". */
|
||||||
cardsHeading?: string;
|
cardsHeading?: string;
|
||||||
/** Accent theme for the dashboard column (defaults to the section red). */
|
/** Accent theme for the dashboard column (defaults to the section red). */
|
||||||
cardsTheme?: EVCardsTheme;
|
cardsTheme?: EVCardsTheme;
|
||||||
|
/** Dashboard system label, top-right of the header row. Defaults to "AI ENGINE". */
|
||||||
|
sysLabel?: string;
|
||||||
|
/** Extra class appended to `.evnd__imgwrap`, for workflow-specific media
|
||||||
|
* sizing overrides (e.g. video object-fit) defined in the caller's own
|
||||||
|
* scoped <style> tag rather than here. */
|
||||||
|
mediaClassName?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- Default content = original homepage EV Logistics section ---- */
|
/* ---- Default content = original homepage EV Logistics section ---- */
|
||||||
@@ -260,11 +272,13 @@ function DashboardPanel({
|
|||||||
features,
|
features,
|
||||||
heading,
|
heading,
|
||||||
theme,
|
theme,
|
||||||
|
sysLabel = "AI ENGINE",
|
||||||
}: {
|
}: {
|
||||||
metrics: EVStat[];
|
metrics: EVStat[];
|
||||||
features: EVFeature[];
|
features: EVFeature[];
|
||||||
heading?: string;
|
heading?: string;
|
||||||
theme?: EVCardsTheme;
|
theme?: EVCardsTheme;
|
||||||
|
sysLabel?: string;
|
||||||
}) {
|
}) {
|
||||||
const themeVars = theme
|
const themeVars = theme
|
||||||
? ({
|
? ({
|
||||||
@@ -287,7 +301,7 @@ function DashboardPanel({
|
|||||||
<span className="evnd__dash-livedot" />
|
<span className="evnd__dash-livedot" />
|
||||||
{heading}
|
{heading}
|
||||||
</span>
|
</span>
|
||||||
<span className="evnd__dash-sys">AI ENGINE</span>
|
<span className="evnd__dash-sys">{sysLabel}</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -295,12 +309,16 @@ function DashboardPanel({
|
|||||||
<div className="evnd__dash-kpis">
|
<div className="evnd__dash-kpis">
|
||||||
{metrics.map((m) => (
|
{metrics.map((m) => (
|
||||||
<div className="evnd__kpi" key={m.label}>
|
<div className="evnd__kpi" key={m.label}>
|
||||||
<CountUp
|
{m.text ? (
|
||||||
value={m.value}
|
<b className="evnd__kpi-val evnd__kpi-val--text">{m.text}</b>
|
||||||
decimals={m.decimals}
|
) : (
|
||||||
suffix={m.suffix}
|
<CountUp
|
||||||
className="evnd__kpi-val"
|
value={m.value}
|
||||||
/>
|
decimals={m.decimals}
|
||||||
|
suffix={m.suffix}
|
||||||
|
className="evnd__kpi-val"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<span className="evnd__kpi-label">{m.label}</span>
|
<span className="evnd__kpi-label">{m.label}</span>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
@@ -342,11 +360,15 @@ export default function EVSection({
|
|||||||
ariaLabel,
|
ariaLabel,
|
||||||
mediaSlot,
|
mediaSlot,
|
||||||
metrics,
|
metrics,
|
||||||
|
barStats,
|
||||||
cardsHeading,
|
cardsHeading,
|
||||||
cardsTheme,
|
cardsTheme,
|
||||||
|
sysLabel,
|
||||||
|
mediaClassName,
|
||||||
}: EVSectionProps) {
|
}: EVSectionProps) {
|
||||||
const bannerRef = useRef<HTMLDivElement>(null);
|
const bannerRef = useRef<HTMLDivElement>(null);
|
||||||
const useDashboard = !!metrics && metrics.length > 0;
|
const useDashboard = !!metrics && metrics.length > 0;
|
||||||
|
const isVideo = image.endsWith(".mp4");
|
||||||
|
|
||||||
/* In dashboard mode the workflow's accent theme is applied at the .evnd
|
/* In dashboard mode the workflow's accent theme is applied at the .evnd
|
||||||
section level so the shared animated network backdrop (below) is tinted to
|
section level so the shared animated network backdrop (below) is tinted to
|
||||||
@@ -599,13 +621,31 @@ export default function EVSection({
|
|||||||
background: #ef4444;
|
background: #ef4444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- Heading → content gap ----
|
||||||
|
Deliberately set here (on the wrapper div) rather than as
|
||||||
|
margin-bottom on .evnd__title (the <h2>): a global site.css rule
|
||||||
|
(.logico-front-end h2:not([class*="logico-title-h"]):last-child)
|
||||||
|
outweighs .evnd__title's own specificity and zeroes any bottom
|
||||||
|
margin placed on the heading itself, since the h2 is always the
|
||||||
|
last child of .evnd__heading. Setting it here sidesteps that
|
||||||
|
collision and gives every workflow section + Fleet Strategy one
|
||||||
|
shared, consistent gap. */
|
||||||
|
.evnd__heading {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
@media (max-width: 991px) and (min-width: 768px) {
|
||||||
|
.evnd__heading {
|
||||||
|
margin-bottom: 34px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.evnd__title {
|
.evnd__title {
|
||||||
color: #fff !important;
|
color: #fff !important;
|
||||||
font-weight: 800 !important;
|
font-weight: 800 !important;
|
||||||
font-size: clamp(32px, 3.8vw, 48px) !important;
|
font-size: clamp(32px, 3.8vw, 48px) !important;
|
||||||
line-height: 1.15 !important;
|
line-height: 1.15 !important;
|
||||||
letter-spacing: -0.01em;
|
letter-spacing: -0.01em;
|
||||||
margin: 0 0 36px 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
.evnd__title {
|
.evnd__title {
|
||||||
@@ -756,7 +796,7 @@ export default function EVSection({
|
|||||||
.evnd-feature__desc {
|
.evnd-feature__desc {
|
||||||
color: rgba(255,255,255,0.65) !important;
|
color: rgba(255,255,255,0.65) !important;
|
||||||
font-weight: 400 !important;
|
font-weight: 400 !important;
|
||||||
font-size: 14px !important;
|
font-size: 16px !important;
|
||||||
line-height: 1.6 !important;
|
line-height: 1.6 !important;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
@@ -871,6 +911,13 @@ export default function EVSection({
|
|||||||
font-size: 12.5px;
|
font-size: 12.5px;
|
||||||
letter-spacing: 0.01em;
|
letter-spacing: 0.01em;
|
||||||
}
|
}
|
||||||
|
/* Non-numeric KPI concepts (EVStat.text) — same gradient treatment as
|
||||||
|
the counted values, sized down so short codes/words don't inherit
|
||||||
|
the numeric clamp() meant for 2-3 digit figures. */
|
||||||
|
.evnd__kpi-val--text {
|
||||||
|
font-size: clamp(18px, 1.6vw, 22px) !important;
|
||||||
|
letter-spacing: 0.02em;
|
||||||
|
}
|
||||||
|
|
||||||
/* ============================================================
|
/* ============================================================
|
||||||
Large-screen layout balance (WF1/WF2 dashboard only, ≥1440px).
|
Large-screen layout balance (WF1/WF2 dashboard only, ≥1440px).
|
||||||
@@ -973,8 +1020,8 @@ export default function EVSection({
|
|||||||
.evnd__dfeat-desc {
|
.evnd__dfeat-desc {
|
||||||
color: rgba(255,255,255,0.6) !important;
|
color: rgba(255,255,255,0.6) !important;
|
||||||
font-weight: 400 !important;
|
font-weight: 400 !important;
|
||||||
font-size: 13px !important;
|
font-size: 16px !important;
|
||||||
line-height: 1.45 !important;
|
line-height: 1.55 !important;
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1045,6 +1092,14 @@ export default function EVSection({
|
|||||||
text-transform: none;
|
text-transform: none;
|
||||||
opacity: 0.9;
|
opacity: 0.9;
|
||||||
}
|
}
|
||||||
|
/* Non-numeric bar values (EVStat.text) — the bar's numeric clamp()
|
||||||
|
(up to 56px) is too large for words/phrases; size down and let
|
||||||
|
longer phrases (e.g. "Cost + Distance + SLA") wrap cleanly. */
|
||||||
|
.evnd__bar-val--text {
|
||||||
|
font-size: clamp(16px, 1.8vw, 22px);
|
||||||
|
line-height: 1.25;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes evndGlow { 0%,100% { opacity: 0.75; } 50% { opacity: 1; } }
|
@keyframes evndGlow { 0%,100% { opacity: 0.75; } 50% { opacity: 1; } }
|
||||||
|
|
||||||
@@ -1056,7 +1111,6 @@ export default function EVSection({
|
|||||||
@media (max-width: 991px) {
|
@media (max-width: 991px) {
|
||||||
.evnd { padding: 48px 32px 56px; }
|
.evnd { padding: 48px 32px 56px; }
|
||||||
.evnd__grid { grid-template-columns: 1fr; gap: 40px; }
|
.evnd__grid { grid-template-columns: 1fr; gap: 40px; }
|
||||||
.evnd__title { margin-bottom: 28px; }
|
|
||||||
.evnd__features { gap: 14px; }
|
.evnd__features { gap: 14px; }
|
||||||
/* Stacked: KPI row can spread to 4-up since it now has full width. */
|
/* Stacked: KPI row can spread to 4-up since it now has full width. */
|
||||||
.evnd__dash-kpis { grid-template-columns: repeat(4, 1fr); }
|
.evnd__dash-kpis { grid-template-columns: repeat(4, 1fr); }
|
||||||
@@ -1335,7 +1389,7 @@ export default function EVSection({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={`evnd__inner${useDashboard ? " evnd__inner--cards" : ""}${
|
className={`evnd__inner${useDashboard ? " evnd__inner--cards" : ""}${
|
||||||
useDashboard && !mediaSlot ? " evnd__inner--img" : ""
|
useDashboard && !mediaSlot && !isVideo ? " evnd__inner--img" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="evnd__heading">
|
<div className="evnd__heading">
|
||||||
@@ -1349,7 +1403,7 @@ export default function EVSection({
|
|||||||
{/* MAIN GRID */}
|
{/* MAIN GRID */}
|
||||||
<div
|
<div
|
||||||
className={`evnd__grid${useDashboard ? " evnd__grid--cards" : ""}${
|
className={`evnd__grid${useDashboard ? " evnd__grid--cards" : ""}${
|
||||||
useDashboard && !mediaSlot ? " evnd__grid--img" : ""
|
useDashboard && !mediaSlot && !isVideo ? " evnd__grid--img" : ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{/* Left column */}
|
{/* Left column */}
|
||||||
@@ -1357,11 +1411,11 @@ export default function EVSection({
|
|||||||
<div className="evnd__media">
|
<div className="evnd__media">
|
||||||
<div className="evnd__glow" />
|
<div className="evnd__glow" />
|
||||||
<div
|
<div
|
||||||
className={`evnd__imgwrap${mediaSlot ? " evnd__imgwrap--media" : ""}${image.endsWith(".mp4") ? " evnd__imgwrap--video" : ""}`}
|
className={`evnd__imgwrap${mediaSlot ? " evnd__imgwrap--media" : ""}${isVideo ? " evnd__imgwrap--video" : ""}${mediaClassName ? ` ${mediaClassName}` : ""}`}
|
||||||
>
|
>
|
||||||
{mediaSlot ? (
|
{mediaSlot ? (
|
||||||
mediaSlot
|
mediaSlot
|
||||||
) : image.endsWith(".mp4") ? (
|
) : isVideo ? (
|
||||||
<video
|
<video
|
||||||
className="evnd__video"
|
className="evnd__video"
|
||||||
autoPlay
|
autoPlay
|
||||||
@@ -1405,6 +1459,7 @@ export default function EVSection({
|
|||||||
features={features}
|
features={features}
|
||||||
heading={cardsHeading}
|
heading={cardsHeading}
|
||||||
theme={cardsTheme}
|
theme={cardsTheme}
|
||||||
|
sysLabel={sysLabel}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
<div className="evnd__features">
|
<div className="evnd__features">
|
||||||
@@ -1438,15 +1493,19 @@ export default function EVSection({
|
|||||||
all breakpoints. In dashboard mode it summarises the same workflow
|
all breakpoints. In dashboard mode it summarises the same workflow
|
||||||
metrics; the homepage section uses its own stat set. */}
|
metrics; the homepage section uses its own stat set. */}
|
||||||
<div className="evnd__bar">
|
<div className="evnd__bar">
|
||||||
{(useDashboard ? metrics! : stats).map((s) => (
|
{(useDashboard ? (barStats ?? metrics!) : stats).map((s) => (
|
||||||
<div className="evnd__bar-item" key={s.label}>
|
<div className="evnd__bar-item" key={s.label}>
|
||||||
<span className="evnd__bar-label">{s.label}</span>
|
<span className="evnd__bar-label">{s.label}</span>
|
||||||
<CountUp
|
{s.text ? (
|
||||||
value={s.value}
|
<b className="evnd__bar-val evnd__bar-val--text">{s.text}</b>
|
||||||
decimals={s.decimals}
|
) : (
|
||||||
suffix={s.suffix}
|
<CountUp
|
||||||
className="evnd__bar-val"
|
value={s.value}
|
||||||
/>
|
decimals={s.decimals}
|
||||||
|
suffix={s.suffix}
|
||||||
|
className="evnd__bar-val"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
162
src/components/sections/EmpowermentCTA.tsx
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
|
||||||
|
export default function EmpowermentCTA() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.emp-cta-wrap {
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
margin: clamp(24px, 4vw, 40px) auto clamp(40px, 5vw, 64px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 28px;
|
||||||
|
min-height: clamp(480px, 48vw, 680px);
|
||||||
|
background: #09090b;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(180deg, rgba(9, 9, 11, 0.18) 0%, rgba(9, 9, 11, 0.52) 48%, rgba(9, 9, 11, 0.96) 100%),
|
||||||
|
linear-gradient(90deg, rgba(9, 9, 11, 0.88) 0%, rgba(9, 9, 11, 0.35) 58%, rgba(9, 9, 11, 0.12) 100%),
|
||||||
|
url("/images/bg-header-women.webp");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
max-width: 1380px;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: clamp(46px, 7vw, 88px) clamp(24px, 5vw, 64px);
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-recap {
|
||||||
|
display: flex;
|
||||||
|
gap: clamp(28px, 4vw, 52px);
|
||||||
|
margin-bottom: clamp(28px, 4vw, 44px);
|
||||||
|
padding-bottom: clamp(24px, 3vw, 36px);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-recap-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-recap-num {
|
||||||
|
font-size: clamp(22px, 2.4vw, 32px);
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
color: transparent;
|
||||||
|
-webkit-text-stroke: 1.3px rgba(255, 255, 255, 0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-recap-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.6px;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-kicker {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: 2.4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #ff5470;
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-title {
|
||||||
|
max-width: 980px;
|
||||||
|
color: #ffffff !important;
|
||||||
|
font-size: clamp(44px, 7vw, 108px) !important;
|
||||||
|
font-weight: 900 !important;
|
||||||
|
line-height: 0.95 !important;
|
||||||
|
letter-spacing: -0.06em !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
margin: 0 0 clamp(24px, 3vw, 34px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-title em {
|
||||||
|
color: #ff5470;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-bottom {
|
||||||
|
display: block;
|
||||||
|
max-width: 1120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.emp-cta-sub {
|
||||||
|
max-width: 620px;
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.68;
|
||||||
|
color: rgba(255, 255, 255, 0.68);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.emp-cta-wrap { width: calc(100% - 24px); }
|
||||||
|
.emp-cta { border-radius: 22px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 760px) {
|
||||||
|
.emp-cta { min-height: 600px; }
|
||||||
|
.emp-cta::before { background-position: 76% center; }
|
||||||
|
.emp-cta-title {
|
||||||
|
font-size: clamp(36px, 11vw, 44px) !important;
|
||||||
|
letter-spacing: -0.05em !important;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.emp-cta-recap { flex-wrap: wrap; row-gap: 14px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div className="emp-cta-wrap">
|
||||||
|
<section className="emp-cta" aria-labelledby="empowerment-final-cta">
|
||||||
|
<div className="emp-cta-inner">
|
||||||
|
<div className="emp-cta-recap">
|
||||||
|
<div className="emp-cta-recap-item">
|
||||||
|
<span className="emp-cta-recap-num">500+</span>
|
||||||
|
<span className="emp-cta-recap-label">Women Founders</span>
|
||||||
|
</div>
|
||||||
|
<div className="emp-cta-recap-item">
|
||||||
|
<span className="emp-cta-recap-num">100+</span>
|
||||||
|
<span className="emp-cta-recap-label">Enterprises Launched</span>
|
||||||
|
</div>
|
||||||
|
<div className="emp-cta-recap-item">
|
||||||
|
<span className="emp-cta-recap-num">25+</span>
|
||||||
|
<span className="emp-cta-recap-label">Industry Partners</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="emp-cta-kicker">/ Build the next mile /</div>
|
||||||
|
<h2 className="emp-cta-title" id="empowerment-final-cta">
|
||||||
|
Build what’s <em>next.</em>
|
||||||
|
</h2>
|
||||||
|
<div className="emp-cta-bottom">
|
||||||
|
<p className="emp-cta-sub">
|
||||||
|
Join Doormile Wings in building the entrepreneurial ecosystem where women launch, grow, and lead enterprises that create lasting value across logistics.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
183
src/components/sections/EmpowermentHero.tsx
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { ScrollReveal, ShimmerText } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
export default function EmpowermentHero() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.eh-outer {
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-section {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 25px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
height: 800px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: #07080c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-bg {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background-image: url("/images/women-hero-slider-2.png");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: 30% 72%;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-scrim {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background:
|
||||||
|
linear-gradient(180deg, rgba(7, 8, 12, 0.72) 0%, rgba(7, 8, 12, 0.5) 40%, rgba(7, 8, 12, 0.86) 100%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 2;
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1000px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 15px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.6px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #ff5470;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-title {
|
||||||
|
width: 100%;
|
||||||
|
font-size: clamp(30px, 5.5vw, 80px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.08 !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-title .eh-soft {
|
||||||
|
color: rgba(255, 255, 255, 0.72);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-desc {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 680px;
|
||||||
|
margin: 23px auto 0;
|
||||||
|
font-size: clamp(16px, 1.35vw, 23px);
|
||||||
|
line-height: 1.65;
|
||||||
|
color: rgba(255, 255, 255, 0.78);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-btns {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 14px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-btn-primary,
|
||||||
|
.eh-btn-outline {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 15px 28px;
|
||||||
|
border-radius: 100px;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 14px;
|
||||||
|
text-decoration: none;
|
||||||
|
transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.35s ease, background-color 0.3s ease, border-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-btn-primary {
|
||||||
|
background: #c01227;
|
||||||
|
color: #ffffff !important;
|
||||||
|
border: 1.5px solid #c01227;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-btn-primary:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 14px 30px rgba(192, 18, 39, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-btn-outline {
|
||||||
|
background: rgba(255, 255, 255, 0.04);
|
||||||
|
color: #ffffff !important;
|
||||||
|
border: 1.5px solid rgba(255, 255, 255, 0.36);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eh-btn-outline:hover {
|
||||||
|
border-color: rgba(255, 255, 255, 0.82);
|
||||||
|
background: rgba(255, 255, 255, 0.09);
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1020px) {
|
||||||
|
.eh-outer { padding: 10px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 840px) {
|
||||||
|
.eh-section { height: 600px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 660px) {
|
||||||
|
.eh-title {
|
||||||
|
font-size: clamp(20px, 7vw, 32px) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.eh-section { height: 520px; }
|
||||||
|
.eh-btn-primary, .eh-btn-outline { width: 100%; justify-content: center; padding: 14px 22px; }
|
||||||
|
.eh-btns { width: 100%; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="eh-outer">
|
||||||
|
<section className="eh-section" id="wings-empowerment-hero">
|
||||||
|
<div className="eh-bg" aria-hidden="true" />
|
||||||
|
<div className="eh-scrim" aria-hidden="true" />
|
||||||
|
<ScrollReveal delay={0.05} duration={0.85} yOffset={30} className="eh-inner">
|
||||||
|
{/* <div className="eh-eyebrow">Build the Next Mile</div> */}
|
||||||
|
<h1 className="eh-title">
|
||||||
|
Building <span className="eh-soft">Women</span><br />
|
||||||
|
<ShimmerText>Entrepreneurs.</ShimmerText>
|
||||||
|
</h1>
|
||||||
|
<p className="eh-desc">
|
||||||
|
Empowering ambitious women with the network, strategic partnerships, and opportunities to build, grow, and lead successful enterprises that shape the future of logistics.
|
||||||
|
</p>
|
||||||
|
{/* <div className="eh-btns">
|
||||||
|
<Link href="/contact" className="eh-btn-primary">Join Doormile Wings</Link>
|
||||||
|
<Link href="/contact" className="eh-btn-outline">Become a Partner</Link>
|
||||||
|
</div> */}
|
||||||
|
</ScrollReveal>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
259
src/components/sections/EmpowermentHowItWorks.tsx
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
const STEPS = [
|
||||||
|
{ step: "01", title: "Apply", desc: "Submit your venture idea and tell us about your entrepreneurial vision." },
|
||||||
|
{ step: "02", title: "Discovery", desc: "Identify the right market opportunity and validate your business model." },
|
||||||
|
{ step: "03", title: "Build", desc: "Develop your enterprise with ecosystem support, mentorship, and partnerships." },
|
||||||
|
{ step: "04", title: "Launch", desc: "Go to market with strategic backing, industry connections, and operational infrastructure." },
|
||||||
|
{ step: "05", title: "Scale", desc: "Grow your team, expand operations, and lead your enterprise to sustainable impact." },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function EmpowermentHowItWorks() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.ehw-section {
|
||||||
|
background: #ffffff;
|
||||||
|
padding: var(--space-section-lg, 80px) 0 calc(var(--space-section-lg, 80px) + 32px) 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-inner {
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: clamp(126px, calc(7vw + 60px), 172px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-head-reveal {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
margin: 18px 0 clamp(24px, 3.5vw, 40px) 0;
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(17, 17, 17, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-title {
|
||||||
|
max-width: 820px;
|
||||||
|
font-size: clamp(32px, 4vw, 54px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.08 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ----------------------------------------------------------------
|
||||||
|
Premium editorial process row: five milestones in a single
|
||||||
|
sequence, separated by thin hairline dividers (no cards, no
|
||||||
|
shadows, no boxed containers). One flex-wrap sequence with
|
||||||
|
responsive flex-basis handles the composition at every size —
|
||||||
|
5-across on desktop, 3 + 2 (centered) on tablet, a single
|
||||||
|
vertical column on mobile — from one shared markup.
|
||||||
|
---------------------------------------------------------------- */
|
||||||
|
.ehw-sequence {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
--ehw-gap: clamp(24px, 2.6vw, 48px);
|
||||||
|
column-gap: var(--ehw-gap);
|
||||||
|
row-gap: clamp(48px, 6vw, 84px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step {
|
||||||
|
position: relative;
|
||||||
|
flex: 0 1 calc((100% - 4 * var(--ehw-gap)) / 5);
|
||||||
|
min-width: 168px;
|
||||||
|
padding-left: clamp(20px, 2vw, 32px);
|
||||||
|
border-left: 1px solid rgba(17, 17, 17, 0.14);
|
||||||
|
transition: border-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step:first-child {
|
||||||
|
border-left: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step:hover {
|
||||||
|
border-left-color: rgba(200, 16, 46, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step-num {
|
||||||
|
font-size: clamp(48px, 4.6vw, 78px);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 0.84;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
color: transparent;
|
||||||
|
-webkit-text-stroke: 1.5px rgba(17, 17, 17, 0.24);
|
||||||
|
margin: 0 0 18px 0;
|
||||||
|
transition: -webkit-text-stroke-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step.is-accent .ehw-step-num {
|
||||||
|
-webkit-text-stroke: 1.5px rgba(200, 16, 46, 0.58);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step-kicker {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 7px;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step-kicker svg {
|
||||||
|
width: 13px;
|
||||||
|
height: 13px;
|
||||||
|
color: rgba(17, 17, 17, 0.36);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step-kicker span {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step.is-accent .ehw-step-kicker svg,
|
||||||
|
.ehw-step.is-accent .ehw-step-kicker span {
|
||||||
|
color: #c8102e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step-title {
|
||||||
|
font-size: clamp(18px, 1.4vw, 22px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.02em !important;
|
||||||
|
line-height: 1.2 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0 0 22px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ehw-step-desc {
|
||||||
|
max-width: 27ch;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: rgba(17, 17, 17, 0.55);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet: 3 + 2, centered — sever the divider at the start of each row. */
|
||||||
|
@media (max-width: 1100px) {
|
||||||
|
.ehw-step {
|
||||||
|
flex-basis: calc((100% - 2 * var(--ehw-gap)) / 3);
|
||||||
|
}
|
||||||
|
.ehw-step:nth-child(4) {
|
||||||
|
border-left: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mobile: single column — dividers rotate from vertical to horizontal. */
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.ehw-sequence {
|
||||||
|
row-gap: 0;
|
||||||
|
}
|
||||||
|
.ehw-step {
|
||||||
|
flex-basis: 100%;
|
||||||
|
padding-left: 0;
|
||||||
|
padding-top: clamp(28px, 8vw, 38px);
|
||||||
|
border-left: none;
|
||||||
|
border-top: 1px solid rgba(17, 17, 17, 0.14);
|
||||||
|
}
|
||||||
|
.ehw-step:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
.ehw-step:nth-child(4) {
|
||||||
|
border-top: 1px solid rgba(17, 17, 17, 0.14);
|
||||||
|
}
|
||||||
|
.ehw-step:hover {
|
||||||
|
border-left-color: transparent;
|
||||||
|
}
|
||||||
|
.ehw-step-num {
|
||||||
|
font-size: clamp(44px, 13vw, 56px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Mobile title-to-description gap fix ----
|
||||||
|
.ehw-step-title is an <h3>, so it's matched by a global,
|
||||||
|
unscoped site.css rule (.logico-front-end h3:not(...) { margin:
|
||||||
|
0 0 30px }) that outranks this component's own single-class
|
||||||
|
".ehw-step-title { margin: 0 0 22px }" by specificity — bumping
|
||||||
|
the real margin-bottom to 30px everywhere, not just here. There
|
||||||
|
is no min-height, space-between, or fixed row height anywhere in
|
||||||
|
this component; the gap is purely that extra 8px of margin
|
||||||
|
reading as oversized once every other element around it (kicker,
|
||||||
|
description line-height) is tightly spaced. Overridden with
|
||||||
|
!important, matching how every other property on this selector
|
||||||
|
already has to defeat the same global rule. Scoped to mobile
|
||||||
|
only, per spec — desktop/tablet are left exactly as they were
|
||||||
|
(still resolving to the global 30px; out of scope here). */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.ehw-step-title {
|
||||||
|
margin-bottom: 16px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.ehw-section { padding-top: 48px; padding-bottom: 68px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="ehw-section" id="wings-empowerment-how-it-works">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="ehw-inner">
|
||||||
|
<div className="ehw-head">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={20} className="ehw-head-reveal">
|
||||||
|
<div className="ehw-eyebrow">/ The Founder Journey /</div>
|
||||||
|
<hr className="ehw-divider" />
|
||||||
|
</ScrollReveal>
|
||||||
|
<ScrollReveal delay={0.1} duration={0.85} yOffset={24}>
|
||||||
|
<h2 className="ehw-title">A clear path, from idea to enterprise</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<StaggerChildren className="ehw-sequence" stagger={0.12} duration={0.7} yOffset={30} triggerOnce>
|
||||||
|
{STEPS.map((s, i) => (
|
||||||
|
<div className={`ehw-step${i === 2 ? " is-accent" : ""}`} key={s.step}>
|
||||||
|
<div className="ehw-step-num">{s.step}</div>
|
||||||
|
<div className="ehw-step-kicker" aria-hidden="true">
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M7 17 17 7M9 7h8v8" />
|
||||||
|
</svg>
|
||||||
|
<span>Step</span>
|
||||||
|
</div>
|
||||||
|
<h3 className="ehw-step-title">{s.title}</h3>
|
||||||
|
<p className="ehw-step-desc">{s.desc}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</StaggerChildren>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
192
src/components/sections/EmpowermentImpact.tsx
Normal file
@@ -0,0 +1,192 @@
|
|||||||
|
import { ScrollReveal, CountUp } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
const SECONDARY_STATS = [
|
||||||
|
{ end: 100, suffix: "+", label: "Enterprises Launched", isCount: true },
|
||||||
|
{ end: 25, suffix: "+", label: "Industry Partners", isCount: true },
|
||||||
|
{ end: 15, suffix: "+", label: "Cities Active", isCount: true },
|
||||||
|
{ value: "Millions", label: "In Economic Value Created", isCount: false },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function EmpowermentImpact() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.eim-section {
|
||||||
|
position: relative;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
margin: clamp(24px, 4vw, 40px) auto;
|
||||||
|
border-radius: 28px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #09090b;
|
||||||
|
padding: clamp(64px, 8vw, 100px) clamp(24px, 5vw, 48px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #c8102e;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-title {
|
||||||
|
font-size: clamp(28px, 3.4vw, 46px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0 0 clamp(32px, 4vw, 48px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-divider {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
margin: 0 0 clamp(40px, 5vw, 56px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-hero-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: clamp(20px, 3vw, 36px);
|
||||||
|
margin-bottom: clamp(48px, 6vw, 72px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-hero-num {
|
||||||
|
font-size: clamp(90px, 13vw, 190px);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 0.85;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
color: transparent;
|
||||||
|
-webkit-text-stroke: 2px #c8102e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-hero-label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
max-width: 90px;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding-bottom: clamp(12px, 1.5vw, 24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-divider-2 {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
margin: 0 0 clamp(40px, 5vw, 56px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-secondary-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-secondary-item {
|
||||||
|
padding-right: 24px;
|
||||||
|
border-left: 1px solid rgba(255, 255, 255, 0.12);
|
||||||
|
padding-left: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-secondary-item:first-child {
|
||||||
|
border-left: none;
|
||||||
|
padding-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-secondary-num {
|
||||||
|
display: block;
|
||||||
|
font-size: clamp(32px, 3vw, 44px);
|
||||||
|
font-weight: 800;
|
||||||
|
color: #ff5470;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-secondary-label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eim-copy {
|
||||||
|
max-width: 720px;
|
||||||
|
margin-top: clamp(48px, 6vw, 72px);
|
||||||
|
font-size: 15.5px;
|
||||||
|
line-height: 1.7;
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.eim-section { width: calc(100% - 24px); border-radius: 22px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.eim-hero-row { align-items: flex-start; flex-direction: column; gap: 12px; }
|
||||||
|
.eim-hero-label { padding-bottom: 0; max-width: none; }
|
||||||
|
.eim-secondary-row { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 28px; }
|
||||||
|
.eim-secondary-item:nth-child(3) { border-left: none; padding-left: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 460px) {
|
||||||
|
.eim-section { padding-top: 48px; padding-bottom: 48px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="eim-section" id="wings-empowerment-impact">
|
||||||
|
<div className="eim-inner dm-section-container">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={20}>
|
||||||
|
<div className="eim-eyebrow">/ Our Impact /</div>
|
||||||
|
<h2 className="eim-title">Growth we can measure</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<hr className="eim-divider" />
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.08} duration={0.8} yOffset={20} className="eim-hero-row">
|
||||||
|
<span className="eim-hero-num">
|
||||||
|
<CountUp end={500} suffix="+" duration={2.2} triggerOnce />
|
||||||
|
</span>
|
||||||
|
<span className="eim-hero-label">Women-Led Enterprises In The Ecosystem</span>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<hr className="eim-divider-2" />
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.1} duration={0.8} yOffset={20}>
|
||||||
|
<div className="eim-secondary-row">
|
||||||
|
{SECONDARY_STATS.map((s) => (
|
||||||
|
<div className="eim-secondary-item" key={s.label}>
|
||||||
|
<span className="eim-secondary-num">
|
||||||
|
{s.isCount ? <CountUp end={s.end as number} suffix={s.suffix} duration={2} triggerOnce /> : s.value}
|
||||||
|
</span>
|
||||||
|
<span className="eim-secondary-label">{s.label}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.12} duration={0.75} yOffset={20} className="eim-copy">
|
||||||
|
<p>
|
||||||
|
Every number on this page represents a woman who walked into a room with an idea and
|
||||||
|
walked out with a business. An enterprise launched, a partnership secured, a market entered — each is a
|
||||||
|
marker of real economic impact, not a statistic collected for its own sake.
|
||||||
|
</p>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
290
src/components/sections/EmpowermentPartners.tsx
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { ScrollReveal } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
const PARTNER_TYPES = [
|
||||||
|
{
|
||||||
|
title: "Industry Partners",
|
||||||
|
desc: "Strategic alliances with logistics operators, supply chain leaders, and distribution networks that open doors for women-led enterprises.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Technology Partners",
|
||||||
|
desc: "Platforms, tools, and infrastructure that give founders the operational edge to compete and scale efficiently.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Community Networks",
|
||||||
|
desc: "Founder communities, women's business networks, and ecosystem builders who extend reach and amplify impact.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Investment Partners",
|
||||||
|
desc: "Funding access, grant programs, angel networks, and capital partners aligned with women-led enterprise growth.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function EmpowermentPartners() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.epn-section {
|
||||||
|
position: relative;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
margin: clamp(24px, 4vw, 40px) auto;
|
||||||
|
border-radius: 28px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #09090b;
|
||||||
|
padding: clamp(68px, 8vw, 112px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-section::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background:
|
||||||
|
linear-gradient(120deg, rgba(9, 9, 11, 0.98) 0%, rgba(9, 9, 11, 0.88) 54%, rgba(192, 18, 39, 0.18) 100%),
|
||||||
|
url("/images/wings-heroslider3.png");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center 70%;
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: clamp(50px, 7vw, 84px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-head-reveal {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
margin: 18px 0 clamp(24px, 3.5vw, 40px) 0;
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(255, 255, 255, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(255, 255, 255, 0.52);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-title {
|
||||||
|
max-width: 930px;
|
||||||
|
font-size: clamp(42px, 6.3vw, 90px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.055em !important;
|
||||||
|
line-height: 0.98 !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-title em {
|
||||||
|
color: #ff5470;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-body {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(280px, 0.46fr) minmax(0, 1fr);
|
||||||
|
gap: clamp(32px, 5vw, 72px);
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-manifesto {
|
||||||
|
border-left: 4px solid #c8102e;
|
||||||
|
padding-left: clamp(22px, 3vw, 36px);
|
||||||
|
align-self: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-intro {
|
||||||
|
font-size: clamp(22px, 2.5vw, 36px);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.12;
|
||||||
|
letter-spacing: -0.035em;
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0 0 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-note {
|
||||||
|
font-size: 15.5px;
|
||||||
|
line-height: 1.68;
|
||||||
|
color: rgba(255, 255, 255, 0.62);
|
||||||
|
margin: 0;
|
||||||
|
max-width: 38ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-card {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 70px minmax(0, 1fr) 32px;
|
||||||
|
align-items: center;
|
||||||
|
gap: clamp(18px, 3vw, 40px);
|
||||||
|
padding: clamp(22px, 2.6vw, 32px) clamp(4px, 1vw, 16px);
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.16);
|
||||||
|
border-left: 2px solid transparent;
|
||||||
|
transition: border-color 0.3s ease, padding-left 0.3s ease, background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-card:hover {
|
||||||
|
border-left-color: #c8102e;
|
||||||
|
background: rgba(200, 16, 46, 0.06);
|
||||||
|
padding-left: clamp(20px, 2.5vw, 32px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-card-index {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 900;
|
||||||
|
color: rgba(255, 255, 255, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-card-title {
|
||||||
|
font-size: clamp(22px, 2.2vw, 30px) !important;
|
||||||
|
font-weight: 900 !important;
|
||||||
|
line-height: 1.1 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-card-desc {
|
||||||
|
max-width: 46ch;
|
||||||
|
font-size: 14.5px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: rgba(255, 255, 255, 0.58);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-card-arrow {
|
||||||
|
display: flex;
|
||||||
|
color: rgba(255, 255, 255, 0.3);
|
||||||
|
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1), color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-card:hover .epn-card-arrow {
|
||||||
|
transform: translateX(4px);
|
||||||
|
color: #ff5470;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-cta {
|
||||||
|
margin-top: clamp(42px, 5vw, 64px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-cta-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
padding: 16px 36px;
|
||||||
|
border-radius: 100px;
|
||||||
|
background: #c01227;
|
||||||
|
color: #ffffff !important;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 15px;
|
||||||
|
text-decoration: none !important;
|
||||||
|
transition: transform 0.35s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.35s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epn-cta-btn:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 14px 30px rgba(192, 18, 39, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.epn-section { width: calc(100% - 24px); border-radius: 22px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.epn-head,
|
||||||
|
.epn-body { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
.epn-title {
|
||||||
|
font-size: clamp(34px, 10vw, 40px) !important;
|
||||||
|
letter-spacing: -0.045em !important;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.epn-card {
|
||||||
|
grid-template-columns: 44px minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
.epn-card-arrow { display: none; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 460px) {
|
||||||
|
.epn-section { padding-top: 54px; padding-bottom: 54px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="epn-section" id="wings-empowerment-partners">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="epn-inner">
|
||||||
|
<div className="epn-head">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={20} className="epn-head-reveal">
|
||||||
|
<div className="epn-eyebrow">/ Collaboration /</div>
|
||||||
|
<hr className="epn-divider" />
|
||||||
|
</ScrollReveal>
|
||||||
|
<ScrollReveal delay={0.1} duration={0.85} yOffset={24}>
|
||||||
|
<h2 className="epn-title">Partnership is how <em>ecosystems grow.</em></h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="epn-body">
|
||||||
|
<ScrollReveal delay={0.08} duration={0.8} yOffset={22} className="epn-manifesto">
|
||||||
|
<p className="epn-intro">The strongest ecosystems are built by many hands.</p>
|
||||||
|
<p className="epn-note">
|
||||||
|
Wings partners bring industry access, strategic infrastructure, capital, and networks. Together, we build the foundation women-led enterprises need to thrive.
|
||||||
|
</p>
|
||||||
|
{/* <div className="epn-cta">
|
||||||
|
<Link href="/contact" className="epn-cta-btn">Become a Partner</Link>
|
||||||
|
</div> */}
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.12} duration={0.8} yOffset={22}>
|
||||||
|
<div className="epn-grid">
|
||||||
|
{PARTNER_TYPES.map((p, i) => (
|
||||||
|
<article className="epn-card" key={p.title}>
|
||||||
|
<span className="epn-card-index">{String(i + 1).padStart(2, "0")}</span>
|
||||||
|
<div>
|
||||||
|
<h3 className="epn-card-title">{p.title}</h3>
|
||||||
|
<p className="epn-card-desc">{p.desc}</p>
|
||||||
|
</div>
|
||||||
|
<span className="epn-card-arrow" aria-hidden="true">
|
||||||
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M5 12h14M13 6l6 6-6 6" />
|
||||||
|
</svg>
|
||||||
|
</span>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
405
src/components/sections/EmpowermentPrograms.tsx
Normal file
@@ -0,0 +1,405 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
import { ScrollReveal } from "@/animations/Reveal";
|
||||||
|
import IndustryWorldMap from "./IndustryWorldMap";
|
||||||
|
|
||||||
|
const PROGRAMS = [
|
||||||
|
{
|
||||||
|
title: "Venture Launchpad",
|
||||||
|
desc: "Business model validation, market entry strategy, entity formation, and go-to-market execution for women-led logistics enterprises.",
|
||||||
|
type: "Start building",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Strategic Partnerships",
|
||||||
|
desc: "Direct access to supply chain networks, B2B relationships, industry alliances, and commercial agreements that accelerate growth.",
|
||||||
|
type: "Connect and grow",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Founder Mentorship",
|
||||||
|
desc: "One-on-one guidance from established entrepreneurs, operators, and investors who understand what it takes to scale.",
|
||||||
|
type: "Learn from builders",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Growth & Scale",
|
||||||
|
desc: "Operational expansion frameworks, team building, funding readiness, and sustainable unit economics for ventures ready to grow.",
|
||||||
|
type: "Scale with confidence",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Industry Immersion",
|
||||||
|
desc: "Real exposure to logistics operations, supply chain infrastructure, and distribution networks before founders commit to a vertical.",
|
||||||
|
type: "See the opportunity",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function EmpowermentPrograms() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.epr-section {
|
||||||
|
background: #ffffff;
|
||||||
|
padding: clamp(78px, 9vw, 132px) 0;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-inner {
|
||||||
|
max-width: 1380px;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: clamp(54px, 7vw, 86px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-head-reveal {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
margin: 18px 0 clamp(24px, 3.5vw, 40px) 0;
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(17, 17, 17, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-title {
|
||||||
|
max-width: 950px;
|
||||||
|
font-size: clamp(42px, 6.2vw, 88px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.055em !important;
|
||||||
|
line-height: 0.98 !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-stage {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(300px, 0.38fr) minmax(0, 0.62fr);
|
||||||
|
gap: clamp(32px, 5vw, 78px);
|
||||||
|
align-items: start;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-stage > * {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-aside {
|
||||||
|
position: sticky;
|
||||||
|
top: 110px;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: clamp(480px, 44vw, 660px);
|
||||||
|
padding: clamp(18px, 3vw, 34px) 0 clamp(24px, 3.5vw, 42px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
color: #111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-aside-map {
|
||||||
|
position: absolute;
|
||||||
|
inset: 8% -22% 8% -28%;
|
||||||
|
z-index: 0;
|
||||||
|
opacity: 0.22;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-aside-map canvas {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-aside-content {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: grid;
|
||||||
|
gap: clamp(18px, 2.2vw, 28px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-aside-num {
|
||||||
|
position: relative;
|
||||||
|
font-size: clamp(164px, 17vw, 300px);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 0.78;
|
||||||
|
letter-spacing: -0.06em;
|
||||||
|
color: transparent;
|
||||||
|
-webkit-text-stroke: 2px rgba(200, 16, 46, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-aside-copy {
|
||||||
|
position: relative;
|
||||||
|
max-width: 12ch;
|
||||||
|
font-size: clamp(30px, 3.6vw, 56px);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 0.98;
|
||||||
|
letter-spacing: -0.055em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #111111;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-aside-copy em {
|
||||||
|
font-style: normal;
|
||||||
|
color: #c8102e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-aside-desc {
|
||||||
|
position: relative;
|
||||||
|
max-width: 34ch;
|
||||||
|
font-size: 15.5px;
|
||||||
|
line-height: 1.65;
|
||||||
|
color: rgba(17, 17, 17, 0.58);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-list {
|
||||||
|
display: grid;
|
||||||
|
gap: clamp(28px, 4vw, 52px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 110px minmax(0, 1fr);
|
||||||
|
gap: clamp(22px, 4vw, 58px);
|
||||||
|
padding-bottom: clamp(28px, 4vw, 52px);
|
||||||
|
border-bottom: 1px solid rgba(17, 17, 17, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row:nth-child(even) {
|
||||||
|
margin-left: clamp(0px, 7vw, 96px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-index {
|
||||||
|
font-size: clamp(38px, 4.2vw, 62px);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1;
|
||||||
|
letter-spacing: -0.05em;
|
||||||
|
color: #c8102e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-featured {
|
||||||
|
padding: clamp(28px, 4vw, 44px) clamp(24px, 3vw, 40px);
|
||||||
|
background: #09090b;
|
||||||
|
border-radius: 22px;
|
||||||
|
border-bottom: none;
|
||||||
|
margin-bottom: clamp(28px, 4vw, 52px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-featured .epr-row-index {
|
||||||
|
color: rgba(255, 255, 255, 0.22);
|
||||||
|
-webkit-text-stroke: 1.5px rgba(255, 255, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-featured .epr-row-title {
|
||||||
|
font-size: clamp(32px, 4vw, 64px) !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-featured .epr-row-desc {
|
||||||
|
color: rgba(255, 255, 255, 0.62);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-flag {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1.3px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #ffffff;
|
||||||
|
background: #c8102e;
|
||||||
|
padding: 6px 14px;
|
||||||
|
border-radius: 100px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-type {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1.7px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.42);
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-featured .epr-row-type {
|
||||||
|
color: rgba(255, 255, 255, 0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-title {
|
||||||
|
font-size: clamp(28px, 3.4vw, 52px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.045em !important;
|
||||||
|
line-height: 1.02 !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0 0 14px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-row-desc {
|
||||||
|
max-width: 660px;
|
||||||
|
font-size: 15.5px;
|
||||||
|
line-height: 1.65;
|
||||||
|
color: rgba(17, 17, 17, 0.58);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-cta {
|
||||||
|
margin-top: clamp(48px, 6vw, 72px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-cta-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 10px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 800;
|
||||||
|
color: #111111 !important;
|
||||||
|
text-decoration: none !important;
|
||||||
|
border-bottom: 1.5px solid rgba(17, 17, 17, 0.2);
|
||||||
|
padding-bottom: 5px;
|
||||||
|
transition: border-color 0.3s ease, color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.epr-cta-link:hover {
|
||||||
|
color: #c8102e !important;
|
||||||
|
border-color: #c8102e;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Laptop: sticky stays enabled, just a narrower gutter. */
|
||||||
|
@media (max-width: 1180px) {
|
||||||
|
.epr-stage { grid-template-columns: minmax(220px, 0.34fr) minmax(0, 0.66fr); gap: clamp(28px, 4vw, 46px); }
|
||||||
|
.epr-aside { min-height: 420px; }
|
||||||
|
.epr-row:nth-child(even) { margin-left: 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet: sticky disabled — the aside scrolls in normal flow instead
|
||||||
|
of pinning, since there's no longer enough vertical runway next to
|
||||||
|
the (now narrower, shorter) program list for pinning to read well. */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
.epr-aside {
|
||||||
|
position: relative;
|
||||||
|
top: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.epr-stage { grid-template-columns: 1fr; }
|
||||||
|
.epr-aside {
|
||||||
|
position: relative;
|
||||||
|
top: auto;
|
||||||
|
min-height: 300px;
|
||||||
|
padding-top: 4px;
|
||||||
|
padding-bottom: clamp(24px, 7vw, 38px);
|
||||||
|
}
|
||||||
|
.epr-aside-map {
|
||||||
|
inset: -8% -18% 0 -20%;
|
||||||
|
opacity: 0.18;
|
||||||
|
}
|
||||||
|
.epr-aside-content {
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
.epr-aside-num {
|
||||||
|
font-size: clamp(128px, 34vw, 180px);
|
||||||
|
}
|
||||||
|
.epr-aside-copy {
|
||||||
|
max-width: 11ch;
|
||||||
|
font-size: clamp(34px, 10vw, 48px);
|
||||||
|
}
|
||||||
|
.epr-aside-desc {
|
||||||
|
max-width: 32ch;
|
||||||
|
font-size: 14.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.epr-section { padding-top: 54px; padding-bottom: 54px; }
|
||||||
|
.epr-title {
|
||||||
|
font-size: clamp(34px, 10vw, 40px) !important;
|
||||||
|
letter-spacing: -0.045em !important;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.epr-row-title {
|
||||||
|
font-size: clamp(27px, 8.5vw, 36px) !important;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.epr-row { grid-template-columns: 1fr; gap: 12px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="epr-section" id="wings-empowerment-programs">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="epr-inner">
|
||||||
|
<div className="epr-head">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={20} className="epr-head-reveal">
|
||||||
|
<div className="epr-eyebrow">/ Entrepreneurship Pillars /</div>
|
||||||
|
<hr className="epr-divider" />
|
||||||
|
</ScrollReveal>
|
||||||
|
<ScrollReveal delay={0.1} duration={0.85} yOffset={24}>
|
||||||
|
<h2 className="epr-title">Every pillar is built around ownership.</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="epr-stage">
|
||||||
|
<aside className="epr-aside">
|
||||||
|
<div className="epr-aside-map" aria-hidden="true">
|
||||||
|
<IndustryWorldMap />
|
||||||
|
</div>
|
||||||
|
<ScrollReveal delay={0.05} duration={0.8} yOffset={24} className="epr-aside-content">
|
||||||
|
<div className="epr-aside-num">05</div>
|
||||||
|
<p className="epr-aside-copy">Building tomorrow’s <em>women entrepreneurs.</em></p>
|
||||||
|
<p className="epr-aside-desc">
|
||||||
|
Every enterprise starts with one decision. Our pillars move women from idea to ownership through mentorship, real industry partnerships, and hands-on venture building across the logistics ecosystem.
|
||||||
|
</p>
|
||||||
|
</ScrollReveal>
|
||||||
|
</aside>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<ScrollReveal delay={0.1} duration={0.8} yOffset={24}>
|
||||||
|
<div className="epr-list">
|
||||||
|
{PROGRAMS.map((p, i) => (
|
||||||
|
<article className={`epr-row${i === 0 ? " epr-row-featured" : ""}`} key={p.title}>
|
||||||
|
<div className="epr-row-index">{String(i + 1).padStart(2, "0")}</div>
|
||||||
|
<div>
|
||||||
|
{i === 0 && <span className="epr-row-flag">Where most founders begin</span>}
|
||||||
|
<span className="epr-row-type">{p.type}</span>
|
||||||
|
<h3 className="epr-row-title">{p.title}</h3>
|
||||||
|
<p className="epr-row-desc">{p.desc}</p>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<div className="epr-cta">
|
||||||
|
<Link href="/contact" className="epr-cta-link">
|
||||||
|
Start your venture
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M5 12h14M13 6l6 6-6 6" />
|
||||||
|
</svg>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
335
src/components/sections/EmpowermentStories.tsx
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
import Image from "next/image";
|
||||||
|
import { ScrollReveal } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default function EmpowermentStories() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.est-section {
|
||||||
|
position: relative;
|
||||||
|
background: #ffffff;
|
||||||
|
padding-top: clamp(24px, 3vw, 48px);
|
||||||
|
padding-bottom: clamp(78px, 9vw, 132px);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-inner {
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: clamp(54px, 7vw, 86px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-head-reveal {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
margin: 18px 0 clamp(24px, 3.5vw, 40px) 0;
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(17, 17, 17, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.42);
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-title {
|
||||||
|
max-width: 940px;
|
||||||
|
font-size: clamp(42px, 6.3vw, 90px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.055em !important;
|
||||||
|
line-height: 0.98 !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-feature {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 0.94fr) minmax(340px, 0.64fr);
|
||||||
|
gap: clamp(28px, 4vw, 62px);
|
||||||
|
align-items: stretch;
|
||||||
|
margin-bottom: clamp(44px, 6vw, 76px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-photo {
|
||||||
|
position: relative;
|
||||||
|
min-height: clamp(460px, 48vw, 680px);
|
||||||
|
border-radius: 26px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: #09090b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-photo img {
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-photo::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: linear-gradient(180deg, rgba(9, 9, 11, 0) 34%, rgba(9, 9, 11, 0.72) 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-photo-caption {
|
||||||
|
position: absolute;
|
||||||
|
left: clamp(24px, 3vw, 42px);
|
||||||
|
right: clamp(24px, 3vw, 42px);
|
||||||
|
bottom: clamp(24px, 3vw, 42px);
|
||||||
|
z-index: 1;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-photo-caption span {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1.7px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(255, 255, 255, 0.6);
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-photo-caption strong {
|
||||||
|
display: block;
|
||||||
|
font-size: clamp(24px, 3vw, 42px);
|
||||||
|
font-weight: 900;
|
||||||
|
line-height: 1.02;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-copy {
|
||||||
|
border-radius: 26px;
|
||||||
|
background: #09090b;
|
||||||
|
color: #ffffff;
|
||||||
|
padding: clamp(34px, 5vw, 62px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-copy::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
width: 320px;
|
||||||
|
height: 320px;
|
||||||
|
right: -120px;
|
||||||
|
top: -120px;
|
||||||
|
background: radial-gradient(circle, rgba(200, 16, 46, 0.42), transparent 68%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-quote-mark {
|
||||||
|
position: relative;
|
||||||
|
font-family: Georgia, serif;
|
||||||
|
font-size: 92px;
|
||||||
|
line-height: 0.8;
|
||||||
|
color: #c8102e;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-quote-text {
|
||||||
|
position: relative;
|
||||||
|
font-size: clamp(25px, 3vw, 44px);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.12;
|
||||||
|
letter-spacing: -0.04em;
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0 0 28px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-narrative {
|
||||||
|
position: relative;
|
||||||
|
font-size: 15.5px;
|
||||||
|
line-height: 1.68;
|
||||||
|
color: rgba(255, 255, 255, 0.66);
|
||||||
|
margin: 0;
|
||||||
|
max-width: 46ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-person {
|
||||||
|
position: relative;
|
||||||
|
padding-top: clamp(38px, 5vw, 64px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-person-name {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-person-role {
|
||||||
|
font-size: 13.5px;
|
||||||
|
color: rgba(255, 255, 255, 0.52);
|
||||||
|
margin-top: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-list-label {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1.8px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.42);
|
||||||
|
padding-top: clamp(24px, 3vw, 38px);
|
||||||
|
border-top: 1px solid rgba(17, 17, 17, 0.14);
|
||||||
|
margin-bottom: clamp(20px, 3vw, 30px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-list {
|
||||||
|
display: flex;
|
||||||
|
gap: 18px;
|
||||||
|
overflow-x: auto;
|
||||||
|
scroll-snap-type: x mandatory;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
margin-bottom: clamp(6px, 1vw, 10px);
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-list::-webkit-scrollbar {
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-list::-webkit-scrollbar-thumb {
|
||||||
|
background: rgba(17, 17, 17, 0.18);
|
||||||
|
border-radius: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-row {
|
||||||
|
scroll-snap-align: start;
|
||||||
|
flex: 0 0 clamp(260px, 30vw, 340px);
|
||||||
|
min-height: 220px;
|
||||||
|
padding: clamp(24px, 2.6vw, 32px);
|
||||||
|
border: 1px solid rgba(17, 17, 17, 0.14);
|
||||||
|
border-radius: 20px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
transition: border-color 0.3s ease, transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-row:hover {
|
||||||
|
border-color: rgba(200, 16, 46, 0.4);
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-row-name {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #111111;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-row-role {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1.4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #c8102e;
|
||||||
|
margin-top: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.est-row-line {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: rgba(17, 17, 17, 0.62);
|
||||||
|
margin: 28px 0 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1000px) {
|
||||||
|
.est-head,
|
||||||
|
.est-feature { grid-template-columns: 1fr; }
|
||||||
|
.est-row {
|
||||||
|
min-height: 0;
|
||||||
|
flex-basis: 78vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.est-section { padding-top: 54px; padding-bottom: 54px; }
|
||||||
|
.est-title {
|
||||||
|
font-size: clamp(34px, 10vw, 40px) !important;
|
||||||
|
letter-spacing: -0.045em !important;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.est-quote-text {
|
||||||
|
font-size: clamp(25px, 8vw, 34px);
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
.est-photo,
|
||||||
|
.est-copy { border-radius: 20px; }
|
||||||
|
.est-photo { min-height: 420px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="est-section" id="wings-empowerment-stories">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="est-inner">
|
||||||
|
<div className="est-head">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={20} className="est-head-reveal">
|
||||||
|
<div className="est-eyebrow">/ Women in Motion /</div>
|
||||||
|
<hr className="est-divider" />
|
||||||
|
</ScrollReveal>
|
||||||
|
<ScrollReveal delay={0.1} duration={0.85} yOffset={24}>
|
||||||
|
<h2 className="est-title">Small steps. Lasting careers.</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="est-feature">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.85} yOffset={24} className="est-photo">
|
||||||
|
<Image
|
||||||
|
src="/images/women-hero-slider-3.png"
|
||||||
|
alt="A Wings program participant on the operations floor at a Doormile logistics hub"
|
||||||
|
fill
|
||||||
|
sizes="(max-width: 1000px) 100vw, 58vw"
|
||||||
|
style={{ objectPosition: "80% 22%" }}
|
||||||
|
/>
|
||||||
|
<div className="est-photo-caption">
|
||||||
|
<span>Wings Participant</span>
|
||||||
|
<strong> building a career, one milestone at a time.</strong>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.12} duration={0.85} yOffset={24} className="est-copy">
|
||||||
|
<div>
|
||||||
|
<span className="est-quote-mark" aria-hidden="true">“</span>
|
||||||
|
<p className="est-quote-text">I came in looking for a way forward. I found the confidence to lead.</p>
|
||||||
|
<p className="est-narrative">
|
||||||
|
She started with limited exposure to logistics. Through structured training, mentorship, and hands-on experience, she built real operational skills — and now supports other women stepping into the industry.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{/* <div className="est-person">
|
||||||
|
<div className="est-person-name">Kavya</div>
|
||||||
|
<div className="est-person-role">Logistics Coordinator, Wings Program</div>
|
||||||
|
</div> */}
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
164
src/components/sections/EmpowermentStory.tsx
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
import { ScrollReveal } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
export default function EmpowermentStory() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.esy-section {
|
||||||
|
position: relative;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
margin: 0 auto clamp(24px, 4vw, 40px);
|
||||||
|
border-radius: 28px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
min-height: clamp(500px, 50vw, 690px);
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
background: #09090b;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-bg {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background-image:
|
||||||
|
linear-gradient(100deg, rgba(9, 9, 11, 0.96) 0%, rgba(9, 9, 11, 0.84) 43%, rgba(192, 18, 39, 0.28) 100%),
|
||||||
|
url("/images/wings-heroslider4.png");
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: clamp(60px, 7vw, 98px);
|
||||||
|
padding-bottom: clamp(60px, 7vw, 98px);
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 0.9fr) minmax(280px, 0.52fr);
|
||||||
|
gap: clamp(36px, 6vw, 96px);
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #ff5470;
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-statement {
|
||||||
|
max-width: 920px;
|
||||||
|
font-size: clamp(38px, 5.7vw, 86px);
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: -0.055em;
|
||||||
|
line-height: 0.98;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-statement em {
|
||||||
|
color: #ff5470;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-side {
|
||||||
|
border-left: 1px solid rgba(255, 255, 255, 0.18);
|
||||||
|
padding-left: clamp(24px, 3vw, 42px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-label {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1.8px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-pull {
|
||||||
|
font-size: clamp(22px, 2.3vw, 34px);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1.16;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
color: #ffffff;
|
||||||
|
margin: 0 0 22px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.esy-sub {
|
||||||
|
font-size: 15.5px;
|
||||||
|
line-height: 1.65;
|
||||||
|
color: rgba(255, 255, 255, 0.64);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Desktop-only inner spacing compaction ----
|
||||||
|
.esy-section never set its own padding, so it was silently
|
||||||
|
inheriting 96px top/bottom from the global bare-tag "section"
|
||||||
|
padding rule (6rem 0) in site.css — stacking with .esy-inner's
|
||||||
|
own clamp(60-98px) padding into a huge, unbalanced inset. Cancel
|
||||||
|
that leak here. Also tighten .esy-inner's own padding and widen its
|
||||||
|
max-width (so a fixed padding, not a narrow centered column,
|
||||||
|
controls the left/right inset at every desktop width) — and bring
|
||||||
|
min-height down to match the now-tighter natural content height,
|
||||||
|
since the old 690px floor was itself forcing extra empty space
|
||||||
|
once the padding was reduced. Scoped to desktop only; tablet/
|
||||||
|
mobile keep their original spacing untouched below. */
|
||||||
|
@media (min-width: 1025px) {
|
||||||
|
.esy-section {
|
||||||
|
padding: 0;
|
||||||
|
min-height: clamp(420px, 38vw, 500px);
|
||||||
|
}
|
||||||
|
.esy-inner {
|
||||||
|
max-width: 1900px;
|
||||||
|
padding-top: 64px;
|
||||||
|
padding-bottom: 56px;
|
||||||
|
padding-left: 64px;
|
||||||
|
padding-right: 64px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.esy-section { width: calc(100% - 24px); border-radius: 22px; }
|
||||||
|
.esy-inner { grid-template-columns: 1fr; align-items: start; }
|
||||||
|
.esy-side { max-width: 640px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.esy-section { min-height: 560px; }
|
||||||
|
.esy-side { border-left: none; padding-left: 0; border-top: 1px solid rgba(255, 255, 255, 0.18); padding-top: 24px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="esy-section" id="wings-empowerment-story">
|
||||||
|
<div className="esy-bg" aria-hidden="true" />
|
||||||
|
<div className="esy-inner dm-section-container">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.85} yOffset={25}>
|
||||||
|
<div className="esy-eyebrow">/ Our Approach /</div>
|
||||||
|
<p className="esy-statement">
|
||||||
|
Not a program on a brochure. <em>An ecosystem women can build in.</em>
|
||||||
|
</p>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.15} duration={0.8} yOffset={25} className="esy-side">
|
||||||
|
<span className="esy-label">Manifesto</span>
|
||||||
|
<p className="esy-pull">A venture that starts with conviction. A network that turns it into momentum.</p>
|
||||||
|
<p className="esy-sub">
|
||||||
|
Wings is designed around the moments that define a founder: the first partnership signed, the first client won, the first team hired, the first enterprise scaled.
|
||||||
|
</p>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
215
src/components/sections/EmpowermentVision.tsx
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
import { ScrollReveal } from "@/animations/Reveal";
|
||||||
|
import IndustryWorldMap from "./IndustryWorldMap";
|
||||||
|
|
||||||
|
export default function EmpowermentVision() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.ev-section {
|
||||||
|
position: relative;
|
||||||
|
background: #ffffff;
|
||||||
|
padding: clamp(96px, 11vw, 150px) 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-map {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 0;
|
||||||
|
opacity: 0.4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
max-width: 1380px;
|
||||||
|
min-width: 0;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-eyebrow {
|
||||||
|
margin: 0 0 18px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
color: rgba(17, 17, 17, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
margin: 0 0 clamp(64px, 7.8vw, 92px) 0;
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(17, 17, 17, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-title {
|
||||||
|
margin: 0 0 clamp(92px, 11.5vw, 156px) 0 !important;
|
||||||
|
max-width: 1320px;
|
||||||
|
display: block;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
font-style: normal !important;
|
||||||
|
letter-spacing: -0.018em !important;
|
||||||
|
word-spacing: 0.02em;
|
||||||
|
line-height: 1.04em !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
-webkit-text-stroke: 2px #c01227;
|
||||||
|
paint-order: stroke fill;
|
||||||
|
font-size: clamp(48px, 8.5vw, 128px) !important;
|
||||||
|
white-space: normal;
|
||||||
|
overflow-wrap: normal;
|
||||||
|
text-wrap: balance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1280px) {
|
||||||
|
.ev-title {
|
||||||
|
font-size: 118px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1020px) {
|
||||||
|
.ev-title {
|
||||||
|
font-size: 96px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.ev-title {
|
||||||
|
font-size: 72px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-columns {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 0.6fr) minmax(0, 1fr);
|
||||||
|
gap: clamp(60px, 8vw, 122px);
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-columns > * {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-statement {
|
||||||
|
margin: 0;
|
||||||
|
font-size: clamp(28px, 3vw, 52px);
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 1.2;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
color: #111111;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-support {
|
||||||
|
display: grid;
|
||||||
|
gap: clamp(34px, 3.8vw, 52px);
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-block {
|
||||||
|
display: grid;
|
||||||
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-block-label {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 1.4px;
|
||||||
|
line-height: 1;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(192, 18, 39, 0.86);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-lead,
|
||||||
|
.ev-note {
|
||||||
|
font-size: clamp(17px, 1.3vw, 20px);
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #333333;
|
||||||
|
margin: 0;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-lead {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ev-note {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.ev-columns {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.ev-section { padding-top: 64px; padding-bottom: 64px; }
|
||||||
|
.ev-title {
|
||||||
|
max-width: calc(100vw - (var(--dm-mobile-gutter, 10px) * 2));
|
||||||
|
font-size: 56px !important;
|
||||||
|
letter-spacing: -0.025em !important;
|
||||||
|
line-height: 1.02em !important;
|
||||||
|
-webkit-text-stroke-width: 1.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="ev-section" id="wings-empowerment-vision">
|
||||||
|
<div className="ev-map" aria-hidden="true">
|
||||||
|
<IndustryWorldMap />
|
||||||
|
</div>
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="ev-inner">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.7} yOffset={20}>
|
||||||
|
<p className="ev-eyebrow">/ Our Vision /</p>
|
||||||
|
<hr className="ev-divider" />
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.12} duration={0.85} yOffset={30}>
|
||||||
|
<h2 className="ev-title">Women Own The Future.</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<div className="ev-columns">
|
||||||
|
<ScrollReveal delay={0.18} duration={0.8} yOffset={25}>
|
||||||
|
<p className="ev-statement">
|
||||||
|
A logistics ecosystem where women don’t just participate — they own, build, and lead.
|
||||||
|
</p>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.26} duration={0.8} yOffset={25} className="ev-support">
|
||||||
|
<div className="ev-block">
|
||||||
|
<p className="ev-block-label">The vision</p>
|
||||||
|
<p className="ev-lead">
|
||||||
|
We are building an entrepreneurial ecosystem where women launch enterprises, scale operations, and shape the future of logistics from the inside.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ev-block">
|
||||||
|
<p className="ev-block-label">Why it matters</p>
|
||||||
|
<p className="ev-note">
|
||||||
|
Industries transform when founders emerge from every corner. Women-led enterprises bring fresh perspective, operational resilience, and long-term economic impact to logistics.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="ev-block">
|
||||||
|
<p className="ev-block-label">How Wings contributes</p>
|
||||||
|
<p className="ev-note">
|
||||||
|
Wings provides the infrastructure — strategic partnerships, funding access, founder networks, and industry connections — that turns ambition into enterprise.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
75
src/components/sections/TheDoormileWay.module.css
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/* "The Doormile Way" — eyebrow + hairline divider + a huge display/graphic
|
||||||
|
headline (transparent fill, thin red stroke), composed as a poster-scale
|
||||||
|
typographic element rather than a plain large heading. Font, scale, and
|
||||||
|
line length are deliberately tuned to the reference composition, not
|
||||||
|
derived from a generic fluid-fill formula. */
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kicker {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
margin: 0 0 18px 0;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
color: rgba(23, 20, 15, 0.6);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 100%;
|
||||||
|
height: 1px;
|
||||||
|
margin: 0 0 40px 0;
|
||||||
|
border: none;
|
||||||
|
background-color: rgba(23, 20, 15, 0.16);
|
||||||
|
}
|
||||||
|
|
||||||
|
.headingWrap {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* !important beats the sitewide `.elementor-kit-5 h2` rule
|
||||||
|
(public/css/site.css:48-56, specificity 0-1-1 > this single class). */
|
||||||
|
.heading {
|
||||||
|
margin: 0;
|
||||||
|
max-width: 1220px;
|
||||||
|
font-family: "Manrope Alt", Sans-serif !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
font-style: normal !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1em !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: transparent !important;
|
||||||
|
-webkit-text-stroke-width: 2px;
|
||||||
|
-webkit-text-stroke-color: #c01227;
|
||||||
|
font-size: 180px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1020px) {
|
||||||
|
.heading {
|
||||||
|
font-size: 120px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.heading {
|
||||||
|
font-size: 80px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.heading {
|
||||||
|
font-size: 56px !important;
|
||||||
|
-webkit-text-stroke-width: 1.5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
||||||
|
import styles from "./TheDoormileWay.module.css";
|
||||||
|
|
||||||
export default function TheDoormileWay() {
|
export default function TheDoormileWay() {
|
||||||
return (
|
return (
|
||||||
@@ -19,29 +20,28 @@ export default function TheDoormileWay() {
|
|||||||
<div className="elementor-element elementor-element-88745f4 e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent" data-id="88745f4" data-element_type="container" data-e-type="container">
|
<div className="elementor-element elementor-element-88745f4 e-flex e-con-boxed cut-corner-no sticky-container-off e-con e-parent" data-id="88745f4" data-element_type="container" data-e-type="container">
|
||||||
<div className="e-con-inner">
|
<div className="e-con-inner">
|
||||||
<div className="elementor-element elementor-element-343b363 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="343b363" data-element_type="container" data-e-type="container">
|
<div className="elementor-element elementor-element-343b363 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="343b363" data-element_type="container" data-e-type="container">
|
||||||
<ScrollReveal delay={0.05} duration={0.7} yOffset={20}>
|
<div className={styles.header}>
|
||||||
<div className="elementor-element elementor-element-7afb238 elementor-widget elementor-widget-logico_heading" data-id="7afb238" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
<ScrollReveal delay={0.05} duration={0.7} yOffset={20} className={styles.kicker}>
|
||||||
<div className="elementor-widget-container">
|
<p className={styles.eyebrow}>/ Doormile Approach /</p>
|
||||||
<div className="logico-title">/ Doormile Approach /</div>
|
<hr className={styles.divider} />
|
||||||
</div>
|
</ScrollReveal>
|
||||||
</div>
|
<ScrollReveal delay={0.15} duration={0.85} yOffset={30}>
|
||||||
</ScrollReveal>
|
<div className="elementor-element elementor-element-1cc335a elementor-widget elementor-widget-logico_heading" data-id="1cc335a" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
||||||
<ScrollReveal delay={0.15} duration={0.85} yOffset={30}>
|
<div className="elementor-widget-container">
|
||||||
<div className="elementor-element elementor-element-1cc335a elementor-widget elementor-widget-logico_heading" data-id="1cc335a" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
<div
|
||||||
<div className="elementor-widget-container">
|
className="logico-title"
|
||||||
<div
|
style={{
|
||||||
className="logico-title"
|
WebkitTextStroke: "4px #c01227",
|
||||||
style={{
|
color: "#fff",
|
||||||
WebkitTextStroke: "4px #c01227",
|
fontWeight: 800,
|
||||||
color: "#fff",
|
}}
|
||||||
fontWeight: 800,
|
>
|
||||||
}}
|
The Doormile Way
|
||||||
>
|
</div>
|
||||||
The Doormile Way
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ScrollReveal>
|
||||||
</ScrollReveal>
|
</div>
|
||||||
<div className="elementor-element elementor-element-4c3e632 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="4c3e632" data-element_type="container" data-e-type="container">
|
<div className="elementor-element elementor-element-4c3e632 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="4c3e632" data-element_type="container" data-e-type="container">
|
||||||
<ScrollReveal delay={0.2} duration={0.8} yOffset={25}>
|
<ScrollReveal delay={0.2} duration={0.8} yOffset={25}>
|
||||||
<div className="elementor-element elementor-element-5ba6bbf e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="5ba6bbf" data-element_type="container" data-e-type="container">
|
<div className="elementor-element elementor-element-5ba6bbf e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="5ba6bbf" data-element_type="container" data-e-type="container">
|
||||||
|
|||||||
177
src/components/sections/WingsFeatureGrid.tsx
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
const FEATURES = [
|
||||||
|
{
|
||||||
|
title: "AI Decision Engine",
|
||||||
|
desc: "MileTruth™ AI continuously forecasts demand and optimizes routing and load decisions in real time.",
|
||||||
|
icon: (
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M12 2a4 4 0 0 1 4 4v1a4 4 0 0 1-1.2 2.86A4 4 0 0 1 16 13v1a4 4 0 0 1-4 4 4 4 0 0 1-4-4v-1a4 4 0 0 1 1.2-3.14A4 4 0 0 1 8 7V6a4 4 0 0 1 4-4Z" />
|
||||||
|
<path d="M9 21h6M12 18v3" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Real-Time Visibility",
|
||||||
|
desc: "Live shipment tracking keeps shippers, ground teams, and customers updated at every stage.",
|
||||||
|
icon: (
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7Z" />
|
||||||
|
<circle cx="12" cy="12" r="3" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Faster Regional Reach",
|
||||||
|
desc: "A dedicated network linking major regional capitals, built for speed rather than passenger schedules.",
|
||||||
|
icon: (
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
|
<polyline points="13 6 19 12 13 18" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Reliable Scheduled Operations",
|
||||||
|
desc: "Dedicated freighter capacity with consistent schedules — not subject to passenger-belly bumping.",
|
||||||
|
icon: (
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<rect x="3" y="4" width="18" height="18" rx="3" />
|
||||||
|
<line x1="16" y1="2" x2="16" y2="6" />
|
||||||
|
<line x1="8" y1="2" x2="8" y2="6" />
|
||||||
|
<line x1="3" y1="10" x2="21" y2="10" />
|
||||||
|
<path d="M9 16l2 2 4-4" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function WingsFeatureGrid() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.wfg-section {
|
||||||
|
background: #ffffff;
|
||||||
|
padding: var(--space-section-lg, 80px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-header {
|
||||||
|
margin: 0 auto 48px auto;
|
||||||
|
max-width: 720px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.45);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-title {
|
||||||
|
font-size: clamp(28px, 3.2vw, 46px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-inner {
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-card {
|
||||||
|
min-width: 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid rgba(17, 17, 17, 0.1);
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 30px 26px;
|
||||||
|
transition: border-color 0.35s ease, box-shadow 0.35s ease, transform 0.35s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-card:hover {
|
||||||
|
border-color: #c8102e;
|
||||||
|
box-shadow: 0 20px 40px rgba(200, 16, 46, 0.08);
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-icon {
|
||||||
|
width: 46px;
|
||||||
|
height: 46px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 12px;
|
||||||
|
background: rgba(200, 16, 46, 0.06);
|
||||||
|
color: #c8102e;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-card-title {
|
||||||
|
font-size: 16.5px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wfg-card-desc {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: rgba(17, 17, 17, 0.6);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.wfg-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.wfg-grid { grid-template-columns: 1fr; }
|
||||||
|
/* var(--space-section-lg) floors at a fixed 52px on any phone
|
||||||
|
width (clamp() has no further breakpoint-based step-down), which
|
||||||
|
reads as noticeably heavier than the section rhythm near the
|
||||||
|
footer. Trimmed to a real mobile value here instead. */
|
||||||
|
.wfg-section { padding-top: 44px; padding-bottom: 44px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="wfg-section" id="wings-features">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="wfg-inner">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={25} className="wfg-header">
|
||||||
|
<div className="wfg-eyebrow">/ Why Businesses Choose Doormile Wings /</div>
|
||||||
|
<h2 className="wfg-title">Built to be depended on</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<StaggerChildren stagger={0.1} duration={0.7} yOffset={30} className="wfg-grid">
|
||||||
|
{FEATURES.map((f) => (
|
||||||
|
<div key={f.title} className="wfg-card">
|
||||||
|
<div className="wfg-icon" aria-hidden="true">{f.icon}</div>
|
||||||
|
<h3 className="wfg-card-title">{f.title}</h3>
|
||||||
|
<p className="wfg-card-desc">{f.desc}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</StaggerChildren>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
105
src/components/sections/WingsFleetStrategy.tsx
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
import EVSection, { type EVFeature, type EVStat } from "./EVSection";
|
||||||
|
|
||||||
|
// Placeholder banner — plain dark gradient, no photography (no ATR72/Q400
|
||||||
|
// or airport-hub imagery exists in the repo yet). Swap `bannerImage` for a
|
||||||
|
// sourced photo later; this keeps the section shippable in the meantime.
|
||||||
|
// EVSection embeds this value in an UNQUOTED CSS url(...).
|
||||||
|
|
||||||
|
const METRICS: EVStat[] = [
|
||||||
|
{ value: 8.2, decimals: 1, suffix: "t", label: "ATR72 Payload" },
|
||||||
|
{ value: 1600, suffix: "km", label: "ATR72 Range" },
|
||||||
|
{ value: 8.6, decimals: 1, suffix: "t", label: "Q400 Payload" },
|
||||||
|
{ value: 667, suffix: "km/h", label: "Q400 Speed" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const FEATURES: EVFeature[] = [
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M22 12h-6l-4-9-2 1 3 8H5l-2-3-1.5.6L3 14l1.5 4.4L6 18l-2-3h8l3 8 2-1-3-8h6z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
title: "Dedicated Freighter Capacity",
|
||||||
|
desc: "Purpose-built cargo lift, not passenger-belly space competing with baggage.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="6" cy="6" r="2.5" />
|
||||||
|
<circle cx="18" cy="6" r="2.5" />
|
||||||
|
<circle cx="12" cy="18" r="2.5" />
|
||||||
|
<path d="M8.2 7.2 10 16M15.8 7.2 14 16M8.5 6h7" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
title: "Regional Route Design",
|
||||||
|
desc: "Turboprop range and speed matched to India's major regional-capital corridors.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: (
|
||||||
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M3 3v18h18" />
|
||||||
|
<path d="M7 14l4-4 3 3 6-7" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
title: "Scaling to 25 Aircraft",
|
||||||
|
desc: "Phase 2 vision: pan-India connectivity hubs and international regional routes.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function WingsFleetStrategy() {
|
||||||
|
return (
|
||||||
|
<div id="wings-fleet">
|
||||||
|
{/* EVSection is shared across other pages (Workflow1/Workflow2 etc.),
|
||||||
|
so its own .evnd-gap-bottom rule isn't touched — this override is
|
||||||
|
scoped to #wings-fleet only, and reduces just the blank margin
|
||||||
|
immediately before Infrastructure by ~42% (24px -> 14px) on
|
||||||
|
mobile, matching the site's tighter mobile rhythm elsewhere. */}
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
#wings-fleet .evnd-gap-bottom { margin-bottom: 14px !important; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<EVSection
|
||||||
|
bannerImage="/images/pahse1.png"
|
||||||
|
cardTitle="Quick Logistics"
|
||||||
|
cardSubtitle="Same-Day Air Network"
|
||||||
|
eyebrow="/ Fleet Strategy /"
|
||||||
|
titleLead="Purpose-Built for "
|
||||||
|
titleAccent="Regional Speed"
|
||||||
|
features={FEATURES}
|
||||||
|
metrics={METRICS}
|
||||||
|
cardsHeading="FLEET SPECIFICATIONS"
|
||||||
|
cardsTheme={{ accent: "#c8102e", accent2: "#ff6a3d", glow: "rgba(200,16,46,0.22)" }}
|
||||||
|
gapTop
|
||||||
|
gapBottom
|
||||||
|
badges={[
|
||||||
|
{ value: "3", label: "Phase 1 Aircraft" },
|
||||||
|
{ value: "25", label: "Phase 2 Vision" },
|
||||||
|
]}
|
||||||
|
mediaSlot={
|
||||||
|
<video
|
||||||
|
autoPlay
|
||||||
|
loop
|
||||||
|
muted
|
||||||
|
playsInline
|
||||||
|
style={{
|
||||||
|
width: "100%",
|
||||||
|
height: "100%",
|
||||||
|
minHeight: 420,
|
||||||
|
objectFit: "cover",
|
||||||
|
display: "block",
|
||||||
|
background: "#17171b"
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<source src="/videos/fleet.mp4" type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
}
|
||||||
|
ariaLabel="Doormile Wings fleet strategy"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
584
src/components/sections/WingsHero.tsx
Normal file
@@ -0,0 +1,584 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import Link from "next/link";
|
||||||
|
import gsap from "gsap";
|
||||||
|
import { ShimmerText } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
export default function WingsHero() {
|
||||||
|
const [activeSlide, setActiveSlide] = useState(0);
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setActiveSlide((prev) => (prev === 0 ? 1 : 0));
|
||||||
|
}, 7000);
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!containerRef.current) return;
|
||||||
|
|
||||||
|
const activeItem = containerRef.current.querySelector(".owl-item.active");
|
||||||
|
if (!activeItem) return;
|
||||||
|
|
||||||
|
const heading = activeItem.querySelector(".heading-content");
|
||||||
|
const text = activeItem.querySelector(".text-content");
|
||||||
|
const actions = activeItem.querySelector(".wings-hero-actions");
|
||||||
|
|
||||||
|
const animatedEls = [heading, text, actions].filter(Boolean);
|
||||||
|
if (!animatedEls.length) return;
|
||||||
|
|
||||||
|
gsap.killTweensOf(animatedEls);
|
||||||
|
|
||||||
|
if (window.matchMedia?.("(prefers-reduced-motion: reduce)").matches) {
|
||||||
|
gsap.set(animatedEls, { y: 0, opacity: 1, scale: 1 });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heading) {
|
||||||
|
gsap.fromTo(
|
||||||
|
heading,
|
||||||
|
{ y: 55, opacity: 0, scale: 0.95 },
|
||||||
|
{ y: 0, opacity: 1, scale: 1, duration: 1.1, ease: "power4.out" }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text) {
|
||||||
|
gsap.fromTo(
|
||||||
|
text,
|
||||||
|
{ y: 30, opacity: 0 },
|
||||||
|
{ y: 0, opacity: 1, duration: 0.95, ease: "power3.out", delay: 0.25 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (actions) {
|
||||||
|
gsap.fromTo(
|
||||||
|
actions,
|
||||||
|
{ y: 24, opacity: 0 },
|
||||||
|
{ y: 0, opacity: 1, duration: 0.85, ease: "power3.out", delay: 0.38 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [activeSlide]);
|
||||||
|
|
||||||
|
const handleDotClick = (index: number) => {
|
||||||
|
setActiveSlide(index);
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleSlide = () => {
|
||||||
|
setActiveSlide((prev) => (prev === 0 ? 1 : 0));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<link rel="preload" as="image" href="/images/wings-heroslider4.png" />
|
||||||
|
<link rel="preload" as="image" href="/images/wings-heroslide2.png" />
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.wings-hero-bg.elementor-repeater-item-3264830,
|
||||||
|
.wings-hero-bg.elementor-repeater-item-6867061 {
|
||||||
|
background-image: linear-gradient(rgba(0, 0, 0, 0.38), rgba(0, 0, 0, 0.06)), url('/images/wings-heroslider4.png') !important;
|
||||||
|
background-position: center !important;
|
||||||
|
background-repeat: no-repeat !important;
|
||||||
|
background-size: cover !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-bg.elementor-repeater-item-6867061 {
|
||||||
|
background-image: linear-gradient(rgba(0, 0, 0, 0.38), rgba(0, 0, 0, 0.06)), url('/images/wings-heroslide2.png') !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#doormile-wings,
|
||||||
|
#doormile-wings .content,
|
||||||
|
#doormile-wings .content-inner,
|
||||||
|
#doormile-wings .elementor {
|
||||||
|
max-width: 100vw !important;
|
||||||
|
overflow-x: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page,
|
||||||
|
.wings-hero-page .elementor-widget-container,
|
||||||
|
.wings-hero-page .logico-content-slider-widget,
|
||||||
|
.wings-hero-page .content-slider-wrapper,
|
||||||
|
.wings-hero-page .content-slider-container,
|
||||||
|
.wings-hero-page .content-slider,
|
||||||
|
.wings-hero-page .owl-stage-outer,
|
||||||
|
.wings-hero-page .owl-stage,
|
||||||
|
.wings-hero-page .owl-item,
|
||||||
|
.wings-hero-page .slider-item,
|
||||||
|
.wings-hero-page .slide-content,
|
||||||
|
.wings-hero-page .slide-content-inner {
|
||||||
|
max-width: 100% !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .slide-content {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
align-items: center !important;
|
||||||
|
text-align: center !important;
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
padding: 0 !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .slide-content-inner {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
align-items: center !important;
|
||||||
|
text-align: center !important;
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 1000px !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .content-slider-item-heading,
|
||||||
|
.wings-hero-page .content-slider-item-heading .heading-content {
|
||||||
|
text-align: center !important;
|
||||||
|
display: block !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
padding-left: 15px !important;
|
||||||
|
padding-right: 15px !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .content-slider-item-heading {
|
||||||
|
font-size: clamp(30px, 5.5vw, 80px) !important;
|
||||||
|
line-height: 1.05 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .content-slider-item-text {
|
||||||
|
display: flex !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin-top: 23px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .text-content {
|
||||||
|
text-align: center !important;
|
||||||
|
max-width: 680px !important;
|
||||||
|
width: 100% !important;
|
||||||
|
margin: 0 auto !important;
|
||||||
|
padding-left: 15px !important;
|
||||||
|
padding-right: 15px !important;
|
||||||
|
box-sizing: border-box !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .content-slider-item-text p {
|
||||||
|
font-size: clamp(16px, 1.35vw, 23px) !important;
|
||||||
|
line-height: 1.65 !important;
|
||||||
|
white-space: normal !important;
|
||||||
|
overflow-wrap: break-word !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 30px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 5;
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-actions .logico-alter-button {
|
||||||
|
display: inline-flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
justify-content: center !important;
|
||||||
|
opacity: 1;
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-mobile-break {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page a.wings-hero-primary,
|
||||||
|
.wings-hero-page a.wings-hero-secondary {
|
||||||
|
--logico-button-text-color: #ffffff;
|
||||||
|
--logico-button-border-color: rgba(255, 255, 255, 0.72);
|
||||||
|
--logico-button-background-color: rgba(255, 255, 255, 0);
|
||||||
|
--logico-button-text-hover: #ffffff;
|
||||||
|
--logico-button-border-hover: #c01227;
|
||||||
|
--logico-button-background-hover: #c01227;
|
||||||
|
--logico-button-icon-hover: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page a.wings-hero-primary {
|
||||||
|
--logico-button-border-color: #c01227;
|
||||||
|
--logico-button-background-color: #c01227;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page a.wings-hero-primary:hover,
|
||||||
|
.wings-hero-page a.wings-hero-secondary:hover {
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1025px) {
|
||||||
|
.wings-hero-page.elementor-element-741f56c {
|
||||||
|
padding: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .owl-carousel.owl-theme .content-item {
|
||||||
|
height: 800px !important;
|
||||||
|
min-height: 800px !important;
|
||||||
|
border-radius: 24px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.wings-hero-page.elementor-element-741f56c {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
margin-left: auto !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
--padding-top: 0px !important;
|
||||||
|
--padding-block-start: 0px !important;
|
||||||
|
padding: 0 10px 10px 10px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
border-radius: 25px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .owl-stage-outer {
|
||||||
|
height: 600px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .owl-carousel.owl-theme .content-item {
|
||||||
|
width: 100% !important;
|
||||||
|
min-height: 600px !important;
|
||||||
|
height: 600px !important;
|
||||||
|
border-radius: 25px !important;
|
||||||
|
overflow: hidden !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .slide-content {
|
||||||
|
min-height: 600px !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.wings-hero-page.elementor-element-741f56c {
|
||||||
|
width: 100vw !important;
|
||||||
|
--padding-top: 0px !important;
|
||||||
|
--padding-block-start: 0px !important;
|
||||||
|
padding: 0 10px 10px 10px !important;
|
||||||
|
border-radius: 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .slide-content,
|
||||||
|
.wings-hero-page .slide-content-inner {
|
||||||
|
width: calc(100vw - 20px) !important;
|
||||||
|
max-width: calc(100vw - 20px) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .slide-content-inner {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
margin-right: auto !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .owl-stage-outer {
|
||||||
|
height: 520px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .owl-carousel.owl-theme .content-item,
|
||||||
|
.wings-hero-page .slide-content {
|
||||||
|
min-height: 520px !important;
|
||||||
|
height: 520px !important;
|
||||||
|
border-radius: 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .slide-content-inner {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
padding-left: 0 !important;
|
||||||
|
padding-right: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .content-slider-item-heading {
|
||||||
|
max-width: 100% !important;
|
||||||
|
font-size: clamp(20px, 7vw, 32px) !important;
|
||||||
|
line-height: 1.12 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .content-slider-item-text p {
|
||||||
|
font-size: 14px !important;
|
||||||
|
line-height: 1.58 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .content-slider-item-text,
|
||||||
|
.wings-hero-page .text-content {
|
||||||
|
max-width: min(280px, calc(100vw - 70px)) !important;
|
||||||
|
width: min(280px, calc(100vw - 70px)) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-actions {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-actions .logico-alter-button {
|
||||||
|
min-width: 0 !important;
|
||||||
|
width: 190px !important;
|
||||||
|
padding: 0.78em 3.7em 0.78em 1.35em !important;
|
||||||
|
min-height: 42px !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-mobile-break {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .slider-footer {
|
||||||
|
text-align: right !important;
|
||||||
|
margin-bottom: 24px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .slider-footer .slider-pagination {
|
||||||
|
justify-content: flex-end !important;
|
||||||
|
margin-left: 22px !important;
|
||||||
|
margin-right: 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wings-hero-page .owl-dots,
|
||||||
|
.wings-hero-page .owl-nav {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="wings-hero-page elementor-element elementor-element-741f56c e-con-full e-flex cut-corner-no sticky-container-off e-con e-parent"
|
||||||
|
data-id="741f56c"
|
||||||
|
data-element_type="container"
|
||||||
|
data-e-type="container"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="elementor-element elementor-element-6c7cbcb elementor-widget elementor-widget-logico_content_slider"
|
||||||
|
data-id="6c7cbcb"
|
||||||
|
data-element_type="widget"
|
||||||
|
data-e-type="widget"
|
||||||
|
data-widget_type="logico_content_slider.default"
|
||||||
|
>
|
||||||
|
<div className="elementor-widget-container">
|
||||||
|
<div className="logico-content-slider-widget">
|
||||||
|
<div className="content-slider-wrapper">
|
||||||
|
<div className="content-slider-container">
|
||||||
|
<div
|
||||||
|
className="content-slider owl-carousel owl-theme nav-view-vertical nav-h-position-right nav-v-position-bottom owl-loaded owl-drag"
|
||||||
|
ref={containerRef}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="owl-stage-outer"
|
||||||
|
style={{ position: "relative", overflow: "hidden", height: "800px" }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="owl-stage"
|
||||||
|
style={{ position: "relative", width: "100%", height: "100%" }}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={`owl-item ${activeSlide === 0 ? "active" : ""}`}
|
||||||
|
style={{
|
||||||
|
position: "relative",
|
||||||
|
width: "100%",
|
||||||
|
opacity: activeSlide === 0 ? 1 : 0,
|
||||||
|
visibility: activeSlide === 0 ? "visible" : "hidden",
|
||||||
|
transition: "opacity 0.8s ease-in-out, visibility 0.8s ease-in-out",
|
||||||
|
zIndex: activeSlide === 0 ? 2 : 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="content-item slider-item elementor-repeater-item-3264830 slide-style-standard wings-hero-bg">
|
||||||
|
<div className="slide-content">
|
||||||
|
<div className="slide-content-inner">
|
||||||
|
<h1 className="content-slider-item-heading logico-content-wrapper-1">
|
||||||
|
<span className="heading-content">
|
||||||
|
Where Quick Commerce
|
||||||
|
<br />
|
||||||
|
<ShimmerText className="font-extrabold">
|
||||||
|
Meets Quick Logistics
|
||||||
|
</ShimmerText>
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<div className="content-slider-item-text logico-content-wrapper-2">
|
||||||
|
<div className="text-content">
|
||||||
|
<p>
|
||||||
|
MileTruth™ AI forecasts demand, protects promises,
|
||||||
|
and coordinates every handoff from runway to
|
||||||
|
doorstep.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={`owl-item ${activeSlide === 1 ? "active" : ""}`}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
width: "100%",
|
||||||
|
opacity: activeSlide === 1 ? 1 : 0,
|
||||||
|
visibility: activeSlide === 1 ? "visible" : "hidden",
|
||||||
|
transition: "opacity 0.8s ease-in-out, visibility 0.8s ease-in-out",
|
||||||
|
zIndex: activeSlide === 1 ? 2 : 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="content-item slider-item elementor-repeater-item-6867061 slide-style-standard wings-hero-bg">
|
||||||
|
<div className="slide-content">
|
||||||
|
<div className="slide-content-inner">
|
||||||
|
<h1 className="content-slider-item-heading logico-content-wrapper-1">
|
||||||
|
<span className="heading-content">
|
||||||
|
Doormile Wings
|
||||||
|
<br />
|
||||||
|
<ShimmerText className="font-extrabold">
|
||||||
|
Same-Day Air
|
||||||
|
</ShimmerText>
|
||||||
|
<br className="wings-mobile-break" />
|
||||||
|
{" "}
|
||||||
|
<ShimmerText className="font-extrabold">
|
||||||
|
Logistics
|
||||||
|
</ShimmerText>
|
||||||
|
</span>
|
||||||
|
</h1>
|
||||||
|
<div className="content-slider-item-text logico-content-wrapper-2">
|
||||||
|
<div className="text-content">
|
||||||
|
<p>
|
||||||
|
Linking India's major regional capitals with
|
||||||
|
AI-powered air movement and zero-emission EV
|
||||||
|
last-mile delivery.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="owl-nav">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="owl-next"
|
||||||
|
onClick={toggleSlide}
|
||||||
|
aria-label="Next"
|
||||||
|
style={{ cursor: "pointer", border: "none", outline: "none" }}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className="owl-prev"
|
||||||
|
onClick={toggleSlide}
|
||||||
|
aria-label="Previous"
|
||||||
|
style={{ cursor: "pointer", border: "none", outline: "none" }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="slider-footer slider-footer-position-after slider-footer-width-full slider-footer-view-inside">
|
||||||
|
<div className="slider-footer-content">
|
||||||
|
<div
|
||||||
|
className="slider-pagination"
|
||||||
|
style={{
|
||||||
|
display: "flex",
|
||||||
|
justifyContent: "flex-end",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: "10px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className="slider-progress-wrapper"
|
||||||
|
style={{
|
||||||
|
marginRight: "35px",
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
alignItems: "flex-start",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: "16px",
|
||||||
|
fontWeight: 600,
|
||||||
|
color: "#FFFFFF",
|
||||||
|
marginBottom: "4px",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="slider-progress-current">
|
||||||
|
{activeSlide === 0 ? "01" : "02"}
|
||||||
|
</span>
|
||||||
|
{" / "}
|
||||||
|
<span className="slider-progress-all" style={{ opacity: 0.6 }}>
|
||||||
|
02
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: "80px",
|
||||||
|
height: "2px",
|
||||||
|
background: "rgba(255, 255, 255, 0.2)",
|
||||||
|
position: "relative",
|
||||||
|
borderRadius: "1px",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
left: activeSlide === 0 ? "0" : "50%",
|
||||||
|
width: "50%",
|
||||||
|
height: "100%",
|
||||||
|
background: "#c01227",
|
||||||
|
transition: "left 0.3s ease",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="owl-dots owl-dots-6c7cbcb" style={{ display: "none" }}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
role="button"
|
||||||
|
className={`owl-dot ${activeSlide === 0 ? "active" : ""}`}
|
||||||
|
onClick={() => handleDotClick(0)}
|
||||||
|
>
|
||||||
|
<span></span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
role="button"
|
||||||
|
className={`owl-dot ${activeSlide === 1 ? "active" : ""}`}
|
||||||
|
onClick={() => handleDotClick(1)}
|
||||||
|
>
|
||||||
|
<span></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
312
src/components/sections/WingsHowItWorks.module.css
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
/* Premium editorial "How It Works" — bright warm-white surface, huge uppercase
|
||||||
|
display headline, top-right slide counter, and a five-column process row
|
||||||
|
divided by hairlines with oversized outlined numerals pinned to the top and
|
||||||
|
copy pinned to the bottom, echoing a tall, breathing editorial layout. */
|
||||||
|
|
||||||
|
.section {
|
||||||
|
background: linear-gradient(180deg, #fbfaf7 0%, #f5f2ec 100%);
|
||||||
|
padding: clamp(56px, 7vw, 96px) 0 clamp(80px, 8vw, 120px);
|
||||||
|
margin-bottom: 40px;
|
||||||
|
margin-right: 24px;
|
||||||
|
margin-left: 24px;
|
||||||
|
border-radius: 30px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionShell {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1420px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 5vw;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.sectionShell {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Header: eyebrow + giant display headline, counter top-right ---------- */
|
||||||
|
|
||||||
|
.headerRow {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 32px;
|
||||||
|
margin-bottom: clamp(48px, 6vw, 88px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
text-align: left;
|
||||||
|
max-width: 780px;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 1.5px;
|
||||||
|
text-transform: lowercase;
|
||||||
|
color: rgba(20, 18, 15, 0.5);
|
||||||
|
margin-bottom: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: clamp(32px, 4.6vw, 68px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.01em !important;
|
||||||
|
line-height: 1.02 !important;
|
||||||
|
text-transform: uppercase !important;
|
||||||
|
color: #17140f !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressCount {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
color: #17140f;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressTrack {
|
||||||
|
position: relative;
|
||||||
|
width: 110px;
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: rgba(23, 20, 15, 0.12);
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progressFill {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
height: 100%;
|
||||||
|
background: #c8102e;
|
||||||
|
transition: width 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---------- Process row: hairline-divided columns, numeral up top, copy at the bottom ---------- */
|
||||||
|
|
||||||
|
.flow {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step {
|
||||||
|
position: relative;
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: clamp(200px, 18vw, 260px);
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto 1fr auto;
|
||||||
|
padding: 0 32px;
|
||||||
|
border-left: 1px solid rgba(23, 20, 15, 0.14);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:last-child {
|
||||||
|
padding-right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:focus-visible {
|
||||||
|
outline: 2px solid rgba(200, 16, 46, 0.55);
|
||||||
|
outline-offset: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepTop {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepNum {
|
||||||
|
font-size: clamp(44px, 5.1vw, 92px);
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 0.85;
|
||||||
|
letter-spacing: -0.02em;
|
||||||
|
color: transparent;
|
||||||
|
-webkit-text-stroke: 1.5px rgba(23, 20, 15, 0.22);
|
||||||
|
transition: -webkit-text-stroke-color 0.4s ease, filter 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepActive .stepNum {
|
||||||
|
-webkit-text-stroke-color: #c8102e;
|
||||||
|
filter: drop-shadow(0 0 14px rgba(200, 16, 46, 0.18));
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepTag {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 10.5px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(23, 20, 15, 0.4);
|
||||||
|
transition: color 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepIcon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepActive .stepTag {
|
||||||
|
color: #c8102e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepBottom {
|
||||||
|
align-self: end;
|
||||||
|
max-width: 280px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepTitle {
|
||||||
|
font-size: 19px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #17140f !important;
|
||||||
|
margin: 0 0 10px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepDesc {
|
||||||
|
font-size: 13.5px;
|
||||||
|
line-height: 1.65;
|
||||||
|
color: rgba(23, 20, 15, 0.55);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- Step 04 / Step 05 title-alignment fix ----
|
||||||
|
.stepBottom sits in .step's 1fr grid row with align-self:end, so its
|
||||||
|
title starts wherever the leftover space above it runs out — driven by
|
||||||
|
its OWN content height, not a shared position. Steps 01-03's
|
||||||
|
descriptions wrap to 3 lines, giving .stepBottom a natural height of
|
||||||
|
~101px; "Destination Hub" (04) and "EV Last-Mile" (05) wrap to only 2
|
||||||
|
lines (~79px) at several desktop widths, so under that same
|
||||||
|
end-alignment they're left with ~22px more leftover space above them
|
||||||
|
and their titles land visibly lower. Reserving that same ~101px on
|
||||||
|
.stepBottom for these two steps only — a floor, not a cap, so it's
|
||||||
|
inert wherever their content is already taller — reproduces Steps
|
||||||
|
01-03's exact title position without touching their CSS, the shared
|
||||||
|
grid, or align-self. */
|
||||||
|
@media (min-width: 1200px) {
|
||||||
|
.step:nth-child(4) .stepBottom,
|
||||||
|
.step:nth-child(5) .stepBottom {
|
||||||
|
min-height: 102px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.headerRow {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 40px 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step {
|
||||||
|
flex: none;
|
||||||
|
border-left: none;
|
||||||
|
padding: 0;
|
||||||
|
min-height: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stepBottom {
|
||||||
|
max-width: none;
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:nth-child(n + 3) {
|
||||||
|
border-top: 1px solid rgba(23, 20, 15, 0.14);
|
||||||
|
padding-top: 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow > .step:last-child {
|
||||||
|
grid-column: 1 / -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
/* The clamp() on .section floors at a fixed 56px/80px on any phone width
|
||||||
|
(no further breakpoint-based step-down), noticeably heavier than the
|
||||||
|
rhythm every other Wings section settles into on mobile — normalized to
|
||||||
|
the same 44px/44px here.
|
||||||
|
|
||||||
|
.section's own margin-left/right also stays fixed at 24px below 1024px
|
||||||
|
(set at the top of this file, for the card's inset from the viewport
|
||||||
|
edge). Stacked with .sectionShell's padding that put the card's content
|
||||||
|
34px from the screen edge on phones — noticeably narrower than the
|
||||||
|
Footer's real 10px edge gutter (--dm-mobile-gutter, see globals.css's
|
||||||
|
.dm-section-container). Moving the full 10px gutter onto .section's
|
||||||
|
margin reproduces that same 10px screen-to-card edge, so the card sits
|
||||||
|
flush with its neighbors. .sectionShell then gets its own small 10px
|
||||||
|
breathing-room padding (independent of the outer gutter) so the eyebrow,
|
||||||
|
heading, progress indicator, and step content aren't flush against the
|
||||||
|
card's inner edge. */
|
||||||
|
.section {
|
||||||
|
padding-top: 44px;
|
||||||
|
padding-bottom: 44px;
|
||||||
|
margin-left: var(--dm-mobile-gutter, 10px);
|
||||||
|
margin-right: var(--dm-mobile-gutter, 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionShell {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.flow {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step {
|
||||||
|
padding: 28px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:not(:first-child) {
|
||||||
|
border-top: 1px solid rgba(23, 20, 15, 0.14);
|
||||||
|
}
|
||||||
|
|
||||||
|
.step:last-child {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
124
src/components/sections/WingsHowItWorks.tsx
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
||||||
|
import styles from "./WingsHowItWorks.module.css";
|
||||||
|
|
||||||
|
const STEPS = [
|
||||||
|
{
|
||||||
|
label: "Local Pickup",
|
||||||
|
desc: "Fast, on-demand first-mile logistics dispatched by electric vehicle.",
|
||||||
|
icon: (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M3 13h13l3 4h2v3H3z" />
|
||||||
|
<circle cx="7" cy="20" r="1.6" />
|
||||||
|
<circle cx="16" cy="20" r="1.6" />
|
||||||
|
<path d="M6 13V8a1 1 0 0 1 1-1h5l3 6" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Airport Clearance",
|
||||||
|
desc: "Automated screening, grouping, and rapid cross-docking at the hub.",
|
||||||
|
icon: (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z" />
|
||||||
|
<path d="m9 12 2 2 4-4" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Air Transit",
|
||||||
|
desc: "Optimized cargo flights bypass surface congestion on our regional fleet.",
|
||||||
|
icon: (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M22 12h-6l-4-9-2 1 3 8H5l-2-3-1.5.6L3 14l1.5 4.4L6 18l-2-3h8l3 8 2-1-3-8h6z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Destination Hub",
|
||||||
|
desc: "Immediate de-consolidation and screening upon arrival.",
|
||||||
|
icon: (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<rect x="4" y="10" width="16" height="11" rx="1" />
|
||||||
|
<path d="M9 21v-6h6v6M4 10 12 3l8 7" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "EV Last-Mile",
|
||||||
|
desc: "Zero-emission electric vehicles complete the final delivery.",
|
||||||
|
icon: (
|
||||||
|
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M3 13h13l3 4h2v3H3z" />
|
||||||
|
<circle cx="7" cy="20" r="1.6" />
|
||||||
|
<circle cx="16" cy="20" r="1.6" />
|
||||||
|
<path d="M6 13V8a1 1 0 0 1 1-1h5l3 6" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function WingsHowItWorks() {
|
||||||
|
const [active, setActive] = useState(0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className={styles.section} id="wings-how-it-works">
|
||||||
|
<div className={styles.sectionShell}>
|
||||||
|
<div className={styles.headerRow}>
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={25} className={styles.header}>
|
||||||
|
<div className={styles.eyebrow}>/ How It Works /</div>
|
||||||
|
<h2 className={styles.title}>
|
||||||
|
Pickup to
|
||||||
|
<br />
|
||||||
|
delivery, one
|
||||||
|
<br />
|
||||||
|
connected flow
|
||||||
|
</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<div className={styles.progress} aria-hidden="true">
|
||||||
|
<span className={styles.progressCount}>
|
||||||
|
{String(active + 1).padStart(2, "0")} / {String(STEPS.length).padStart(2, "0")}
|
||||||
|
</span>
|
||||||
|
<span className={styles.progressTrack}>
|
||||||
|
<span
|
||||||
|
className={styles.progressFill}
|
||||||
|
style={{ width: `${((active + 1) / STEPS.length) * 100}%` }}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<StaggerChildren stagger={0.06} duration={0.6} yOffset={24} className={styles.flow}>
|
||||||
|
{STEPS.map((step, i) => (
|
||||||
|
<div
|
||||||
|
key={step.label}
|
||||||
|
className={`${styles.step} ${i === active ? styles.stepActive : ""}`}
|
||||||
|
onMouseEnter={() => setActive(i)}
|
||||||
|
onClick={() => setActive(i)}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" || e.key === " ") setActive(i);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className={styles.stepTop}>
|
||||||
|
<span className={styles.stepNum}>{String(i + 1).padStart(2, "0")}</span>
|
||||||
|
<span className={styles.stepTag}>
|
||||||
|
<span className={styles.stepIcon} aria-hidden="true">{step.icon}</span>
|
||||||
|
Step
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className={styles.stepBottom}>
|
||||||
|
<h3 className={styles.stepTitle}>{step.label}</h3>
|
||||||
|
<p className={styles.stepDesc}>{step.desc}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</StaggerChildren>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
238
src/components/sections/WingsIndustries.tsx
Normal file
@@ -0,0 +1,238 @@
|
|||||||
|
import { ScrollReveal } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
const INDUSTRIES = [
|
||||||
|
{
|
||||||
|
title: "Quick Commerce",
|
||||||
|
challenge: "Sub-3-hour delivery windows for dark stores and micro-warehouses.",
|
||||||
|
solution: "Dedicated same-day air capacity linking metros to fulfillment hubs.",
|
||||||
|
image: "/images/card 1.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Express B2B Cargo",
|
||||||
|
challenge: "Time-critical industrial parts and components stuck on slow trunk routes.",
|
||||||
|
solution: "Rapid airport-to-airport transport between major economic nodes.",
|
||||||
|
image: "/images/card 2.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Healthcare & Pharma",
|
||||||
|
challenge: "Temperature-sensitive, compliance-critical shipments with zero margin for error.",
|
||||||
|
solution: "Active and passive cold-chain packaging with full chain-of-custody tracking.",
|
||||||
|
image: "/images/card 3.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cold Chain Logistics",
|
||||||
|
challenge: "Perishables and fresh goods that can't survive slow surface transit.",
|
||||||
|
solution: "Precision temperature tracking matched to airline-speed transit.",
|
||||||
|
image: "/images/card4.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "High-Value Cargo",
|
||||||
|
challenge: "Zero tolerance for loss, tampering, or delay on high-value shipments.",
|
||||||
|
solution: "Maximum-security handling with real-time electronic custody tracking.",
|
||||||
|
image: "/images/card5.png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "E-Commerce Logistics",
|
||||||
|
challenge: "Next-day and same-day fulfillment expectations across tier-1 and tier-2 cities.",
|
||||||
|
solution: "Direct sorting-hub-to-airport clearance network for faster regional delivery.",
|
||||||
|
image: "/images/card6.png",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function WingsIndustries() {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.wind-section {
|
||||||
|
background: #fafafa;
|
||||||
|
padding: var(--space-section-lg, 80px) 0 20px 0; /* Reduced bottom padding to minimize gap */
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-header {
|
||||||
|
margin: 0 auto 48px auto;
|
||||||
|
max-width: 720px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.45);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-title {
|
||||||
|
font-size: clamp(28px, 3.2vw, 46px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-inner {
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card {
|
||||||
|
position: relative;
|
||||||
|
min-width: 0;
|
||||||
|
aspect-ratio: 4 / 5;
|
||||||
|
border-radius: 20px;
|
||||||
|
overflow: hidden;
|
||||||
|
background: linear-gradient(150deg, #17171b 0%, #0b0b0d 100%);
|
||||||
|
isolation: isolate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card-wash {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: linear-gradient(160deg, rgba(200, 16, 46, 0.35) 0%, rgba(200, 16, 46, 0) 60%);
|
||||||
|
opacity: 0.5;
|
||||||
|
transition: opacity 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card:hover .wind-card-wash {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card-default {
|
||||||
|
position: absolute;
|
||||||
|
left: 24px;
|
||||||
|
right: 24px;
|
||||||
|
bottom: 24px;
|
||||||
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card:hover .wind-card-default {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(8px);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card .wind-card-default-title {
|
||||||
|
font-size: 20px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card-hover {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
padding: 26px 24px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 14px;
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(10px);
|
||||||
|
transition: opacity 0.35s ease, transform 0.35s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card:hover .wind-card-hover {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card .wind-card-hover-title {
|
||||||
|
font-size: 19px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card-label {
|
||||||
|
font-size: 10.5px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1.2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(255, 255, 255, 0.5);
|
||||||
|
margin: 0 0 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wind-card-text {
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.wind-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.wind-grid { grid-template-columns: 1fr; }
|
||||||
|
.wind-card { aspect-ratio: 3 / 4; }
|
||||||
|
/* var(--space-section-lg) floors at a fixed 52px on any phone
|
||||||
|
width; bottom is already trimmed to 20px, so only the top needs
|
||||||
|
a real mobile reduction to match the section rhythm near the
|
||||||
|
footer. */
|
||||||
|
.wind-section { padding-top: 44px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="wind-section" id="wings-industries">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="wind-inner">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={25} className="wind-header">
|
||||||
|
<div className="wind-eyebrow">/ Industries We Serve /</div>
|
||||||
|
<h2 className="wind-title">Built for the shipments that can’t wait</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.1} duration={0.8} yOffset={30} className="wind-grid">
|
||||||
|
{INDUSTRIES.map((ind) => (
|
||||||
|
<div
|
||||||
|
key={ind.title}
|
||||||
|
className="wind-card"
|
||||||
|
style={ind.image ? {
|
||||||
|
backgroundImage: `linear-gradient(to bottom, rgba(0,0,0,0.2), rgba(0,0,0,0.8)), url('${ind.image}')`,
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundPosition: 'center'
|
||||||
|
} : undefined}
|
||||||
|
>
|
||||||
|
<div className="wind-card-wash" aria-hidden="true" />
|
||||||
|
|
||||||
|
<div className="wind-card-default">
|
||||||
|
<h3 className="wind-card-default-title">{ind.title}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="wind-card-hover">
|
||||||
|
<h3 className="wind-card-hover-title">{ind.title}</h3>
|
||||||
|
<div>
|
||||||
|
<p className="wind-card-label">Challenge</p>
|
||||||
|
<p className="wind-card-text">{ind.challenge}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="wind-card-label">Doormile Wings</p>
|
||||||
|
<p className="wind-card-text">{ind.solution}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
247
src/components/sections/WingsInfrastructure.module.css
Normal file
@@ -0,0 +1,247 @@
|
|||||||
|
.section {
|
||||||
|
background: #09090b;
|
||||||
|
padding: var(--space-section-lg, 100px) 0;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
margin-right: 24px;
|
||||||
|
margin-left: 24px;
|
||||||
|
border-radius: 30px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ambient depth: calm, near-static light (stable mood) instead of the more
|
||||||
|
energetic red glow used on How It Works, plus a matching vignette. */
|
||||||
|
.section::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: -18%;
|
||||||
|
left: 50%;
|
||||||
|
width: 58%;
|
||||||
|
aspect-ratio: 1;
|
||||||
|
background: radial-gradient(circle, rgba(255, 255, 255, 0.05) 0%, rgba(200, 16, 46, 0.05) 40%, rgba(0, 0, 0, 0) 68%);
|
||||||
|
transform: translateX(-50%);
|
||||||
|
filter: blur(8px);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
animation: infraGlowDrift 14s ease-in-out infinite alternate;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: radial-gradient(120% 90% at 50% 0%, transparent 50%, rgba(0, 0, 0, 0.5) 100%);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes infraGlowDrift {
|
||||||
|
0% {
|
||||||
|
transform: translateX(-52%) translateY(0);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: translateX(-48%) translateY(10px);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.section::before {
|
||||||
|
animation: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Single page-alignment wrapper. Numbers verified against the Footer's real
|
||||||
|
* rendered content container (elementor-element-e183a77) with Playwright at
|
||||||
|
* 320-2560px: that row sits flush at a 20px gutter up to a 1420px cap, then
|
||||||
|
* centers beyond it, dropping to a 10px gutter under ~1024px. max-width:1420
|
||||||
|
* + padding:20px reproduces the same edge as a 20px-gutter + inner
|
||||||
|
* max-width:1380px pair in one box (no nested max-width needed). Kept
|
||||||
|
* byte-identical to WingsHowItWorks.module.css's .sectionShell.
|
||||||
|
*/
|
||||||
|
.sectionShell {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 1420px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 5vw;
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.sectionShell {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 700px;
|
||||||
|
margin: 0 auto 56px auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
font-size: clamp(28px, 3.2vw, 46px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Capability row — editorial columns divided by hairline rules, matching
|
||||||
|
How It Works' language but unnumbered (these aren't sequential steps). */
|
||||||
|
.grid {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
flex: 1 1 0;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0 30px;
|
||||||
|
border-left: 1px solid rgba(255, 255, 255, 0.09);
|
||||||
|
transition: border-color 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:first-child {
|
||||||
|
padding-left: 0;
|
||||||
|
border-left: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.14);
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
margin-bottom: 26px;
|
||||||
|
transition: border-color 0.4s ease, color 0.4s ease, background-color 0.4s ease, box-shadow 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover .icon {
|
||||||
|
border-color: rgba(200, 16, 46, 0.55);
|
||||||
|
color: #ffffff;
|
||||||
|
background: rgba(200, 16, 46, 0.12);
|
||||||
|
box-shadow: 0 0 18px rgba(200, 16, 46, 0.28);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardTitle {
|
||||||
|
font-size: 17px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: rgba(255, 255, 255, 0.82) !important;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
transition: color 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover .cardTitle {
|
||||||
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardDesc {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: rgba(255, 255, 255, 0.45);
|
||||||
|
margin: 0;
|
||||||
|
transition: color 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:hover .cardDesc {
|
||||||
|
color: rgba(255, 255, 255, 0.62);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.section {
|
||||||
|
padding: var(--space-section, 64px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 34px 28px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
border-left: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:nth-child(n + 3) {
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.09);
|
||||||
|
padding-top: 28px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
/* var(--space-section) floors at a fixed 40px on any phone width (no
|
||||||
|
further breakpoint-based step-down), and .sectionShell's horizontal
|
||||||
|
padding otherwise stays fixed at 24px below 1024px — both read as
|
||||||
|
inconsistent with the rhythm every other Wings section settles into on
|
||||||
|
mobile. Normalized to match: 44px/44px vertical, and the shared
|
||||||
|
--dm-mobile-gutter (10px) horizontal.
|
||||||
|
|
||||||
|
.section's own margin-left/right also stays fixed at 24px below 1024px
|
||||||
|
(set at the top of this file, for the card's inset from the viewport
|
||||||
|
edge). Stacked with .sectionShell's padding that put the card's content
|
||||||
|
34px from the screen edge on phones — noticeably narrower than the
|
||||||
|
Footer's real 10px edge gutter (--dm-mobile-gutter, see globals.css's
|
||||||
|
.dm-section-container). Moving the full 10px gutter onto .section's
|
||||||
|
margin reproduces that same 10px screen-to-card edge, so the card sits
|
||||||
|
flush with its neighbors. .sectionShell then gets its own small 10px
|
||||||
|
breathing-room padding (independent of the outer gutter) so the eyebrow,
|
||||||
|
heading, icons, card titles/descriptions, and dividers (which span
|
||||||
|
.card's full width) aren't flush against the card's inner edge. */
|
||||||
|
.section {
|
||||||
|
padding-top: 44px;
|
||||||
|
padding-bottom: 44px;
|
||||||
|
margin-left: var(--dm-mobile-gutter, 10px);
|
||||||
|
margin-right: var(--dm-mobile-gutter, 10px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionShell {
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
padding: 28px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:first-child {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:not(:first-child) {
|
||||||
|
border-top: 1px solid rgba(255, 255, 255, 0.09);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card:last-child {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/components/sections/WingsInfrastructure.tsx
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
||||||
|
import styles from "./WingsInfrastructure.module.css";
|
||||||
|
|
||||||
|
const CARDS = [
|
||||||
|
{
|
||||||
|
title: "Airport Cargo Hubs",
|
||||||
|
desc: "Near-airport clearance hubs instead of standalone warehouses — closer to the tarmac, faster to the aircraft.",
|
||||||
|
icon: (
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10Z" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Cross-Docking",
|
||||||
|
desc: "Screening, grouping, and consolidation happen in one motion, minimizing dwell time between arrival and departure.",
|
||||||
|
icon: (
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M3 7h7l2 3h9M3 7v10h18V10" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Asset-Light Network",
|
||||||
|
desc: "Leased terminal space that flexes with demand, keeping the network nimble as new routes come online.",
|
||||||
|
icon: (
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||||
|
<rect x="14" y="3" width="7" height="7" rx="1" />
|
||||||
|
<rect x="3" y="14" width="7" height="7" rx="1" />
|
||||||
|
<rect x="14" y="14" width="7" height="7" rx="1" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Scalable Operations",
|
||||||
|
desc: "Modular ground infrastructure built to expand city by city without slowing down day-to-day operations.",
|
||||||
|
icon: (
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M3 21V9l6-4 6 4v12M9 21V13h6v8" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function WingsInfrastructure() {
|
||||||
|
return (
|
||||||
|
<section className={styles.section} id="wings-infrastructure">
|
||||||
|
<div className={styles.sectionShell}>
|
||||||
|
<ScrollReveal delay={0.05} duration={0.7} yOffset={20} className={styles.header}>
|
||||||
|
<div className={styles.eyebrow}>/ Infrastructure /</div>
|
||||||
|
<h2 className={styles.title}>A network built to move, not to sit idle</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<StaggerChildren stagger={0.08} duration={0.6} yOffset={20} className={styles.grid}>
|
||||||
|
{CARDS.map((c) => (
|
||||||
|
<div key={c.title} className={styles.card}>
|
||||||
|
<div className={styles.icon} aria-hidden="true">{c.icon}</div>
|
||||||
|
<h3 className={styles.cardTitle}>{c.title}</h3>
|
||||||
|
<p className={styles.cardDesc}>{c.desc}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</StaggerChildren>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
507
src/components/sections/WingsMileTruth.tsx
Normal file
@@ -0,0 +1,507 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import gsap from "gsap";
|
||||||
|
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
const CAPABILITIES = [
|
||||||
|
{
|
||||||
|
title: "Demand Forecasting",
|
||||||
|
desc: "AI-driven forward projection of cargo demand, preemptively positioning aircraft and ground capacity.",
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M3 3v18h18" />
|
||||||
|
<path d="M7 14l4-4 3 3 6-7" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Dynamic Pricing",
|
||||||
|
desc: "Real-time rate adjustments that track market capacity fluctuations and optimize yield.",
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M9.5 9.5c0-1.4 1.1-2.3 2.5-2.3s2.5.9 2.5 2c0 1.6-2.2 1.8-2.5 3.3M12 17h.01" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Route Optimization",
|
||||||
|
desc: "Dynamic scheduling for ground EV pickups and flights that bypasses congestion for the fastest path.",
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12" />
|
||||||
|
<polyline points="13 6 19 12 13 18" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Load Balancing",
|
||||||
|
desc: "Automated weight-and-balance modeling for optimal, safe aircraft load layout within minutes.",
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M12 3v18M5 8l7-5 7 5M5 8v9a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Predictive Maintenance",
|
||||||
|
desc: "Continuous telemetry monitoring triggers preemptive alerts, drastically reducing aircraft downtime.",
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M22 12h-6l-4 8-4-16-4 8H2" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Real-Time Tracking",
|
||||||
|
desc: "End-to-end sensor monitoring keeps shippers, airline, and EV operators updated on exact shipment state.",
|
||||||
|
icon: (
|
||||||
|
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7Z" />
|
||||||
|
<circle cx="12" cy="12" r="3" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
function TiltCard({ children, className }: { children: React.ReactNode, className: string }) {
|
||||||
|
const cardRef = useRef<HTMLDivElement>(null);
|
||||||
|
const glareRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const card = cardRef.current;
|
||||||
|
const glare = glareRef.current;
|
||||||
|
if (!card || !glare) return;
|
||||||
|
const reduced = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
||||||
|
if (reduced) return;
|
||||||
|
|
||||||
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
|
const rect = card.getBoundingClientRect();
|
||||||
|
const x = e.clientX - rect.left;
|
||||||
|
const y = e.clientY - rect.top;
|
||||||
|
|
||||||
|
const centerX = rect.width / 2;
|
||||||
|
const centerY = rect.height / 2;
|
||||||
|
|
||||||
|
// Softer, more premium 3D tilt
|
||||||
|
const rotateX = ((y - centerY) / centerY) * -3;
|
||||||
|
const rotateY = ((x - centerX) / centerX) * 3;
|
||||||
|
|
||||||
|
gsap.to(card, {
|
||||||
|
duration: 0.4,
|
||||||
|
rotateX,
|
||||||
|
rotateY,
|
||||||
|
y: -4,
|
||||||
|
transformPerspective: 1000,
|
||||||
|
ease: "power2.out"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Dynamic glare tracking
|
||||||
|
gsap.set(glare, {
|
||||||
|
"--mx": `${x}px`,
|
||||||
|
"--my": `${y}px`,
|
||||||
|
});
|
||||||
|
gsap.to(glare, {
|
||||||
|
duration: 0.3,
|
||||||
|
opacity: 1,
|
||||||
|
ease: "power2.out"
|
||||||
|
});
|
||||||
|
|
||||||
|
// Z-axis pop
|
||||||
|
gsap.to(card.querySelector('.wmt-card-icon'), {
|
||||||
|
duration: 0.4,
|
||||||
|
z: 25,
|
||||||
|
ease: "power2.out"
|
||||||
|
});
|
||||||
|
gsap.to(card.querySelector('.wmt-card-title'), {
|
||||||
|
duration: 0.4,
|
||||||
|
z: 15,
|
||||||
|
ease: "power2.out"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleMouseLeave = () => {
|
||||||
|
gsap.to(card, {
|
||||||
|
duration: 0.8,
|
||||||
|
rotateX: 0,
|
||||||
|
rotateY: 0,
|
||||||
|
y: 0,
|
||||||
|
ease: "power3.out"
|
||||||
|
});
|
||||||
|
gsap.to(glare, {
|
||||||
|
duration: 0.5,
|
||||||
|
opacity: 0,
|
||||||
|
ease: "power3.out"
|
||||||
|
});
|
||||||
|
gsap.to(card.querySelectorAll('.wmt-card-icon, .wmt-card-title'), {
|
||||||
|
duration: 0.8,
|
||||||
|
z: 0,
|
||||||
|
ease: "power3.out"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
card.addEventListener("mousemove", handleMouseMove);
|
||||||
|
card.addEventListener("mouseleave", handleMouseLeave);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
card.removeEventListener("mousemove", handleMouseMove);
|
||||||
|
card.removeEventListener("mouseleave", handleMouseLeave);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={cardRef} className={className}>
|
||||||
|
<div
|
||||||
|
ref={glareRef}
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
inset: 0,
|
||||||
|
borderRadius: "inherit",
|
||||||
|
background: "radial-gradient(circle 220px at var(--mx, 50%) var(--my, 50%), rgba(255, 255, 255, 0.08) 0%, rgba(200, 16, 46, 0.03) 40%, transparent 100%)",
|
||||||
|
opacity: 0,
|
||||||
|
pointerEvents: "none",
|
||||||
|
zIndex: 0,
|
||||||
|
transform: "translateZ(1px)",
|
||||||
|
mixBlendMode: "screen"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<div style={{ position: "relative", zIndex: 1, transformStyle: "preserve-3d" }}>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WingsMileTruth() {
|
||||||
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
if (!canvas) return;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
const reduced = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches;
|
||||||
|
|
||||||
|
let width = (canvas.width = canvas.offsetWidth);
|
||||||
|
let height = (canvas.height = canvas.offsetHeight);
|
||||||
|
const handleResize = () => {
|
||||||
|
width = canvas.width = canvas.offsetWidth;
|
||||||
|
height = canvas.height = canvas.offsetHeight;
|
||||||
|
};
|
||||||
|
window.addEventListener("resize", handleResize);
|
||||||
|
|
||||||
|
const nodes = [
|
||||||
|
{ x: 0.12, y: 0.5 },
|
||||||
|
{ x: 0.34, y: 0.25 },
|
||||||
|
{ x: 0.56, y: 0.65 },
|
||||||
|
{ x: 0.78, y: 0.3 },
|
||||||
|
{ x: 0.92, y: 0.6 },
|
||||||
|
];
|
||||||
|
const particles = [
|
||||||
|
{ from: 0, to: 1, t: 0, speed: 0.006 },
|
||||||
|
{ from: 1, to: 2, t: 0.4, speed: 0.005 },
|
||||||
|
{ from: 2, to: 3, t: 0.15, speed: 0.007 },
|
||||||
|
{ from: 3, to: 4, t: 0.6, speed: 0.005 },
|
||||||
|
];
|
||||||
|
|
||||||
|
let raf: number;
|
||||||
|
const draw = () => {
|
||||||
|
ctx.clearRect(0, 0, width, height);
|
||||||
|
ctx.lineWidth = 1.5;
|
||||||
|
ctx.setLineDash([6, 6]);
|
||||||
|
ctx.strokeStyle = "rgba(200, 16, 46, 0.16)";
|
||||||
|
ctx.beginPath();
|
||||||
|
for (let i = 0; i < nodes.length - 1; i++) {
|
||||||
|
ctx.moveTo(nodes[i].x * width, nodes[i].y * height);
|
||||||
|
ctx.lineTo(nodes[i + 1].x * width, nodes[i + 1].y * height);
|
||||||
|
}
|
||||||
|
ctx.stroke();
|
||||||
|
ctx.setLineDash([]);
|
||||||
|
|
||||||
|
if (!reduced) {
|
||||||
|
particles.forEach((p) => {
|
||||||
|
p.t += p.speed;
|
||||||
|
if (p.t >= 1) p.t = 0;
|
||||||
|
const a = nodes[p.from];
|
||||||
|
const b = nodes[p.to];
|
||||||
|
const x = a.x * width + (b.x * width - a.x * width) * p.t;
|
||||||
|
const y = a.y * height + (b.y * height - a.y * height) * p.t;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, 3.5, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = "#c8102e";
|
||||||
|
ctx.shadowColor = "#c8102e";
|
||||||
|
ctx.shadowBlur = 10;
|
||||||
|
ctx.fill();
|
||||||
|
ctx.shadowBlur = 0;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
nodes.forEach((n) => {
|
||||||
|
const x = n.x * width;
|
||||||
|
const y = n.y * height;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, 7, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = "rgba(200, 16, 46, 0.1)";
|
||||||
|
ctx.fill();
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(x, y, 3, 0, Math.PI * 2);
|
||||||
|
ctx.fillStyle = "rgba(255, 255, 255, 0.25)";
|
||||||
|
ctx.fill();
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!reduced) raf = requestAnimationFrame(draw);
|
||||||
|
};
|
||||||
|
draw();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
window.removeEventListener("resize", handleResize);
|
||||||
|
if (raf) cancelAnimationFrame(raf);
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.wmt-section {
|
||||||
|
position: relative;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
margin: clamp(24px, 4vw, 40px) auto;
|
||||||
|
border-radius: 28px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #09090b;
|
||||||
|
padding: clamp(64px, 8vw, 100px) clamp(24px, 5vw, 48px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-canvas {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.12;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-glow {
|
||||||
|
position: absolute;
|
||||||
|
width: 480px;
|
||||||
|
height: 480px;
|
||||||
|
border-radius: 50%;
|
||||||
|
pointer-events: none;
|
||||||
|
filter: blur(140px);
|
||||||
|
z-index: 0;
|
||||||
|
opacity: 0.06;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-glow.tl { left: -8%; top: -12%; background: radial-gradient(circle, #c8102e 0%, transparent 70%); }
|
||||||
|
.wmt-glow.br { right: -8%; bottom: -12%; background: radial-gradient(circle, #ff3b3b 0%, transparent 70%); }
|
||||||
|
|
||||||
|
.wmt-inner {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-header {
|
||||||
|
margin: 0 auto 48px auto;
|
||||||
|
max-width: 720px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #c8102e;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-title {
|
||||||
|
font-size: clamp(28px, 3.4vw, 48px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||||
|
gap: 18px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-card {
|
||||||
|
min-width: 0;
|
||||||
|
position: relative;
|
||||||
|
background: rgba(255, 255, 255, 0.035);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||||
|
border-radius: 18px;
|
||||||
|
padding: 26px 22px;
|
||||||
|
transition: border-color 0.5s cubic-bezier(0.16, 1, 0.3, 1), background-color 0.5s cubic-bezier(0.16, 1, 0.3, 1), box-shadow 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-card:hover {
|
||||||
|
border-color: rgba(255, 255, 255, 0.16);
|
||||||
|
background-color: rgba(255, 255, 255, 0.055);
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.35);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-card-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: rgba(200, 16, 46, 0.12);
|
||||||
|
color: #ffffff;
|
||||||
|
margin-bottom: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-card-title {
|
||||||
|
font-size: 15.5px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-card-desc {
|
||||||
|
font-size: 16px;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-callout {
|
||||||
|
position: relative;
|
||||||
|
border-radius: 20px;
|
||||||
|
padding: 34px 32px;
|
||||||
|
background: linear-gradient(135deg, rgba(200, 16, 46, 0.95) 0%, rgba(110, 8, 26, 0.98) 100%);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.15);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 24px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-callout::after {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
inset: -1.5px;
|
||||||
|
border-radius: 20px;
|
||||||
|
background: linear-gradient(90deg, #c8102e, #ff3366, #ff0033, #c8102e);
|
||||||
|
background-size: 300% 100%;
|
||||||
|
animation: wmt-sweep 6s linear infinite;
|
||||||
|
z-index: -1;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes wmt-sweep {
|
||||||
|
0% { background-position: 0% 50%; }
|
||||||
|
50% { background-position: 100% 50%; }
|
||||||
|
100% { background-position: 0% 50%; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-callout-body { max-width: 640px; }
|
||||||
|
|
||||||
|
.wmt-callout-title {
|
||||||
|
font-size: 20px !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-callout-desc {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wmt-callout-badge {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 11.5px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #ffffff;
|
||||||
|
background: rgba(255, 255, 255, 0.15);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.25);
|
||||||
|
border-radius: 999px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.wmt-section { width: calc(100% - 24px); border-radius: 22px; }
|
||||||
|
.wmt-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.wmt-grid { grid-template-columns: 1fr; }
|
||||||
|
.wmt-callout { flex-direction: column; align-items: flex-start; }
|
||||||
|
/* The vertical clamp() floors at a fixed 64px on any phone width;
|
||||||
|
trimmed to a real mobile value to match the section rhythm near
|
||||||
|
the footer. Horizontal padding is left untouched (its own
|
||||||
|
clamp already resolves small on phones). */
|
||||||
|
.wmt-section { padding-top: 48px; padding-bottom: 48px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="wmt-section" id="wings-miletruth">
|
||||||
|
<canvas ref={canvasRef} className="wmt-canvas" aria-hidden="true" />
|
||||||
|
<div className="wmt-glow tl" aria-hidden="true" />
|
||||||
|
<div className="wmt-glow br" aria-hidden="true" />
|
||||||
|
|
||||||
|
<div className="wmt-inner dm-section-container">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={25} className="wmt-header">
|
||||||
|
<div className="wmt-eyebrow">/ MileTruth™ AI /</div>
|
||||||
|
<h2 className="wmt-title">The intelligence layer behind every delivery</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<StaggerChildren stagger={0.08} duration={0.65} yOffset={25} className="wmt-grid">
|
||||||
|
{CAPABILITIES.map((c) => (
|
||||||
|
<TiltCard key={c.title} className="wmt-card">
|
||||||
|
<div className="wmt-card-icon" aria-hidden="true">{c.icon}</div>
|
||||||
|
<h3 className="wmt-card-title">{c.title}</h3>
|
||||||
|
<p className="wmt-card-desc">{c.desc}</p>
|
||||||
|
</TiltCard>
|
||||||
|
))}
|
||||||
|
</StaggerChildren>
|
||||||
|
|
||||||
|
<ScrollReveal delay={0.15} duration={0.8} yOffset={25}>
|
||||||
|
<div className="wmt-callout">
|
||||||
|
<div className="wmt-callout-body">
|
||||||
|
<h3 className="wmt-callout-title">Digital Twin of the Logistics Network</h3>
|
||||||
|
<p className="wmt-callout-desc">
|
||||||
|
A live virtual model of every ground vehicle, terminal, and flight — simulating
|
||||||
|
what-if scenarios before bottlenecks ever form.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<span className="wmt-callout-badge">SaaS Ready</span>
|
||||||
|
</div>
|
||||||
|
</ScrollReveal>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
301
src/components/sections/WingsRoadmap.tsx
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
const ROADMAP_DATA = [
|
||||||
|
{
|
||||||
|
year: "Year 1",
|
||||||
|
trackLeft: "10%",
|
||||||
|
phase: "Launch Phase",
|
||||||
|
phaseClass: "yellow",
|
||||||
|
desc: "Establish localized clearance hubs and launch cargo services connecting 5–6 key commercial cities.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
year: "Year 2",
|
||||||
|
trackLeft: "30%",
|
||||||
|
phase: "Network Growth",
|
||||||
|
phaseClass: "green",
|
||||||
|
desc: "Expand hub operations and airfield schedules, serving 15 regional and secondary cargo airports.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
year: "Year 3",
|
||||||
|
trackLeft: "50%",
|
||||||
|
phase: "Cross-Border Nodes",
|
||||||
|
phaseClass: "blue",
|
||||||
|
desc: "Add international regional routes, bridging key freight corridors in South Asia.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
year: "Year 5",
|
||||||
|
trackLeft: "70%",
|
||||||
|
phase: "Operational Scale",
|
||||||
|
phaseClass: "blue",
|
||||||
|
desc: "Expand to a fleet of 25 leased cargo aircraft, servicing over 50 regional airports nationwide.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
year: "Year 10",
|
||||||
|
trackLeft: "90%",
|
||||||
|
phase: "Global Duplication",
|
||||||
|
phaseClass: "white-pill",
|
||||||
|
desc: "Replicate our asset-light, AI-powered logistics framework across Asia, Africa, and the Middle East.",
|
||||||
|
glow: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function WingsRoadmap() {
|
||||||
|
const [active, setActive] = useState(ROADMAP_DATA.length - 1);
|
||||||
|
const fillPct = ((active + 1) / ROADMAP_DATA.length) * 100;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.wrm-section {
|
||||||
|
position: relative;
|
||||||
|
width: calc(100% - 40px);
|
||||||
|
margin: clamp(32px, 4vw, 56px) auto clamp(16px, 2vw, 32px);
|
||||||
|
border-radius: 28px;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
background: #09090b;
|
||||||
|
padding: clamp(40px, 5vw, 64px) clamp(24px, 5vw, 48px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-header {
|
||||||
|
margin: 0 auto 44px auto;
|
||||||
|
max-width: 720px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2.5px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: #c8102e;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-title {
|
||||||
|
font-size: clamp(28px, 3.4vw, 48px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #ffffff !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-inner {
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-track-wrap {
|
||||||
|
position: relative;
|
||||||
|
height: 4px;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: 2px;
|
||||||
|
margin: 0 auto 44px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-track-fill {
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
background: #c8102e;
|
||||||
|
box-shadow: 0 0 10px #c8102e;
|
||||||
|
border-radius: 2px;
|
||||||
|
transition: width 0.5s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-nodes { position: absolute; inset: 0; }
|
||||||
|
|
||||||
|
.wrm-node {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #18181b;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
transition: background 0.4s ease, border-color 0.4s ease, box-shadow 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-node.is-active {
|
||||||
|
background: #c8102e;
|
||||||
|
border-color: #ffffff;
|
||||||
|
box-shadow: 0 0 10px #c8102e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, minmax(0, 1fr));
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-card {
|
||||||
|
position: relative;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 24px 20px;
|
||||||
|
border-radius: 16px;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.07);
|
||||||
|
background: rgba(255, 255, 255, 0.03);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.4s ease, background-color 0.4s ease, transform 0.4s ease, box-shadow 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-card:hover,
|
||||||
|
.wrm-card.is-active {
|
||||||
|
transform: translateY(-6px);
|
||||||
|
border-color: rgba(200, 16, 46, 0.5);
|
||||||
|
background: rgba(200, 16, 46, 0.06);
|
||||||
|
box-shadow: 0 16px 32px rgba(0, 0, 0, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-card-year {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 900;
|
||||||
|
color: #ffffff;
|
||||||
|
letter-spacing: -0.5px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-phase-pill {
|
||||||
|
display: inline-block;
|
||||||
|
font-size: 10.5px;
|
||||||
|
font-weight: 800;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.5px;
|
||||||
|
padding: 4px 9px;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-phase-pill.yellow { background: rgba(234,179,8,0.12); color: #eab308; border: 1px solid rgba(234,179,8,0.2); }
|
||||||
|
.wrm-phase-pill.green { background: rgba(34,197,94,0.12); color: #22c55e; border: 1px solid rgba(34,197,94,0.2); }
|
||||||
|
.wrm-phase-pill.blue { background: rgba(59,130,246,0.12); color: #3b82f6; border: 1px solid rgba(59,130,246,0.2); }
|
||||||
|
.wrm-phase-pill.white-pill { background: rgba(255,255,255,0.12); color: #ffffff; border: 1px solid rgba(255,255,255,0.2); }
|
||||||
|
|
||||||
|
.wrm-card-desc {
|
||||||
|
font-size: 14.5px;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: rgba(255, 255, 255, 0.55);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-card.wrm-glow {
|
||||||
|
background: linear-gradient(135deg, rgba(200, 16, 46, 0.95) 0%, rgba(110, 8, 26, 0.98) 100%);
|
||||||
|
border-color: rgba(255, 255, 255, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrm-card.wrm-glow .wrm-card-desc { color: rgba(255, 255, 255, 0.85); }
|
||||||
|
.wrm-card.wrm-glow:hover,
|
||||||
|
.wrm-card.wrm-glow.is-active {
|
||||||
|
box-shadow: 0 20px 40px rgba(200, 16, 46, 0.45);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.wrm-section { width: calc(100% - 24px); border-radius: 22px; }
|
||||||
|
.wrm-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
/* Both the outer margin-top and the internal vertical padding
|
||||||
|
clamp()s floor at fixed, sizeable values (56px + 64px = ~120px
|
||||||
|
combined) on any phone width — noticeably heavier than the
|
||||||
|
section rhythm near the footer. Horizontal padding is left
|
||||||
|
untouched (its own clamp already resolves small on phones). */
|
||||||
|
.wrm-section {
|
||||||
|
margin-top: 24px;
|
||||||
|
padding-top: 32px;
|
||||||
|
padding-bottom: 32px;
|
||||||
|
}
|
||||||
|
.wrm-track-wrap { display: none; }
|
||||||
|
.wrm-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
gap: 14px;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 26px;
|
||||||
|
}
|
||||||
|
.wrm-grid::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 5px;
|
||||||
|
top: 8px;
|
||||||
|
bottom: 8px;
|
||||||
|
width: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: rgba(255, 255, 255, 0.12);
|
||||||
|
}
|
||||||
|
.wrm-card { position: relative; }
|
||||||
|
.wrm-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: -25px;
|
||||||
|
top: 22px;
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #18181b;
|
||||||
|
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
.wrm-card.is-active::before,
|
||||||
|
.wrm-card:hover::before {
|
||||||
|
background: #c8102e;
|
||||||
|
border-color: #ffffff;
|
||||||
|
box-shadow: 0 0 10px rgba(200, 16, 46, 0.7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="wrm-section" id="wings-roadmap">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="wrm-header">
|
||||||
|
<div className="wrm-eyebrow">/ Future Roadmap /</div>
|
||||||
|
<h2 className="wrm-title">A ten-year path to national scale</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="wrm-inner">
|
||||||
|
<div className="wrm-track-wrap" aria-hidden="true">
|
||||||
|
<div className="wrm-track-fill" style={{ width: `${fillPct}%` }} />
|
||||||
|
<div className="wrm-nodes">
|
||||||
|
{ROADMAP_DATA.map((_, i) => (
|
||||||
|
<div
|
||||||
|
key={i}
|
||||||
|
className={`wrm-node${i <= active ? " is-active" : ""}`}
|
||||||
|
style={{ left: `${((i + 0.5) / ROADMAP_DATA.length) * 100}%` }}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="wrm-grid">
|
||||||
|
{ROADMAP_DATA.map((item, i) => (
|
||||||
|
<div
|
||||||
|
key={item.year}
|
||||||
|
className={`wrm-card${item.glow ? " wrm-glow" : ""}${i === active ? " is-active" : ""}`}
|
||||||
|
onMouseEnter={() => setActive(i)}
|
||||||
|
onClick={() => setActive(i)}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" || e.key === " ") setActive(i);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="wrm-card-year">{item.year}</div>
|
||||||
|
<span className={`wrm-phase-pill ${item.phaseClass}`}>{item.phase}</span>
|
||||||
|
<p className="wrm-card-desc">{item.desc}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
215
src/components/sections/WingsSpeedAdvantage.tsx
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import gsap from "gsap";
|
||||||
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
|
import { CountUp } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
gsap.registerPlugin(ScrollTrigger);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WingsSpeedAdvantage() {
|
||||||
|
const sectionRef = useRef<HTMLDivElement>(null);
|
||||||
|
const headerRef = useRef<HTMLDivElement>(null);
|
||||||
|
const leftRef = useRef<HTMLDivElement>(null);
|
||||||
|
const rightRef = useRef<HTMLDivElement>(null);
|
||||||
|
const vsRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const section = sectionRef.current;
|
||||||
|
const header = headerRef.current;
|
||||||
|
const left = leftRef.current;
|
||||||
|
const right = rightRef.current;
|
||||||
|
const vs = vsRef.current;
|
||||||
|
if (!section || !header || !left || !right || !vs) return;
|
||||||
|
|
||||||
|
gsap.set([header, left, right], { opacity: 0, y: 40 });
|
||||||
|
gsap.set(vs, { opacity: 0, scale: 0.7 });
|
||||||
|
|
||||||
|
const tl = gsap.timeline({
|
||||||
|
scrollTrigger: { trigger: section, start: "top 78%", toggleActions: "play none none none" },
|
||||||
|
});
|
||||||
|
|
||||||
|
tl.to(header, { opacity: 1, y: 0, duration: 0.85, ease: "power3.out" })
|
||||||
|
.to([left, right], { opacity: 1, y: 0, duration: 0.8, stagger: 0.12, ease: "power3.out" }, "-=0.5")
|
||||||
|
.to(vs, { opacity: 1, scale: 1, duration: 0.5, ease: "back.out(1.7)" }, "-=0.35");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.wsa-section {
|
||||||
|
background: #fafafa;
|
||||||
|
padding: var(--space-section-lg, 80px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-header {
|
||||||
|
max-width: 720px;
|
||||||
|
margin: 0 auto 48px auto;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.45);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-title {
|
||||||
|
font-size: clamp(28px, 3.2vw, 46px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-inner {
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-face-off {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-panel {
|
||||||
|
min-width: 0;
|
||||||
|
border-radius: 22px;
|
||||||
|
padding: clamp(36px, 4vw, 56px) clamp(24px, 3vw, 40px);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-panel-surface {
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid rgba(17, 17, 17, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-panel-wings {
|
||||||
|
background: #111114;
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-panel-label {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-panel-surface .wsa-panel-label { color: rgba(17, 17, 17, 0.4); }
|
||||||
|
.wsa-panel-wings .wsa-panel-label { color: #c8102e; }
|
||||||
|
|
||||||
|
.wsa-value {
|
||||||
|
font-size: clamp(40px, 6vw, 76px);
|
||||||
|
font-weight: 900;
|
||||||
|
letter-spacing: -0.03em;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-panel-surface .wsa-value { color: #c8102e; }
|
||||||
|
.wsa-panel-wings .wsa-value { color: #ffffff; }
|
||||||
|
|
||||||
|
.wsa-unit {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-panel-surface .wsa-unit { color: rgba(17, 17, 17, 0.45); }
|
||||||
|
.wsa-panel-wings .wsa-unit { color: rgba(255, 255, 255, 0.75); }
|
||||||
|
|
||||||
|
.wsa-panel-sub {
|
||||||
|
margin-top: 18px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wsa-panel-surface .wsa-panel-sub { color: rgba(17, 17, 17, 0.4); }
|
||||||
|
.wsa-panel-wings .wsa-panel-sub { color: rgba(255, 255, 255, 0.55); }
|
||||||
|
|
||||||
|
.wsa-vs {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid rgba(17, 17, 17, 0.1);
|
||||||
|
box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
letter-spacing: 1px;
|
||||||
|
color: #c8102e;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
.wsa-face-off { grid-template-columns: 1fr; gap: 40px; }
|
||||||
|
/* Effective visible gap above/below the VS circle is (grid gap +
|
||||||
|
this margin), i.e. 40px - 28px = 12px on each side — an equal,
|
||||||
|
~40% reduction from the previous 20px/20px so the circle reads
|
||||||
|
as bridging the two cards instead of floating in a large empty
|
||||||
|
gap. margin: auto keeps it horizontally centered; the shorthand
|
||||||
|
applies identically to top and bottom so both stay equal. */
|
||||||
|
.wsa-vs { position: static; transform: none; margin: -28px auto; }
|
||||||
|
/* var(--space-section-lg) floors at a fixed 52px on any phone
|
||||||
|
width; trimmed to a real mobile value to match the section
|
||||||
|
rhythm near the footer. */
|
||||||
|
.wsa-section { padding-top: 44px; padding-bottom: 44px; }
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="wsa-section" ref={sectionRef} id="wings-speed">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="wsa-header" ref={headerRef}>
|
||||||
|
<div className="wsa-eyebrow">/ The Speed Advantage /</div>
|
||||||
|
<h2 className="wsa-title">Same-day, not some-day</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="wsa-inner">
|
||||||
|
<div className="wsa-face-off">
|
||||||
|
<div className="wsa-panel wsa-panel-surface" ref={leftRef}>
|
||||||
|
<div className="wsa-panel-label">Surface Logistics</div>
|
||||||
|
<div className="wsa-value">
|
||||||
|
48–<CountUp end={96} duration={1.6} triggerOnce />
|
||||||
|
</div>
|
||||||
|
<div className="wsa-unit">Hours</div>
|
||||||
|
<p className="wsa-panel-sub">Delhi–Mumbai standard surface transit time.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="wsa-vs" ref={vsRef}>VS</div>
|
||||||
|
|
||||||
|
<div className="wsa-panel wsa-panel-wings" ref={rightRef}>
|
||||||
|
<div className="wsa-panel-label">Doormile Wings</div>
|
||||||
|
<div className="wsa-value">
|
||||||
|
Max <CountUp end={3} duration={1.4} triggerOnce />
|
||||||
|
</div>
|
||||||
|
<div className="wsa-unit">Hours, Door-to-Door</div>
|
||||||
|
<p className="wsa-panel-sub">Same-day delivery between major regional capitals.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
955
src/components/sections/WingsWhyGrid.tsx
Normal file
@@ -0,0 +1,955 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect, useRef } from "react";
|
||||||
|
import gsap from "gsap";
|
||||||
|
import { ScrollTrigger } from "gsap/ScrollTrigger";
|
||||||
|
import { MotionPathPlugin } from "gsap/MotionPathPlugin";
|
||||||
|
import { DrawSVGPlugin } from "gsap/DrawSVGPlugin";
|
||||||
|
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
||||||
|
|
||||||
|
let hasDrawSVG = false;
|
||||||
|
if (typeof window !== "undefined") {
|
||||||
|
gsap.registerPlugin(ScrollTrigger, MotionPathPlugin);
|
||||||
|
try {
|
||||||
|
// DrawSVGPlugin ships free with gsap 3.13+. Registration (not the import
|
||||||
|
// itself) is guarded so the draw step can fall back to plain
|
||||||
|
// stroke-dasharray if it's ever unavailable, without touching timing.
|
||||||
|
gsap.registerPlugin(DrawSVGPlugin);
|
||||||
|
hasDrawSVG = true;
|
||||||
|
} catch {
|
||||||
|
hasDrawSVG = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const MOBILE_BQ = 600;
|
||||||
|
const AIRCRAFT_ROTATION_OFFSET = 90; // glyph's rest pose points "up"
|
||||||
|
const WAVE_AMPLITUDE_MIN = 20;
|
||||||
|
const WAVE_AMPLITUDE_MAX = 38;
|
||||||
|
// Row-wrapped grid layouts only (e.g. the 601-1024px 2-column tablet
|
||||||
|
// breakpoint) — single-row layouts (desktop, mobile) never use these.
|
||||||
|
const ROW_Y_TOLERANCE = 12; // px tolerance for clustering card anchors into the same row
|
||||||
|
const ROW_CLEARANCE_PADDING = 6; // safety margin between a crest and the row above's card bottom
|
||||||
|
// Exact max of buildWavePoints' ampFactor (1 + 0.28 + 0.15), used to clamp a
|
||||||
|
// row's wave amplitude so no crest can ever reach into the row above it,
|
||||||
|
// regardless of segment index/phase.
|
||||||
|
const MAX_WAVE_AMP_FACTOR = 1 + 0.28 + 0.15;
|
||||||
|
|
||||||
|
const FEATURES = [
|
||||||
|
{
|
||||||
|
title: "Same-Day Air Logistics",
|
||||||
|
desc: "Max 3-hour door-to-door delivery between India's major regional capitals.",
|
||||||
|
icon: (
|
||||||
|
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<polyline points="12 6 12 12 16 14" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "AI-Powered Operations",
|
||||||
|
desc: "MileTruth™ AI continuously forecasts demand and optimizes every route and load.",
|
||||||
|
icon: (
|
||||||
|
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<rect x="4" y="4" width="16" height="16" rx="3" />
|
||||||
|
<path d="M9 9h6v6H9z" />
|
||||||
|
<path d="M9 1v3M15 1v3M9 20v3M15 20v3M1 9h3M1 15h3M20 9h3M20 15h3" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Regional Connectivity",
|
||||||
|
desc: "Dedicated air capacity linking major economic hubs, not passenger-belly space.",
|
||||||
|
icon: (
|
||||||
|
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<circle cx="6" cy="6" r="2.5" />
|
||||||
|
<circle cx="18" cy="6" r="2.5" />
|
||||||
|
<circle cx="12" cy="18" r="2.5" />
|
||||||
|
<path d="M8.2 7.2 10 16M15.8 7.2 14 16M8.5 6h7" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "EV Last Mile",
|
||||||
|
desc: "Zero-emission electric vehicles complete every first- and final-mile delivery.",
|
||||||
|
icon: (
|
||||||
|
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.25" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<path d="M3 13h13l3 4h2v3H3z" />
|
||||||
|
<circle cx="7" cy="20" r="1.6" />
|
||||||
|
<circle cx="16" cy="20" r="1.6" />
|
||||||
|
<path d="M6 13V8a1 1 0 0 1 1-1h5l3 6" />
|
||||||
|
</svg>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface Point {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A non-mobile card anchor, carrying its source card's full box (wrap-
|
||||||
|
// relative) alongside the {x,y} route point — top/bottom are needed to
|
||||||
|
// group anchors into rows and clamp a row's wave amplitude against the row
|
||||||
|
// above it; left/right are needed to find the empty column gutter (the
|
||||||
|
// icon's own x sits near the left edge of its card, not the card's true
|
||||||
|
// horizontal center, so card edges — not icon x — must drive that
|
||||||
|
// calculation). Never leaves measure(); only plain Point[] is returned.
|
||||||
|
interface CardAnchor extends Point {
|
||||||
|
top: number;
|
||||||
|
bottom: number;
|
||||||
|
left: number;
|
||||||
|
right: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPathD(points: Point[]): string {
|
||||||
|
if (points.length < 2) return "";
|
||||||
|
let d = `M ${points[0].x} ${points[0].y}`;
|
||||||
|
for (let i = 0; i < points.length - 1; i++) {
|
||||||
|
const p0 = points[i];
|
||||||
|
const p1 = points[i + 1];
|
||||||
|
const dx = (p1.x - p0.x) / 2;
|
||||||
|
d += ` C ${p0.x + dx} ${p0.y}, ${p1.x - dx} ${p1.y}, ${p1.x} ${p1.y}`;
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inserts one raised "crest" point between every consecutive pair of card
|
||||||
|
// anchors, turning the mostly-straight spline into an alternating
|
||||||
|
// trough-crest-trough wave: each card position stays a trough (so the pulse
|
||||||
|
// still arrives level with its icon, unchanged), while the gap between cards
|
||||||
|
// arcs upward. buildPathD's per-segment horizontal-tangent control points
|
||||||
|
// already keep every trough/crest transition smooth (no sharp bends) without
|
||||||
|
// needing a fancier spline — only the input point list changes.
|
||||||
|
//
|
||||||
|
// Each crest's height and horizontal position are nudged by a small,
|
||||||
|
// index-derived (not random) offset, so consecutive humps are never
|
||||||
|
// identical — reading as a real, slightly irregular air route rather than a
|
||||||
|
// generated sine wave — while staying perfectly stable across re-measures
|
||||||
|
// (resize, etc.) since the offsets depend only on segment index, not chance.
|
||||||
|
// Each offset sums two sine terms at unrelated frequencies/phases rather
|
||||||
|
// than one, so the sequence of crest heights doesn't itself read as a
|
||||||
|
// single obvious repeating rhythm.
|
||||||
|
function buildWavePoints(points: Point[], amplitude: number): Point[] {
|
||||||
|
if (points.length < 2 || amplitude <= 0) return points;
|
||||||
|
const waved: Point[] = [points[0]];
|
||||||
|
for (let i = 0; i < points.length - 1; i++) {
|
||||||
|
const p0 = points[i];
|
||||||
|
const p1 = points[i + 1];
|
||||||
|
const ampFactor =
|
||||||
|
1 + 0.28 * Math.sin(i * 2.9 + 0.4) + 0.15 * Math.sin(i * 5.3 + 2.1);
|
||||||
|
const xJitter =
|
||||||
|
(p1.x - p0.x) * (0.08 * Math.sin(i * 1.7 + 1.3) + 0.06 * Math.sin(i * 3.6 + 0.2));
|
||||||
|
waved.push({
|
||||||
|
x: (p0.x + p1.x) / 2 + xJitter,
|
||||||
|
y: Math.min(p0.y, p1.y) - amplitude * ampFactor,
|
||||||
|
});
|
||||||
|
waved.push(p1);
|
||||||
|
}
|
||||||
|
return waved;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clusters card anchors into visual rows by their shared top edge. CSS grid
|
||||||
|
// auto-flow is row-major and `align-items: stretch` (the grid default) makes
|
||||||
|
// same-row cards share cardRect.top exactly, so a single-pass walk against
|
||||||
|
// the current row's reference y (with a small tolerance for sub-pixel
|
||||||
|
// getBoundingClientRect noise) is sufficient — no need for full clustering.
|
||||||
|
function groupIntoRows(anchors: CardAnchor[]): CardAnchor[][] {
|
||||||
|
const rows: CardAnchor[][] = [];
|
||||||
|
for (const a of anchors) {
|
||||||
|
const row = rows[rows.length - 1];
|
||||||
|
if (row && Math.abs(row[0].top - a.top) <= ROW_Y_TOLERANCE) {
|
||||||
|
row.push(a);
|
||||||
|
} else {
|
||||||
|
rows.push([a]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The x midpoint of the empty strip between two adjacent columns in a row —
|
||||||
|
// i.e. a real, always-card-free gutter, derived from actual card edges (not
|
||||||
|
// icon x, which sits near each card's left padding rather than its true
|
||||||
|
// horizontal center). `side` picks which internal gutter: "right" is
|
||||||
|
// between the two rightmost columns, "left" between the two leftmost — for
|
||||||
|
// the current 2-column layout these are the same single gutter, but this
|
||||||
|
// stays correct if a wider grid (3+ columns) ever wraps into multiple rows.
|
||||||
|
function computeColumnGutterX(row: CardAnchor[], side: "left" | "right"): number {
|
||||||
|
const sorted = [...row].sort((a, b) => a.left - b.left);
|
||||||
|
if (sorted.length < 2) return (sorted[0].left + sorted[0].right) / 2;
|
||||||
|
return side === "right"
|
||||||
|
? (sorted[sorted.length - 2].right + sorted[sorted.length - 1].left) / 2
|
||||||
|
: (sorted[0].right + sorted[1].left) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds a safe serpentine (boustrophedon) route across a row-wrapped grid:
|
||||||
|
// each row is traversed in alternating direction so consecutive rows' ends
|
||||||
|
// line up on the same side, and the vertical drop between rows is routed
|
||||||
|
// through the always-empty column gutter on that side rather than through
|
||||||
|
// either column's own x — the only way to guarantee the connector never
|
||||||
|
// crosses a card, since a card anchor sits only ~10px below the card above
|
||||||
|
// it in the same column. Each row's own wave amplitude is clamped to the
|
||||||
|
// measured clearance above it so no crest can reach into the row above.
|
||||||
|
function buildMultiRowRoute(rows: CardAnchor[][], baseAmplitude: number): Point[] {
|
||||||
|
const out: Point[] = [];
|
||||||
|
let prevRowBottom: number | null = null;
|
||||||
|
|
||||||
|
rows.forEach((row, ri) => {
|
||||||
|
const ordered = ri % 2 === 0 ? row : [...row].reverse();
|
||||||
|
const rowAnchorY = ordered[0].y;
|
||||||
|
|
||||||
|
const amplitude =
|
||||||
|
prevRowBottom === null
|
||||||
|
? baseAmplitude
|
||||||
|
: Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(baseAmplitude, (rowAnchorY - prevRowBottom - ROW_CLEARANCE_PADDING) / MAX_WAVE_AMP_FACTOR)
|
||||||
|
);
|
||||||
|
|
||||||
|
const waved = buildWavePoints(
|
||||||
|
ordered.map(({ x, y }) => ({ x, y })),
|
||||||
|
amplitude
|
||||||
|
);
|
||||||
|
|
||||||
|
if (out.length > 0) {
|
||||||
|
const prevPoint = out[out.length - 1];
|
||||||
|
const nextPoint = waved[0];
|
||||||
|
// Row (ri-1) ends at its right extreme when it traveled left-to-right
|
||||||
|
// (even index, unreversed), left extreme when it traveled
|
||||||
|
// right-to-left (odd index, reversed) — the gutter must sit on that
|
||||||
|
// same side so the dogleg never has to cross back over a card.
|
||||||
|
const side: "left" | "right" = (ri - 1) % 2 === 0 ? "right" : "left";
|
||||||
|
const gutterX = computeColumnGutterX(rows[ri - 1], side);
|
||||||
|
out.push({ x: gutterX, y: prevPoint.y }, { x: gutterX, y: nextPoint.y });
|
||||||
|
}
|
||||||
|
out.push(...waved);
|
||||||
|
|
||||||
|
prevRowBottom = Math.max(...row.map((a) => a.bottom));
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WingsWhyGrid() {
|
||||||
|
const wrapRef = useRef<HTMLDivElement>(null);
|
||||||
|
const svgRef = useRef<SVGSVGElement>(null);
|
||||||
|
const pathRef = useRef<SVGPathElement>(null);
|
||||||
|
// A second copy of the same "d", drawn brighter/thicker/blurred. A moving
|
||||||
|
// dasharray window (kept in sync with the pulse's own progress, see
|
||||||
|
// onUpdate below) makes a soft, feathered segment of the route glow right
|
||||||
|
// under the pulse and fade back to the resting line just behind it — the
|
||||||
|
// route being "energized" as the pulse passes, not just a static line.
|
||||||
|
const illumRef = useRef<SVGPathElement>(null);
|
||||||
|
// The traveler is the primary visual: a bright optical pulse riding the
|
||||||
|
// route. It's the outer group MotionPathPlugin translates. The aircraft is
|
||||||
|
// nested inside it as a small, secondary glyph — never its own focal point.
|
||||||
|
const travelerRef = useRef<SVGGElement>(null);
|
||||||
|
// Only this inner group rotates to face the direction of travel (the
|
||||||
|
// halo). Kept separate from travelerRef with an explicit 0,0 transform
|
||||||
|
// origin so rotation never pivots around a fill-box-derived point.
|
||||||
|
const directionalRef = useRef<SVGGElement>(null);
|
||||||
|
// The aircraft's own rotation wrapper, painted as a sibling *after* the
|
||||||
|
// core circle (see JSX) so its silhouette stays readable on top of the
|
||||||
|
// bright core instead of being buried under it. Mirrors directionalRef's
|
||||||
|
// rotation exactly (set alongside it in onUpdate) so both halves of the
|
||||||
|
// traveler always face the same way despite being separate DOM branches.
|
||||||
|
const aircraftDirectionalRef = useRef<SVGGElement>(null);
|
||||||
|
const cardRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||||
|
const iconRefs = useRef<(HTMLDivElement | null)[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const wrap = wrapRef.current;
|
||||||
|
const svg = svgRef.current;
|
||||||
|
const path = pathRef.current;
|
||||||
|
const illum = illumRef.current;
|
||||||
|
const traveler = travelerRef.current;
|
||||||
|
const directional = directionalRef.current;
|
||||||
|
const aircraftDirectional = aircraftDirectionalRef.current;
|
||||||
|
if (!wrap || !svg || !path || !illum || !traveler || !directional || !aircraftDirectional) return;
|
||||||
|
|
||||||
|
// Declared here (not inside the gsap.context callback below) so the
|
||||||
|
// cleanup function — which must run on every unmount, including ones
|
||||||
|
// gsap.context() itself doesn't know about, like these plain rAF/
|
||||||
|
// ResizeObserver handles — can always reach them.
|
||||||
|
let resizeRaf = 0;
|
||||||
|
let initialRaf = 0;
|
||||||
|
let rafIn = 0;
|
||||||
|
let ro: ResizeObserver | undefined;
|
||||||
|
let loadHandler: (() => void) | undefined;
|
||||||
|
// Also assigned (not re-declared) inside the gsap.context callback below,
|
||||||
|
// for the same reason: cleanup needs to reach them even though
|
||||||
|
// ctx.revert() already kills anything the context tracked, since the
|
||||||
|
// ScrollTrigger is created synchronously (tracked) but the flight
|
||||||
|
// timeline is only created later, asynchronously, once onEnter actually
|
||||||
|
// fires (not reliably tracked by the context in that case).
|
||||||
|
let st: ScrollTrigger | null = null;
|
||||||
|
let playedTimeline: gsap.core.Timeline | null = null;
|
||||||
|
|
||||||
|
// Everything GSAP-related (ScrollTrigger, timelines, gsap.set calls) is
|
||||||
|
// created inside this context so a single ctx.revert() on unmount kills
|
||||||
|
// all of it — this is what makes the whole setup safe to re-run on every
|
||||||
|
// mount (initial load, client-side route navigation, back/forward, hot
|
||||||
|
// reload) instead of only working after a hard refresh, where a prior
|
||||||
|
// mount's leftover ScrollTrigger/timeline could otherwise linger or hold
|
||||||
|
// a stale measurement.
|
||||||
|
const ctx = gsap.context(() => {
|
||||||
|
const reduced = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||||||
|
let played = false;
|
||||||
|
|
||||||
|
const isMobileLayout = () => window.matchMedia(`(max-width: ${MOBILE_BQ}px)`).matches;
|
||||||
|
|
||||||
|
const measure = (): { points: Point[]; isMobile: boolean } => {
|
||||||
|
const wrapRect = wrap.getBoundingClientRect();
|
||||||
|
const isMobile = isMobileLayout();
|
||||||
|
|
||||||
|
// Card box (wrap-relative) is captured alongside x/y for every card,
|
||||||
|
// mobile included, so the shape is uniform — only the non-mobile
|
||||||
|
// branch below actually reads top/bottom/left/right (to group into
|
||||||
|
// rows, clamp wave amplitude against the row above, and find the
|
||||||
|
// empty column gutter for inter-row transitions).
|
||||||
|
const anchors = cardRefs.current
|
||||||
|
.map((card, i) => {
|
||||||
|
const icon = iconRefs.current[i];
|
||||||
|
if (!card || !icon) return null;
|
||||||
|
const cardRect = card.getBoundingClientRect();
|
||||||
|
const iconRect = icon.getBoundingClientRect();
|
||||||
|
const top = cardRect.top - wrapRect.top;
|
||||||
|
const bottom = cardRect.bottom - wrapRect.top;
|
||||||
|
const left = cardRect.left - wrapRect.left;
|
||||||
|
const right = cardRect.right - wrapRect.left;
|
||||||
|
if (isMobile) {
|
||||||
|
return {
|
||||||
|
x: 12,
|
||||||
|
y: iconRect.top + iconRect.height / 2 - wrapRect.top,
|
||||||
|
top,
|
||||||
|
bottom,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
x: iconRect.left + iconRect.width / 2 - wrapRect.left,
|
||||||
|
y: cardRect.top - wrapRect.top - 14,
|
||||||
|
top,
|
||||||
|
bottom,
|
||||||
|
left,
|
||||||
|
right,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter((p): p is CardAnchor => p !== null);
|
||||||
|
|
||||||
|
const points: Point[] = anchors.map(({ x, y }) => ({ x, y }));
|
||||||
|
|
||||||
|
svg.setAttribute("width", String(wrapRect.width));
|
||||||
|
svg.setAttribute("height", String(wrapRect.height));
|
||||||
|
svg.setAttribute("viewBox", `0 0 ${wrapRect.width} ${wrapRect.height}`);
|
||||||
|
|
||||||
|
// Wave only applies to the horizontal (non-mobile) layout — mobile
|
||||||
|
// keeps its existing straight vertical line unchanged. Amplitude
|
||||||
|
// scales with the section's width so it stays "gentle" (never a
|
||||||
|
// straight line, never overly aggressive) at any breakpoint.
|
||||||
|
const amplitude = isMobile
|
||||||
|
? 0
|
||||||
|
: Math.max(WAVE_AMPLITUDE_MIN, Math.min(WAVE_AMPLITUDE_MAX, wrapRect.width / 20));
|
||||||
|
|
||||||
|
let routePoints: Point[];
|
||||||
|
if (isMobile) {
|
||||||
|
routePoints = points;
|
||||||
|
} else {
|
||||||
|
const rows = groupIntoRows(anchors);
|
||||||
|
// A single row (desktop, always 4-up) takes the exact original
|
||||||
|
// path — no behavior change there. Only a row-wrapped layout (the
|
||||||
|
// tablet/small-laptop 2-column breakpoint) uses the row-aware
|
||||||
|
// serpentine route.
|
||||||
|
routePoints = rows.length <= 1 ? buildWavePoints(points, amplitude) : buildMultiRowRoute(rows, amplitude);
|
||||||
|
}
|
||||||
|
const d = buildPathD(routePoints);
|
||||||
|
path.setAttribute("d", d);
|
||||||
|
illum.setAttribute("d", d);
|
||||||
|
|
||||||
|
return { points, isMobile };
|
||||||
|
};
|
||||||
|
|
||||||
|
ro = new ResizeObserver(() => {
|
||||||
|
cancelAnimationFrame(resizeRaf);
|
||||||
|
resizeRaf = requestAnimationFrame(() => {
|
||||||
|
if (!reduced) {
|
||||||
|
buildSequence();
|
||||||
|
} else {
|
||||||
|
measure();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ro.observe(wrap);
|
||||||
|
|
||||||
|
// Initial synchronous measurement so `path` has real geometry before any
|
||||||
|
// gsap.set/timeline touches it (ResizeObserver's first callback is async).
|
||||||
|
// Followed by one deferred re-measure: StaggerChildren's own mount effect
|
||||||
|
// (a sibling/child effect) can still have cards translated by their
|
||||||
|
// pre-reveal yOffset at this exact tick, so the very first synchronous
|
||||||
|
// read can be transiently stale — a post-paint rAF corrects it.
|
||||||
|
measure();
|
||||||
|
initialRaf = requestAnimationFrame(() => {
|
||||||
|
measure();
|
||||||
|
// Recalculates every ScrollTrigger's start/end position against the
|
||||||
|
// now-settled layout. Without this, a trigger created a moment before
|
||||||
|
// web fonts/images finish shifting the page can end up with a stale
|
||||||
|
// "top 85%" position that a normal scroll never actually crosses —
|
||||||
|
// the classic reason a GSAP ScrollTrigger works after a hard refresh
|
||||||
|
// (cache-warm, layout already stable) but not after a fresh client-side
|
||||||
|
// navigation.
|
||||||
|
ScrollTrigger.refresh();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Extra safety net: images loading in after this point can still shift
|
||||||
|
// layout. One more refresh once the whole page (including images) has
|
||||||
|
// finished loading catches that — a no-op if `load` already fired.
|
||||||
|
if (document.readyState !== "complete") {
|
||||||
|
loadHandler = () => ScrollTrigger.refresh();
|
||||||
|
window.addEventListener("load", loadHandler, { once: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
const activateCard = (i: number) => {
|
||||||
|
const card = cardRefs.current[i];
|
||||||
|
const icon = iconRefs.current[i];
|
||||||
|
if (!card) return;
|
||||||
|
// Arrival: border gently strengthens, ambient glow grows, icon
|
||||||
|
// brightens, card lifts a touch (CSS-driven, eased over ~0.5s — see
|
||||||
|
// .wwg-card / .wwg-card-glow transition durations below — so it grows
|
||||||
|
// and settles rather than snapping on).
|
||||||
|
card.classList.add("wwg-card-flown", "wwg-card-pulse");
|
||||||
|
// Icon's own gentle scale pulse starts a beat after the arrival
|
||||||
|
// begins, so the sequence reads as "arrives, then responds" rather
|
||||||
|
// than everything happening at once.
|
||||||
|
if (icon) {
|
||||||
|
gsap.timeline({ delay: 0.28 })
|
||||||
|
.to(icon, { scale: 1.02, duration: 0.32, ease: "expo.out" })
|
||||||
|
.to(icon, { scale: 1, duration: 0.32, ease: "sine.inOut" });
|
||||||
|
}
|
||||||
|
// Hold the arrival state, then ease into the subtler, persistent
|
||||||
|
// "flown" resting state.
|
||||||
|
gsap.delayedCall(0.95, () => card.classList.remove("wwg-card-pulse"));
|
||||||
|
};
|
||||||
|
|
||||||
|
const buildSequence = () => {
|
||||||
|
if (playedTimeline) playedTimeline.kill();
|
||||||
|
|
||||||
|
const { points, isMobile } = measure();
|
||||||
|
if (points.length < 2) return;
|
||||||
|
|
||||||
|
const restOpacity = isMobile ? 0.15 : 0.18;
|
||||||
|
const total = path.getTotalLength();
|
||||||
|
// The illuminated window trailing the pulse: roughly a fifth of the
|
||||||
|
// route's length, so it reads as a genuine glowing trail rather than
|
||||||
|
// a small blip or the entire line at once.
|
||||||
|
const trailLen = total * 0.22;
|
||||||
|
const illumDashGap = total + 200;
|
||||||
|
illum.setAttribute("stroke-dasharray", `${trailLen} ${illumDashGap}`);
|
||||||
|
const fractions = points.map((pt) => {
|
||||||
|
let best = 0;
|
||||||
|
let bestDist = Infinity;
|
||||||
|
const step = Math.max(2, total / 400);
|
||||||
|
for (let l = 0; l <= total; l += step) {
|
||||||
|
const p = path.getPointAtLength(l);
|
||||||
|
const dist = (p.x - pt.x) ** 2 + (p.y - pt.y) ** 2;
|
||||||
|
if (dist < bestDist) {
|
||||||
|
bestDist = dist;
|
||||||
|
best = l;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best / total;
|
||||||
|
});
|
||||||
|
const fired = fractions.map(() => false);
|
||||||
|
|
||||||
|
let currentAngle = 0;
|
||||||
|
let angleInit = false;
|
||||||
|
|
||||||
|
const tl = gsap.timeline({
|
||||||
|
scrollTrigger: {
|
||||||
|
trigger: wrap,
|
||||||
|
start: "top 75%",
|
||||||
|
end: "bottom 35%",
|
||||||
|
scrub: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
playedTimeline = tl;
|
||||||
|
|
||||||
|
// The energy pulse's own journey — calm and deliberate, ~4.8-5.5s so
|
||||||
|
// it glides rather than rushes from the first card to the last.
|
||||||
|
const FLIGHT_DURATION = 5.2;
|
||||||
|
// The route draws slightly faster than the pulse travels, both
|
||||||
|
// starting together at t=0 — so the drawn extent always leads the
|
||||||
|
// pulse's position (never the other way round) and the whole route
|
||||||
|
// is guaranteed fully illuminated well before the pulse's own journey
|
||||||
|
// ends, while both still read as one continuous, synchronized motion.
|
||||||
|
const DRAW_DURATION = FLIGHT_DURATION * 0.9;
|
||||||
|
|
||||||
|
if (hasDrawSVG) {
|
||||||
|
tl.to(path, { drawSVG: "100%", duration: DRAW_DURATION, ease: "power2.inOut" }, 0);
|
||||||
|
} else {
|
||||||
|
gsap.set(path, { strokeDasharray: total, strokeDashoffset: total });
|
||||||
|
tl.to(path, { strokeDashoffset: 0, duration: DRAW_DURATION, ease: "power2.inOut" }, 0);
|
||||||
|
}
|
||||||
|
// The base route ramps straight to its final resting opacity as it
|
||||||
|
// draws — never to an elevated "traveling" brightness — so there is
|
||||||
|
// only ever one illuminated layer on screen: the bright illum trail
|
||||||
|
// riding with the pulse (below). Without this, the whole drawn route
|
||||||
|
// would sit at a visibly elevated opacity for the entire flight,
|
||||||
|
// reading as a second, competing "lit" route behind the pulse.
|
||||||
|
tl.to(path, { opacity: restOpacity, duration: 0.9, ease: "sine.out" }, 0);
|
||||||
|
|
||||||
|
// GSAP computes its own rotation pivot for SVG elements from the
|
||||||
|
// target's bounding box (stamped as a data-svg-origin attribute) and
|
||||||
|
// ignores plain CSS transform-origin for that purpose — so without
|
||||||
|
// this, both directional groups were actually rotating around their
|
||||||
|
// bbox corner instead of (0,0), visibly orbiting off the path's
|
||||||
|
// centerline as they turned. svgOrigin is GSAP's own equivalent,
|
||||||
|
// stated in the element's literal SVG coordinates (not bbox-relative
|
||||||
|
// percentages), and only needs setting once — the per-frame
|
||||||
|
// rotation-only gsap.set calls below then reuse it automatically.
|
||||||
|
gsap.set([directional, aircraftDirectional], { svgOrigin: "0 0" });
|
||||||
|
|
||||||
|
// A soft fade-in rather than an instant pop, so the pulse's arrival
|
||||||
|
// reads as one continuous gesture from the very first frame.
|
||||||
|
tl.to(traveler, { opacity: 1, duration: 0.3, ease: "sine.out" }, 0);
|
||||||
|
tl.to(illum, { opacity: 1, duration: 0.4, ease: "sine.out" }, 0);
|
||||||
|
tl.to(traveler, {
|
||||||
|
// alignOrigin locks the traveler's own center (not a default
|
||||||
|
// corner/bbox reference) to the path, matching the explicit
|
||||||
|
// transform-origin: 50% 50% above — keeps the pulse/aircraft
|
||||||
|
// precisely on the route's centerline with no vertical drift.
|
||||||
|
motionPath: { path, autoRotate: false, alignOrigin: [0.5, 0.5] },
|
||||||
|
duration: FLIGHT_DURATION,
|
||||||
|
ease: "power2.inOut",
|
||||||
|
onUpdate: function () {
|
||||||
|
const p = this.progress();
|
||||||
|
const l = p * total;
|
||||||
|
const eps = Math.max(1, total * 0.01);
|
||||||
|
const a = path.getPointAtLength(Math.max(0, l - eps));
|
||||||
|
const b = path.getPointAtLength(Math.min(total, l + eps));
|
||||||
|
const target = (Math.atan2(b.y - a.y, b.x - a.x) * 180) / Math.PI + AIRCRAFT_ROTATION_OFFSET;
|
||||||
|
|
||||||
|
if (!angleInit) {
|
||||||
|
currentAngle = target;
|
||||||
|
angleInit = true;
|
||||||
|
} else {
|
||||||
|
let delta = target - currentAngle;
|
||||||
|
// shortest angular path so the lerp never spins the long way round
|
||||||
|
delta = ((delta + 180) % 360 + 360) % 360 - 180;
|
||||||
|
currentAngle += delta * 0.12;
|
||||||
|
}
|
||||||
|
// Only the two directional groups (trail + halo, and the aircraft)
|
||||||
|
// rotate — the core stays a perfectly round point of light on the
|
||||||
|
// path. Both are set to the same angle every frame so they always
|
||||||
|
// face the same way despite being separate DOM branches (see
|
||||||
|
// aircraftDirectionalRef for why they're split).
|
||||||
|
gsap.set(directional, { rotation: currentAngle });
|
||||||
|
gsap.set(aircraftDirectional, { rotation: currentAngle });
|
||||||
|
|
||||||
|
// Slide the illuminated window's trailing edge to l - trailLen and
|
||||||
|
// its leading edge to l (i.e. right under the pulse) by shifting
|
||||||
|
// the dash pattern's phase — the CSS blur on .wwg-illum-trail turns
|
||||||
|
// those two hard edges into a soft glow that fades into the resting
|
||||||
|
// route, rather than a sharp on/off cut.
|
||||||
|
illum.setAttribute("stroke-dashoffset", String(trailLen - l));
|
||||||
|
|
||||||
|
fractions.forEach((f, i) => {
|
||||||
|
if (!fired[i] && p >= f) {
|
||||||
|
fired[i] = true;
|
||||||
|
activateCard(i);
|
||||||
|
} else if (fired[i] && p < f) {
|
||||||
|
fired[i] = false;
|
||||||
|
// Reset card state if scrubbed backwards
|
||||||
|
cardRefs.current[i]?.classList.remove("wwg-card-flown", "wwg-card-pulse");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}, 0);
|
||||||
|
|
||||||
|
// Only once the pulse's own journey is complete does it fade out —
|
||||||
|
// the route itself is already at its resting opacity by this point
|
||||||
|
// (see above), so nothing further happens to it here.
|
||||||
|
tl.to(traveler, { opacity: 0, duration: 0.6, ease: "sine.out" }, FLIGHT_DURATION);
|
||||||
|
tl.to(illum, { opacity: 0, duration: 0.6, ease: "sine.out" }, FLIGHT_DURATION);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (reduced) {
|
||||||
|
const { isMobile } = measure();
|
||||||
|
gsap.set(path, { opacity: isMobile ? 0.15 : 0.18 });
|
||||||
|
if (hasDrawSVG) {
|
||||||
|
gsap.set(path, { drawSVG: "100%" });
|
||||||
|
} else {
|
||||||
|
gsap.set(path, { strokeDasharray: "none", strokeDashoffset: 0 });
|
||||||
|
}
|
||||||
|
gsap.set(traveler, { opacity: 0 });
|
||||||
|
gsap.set(illum, { opacity: 0 });
|
||||||
|
} else {
|
||||||
|
gsap.set(path, { opacity: 0 });
|
||||||
|
if (hasDrawSVG) {
|
||||||
|
gsap.set(path, { drawSVG: "0%" });
|
||||||
|
}
|
||||||
|
gsap.set(traveler, { opacity: 0 });
|
||||||
|
gsap.set(illum, { opacity: 0 });
|
||||||
|
|
||||||
|
// Build the scrubbing sequence immediately
|
||||||
|
buildSequence();
|
||||||
|
}
|
||||||
|
}, wrap);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
ro?.disconnect();
|
||||||
|
cancelAnimationFrame(resizeRaf);
|
||||||
|
cancelAnimationFrame(initialRaf);
|
||||||
|
cancelAnimationFrame(rafIn);
|
||||||
|
if (loadHandler) window.removeEventListener("load", loadHandler);
|
||||||
|
st?.kill();
|
||||||
|
playedTimeline?.kill();
|
||||||
|
// Reverts/kills everything gsap.context tracked (ScrollTrigger.create,
|
||||||
|
// gsap.set calls, etc.) as a safety net beyond the explicit kills
|
||||||
|
// above, which exist specifically because the flight timeline is
|
||||||
|
// created asynchronously (inside onEnter) and so isn't guaranteed to
|
||||||
|
// be tracked by the context itself.
|
||||||
|
ctx.revert();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<style
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
.wwg-section {
|
||||||
|
background: #ffffff;
|
||||||
|
padding: var(--space-section-lg, 80px) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-header {
|
||||||
|
margin: 0 auto 48px auto;
|
||||||
|
max-width: 720px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-eyebrow {
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
color: rgba(17, 17, 17, 0.45);
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-title {
|
||||||
|
font-size: clamp(28px, 3.2vw, 46px) !important;
|
||||||
|
font-weight: 800 !important;
|
||||||
|
letter-spacing: -0.03em !important;
|
||||||
|
line-height: 1.15 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-inner {
|
||||||
|
max-width: 1380px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-grid-wrap {
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-flightpath {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
overflow: visible;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-flightpath-line {
|
||||||
|
fill: none;
|
||||||
|
stroke: #c8102e;
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* A brighter, blurred copy of the same route, painted *behind* the
|
||||||
|
crisp foreground line (see JSX order). Its dasharray is driven
|
||||||
|
per-frame (see onUpdate) to keep a soft window of it trailing
|
||||||
|
directly beneath the pulse; because it sits behind rather than on
|
||||||
|
top, its blur reads as a glow radiating from under the route
|
||||||
|
instead of visually thickening the route's own edges. Stroke
|
||||||
|
width intentionally matches the base route so it never peeks out
|
||||||
|
wider than the crisp line drawn on top of it. */
|
||||||
|
.wwg-illum-trail {
|
||||||
|
fill: none;
|
||||||
|
stroke: #ff3d5a;
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-linecap: round;
|
||||||
|
filter: blur(5px);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The traveler is the primary visual: a single centered optical
|
||||||
|
pulse riding the route, with a small secondary aircraft glyph
|
||||||
|
carried inside it. Position (translate) lives on .wwg-traveler;
|
||||||
|
only the inner .wwg-traveler-directional group rotates to face
|
||||||
|
the direction of travel — the round pulse core stays outside it
|
||||||
|
so it never rotates, always reading as a clean point of light. */
|
||||||
|
.wwg-traveler {
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
will-change: transform;
|
||||||
|
/* Explicit center alignment (paired with alignOrigin: [0.5, 0.5]
|
||||||
|
on the motionPath tween) so the traveler's own center — not a
|
||||||
|
corner/default reference point — is what MotionPathPlugin locks
|
||||||
|
to the route, keeping it precisely on the path's centerline
|
||||||
|
with no vertical drift. */
|
||||||
|
transform-origin: 50% 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-traveler-directional {
|
||||||
|
transform-origin: 0px 0px;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Large, radially-symmetric ambient glow — the true hero of the
|
||||||
|
traveler. Centered on the pulse with no directional offset, so
|
||||||
|
there is never a brighter patch leading ahead of the aircraft.
|
||||||
|
Non-directional, so it lives outside the rotating
|
||||||
|
.wwg-traveler-directional group. */
|
||||||
|
.wwg-pulse-bloom {
|
||||||
|
fill: #c8102e;
|
||||||
|
opacity: 0.2;
|
||||||
|
filter: blur(18px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-pulse-halo {
|
||||||
|
fill: #c8102e;
|
||||||
|
opacity: 0.52;
|
||||||
|
filter: blur(7px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-pulse-core {
|
||||||
|
fill: #ffffff;
|
||||||
|
filter: drop-shadow(0 0 4px rgba(255, 255, 255, 0.98)) drop-shadow(0 0 9px rgba(200, 16, 46, 0.85))
|
||||||
|
drop-shadow(0 0 15px rgba(200, 16, 46, 0.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Deliberately no drop-shadow of its own — the bloom/halo/trail
|
||||||
|
above supply all the glow. Sized to be clearly recognizable as an
|
||||||
|
aircraft without becoming the focal point: the pulse is still the
|
||||||
|
brightest, largest element on screen at every moment. */
|
||||||
|
.wwg-aircraft {
|
||||||
|
color: #c8102e;
|
||||||
|
opacity: 0.65;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
gap: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card {
|
||||||
|
position: relative;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 30px 24px;
|
||||||
|
border: 1px solid rgba(17, 17, 17, 0.08);
|
||||||
|
border-radius: 18px;
|
||||||
|
background: #fafafa;
|
||||||
|
/* Staggered delays so activation reads as a cascade — glow moves
|
||||||
|
first (see .wwg-card-glow below, no delay), then border/shape,
|
||||||
|
then the background tint settles last — rather than every
|
||||||
|
property snapping at once. The "expo-out" curve (rather than
|
||||||
|
plain ease) is what gives it a soft, premium settle instead of a
|
||||||
|
mechanical linear-ish snap. */
|
||||||
|
transition:
|
||||||
|
border-color 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.08s,
|
||||||
|
box-shadow 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.08s,
|
||||||
|
transform 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.08s,
|
||||||
|
background-color 0.75s cubic-bezier(0.16, 1, 0.3, 1) 0.22s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card:hover {
|
||||||
|
border-color: rgba(200, 16, 46, 0.35);
|
||||||
|
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.06);
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-glow {
|
||||||
|
position: absolute;
|
||||||
|
inset: -12px;
|
||||||
|
border-radius: 22px;
|
||||||
|
background: radial-gradient(circle, rgba(200, 16, 46, 0.45) 0%, rgba(200, 16, 46, 0) 70%);
|
||||||
|
opacity: 0;
|
||||||
|
z-index: -1;
|
||||||
|
pointer-events: none;
|
||||||
|
filter: blur(6px);
|
||||||
|
transition: opacity 0.75s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-flown {
|
||||||
|
border-color: rgba(200, 16, 46, 0.25);
|
||||||
|
background-color: #fdf7f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-flown .wwg-card-glow {
|
||||||
|
opacity: 0.32;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-pulse {
|
||||||
|
border-color: rgba(200, 16, 46, 0.5);
|
||||||
|
box-shadow: 0 16px 34px rgba(0, 0, 0, 0.07);
|
||||||
|
transform: translateY(-3px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-pulse .wwg-card-glow {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-icon {
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 14px;
|
||||||
|
background: rgba(200, 16, 46, 0.06);
|
||||||
|
color: #c8102e;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
transition:
|
||||||
|
background-color 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.16s,
|
||||||
|
filter 0.7s cubic-bezier(0.16, 1, 0.3, 1) 0.16s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-flown .wwg-icon {
|
||||||
|
background: rgba(200, 16, 46, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-pulse .wwg-icon {
|
||||||
|
background: rgba(200, 16, 46, 0.14);
|
||||||
|
filter: brightness(1.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-title {
|
||||||
|
font-size: 17px !important;
|
||||||
|
font-weight: 700 !important;
|
||||||
|
line-height: 1.3 !important;
|
||||||
|
text-transform: none !important;
|
||||||
|
color: #111111 !important;
|
||||||
|
margin: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wwg-card-desc {
|
||||||
|
font-size: 14.5px;
|
||||||
|
line-height: 1.55;
|
||||||
|
color: rgba(17, 17, 17, 0.6);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.wwg-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.wwg-grid { grid-template-columns: 1fr; padding-left: 28px; }
|
||||||
|
/* var(--space-section-lg) floors at a fixed 52px on any phone
|
||||||
|
width; trimmed to a real mobile value to match the section
|
||||||
|
rhythm near the footer. Matches this file's own MOBILE_BQ
|
||||||
|
breakpoint used by the flight-path animation logic. */
|
||||||
|
.wwg-section { padding-top: 44px; padding-bottom: 44px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.wwg-card, .wwg-card-glow, .wwg-icon {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<section className="wwg-section" id="wings-why">
|
||||||
|
<div className="dm-section-container">
|
||||||
|
<div className="wwg-inner">
|
||||||
|
<ScrollReveal delay={0.05} duration={0.75} yOffset={25} className="wwg-header">
|
||||||
|
<div className="wwg-eyebrow">/ Why Doormile Wings /</div>
|
||||||
|
<h2 className="wwg-title">Built for the speed of the Gen-Z economy</h2>
|
||||||
|
</ScrollReveal>
|
||||||
|
|
||||||
|
<div className="wwg-grid-wrap" ref={wrapRef}>
|
||||||
|
<svg ref={svgRef} className="wwg-flightpath" aria-hidden="true" focusable="false">
|
||||||
|
{/* Rendered first (behind) so its blur/extra width reads as a
|
||||||
|
soft glow sitting under the route rather than visually
|
||||||
|
thickening it — the crisp .wwg-flightpath-line painted
|
||||||
|
after it always defines the route's actual edges. */}
|
||||||
|
<path ref={illumRef} className="wwg-illum-trail" d="" />
|
||||||
|
<path ref={pathRef} className="wwg-flightpath-line" d="" />
|
||||||
|
<g ref={travelerRef} className="wwg-traveler">
|
||||||
|
{/* Large feathered bloom: the true hero of the traveler, an
|
||||||
|
ambient glow with no direction of its own. */}
|
||||||
|
<circle className="wwg-pulse-bloom" cx="0" cy="0" r="26" />
|
||||||
|
<g ref={directionalRef} className="wwg-traveler-directional">
|
||||||
|
<ellipse className="wwg-pulse-halo" cx="0" cy="0" rx="11" ry="6" />
|
||||||
|
</g>
|
||||||
|
<circle className="wwg-pulse-core" cx="0" cy="0" r="2.6" />
|
||||||
|
{/* Painted after (on top of) the core so the aircraft's
|
||||||
|
silhouette stays readable instead of being buried under
|
||||||
|
the bright core dot. Rotation mirrors directionalRef
|
||||||
|
exactly (see onUpdate) despite living in its own group. */}
|
||||||
|
<g ref={aircraftDirectionalRef} className="wwg-traveler-directional">
|
||||||
|
{/* The stock glyph's original 24x24 viewBox had ~3.5
|
||||||
|
units of dead padding on every side around the actual
|
||||||
|
ink (true silhouette bbox was only 17x20) — scaling
|
||||||
|
from the nominal 24-unit box would have kept baking
|
||||||
|
that padding in and rendering smaller than intended.
|
||||||
|
These coordinates instead start from the silhouette's
|
||||||
|
own tight bbox (no padding), centered on (0,0), then
|
||||||
|
scaled 1.3x — giving a true rendered size of ~22x26px,
|
||||||
|
clearing 22px on both axes. Deliberately no
|
||||||
|
scale()/translate() transform: baked directly into
|
||||||
|
the path data so it renders at full native
|
||||||
|
resolution rather than through a transform stacked on
|
||||||
|
top of this group's own rotation transform. */}
|
||||||
|
<path
|
||||||
|
className="wwg-aircraft"
|
||||||
|
d="M0,-13 C-0.78,-13 -1.3,-12.22 -1.3,-11.31 L-1.3,-3.9 L-11.05,1.3 L-11.05,3.9 L-1.3,0.91 L-1.3,9.1 L-4.55,11.44 L-4.55,13 L0,11.7 L4.55,13 L4.55,11.44 L1.3,9.1 L1.3,0.91 L11.05,3.9 L11.05,1.3 L1.3,-3.9 L1.3,-11.31 C1.3,-12.22 0.78,-13 0,-13 Z"
|
||||||
|
fill="currentColor"
|
||||||
|
/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
<StaggerChildren stagger={0.1} duration={0.7} yOffset={30} className="wwg-grid">
|
||||||
|
{FEATURES.map((f, i) => (
|
||||||
|
<div
|
||||||
|
key={f.title}
|
||||||
|
className="wwg-card"
|
||||||
|
ref={(el) => {
|
||||||
|
cardRefs.current[i] = el;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span className="wwg-card-glow" aria-hidden="true" />
|
||||||
|
<div
|
||||||
|
className="wwg-icon"
|
||||||
|
aria-hidden="true"
|
||||||
|
ref={(el) => {
|
||||||
|
iconRefs.current[i] = el;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{f.icon}
|
||||||
|
</div>
|
||||||
|
<h3 className="wwg-card-title">{f.title}</h3>
|
||||||
|
<p className="wwg-card-desc">{f.desc}</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</StaggerChildren>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
import { CountUp, ScrollReveal, StaggerChildren } from "@/animations/Reveal";
|
||||||
import IndustryWorldMap from "./IndustryWorldMap";
|
import IndustryWorldMap from "./IndustryWorldMap";
|
||||||
|
|
||||||
const WS_STATS = [
|
const WS_STATS = [
|
||||||
@@ -42,7 +42,7 @@ const WS_CARDS = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Community Growth",
|
title: "Community Growth",
|
||||||
desc: "Creating local opportunities through training, employment, and empowerment.",
|
desc: "Creating local opportunities through entrepreneurship, collaboration, and community-driven growth.",
|
||||||
icon: (
|
icon: (
|
||||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round">
|
||||||
<circle cx="9" cy="8" r="3" />
|
<circle cx="9" cy="8" r="3" />
|
||||||
@@ -52,7 +52,48 @@ const WS_CARDS = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function WomenSection() {
|
interface WomenSectionStat {
|
||||||
|
value: string;
|
||||||
|
plus: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WomenSectionCard {
|
||||||
|
title: string;
|
||||||
|
desc: string;
|
||||||
|
icon: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface WomenSectionProps {
|
||||||
|
eyebrow?: string;
|
||||||
|
bannerLabel?: string;
|
||||||
|
bannerCopy?: string;
|
||||||
|
ctaLabel?: string;
|
||||||
|
ctaHref?: string;
|
||||||
|
heading?: string;
|
||||||
|
stats?: WomenSectionStat[];
|
||||||
|
cards?: WomenSectionCard[];
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStatValue(value: string) {
|
||||||
|
const match = value.trim().match(/^(\d+(?:\.\d+)?)(.*)$/);
|
||||||
|
if (!match) return null;
|
||||||
|
return {
|
||||||
|
end: Number(match[1]),
|
||||||
|
suffix: match[2],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function WomenSection({
|
||||||
|
eyebrow = "/ Women Empowerment /",
|
||||||
|
bannerLabel = "Empowering Women",
|
||||||
|
bannerCopy = "Doormile empowers women to lead the future of logistics.",
|
||||||
|
ctaLabel = "Explore more",
|
||||||
|
ctaHref = "#",
|
||||||
|
heading = "Women Leading the Way",
|
||||||
|
stats = WS_STATS,
|
||||||
|
cards = WS_CARDS,
|
||||||
|
}: WomenSectionProps = {}) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<style dangerouslySetInnerHTML={{ __html: `
|
<style dangerouslySetInnerHTML={{ __html: `
|
||||||
@@ -419,12 +460,12 @@ export default function WomenSection() {
|
|||||||
</div>
|
</div>
|
||||||
<div className="elementor-element elementor-element-239afbb elementor-widget elementor-widget-logico_heading" data-id="239afbb" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
<div className="elementor-element elementor-element-239afbb elementor-widget elementor-widget-logico_heading" data-id="239afbb" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
||||||
<div className="elementor-widget-container">
|
<div className="elementor-widget-container">
|
||||||
<h5 className="logico-title">Empowering Women</h5>
|
<h5 className="logico-title">{bannerLabel}</h5>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="elementor-element elementor-element-0d307dd elementor-widget elementor-widget-text-editor" data-id="0d307dd" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
|
<div className="elementor-element elementor-element-0d307dd elementor-widget elementor-widget-text-editor" data-id="0d307dd" data-element_type="widget" data-e-type="widget" data-widget_type="text-editor.default">
|
||||||
<div className="elementor-widget-container">
|
<div className="elementor-widget-container">
|
||||||
<p>Doormile empowers women to lead the future of logistics.</p>
|
<p>{bannerCopy}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -433,8 +474,8 @@ export default function WomenSection() {
|
|||||||
<div className="elementor-widget-container">
|
<div className="elementor-widget-container">
|
||||||
<div className="button-widget">
|
<div className="button-widget">
|
||||||
<div className="button-container">
|
<div className="button-container">
|
||||||
<a href="#" className="logico-small-button">
|
<a href={ctaHref} className="logico-small-button">
|
||||||
Explore more
|
{ctaLabel}
|
||||||
<svg viewBox="0 0 13 20">
|
<svg viewBox="0 0 13 20">
|
||||||
<polyline points="0.5 19.5 3 19.5 12.5 10 3 0.5"></polyline>
|
<polyline points="0.5 19.5 3 19.5 12.5 10 3 0.5"></polyline>
|
||||||
</svg>
|
</svg>
|
||||||
@@ -455,7 +496,7 @@ export default function WomenSection() {
|
|||||||
<div className="elementor-element elementor-element-90cc867 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="90cc867" data-element_type="container" data-e-type="container">
|
<div className="elementor-element elementor-element-90cc867 e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="90cc867" data-element_type="container" data-e-type="container">
|
||||||
<div className="elementor-element elementor-element-24c0280 elementor-widget__width-inherit elementor-widget elementor-widget-logico_heading" data-id="24c0280" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
<div className="elementor-element elementor-element-24c0280 elementor-widget__width-inherit elementor-widget elementor-widget-logico_heading" data-id="24c0280" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
||||||
<div className="elementor-widget-container">
|
<div className="elementor-widget-container">
|
||||||
<div className="logico-title">/ Women Empowerment /</div>
|
<div className="logico-title">{eyebrow}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -464,7 +505,7 @@ export default function WomenSection() {
|
|||||||
<ScrollReveal duration={0.8} yOffset={28} triggerOnce>
|
<ScrollReveal duration={0.8} yOffset={28} triggerOnce>
|
||||||
<div className="elementor-element elementor-element-778840d elementor-widget elementor-widget-logico_heading" data-id="778840d" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
<div className="elementor-element elementor-element-778840d elementor-widget elementor-widget-logico_heading" data-id="778840d" data-element_type="widget" data-e-type="widget" data-widget_type="logico_heading.default">
|
||||||
<div className="elementor-widget-container">
|
<div className="elementor-widget-container">
|
||||||
<h3 className="logico-title">Women Leading the Way</h3>
|
<h3 className="logico-title">{heading}</h3>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ScrollReveal>
|
</ScrollReveal>
|
||||||
@@ -484,16 +525,30 @@ export default function WomenSection() {
|
|||||||
<div className="elementor-element elementor-element-b2c956f e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="b2c956f" data-element_type="container" data-e-type="container">
|
<div className="elementor-element elementor-element-b2c956f e-con-full e-flex cut-corner-no sticky-container-off e-con e-child" data-id="b2c956f" data-element_type="container" data-e-type="container">
|
||||||
<div id="ws-stories">
|
<div id="ws-stories">
|
||||||
<StaggerChildren className="ws__stats" stagger={0.1} duration={0.6} yOffset={20} triggerOnce>
|
<StaggerChildren className="ws__stats" stagger={0.1} duration={0.6} yOffset={20} triggerOnce>
|
||||||
{WS_STATS.map((s) => (
|
{stats.map((s) => (
|
||||||
<div className="ws__stat" key={s.label}>
|
<div className="ws__stat" key={s.label}>
|
||||||
<div className="ws__stat-num">{s.value}<span>{s.plus}</span></div>
|
<div className="ws__stat-num">
|
||||||
|
{(() => {
|
||||||
|
const parsed = parseStatValue(s.value);
|
||||||
|
if (!parsed) return s.value;
|
||||||
|
return (
|
||||||
|
<CountUp
|
||||||
|
end={parsed.end}
|
||||||
|
suffix={parsed.suffix}
|
||||||
|
duration={1.8}
|
||||||
|
triggerOnce
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
<span>{s.plus}</span>
|
||||||
|
</div>
|
||||||
<div className="ws__stat-label">{s.label}</div>
|
<div className="ws__stat-label">{s.label}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</StaggerChildren>
|
</StaggerChildren>
|
||||||
|
|
||||||
<StaggerChildren className="ws__cards" stagger={0.08} duration={0.6} yOffset={24} triggerOnce>
|
<StaggerChildren className="ws__cards" stagger={0.08} duration={0.6} yOffset={24} triggerOnce>
|
||||||
{WS_CARDS.map((c) => (
|
{cards.map((c) => (
|
||||||
<div className="ws__card" key={c.title}>
|
<div className="ws__card" key={c.title}>
|
||||||
<span className="ws__card-icon" aria-hidden="true">{c.icon}</span>
|
<span className="ws__card-icon" aria-hidden="true">{c.icon}</span>
|
||||||
<div className="ws__card-title">{c.title}</div>
|
<div className="ws__card-title">{c.title}</div>
|
||||||
|
|||||||
123
src/components/sections/Workflow4.tsx
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import EVSection, { EVStat, EVBadge, EVFeature, EVCardsTheme } from "./EVSection";
|
||||||
|
|
||||||
|
/* Violet / Electric Purple — distinct from WF1 (cyan/teal) and WF2 (red/crimson). */
|
||||||
|
const THEME: EVCardsTheme = {
|
||||||
|
accent: "#7C5CFF",
|
||||||
|
accent2: "#4C3AE3",
|
||||||
|
glow: "rgba(124,92,255,0.20)",
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workflow 4 — Quantum Intelligence (hybrid split-screen).
|
||||||
|
*
|
||||||
|
* • Left — video: the hybrid optimization / energy-landscape visual.
|
||||||
|
* • Right — a COMPACT DASHBOARD: a quick KPI row (QUBO / Global / Hybrid /
|
||||||
|
* Multi) over feature cards describing the encode → energy
|
||||||
|
* landscape → global minimum → hybrid intelligence pipeline,
|
||||||
|
* sitting on the animated network backdrop (violet).
|
||||||
|
*
|
||||||
|
* Messaging is deliberately framed as work-in-progress ("building",
|
||||||
|
* "exploring") rather than a shipped quantum-computing claim.
|
||||||
|
*/
|
||||||
|
const METRICS: EVStat[] = [
|
||||||
|
{ text: "QUBO", value: 0, suffix: "", label: "Problem Model" },
|
||||||
|
{ text: "GLOBAL", value: 0, suffix: "", label: "Minimum Search" },
|
||||||
|
{ text: "HYBRID", value: 0, suffix: "", label: "Intelligence" },
|
||||||
|
{ text: "MULTI", value: 0, suffix: "", label: "Objective" },
|
||||||
|
];
|
||||||
|
|
||||||
|
/* Distinct copy for the bottom capability bar (not a repeat of the KPI row —
|
||||||
|
* Workflow 4's brief specifies separate content for each). */
|
||||||
|
const BAR_STATS: EVStat[] = [
|
||||||
|
{ text: "Multi-Rider", value: 0, suffix: "", label: "Dynamic Routing" },
|
||||||
|
{ text: "Optimized", value: 0, suffix: "", label: "Time Windows" },
|
||||||
|
{ text: "Real-Time", value: 0, suffix: "", label: "Fleet Balancing" },
|
||||||
|
{ text: "Cost + Distance + SLA", value: 0, suffix: "", label: "Objectives" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const ico = (path: React.ReactNode) => (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
>
|
||||||
|
{path}
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
|
||||||
|
const FEATURES: EVFeature[] = [
|
||||||
|
{
|
||||||
|
icon: ico(
|
||||||
|
<>
|
||||||
|
<rect x="3" y="3" width="7" height="7" />
|
||||||
|
<rect x="14" y="3" width="7" height="7" />
|
||||||
|
<rect x="14" y="14" width="7" height="7" />
|
||||||
|
<rect x="3" y="14" width="7" height="7" />
|
||||||
|
</>,
|
||||||
|
),
|
||||||
|
title: "Encode the Problem",
|
||||||
|
desc: "Rider assignments, delivery sequences, capacity, traffic, service time, and SLA constraints become part of one mathematical optimization model.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: ico(<path d="m8 3 4 8 5-5 5 15H2L8 3z" />),
|
||||||
|
title: "Build the Energy Landscape",
|
||||||
|
desc: "Each feasible routing solution is represented as a state with its own energy through a QUBO formulation.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: ico(
|
||||||
|
<>
|
||||||
|
<circle cx="12" cy="12" r="9" />
|
||||||
|
<circle cx="12" cy="12" r="5" />
|
||||||
|
<circle cx="12" cy="12" r="1" />
|
||||||
|
</>,
|
||||||
|
),
|
||||||
|
title: "Search the Global Minimum",
|
||||||
|
desc: "Optimization becomes the search for the lowest-energy state instead of evaluating route combinations one by one.",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: ico(
|
||||||
|
<>
|
||||||
|
<polygon points="12 2 2 7 12 12 22 7 12 2" />
|
||||||
|
<polyline points="2 17 12 22 22 17" />
|
||||||
|
<polyline points="2 12 12 17 22 12" />
|
||||||
|
</>,
|
||||||
|
),
|
||||||
|
title: "Hybrid Intelligence",
|
||||||
|
desc: "Classical algorithms, AI models, and quantum optimization work together using the right computational model for each problem.",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const BADGES: EVBadge[] = [
|
||||||
|
{ value: "QUBO", label: "PROBLEM MODEL" },
|
||||||
|
{ value: "HYBRID", label: "INTELLIGENCE" },
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Workflow4() {
|
||||||
|
return (
|
||||||
|
<EVSection
|
||||||
|
ariaLabel="Workflow 4 — Quantum Intelligence"
|
||||||
|
gapBottom
|
||||||
|
image="/videos/Doormile-quantum-video3.mp4"
|
||||||
|
cardTitle="BUILDING QUANTUM INTELLIGENCE"
|
||||||
|
cardSubtitle="Exploring a hybrid optimization layer that treats routing as an energy landscape, not a search problem."
|
||||||
|
eyebrow="/ Quantum Intelligence /"
|
||||||
|
titleLead="FROM ROUTE SEARCH. "
|
||||||
|
titleAccent="TO ENERGY MINIMIZATION."
|
||||||
|
imageAlt="Hybrid quantum-classical optimization engine visualizing an energy landscape"
|
||||||
|
metrics={METRICS}
|
||||||
|
barStats={BAR_STATS}
|
||||||
|
features={FEATURES}
|
||||||
|
cardsHeading="Quantum Intelligence"
|
||||||
|
sysLabel="HYBRID ENGINE"
|
||||||
|
cardsTheme={THEME}
|
||||||
|
badges={BADGES}
|
||||||
|
mediaClassName="evnd__imgwrap--wf4"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -24,6 +24,9 @@ const ROUTE_CLASSES: Record<string, string> = {
|
|||||||
"/solutions": `${SHARED} page-id-59 elementor-page-59`,
|
"/solutions": `${SHARED} page-id-59 elementor-page-59`,
|
||||||
// PHP source quirk: about-us.php has `home` class (upstream bug, preserved)
|
// PHP source quirk: about-us.php has `home` class (upstream bug, preserved)
|
||||||
"/about-us": `home ${SHARED} page-id-59 elementor-page-59`,
|
"/about-us": `home ${SHARED} page-id-59 elementor-page-59`,
|
||||||
|
// Reuse the About page Elementor/runtime context for the Wings Empowerment
|
||||||
|
// page sections that intentionally share the same migrated Elementor markup.
|
||||||
|
"/empowerment": `home ${SHARED} page-id-59 elementor-page-59`,
|
||||||
"/blog": `${SHARED} page-id-59 elementor-page-59`,
|
"/blog": `${SHARED} page-id-59 elementor-page-59`,
|
||||||
"/contact": `${SHARED} page-id-59 elementor-page-59`,
|
"/contact": `${SHARED} page-id-59 elementor-page-59`,
|
||||||
};
|
};
|
||||||
|
|||||||