feat: Enhance cultural heritage site pages with new components and styling

- Added a new "HeritageSeal" SVG component for branding consistency across the site.
- Updated "CulturalSpaceExplorer" to include a descriptive header and introductory text.
- Modified "SiteHeader" to replace the logo with the new "HeritageSeal".
- Enhanced "HeritageDetailPage" with improved layout and styling for better readability.
- Updated "HeritageListPage" to include new visual elements and improved section layouts.
- Revamped "AboutPage" with new content structure and visual enhancements.
- Improved "ContactPage" with a visually appealing hero section and map integration.
- Updated global styles for better visual consistency and improved accessibility.
- Introduced "SectionDivider" component for decorative separation in content sections.
This commit is contained in:
tuyenio
2026-06-26 16:17:24 +07:00
parent 6bc57fe144
commit e081f54bd4
12 changed files with 327 additions and 105 deletions
+34 -21
View File
@@ -3,6 +3,7 @@ import Image from "next/image";
import { Boxes, ScanLine, Sparkles } from "lucide-react";
import AntiquityShowcaseClient from "./AntiquityShowcaseClient";
import { BackToTopButton } from "@/app/components/landing/BackToTopButton";
import { HeritageSeal } from "@/app/components/landing/HeritageOrnament";
import { SiteFooter } from "@/app/components/landing/SiteFooter";
import { SiteHeader } from "@/app/components/landing/SiteHeader";
import { antiquities, subpageHero } from "@/app/content";
@@ -25,14 +26,14 @@ export default function AntiquityPage() {
<SiteHeader />
<section className="public-subpage-hero public-subpage-hero--compact relative isolate overflow-hidden border-b border-[var(--surface-border)]">
<Image src={subpageHero.coVat} alt="" fill priority sizes="100vw" className="public-subpage-hero-image public-parallax-bg object-cover" />
<div className="public-subpage-hero-content mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:py-22">
<Image src={subpageHero.coVat} alt="" fill preload sizes="100vw" className="public-subpage-hero-image public-parallax-bg object-cover" />
<div className="public-subpage-hero-content mx-auto max-w-7xl px-4 py-20 sm:px-6 lg:py-28">
<div className="public-rise public-hero-rule max-w-4xl">
<p className="public-kicker">Phòng trưng bày số</p>
<h1 className="public-gradient-text public-heading-safe mt-4 text-[clamp(2.55rem,5.8vw,5rem)] font-extrabold">
Cổ vật & hiện vật 3D
<h1 className="public-hero-display public-heading-safe mt-5 text-[clamp(2.55rem,5.8vw,5rem)] font-extrabold">
Cổ vật &amp; <span className="public-display-accent">hiện vật 3D</span>
</h1>
<p className="public-subpage-copy mt-6 max-w-[60ch] text-base leading-8 text-[var(--foreground)]/82 sm:text-lg">
<p className="public-subpage-copy mt-6 max-w-[60ch] text-base leading-8 sm:text-lg">
Bộ sưu tập số các cổ vật tiêu biểu thuộc hệ thống di tích Như Quỳnh. Mỗi hiện vật đưc
dựng thành hình ba chiều đ chiêm ngưỡng trọn vẹn từ mọi góc nhìn.
</p>
@@ -43,27 +44,39 @@ export default function AntiquityPage() {
<AntiquityShowcaseClient items={antiquities} />
<section className="border-t border-[var(--surface-border)] bg-[var(--surface-band)]">
<div className="mx-auto grid max-w-7xl gap-4 px-4 py-14 sm:px-6 md:grid-cols-3 lg:py-18">
{highlights.map((item) => {
const Icon = item.icon;
return (
<article key={item.title} className="public-panel public-stagger-item rounded-[14px] p-6">
<Icon className="h-7 w-7 text-[var(--primary)]" strokeWidth={1.7} />
<h3 className="mt-4 text-lg font-bold text-[var(--tour-ink)]">{item.title}</h3>
<p className="mt-2 text-sm leading-6 text-[var(--muted-foreground)]">{item.detail}</p>
</article>
);
})}
<div className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:py-20">
<div className="mx-auto max-w-2xl text-center">
<p className="public-kicker public-kicker--center"> sao số hóa 3D</p>
<h2 className="public-display public-heading-safe mt-3 text-[2rem] font-bold text-[var(--tour-ink)] sm:text-4xl">
Giá trị của hiện vật số
</h2>
</div>
<div className="mt-12 grid gap-x-8 gap-y-12 md:grid-cols-3">
{highlights.map((item) => {
const Icon = item.icon;
return (
<article key={item.title} className="public-stagger-item flex flex-col items-center text-center">
<span className="relative grid h-16 w-16 place-items-center">
<HeritageSeal className="absolute inset-0 h-full w-full text-[var(--primary)] opacity-35" />
<Icon className="relative h-6 w-6 text-[var(--primary)]" strokeWidth={1.7} />
</span>
<h3 className="mt-6 text-lg font-bold text-[var(--tour-ink)]">{item.title}</h3>
<p className="mt-2.5 max-w-[34ch] text-sm leading-6 text-[var(--muted-foreground)]">{item.detail}</p>
</article>
);
})}
</div>
</div>
</section>
<section className="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:py-16">
<div className="public-panel public-glow rounded-[16px] p-6 text-center sm:p-9">
<p className="public-kicker justify-center">Mở rộng bộ sưu tập</p>
<h2 className="public-display mt-3 text-2xl font-bold text-[var(--tour-ink)] sm:text-3xl">
<section className="mx-auto max-w-7xl px-4 py-14 sm:px-6 lg:py-20">
<div className="public-panel public-glow rounded-[18px] p-8 text-center sm:p-12">
<HeritageSeal className="mx-auto h-10 w-10 text-[var(--primary)] opacity-80" />
<p className="public-kicker public-kicker--center mt-5">Mở rộng bộ sưu tập</p>
<h2 className="public-display public-heading-safe mx-auto mt-3 max-w-[24ch] text-2xl font-bold text-[var(--tour-ink)] sm:text-3xl">
Hiện vật từ các di tích khác đang đưc số hóa
</h2>
<p className="mx-auto mt-3 max-w-[64ch] text-sm leading-7 text-[var(--foreground)]/76 sm:text-base">
<p className="mx-auto mt-4 max-w-[64ch] text-sm leading-7 text-[var(--muted-foreground)] sm:text-base">
Phòng trưng bày sẽ tiếp tục bổ sung các cổ vật, sắc phong, bia đá đ thờ tự tiêu biểu
của toàn bộ hệ thống di tích Như Quỳnh trong giai đoạn 20262030.
</p>
@@ -55,6 +55,16 @@ export function CulturalSpaceExplorer({ sites }: { sites: HeritageSite[] }) {
return (
<section className="mx-auto max-w-7xl px-4 py-14 sm:px-6 lg:py-20">
<div className="mb-8 max-w-3xl">
<p className="public-kicker">Tra cứu di tích</p>
<h2 className="public-display public-heading-safe mt-3 text-[2rem] font-bold text-[var(--tour-ink)] sm:text-4xl">
Tìm theo loại hình & cấp xếp hạng
</h2>
<p className="mt-3 text-sm leading-6 text-[var(--muted-foreground)] sm:text-base">
Lọc nhanh toàn bộ hệ thống di tích đ chọn không gian bạn muốn khám phá.
</p>
</div>
<div className="public-panel rounded-[20px] p-5 sm:p-7">
{/* Hàng tìm kiếm + tóm tắt kết quả */}
<div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
@@ -0,0 +1,37 @@
// Hệ motif "triện di sản" — dấu triện hình hoa thị (hai hình vuông lồng nhau tạo sao tám
// cánh) gợi con dấu sắc phong & hoa văn chạm khắc. Dùng làm dấu hiệu thương hiệu xuyên suốt:
// nhãn mục, đường phân cách trang trí, điểm nhấn ở hero và chân trang. Thuần SVG (server
// component), kế thừa màu qua currentColor nên đặt màu bằng class text-* ở nơi sử dụng.
export function HeritageSeal({ className }: { className?: string }) {
return (
<svg viewBox="0 0 48 48" fill="none" aria-hidden="true" className={className}>
<circle cx="24" cy="24" r="22.5" stroke="currentColor" strokeWidth="1" opacity="0.4" />
<circle cx="24" cy="24" r="18" stroke="currentColor" strokeWidth="1.1" />
<rect x="13" y="13" width="22" height="22" stroke="currentColor" strokeWidth="1.2" />
<rect
x="13"
y="13"
width="22"
height="22"
stroke="currentColor"
strokeWidth="1.2"
transform="rotate(45 24 24)"
/>
<circle cx="24" cy="24" r="4.2" stroke="currentColor" strokeWidth="1.2" />
<circle cx="24" cy="24" r="1.4" fill="currentColor" />
</svg>
);
}
// Đường phân cách trang trí: dấu triện ở giữa, hai sợi chỉ vàng mảnh tan dần hai bên.
// Tĩnh, an toàn với prefers-reduced-motion; tạo nhịp nghỉ giữa các mục lớn.
export function SectionDivider({ className = "" }: { className?: string }) {
return (
<div className={`flex items-center justify-center gap-4 ${className}`} aria-hidden="true">
<span className="h-px w-full max-w-[8.5rem] bg-[linear-gradient(90deg,transparent,rgb(142_95_11_/_0.42))]" />
<HeritageSeal className="h-[1.7rem] w-[1.7rem] shrink-0 text-[var(--primary)]" />
<span className="h-px w-full max-w-[8.5rem] bg-[linear-gradient(90deg,rgb(142_95_11_/_0.42),transparent)]" />
</div>
);
}
@@ -1,5 +1,6 @@
import Link from "next/link";
import { Globe2, Mail, MapPin, Phone } from "lucide-react";
import { HeritageSeal } from "@/app/components/landing/HeritageOrnament";
import { contact, navigation } from "@/app/content";
export function SiteFooter() {
+3 -2
View File
@@ -1,9 +1,10 @@
"use client";
import Link from "next/link";
import { Landmark, Menu, PlayCircle, X } from "lucide-react";
import { Menu, PlayCircle, X } from "lucide-react";
import { usePathname } from "next/navigation";
import { useState } from "react";
import { HeritageSeal } from "@/app/components/landing/HeritageOrnament";
import { activeTour, navigation } from "@/app/content";
export function SiteHeader() {
@@ -19,7 +20,7 @@ export function SiteHeader() {
aria-label="VR360 Di sản Như Quỳnh"
>
<span className="public-logo-mark grid h-11 w-11 shrink-0 place-items-center overflow-hidden rounded-full border border-[rgb(142_95_11_/_0.24)] bg-[linear-gradient(135deg,rgb(255_255_255_/_0.94),rgb(232_219_176_/_0.74))] shadow-[0_10px_24px_rgb(37_75_54_/_0.1),inset_0_1px_0_rgb(255_255_255_/_0.92)]">
<Landmark className="h-5 w-5 text-[var(--primary)]" strokeWidth={1.8} />
<HeritageSeal className="h-6 w-6 text-[var(--primary)]" />
</span>
<span className="min-w-0">
<span className="public-display block truncate text-base font-bold leading-tight text-[var(--ink-on-light)] sm:text-lg">
+1
View File
@@ -149,6 +149,7 @@ export const subpageHero = {
diTich: leHoi("nghi-thuc-san-chua.jpg"),
diTichFeature: leHoi("kieu-long-vang.jpg"),
coVat: leHoi("tru-tri-le-hoi.jpg"),
lienHe: leHoi("le-cau-an-ban-dem.jpg"), // Đêm cầu an — trầm lắng, mời gọi, hợp trang liên hệ
} as const;
export const navigation = [
+11 -8
View File
@@ -42,9 +42,9 @@ export default async function HeritageDetailPage({
<SiteHeader />
<section className="public-subpage-hero relative isolate overflow-hidden border-b border-[var(--surface-border)]">
<Image src={site.image} alt={site.name} fill priority sizes="100vw" className="public-subpage-hero-image object-cover" />
<div className="public-subpage-hero-content mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:py-22">
<Link href="/di-tich" className="inline-flex items-center gap-2 text-sm font-bold text-[var(--tour-ink)] transition hover:text-[var(--primary)]">
<Image src={site.image} alt={site.name} fill preload sizes="100vw" className="public-subpage-hero-image public-parallax-bg object-cover" />
<div className="public-subpage-hero-content mx-auto flex min-h-[inherit] max-w-7xl flex-col justify-end px-4 py-16 sm:px-6 lg:py-22">
<Link href="/di-tich" className="inline-flex w-fit items-center gap-2 text-sm font-bold text-[rgb(243_236_221_/_0.86)] transition hover:text-[var(--tour-gold-light)]">
<ArrowLeft className="h-4 w-4" strokeWidth={1.8} />
Tất cả di tích
</Link>
@@ -54,12 +54,12 @@ export default async function HeritageDetailPage({
<span className="public-chip">{site.type}</span>
<span className="public-chip">{site.status}</span>
</div>
<h1 className="public-gradient-text public-heading-safe mt-4 text-[clamp(2.4rem,5.4vw,4.6rem)] font-extrabold">
<h1 className="public-hero-display public-heading-safe mt-5 text-[clamp(2.4rem,5.4vw,4.6rem)] font-extrabold">
{site.name}
</h1>
{site.location ? (
<p className="mt-4 inline-flex items-center gap-2 text-sm font-semibold text-[var(--foreground)]/82">
<MapPin className="h-4 w-4 text-[var(--primary)]" strokeWidth={1.8} />
<p className="mt-4 inline-flex items-center gap-2 text-sm font-semibold text-[rgb(243_236_221_/_0.86)]">
<MapPin className="h-4 w-4 text-[var(--tour-gold-light)]" strokeWidth={1.8} />
{site.location}
</p>
) : null}
@@ -68,9 +68,12 @@ export default async function HeritageDetailPage({
</section>
<section className="mx-auto max-w-3xl px-4 py-14 sm:px-6 lg:py-20">
<div className="grid gap-4 text-base leading-8 text-[var(--foreground)]/82">
<p className="public-kicker">Về di tích</p>
<div className="mt-5 grid gap-5 text-base leading-8 text-[var(--foreground)]/82">
{paragraphs.map((text, i) => (
<p key={i}>{text}</p>
<p key={i} className={i === 0 ? "text-lg leading-9 text-[var(--tour-ink)]" : undefined}>
{text}
</p>
))}
</div>
+19 -13
View File
@@ -4,6 +4,7 @@ import Link from "next/link";
import { Mail, MapPinned } from "lucide-react";
import { BackToTopButton } from "@/app/components/landing/BackToTopButton";
import { CulturalSpaceExplorer } from "@/app/components/landing/CulturalSpaceExplorer";
import { HeritageSeal } from "@/app/components/landing/HeritageOrnament";
import { SiteFooter } from "@/app/components/landing/SiteFooter";
import { SiteHeader } from "@/app/components/landing/SiteHeader";
import { heritageSites, subpageHero } from "@/app/content";
@@ -24,18 +25,18 @@ export default function HeritageListPage() {
src={subpageHero.diTich}
alt=""
fill
priority
preload
sizes="100vw"
className="public-subpage-hero-image public-parallax-bg object-cover"
/>
<div className="public-subpage-hero-content mx-auto grid max-w-7xl gap-10 px-4 py-16 sm:px-6 lg:min-h-[580px] lg:grid-cols-[0.95fr_0.7fr] lg:items-center lg:py-22">
<div className="public-subpage-hero-content mx-auto grid max-w-7xl gap-10 px-4 py-16 sm:px-6 lg:min-h-[600px] lg:grid-cols-[0.95fr_0.7fr] lg:items-center lg:py-24">
<div className="public-rise public-hero-rule w-full min-w-0 max-w-4xl">
<p className="public-kicker">Danh sách di tích tour VR360</p>
<h1 className="public-gradient-text public-heading-safe public-subpage-title mt-4 text-[clamp(2.55rem,5.8vw,5.35rem)] font-extrabold">
Hệ thống di tích Như Quỳnh
<h1 className="public-hero-display public-heading-safe public-subpage-title mt-5 text-[clamp(2.55rem,5.8vw,5.35rem)] font-extrabold">
Hệ thống di tích <span className="public-display-accent"> Như Quỳnh</span>
</h1>
<p className="public-subpage-copy mt-6 text-base leading-8 text-[var(--foreground)]/82 sm:text-lg">
<p className="public-subpage-copy mt-6 text-base leading-8 sm:text-lg">
Khám phá 15 di tích lịch sử, văn hóa của Như Quỳnh: 9 di tích xếp hạng cấp quốc gia 6 di tích cấp tỉnh.
</p>
<div className="mt-7 flex flex-wrap gap-3">
@@ -50,7 +51,7 @@ export default function HeritageListPage() {
</div>
</div>
<div className="public-image-stage public-hero-photo public-stagger-item hidden lg:block">
<div className="public-image-stage public-hero-photo public-stagger-item relative hidden lg:block">
<Image
src={subpageHero.diTichFeature}
alt="Kiệu rước dưới tán lọng vàng trong lễ hội Chùa Pháp Vân (Thái Lạc)"
@@ -58,24 +59,29 @@ export default function HeritageListPage() {
sizes="38vw"
className="object-cover"
/>
<HeritageSeal className="absolute right-4 top-4 z-10 h-9 w-9 text-[var(--tour-gold-light)] opacity-90 drop-shadow-[0_2px_8px_rgb(6_22_16_/_0.55)]" />
</div>
</div>
</section>
<CulturalSpaceExplorer sites={heritageSites} />
<section className="px-4 py-14 sm:px-6 lg:py-20">
<div className="public-panel public-glow mx-auto grid max-w-7xl gap-5 rounded-[8px] p-6 lg:grid-cols-[1fr_auto] lg:items-center lg:p-8">
<div>
<section className="px-4 py-16 sm:px-6 lg:py-24">
<div className="public-on-dark relative mx-auto grid max-w-7xl gap-6 overflow-hidden rounded-[22px] border border-[rgb(214_180_104_/_0.3)] bg-[linear-gradient(135deg,#11483593,#0a261d_72%),linear-gradient(135deg,#0e3a2c,#0a261d)] p-8 text-white shadow-[0_40px_110px_rgb(8_31_23_/_0.42)] lg:grid-cols-[1fr_auto] lg:items-center lg:p-12">
<HeritageSeal
aria-hidden
className="pointer-events-none absolute -right-10 -top-10 h-48 w-48 text-[var(--tour-gold-light)] opacity-[0.08]"
/>
<div className="relative min-w-0">
<p className="public-kicker">Đ xuất đa điểm</p>
<h2 className="public-heading-safe mt-2 text-3xl font-bold text-[var(--tour-ink)] sm:text-4xl">
Bạn muốn đ xuất thêm đa điểm văn hóa?
<h2 className="public-display public-heading-safe mt-3 text-3xl font-bold text-[#f8f1e2] sm:text-[2.4rem]">
Bạn muốn đ xuất thêm <span className="public-display-accent text-[var(--tour-gold-light)]">đa điểm văn hóa?</span>
</h2>
<p className="mt-3 max-w-[66ch] text-sm leading-6 text-[var(--foreground)]/76 sm:text-base">
<p className="mt-4 max-w-[66ch] text-sm leading-7 text-[rgb(243_236_221_/_0.82)] sm:text-base">
Gửi thông tin, hình nh hoặc câu chuyện liên quan đ cùng hoàn thiện kho liệu số của Như Quỳnh.
</p>
</div>
<Link href="/lien-he" className="public-cta w-fit">
<Link href="/lien-he" className="public-cta relative w-fit">
<Mail className="h-4 w-4" strokeWidth={1.8} />
Liên hệ với chúng tôi
</Link>
+21 -10
View File
@@ -15,6 +15,7 @@ import {
} from "lucide-react";
import { BackToTopButton } from "@/app/components/landing/BackToTopButton";
import { CountUp } from "@/app/components/landing/CountUp";
import { HeritageSeal, SectionDivider } from "@/app/components/landing/HeritageOrnament";
import { SiteFooter } from "@/app/components/landing/SiteFooter";
import { SiteHeader } from "@/app/components/landing/SiteHeader";
import { activeTour, antiquities, heritageSites, img, subpageHero } from "@/app/content";
@@ -61,14 +62,14 @@ export default function AboutPage() {
<SiteHeader />
<section className="public-subpage-hero public-subpage-hero--compact relative isolate overflow-hidden border-b border-[var(--surface-border)]">
<Image src={subpageHero.gioiThieu} alt="" fill priority sizes="100vw" className="public-subpage-hero-image public-parallax-bg object-cover" />
<div className="public-subpage-hero-content mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:py-22">
<Image src={subpageHero.gioiThieu} alt="" fill preload sizes="100vw" className="public-subpage-hero-image public-parallax-bg object-cover" />
<div className="public-subpage-hero-content mx-auto max-w-7xl px-4 py-20 sm:px-6 lg:py-28">
<div className="public-rise public-hero-rule max-w-4xl">
<p className="public-kicker">Giới thiệu</p>
<h1 className="public-gradient-text public-heading-safe mt-4 text-[clamp(2.55rem,5.8vw,5rem)] font-extrabold">
Di sản văn hóa Như Quỳnh
<h1 className="public-hero-display public-heading-safe mt-5 text-[clamp(2.55rem,5.8vw,5rem)] font-extrabold">
Di sản văn hóa <span className="public-display-accent"> Như Quỳnh</span>
</h1>
<p className="public-subpage-copy mt-6 max-w-[60ch] text-base leading-8 text-[var(--foreground)]/82 sm:text-lg">
<p className="public-subpage-copy mt-6 max-w-[60ch] text-base leading-8 sm:text-lg">
Một vùng đt văn hiến với hệ thống di tích trải dài nhiều thế kỷ, nay đưc gìn giữ lan tỏa
bằng công nghệ số.
</p>
@@ -95,13 +96,17 @@ export default function AboutPage() {
{/* Stats */}
<section className="border-y border-[var(--surface-border)] bg-[var(--surface-band)]">
<div className="mx-auto grid max-w-7xl gap-4 px-4 py-14 sm:px-6 sm:grid-cols-2 lg:grid-cols-4 lg:py-18">
<div className="mx-auto grid max-w-7xl gap-px overflow-hidden rounded-[16px] border border-[var(--surface-border)] bg-[var(--surface-border)] px-0 sm:grid-cols-2 lg:grid-cols-4">
{stats.map((s) => (
<div key={s.label} className="public-panel public-stagger-item rounded-[14px] p-6 text-center">
<p className="public-gradient-text font-display-vn text-[2.6rem] font-extrabold leading-none [font-variant-numeric:tabular-nums]">
<div
key={s.label}
className="public-stagger-item flex flex-col items-center bg-[rgb(255_255_255_/_0.5)] px-6 py-10 text-center"
>
<p className="public-display text-[clamp(2.6rem,5.4vw,3.5rem)] font-bold leading-none text-[var(--primary)] [font-variant-numeric:tabular-nums]">
<CountUp value={Number(s.value)} />
</p>
<p className="mt-3 text-sm leading-6 text-[var(--muted-foreground)]">{s.label}</p>
<span aria-hidden className="mt-3 h-px w-8 bg-[rgb(142_95_11_/_0.4)]" />
<p className="mt-3 max-w-[18ch] text-sm leading-6 text-[var(--muted-foreground)]">{s.label}</p>
</div>
))}
</div>
@@ -165,7 +170,8 @@ export default function AboutPage() {
</section>
{/* Featured tour CTA */}
<section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:py-20">
<section className="mx-auto max-w-7xl px-4 py-16 sm:px-6 lg:py-24">
<SectionDivider className="mx-auto mb-14 max-w-3xl" />
<div className="public-card group grid overflow-hidden rounded-[18px] lg:grid-cols-[1.05fr_0.95fr]">
<div className="relative min-h-[20rem]">
<Image
@@ -175,6 +181,11 @@ export default function AboutPage() {
sizes="(max-width: 1024px) 100vw, 50vw"
className="object-cover transition-transform duration-700 group-hover:scale-[1.03]"
/>
<span
aria-hidden
className="absolute inset-0 bg-[linear-gradient(120deg,transparent_55%,rgb(8_31_23_/_0.32))]"
/>
<HeritageSeal className="absolute right-4 top-4 h-9 w-9 text-[var(--tour-gold-light)] opacity-90 drop-shadow-[0_2px_8px_rgb(6_22_16_/_0.55)]" />
</div>
<div className="flex flex-col justify-center p-7 sm:p-10">
<p className="public-kicker">Điểm đến tiêu biểu</p>
+106 -19
View File
@@ -315,8 +315,12 @@ a {
max-width: min(15ch, 100%);
}
/* Hero trang con — bản ĐIỆN ẢNH: ảnh thật chiếm sáng, phủ lớp sẫm xanh rêu để chữ ngà
nổi bật. Tạo "mảng sẫm" tương phản với thân trang sáng, cảm giác triển lãm cao cấp. */
.public-subpage-hero {
min-height: min(760px, calc(100dvh - 72px));
background: #0a261d;
color: #f3ecdd;
}
.public-subpage-hero--compact {
@@ -324,8 +328,30 @@ a {
}
.public-subpage-hero-image {
opacity: 0.62;
filter: saturate(1.08) contrast(1.05) brightness(1.02);
opacity: 0.96;
filter: saturate(1.12) contrast(1.06) brightness(0.86);
}
/* Trong hero điện ảnh: nhãn triện sáng hơn, chữ phụ màu ngà mờ. */
.public-subpage-hero .public-kicker {
color: var(--tour-gold-light);
}
.public-subpage-hero .public-kicker::before {
border-color: color-mix(in oklab, var(--tour-gold-light) 70%, transparent);
background: radial-gradient(circle, var(--tour-gold-light) 0 26%, transparent 30%);
}
.public-subpage-hero .public-chip {
border-color: rgb(224 207 159 / 0.32);
background: rgb(10 38 29 / 0.5);
color: #f3ecdd;
-webkit-backdrop-filter: blur(8px);
backdrop-filter: blur(8px);
}
.public-subpage-hero .public-chip svg {
color: var(--tour-gold-light);
}
.public-subpage-hero::before,
@@ -339,16 +365,29 @@ a {
.public-subpage-hero::before {
z-index: 1;
background:
radial-gradient(ellipse at 24% 48%, rgb(243 247 240 / 0.98) 0%, rgb(243 247 240 / 0.88) 36%, rgb(243 247 240 / 0.48) 58%, transparent 78%),
linear-gradient(90deg, rgb(243 247 240 / 0.98) 0%, rgb(243 247 240 / 0.9) 40%, rgb(243 247 240 / 0.36) 72%, rgb(243 247 240 / 0.12) 100%),
linear-gradient(180deg, rgb(243 247 240 / 0.18), rgb(243 247 240 / 0.96) 100%);
linear-gradient(90deg, rgb(7 25 19 / 0.94) 0%, rgb(8 30 23 / 0.78) 34%, rgb(9 34 26 / 0.32) 66%, transparent 100%),
linear-gradient(0deg, rgb(6 22 16 / 0.88) 0%, rgb(7 26 20 / 0.34) 38%, transparent 66%),
radial-gradient(125% 130% at 28% 26%, transparent 38%, rgb(5 19 14 / 0.62) 100%);
}
/* Mép dưới hòa nhẹ vào thân trang sáng + một sợi chỉ vàng mảnh phân tách tinh tế. */
.public-subpage-hero::after {
z-index: 2;
inset: auto 0 0 0;
height: 44%;
background: linear-gradient(180deg, transparent, rgb(243 247 240 / 0.98));
height: 30%;
background: linear-gradient(180deg, transparent 0%, transparent 58%, rgb(243 247 240 / 0.14) 86%, rgb(243 247 240 / 0.32) 100%);
}
.public-subpage-hero .public-subpage-copy {
color: rgb(243 236 221 / 0.84);
}
.public-subpage-hero .public-hero-rule::before {
background: linear-gradient(90deg, transparent, rgb(224 207 159 / 0.5), transparent);
}
.public-subpage-hero .public-hero-rule::after {
background: linear-gradient(90deg, rgb(224 207 159 / 0.46), rgb(141 146 118 / 0.32), transparent);
}
.public-subpage-hero-content {
@@ -562,13 +601,13 @@ a {
@media (max-width: 640px) {
.public-subpage-hero-image {
opacity: 0.44;
opacity: 0.9;
}
.public-subpage-hero::before {
background:
linear-gradient(180deg, rgb(243 247 240 / 0.84) 0%, rgb(243 247 240 / 0.78) 52%, rgb(243 247 240 / 0.96) 100%),
linear-gradient(90deg, rgb(243 247 240 / 0.94), rgb(243 247 240 / 0.64));
linear-gradient(180deg, rgb(7 25 19 / 0.46) 0%, rgb(7 25 19 / 0.34) 30%, rgb(6 22 16 / 0.82) 100%),
linear-gradient(90deg, rgb(7 25 19 / 0.6), rgb(7 25 19 / 0.28) 80%);
}
}
@@ -850,12 +889,44 @@ a {
transform: translateX(22%);
}
/* Nhãn "triện di sản" — thay cho eyebrow chữ hoa lặp lại (vốn là dấu hiệu AI rõ rệt).
Một dấu triện vàng hình thoi + chữ thường, đọc như bảng tên hiện vật trong bảo tàng;
là một thành tố thương hiệu CÓ CHỦ ĐÍCH, không phải khung sườn phản xạ. */
.public-kicker {
display: inline-flex;
align-items: center;
gap: 0.6rem;
color: var(--primary);
font-size: 0.8rem;
font-weight: 700;
letter-spacing: 0;
text-transform: uppercase;
font-weight: 800;
letter-spacing: 0.005em;
line-height: 1.25;
text-transform: none;
}
.public-kicker::before {
content: "";
flex: 0 0 auto;
width: 0.82rem;
height: 0.82rem;
transform: rotate(45deg);
border: 1.5px solid color-mix(in oklab, var(--primary) 70%, transparent);
border-radius: 2px;
background: radial-gradient(circle, var(--primary) 0 26%, transparent 30%);
}
.public-kicker--center {
justify-content: center;
}
/* Trên nền sẫm (panel điện ảnh, CTA tối): nhãn triện chuyển sang vàng sáng cho dễ đọc. */
.public-on-dark .public-kicker {
color: var(--tour-gold-light);
}
.public-on-dark .public-kicker::before {
border-color: color-mix(in oklab, var(--tour-gold-light) 70%, transparent);
background: radial-gradient(circle, var(--tour-gold-light) 0 26%, transparent 30%);
}
.public-cta,
@@ -978,13 +1049,29 @@ a {
padding-top: 0.9rem;
}
/* Tiêu đề hiển thị: màu mực xanh rêu đậm ĐẶC (bỏ gradient-clip để tránh "tell" AI và
tăng tương phản, độ nét). Nhấn nhá chuyển sang từ điểm nhấn .public-display-accent. */
.public-gradient-text {
color: transparent;
background: linear-gradient(90deg, #0e3427, #8e5f0b 54%, #245f4b);
background-size: 180% 100%;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
color: var(--tour-ink);
}
/* Từ/cụm nhấn trong tiêu đề: serif nghiêng, màu vàng đồng — sang, có nhịp, đặc một màu. */
.public-display-accent {
color: var(--primary);
font-style: italic;
font-weight: 600;
}
/* Tiêu đề hero trên nền ảnh sẫm (điện ảnh): chữ sáng ngà, viền sáng nhẹ cho dễ đọc. */
.public-hero-display {
color: #f8f1e2;
font-family: var(--font-serif);
letter-spacing: -0.02em;
text-shadow: 0 1px 28px rgb(6 22 16 / 0.5);
}
.public-hero-display .public-display-accent {
color: var(--tour-gold-light);
}
.public-shimmer::after {
+25 -11
View File
@@ -1,9 +1,11 @@
import type { Metadata } from "next";
import Image from "next/image";
import { Clock, Globe2, ImagePlus, Landmark, MapPin, MessageSquareText, Phone, Send } from "lucide-react";
import { BackToTopButton } from "@/app/components/landing/BackToTopButton";
import { HeritageSeal } from "@/app/components/landing/HeritageOrnament";
import { SiteFooter } from "@/app/components/landing/SiteFooter";
import { SiteHeader } from "@/app/components/landing/SiteHeader";
import { contact } from "@/app/content";
import { contact, subpageHero } from "@/app/content";
export const metadata: Metadata = {
title: "Liên hệ | VR360 Như Quỳnh",
@@ -28,16 +30,19 @@ export default function ContactPage() {
<main className="public-page min-h-[100dvh] bg-[var(--background)] text-[var(--foreground)]">
<SiteHeader />
<section className="mx-auto max-w-7xl px-4 pt-16 sm:px-6 lg:pt-24">
<div className="public-rise max-w-3xl">
<p className="public-kicker">Liên hệ</p>
<h1 className="public-gradient-text public-heading-safe mt-3 text-[clamp(2.2rem,5vw,3.8rem)] font-extrabold">
Liên hệ & đóng góp liệu
</h1>
<p className="mt-5 text-base leading-8 text-[var(--foreground)]/82 sm:text-lg">
Người dân, tổ chức liệu, hình nh hoặc câu chuyện về các di tích của Như Quỳnh
vui lòng liên hệ đ cùng hoàn thiện kho dữ liệu số di sản.
</p>
<section className="public-subpage-hero public-subpage-hero--compact relative isolate overflow-hidden border-b border-[var(--surface-border)]">
<Image src={subpageHero.lienHe} alt="" fill preload sizes="100vw" className="public-subpage-hero-image public-parallax-bg object-cover" />
<div className="public-subpage-hero-content mx-auto max-w-7xl px-4 py-20 sm:px-6 lg:py-28">
<div className="public-rise public-hero-rule max-w-4xl">
<p className="public-kicker">Liên hệ</p>
<h1 className="public-hero-display public-heading-safe mt-5 text-[clamp(2.2rem,5vw,4.2rem)] font-extrabold">
Liên hệ &amp; <span className="public-display-accent">đóng góp liệu</span>
</h1>
<p className="public-subpage-copy mt-6 max-w-[58ch] text-base leading-8 sm:text-lg">
Người dân, tổ chức liệu, hình nh hoặc câu chuyện về các di tích của Như Quỳnh
vui lòng liên hệ đ cùng hoàn thiện kho dữ liệu số di sản.
</p>
</div>
</div>
</section>
@@ -116,6 +121,15 @@ export default function ContactPage() {
{/* Map */}
<section className="mx-auto max-w-7xl px-4 pb-16 sm:px-6 lg:pb-24">
<div className="mb-6 flex items-center gap-4">
<HeritageSeal className="h-8 w-8 shrink-0 text-[var(--primary)] opacity-80" />
<div>
<p className="public-kicker">Vị trí trên bản đ</p>
<h2 className="public-display public-heading-safe mt-1.5 text-xl font-bold text-[var(--tour-ink)] sm:text-2xl">
{contact.address}
</h2>
</div>
</div>
<div className="public-map-frame overflow-hidden rounded-[16px] border border-[var(--surface-border)]">
<iframe
title="Bản đồ Thôn Đình Dù, Xã Như Quỳnh, tỉnh Hưng Yên"
+59 -21
View File
@@ -6,6 +6,7 @@ import { BackToTopButton } from "@/app/components/landing/BackToTopButton";
import { FestivalGallery } from "@/app/components/landing/FestivalGallery";
import { FestivalVideo } from "@/app/components/landing/FestivalVideo";
import { HeritageMarquee } from "@/app/components/landing/HeritageMarquee";
import { HeritageSeal, SectionDivider } from "@/app/components/landing/HeritageOrnament";
import { SiteFooter } from "@/app/components/landing/SiteFooter";
import { SiteHeader } from "@/app/components/landing/SiteHeader";
import { Typewriter } from "@/app/components/landing/Typewriter";
@@ -54,9 +55,9 @@ export default function Home() {
<div className="public-rise min-w-0">
<p className="public-kicker">VR360 số hóa di sản Như Quỳnh</p>
<h1 className="public-gradient-text public-heading-safe mt-5 max-w-[18ch] text-[clamp(2.85rem,5.35vw,5rem)] font-extrabold">
Khám phá di sản văn hóa Như Quỳnh qua trải nghiệm VR360
Khám phá <span className="public-display-accent">di sản văn hóa</span> Như Quỳnh qua trải nghiệm VR360
</h1>
<p className="mt-6 max-w-[58ch] text-base leading-8 text-[var(--foreground)]/82 sm:text-lg">
<p className="mt-6 max-w-[58ch] text-base leading-8 text-[var(--ink-on-light)]/78 sm:text-lg">
Hành trình số hóa hệ thống di tích, không gian văn hóa giá trị truyền thống của Như Quỳnh, tỉnh Hưng Yên bằng công nghệ tham quan thực tế o 360 đ.
</p>
<p className="mt-5 flex flex-wrap items-baseline gap-x-2 gap-y-1 text-base font-semibold sm:text-lg">
@@ -83,16 +84,35 @@ export default function Home() {
</div>
</div>
<div className="public-image-stage public-stagger-item relative hidden min-h-[31rem] lg:block">
<Link
href={activeTour.href}
aria-label={`Tham quan VR360 ${activeTour.title}`}
className="public-image-stage public-stagger-item group relative hidden min-h-[31rem] lg:block"
>
<Image
src={activeTour.heroFeatureImage}
alt="Nghi lễ rước kiệu trong lễ hội Chùa Thái Lạc (Pháp Vân tự), Xã Như Quỳnh"
fill
priority
preload
sizes="42vw"
className="object-cover"
className="object-cover transition-transform duration-[1200ms] ease-[cubic-bezier(.16,1,.3,1)] group-hover:scale-[1.04]"
/>
</div>
<HeritageSeal className="absolute right-4 top-4 z-10 h-9 w-9 text-[var(--tour-gold-light)] opacity-90 drop-shadow-[0_2px_8px_rgb(6_22_16_/_0.6)]" />
<div className="absolute inset-x-3 bottom-3 z-10 flex items-end justify-between gap-4 rounded-[8px] border border-white/15 bg-[rgb(8_31_23_/_0.6)] p-4 text-white backdrop-blur-md">
<div className="min-w-0">
<span className="inline-flex items-center gap-1.5 text-[0.68rem] font-bold uppercase tracking-[0.14em] text-[var(--tour-gold-light)]">
<span className="h-1.5 w-1.5 animate-pulse rounded-full bg-[var(--tour-gold-light)]" />
Đang mở tour
</span>
<p className="public-display mt-1.5 truncate text-lg font-bold leading-tight">{activeTour.title}</p>
</div>
<span className="shrink-0 text-right text-[0.72rem] font-semibold leading-snug text-white/82">
{activeTour.sceneCount} điểm
<br />
nh 360°
</span>
</div>
</Link>
</section>
</HeroShaderBackground>
@@ -276,19 +296,28 @@ export default function Home() {
<div className="mx-auto max-w-7xl px-4 py-14 sm:px-6 lg:py-20">
<div className="public-split-reveal max-w-3xl rounded-[6px]">
<p className="public-kicker">Điểm nổi bật</p>
<h2 className="public-heading-safe mt-3 text-[2rem] font-bold text-[var(--tour-ink)] sm:text-4xl">
Giá trị của nền tảng VR360.
<h2 className="public-display public-heading-safe mt-3 text-[2rem] font-bold text-[var(--tour-ink)] sm:text-4xl">
sao một nền tảng VR360 cho di sản?
</h2>
<p className="mt-3 max-w-[60ch] text-sm leading-6 text-[var(--muted-foreground)] sm:text-base">
Bốn giá trị cốt lõi đnh hình cách dự án gìn giữ lan tỏa di sản Như Quỳnh.
</p>
</div>
<div className="mt-10 grid gap-4 md:grid-cols-2 xl:grid-cols-4">
<div className="mt-12 grid gap-x-8 gap-y-10 sm:grid-cols-2 lg:grid-cols-4">
{benefits.map((benefit) => {
const Icon = benefit.icon;
return (
<article key={benefit.title} className="public-panel public-stagger-item rounded-[16px] p-6">
<Icon className="h-7 w-7 text-[var(--primary)]" strokeWidth={1.8} />
<h3 className="mt-5 text-xl font-bold text-[var(--tour-ink)]">{benefit.title}</h3>
<p className="mt-3 text-sm leading-6 text-[var(--muted-foreground)]">{benefit.detail}</p>
<article key={benefit.title} className="public-stagger-item relative pt-6">
<span
aria-hidden
className="absolute inset-x-0 top-0 h-px bg-[linear-gradient(90deg,rgb(142_95_11_/_0.55),rgb(142_95_11_/_0.08))]"
/>
<span className="grid h-12 w-12 place-items-center rounded-full border border-[rgb(142_95_11_/_0.24)] bg-[rgb(200_164_106_/_0.1)] text-[var(--primary)] shadow-[inset_0_1px_0_rgb(255_255_255_/_0.6)]">
<Icon className="h-6 w-6" strokeWidth={1.7} />
</span>
<h3 className="mt-5 text-lg font-bold leading-snug text-[var(--tour-ink)]">{benefit.title}</h3>
<p className="mt-2.5 text-sm leading-6 text-[var(--muted-foreground)]">{benefit.detail}</p>
</article>
);
})}
@@ -296,18 +325,27 @@ export default function Home() {
</div>
</section>
<section className="px-4 py-14 sm:px-6 lg:py-20">
<div className="public-panel public-glow mx-auto grid max-w-7xl gap-5 rounded-[18px] p-6 lg:grid-cols-[1fr_auto] lg:items-center lg:p-8">
<div>
<section className="px-4 py-16 sm:px-6 lg:py-24">
<SectionDivider className="mx-auto mb-14 max-w-7xl" />
<div className="public-on-dark relative mx-auto grid max-w-7xl gap-6 overflow-hidden rounded-[22px] border border-[rgb(214_180_104_/_0.3)] bg-[linear-gradient(135deg,#11483593,#0a261d_72%),linear-gradient(135deg,#0e3a2c,#0a261d)] p-8 text-white shadow-[0_40px_110px_rgb(8_31_23_/_0.42)] lg:grid-cols-[1fr_auto] lg:items-center lg:p-12">
<HeritageSeal
aria-hidden
className="pointer-events-none absolute -right-10 -top-10 h-48 w-48 text-[var(--tour-gold-light)] opacity-[0.08]"
/>
<div
aria-hidden
className="pointer-events-none absolute inset-0 bg-[radial-gradient(120%_120%_at_85%_-10%,rgb(214_180_104_/_0.14),transparent_55%)]"
/>
<div className="relative min-w-0">
<p className="public-kicker">Bắt đu khám phá</p>
<h2 className="public-heading-safe mt-2 text-3xl font-bold text-[var(--tour-ink)] sm:text-4xl">
Bắt đu hành trình khám phá Như Quỳnh
<h2 className="public-display public-heading-safe mt-3 text-3xl font-bold text-[#f8f1e2] sm:text-[2.6rem]">
Mở cánh cửa di sản <span className="public-display-accent text-[var(--tour-gold-light)]">Như Quỳnh</span>
</h2>
<p className="mt-3 max-w-[66ch] text-sm leading-6 text-[var(--foreground)]/76 sm:text-base">
Mở danh sách không gian văn hóa đ tìm kiếm, lọc lựa chọn điểm tham quan phù hợp.
<p className="mt-4 max-w-[60ch] text-sm leading-7 text-[rgb(243_236_221_/_0.82)] sm:text-base">
Mở danh sách không gian văn hóa đ tìm kiếm, lọc lựa chọn điểm tham quan phù hợp bắt đu ngay từ ngôi chùa cổ Thái Lạc.
</p>
</div>
<Link href="/di-tich" className="public-cta w-fit">
<Link href="/di-tich" className="public-cta relative w-fit">
<Sparkles className="h-4 w-4" strokeWidth={1.8} />
Xem không gian văn hóa
</Link>