diff --git a/extracted_mid_mile.svg b/extracted_mid_mile.svg new file mode 100644 index 0000000..fc8d4a6 --- /dev/null +++ b/extracted_mid_mile.svg @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 4f9aa8d..5b13faf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "doormile-next", "version": "0.1.0", "dependencies": { + "gsap": "^3.15.0", "next": "16.2.6", "react": "19.2.4", "react-dom": "19.2.4" @@ -3854,6 +3855,12 @@ "dev": true, "license": "ISC" }, + "node_modules/gsap": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.15.0.tgz", + "integrity": "sha512-dMW4CWBTUK1AEEDeZc1g4xpPGIrSf9fJF960qbTZmN/QwZIWY5wgliS6JWl9/25fpTGJrMRtSjGtOmPnfjZB+A==", + "license": "Standard 'no charge' license: https://gsap.com/standard-license." + }, "node_modules/has-bigints": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", diff --git a/package.json b/package.json index f3f941d..a20b1ee 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,7 @@ "lint": "eslint" }, "dependencies": { + "gsap": "^3.15.0", "next": "16.2.6", "react": "19.2.4", "react-dom": "19.2.4" diff --git a/src/animations/AnimationProvider.tsx b/src/animations/AnimationProvider.tsx new file mode 100644 index 0000000..7718fbb --- /dev/null +++ b/src/animations/AnimationProvider.tsx @@ -0,0 +1,103 @@ +"use client"; + +import React, { useEffect } from "react"; +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; + +/** + * AnimationProvider + * Initializes GSAP + ScrollTrigger globally, refreshes on route changes, + * and provides smooth defaults. + */ +export default function AnimationProvider({ children }: { children: React.ReactNode }) { + useEffect(() => { + gsap.registerPlugin(ScrollTrigger); + + // Global GSAP defaults for buttery animations + gsap.defaults({ + ease: "power3.out", + duration: 0.8, + }); + + const initDecorativeBlocks = () => { + // Clean up previous block triggers to avoid duplicates + ScrollTrigger.getAll().forEach((t) => { + if (t.vars && (t.vars as any).id === "block-deco") { + t.kill(); + } + }); + + const decorativeBlocks = document.querySelectorAll(".block-decoration"); + decorativeBlocks.forEach((block) => { + ScrollTrigger.create({ + id: "block-deco", + trigger: block, + start: "top 92%", + onEnter: () => { + setTimeout(() => { + block.classList.add("animated"); + }, 150); + }, + onEnterBack: () => { + setTimeout(() => { + block.classList.add("animated"); + }, 150); + }, + onLeave: () => { + block.classList.remove("animated"); + }, + onLeaveBack: () => { + block.classList.remove("animated"); + }, + }); + }); + }; + + // Run initializations + initDecorativeBlocks(); + + // Refresh ScrollTrigger at staggered intervals to account for lazy-loaded assets/images over the network + const timeouts = [ + setTimeout(() => { + initDecorativeBlocks(); + ScrollTrigger.refresh(true); + }, 100), + setTimeout(() => { + initDecorativeBlocks(); + ScrollTrigger.refresh(true); + }, 500), + setTimeout(() => { + initDecorativeBlocks(); + ScrollTrigger.refresh(true); + }, 1500), + setTimeout(() => { + initDecorativeBlocks(); + ScrollTrigger.refresh(true); + }, 3000), + ]; + + // Refresh on full window load (when all images, styles, and fonts are in place) + const handleLoad = () => { + initDecorativeBlocks(); + ScrollTrigger.refresh(true); + }; + window.addEventListener("load", handleLoad); + + // Also refresh on window resize + const handleResize = () => { + initDecorativeBlocks(); + ScrollTrigger.refresh(true); + }; + window.addEventListener("resize", handleResize); + + return () => { + timeouts.forEach(clearTimeout); + window.removeEventListener("load", handleLoad); + window.removeEventListener("resize", handleResize); + ScrollTrigger.getAll().forEach((t) => t.kill()); + }; + }, []); + + return <>{children}; +} + diff --git a/src/animations/Reveal.tsx b/src/animations/Reveal.tsx new file mode 100644 index 0000000..e29cb36 --- /dev/null +++ b/src/animations/Reveal.tsx @@ -0,0 +1,680 @@ +"use client"; + +import React, { useEffect, useRef, useState, useCallback } from "react"; +import gsap from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; + +// Register ScrollTrigger safely +if (typeof window !== "undefined") { + gsap.registerPlugin(ScrollTrigger); +} + +/* ============================================================ + 1. ScrollReveal + Fade-in + slide-up on scroll. The workhorse animation. + ============================================================ */ +interface ScrollRevealProps { + children: React.ReactNode; + delay?: number; + duration?: number; + yOffset?: number; + xOffset?: number; + className?: string; + triggerOnce?: boolean; +} + +export function ScrollReveal({ + children, + delay = 0, + duration = 0.8, + yOffset = 40, + xOffset = 0, + className = "", + triggerOnce = false, +}: ScrollRevealProps) { + const elementRef = useRef(null); + + useEffect(() => { + const el = elementRef.current; + if (!el) return; + + gsap.set(el, { y: yOffset, x: xOffset, opacity: 0 }); + + const trigger = ScrollTrigger.create({ + trigger: el, + start: "top 88%", + onEnter: (self) => { + gsap.to(el, { + y: 0, + x: 0, + opacity: 1, + duration, + ease: "power3.out", + delay, + overwrite: "auto", + }); + if (triggerOnce) self.kill(); + }, + onEnterBack: () => { + if (!triggerOnce) { + gsap.to(el, { + y: 0, + x: 0, + opacity: 1, + duration, + ease: "power3.out", + delay, + overwrite: "auto", + }); + } + }, + onLeave: () => { + if (!triggerOnce) { + gsap.set(el, { y: yOffset, x: xOffset, opacity: 0 }); + } + }, + onLeaveBack: () => { + if (!triggerOnce) { + gsap.set(el, { y: yOffset, x: xOffset, opacity: 0 }); + } + }, + }); + + return () => trigger?.kill(); + }, [delay, duration, yOffset, xOffset, triggerOnce]); + + return ( +
+ {children} +
+ ); +} + +/* ============================================================ + 2. RevealText + Splits text into words or characters and reveals with slide-up mask. + ============================================================ */ +interface RevealTextProps { + children: string; + type?: "words" | "chars"; + delay?: number; + duration?: number; + className?: string; + triggerOnce?: boolean; +} + +export function RevealText({ + children, + type = "words", + delay = 0, + duration = 0.85, + className = "", + triggerOnce = false, +}: RevealTextProps) { + const containerRef = useRef(null); + + useEffect(() => { + const el = containerRef.current; + if (!el) return; + + const items = el.querySelectorAll(".reveal-item"); + if (!items.length) return; + + gsap.set(items, { y: "110%", opacity: 0 }); + + const trigger = ScrollTrigger.create({ + trigger: el, + start: "top 88%", + onEnter: (self) => { + gsap.to(items, { + y: "0%", + opacity: 1, + duration, + ease: "power4.out", + stagger: type === "chars" ? 0.02 : 0.04, + delay, + overwrite: "auto", + }); + if (triggerOnce) self.kill(); + }, + onEnterBack: () => { + if (!triggerOnce) { + gsap.to(items, { + y: "0%", + opacity: 1, + duration, + ease: "power4.out", + stagger: type === "chars" ? 0.02 : 0.04, + delay, + overwrite: "auto", + }); + } + }, + onLeave: () => { + if (!triggerOnce) { + gsap.set(items, { y: "110%", opacity: 0 }); + } + }, + onLeaveBack: () => { + if (!triggerOnce) { + gsap.set(items, { y: "110%", opacity: 0 }); + } + }, + }); + + return () => trigger?.kill(); + }, [children, type, delay, duration, triggerOnce]); + + const renderContent = () => { + if (type === "chars") { + return children.split("").map((char, i) => ( + + {char === " " ? "\u00A0" : char} + + )); + } + return children.split(" ").map((word, i) => ( + + {word} + + )); + }; + + return ( +
+ {renderContent()} +
+ ); +} + +/* ============================================================ + 3. Magnetic + Cursor-tracking magnetic pull on elements. + ============================================================ */ +interface MagneticProps { + children: React.ReactElement; + range?: number; + strength?: number; +} + +export function Magnetic({ children, range = 45, strength = 0.35 }: MagneticProps) { + const containerRef = useRef(null); + + useEffect(() => { + const el = containerRef.current; + if (!el) return; + const child = el.firstElementChild as HTMLElement; + if (!child) return; + + const handleMouseMove = (e: MouseEvent) => { + const rect = el.getBoundingClientRect(); + const relX = e.clientX - (rect.left + rect.width / 2); + const relY = e.clientY - (rect.top + rect.height / 2); + const dist = Math.sqrt(relX * relX + relY * relY); + + if (dist < range) { + gsap.to(child, { x: relX * strength, y: relY * strength, ease: "power2.out", duration: 0.4 }); + } else { + gsap.to(child, { x: 0, y: 0, ease: "elastic.out(1.2, 0.4)", duration: 0.8 }); + } + }; + + const handleMouseLeave = () => { + gsap.to(child, { x: 0, y: 0, ease: "elastic.out(1.2, 0.4)", duration: 0.8 }); + }; + + el.addEventListener("mousemove", handleMouseMove); + el.addEventListener("mouseleave", handleMouseLeave); + return () => { + el.removeEventListener("mousemove", handleMouseMove); + el.removeEventListener("mouseleave", handleMouseLeave); + }; + }, [range, strength]); + + return ( +
+ {children} +
+ ); +} + +/* ============================================================ + 4. ShimmerText + Premium shimmering gradient sweep on text. + ============================================================ */ +interface ShimmerTextProps { + children: string; + className?: string; +} + +export function ShimmerText({ children, className = "" }: ShimmerTextProps) { + return ( + + {children} + + ); +} + +/* ============================================================ + 5. StaggerChildren + Wraps children and reveals them with staggered scroll animation. + ============================================================ */ +interface StaggerChildrenProps { + children: React.ReactNode; + stagger?: number; + duration?: number; + yOffset?: number; + className?: string; + triggerOnce?: boolean; +} + +export function StaggerChildren({ + children, + stagger = 0.1, + duration = 0.7, + yOffset = 35, + className = "", + triggerOnce = false, +}: StaggerChildrenProps) { + const containerRef = useRef(null); + + useEffect(() => { + const el = containerRef.current; + if (!el) return; + + const items = el.children; + if (!items.length) return; + + gsap.set(items, { y: yOffset, opacity: 0 }); + + const trigger = ScrollTrigger.create({ + trigger: el, + start: "top 85%", + onEnter: (self) => { + gsap.to(items, { + y: 0, + opacity: 1, + duration, + ease: "power3.out", + stagger, + overwrite: "auto", + }); + if (triggerOnce) self.kill(); + }, + onEnterBack: () => { + if (!triggerOnce) { + gsap.to(items, { + y: 0, + opacity: 1, + duration, + ease: "power3.out", + stagger, + overwrite: "auto", + }); + } + }, + onLeave: () => { + if (!triggerOnce) { + gsap.set(items, { y: yOffset, opacity: 0 }); + } + }, + onLeaveBack: () => { + if (!triggerOnce) { + gsap.set(items, { y: yOffset, opacity: 0 }); + } + }, + }); + + return () => trigger?.kill(); + }, [stagger, duration, yOffset, triggerOnce]); + + return ( +
+ {children} +
+ ); +} + +/* ============================================================ + 6. ScaleReveal + Scales from 0.85 → 1.0 + fades in on scroll. Great for cards/images. + ============================================================ */ +interface ScaleRevealProps { + children: React.ReactNode; + delay?: number; + duration?: number; + className?: string; + triggerOnce?: boolean; +} + +export function ScaleReveal({ + children, + delay = 0, + duration = 0.8, + className = "", + triggerOnce = false, +}: ScaleRevealProps) { + const elementRef = useRef(null); + + useEffect(() => { + const el = elementRef.current; + if (!el) return; + + gsap.set(el, { scale: 0.85, opacity: 0 }); + + const trigger = ScrollTrigger.create({ + trigger: el, + start: "top 88%", + onEnter: (self) => { + gsap.to(el, { + scale: 1, + opacity: 1, + duration, + ease: "power3.out", + delay, + overwrite: "auto", + }); + if (triggerOnce) self.kill(); + }, + onEnterBack: () => { + if (!triggerOnce) { + gsap.to(el, { + scale: 1, + opacity: 1, + duration, + ease: "power3.out", + delay, + overwrite: "auto", + }); + } + }, + onLeave: () => { + if (!triggerOnce) { + gsap.set(el, { scale: 0.85, opacity: 0 }); + } + }, + onLeaveBack: () => { + if (!triggerOnce) { + gsap.set(el, { scale: 0.85, opacity: 0 }); + } + }, + }); + + return () => trigger?.kill(); + }, [delay, duration, triggerOnce]); + + return ( +
+ {children} +
+ ); +} + +/* ============================================================ + 7. SlideReveal + Slides from left or right with smooth reveal. + ============================================================ */ +interface SlideRevealProps { + children: React.ReactNode; + direction?: "left" | "right"; + delay?: number; + duration?: number; + className?: string; + triggerOnce?: boolean; +} + +export function SlideReveal({ + children, + direction = "left", + delay = 0, + duration = 0.9, + className = "", + triggerOnce = false, +}: SlideRevealProps) { + const elementRef = useRef(null); + + useEffect(() => { + const el = elementRef.current; + if (!el) return; + + const xStart = direction === "left" ? -60 : 60; + gsap.set(el, { x: xStart, opacity: 0 }); + + const trigger = ScrollTrigger.create({ + trigger: el, + start: "top 88%", + onEnter: (self) => { + gsap.to(el, { + x: 0, + opacity: 1, + duration, + ease: "power3.out", + delay, + overwrite: "auto", + }); + if (triggerOnce) self.kill(); + }, + onEnterBack: () => { + if (!triggerOnce) { + gsap.to(el, { + x: 0, + opacity: 1, + duration, + ease: "power3.out", + delay, + overwrite: "auto", + }); + } + }, + onLeave: () => { + if (!triggerOnce) { + gsap.set(el, { x: xStart, opacity: 0 }); + } + }, + onLeaveBack: () => { + if (!triggerOnce) { + gsap.set(el, { x: xStart, opacity: 0 }); + } + }, + }); + + return () => trigger?.kill(); + }, [direction, delay, duration, triggerOnce]); + + return ( +
+ {children} +
+ ); +} + +/* ============================================================ + 8. ParallaxSection + Subtle parallax depth on scroll for images/backgrounds. + ============================================================ */ +interface ParallaxSectionProps { + children: React.ReactNode; + speed?: number; + className?: string; +} + +export function ParallaxSection({ + children, + speed = 0.15, + className = "", +}: ParallaxSectionProps) { + const elementRef = useRef(null); + + useEffect(() => { + const el = elementRef.current; + if (!el) return; + + // Check for reduced motion preference + if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return; + + gsap.to(el, { + y: () => -ScrollTrigger.maxScroll(window) * speed * 0.1, + ease: "none", + scrollTrigger: { + trigger: el, + start: "top bottom", + end: "bottom top", + scrub: 1.5, + invalidateOnRefresh: true, + }, + }); + + return () => { + ScrollTrigger.getAll().forEach((t) => { + if (t.trigger === el) t.kill(); + }); + }; + }, [speed]); + + return ( +
+ {children} +
+ ); +} + +/* ============================================================ + 9. CountUp + Animated number counter that triggers on scroll. + ============================================================ */ +interface CountUpProps { + end: number; + start?: number; + duration?: number; + decimals?: number; + suffix?: string; + prefix?: string; + className?: string; + triggerOnce?: boolean; +} + +export function CountUp({ + end, + start = 0, + duration = 2, + decimals = 0, + suffix = "", + prefix = "", + className = "", + triggerOnce = false, +}: CountUpProps) { + const [value, setValue] = useState(start); + const elementRef = useRef(null); + + useEffect(() => { + const el = elementRef.current; + if (!el) return; + + const trigger = ScrollTrigger.create({ + trigger: el, + start: "top 90%", + onEnter: (self) => { + const obj = { val: start }; + gsap.to(obj, { + val: end, + duration, + ease: "power2.out", + onUpdate: () => setValue(obj.val), + }); + if (triggerOnce) self.kill(); + }, + onEnterBack: () => { + if (!triggerOnce) { + const obj = { val: start }; + gsap.to(obj, { + val: end, + duration, + ease: "power2.out", + onUpdate: () => setValue(obj.val), + }); + } + }, + onLeave: () => { + if (!triggerOnce) { + setValue(start); + } + }, + onLeaveBack: () => { + if (!triggerOnce) { + setValue(start); + } + }, + }); + + return () => trigger?.kill(); + }, [start, end, duration, triggerOnce]); + + return ( + + {prefix}{value.toFixed(decimals)}{suffix} + + ); +} + +/* ============================================================ + 10. Tilt3D + Subtle 3D perspective tilt following mouse on hover. + ============================================================ */ +interface Tilt3DProps { + children: React.ReactNode; + intensity?: number; + className?: string; +} + +export function Tilt3D({ + children, + intensity = 8, + className = "", +}: Tilt3DProps) { + const containerRef = useRef(null); + + const handleMouseMove = useCallback((e: React.MouseEvent) => { + const el = containerRef.current; + if (!el) return; + + const rect = el.getBoundingClientRect(); + const x = (e.clientX - rect.left) / rect.width - 0.5; + const y = (e.clientY - rect.top) / rect.height - 0.5; + + gsap.to(el, { + rotateY: x * intensity, + rotateX: -y * intensity, + duration: 0.5, + ease: "power2.out", + }); + }, [intensity]); + + const handleMouseLeave = useCallback(() => { + const el = containerRef.current; + if (!el) return; + + gsap.to(el, { + rotateY: 0, + rotateX: 0, + duration: 0.8, + ease: "elastic.out(1, 0.5)", + }); + }, []); + + return ( +
+
+ {children} +
+
+ ); +} diff --git a/src/app/globals.css b/src/app/globals.css index eb5ad7a..5071d6a 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,6 +1,30 @@ /* Tailwind utilities ONLY — preflight/base and components are disabled so production Elementor/WordPress CSS controls body, typography, colors, spacing, and resets. */ @import "tailwindcss/utilities"; +/* ============================================================ + GLOBAL FONT OVERRIDE — Strict Manrope Default + Forced on all textual and structural elements while protecting + third-party icon fonts (FontAwesome, Fontello, Eicons, etc.). + ============================================================ */ +html { + scroll-behavior: smooth; +} + +*, *::before, *::after { + font-family: var(--font-manrope), "Manrope", system-ui, -apple-system, sans-serif; +} + +*:not(i):not(svg):not(path):not(canvas):not(.fa):not(.fas):not(.far):not(.fab):not([class*="fa-"]):not([class*="eicon-"]):not([class*="fontello"]) { + font-family: var(--font-manrope), "Manrope", system-ui, -apple-system, sans-serif !important; +} + +/* Smoother text rendering */ +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; +} + @media (min-width: 1025px) { #masthead .elementor-element.elementor-element-466de1b { top: 5px !important; @@ -10,7 +34,7 @@ #masthead .elementor-element.elementor-element-e052838 { margin-left: 1.4% !important; border-radius: 28px !important; - background: rgba(31, 31, 31, 0.82) !important; + background: transparent !important; } #masthead .elementor-element.elementor-element-d681ece { @@ -542,7 +566,7 @@ margin: 0 !important; letter-spacing: -0.5px !important; text-transform: none !important; - font-family: 'Inter', system-ui, -apple-system, sans-serif !important; + font-family: var(--font-manrope), 'Manrope', system-ui, -apple-system, sans-serif !important; } /* Hover state content wrapper */ @@ -589,7 +613,7 @@ margin: 0 !important; font-weight: 600 !important; letter-spacing: -0.2px !important; - font-family: 'Inter', system-ui, -apple-system, sans-serif !important; + font-family: var(--font-manrope), 'Manrope', system-ui, -apple-system, sans-serif !important; } .industry-card-section-title { @@ -600,7 +624,7 @@ margin-bottom: 10px !important; text-transform: uppercase !important; opacity: 0.9 !important; - font-family: 'Inter', system-ui, -apple-system, sans-serif !important; + font-family: var(--font-manrope), 'Manrope', system-ui, -apple-system, sans-serif !important; } .industry-card-list { @@ -621,7 +645,7 @@ opacity: 0; transform: translateY(5px); transition: opacity 0.3s cubic-bezier(0.2, 0.8, 0.2, 1), transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1); - font-family: 'Inter', system-ui, -apple-system, sans-serif !important; + font-family: var(--font-manrope), 'Manrope', system-ui, -apple-system, sans-serif !important; } .industry-card-link:hover .industry-card-list-item { @@ -681,7 +705,7 @@ font-size: 24px !important; font-weight: 700 !important; margin: 0 !important; - font-family: 'Inter', system-ui, -apple-system, sans-serif !important; + font-family: var(--font-manrope), 'Manrope', system-ui, -apple-system, sans-serif !important; text-transform: none !important; letter-spacing: -0.5px !important; } @@ -690,3 +714,88 @@ padding-left: clamp(20px, 4vw, 50px) !important; padding-right: clamp(20px, 4vw, 50px) !important; } + +/* Custom premium animations keyframes */ +@keyframes shimmer-sweep { + 0% { + background-position: 200% center; + } + 100% { + background-position: -200% center; + } +} + +/* ============================================================ + CUSTOM STANDARDIZED HERO STYLES + Ensures all subpage hero sections have the exact same height, + width, framing margin/padding, and border-radius as the + Home Page content slider / hero container. + ============================================================ */ + +/* Outer parent layout wrapper mirroring .elementor-element-741f56c */ +.custom-standard-hero-container { + width: 100% !important; + max-width: 100% !important; + margin: 0 auto !important; + box-sizing: border-box !important; + padding: 20px 20px 20px 20px !important; /* Top, Right, Bottom, Left margin framing */ + display: flex !important; + flex-direction: column !important; + position: relative !important; +} + +/* Inner hero card mirroring .owl-stage-outer border-radius and slider dimensions */ +.custom-standard-hero-card { + position: relative !important; + width: 100% !important; + height: 800px !important; + min-height: 800px !important; + border-radius: 25px !important; + overflow: hidden !important; + box-shadow: none !important; + margin: 0 !important; + display: flex !important; + align-items: center !important; + justify-content: center !important; + background-size: cover !important; + background-position: center !important; + background-repeat: no-repeat !important; +} + +/* Dark background tint overlay to match home sliders and protect text contrast */ +.custom-standard-hero-card::before { + content: '' !important; + position: absolute !important; + inset: 0 !important; + background: linear-gradient(to bottom, rgba(0, 0, 0, 0.45) 0%, rgba(0, 0, 0, 0.78) 55%, rgba(0, 0, 0, 0.95) 100%) !important; + z-index: 1 !important; +} + +.custom-standard-hero-card > * { + position: relative !important; + z-index: 2 !important; +} + +/* Responsive constraints to keep all heroes matching the home page carousel perfectly */ +@media (max-width: 1024px) { + .custom-standard-hero-container { + padding: 10px 10px 10px 10px !important; + } + .custom-standard-hero-card { + height: 620px !important; + min-height: 620px !important; + border-radius: 25px !important; + } +} + +@media (max-width: 767px) { + .custom-standard-hero-container { + padding: 10px 10px 10px 10px !important; + } + .custom-standard-hero-card { + height: 560px !important; + min-height: 560px !important; + border-radius: 22px !important; + } +} + diff --git a/src/app/how-it-works/page.tsx b/src/app/how-it-works/page.tsx index 4d99575..1c72f96 100644 --- a/src/app/how-it-works/page.tsx +++ b/src/app/how-it-works/page.tsx @@ -1,6 +1,7 @@ import React from "react"; import HowItWorksHero from "../../components/sections/HowItWorksHero"; import Miles3 from "../../components/sections/Miles3"; +import WhyChooseDoormile from "../../components/sections/WhyChooseDoormile"; import TheDoormileWay from "../../components/sections/TheDoormileWay"; export const metadata = { @@ -16,6 +17,7 @@ export default function HowItWorksPage() {
+
diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 4b48e02..ab15e66 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import { Manrope } from "next/font/google"; +import { Manrope, Space_Grotesk } from "next/font/google"; import "./globals.css"; import Header from "@/components/layout/Header"; import Footer from "@/components/layout/Footer"; @@ -7,11 +7,18 @@ import BodyClasses from "@/components/layout/BodyClasses"; import BodyOverlay from "@/components/layout/BodyOverlay"; import { HeaderUIProvider } from "@/components/layout/HeaderUIProvider"; import { SHARED_BODY_CLASSES } from "@/lib/bodyClasses"; +import AnimationProvider from "@/animations/AnimationProvider"; const manrope = Manrope({ subsets: ["latin"], variable: "--font-manrope", - weight: ["300", "400", "500", "600", "700", "800"], + weight: ["200", "300", "400", "500", "600", "700", "800"], +}); + +const spaceGrotesk = Space_Grotesk({ + subsets: ["latin"], + variable: "--font-space-grotesk", + weight: ["400", "500", "600", "700"], }); export const metadata: Metadata = { @@ -29,7 +36,7 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - + {/* FontAwesome icons */} - - -
-
-
- {children} -
+ + + +
+
+
+ {children} +
+
-
- + + ); diff --git a/src/components/icons/FirstMileIcon.tsx b/src/components/icons/FirstMileIcon.tsx index c2b0984..4712164 100644 --- a/src/components/icons/FirstMileIcon.tsx +++ b/src/components/icons/FirstMileIcon.tsx @@ -2,98 +2,91 @@ import React from "react"; export default function FirstMileIcon(props: React.SVGProps) { return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/src/components/icons/LastMileIcon.tsx b/src/components/icons/LastMileIcon.tsx index acccd84..d8e184c 100644 --- a/src/components/icons/LastMileIcon.tsx +++ b/src/components/icons/LastMileIcon.tsx @@ -2,128 +2,123 @@ import React from "react"; export default function LastMileIcon(props: React.SVGProps) { return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/src/components/icons/MidMileIcon.tsx b/src/components/icons/MidMileIcon.tsx index 6331362..a994555 100644 --- a/src/components/icons/MidMileIcon.tsx +++ b/src/components/icons/MidMileIcon.tsx @@ -2,136 +2,214 @@ import React from "react"; export default function MidMileIcon(props: React.SVGProps) { return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); } diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx index cc061df..2388d6f 100644 --- a/src/components/layout/Footer.tsx +++ b/src/components/layout/Footer.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from "react"; import Link from "next/link"; +import { ScrollReveal } from "@/animations/Reveal"; export default function Footer() { const socialIconSpacing = { @@ -88,9 +89,11 @@ export default function Footer() {
-

- We are always ready to help you and answer your questions -

+ +

+ We are always ready to help you and answer your questions +

+
@@ -301,9 +304,11 @@ export default function Footer() {
-

- Delivered on time with no hassle. -

+ +

+ Delivered on time with no hassle. +

+
diff --git a/src/components/sections/AboutHero.tsx b/src/components/sections/AboutHero.tsx index 60e6a5b..cfd722f 100644 --- a/src/components/sections/AboutHero.tsx +++ b/src/components/sections/AboutHero.tsx @@ -4,93 +4,47 @@ export default function AboutHero() { return ( <>