updates
This commit is contained in:
11 files changed
+272
-574
No files matched your search
@@ -1,74 +0,0 @@
|
||||
import re
|
||||
|
||||
with open("src/pages/Home.tsx", "r") as f:
|
||||
content = f.read()
|
||||
|
||||
# I will write a simple string splitting mechanism based on `{block_name.length > 0 && (`
|
||||
|
||||
blocks = [
|
||||
("hero", "{hero && ("),
|
||||
("marquee", "{marquee.length > 0 && ("),
|
||||
("whatWeDo", "{whatWeDo.length > 0 && ("),
|
||||
("partners", "{partners.length > 0 && ("),
|
||||
("services", "{services.length > 0 && ("),
|
||||
("testimonials", "{testimonials.length > 0 && ("),
|
||||
("sisterCompanies", "{sisterCompanies.length > 0 && ("),
|
||||
("cta", "<section className=\"relative z-10 border-y border-border bg-mist/60\">")
|
||||
]
|
||||
|
||||
# We need to split the content accurately
|
||||
def get_block_end(start_idx, is_cta=False):
|
||||
if is_cta:
|
||||
# CTA goes till the end of the div
|
||||
return content.find(" </div>\n );\n}", start_idx)
|
||||
|
||||
open_braces = 0
|
||||
in_block = False
|
||||
|
||||
for i in range(start_idx, len(content)):
|
||||
if content[i] == '{':
|
||||
open_braces += 1
|
||||
in_block = True
|
||||
elif content[i] == '}':
|
||||
open_braces -= 1
|
||||
|
||||
if in_block and open_braces == 0:
|
||||
return i + 1
|
||||
|
||||
return -1
|
||||
|
||||
extracted = {}
|
||||
for name, token in blocks:
|
||||
idx = content.find(token)
|
||||
if idx != -1:
|
||||
end_idx = get_block_end(idx, is_cta=(name == "cta"))
|
||||
extracted[name] = content[idx:end_idx].strip()
|
||||
|
||||
# Now reconstruct the body
|
||||
new_order = [
|
||||
"hero",
|
||||
"marquee",
|
||||
"partners",
|
||||
"whatWeDo",
|
||||
"services",
|
||||
"sisterCompanies",
|
||||
"testimonials",
|
||||
"cta"
|
||||
]
|
||||
|
||||
body_content = ""
|
||||
for name in new_order:
|
||||
if name in extracted:
|
||||
body_content += "\n " + extracted[name] + "\n"
|
||||
|
||||
# Replace the old body with the new body
|
||||
# The body starts after `<div className="relative">\n <LightField />`
|
||||
start_body = content.find("<LightField />") + len("<LightField />")
|
||||
end_body = content.find(" </div>\n );\n}")
|
||||
|
||||
new_content = content[:start_body] + "\n" + body_content + "\n" + content[end_body:]
|
||||
|
||||
with open("src/pages/Home.tsx", "w") as f:
|
||||
f.write(new_content)
|
||||
|
||||
print("Reorganized Home.tsx")
|
||||
@@ -1,7 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { AnimatePresence, motion } from "framer-motion";
|
||||
|
||||
const COLUMNS = 4;
|
||||
import { useEffect, useRef } from "react";
|
||||
import { animate, motion, type AnimationPlaybackControls, useMotionValue } from "framer-motion";
|
||||
|
||||
interface Partner {
|
||||
id: number;
|
||||
@@ -9,65 +7,78 @@ interface Partner {
|
||||
logo: string | null;
|
||||
}
|
||||
|
||||
const LOGO_WIDTH = 220; // fixed width per logo block
|
||||
const GAP = 24; // gap between logos
|
||||
|
||||
export function ClientsMarquee({
|
||||
partners,
|
||||
}: {
|
||||
partners: Partner[];
|
||||
}) {
|
||||
const [step, setStep] = useState(0);
|
||||
const x = useMotionValue(0);
|
||||
const controls = useRef<AnimationPlaybackControls | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (partners.length <= COLUMNS) return;
|
||||
const id = setInterval(() => {
|
||||
setStep((s) => (s + 1) % partners.length);
|
||||
}, 2500);
|
||||
return () => clearInterval(id);
|
||||
}, [partners.length]);
|
||||
if (partners.length === 0) return;
|
||||
|
||||
// We duplicate the array to ensure smooth infinite scroll
|
||||
const setWidth = partners.length * (LOGO_WIDTH + GAP);
|
||||
|
||||
controls.current = animate(x, [-setWidth, 0], {
|
||||
duration: 30, // adjust speed
|
||||
ease: "linear",
|
||||
repeat: Infinity,
|
||||
});
|
||||
|
||||
return () => {
|
||||
controls.current?.stop();
|
||||
};
|
||||
}, [partners.length, x]);
|
||||
|
||||
if (partners.length === 0) return null;
|
||||
|
||||
const visible: (Partner | null)[] = [];
|
||||
for (let i = 0; i < COLUMNS; i++) {
|
||||
visible.push(partners[(step + i) % partners.length]);
|
||||
}
|
||||
// Duplicate items enough times to fill the screen seamlessly
|
||||
const items = [...partners, ...partners, ...partners, ...partners, ...partners, ...partners];
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-2 gap-x-6 gap-y-5 sm:grid-cols-4">
|
||||
{visible.map((client, i) => (
|
||||
<div key={`slot-${i}`} className="relative flex h-40 items-center justify-center overflow-hidden">
|
||||
<AnimatePresence initial={false} mode="popLayout">
|
||||
{client && (
|
||||
<motion.div
|
||||
key={client.id}
|
||||
layoutId={`client-slot-${i}`}
|
||||
className="flex flex-col items-center justify-center text-center gap-4 w-full"
|
||||
initial={{ opacity: 0, filter: "blur(8px)", scale: 0.92 }}
|
||||
animate={{ opacity: 1, filter: "blur(0px)", scale: 1 }}
|
||||
exit={{ opacity: 0, filter: "blur(8px)", scale: 1.08 }}
|
||||
transition={{
|
||||
duration: 0.4,
|
||||
ease: "easeInOut",
|
||||
}}
|
||||
>
|
||||
<div className="grid h-24 w-24 shrink-0 place-items-center overflow-hidden rounded-full border border-border bg-mist shadow-sm">
|
||||
{client.logo ? (
|
||||
<img
|
||||
src={client.logo}
|
||||
alt={client.name}
|
||||
className="h-full w-full object-contain p-2"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-2xl font-bold text-accent">{client.name.charAt(0)}</span>
|
||||
)}
|
||||
</div>
|
||||
<span className="truncate w-full text-xs font-bold uppercase leading-snug tracking-wide text-foreground">
|
||||
{client.name}
|
||||
</span>
|
||||
</motion.div>
|
||||
<div
|
||||
className="relative flex w-full overflow-hidden mask-edges-clients"
|
||||
onMouseEnter={() => controls.current?.pause()}
|
||||
onMouseLeave={() => controls.current?.play()}
|
||||
>
|
||||
<motion.div
|
||||
style={{ x, gap: GAP }}
|
||||
className="flex items-center w-max"
|
||||
>
|
||||
{items.map((client, i) => (
|
||||
<div
|
||||
key={`${client.id}-${i}`}
|
||||
className="group relative flex h-32 w-[220px] shrink-0 items-center justify-center transition-all"
|
||||
>
|
||||
{client.logo ? (
|
||||
<img
|
||||
src={client.logo}
|
||||
alt={client.name}
|
||||
className="h-16 w-32 object-contain filter grayscale transition-all duration-300 group-hover:grayscale-0 group-hover:scale-110 opacity-70 group-hover:opacity-100"
|
||||
/>
|
||||
) : (
|
||||
<span className="text-xl font-bold tracking-widest text-muted-foreground transition-colors group-hover:text-brand">
|
||||
{client.name}
|
||||
</span>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</motion.div>
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
.mask-edges-clients {
|
||||
mask-image: linear-gradient(to right, transparent, black 15%, black 85%, transparent);
|
||||
-webkit-mask-image: linear-gradient(to right, transparent, black 15%, black 85%, transparent);
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { ArrowUp, MessageSquare } from "lucide-react";
|
||||
import { useApi } from "@/hooks/useApi";
|
||||
|
||||
interface Organization {
|
||||
whatsapp_no: string;
|
||||
}
|
||||
|
||||
export function FloatingActionButtons() {
|
||||
const [isVisible, setIsVisible] = useState(false);
|
||||
const { data: org } = useApi<Organization>("org/organization");
|
||||
|
||||
// Show button when page is scrolled down
|
||||
useEffect(() => {
|
||||
const toggleVisibility = () => {
|
||||
if (window.scrollY > 300) {
|
||||
setIsVisible(true);
|
||||
} else {
|
||||
setIsVisible(false);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", toggleVisibility);
|
||||
return () => window.removeEventListener("scroll", toggleVisibility);
|
||||
}, []);
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: "smooth",
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed bottom-6 right-6 z-50 flex flex-col gap-3">
|
||||
{/* WhatsApp Button */}
|
||||
{org?.whatsapp_no && (
|
||||
<a
|
||||
href={`https://wa.me/${org.whatsapp_no}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="group flex h-12 w-12 items-center justify-center rounded-full bg-[#25D366] text-white shadow-lg transition-all hover:scale-110 hover:shadow-xl hover:shadow-[#25D366]/20"
|
||||
aria-label="Chat on WhatsApp"
|
||||
>
|
||||
<MessageSquare className="h-6 w-6" />
|
||||
</a>
|
||||
)}
|
||||
|
||||
{/* Scroll to Top Button */}
|
||||
<button
|
||||
onClick={scrollToTop}
|
||||
className={`group flex h-12 w-12 items-center justify-center rounded-full border border-border bg-card shadow-lg transition-all hover:scale-110 hover:border-brand/50 hover:text-brand ${
|
||||
isVisible ? "translate-y-0 opacity-100" : "pointer-events-none translate-y-10 opacity-0"
|
||||
}`}
|
||||
aria-label="Scroll to top"
|
||||
>
|
||||
<ArrowUp className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export function Header() {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<header className="sticky top-0 z-40 border-b border-border bg-background/95 backdrop-blur-md">
|
||||
<header className="fixed top-0 inset-x-0 w-full z-50 border-b border-border bg-background/95 backdrop-blur-md transition-all duration-300">
|
||||
<div className="mx-auto max-w-7xl px-4 sm:px-6">
|
||||
<nav className="flex h-20 items-center justify-between gap-4">
|
||||
<Link to="/" className="flex items-center gap-3">
|
||||
@@ -61,7 +61,7 @@ export function Header() {
|
||||
<div className="flex items-center gap-2">
|
||||
<Link
|
||||
to="/contact"
|
||||
className="hidden bg-ink px-5 py-2.5 text-sm font-semibold text-primary-foreground transition-colors hover:bg-brand sm:inline-block"
|
||||
className="hidden rounded-full bg-brand px-6 py-2.5 text-sm font-bold text-primary-foreground shadow-sm transition-all hover:scale-105 hover:bg-brand/90 sm:inline-block"
|
||||
>
|
||||
Get a Quote
|
||||
</Link>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { animate, motion, type AnimationPlaybackControls, useMotionValue } from
|
||||
import { Star } from "lucide-react";
|
||||
|
||||
const CARD_WIDTH = 480;
|
||||
const GAP = 0;
|
||||
const GAP = 24;
|
||||
|
||||
interface Testimonial {
|
||||
id: number;
|
||||
@@ -54,7 +54,7 @@ export function TestimonialsMarquee({
|
||||
const items = [...testimonials, ...testimonials, ...testimonials, ...testimonials, ...testimonials];
|
||||
|
||||
return (
|
||||
<section className="relative z-10 overflow-hidden bg-[#0A0A0A] py-24 text-primary-foreground border-y border-white/10">
|
||||
<section className="relative z-10 overflow-hidden bg-mist/30 py-24 text-foreground border-y border-border/50">
|
||||
<div className="mx-auto mb-16 max-w-7xl px-6">
|
||||
<div className="flex flex-col justify-between gap-5 md:flex-row md:items-end">
|
||||
<div>
|
||||
@@ -114,7 +114,7 @@ export function TestimonialsMarquee({
|
||||
function TestimonialCard({ t }: { t: Testimonial }) {
|
||||
return (
|
||||
<article
|
||||
className="flex min-h-[260px] w-[480px] shrink-0 flex-col justify-between border border-white/10 bg-[#0F0F0F] p-8 transition-colors hover:bg-[#141414] -ml-px -mt-px"
|
||||
className="flex min-h-[260px] w-[480px] shrink-0 flex-col justify-between border border-border/50 bg-card p-8 shadow-sm transition-colors hover:bg-muted/30 m-3 rounded-3xl ml-4"
|
||||
>
|
||||
<div>
|
||||
<div className="flex items-center gap-2 mb-6 text-accent">
|
||||
@@ -122,21 +122,21 @@ function TestimonialCard({ t }: { t: Testimonial }) {
|
||||
{[0, 1, 2, 3, 4].map((star) => <Star key={star} className="h-4 w-4 fill-accent" aria-hidden="true" />)}
|
||||
</div>
|
||||
</div>
|
||||
<blockquote className="text-[17px] font-medium leading-[1.6] tracking-tight text-white/90">
|
||||
<blockquote className="text-[17px] font-medium leading-[1.6] tracking-tight text-foreground">
|
||||
{t.testimonial}
|
||||
</blockquote>
|
||||
</div>
|
||||
<footer className="mt-8 flex items-center gap-4">
|
||||
{t.image ? (
|
||||
<img src={t.image} alt={t.name} className="h-11 w-11 rounded-lg object-cover ring-1 ring-white/10" />
|
||||
<img src={t.image} alt={t.name} className="h-11 w-11 rounded-lg object-cover ring-1 ring-border/50" />
|
||||
) : (
|
||||
<div className="grid h-11 w-11 place-items-center rounded-lg bg-white/5 ring-1 ring-white/10 text-lg font-bold text-white/80">
|
||||
<div className="grid h-11 w-11 place-items-center rounded-lg bg-muted ring-1 ring-border/50 text-lg font-bold text-muted-foreground">
|
||||
{t.name.charAt(0)}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<p className="text-[15px] font-semibold text-white">{t.name}</p>
|
||||
<p className="text-[13px] text-white/50">{t.organization}</p>
|
||||
<p className="text-[15px] font-semibold text-foreground">{t.name}</p>
|
||||
<p className="text-[13px] text-muted-foreground">{t.organization}</p>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
|
||||
@@ -70,21 +70,21 @@ function About() {
|
||||
<div className="mx-auto grid max-w-7xl items-center gap-12 px-6 pb-24 lg:grid-cols-12">
|
||||
<div className="lg:col-span-5">
|
||||
<Parallax speed={0.1}>
|
||||
<div className="border border-border bg-mist p-3 shadow-glass">
|
||||
<div className="group overflow-hidden rounded-3xl border border-border/50 bg-card p-2 shadow-2xl transition-all hover:shadow-brand/10">
|
||||
<img
|
||||
src={about.cover_image }
|
||||
loading="lazy"
|
||||
width={1024}
|
||||
height={1152}
|
||||
alt={about.title}
|
||||
className="aspect-[4/5] w-full object-cover"
|
||||
className="aspect-[4/5] w-full rounded-2xl object-cover transition duration-700 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
</Parallax>
|
||||
</div>
|
||||
<div className="lg:col-span-7">
|
||||
<Reveal>
|
||||
<h2 className="text-3xl font-bold md:text-4xl">{about.title}</h2>
|
||||
<h2 className="text-4xl font-black tracking-tight md:text-5xl">{about.title}</h2>
|
||||
<div className="mt-5 text-muted-foreground prose prose-invert" dangerouslySetInnerHTML={{ __html: about.about_us }} />
|
||||
</Reveal>
|
||||
</div>
|
||||
@@ -96,8 +96,8 @@ function About() {
|
||||
<section className="relative z-10">
|
||||
<div className="mx-auto max-w-7xl px-6 pb-24">
|
||||
<Reveal>
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.2em] text-brand">Vision</p>
|
||||
<h2 className="mt-3 text-3xl font-bold md:text-4xl">{vision.title}</h2>
|
||||
<p className="inline-flex items-center gap-2 rounded-full bg-brand/10 px-3 py-1 text-xs font-bold uppercase tracking-widest text-brand">Vision</p>
|
||||
<h2 className="mt-3 text-4xl font-black tracking-tight md:text-5xl">{vision.title}</h2>
|
||||
</Reveal>
|
||||
<div className="mt-10 grid items-center gap-10 lg:grid-cols-12">
|
||||
<div className="lg:col-span-7">
|
||||
@@ -130,8 +130,8 @@ function About() {
|
||||
<section className="relative z-10">
|
||||
<div className="mx-auto max-w-7xl px-6 pb-24">
|
||||
<Reveal>
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.2em] text-brand">Mission</p>
|
||||
<h2 className="mt-3 text-3xl font-bold md:text-4xl">{mision.title}</h2>
|
||||
<p className="inline-flex items-center gap-2 rounded-full bg-brand/10 px-3 py-1 text-xs font-bold uppercase tracking-widest text-brand">Mission</p>
|
||||
<h2 className="mt-3 text-4xl font-black tracking-tight md:text-5xl">{mision.title}</h2>
|
||||
</Reveal>
|
||||
<div className="mt-10 grid items-center gap-10 lg:grid-cols-12">
|
||||
{mision.subtitle && (
|
||||
@@ -164,8 +164,8 @@ function About() {
|
||||
<section className="relative z-10">
|
||||
<div className="mx-auto max-w-7xl px-6 pb-24">
|
||||
<Reveal>
|
||||
<p className="text-sm font-semibold uppercase tracking-[0.2em] text-brand">Values</p>
|
||||
<h2 className="mt-3 text-3xl font-bold md:text-4xl">What we stand for</h2>
|
||||
<p className="inline-flex items-center gap-2 rounded-full bg-brand/10 px-3 py-1 text-xs font-bold uppercase tracking-widest text-brand">Values</p>
|
||||
<h2 className="mt-3 text-4xl font-black tracking-tight md:text-5xl">What we stand for</h2>
|
||||
<p className="mt-4 max-w-2xl text-muted-foreground">
|
||||
The principles behind every machine we export, manufacture and supply.
|
||||
</p>
|
||||
|
||||
+104
-94
@@ -1,17 +1,5 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import {
|
||||
ArrowRight,
|
||||
Building2,
|
||||
Factory,
|
||||
Headphones,
|
||||
Leaf,
|
||||
Quote,
|
||||
Settings2,
|
||||
ShieldCheck,
|
||||
Star,
|
||||
Wrench,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { Star, ArrowRight } from "lucide-react";
|
||||
import { LightField, Parallax, Reveal } from "@/components/site/Parallax";
|
||||
import { OrbitalVisualization } from "@/components/site/OrbitalVisualization";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -19,7 +7,6 @@ import { ClientsMarquee } from "@/components/site/ClientsMarquee";
|
||||
import { TestimonialsMarquee } from "@/components/site/TestimonialsMarquee";
|
||||
import { useApi } from "@/hooks/useApi";
|
||||
|
||||
|
||||
export default Index;
|
||||
|
||||
function Index() {
|
||||
@@ -42,36 +29,40 @@ function Index() {
|
||||
const testimonials = testimonialsList || [];
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="relative bg-background">
|
||||
<LightField />
|
||||
|
||||
{hero && (
|
||||
<section className="relative z-10 overflow-hidden">
|
||||
<div className="mx-auto grid min-h-[720px] max-w-7xl items-center gap-12 px-6 py-16 lg:grid-cols-12 lg:py-24">
|
||||
<div className="lg:col-span-6">
|
||||
<div className="rise flex items-center gap-4 text-xs font-bold uppercase text-brand">
|
||||
<span className="h-px w-12 bg-brand" /> {hero.eyebrow || "Est. 2020 · Kohalpur, Banke"}
|
||||
<div className="rise inline-flex items-center gap-3 rounded-full bg-brand/10 px-4 py-1.5 text-xs font-bold uppercase tracking-wider text-brand">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-brand opacity-75"></span>
|
||||
<span className="relative inline-flex h-2 w-2 rounded-full bg-brand"></span>
|
||||
</span>
|
||||
{hero.eyebrow || "Est. 2020 · Kohalpur, Banke"}
|
||||
</div>
|
||||
<h1 className="rise-2 mt-8 text-5xl leading-[1.02] md:text-7xl">
|
||||
<h1 className="rise-2 mt-8 text-5xl font-black leading-[1.05] tracking-tight md:text-7xl">
|
||||
{hero.title || "Machinery built for"} <span className="text-gradient">{hero.highlight || "Nepal."}</span>
|
||||
</h1>
|
||||
<p className="rise-3 mt-7 max-w-xl text-xl leading-relaxed text-muted-foreground">
|
||||
<p className="rise-3 mt-7 max-w-xl text-lg md:text-xl leading-relaxed text-muted-foreground">
|
||||
{hero.description || "Reliable, innovative machinery solutions — exporting, manufacturing, distributing and supplying small, medium and commercial scale equipment across the nation."}
|
||||
</p>
|
||||
<div className="rise-3 mt-8 flex flex-wrap gap-4">
|
||||
<Button asChild size="lg" className="rounded-none bg-brand px-7 shadow-none">
|
||||
<div className="rise-3 mt-10 flex flex-wrap gap-4">
|
||||
<Button asChild size="lg" className="h-14 rounded-full bg-brand px-8 text-base shadow-lg shadow-brand/20 transition-all hover:scale-105 hover:bg-brand/90">
|
||||
<Link to="/products">Explore Products</Link>
|
||||
</Button>
|
||||
<Button asChild size="lg" variant="outline" className="rounded-none bg-background px-7">
|
||||
<Button asChild size="lg" variant="outline" className="h-14 rounded-full bg-background px-8 text-base border-border/50 hover:bg-muted transition-all hover:scale-105">
|
||||
<Link to="/projects-events">View Projects</Link>
|
||||
</Button>
|
||||
</div>
|
||||
{stats.length > 0 && (
|
||||
<div className="rise-3 mt-14 grid max-w-xl grid-cols-3 gap-5 border-t border-border pt-7">
|
||||
<div className="rise-3 mt-16 grid max-w-xl grid-cols-3 gap-6 border-t border-border/40 pt-8">
|
||||
{stats.map((st) => (
|
||||
<div key={st.id}>
|
||||
<p className="font-display text-3xl font-bold">{st.stats}{st.title.includes('delivery') ? '%' : '+'}</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{st.title}</p>
|
||||
<div key={st.id} className="group">
|
||||
<p className="font-display text-3xl font-black tracking-tight text-foreground transition-colors group-hover:text-brand">{st.stats}{st.title.includes('delivery') ? '%' : '+'}</p>
|
||||
<p className="mt-1 text-sm font-medium text-muted-foreground">{st.title}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
@@ -79,23 +70,22 @@ function Index() {
|
||||
</div>
|
||||
|
||||
<div className="relative min-h-[480px] lg:col-span-6">
|
||||
<div className="absolute -right-6 top-0 h-20 w-32 border-r border-t border-brand/40" aria-hidden="true" />
|
||||
<div className="absolute inset-4 right-0 md:left-10">
|
||||
<div className="absolute inset-0 right-0 md:left-10">
|
||||
<Parallax speed={0.06}>
|
||||
<div className="relative overflow-hidden border border-border bg-mist p-3 shadow-glass">
|
||||
<div className="group relative overflow-hidden rounded-3xl border border-border/40 bg-card p-2 shadow-2xl transition-all hover:shadow-brand/10">
|
||||
<img
|
||||
src={hero.image }
|
||||
src={hero.image}
|
||||
width={1024}
|
||||
height={1280}
|
||||
alt="Featured equipment"
|
||||
className="h-[440px] w-full object-cover md:h-[540px]"
|
||||
className="h-[440px] w-full rounded-2xl object-cover transition duration-700 group-hover:scale-105 md:h-[580px]"
|
||||
/>
|
||||
<div className="absolute inset-x-3 bottom-3 flex items-end justify-between bg-ink/90 p-5 text-primary-foreground">
|
||||
<div className="absolute inset-x-4 bottom-4 flex items-end justify-between rounded-xl bg-ink/90 p-6 text-primary-foreground backdrop-blur-md">
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase text-primary-foreground/60">Featured equipment</p>
|
||||
<p className="mt-1 font-display text-lg">{hero.featured_text || "Commercial systems"}</p>
|
||||
<p className="text-xs font-bold uppercase tracking-widest text-primary-foreground/60">Featured equipment</p>
|
||||
<p className="mt-2 font-display text-xl font-bold">{hero.featured_text || "Commercial systems"}</p>
|
||||
</div>
|
||||
<span className="font-display text-3xl text-accent">01</span>
|
||||
<span className="font-display text-4xl font-black text-accent opacity-50">01</span>
|
||||
</div>
|
||||
</div>
|
||||
</Parallax>
|
||||
@@ -106,17 +96,17 @@ function Index() {
|
||||
)}
|
||||
|
||||
{marquee.length > 0 && (
|
||||
<div className="relative z-10 flex overflow-hidden border-y border-border bg-ink py-6">
|
||||
<div className="relative z-10 flex overflow-hidden border-y border-border bg-card py-6">
|
||||
<div className="animate-marquee flex whitespace-nowrap">
|
||||
{[0, 1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex items-center gap-12 pr-12 text-xl md:text-2xl font-black uppercase tracking-[0.1em] text-white"
|
||||
className="flex items-center gap-12 pr-12 text-xl font-black uppercase tracking-[0.1em] text-foreground md:text-2xl"
|
||||
>
|
||||
{marquee.map((m) => (
|
||||
<span key={m.id} className="flex items-center gap-12">
|
||||
<span>{m.title}</span>
|
||||
<Star className="h-6 w-6 text-brand fill-brand" />
|
||||
<Star className="h-6 w-6 fill-brand text-brand" />
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
@@ -126,13 +116,15 @@ function Index() {
|
||||
)}
|
||||
|
||||
{partners.length > 0 && (
|
||||
<section className="relative z-10 overflow-hidden border-y border-border bg-mist/60">
|
||||
<section className="relative z-10 overflow-hidden border-b border-border bg-mist/30">
|
||||
<div className="mx-auto grid max-w-7xl items-center gap-14 px-6 py-24 lg:grid-cols-12">
|
||||
<div className="lg:col-span-5">
|
||||
<Reveal>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">Our clients</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">Built around the people who produce.</h2>
|
||||
<p className="mt-5 max-w-lg leading-relaxed text-muted-foreground">
|
||||
<div className="inline-flex items-center gap-2 rounded-full bg-accent/10 px-3 py-1 text-xs font-bold uppercase tracking-widest text-accent">
|
||||
Our clients
|
||||
</div>
|
||||
<h2 className="mt-6 text-4xl font-black tracking-tight md:text-5xl">Built around the people who produce.</h2>
|
||||
<p className="mt-6 max-w-lg text-lg leading-relaxed text-muted-foreground">
|
||||
We work with ambitious enterprises across agriculture, food production, construction,
|
||||
utilities and commercial manufacturing.
|
||||
</p>
|
||||
@@ -140,8 +132,10 @@ function Index() {
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-7">
|
||||
<Parallax speed={0.06}>
|
||||
<ClientsMarquee partners={partners} />
|
||||
<Parallax speed={0.04}>
|
||||
<div>
|
||||
<ClientsMarquee partners={partners} />
|
||||
</div>
|
||||
</Parallax>
|
||||
</div>
|
||||
</div>
|
||||
@@ -150,38 +144,39 @@ function Index() {
|
||||
|
||||
{whatWeDo.length > 0 && (
|
||||
<section className="relative z-10">
|
||||
<div className="mx-auto max-w-7xl px-6 py-24">
|
||||
<div className="mb-12 flex items-end justify-between">
|
||||
<div className="mx-auto max-w-7xl px-6 py-32">
|
||||
<div className="mb-16 flex flex-col items-start justify-between gap-6 md:flex-row md:items-end">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">
|
||||
<div className="inline-flex items-center gap-2 rounded-full bg-accent/10 px-3 py-1 text-xs font-bold uppercase tracking-widest text-accent">
|
||||
What we do
|
||||
</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">Machinery, end to end</h2>
|
||||
</div>
|
||||
<h2 className="mt-6 text-4xl font-black tracking-tight md:text-5xl">Machinery, end to end</h2>
|
||||
</div>
|
||||
<Link
|
||||
to="/products"
|
||||
className="hidden text-sm font-semibold text-brand hover:underline sm:inline"
|
||||
>
|
||||
See all products →
|
||||
</Link>
|
||||
<Button asChild variant="ghost" className="rounded-full font-bold text-brand hover:bg-brand/10 hover:text-brand">
|
||||
<Link to="/products" className="flex items-center gap-2">
|
||||
See all products <ArrowRight className="h-4 w-4" />
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
<div className="grid gap-8 md:grid-cols-3">
|
||||
{whatWeDo.map((c, i) => (
|
||||
<Reveal key={c.id} delay={i * 90}>
|
||||
<div className="group h-full overflow-hidden border border-border bg-background transition hover:border-brand">
|
||||
<div className="group flex h-full flex-col overflow-hidden rounded-3xl border border-border/50 bg-card shadow-lg transition-all hover:-translate-y-1 hover:shadow-xl hover:shadow-brand/5">
|
||||
{c.cover_image && (
|
||||
<img
|
||||
src={c.cover_image}
|
||||
loading="lazy"
|
||||
width={1024}
|
||||
height={768}
|
||||
alt={c.title}
|
||||
className="aspect-[4/3] w-full object-cover transition duration-500 group-hover:scale-[1.02]"
|
||||
/>
|
||||
<div className="overflow-hidden">
|
||||
<img
|
||||
src={c.cover_image}
|
||||
loading="lazy"
|
||||
width={1024}
|
||||
height={768}
|
||||
alt={c.title}
|
||||
className="aspect-[4/3] w-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="p-7">
|
||||
<h3 className="text-xl font-bold">{c.title}</h3>
|
||||
<div className="mt-2 text-sm text-muted-foreground prose prose-invert" dangerouslySetInnerHTML={{ __html: c.what_we_do }} />
|
||||
<div className="flex flex-1 flex-col p-8">
|
||||
<h3 className="text-2xl font-bold tracking-tight text-foreground">{c.title}</h3>
|
||||
<div className="mt-4 text-muted-foreground prose prose-invert line-clamp-4" dangerouslySetInnerHTML={{ __html: c.what_we_do }} />
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
@@ -192,26 +187,28 @@ function Index() {
|
||||
)}
|
||||
|
||||
{services.length > 0 && (
|
||||
<section className="relative z-10">
|
||||
<div className="mx-auto max-w-7xl px-6 py-24">
|
||||
<section className="relative z-10 border-y border-border bg-mist/30">
|
||||
<div className="mx-auto max-w-7xl px-6 py-32">
|
||||
<Reveal>
|
||||
<div className="grid gap-6 md:grid-cols-[1.1fr_2fr] md:items-end">
|
||||
<div className="grid gap-8 md:grid-cols-[1fr_1.5fr] md:items-end">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">Why Udhamsil</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">From requirement to reliable output.</h2>
|
||||
<div className="inline-flex items-center gap-2 rounded-full bg-accent/10 px-3 py-1 text-xs font-bold uppercase tracking-widest text-accent">
|
||||
Why Udhamsil
|
||||
</div>
|
||||
<h2 className="mt-6 text-4xl font-black tracking-tight md:text-5xl">From requirement to reliable output.</h2>
|
||||
</div>
|
||||
<p className="max-w-2xl text-muted-foreground md:justify-self-end">
|
||||
<p className="max-w-xl text-lg leading-relaxed text-muted-foreground md:justify-self-end">
|
||||
One accountable team helps you choose, receive and keep the right machinery running.
|
||||
</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
<div className="mt-12 grid gap-px overflow-hidden border border-border bg-border sm:grid-cols-2 lg:grid-cols-4">
|
||||
<div className="mt-16 grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
|
||||
{services.map((step, index) => (
|
||||
<Reveal key={step.id} delay={index * 70}>
|
||||
<div className="h-full bg-background p-7">
|
||||
<span className="font-display text-sm font-bold text-accent">{(index + 1).toString().padStart(2, "0")}</span>
|
||||
<h3 className="mt-8 text-xl font-bold">{step.name}</h3>
|
||||
<div className="mt-3 text-sm leading-relaxed text-muted-foreground prose prose-invert" dangerouslySetInnerHTML={{ __html: step.description }} />
|
||||
<div className="group relative flex h-full flex-col rounded-3xl border border-border/50 bg-card p-8 shadow-lg transition-all hover:border-brand/30 hover:bg-muted/50">
|
||||
<span className="font-display text-5xl font-black text-brand/10 transition-colors group-hover:text-brand/20">{(index + 1).toString().padStart(2, "0")}</span>
|
||||
<h3 className="mt-6 text-xl font-bold tracking-tight">{step.name}</h3>
|
||||
<div className="mt-4 text-sm leading-relaxed text-muted-foreground prose prose-invert" dangerouslySetInnerHTML={{ __html: step.description }} />
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
@@ -221,13 +218,15 @@ function Index() {
|
||||
)}
|
||||
|
||||
{sisterCompanies.length > 0 && (
|
||||
<section className="relative z-10 overflow-hidden bg-mist/30">
|
||||
<div className="mx-auto max-w-7xl px-6 py-24">
|
||||
<section className="relative z-10 overflow-hidden bg-background">
|
||||
<div className="mx-auto max-w-7xl px-6 py-32">
|
||||
<Reveal>
|
||||
<div className="mx-auto max-w-2xl text-center">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">Our sister companies</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">One group, connected capability.</h2>
|
||||
<p className="mt-4 text-muted-foreground">A growing ecosystem designed to support enterprise from machinery to long-term operations.</p>
|
||||
<div className="inline-flex items-center gap-2 rounded-full bg-accent/10 px-3 py-1 text-xs font-bold uppercase tracking-widest text-accent">
|
||||
Ecosystem
|
||||
</div>
|
||||
<h2 className="mt-6 text-4xl font-black tracking-tight md:text-5xl">One group, connected capability.</h2>
|
||||
<p className="mt-6 text-lg text-muted-foreground">A growing network designed to support enterprise from machinery to long-term operations.</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
<div className="mt-16">
|
||||
@@ -241,25 +240,36 @@ function Index() {
|
||||
<TestimonialsMarquee testimonials={testimonials} />
|
||||
)}
|
||||
|
||||
<section className="relative z-10 border-y border-border bg-mist/60">
|
||||
<div className="mx-auto max-w-7xl px-6 py-20 lg:items-center text-center">
|
||||
<section className="relative z-10 border-t border-border/50 bg-gradient-to-b from-transparent to-mist/50">
|
||||
<div className="mx-auto max-w-7xl px-6 py-32 text-center lg:py-40">
|
||||
<Reveal>
|
||||
<div className="border border-border bg-background px-8 py-16">
|
||||
<h2 className="text-3xl font-bold md:text-4xl">
|
||||
<div className="mx-auto max-w-4xl">
|
||||
<div className="inline-flex items-center gap-2 rounded-full bg-brand/10 px-4 py-1.5 text-xs font-bold uppercase tracking-widest text-brand">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="absolute inline-flex h-full w-full animate-ping rounded-full bg-brand opacity-75"></span>
|
||||
<span className="relative inline-flex h-2 w-2 rounded-full bg-brand"></span>
|
||||
</span>
|
||||
Ready to scale
|
||||
</div>
|
||||
<h2 className="mt-8 text-4xl font-black tracking-tight text-foreground md:text-6xl md:leading-[1.1]">
|
||||
Need a machine spec'd for your output?
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-xl text-muted-foreground">
|
||||
<p className="mx-auto mt-6 max-w-2xl text-xl leading-relaxed text-muted-foreground">
|
||||
Tell us your scale and target production — our Banke team responds with a tailored
|
||||
recommendation.
|
||||
recommendation and pricing.
|
||||
</p>
|
||||
<Button asChild size="lg" className="mt-8 rounded-none bg-brand">
|
||||
<Link to="/contact">Get a Quote</Link>
|
||||
</Button>
|
||||
<div className="mt-10 flex flex-col items-center justify-center gap-4 sm:flex-row">
|
||||
<Button asChild size="lg" className="h-14 w-full sm:w-auto rounded-full bg-brand px-10 text-base font-bold shadow-xl shadow-brand/20 transition-all hover:scale-105 hover:bg-brand/90">
|
||||
<Link to="/contact">Get a Quote</Link>
|
||||
</Button>
|
||||
<Button asChild size="lg" variant="ghost" className="h-14 w-full sm:w-auto rounded-full px-8 text-base font-bold transition-all hover:bg-muted">
|
||||
<Link to="/products">Browse Catalogue</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -2,15 +2,17 @@
|
||||
import { Outlet } from "react-router-dom";
|
||||
import { Header } from "../components/site/Header";
|
||||
import { Footer } from "../components/site/Footer";
|
||||
import { FloatingActionButtons } from "../components/site/FloatingActionButtons";
|
||||
|
||||
export default function Layout() {
|
||||
return (
|
||||
<div className="relative flex min-h-screen flex-col overflow-x-hidden bg-background">
|
||||
<Header />
|
||||
<main className="flex-1">
|
||||
<main className="flex-1 pt-20">
|
||||
<Outlet />
|
||||
</main>
|
||||
<Footer />
|
||||
<FloatingActionButtons />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -35,28 +35,34 @@ function Products() {
|
||||
/>
|
||||
|
||||
<section className="relative z-10">
|
||||
<div className="mx-auto max-w-7xl space-y-8 px-6 pb-24">
|
||||
<div className="mx-auto max-w-7xl space-y-12 px-6 pb-24">
|
||||
{products?.map((product, i) => (
|
||||
<Reveal key={product.id} delay={i * 80}>
|
||||
<div className="grid items-center gap-8 border-y border-border bg-background py-8 md:grid-cols-2">
|
||||
<div className="group grid items-center gap-8 overflow-hidden rounded-3xl border border-border/50 bg-card p-6 shadow-lg transition-all hover:shadow-xl hover:shadow-brand/5 md:grid-cols-2 md:gap-12 md:p-10">
|
||||
{product.image && (
|
||||
<img
|
||||
src={product.image}
|
||||
loading="lazy"
|
||||
width={1024}
|
||||
height={768}
|
||||
alt={`${product.name} machinery`}
|
||||
className="aspect-[4/3] w-full object-cover"
|
||||
/>
|
||||
<div className="overflow-hidden rounded-2xl shadow-md">
|
||||
<img
|
||||
src={product.image}
|
||||
loading="lazy"
|
||||
width={1024}
|
||||
height={768}
|
||||
alt={`${product.name} machinery`}
|
||||
className="aspect-[4/3] w-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold md:text-3xl">{product.name}</h2>
|
||||
<h2 className="text-3xl font-black tracking-tight text-foreground md:text-4xl">{product.name}</h2>
|
||||
{product.features && product.features.length > 0 && (
|
||||
<ul className="mt-5 grid gap-2 text-sm text-muted-foreground sm:grid-cols-2">
|
||||
<ul className="mt-8 grid gap-4 text-base text-muted-foreground sm:grid-cols-2">
|
||||
{product.features.map((feature) => (
|
||||
<li key={feature.id} className="flex items-center gap-2">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-accent" />
|
||||
{feature.name}
|
||||
<li key={feature.id} className="flex items-center gap-3">
|
||||
<span className="flex h-6 w-6 items-center justify-center rounded-full bg-brand/10 text-brand">
|
||||
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={3}>
|
||||
<path strokeLinecap="round" strokeLinejoin="round" d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
</span>
|
||||
<span className="font-medium">{feature.name}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -84,7 +84,7 @@ function ProjectsEvents() {
|
||||
<div className="mx-auto grid max-w-7xl gap-6 px-6 pb-24 md:grid-cols-3">
|
||||
{entries.map((e, i) => (
|
||||
<Reveal key={e.id} delay={i * 80}>
|
||||
<article className="group h-full overflow-hidden border border-border bg-background transition hover:border-brand">
|
||||
<article className="group flex h-full flex-col overflow-hidden rounded-3xl border border-border/50 bg-card shadow-lg transition-all hover:-translate-y-1 hover:shadow-xl hover:shadow-brand/5">
|
||||
{e.image && (
|
||||
<img
|
||||
src={e.image}
|
||||
@@ -92,11 +92,11 @@ function ProjectsEvents() {
|
||||
width={1024}
|
||||
height={768}
|
||||
alt={e.title}
|
||||
className="aspect-[4/3] w-full object-cover transition duration-500 group-hover:scale-[1.02]"
|
||||
className="aspect-[4/3] w-full object-cover transition-transform duration-700 group-hover:scale-105"
|
||||
/>
|
||||
)}
|
||||
<div className="p-6">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">
|
||||
<p className="inline-flex items-center gap-2 rounded-full bg-accent/10 px-3 py-1 text-xs font-bold uppercase tracking-widest text-accent">
|
||||
{e.year} · {e.tag}
|
||||
</p>
|
||||
<h2 className="mt-2 text-xl font-bold">{e.title}</h2>
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
const fs = require('fs');
|
||||
|
||||
const homeContent = `import { Link } from "react-router-dom";
|
||||
import {
|
||||
ArrowRight,
|
||||
Building2,
|
||||
Factory,
|
||||
Headphones,
|
||||
Leaf,
|
||||
Quote,
|
||||
Settings2,
|
||||
ShieldCheck,
|
||||
Star,
|
||||
Wrench,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { LightField, Parallax, Reveal } from "@/components/site/Parallax";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useApi } from "@/hooks/useApi";
|
||||
import heroMachinery from "@/assets/hero-machinery.jpg";
|
||||
|
||||
export default Index;
|
||||
|
||||
function Index() {
|
||||
const { data: heroList } = useApi("homepage/hero");
|
||||
const { data: statsList } = useApi("homepage/stats");
|
||||
const { data: marqueeList } = useApi("homepage/marquee");
|
||||
const { data: whatWeDoList } = useApi("org/what-we-do");
|
||||
const { data: partnersList } = useApi("org/partners");
|
||||
const { data: sisterCompaniesList } = useApi("org/sister-companies");
|
||||
const { data: servicesList } = useApi("org/services");
|
||||
const { data: testimonialsList } = useApi("testimonials");
|
||||
|
||||
const hero = heroList?.[0] || null;
|
||||
const stats = statsList || [];
|
||||
const marquee = marqueeList || [];
|
||||
const whatWeDo = whatWeDoList || [];
|
||||
const partners = partnersList || [];
|
||||
const sisterCompanies = sisterCompaniesList || [];
|
||||
const services = servicesList || [];
|
||||
const testimonials = testimonialsList || [];
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<LightField />
|
||||
|
||||
{hero && (
|
||||
<section className="relative z-10 overflow-hidden">
|
||||
<div className="mx-auto grid min-h-[720px] max-w-7xl items-center gap-12 px-6 py-16 lg:grid-cols-12 lg:py-24">
|
||||
<div className="lg:col-span-6">
|
||||
<div className="rise flex items-center gap-4 text-xs font-bold uppercase text-brand">
|
||||
<span className="h-px w-12 bg-brand" /> {hero.eyebrow || "Est. 2020 · Kohalpur, Banke"}
|
||||
</div>
|
||||
<h1 className="rise-2 mt-8 text-5xl leading-[1.02] md:text-7xl">
|
||||
{hero.title || "Machinery built for"} <span className="text-gradient">{hero.highlight || "Nepal."}</span>
|
||||
</h1>
|
||||
<p className="rise-3 mt-7 max-w-xl text-xl leading-relaxed text-muted-foreground">
|
||||
{hero.description || "Reliable, innovative machinery solutions — exporting, manufacturing, distributing and supplying small, medium and commercial scale equipment across the nation."}
|
||||
</p>
|
||||
<div className="rise-3 mt-8 flex flex-wrap gap-4">
|
||||
<Button asChild size="lg" className="rounded-none bg-brand px-7 shadow-none">
|
||||
<Link to="/products">Explore Products</Link>
|
||||
</Button>
|
||||
<Button asChild size="lg" variant="outline" className="rounded-none bg-background px-7">
|
||||
<Link to="/projects-events">View Projects</Link>
|
||||
</Button>
|
||||
</div>
|
||||
{stats.length > 0 && (
|
||||
<div className="rise-3 mt-14 grid max-w-xl grid-cols-3 gap-5 border-t border-border pt-7">
|
||||
{stats.map((st) => (
|
||||
<div key={st.id}>
|
||||
<p className="font-display text-3xl font-bold">{st.value}</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">{st.label}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="relative min-h-[480px] lg:col-span-6">
|
||||
<div className="absolute -right-6 top-0 h-20 w-32 border-r border-t border-brand/40" aria-hidden="true" />
|
||||
<div className="absolute inset-4 right-0 md:left-10">
|
||||
<Parallax speed={0.06}>
|
||||
<div className="relative overflow-hidden border border-border bg-mist p-3 shadow-glass">
|
||||
<img
|
||||
src={hero.image || heroMachinery}
|
||||
width={1024}
|
||||
height={1280}
|
||||
alt="Featured equipment"
|
||||
className="h-[440px] w-full object-cover md:h-[540px]"
|
||||
/>
|
||||
<div className="absolute inset-x-3 bottom-3 flex items-end justify-between bg-ink/90 p-5 text-primary-foreground">
|
||||
<div>
|
||||
<p className="text-xs font-bold uppercase text-primary-foreground/60">Featured equipment</p>
|
||||
<p className="mt-1 font-display text-lg">{hero.featured_text || "Commercial systems"}</p>
|
||||
</div>
|
||||
<span className="font-display text-3xl text-accent">01</span>
|
||||
</div>
|
||||
</div>
|
||||
</Parallax>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{marquee.length > 0 && (
|
||||
<div className="relative z-10 overflow-hidden border-y border-border bg-background/60 py-4">
|
||||
<div className="animate-marquee flex whitespace-nowrap">
|
||||
{[0, 1].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex gap-12 pr-12 text-sm font-semibold uppercase tracking-[0.2em] text-muted-foreground"
|
||||
>
|
||||
{marquee.map((m) => (
|
||||
<span key={m.id}>
|
||||
<span>{m.text}</span><span className="text-accent ml-12">/</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{whatWeDo.length > 0 && (
|
||||
<section className="relative z-10">
|
||||
<div className="mx-auto max-w-7xl px-6 py-24">
|
||||
<div className="mb-12 flex items-end justify-between">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">
|
||||
What we do
|
||||
</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">Machinery, end to end</h2>
|
||||
</div>
|
||||
<Link
|
||||
to="/products"
|
||||
className="hidden text-sm font-semibold text-brand hover:underline sm:inline"
|
||||
>
|
||||
See all products →
|
||||
</Link>
|
||||
</div>
|
||||
<div className="grid gap-6 md:grid-cols-3">
|
||||
{whatWeDo.map((c, i) => (
|
||||
<Reveal key={c.id} delay={i * 90}>
|
||||
<div className="group h-full overflow-hidden border border-border bg-background transition hover:border-brand">
|
||||
{c.cover_image && (
|
||||
<img
|
||||
src={c.cover_image}
|
||||
loading="lazy"
|
||||
width={1024}
|
||||
height={768}
|
||||
alt={c.title}
|
||||
className="aspect-[4/3] w-full object-cover transition duration-500 group-hover:scale-[1.02]"
|
||||
/>
|
||||
)}
|
||||
<div className="p-7">
|
||||
<h3 className="text-xl font-bold">{c.title}</h3>
|
||||
<div className="mt-2 text-sm text-muted-foreground prose prose-invert" dangerouslySetInnerHTML={{ __html: c.what_we_do }} />
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{partners.length > 0 && (
|
||||
<section className="relative z-10 overflow-hidden border-y border-border bg-mist/60">
|
||||
<div className="mx-auto grid max-w-7xl items-center gap-14 px-6 py-24 lg:grid-cols-12">
|
||||
<div className="lg:col-span-5">
|
||||
<Reveal>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">Our clients</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">Built around the people who produce.</h2>
|
||||
<p className="mt-5 max-w-lg leading-relaxed text-muted-foreground">
|
||||
We work with ambitious enterprises across agriculture, food production, construction,
|
||||
utilities and commercial manufacturing.
|
||||
</p>
|
||||
</Reveal>
|
||||
</div>
|
||||
|
||||
<div className="lg:col-span-7">
|
||||
<Parallax speed={0.06}>
|
||||
<div className="grid grid-cols-3 gap-6">
|
||||
{partners.map((p) => (
|
||||
<div key={p.id} className="flex flex-col items-center justify-center p-4 border border-border bg-background shadow-glass">
|
||||
{p.logo && <img src={p.logo} alt={p.name} className="h-12 object-contain" />}
|
||||
<span className="mt-2 text-xs font-semibold text-center">{p.name}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Parallax>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{services.length > 0 && (
|
||||
<section className="relative z-10">
|
||||
<div className="mx-auto max-w-7xl px-6 py-24">
|
||||
<Reveal>
|
||||
<div className="grid gap-6 md:grid-cols-[1.1fr_2fr] md:items-end">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">Why Udhamsil</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">From requirement to reliable output.</h2>
|
||||
</div>
|
||||
<p className="max-w-2xl text-muted-foreground md:justify-self-end">
|
||||
One accountable team helps you choose, receive and keep the right machinery running.
|
||||
</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
<div className="mt-12 grid gap-px overflow-hidden border border-border bg-border sm:grid-cols-2 lg:grid-cols-4">
|
||||
{services.map((step, index) => (
|
||||
<Reveal key={step.id} delay={index * 70}>
|
||||
<div className="h-full bg-background p-7">
|
||||
<span className="font-display text-sm font-bold text-accent">{(index + 1).toString().padStart(2, "0")}</span>
|
||||
<h3 className="mt-8 text-xl font-bold">{step.name}</h3>
|
||||
<div className="mt-3 text-sm leading-relaxed text-muted-foreground prose prose-invert" dangerouslySetInnerHTML={{ __html: step.description }} />
|
||||
</div>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{testimonials.length > 0 && (
|
||||
<section className="relative z-10 bg-ink text-primary-foreground">
|
||||
<div className="mx-auto max-w-7xl px-6 py-24">
|
||||
<Reveal>
|
||||
<div className="flex flex-col justify-between gap-5 md:flex-row md:items-end">
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">Client voices</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">Confidence earned on the ground.</h2>
|
||||
</div>
|
||||
</div>
|
||||
</Reveal>
|
||||
<div className="mt-12 grid gap-6 lg:grid-cols-3">
|
||||
{testimonials.map((item, index) => (
|
||||
<Reveal key={item.id} delay={index * 90}>
|
||||
<article className="h-full border border-primary-foreground/15 bg-primary-foreground/5 p-7">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex gap-1">
|
||||
{[0, 1, 2, 3, 4].map((star) => <Star key={star} className="h-4 w-4 fill-accent text-accent" aria-hidden="true" />)}
|
||||
</div>
|
||||
<Quote className="h-7 w-7 text-accent/60" aria-hidden="true" />
|
||||
</div>
|
||||
<blockquote className="mt-8 text-lg leading-relaxed">“{item.testimonial}”</blockquote>
|
||||
<footer className="mt-8 border-t border-primary-foreground/15 pt-5 flex items-center gap-4">
|
||||
{item.image && <img src={item.image} alt={item.name} className="w-10 h-10 rounded-full object-cover" />}
|
||||
<div>
|
||||
<p className="font-semibold">{item.name}</p>
|
||||
<p className="mt-1 text-sm text-primary-foreground/60">{item.organization}</p>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{sisterCompanies.length > 0 && (
|
||||
<section className="relative z-10 overflow-hidden">
|
||||
<div className="mx-auto max-w-7xl px-6 py-24">
|
||||
<Reveal>
|
||||
<div className="mx-auto max-w-2xl text-center">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-accent">Our sister companies</p>
|
||||
<h2 className="mt-3 text-4xl font-bold">One group, connected capability.</h2>
|
||||
<p className="mt-4 text-muted-foreground">A growing ecosystem designed to support enterprise from machinery to long-term operations.</p>
|
||||
</div>
|
||||
</Reveal>
|
||||
<div className="mt-16 grid gap-6 md:grid-cols-3">
|
||||
{sisterCompanies.map((sc, index) => (
|
||||
<Reveal key={sc.id} delay={index * 90}>
|
||||
<article className="h-full border border-border bg-background p-7 transition hover:border-brand">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="flex h-11 w-11 items-center justify-center rounded-xl bg-brand/10 text-brand">
|
||||
{sc.logo ? <img src={sc.logo} alt={sc.name} className="h-6 w-6 object-contain" /> : <Settings2 className="h-5 w-5" />}
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="mt-7 text-xl font-bold">
|
||||
{sc.website_link ? <a href={sc.website_link} target="_blank" rel="noreferrer" className="hover:underline">{sc.name}</a> : sc.name}
|
||||
</h3>
|
||||
</article>
|
||||
</Reveal>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<section className="relative z-10 border-y border-border bg-mist/60">
|
||||
<div className="mx-auto max-w-7xl px-6 py-20 lg:items-center text-center">
|
||||
<Reveal>
|
||||
<div className="border border-border bg-background px-8 py-16">
|
||||
<h2 className="text-3xl font-bold md:text-4xl">
|
||||
Need a machine spec'd for your output?
|
||||
</h2>
|
||||
<p className="mx-auto mt-4 max-w-xl text-muted-foreground">
|
||||
Tell us your scale and target production — our Banke team responds with a tailored
|
||||
recommendation.
|
||||
</p>
|
||||
<Button asChild size="lg" className="mt-8 rounded-none bg-brand">
|
||||
<Link to="/contact">Get a Quote</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</Reveal>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
`;
|
||||
|
||||
fs.writeFileSync('src/pages/Home.tsx', homeContent);
|
||||
Reference in new issue
Block a user