Edit
This commit is contained in:
+3
-58
@@ -1,60 +1,5 @@
|
||||
import { About } from "@/components/About";
|
||||
import { ContactCta } from "@/components/ContactCta";
|
||||
import { Hero } from "@/components/Hero";
|
||||
import { Marquee } from "@/components/Marquee";
|
||||
import { News } from "@/components/News";
|
||||
import { Projects } from "@/components/Projects";
|
||||
import { Services } from "@/components/Services";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { contact, site } from "@/data/site";
|
||||
import { getRecentNews, getRecentPortfolios, toDisplayNewsItem, toDisplayProject } from "@/lib/api";
|
||||
import { OmniyatHome } from "@/components/omniyat/OmniyatHome";
|
||||
|
||||
export const revalidate = 300; // keep in sync with api.ts PAGE_REVALIDATE_SECONDS
|
||||
|
||||
const organizationJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
name: site.name,
|
||||
url: site.url,
|
||||
slogan: site.tagline,
|
||||
foundingDate: "2000",
|
||||
address: {
|
||||
"@type": "PostalAddress",
|
||||
addressLocality: "قم",
|
||||
addressCountry: "IR",
|
||||
streetAddress: contact.address,
|
||||
},
|
||||
telephone: contact.phoneLinks,
|
||||
email: contact.email,
|
||||
sameAs: [contact.instagramUrl],
|
||||
};
|
||||
|
||||
export default async function HomePage() {
|
||||
const [portfolios, newsPosts] = await Promise.all([getRecentPortfolios(12), getRecentNews(3)]);
|
||||
const displayProjects = portfolios.map(toDisplayProject);
|
||||
const displayNews = newsPosts.map(toDisplayNewsItem);
|
||||
|
||||
return (
|
||||
<>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationJsonLd) }}
|
||||
/>
|
||||
|
||||
<SiteHeader />
|
||||
|
||||
<main>
|
||||
<Hero />
|
||||
<About />
|
||||
<Marquee />
|
||||
<Projects projects={displayProjects} />
|
||||
<Services />
|
||||
<News items={displayNews} />
|
||||
<ContactCta />
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
export default function HomePage() {
|
||||
return <OmniyatHome />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
"use client";
|
||||
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { Swiper, SwiperSlide } from "swiper/react";
|
||||
import type { Swiper as SwiperInstance } from "swiper";
|
||||
import "swiper/css";
|
||||
|
||||
import logo from "@/assets/images/brand/logo.png";
|
||||
import { contact } from "@/data/site";
|
||||
import "./omniyat-home.css";
|
||||
|
||||
const asset = (name: string) => `/omniyat-reference/${name}`;
|
||||
|
||||
const stories: { title: string; image: string; href: string }[] = [
|
||||
{ title: "گشایش فصل تازهای از زندگی در الیزیوم", image: asset("news-1.jpg"), href: "/news-and-blog" },
|
||||
{ title: "بیست و پنج سال خلق فضاهای ماندگار", image: asset("news-2.jpg"), href: "/aboutus" },
|
||||
{ title: "آغاز مسیری تازه برای پروژه حیات", image: asset("news-3.jpg"), href: "/news-and-blog" },
|
||||
];
|
||||
|
||||
const projects: { title: string; image: string; subtitle: string }[] = [
|
||||
{ title: "بازآفرینی افقها", subtitle: "الیزیوم", image: asset("project-1.webp") },
|
||||
{ title: "زندگی در میان شاهکارها", subtitle: "اونیکس", image: asset("project-2.webp") },
|
||||
{ title: "نگاهی تازه به معماری", subtitle: "حیات", image: asset("project-3.webp") },
|
||||
];
|
||||
|
||||
const partners = [
|
||||
{ name: "زاها حدید", latin: "Zaha Hadid", image: asset("partner-zaha.webp") },
|
||||
{ name: "فاستر و همکاران", latin: "Foster + Partners", image: asset("partner-foster.webp") },
|
||||
{ name: "ژیل و بواسیه", latin: "Gilles & Boissier", image: asset("partner-gilles.webp") },
|
||||
{ name: "یودیزین", latin: "YODEZEEN", image: asset("partner-yodezeen.jpg") },
|
||||
{ name: "ولادیمیر جوروویچ", latin: "Vladimir Djurovic", image: asset("partner-vladimir.webp") },
|
||||
{ name: "دورچستر کالکشن", latin: "Dorchester Collection", image: asset("partner-dorchester.webp") },
|
||||
];
|
||||
|
||||
const menu = [
|
||||
{ label: "صفحه اصلی", href: "/" },
|
||||
{ label: "پروژهها", href: "/portfolios" },
|
||||
{ label: "خدمات ما", href: "/our-services" },
|
||||
{ label: "داستان ما", href: "/aboutus" },
|
||||
{ label: "اخبار", href: "/news-and-blog" },
|
||||
{ label: "تماس با ما", href: "/contactus" },
|
||||
];
|
||||
|
||||
function ArrowLink({ href, children }: { href: string; children: React.ReactNode }) {
|
||||
return <Link className="om-link" href={href}><span>{children}</span><span aria-hidden="true">←</span></Link>;
|
||||
}
|
||||
|
||||
export function OmniyatHome() {
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const [story, setStory] = useState(0);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const rootRef = useRef<HTMLDivElement>(null);
|
||||
const newsRef = useRef<HTMLElement>(null);
|
||||
const partnerSwiperRef = useRef<SwiperInstance | null>(null);
|
||||
const partnerProgressRef = useRef<HTMLSpanElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 40);
|
||||
onScroll();
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const timer = window.setInterval(() => setStory((value) => (value + 1) % stories.length), 6500);
|
||||
return () => window.clearInterval(timer);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.style.overflow = menuOpen ? "hidden" : "";
|
||||
return () => { document.body.style.overflow = ""; };
|
||||
}, [menuOpen]);
|
||||
|
||||
useEffect(() => {
|
||||
const root = rootRef.current;
|
||||
const news = newsRef.current;
|
||||
if (!root || !news) return;
|
||||
|
||||
const reduceMotion = window.matchMedia("(prefers-reduced-motion: reduce)");
|
||||
root.classList.add("om-motion-ready");
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (entry.isIntersecting) {
|
||||
entry.target.classList.add("is-visible");
|
||||
observer.unobserve(entry.target);
|
||||
}
|
||||
});
|
||||
}, { threshold: 0.13, rootMargin: "0px 0px -35px 0px" });
|
||||
root.querySelectorAll(".om-reveal").forEach((element) => observer.observe(element));
|
||||
|
||||
let raf = 0;
|
||||
const updateScroll = () => {
|
||||
raf = 0;
|
||||
const rect = news.getBoundingClientRect();
|
||||
const range = Math.max(1, news.offsetHeight - window.innerHeight);
|
||||
const progress = reduceMotion.matches ? 1 : Math.min(1, Math.max(0, -rect.top / range));
|
||||
news.style.setProperty("--grow", progress.toFixed(3));
|
||||
root.querySelectorAll<HTMLElement>(".om-project, .om-retreat").forEach((element) => {
|
||||
const box = element.getBoundingClientRect();
|
||||
const distance = (window.innerHeight / 2 - box.top - box.height / 2) / window.innerHeight;
|
||||
element.style.setProperty("--parallax", `${Math.max(-36, Math.min(36, distance * 38)).toFixed(1)}px`);
|
||||
});
|
||||
};
|
||||
const requestUpdate = () => { if (!raf) raf = window.requestAnimationFrame(updateScroll); };
|
||||
updateScroll();
|
||||
window.addEventListener("scroll", requestUpdate, { passive: true });
|
||||
window.addEventListener("resize", requestUpdate);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
window.removeEventListener("scroll", requestUpdate);
|
||||
window.removeEventListener("resize", requestUpdate);
|
||||
if (raf) window.cancelAnimationFrame(raf);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const syncPartnerProgress = (swiper: SwiperInstance) => {
|
||||
const bar = partnerProgressRef.current;
|
||||
if (!bar) return;
|
||||
const index = swiper.realIndex;
|
||||
const previous = Number(bar.dataset.index || 0);
|
||||
bar.style.transitionDuration = Math.abs(index - previous) === partners.length - 1 ? "0ms" : `${swiper.params.speed}ms`;
|
||||
bar.style.transform = `translateX(${index * 100}%)`;
|
||||
bar.dataset.index = String(index);
|
||||
};
|
||||
|
||||
return <div className="om-home" dir="rtl" ref={rootRef}>
|
||||
<header className={`om-header ${scrolled ? "om-header-scrolled" : ""}`}>
|
||||
<button className="om-menu-toggle" type="button" onClick={() => setMenuOpen(true)} aria-label="باز کردن فهرست"><span/><span/></button>
|
||||
<Link href="/" className="om-brand" aria-label="رئوفی، صفحه اصلی"><Image src={logo} alt="رئوفی" priority /></Link>
|
||||
<Link href="/contactus" className="om-contact">تماس با ما</Link>
|
||||
</header>
|
||||
|
||||
<div className={`om-drawer ${menuOpen ? "is-open" : ""}`} aria-hidden={!menuOpen}>
|
||||
<button className="om-drawer-close" onClick={() => setMenuOpen(false)} aria-label="بستن فهرست">×</button>
|
||||
<nav aria-label="فهرست اصلی">{menu.map((item, index) => <Link key={item.href} href={item.href} onClick={() => setMenuOpen(false)} tabIndex={menuOpen ? 0 : -1}><small>{String(index + 1).padStart(2, "0")}</small>{item.label}<span>←</span></Link>)}</nav>
|
||||
<span className="om-drawer-bottom">هنرِ ساختنِ زندگی</span>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<section className="om-hero">
|
||||
<video className="om-hero-video" autoPlay muted loop playsInline preload="metadata" poster={asset("hero.jpg")} aria-label="ویدیوی معماری آغاز صفحه">
|
||||
<source src={asset("hero-video-mobile.mp4")} media="(max-width: 700px)" type="video/mp4" />
|
||||
<source src={asset("hero-video.mp4")} type="video/mp4" />
|
||||
</video>
|
||||
<div className="om-hero-shade" />
|
||||
<h1>هنرِ تعالی</h1>
|
||||
<span className="om-hero-bottom">گروه ساختمانی رئوفی <i /> برای کشف، پیمایش کنید</span>
|
||||
</section>
|
||||
|
||||
<section className="om-intro om-container om-reveal">
|
||||
<p className="om-eyebrow">رئوفی</p>
|
||||
<h2>هنرِ تعالی</h2>
|
||||
<p className="om-intro-copy">فراتر از ساختن، فراتر از مکان. جایی که مهارت بیهمتا، ظرافت و نوآوری را به تجربهای تازه از زندگی بدل میکند. ما ناممکن را تصور میکنیم و آن را میسازیم.</p>
|
||||
<ArrowLink href="/aboutus">داستان ما را ببینید</ArrowLink>
|
||||
</section>
|
||||
|
||||
<section className="om-news" aria-label="تازهترینها" ref={newsRef}>
|
||||
<div className="om-news-frame">
|
||||
{stories.map((item, index) => <div className={`om-news-slide ${story === index ? "is-active" : ""}`} key={item.title}>
|
||||
<Image src={item.image} alt="" fill sizes="(max-width: 700px) 100vw, 90vw" />
|
||||
<div className="om-news-overlay"><small>تازهترینها / {String(index + 1).padStart(2, "0")}</small><h3>{item.title}</h3><ArrowLink href={item.href}>بیشتر بدانید</ArrowLink></div>
|
||||
</div>)}
|
||||
<div className="om-news-controls"><div className="om-progress">{stories.map((_, i) => <button key={i} className={story === i ? "is-active" : ""} onClick={() => setStory(i)} aria-label={`نمایش خبر ${i + 1}`} />)}</div><button onClick={() => setStory((story + stories.length - 1) % stories.length)} aria-label="خبر قبلی">←</button><button onClick={() => setStory((story + 1) % stories.length)} aria-label="خبر بعدی">→</button></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="om-section-lead om-container om-reveal"><p className="om-eyebrow">شاهکارها</p><h2>خلق آثارِ زندهٔ هنری</h2><p>هنر تنها نشانی از زیبایی نیست؛ بیانی از یگانگی، خلاقیت و کیفیت است. هر پروژهٔ ما میراثی برای آینده خواهد بود.</p></section>
|
||||
|
||||
<section className="om-projects">{projects.map((project, index) => <article className={`om-project om-project-${index + 1}`} key={project.title}>
|
||||
<Image src={project.image} alt={project.subtitle} fill sizes="100vw" />
|
||||
<div className="om-project-shade"/><div className="om-project-content om-reveal"><small>پروژههای رئوفی / {String(index + 1).padStart(2, "0")}</small><h3>{project.title}</h3><span>{project.subtitle}</span></div>
|
||||
</article>)}</section>
|
||||
|
||||
<section className="om-retreat"><Image src={asset("project-4.webp")} alt="معماری مسکونی" fill sizes="100vw" /><div className="om-retreat-inner"><p className="om-eyebrow">مجموعهای متمایز</p><h2>آرامشی در دلِ شهر</h2><ArrowLink href="/portfolios">کشف پروژهها</ArrowLink></div></section>
|
||||
|
||||
<section className="om-partners">
|
||||
<div className="om-partners-intro om-container om-reveal"><p className="om-eyebrow">همکاران</p><h2>همراهی با صاحبان اندیشه</h2><p>خلاقیت بیمرز با پیشگامان این صنعت پیوند میخورد و روایتهایی تازه در طراحی و زندگی میآفریند.</p><a className="om-video-link" href="https://www.youtube.com/watch?v=fy8VlBj1pAU" target="_blank" rel="noreferrer">تماشای ویدیو ↗</a></div>
|
||||
<div className="om-partner-carousel om-reveal">
|
||||
<Swiper className="om-partner-swiper" dir="ltr" slidesPerView="auto" spaceBetween={30} loop speed={650} grabCursor simulateTouch threshold={4} onSwiper={(swiper) => { partnerSwiperRef.current = swiper; syncPartnerProgress(swiper); }} onSlideChange={syncPartnerProgress} breakpoints={{ 0: { spaceBetween: 16 }, 701: { spaceBetween: 30 } }}>
|
||||
{partners.map((item) => <SwiperSlide className="om-partner-card" key={item.name}><article><div className="om-partner-image"><Image src={item.image} alt={item.latin} fill sizes="(max-width: 700px) 85vw, 50vw" draggable={false} /></div><div className="om-partner-caption"><h3>{item.name}</h3><span>{item.latin}</span></div></article></SwiperSlide>)}
|
||||
</Swiper>
|
||||
<div className="om-partner-controls"><div className="om-partner-progress"><span ref={partnerProgressRef} data-index="0" /></div><button type="button" onClick={() => partnerSwiperRef.current?.slidePrev()} aria-label="همکار قبلی">←</button><button type="button" onClick={() => partnerSwiperRef.current?.slideNext()} aria-label="همکار بعدی">→</button></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="om-atelier"><Image src={asset("atelier.png")} alt="فضای طراحی" fill sizes="100vw" /><div className="om-atelier-card"><p className="om-eyebrow">آتلیه رئوفی</p><h2>تصورِ شما، هنرِ ما</h2><p>هر فضا با نگاهی شخصی آغاز میشود و با وسواس در جزئیات، به اثری ماندگار بدل میگردد.</p></div></section>
|
||||
|
||||
<section className="om-story om-container om-reveal">
|
||||
<div className="om-story-intro">
|
||||
<p className="om-eyebrow">داستان ما</p>
|
||||
<h2>پیوندِ مکان<br />و معنا</h2>
|
||||
<p>با اشتیاق به معماری، مرزهای ممکن را جابهجا میکنیم و فضاهایی میسازیم که افق شهر و کیفیت زندگی را دگرگون میکنند.</p>
|
||||
</div>
|
||||
<div className="om-stats" aria-label="آمار گروه ساختمانی رئوفی">
|
||||
<div><strong>۲۵</strong><span>سال تجربه</span></div>
|
||||
<div><strong>۱۵</strong><span>پروژهٔ شاخص</span></div>
|
||||
<div><strong>۰۶</strong><span>حوزهٔ تخصصی</span></div>
|
||||
</div>
|
||||
<Link className="om-story-cta" href="/aboutus">داستان ما را کشف کنید</Link>
|
||||
</section>
|
||||
|
||||
<section className="om-featured-news om-container om-reveal"><Image src={asset("news-3.jpg")} alt="تازهترین خبر معماری" fill sizes="(max-width: 700px) 100vw, 90vw" /><div><small>تازهترین خبر</small><h2>روایتی تازه از معماری و زندگی</h2><ArrowLink href="/news-and-blog">مطالعهٔ خبر</ArrowLink></div></section>
|
||||
|
||||
<section className="om-final"><p className="om-eyebrow">با هم گفتوگو کنیم</p><h2>ناممکن را کشف کنید</h2><p>برای آشنایی بیشتر با پروژهها و فرصتهای همکاری، با ما در تماس باشید.</p><ArrowLink href="/contactus">درخواست تماس</ArrowLink></section>
|
||||
</main>
|
||||
|
||||
<footer className="om-footer"><div className="om-footer-main"><div className="om-footer-brand">رئوفی<small>هنرِ ساختنِ زندگی</small></div><div><h3>کاوش</h3>{menu.slice(1,5).map(item => <Link href={item.href} key={item.href}>{item.label}</Link>)}</div><div><h3>ارتباط</h3><Link href="/contactus">تماس با ما</Link><a href={`tel:${contact.phoneLinks[0]}`}>{contact.phones[0]}</a><a href={`mailto:${contact.email}`}>{contact.email}</a></div><div><h3>نشانی</h3><p>{contact.address}</p></div></div><div className="om-footer-bottom"><span>© {new Date().getFullYear()} گروه ساختمانی رئوفی</span><a href={contact.instagramUrl}>اینستاگرام ↗</a></div></footer>
|
||||
</div>;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
.om-home{--om-ink:#20211f;--om-muted:#797a77;background:#fff;color:var(--om-ink);font-family:var(--font-iranyekan),sans-serif;overflow:hidden}
|
||||
.om-home{overflow-x:clip;overflow-y:visible}
|
||||
body:has(.om-home){overflow-x:clip!important}
|
||||
.om-home *{box-sizing:border-box}.om-home h1,.om-home h2,.om-home h3,.om-home p{margin:0}.om-home h1,.om-home h2,.om-home h3{font-weight:400;line-height:1.45}.om-home a{text-decoration:none}.om-container{width:calc(100% - 9.5vw);max-width:1500px;margin-inline:auto}.om-eyebrow{font-size:12px;letter-spacing:.05em;color:#8b8a85;margin-bottom:20px!important}.om-link{display:inline-flex;gap:42px;align-items:center;justify-content:space-between;border-bottom:1px solid currentColor;padding:0 0 10px;color:inherit;font-size:13px;min-width:170px;transition:gap .3s,opacity .3s}.om-link:hover{gap:60px;opacity:.65}.om-link span:last-child{font-size:23px;line-height:1}
|
||||
.om-header{position:fixed;z-index:60;top:0;inset-inline:0;height:84px;display:grid;grid-template-columns:1fr 1fr 1fr;align-items:center;padding-inline:5%;color:white;direction:rtl;transition:background .4s}.om-header-scrolled{background:rgba(33,33,32,.58);backdrop-filter:blur(12px)}.om-menu-toggle{background:none;border:0;color:inherit;width:32px;display:grid;gap:6px;cursor:pointer;padding:10px 0;justify-self:start}.om-menu-toggle span{display:block;width:23px;height:1px;background:currentColor}.om-brand{justify-self:center;display:block}.om-brand img{display:block;width:120px;height:auto;filter:brightness(0) invert(1)}.om-contact{justify-self:end;color:white;border:1px solid rgba(255,255,255,.85);font-size:11px;padding:10px 20px;min-width:110px;text-align:center;direction:rtl;transition:background .3s,color .3s}.om-contact:hover{background:white;color:#222}
|
||||
.om-drawer{position:fixed;z-index:100;inset:0;background:#252522;color:#fff;padding:6vh 8vw;opacity:0;visibility:hidden;transform:translateY(-15px);transition:opacity .35s,visibility .35s,transform .35s;display:flex;flex-direction:column}.om-drawer.is-open{opacity:1;visibility:visible;transform:none}.om-drawer-close{align-self:flex-start;background:none;border:0;color:white;font-size:38px;cursor:pointer;line-height:1}.om-drawer nav{width:min(680px,100%);margin:auto;display:grid}.om-drawer nav a{border-bottom:1px solid #555;display:flex;gap:22px;align-items:center;padding:12px 0;color:#f8f8f6;font-size:clamp(18px,3vw,32px);font-weight:300}.om-drawer nav a:hover{color:#c3ae91}.om-drawer nav small{font:11px Arial;color:#999}.om-drawer nav a span{margin-inline-start:auto;font-size:20px}.om-drawer-bottom{font-size:12px;opacity:.6}
|
||||
.om-hero{height:100svh;min-height:560px;position:relative;color:white;display:flex;align-items:center;justify-content:center;text-align:center}.om-hero-image{object-fit:cover;object-position:center;animation:om-zoom 12s ease-out both}.om-hero-shade,.om-project-shade{position:absolute;inset:0;background:linear-gradient(90deg,rgba(25,18,15,.38),rgba(25,18,15,.35)),linear-gradient(0deg,rgba(0,0,0,.38),transparent 35%)}.om-hero h1{position:relative;color:white;font-size:clamp(38px,5vw,72px);font-weight:300;letter-spacing:.04em}.om-hero-bottom{position:absolute;bottom:35px;left:5%;right:5%;display:flex;justify-content:space-between;align-items:center;font-size:11px;letter-spacing:.02em}.om-hero-bottom i{width:1px;height:40px;background:#fff;position:absolute;bottom:0;left:50%}
|
||||
.om-intro{text-align:center;padding-block:155px 170px}.om-intro h2{font-size:clamp(38px,5vw,72px);margin-bottom:26px}.om-intro-copy{font-size:clamp(15px,1.5vw,20px);font-weight:300;line-height:2.25;color:#666;max-width:820px;margin:0 auto 30px!important}.om-intro .om-link{margin-top:20px}
|
||||
.om-news{margin-bottom:150px}.om-news-frame{height:min(56vw,650px);min-height:430px;position:relative;background:#292929;overflow:hidden}.om-news-slide{position:absolute;inset:0;opacity:0;transition:opacity .9s;pointer-events:none}.om-news-slide.is-active{opacity:1;pointer-events:auto}.om-news-slide img{object-fit:cover}.om-news-slide:after{content:"";position:absolute;inset:0;background:linear-gradient(0deg,rgba(0,0,0,.75),transparent 65%)}.om-news-overlay{position:absolute;z-index:2;bottom:95px;right:6%;color:#fff;max-width:650px}.om-news-overlay small,.om-project-content small{font-size:12px;color:#eee}.om-news-overlay h3{color:#fff;font-size:clamp(25px,3.5vw,46px);margin:13px 0 22px}.om-news-controls{position:absolute;z-index:5;bottom:28px;left:6%;right:6%;display:flex;gap:24px;align-items:center;direction:ltr;color:white}.om-news-controls>button{background:none;border:0;color:white;font-size:24px;cursor:pointer}.om-progress{display:flex;flex:1;gap:0}.om-progress button{height:2px;flex:1;background:rgba(255,255,255,.4);border:0;cursor:pointer;padding:0}.om-progress button.is-active{background:white}
|
||||
.om-section-lead{text-align:center;padding-block:20px 125px}.om-section-lead h2,.om-partners h2,.om-story h2{font-size:clamp(38px,5vw,72px);margin-bottom:24px}.om-section-lead>p:last-child,.om-partners>p:nth-child(3){font-size:17px;color:#777;line-height:2.2;max-width:780px;margin-inline:auto}
|
||||
.om-project{height:100svh;min-height:620px;position:relative;overflow:hidden;color:white}.om-project img,.om-retreat img,.om-atelier img{object-fit:cover;transition:transform 1.3s ease}.om-project:hover img{transform:scale(1.035)}.om-project-content{position:absolute;z-index:2;right:8%;bottom:13%;text-align:right}.om-project-content h3{color:white;font-size:clamp(35px,5vw,75px);margin:12px 0}.om-project-content>span{font-size:15px}.om-project-2 .om-project-content{right:auto;left:8%;text-align:left}.om-project-2 .om-project-shade{background:linear-gradient(0deg,rgba(0,0,0,.6),transparent 60%)}
|
||||
.om-retreat{height:100svh;min-height:580px;position:relative;display:grid;place-items:center;color:#fff;text-align:center}.om-retreat:after,.om-atelier:after{content:"";position:absolute;inset:0;background:rgba(0,0,0,.36)}.om-retreat-inner{position:relative;z-index:2}.om-retreat .om-eyebrow{color:#eee}.om-retreat h2{color:white;font-size:clamp(39px,5.5vw,78px);margin-bottom:35px}.om-retreat .om-link{color:white}
|
||||
.om-partners{text-align:center;padding-block:150px}.om-partner-list{display:grid;grid-template-columns:repeat(4,1fr);margin-top:90px;border-top:1px solid #ddd;border-bottom:1px solid #ddd}.om-partner-list span{font-size:clamp(22px,3vw,40px);font-weight:300;padding:65px 15px;border-left:1px solid #ddd}.om-partner-list span:last-child{border-left:0}
|
||||
.om-atelier{position:relative;min-height:760px;height:90vh;display:flex;align-items:end;padding:8%;color:#fff}.om-atelier-card{position:relative;z-index:2;width:min(460px,100%)}.om-atelier .om-eyebrow{color:#eee}.om-atelier h2{font-size:clamp(38px,5vw,68px);color:white;margin-bottom:20px}.om-atelier-card>p:last-child{font-size:16px;line-height:2.1}
|
||||
.om-story{padding-block:160px;display:grid;grid-template-columns:1fr 1fr;gap:10%}.om-story>div:first-child>p:nth-child(3){max-width:620px;color:#777;line-height:2.2;font-size:16px;margin-bottom:30px}.om-stats{display:grid;grid-template-columns:repeat(3,1fr);align-items:center;gap:25px}.om-stats div{display:grid;gap:14px}.om-stats strong{font:300 clamp(45px,5vw,77px) Arial;direction:ltr}.om-stats span{font-size:13px;color:#777}
|
||||
.om-final{background:#242522;color:#fff;text-align:center;padding:120px 20px 140px}.om-final .om-eyebrow{color:#aaa}.om-final h2{color:#fff;font-size:clamp(40px,5.5vw,76px);margin-bottom:18px}.om-final>p:nth-child(3){color:#bbb;font-size:16px;line-height:2;margin-bottom:35px}.om-final .om-link{color:#fff}.om-footer{background:#161716;color:#ccc;padding:100px 5% 20px}.om-footer-main{display:grid;grid-template-columns:2fr repeat(3,1fr);gap:40px;padding-bottom:100px}.om-footer-brand{color:white;font-size:clamp(40px,5vw,70px)}.om-footer-brand small{display:block;font-size:14px;color:#aaa;margin-top:7px}.om-footer h3{color:#fff;font-size:15px;margin-bottom:22px}.om-footer-main>div:not(:first-child){display:flex;flex-direction:column;gap:13px}.om-footer a{color:#bbb;font-size:13px}.om-footer a:hover{color:white}.om-footer p{font-size:13px;line-height:2}.om-footer-bottom{border-top:1px solid #444;padding-top:25px;display:flex;justify-content:space-between;font-size:12px}
|
||||
@keyframes om-zoom{from{transform:scale(1.08)}to{transform:scale(1)}}
|
||||
@media(max-width:700px){.om-container{width:calc(100% - 36px)}.om-header{height:68px;padding-inline:20px}.om-brand{font-size:23px}.om-contact{font-size:10px;padding:8px;min-width:75px}.om-hero{min-height:520px}.om-hero h1{font-size:43px}.om-hero-bottom{left:20px;right:20px;font-size:9px}.om-intro{padding-block:100px}.om-intro-copy{font-size:14px}.om-news{margin-bottom:90px}.om-news-frame{min-height:500px}.om-news-overlay{right:23px;left:23px;bottom:95px}.om-news-overlay h3{font-size:28px}.om-news-controls{left:23px;right:23px}.om-section-lead{padding-bottom:85px}.om-section-lead>p:last-child,.om-partners>p:nth-child(3){font-size:14px}.om-project{min-height:550px;height:75svh}.om-project-content{right:25px;bottom:45px}.om-project-2 .om-project-content{left:25px}.om-retreat{height:75svh;min-height:500px}.om-partners{padding-block:100px}.om-partner-list{grid-template-columns:repeat(2,1fr);margin-top:50px}.om-partner-list span{padding:35px 8px}.om-partner-list span:nth-child(2){border-left:0}.om-partner-list span:nth-child(-n+2){border-bottom:1px solid #ddd}.om-atelier{height:70svh;min-height:550px;padding:35px}.om-story{padding-block:100px;grid-template-columns:1fr;gap:60px}.om-stats{gap:10px}.om-stats strong{font-size:43px}.om-footer{padding-top:70px}.om-footer-main{grid-template-columns:repeat(2,1fr);gap:45px;padding-bottom:65px}.om-footer-brand{grid-column:span 2}.om-footer-bottom{gap:20px;flex-wrap:wrap}}
|
||||
@media(prefers-reduced-motion:reduce){.om-home *, .om-home *:before,.om-home *:after{animation-duration:.01ms!important;transition-duration:.01ms!important}}
|
||||
|
||||
/* Motion and carousel details shared with the reference layout. */
|
||||
.om-hero h1{animation:om-hero-title 1.35s cubic-bezier(.22,1,.36,1) .25s both}
|
||||
.om-hero-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;object-position:center;background:#181818}
|
||||
.om-hero-bottom{animation:om-hero-title 1.2s cubic-bezier(.22,1,.36,1) .9s both}
|
||||
@keyframes om-hero-title{from{opacity:0;transform:translateY(34px)}to{opacity:1;transform:translateY(0)}}
|
||||
.om-motion-ready .om-reveal{opacity:0;transform:translateY(42px);transition:opacity 1s cubic-bezier(.22,1,.36,1),transform 1s cubic-bezier(.22,1,.36,1)}
|
||||
.om-motion-ready .om-reveal.is-visible{opacity:1;transform:translateY(0)}
|
||||
.om-news{height:180svh;margin-bottom:0;position:relative;--grow:0}
|
||||
.om-news-frame{position:sticky;top:0;width:calc(90vw + 10vw * var(--grow));height:calc(68svh + 32svh * var(--grow));min-height:0;margin-inline:auto;top:calc((100svh - (68svh + 32svh * var(--grow))) / 2);transition:none}
|
||||
.om-news-slide img{transform:scale(1.08);transition:transform 6.5s ease-out}
|
||||
.om-news-slide.is-active img{transform:scale(1)}
|
||||
.om-news-overlay{transition:transform .75s cubic-bezier(.22,1,.36,1),opacity .75s}
|
||||
.om-news-slide:not(.is-active) .om-news-overlay{opacity:0;transform:translateY(28px)}
|
||||
.om-progress button.is-active{position:relative;overflow:hidden;background:rgba(255,255,255,.4)}
|
||||
.om-progress button.is-active:before{content:"";position:absolute;inset:0;background:#fff;transform-origin:left;animation:om-progress-fill 6.5s linear forwards}
|
||||
@keyframes om-progress-fill{from{transform:scaleX(0)}to{transform:scaleX(1)}}
|
||||
.om-project img,.om-retreat img{transform:translateY(var(--parallax,0px)) scale(1.08);transition:none}
|
||||
.om-project:hover img{transform:translateY(var(--parallax,0px)) scale(1.11)}
|
||||
.om-project-content h3{transition:letter-spacing .6s}.om-project:hover .om-project-content h3{letter-spacing:.045em}
|
||||
.om-partners{padding-block:150px 110px;text-align:center}
|
||||
.om-partners-intro>p:nth-child(3){font-size:17px;color:#777;line-height:2.2;max-width:780px;margin-inline:auto}
|
||||
.om-video-link{display:inline-block;color:#333;border:1px solid #555;padding:13px 35px;font-size:12px;margin-top:32px;transition:background .3s,color .3s}.om-video-link:hover{background:#242424;color:white}
|
||||
.om-partner-carousel{margin-top:100px;text-align:left}
|
||||
.om-partner-swiper{width:100%;padding-left:5vw;overflow:hidden}
|
||||
.om-partner-swiper .swiper-wrapper{align-items:stretch}
|
||||
.om-partner-swiper .om-partner-card{width:min(50vw,650px);height:auto;flex-shrink:0;user-select:none}
|
||||
.om-partner-swiper .om-partner-card article{height:100%}
|
||||
.om-partner-swiper .om-partner-card img{-webkit-user-drag:none;user-select:none;pointer-events:none}
|
||||
.om-partner-track{position:relative;display:flex;gap:30px;overflow-x:auto;scroll-snap-type:x mandatory;scroll-behavior:smooth;scrollbar-width:none;padding-inline:5vw}.om-partner-track::-webkit-scrollbar{display:none}
|
||||
.om-partner-card{flex:0 0 min(50vw,650px);scroll-snap-align:start;border:1px solid #e4e4e4;background:white}
|
||||
.om-partner-image{height:clamp(310px,31vw,450px);position:relative;overflow:hidden;background:#e5e5e5}
|
||||
.om-partner-image img{object-fit:cover;transition:transform 1.1s cubic-bezier(.22,1,.36,1)}.om-partner-card:hover img{transform:scale(1.045)}
|
||||
.om-partner-caption{min-height:125px;display:flex;flex-direction:column;justify-content:center;padding:23px 32px;text-align:left}.om-partner-caption h3{font-size:22px;color:#34404d}.om-partner-caption span{font:13px Arial,sans-serif;color:#9a9da0;margin-top:7px;direction:ltr}
|
||||
.om-partner-controls{display:flex;align-items:center;gap:24px;direction:ltr;width:min(840px,85vw);margin:65px auto 0}.om-partner-controls button{border:0;background:none;font:28px Arial;color:#333;cursor:pointer;padding:3px 6px;transition:transform .3s}.om-partner-controls button:hover{transform:scale(1.2)}
|
||||
.om-partner-progress{height:2px;background:#ddd;flex:1;position:relative;overflow:hidden}.om-partner-progress span{position:absolute;left:0;top:0;height:100%;width:calc(100% / 6);background:#222;transition:transform .65s cubic-bezier(.65,0,.35,1)}
|
||||
.om-featured-news{height:520px;position:relative;margin-bottom:0;overflow:hidden;color:white}.om-featured-news img{object-fit:cover;transition:transform 1.2s}.om-featured-news:hover img{transform:scale(1.035)}.om-featured-news:after{content:"";position:absolute;inset:0;background:linear-gradient(0deg,rgba(0,0,0,.72),transparent 70%)}.om-featured-news>div{position:absolute;z-index:2;right:5%;bottom:12%}.om-featured-news small{font-size:12px}.om-featured-news h2{color:#fff;font-size:clamp(27px,3.7vw,52px);margin:12px 0 20px}.om-featured-news .om-link{color:#fff}
|
||||
@media(max-width:700px){.om-news{height:155svh}.om-news-frame{width:calc(90vw + 10vw * var(--grow));height:calc(62svh + 38svh * var(--grow));top:calc((100svh - (62svh + 38svh * var(--grow))) / 2);min-height:0}.om-news-overlay{bottom:80px}.om-partners{padding-block:100px 80px}.om-partners-intro>p:nth-child(3){font-size:14px}.om-partner-carousel{margin-top:65px}.om-partner-track{gap:16px;padding-inline:18px}.om-partner-card{flex-basis:85vw}.om-partner-image{height:340px}.om-partner-caption{min-height:110px;padding:20px}.om-partner-caption h3{font-size:19px}.om-partner-controls{margin-top:35px;width:calc(100% - 40px)}.om-featured-news{height:460px;width:100%}.om-featured-news>div{right:25px;left:25px;bottom:35px}}
|
||||
@media(prefers-reduced-motion:reduce){.om-motion-ready .om-reveal{opacity:1;transform:none}.om-news{height:auto}.om-news-frame{position:relative;top:auto;width:100%;height:70svh;min-height:450px}.om-project img,.om-retreat img{transform:none!important}.om-partner-track{scroll-behavior:auto}}
|
||||
@media(max-width:700px){.om-partner-swiper{padding-left:18px}.om-partner-swiper .om-partner-card{width:85vw}}
|
||||
|
||||
/* Our Story follows the reference's centered editorial composition. */
|
||||
.om-story{display:flex;flex-direction:column;align-items:center;gap:0;padding-block:35px 50px;text-align:center}
|
||||
.om-story-intro{width:100%}
|
||||
.om-story .om-eyebrow{color:#9b8870;font-size:12px;margin-bottom:26px!important}
|
||||
.om-story h2{color:#111a3b;font-size:clamp(42px,4.2vw,58px);font-weight:300;line-height:1.2;margin-bottom:30px}
|
||||
.om-story>div:first-child>p:nth-child(3){max-width:600px;margin:0 auto!important;color:#17203d;font-size:16px;line-height:1.7}
|
||||
.om-stats{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));width:min(1050px,100%);gap:0;margin-top:145px;text-align:right;align-items:stretch}
|
||||
.om-stats div{display:flex;flex-direction:column;justify-content:center;align-items:flex-start;gap:12px;min-height:125px;padding-inline:25px;border-inline-start:1px solid #bfc2d1}
|
||||
.om-stats strong{font-family:var(--font-iranyekan),sans-serif;font-size:clamp(51px,5.4vw,78px);font-weight:300;line-height:1.2;color:#101838;direction:rtl}
|
||||
.om-stats span{font-size:15px;color:#737783}
|
||||
.om-story-cta{display:flex;align-items:center;justify-content:center;width:min(307px,100%);height:42px;margin-top:150px;background:#f6f4f3;color:#101838;font-size:13px;transition:background .3s,color .3s}
|
||||
.om-story-cta:hover{background:#101838;color:#fff}
|
||||
.om-retreat,.om-atelier{overflow:hidden}
|
||||
.om-news-slide::after,.om-retreat::after,.om-atelier::after,.om-featured-news::after{bottom:0}
|
||||
@media(max-width:700px){.om-brand img{width:96px}}
|
||||
@media(max-width:700px){.om-story{padding-block:100px 85px}.om-story h2{font-size:clamp(42px,10vw,58px);margin-bottom:24px}.om-story>div:first-child>p:nth-child(3){font-size:14px;line-height:2.15}.om-stats{margin-top:80px}.om-stats div{min-height:105px;padding-inline:10px;gap:9px}.om-stats strong{font-size:clamp(39px,10vw,55px)}.om-stats span{font-size:11px}.om-story-cta{margin-top:85px}}
|
||||
@@ -0,0 +1,60 @@
|
||||
import { About } from "@/components/About";
|
||||
import { ContactCta } from "@/components/ContactCta";
|
||||
import { Hero } from "@/components/Hero";
|
||||
import { Marquee } from "@/components/Marquee";
|
||||
import { News } from "@/components/News";
|
||||
import { Projects } from "@/components/Projects";
|
||||
import { Services } from "@/components/Services";
|
||||
import { SiteFooter } from "@/components/SiteFooter";
|
||||
import { SiteHeader } from "@/components/SiteHeader";
|
||||
import { contact, site } from "@/data/site";
|
||||
import { getRecentNews, getRecentPortfolios, toDisplayNewsItem, toDisplayProject } from "@/lib/api";
|
||||
|
||||
export const revalidate = 300; // keep in sync with api.ts PAGE_REVALIDATE_SECONDS
|
||||
|
||||
const organizationJsonLd = {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "Organization",
|
||||
name: site.name,
|
||||
url: site.url,
|
||||
slogan: site.tagline,
|
||||
foundingDate: "2000",
|
||||
address: {
|
||||
"@type": "PostalAddress",
|
||||
addressLocality: "قم",
|
||||
addressCountry: "IR",
|
||||
streetAddress: contact.address,
|
||||
},
|
||||
telephone: contact.phoneLinks,
|
||||
email: contact.email,
|
||||
sameAs: [contact.instagramUrl],
|
||||
};
|
||||
|
||||
export default async function HomePage() {
|
||||
const [portfolios, newsPosts] = await Promise.all([getRecentPortfolios(12), getRecentNews(3)]);
|
||||
const displayProjects = portfolios.map(toDisplayProject);
|
||||
const displayNews = newsPosts.map(toDisplayNewsItem);
|
||||
|
||||
return (
|
||||
<>
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(organizationJsonLd) }}
|
||||
/>
|
||||
|
||||
<SiteHeader />
|
||||
|
||||
<main>
|
||||
<Hero />
|
||||
<About />
|
||||
<Marquee />
|
||||
<Projects projects={displayProjects} />
|
||||
<Services />
|
||||
<News items={displayNews} />
|
||||
<ContactCta />
|
||||
</main>
|
||||
|
||||
<SiteFooter />
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user