Add en title
This commit is contained in:
@@ -36,7 +36,6 @@ export default async function AboutPage() {
|
|||||||
<PageHero
|
<PageHero
|
||||||
image={heroImage}
|
image={heroImage}
|
||||||
imageMobile={heroImageMobile}
|
imageMobile={heroImageMobile}
|
||||||
caption={aboutPage.hero.caption}
|
|
||||||
title={aboutPage.hero.title}
|
title={aboutPage.hero.title}
|
||||||
breadcrumb={aboutPage.hero.breadcrumb}
|
breadcrumb={aboutPage.hero.breadcrumb}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -45,7 +45,12 @@ export default async function BlogDetailPage({
|
|||||||
<>
|
<>
|
||||||
<SiteHeader />
|
<SiteHeader />
|
||||||
<main>
|
<main>
|
||||||
<NewsArticleDetail item={toDisplayNewsItem(item)} basePath="/blog" sectionLabel="بلاگ" />
|
<NewsArticleDetail
|
||||||
|
item={toDisplayNewsItem(item)}
|
||||||
|
basePath="/blog"
|
||||||
|
sectionLabel="بلاگ"
|
||||||
|
sectionLabelEn="BLOG"
|
||||||
|
/>
|
||||||
</main>
|
</main>
|
||||||
<SiteFooter />
|
<SiteFooter />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export default async function BlogListPage({
|
|||||||
<PageHero
|
<PageHero
|
||||||
image={heroImage}
|
image={heroImage}
|
||||||
imageMobile={heroImageMobile}
|
imageMobile={heroImageMobile}
|
||||||
caption={blogListingPage.hero.caption}
|
|
||||||
title={blogListingPage.hero.title}
|
title={blogListingPage.hero.title}
|
||||||
breadcrumb={blogListingPage.hero.breadcrumb}
|
breadcrumb={blogListingPage.hero.breadcrumb}
|
||||||
/>
|
/>
|
||||||
|
|||||||
+4
-8
@@ -17,13 +17,9 @@ export const iranYekan = localFont({
|
|||||||
preload: true,
|
preload: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
/** Montserrat — used for the Latin project names and section kickers. */
|
/** Arual — used for every English title/kicker across the site. */
|
||||||
export const montserrat = localFont({
|
export const arual = localFont({
|
||||||
src: [
|
src: "../assets/fonts/arual.ttf",
|
||||||
{ path: "../assets/fonts/montserrat-light.ttf", weight: "300", style: "normal" },
|
variable: "--font-arual",
|
||||||
{ path: "../assets/fonts/montserrat-regular.woff2", weight: "400", style: "normal" },
|
|
||||||
{ path: "../assets/fonts/montserrat-bold.ttf", weight: "700", style: "normal" },
|
|
||||||
],
|
|
||||||
variable: "--font-montserrat",
|
|
||||||
display: "swap",
|
display: "swap",
|
||||||
});
|
});
|
||||||
|
|||||||
+1
-1
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
/* ---- type ---------------------------------------------------------- */
|
/* ---- type ---------------------------------------------------------- */
|
||||||
--font-sans: var(--font-iranyekan), system-ui, sans-serif;
|
--font-sans: var(--font-iranyekan), system-ui, sans-serif;
|
||||||
--font-latin: var(--font-montserrat), system-ui, sans-serif;
|
--font-latin: var(--font-arual), Arial, sans-serif;
|
||||||
|
|
||||||
/* ---- motion -------------------------------------------------------- */
|
/* ---- motion -------------------------------------------------------- */
|
||||||
--ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
|
--ease-out-soft: cubic-bezier(0.22, 1, 0.36, 1);
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
import type { Metadata, Viewport } from "next";
|
import type { Metadata, Viewport } from "next";
|
||||||
|
|
||||||
import { iranYekan, montserrat } from "./fonts";
|
import { arual, iranYekan } from "./fonts";
|
||||||
import { site } from "@/data/site";
|
import { site } from "@/data/site";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ export default function RootLayout({
|
|||||||
children,
|
children,
|
||||||
}: Readonly<{ children: React.ReactNode }>) {
|
}: Readonly<{ children: React.ReactNode }>) {
|
||||||
return (
|
return (
|
||||||
<html lang="fa" dir="rtl" className={`${iranYekan.variable} ${montserrat.variable}`}>
|
<html lang="fa" dir="rtl" className={`${iranYekan.variable} ${arual.variable}`}>
|
||||||
<head>
|
<head>
|
||||||
{/* Scroll reveals are a JS enhancement — without it, show everything. */}
|
{/* Scroll reveals are a JS enhancement — without it, show everything. */}
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { NewsCarousel } from "@/components/news/NewsCarousel";
|
|||||||
import { Editorial } from "@/components/shared/Editorial";
|
import { Editorial } from "@/components/shared/Editorial";
|
||||||
import { PageCta } from "@/components/shared/PageCta";
|
import { PageCta } from "@/components/shared/PageCta";
|
||||||
import { PageHero } from "@/components/shared/PageHero";
|
import { PageHero } from "@/components/shared/PageHero";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
import { SiteFooter } from "@/components/SiteFooter";
|
import { SiteFooter } from "@/components/SiteFooter";
|
||||||
import { SiteHeader } from "@/components/SiteHeader";
|
import { SiteHeader } from "@/components/SiteHeader";
|
||||||
import { newsAndBlogPage } from "@/data/site";
|
import { newsAndBlogPage } from "@/data/site";
|
||||||
@@ -37,7 +38,6 @@ export default async function NewsAndBlogPage() {
|
|||||||
<PageHero
|
<PageHero
|
||||||
image={heroImage}
|
image={heroImage}
|
||||||
imageMobile={heroImageMobile}
|
imageMobile={heroImageMobile}
|
||||||
caption={newsAndBlogPage.hero.caption}
|
|
||||||
title={newsAndBlogPage.hero.title}
|
title={newsAndBlogPage.hero.title}
|
||||||
breadcrumb={newsAndBlogPage.hero.breadcrumb}
|
breadcrumb={newsAndBlogPage.hero.breadcrumb}
|
||||||
/>
|
/>
|
||||||
@@ -62,10 +62,11 @@ export default async function NewsAndBlogPage() {
|
|||||||
<section className="bg-sand py-16 lg:py-24">
|
<section className="bg-sand py-16 lg:py-24">
|
||||||
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-10 justify-center">
|
<SectionLabel
|
||||||
{newsAndBlogPage.news.caption}
|
fa={newsAndBlogPage.news.caption}
|
||||||
<span className="latin">{newsAndBlogPage.news.captionLatin}</span>
|
en={newsAndBlogPage.news.captionLatin}
|
||||||
</p>
|
className="mb-10"
|
||||||
|
/>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -86,10 +87,11 @@ export default async function NewsAndBlogPage() {
|
|||||||
<section className={`py-16 lg:py-24 ${hasNews ? "bg-paper" : "bg-sand"}`}>
|
<section className={`py-16 lg:py-24 ${hasNews ? "bg-paper" : "bg-sand"}`}>
|
||||||
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-10 justify-center">
|
<SectionLabel
|
||||||
{newsAndBlogPage.blog.caption}
|
fa={newsAndBlogPage.blog.caption}
|
||||||
<span className="latin">{newsAndBlogPage.blog.captionLatin}</span>
|
en={newsAndBlogPage.blog.captionLatin}
|
||||||
</p>
|
className="mb-10"
|
||||||
|
/>
|
||||||
</Reveal>
|
</Reveal>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -45,7 +45,12 @@ export default async function NewsDetailPage({
|
|||||||
<>
|
<>
|
||||||
<SiteHeader />
|
<SiteHeader />
|
||||||
<main>
|
<main>
|
||||||
<NewsArticleDetail item={toDisplayNewsItem(item)} basePath="/news" sectionLabel="اخبار" />
|
<NewsArticleDetail
|
||||||
|
item={toDisplayNewsItem(item)}
|
||||||
|
basePath="/news"
|
||||||
|
sectionLabel="اخبار"
|
||||||
|
sectionLabelEn="NEWS"
|
||||||
|
/>
|
||||||
</main>
|
</main>
|
||||||
<SiteFooter />
|
<SiteFooter />
|
||||||
</>
|
</>
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ export default async function NewsListPage({
|
|||||||
<PageHero
|
<PageHero
|
||||||
image={heroImage}
|
image={heroImage}
|
||||||
imageMobile={heroImageMobile}
|
imageMobile={heroImageMobile}
|
||||||
caption={newsListingPage.hero.caption}
|
|
||||||
title={newsListingPage.hero.title}
|
title={newsListingPage.hero.title}
|
||||||
breadcrumb={newsListingPage.hero.breadcrumb}
|
breadcrumb={newsListingPage.hero.breadcrumb}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export default async function ServicesPage() {
|
|||||||
<main>
|
<main>
|
||||||
<PageHero
|
<PageHero
|
||||||
image={servicesHero}
|
image={servicesHero}
|
||||||
caption={servicesPage.hero.caption}
|
|
||||||
title={servicesPage.hero.title}
|
title={servicesPage.hero.title}
|
||||||
breadcrumb={servicesPage.hero.breadcrumb}
|
breadcrumb={servicesPage.hero.breadcrumb}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { ProjectVideo } from "@/components/portfolios/ProjectVideo";
|
|||||||
import { Reveal } from "@/components/Reveal";
|
import { Reveal } from "@/components/Reveal";
|
||||||
import { PageCta } from "@/components/shared/PageCta";
|
import { PageCta } from "@/components/shared/PageCta";
|
||||||
import { PageHero } from "@/components/shared/PageHero";
|
import { PageHero } from "@/components/shared/PageHero";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
import { SiteFooter } from "@/components/SiteFooter";
|
import { SiteFooter } from "@/components/SiteFooter";
|
||||||
import { SiteHeader } from "@/components/SiteHeader";
|
import { SiteHeader } from "@/components/SiteHeader";
|
||||||
import { portfoliosPage, projectVideos } from "@/data/site";
|
import { portfoliosPage, projectVideos } from "@/data/site";
|
||||||
@@ -33,12 +34,6 @@ export async function generateMetadata({
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const publishedDateFormatter = new Intl.DateTimeFormat("fa-IR", {
|
|
||||||
year: "numeric",
|
|
||||||
month: "long",
|
|
||||||
day: "numeric",
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Structurally inspired by binghatti.com's project pages (hero, in-page jump
|
* Structurally inspired by binghatti.com's project pages (hero, in-page jump
|
||||||
* nav, gallery grid, closing CTA) — but scoped strictly to what the meshkee
|
* nav, gallery grid, closing CTA) — but scoped strictly to what the meshkee
|
||||||
@@ -58,7 +53,6 @@ export default async function PortfolioDetailPage({
|
|||||||
const project = toDisplayProject(item);
|
const project = toDisplayProject(item);
|
||||||
const hasGallery = project.gallery.length > 0;
|
const hasGallery = project.gallery.length > 0;
|
||||||
const videoUrl = projectVideos[project.slug];
|
const videoUrl = projectVideos[project.slug];
|
||||||
const publishedLabel = publishedDateFormatter.format(new Date(project.publishedAt));
|
|
||||||
|
|
||||||
const navItems = [
|
const navItems = [
|
||||||
{ id: "overview", label: "معرفی پروژه" },
|
{ id: "overview", label: "معرفی پروژه" },
|
||||||
@@ -79,7 +73,6 @@ export default async function PortfolioDetailPage({
|
|||||||
<main>
|
<main>
|
||||||
<PageHero
|
<PageHero
|
||||||
image={project.image}
|
image={project.image}
|
||||||
caption={project.type}
|
|
||||||
title={project.name}
|
title={project.name}
|
||||||
breadcrumb={[
|
breadcrumb={[
|
||||||
{ label: "صفحه اصلی", href: "/" },
|
{ label: "صفحه اصلی", href: "/" },
|
||||||
@@ -109,12 +102,7 @@ export default async function PortfolioDetailPage({
|
|||||||
<section id="overview" className="scroll-mt-20 bg-paper py-16 lg:py-24">
|
<section id="overview" className="scroll-mt-20 bg-paper py-16 lg:py-24">
|
||||||
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<div className="mx-auto mb-14 flex max-w-[738px] flex-col items-center gap-3 text-center">
|
<SectionLabel fa="معرفی پروژه" en="PROJECT OVERVIEW" className="mb-14" />
|
||||||
<p className="kicker justify-center">معرفی پروژه</p>
|
|
||||||
<time dateTime={project.publishedAt} className="text-xs text-muted">
|
|
||||||
{publishedLabel}
|
|
||||||
</time>
|
|
||||||
</div>
|
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
{project.bodyHtml && (
|
{project.bodyHtml && (
|
||||||
@@ -129,7 +117,7 @@ export default async function PortfolioDetailPage({
|
|||||||
<section id="video" className={`scroll-mt-20 ${videoBg} py-16 lg:py-24`}>
|
<section id="video" className={`scroll-mt-20 ${videoBg} py-16 lg:py-24`}>
|
||||||
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-14 justify-center">ویدیو</p>
|
<SectionLabel fa="ویدیو" en="VIDEO" className="mb-14" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<Reveal delay={90}>
|
<Reveal delay={90}>
|
||||||
@@ -143,7 +131,7 @@ export default async function PortfolioDetailPage({
|
|||||||
<section id="gallery" className={`scroll-mt-20 ${galleryBg} py-16 lg:py-24`}>
|
<section id="gallery" className={`scroll-mt-20 ${galleryBg} py-16 lg:py-24`}>
|
||||||
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-14 justify-center">گالری تصاویر</p>
|
<SectionLabel fa="گالری تصاویر" en="GALLERY" className="mb-14" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<ProjectGalleryLightbox images={project.gallery} alt={project.name} />
|
<ProjectGalleryLightbox images={project.gallery} alt={project.name} />
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ export default async function PortfoliosPage() {
|
|||||||
<PageHero
|
<PageHero
|
||||||
image={heroImage}
|
image={heroImage}
|
||||||
imageMobile={heroImageMobile}
|
imageMobile={heroImageMobile}
|
||||||
caption={portfoliosPage.hero.caption}
|
|
||||||
title={portfoliosPage.hero.title}
|
title={portfoliosPage.hero.title}
|
||||||
breadcrumb={portfoliosPage.hero.breadcrumb}
|
breadcrumb={portfoliosPage.hero.breadcrumb}
|
||||||
/>
|
/>
|
||||||
|
|||||||
Binary file not shown.
@@ -3,6 +3,7 @@ import Link from "next/link";
|
|||||||
|
|
||||||
import aboutImage from "@/assets/images/about/about.jpg";
|
import aboutImage from "@/assets/images/about/about.jpg";
|
||||||
import { Reveal } from "@/components/Reveal";
|
import { Reveal } from "@/components/Reveal";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
import { about, site, stats } from "@/data/site";
|
import { about, site, stats } from "@/data/site";
|
||||||
|
|
||||||
export function About() {
|
export function About() {
|
||||||
@@ -48,7 +49,7 @@ export function About() {
|
|||||||
|
|
||||||
<div className="order-1 lg:order-2">
|
<div className="order-1 lg:order-2">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-7">{about.kicker}</p>
|
<SectionLabel fa={about.kicker} en="ABOUT US" align="start" className="mb-7" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<Reveal delay={90}>
|
<Reveal delay={90}>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import Link from "next/link";
|
|||||||
|
|
||||||
import ctaImage from "@/assets/images/projects/elysium.jpg";
|
import ctaImage from "@/assets/images/projects/elysium.jpg";
|
||||||
import { Reveal } from "@/components/Reveal";
|
import { Reveal } from "@/components/Reveal";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
import { contact } from "@/data/site";
|
import { contact } from "@/data/site";
|
||||||
|
|
||||||
export function ContactCta() {
|
export function ContactCta() {
|
||||||
@@ -21,9 +22,7 @@ export function ContactCta() {
|
|||||||
|
|
||||||
<div className="mx-auto max-w-[1400px] px-5 text-center lg:px-10">
|
<div className="mx-auto max-w-[1400px] px-5 text-center lg:px-10">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-7 justify-center">
|
<SectionLabel fa="تماس با ما" en="GET IN TOUCH" className="mb-7" />
|
||||||
<span className="latin">GET IN TOUCH</span>
|
|
||||||
</p>
|
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<Reveal delay={90}>
|
<Reveal delay={90}>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Image from "next/image";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
import { Reveal } from "@/components/Reveal";
|
import { Reveal } from "@/components/Reveal";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
import type { DisplayNewsItem } from "@/lib/api";
|
import type { DisplayNewsItem } from "@/lib/api";
|
||||||
|
|
||||||
export function News({ items }: { items: DisplayNewsItem[] }) {
|
export function News({ items }: { items: DisplayNewsItem[] }) {
|
||||||
@@ -13,9 +14,7 @@ export function News({ items }: { items: DisplayNewsItem[] }) {
|
|||||||
<div className="flex flex-wrap items-end justify-between gap-8">
|
<div className="flex flex-wrap items-end justify-between gap-8">
|
||||||
<div>
|
<div>
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-6">
|
<SectionLabel fa="مقالات و اخبار" en="NEWS & ARTICLES" align="start" className="mb-6" />
|
||||||
<span className="latin">NEWS & ARTICLES</span>
|
|
||||||
</p>
|
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={90}>
|
<Reveal delay={90}>
|
||||||
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-ink lg:text-[2.625rem]">
|
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-ink lg:text-[2.625rem]">
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import Link from "next/link";
|
|||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import { Reveal } from "@/components/Reveal";
|
import { Reveal } from "@/components/Reveal";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
import type { DisplayProject } from "@/lib/api";
|
import type { DisplayProject } from "@/lib/api";
|
||||||
|
|
||||||
export function Projects({ projects }: { projects: DisplayProject[] }) {
|
export function Projects({ projects }: { projects: DisplayProject[] }) {
|
||||||
@@ -49,9 +50,7 @@ export function Projects({ projects }: { projects: DisplayProject[] }) {
|
|||||||
<div className="flex flex-wrap items-end justify-between gap-8">
|
<div className="flex flex-wrap items-end justify-between gap-8">
|
||||||
<div>
|
<div>
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-6">
|
<SectionLabel fa="نمونه کارها" en="PORTFOLIO" align="start" className="mb-6" />
|
||||||
<span className="latin">PORTFOLIO</span>
|
|
||||||
</p>
|
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={90}>
|
<Reveal delay={90}>
|
||||||
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-white lg:text-[2.625rem]">
|
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-white lg:text-[2.625rem]">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Link from "next/link";
|
|||||||
|
|
||||||
import { Reveal } from "@/components/Reveal";
|
import { Reveal } from "@/components/Reveal";
|
||||||
import { serviceIcons } from "@/components/ServiceIcons";
|
import { serviceIcons } from "@/components/ServiceIcons";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
import { services } from "@/data/site";
|
import { services } from "@/data/site";
|
||||||
|
|
||||||
export function Services() {
|
export function Services() {
|
||||||
@@ -11,9 +12,7 @@ export function Services() {
|
|||||||
<div className="flex flex-wrap items-end justify-between gap-8">
|
<div className="flex flex-wrap items-end justify-between gap-8">
|
||||||
<div>
|
<div>
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-6">
|
<SectionLabel fa="خدمات ما" en="WHAT WE DO" align="start" className="mb-6" />
|
||||||
<span className="latin">WHAT WE DO</span>
|
|
||||||
</p>
|
|
||||||
</Reveal>
|
</Reveal>
|
||||||
<Reveal delay={90}>
|
<Reveal delay={90}>
|
||||||
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-ink lg:text-[2.625rem]">
|
<h2 className="text-[1.625rem] leading-[1.25] font-extrabold whitespace-nowrap text-ink lg:text-[2.625rem]">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { ProjectGalleryLightbox } from "@/components/portfolios/ProjectGalleryLi
|
|||||||
import { Reveal } from "@/components/Reveal";
|
import { Reveal } from "@/components/Reveal";
|
||||||
import { PageCta } from "@/components/shared/PageCta";
|
import { PageCta } from "@/components/shared/PageCta";
|
||||||
import { PageHero } from "@/components/shared/PageHero";
|
import { PageHero } from "@/components/shared/PageHero";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
import { newsAndBlogPage } from "@/data/site";
|
import { newsAndBlogPage } from "@/data/site";
|
||||||
import type { DisplayNewsItem } from "@/lib/api";
|
import type { DisplayNewsItem } from "@/lib/api";
|
||||||
|
|
||||||
@@ -18,10 +19,12 @@ export function NewsArticleDetail({
|
|||||||
item,
|
item,
|
||||||
basePath,
|
basePath,
|
||||||
sectionLabel,
|
sectionLabel,
|
||||||
|
sectionLabelEn,
|
||||||
}: {
|
}: {
|
||||||
item: DisplayNewsItem;
|
item: DisplayNewsItem;
|
||||||
basePath: string;
|
basePath: string;
|
||||||
sectionLabel: string;
|
sectionLabel: string;
|
||||||
|
sectionLabelEn: string;
|
||||||
}) {
|
}) {
|
||||||
const hasGallery = item.gallery.length > 0;
|
const hasGallery = item.gallery.length > 0;
|
||||||
|
|
||||||
@@ -34,7 +37,6 @@ export function NewsArticleDetail({
|
|||||||
<>
|
<>
|
||||||
<PageHero
|
<PageHero
|
||||||
image={item.image}
|
image={item.image}
|
||||||
caption={item.date}
|
|
||||||
title={item.title}
|
title={item.title}
|
||||||
breadcrumb={[
|
breadcrumb={[
|
||||||
{ label: "صفحه اصلی", href: "/" },
|
{ label: "صفحه اصلی", href: "/" },
|
||||||
@@ -64,12 +66,7 @@ export function NewsArticleDetail({
|
|||||||
<section id="overview" className="scroll-mt-20 bg-paper py-16 lg:py-24">
|
<section id="overview" className="scroll-mt-20 bg-paper py-16 lg:py-24">
|
||||||
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<div className="mx-auto mb-14 flex max-w-[738px] flex-col items-center gap-3 text-center">
|
<SectionLabel fa={sectionLabel} en={sectionLabelEn} className="mb-14" />
|
||||||
<p className="kicker justify-center">{sectionLabel}</p>
|
|
||||||
<time dateTime={item.publishedAt} className="text-xs text-muted">
|
|
||||||
{item.date}
|
|
||||||
</time>
|
|
||||||
</div>
|
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
{item.bodyHtml && (
|
{item.bodyHtml && (
|
||||||
@@ -84,7 +81,7 @@ export function NewsArticleDetail({
|
|||||||
<section id="gallery" className="scroll-mt-20 bg-sand py-16 lg:py-24">
|
<section id="gallery" className="scroll-mt-20 bg-sand py-16 lg:py-24">
|
||||||
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
<div className="mx-auto max-w-[1400px] px-6 lg:px-12">
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="kicker mb-14 justify-center">گالری تصاویر</p>
|
<SectionLabel fa="گالری تصاویر" en="GALLERY" className="mb-14" />
|
||||||
</Reveal>
|
</Reveal>
|
||||||
|
|
||||||
<ProjectGalleryLightbox images={item.gallery} alt={item.title} />
|
<ProjectGalleryLightbox images={item.gallery} alt={item.title} />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { Reveal } from "@/components/Reveal";
|
import { Reveal } from "@/components/Reveal";
|
||||||
|
import { SectionLabel } from "@/components/shared/SectionLabel";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The centred editorial column that carries every section of this page:
|
* The centred editorial column that carries every section of this page:
|
||||||
@@ -32,17 +33,9 @@ export function Editorial({
|
|||||||
compact ? "py-12 lg:py-16" : "py-16 lg:py-24"
|
compact ? "py-12 lg:py-16" : "py-16 lg:py-24"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{(caption || captionLatin) && (
|
{caption && captionLatin && (
|
||||||
<Reveal>
|
<Reveal>
|
||||||
<p className="flex items-center justify-center gap-3 text-sm text-gold">
|
<SectionLabel fa={caption} en={captionLatin} tone={tone} />
|
||||||
{caption}
|
|
||||||
{caption && captionLatin && (
|
|
||||||
<span aria-hidden="true" className="h-1 w-1 rotate-45 bg-gold/60" />
|
|
||||||
)}
|
|
||||||
{captionLatin && (
|
|
||||||
<span className="latin tracking-[0.1em]">{captionLatin}</span>
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
</Reveal>
|
</Reveal>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -11,20 +11,18 @@ function isStaticImage(src: ImageSource): src is StaticImageData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Full-bleed opening plate shared by the inner pages: tracked Latin kicker,
|
* Full-bleed opening plate shared by the inner pages: light Persian title,
|
||||||
* light Persian title, breadcrumb — set over a darkened image.
|
* breadcrumb — set over a darkened image.
|
||||||
*/
|
*/
|
||||||
export function PageHero({
|
export function PageHero({
|
||||||
image,
|
image,
|
||||||
imageMobile,
|
imageMobile,
|
||||||
caption,
|
|
||||||
title,
|
title,
|
||||||
breadcrumb,
|
breadcrumb,
|
||||||
}: {
|
}: {
|
||||||
image: ImageSource;
|
image: ImageSource;
|
||||||
/** Portrait crop, when one exists. Falls back to the landscape plate. */
|
/** Portrait crop, when one exists. Falls back to the landscape plate. */
|
||||||
imageMobile?: ImageSource;
|
imageMobile?: ImageSource;
|
||||||
caption: string;
|
|
||||||
title: string;
|
title: string;
|
||||||
breadcrumb: readonly Crumb[];
|
breadcrumb: readonly Crumb[];
|
||||||
}) {
|
}) {
|
||||||
@@ -59,10 +57,8 @@ export function PageHero({
|
|||||||
<div className="absolute inset-0 bg-gradient-to-b from-black/55 via-black/25 to-black/75" />
|
<div className="absolute inset-0 bg-gradient-to-b from-black/55 via-black/25 to-black/75" />
|
||||||
|
|
||||||
<div className="relative z-10 mx-auto w-full max-w-[1400px] px-6 pb-20 text-center lg:px-12 lg:pb-28">
|
<div className="relative z-10 mx-auto w-full max-w-[1400px] px-6 pb-20 text-center lg:px-12 lg:pb-28">
|
||||||
<p className="latin animate-fade-up text-xs tracking-[0.4em] text-gold">{caption}</p>
|
|
||||||
|
|
||||||
<h1
|
<h1
|
||||||
className="mt-6 animate-fade-up text-4xl font-light text-white sm:text-5xl lg:text-6xl"
|
className="animate-fade-up text-4xl font-light text-white sm:text-5xl lg:text-6xl"
|
||||||
style={{ animationDelay: "140ms" }}
|
style={{ animationDelay: "140ms" }}
|
||||||
>
|
>
|
||||||
{title}
|
{title}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
/**
|
||||||
|
* The section-label pattern used across the site: a short gold rule above a
|
||||||
|
* Persian title, with its English equivalent set tight beneath it at 70%
|
||||||
|
* opacity. Replaces the old inline "text + trailing dash" kicker — no
|
||||||
|
* letter-spacing on the Persian line (it breaks the cursive joining), tracked
|
||||||
|
* spacing on the Latin line only.
|
||||||
|
*/
|
||||||
|
export function SectionLabel({
|
||||||
|
fa,
|
||||||
|
en,
|
||||||
|
tone = "light",
|
||||||
|
align = "center",
|
||||||
|
className = "",
|
||||||
|
}: {
|
||||||
|
fa: string;
|
||||||
|
en: string;
|
||||||
|
tone?: "light" | "dark";
|
||||||
|
/** "start" left-aligns everything (RTL: right-aligned to the block's start edge). */
|
||||||
|
align?: "center" | "start";
|
||||||
|
className?: string;
|
||||||
|
}) {
|
||||||
|
const alignClass = align === "start" ? "items-start text-start" : "items-center text-center";
|
||||||
|
const color = tone === "dark" ? "text-gold-soft" : "text-gold";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={`flex flex-col gap-4 ${alignClass} ${className}`}>
|
||||||
|
<span aria-hidden="true" className="h-px w-10 bg-gold" />
|
||||||
|
<div className={`flex flex-col gap-1 ${alignClass}`}>
|
||||||
|
<p className={`text-xl ${color}`}>{fa}</p>
|
||||||
|
<p className={`latin text-sm tracking-[0.15em] opacity-70 ${color}`}>{en}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user