+ {/* Mobile Top Header */}
+
+
+ {/* Drawer Mask & Navigation Panel */}
+
setDrawerOpen(false)}
+ />
+
+
+ 全站导航目录
+
+
+
+ setDrawerOpen(false)}
+ >
+ 首页
+ ›
+
+
+
+ {navGroups.map((group) => (
+
+
{group.label}
+
+ {group.children.map((child) => (
+ `m-drawer-subitem ${isActive ? 'is-active' : ''}`}
+ onClick={() => setDrawerOpen(false)}
+ >
+ {child.label}
+
+ ))}
+
+
+ ))}
+
+
+ {/* Main Content Area */}
+
+ {children || }
+
+
+
+
+ )
+}
diff --git a/src/mobile/MobileViews.css b/src/mobile/MobileViews.css
new file mode 100644
index 0000000..b402d15
--- /dev/null
+++ b/src/mobile/MobileViews.css
@@ -0,0 +1,292 @@
+/* ===== Mobile Views Dedicated CSS ===== */
+
+.m-view-container {
+ width: 100%;
+ padding: 1.25rem 1rem 2.5rem;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+ gap: 1.25rem;
+}
+
+/* Header & Typography */
+.m-eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ font-size: 0.8rem;
+ color: var(--brand, #a61c20);
+ letter-spacing: 0.15em;
+}
+
+.m-eyebrow::before {
+ content: '';
+ width: 1rem;
+ height: 1px;
+ background: currentColor;
+}
+
+.theme-tech .m-eyebrow {
+ color: var(--gold, #c4a35a);
+}
+
+.m-page-title {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(1.4rem, 5.5vw, 1.85rem);
+ font-weight: 600;
+ margin: 0.2rem 0 0.6rem;
+ color: var(--ink, #1a1512);
+ line-height: 1.35;
+}
+
+.theme-tech .m-page-title {
+ color: #ffffff;
+}
+
+.m-lead {
+ font-size: 0.95rem;
+ line-height: 1.65;
+ color: var(--ink-soft, #5c534c);
+ margin: 0 0 1rem;
+}
+
+.theme-tech .m-lead {
+ color: rgba(243, 236, 228, 0.78);
+}
+
+.m-closing {
+ margin-top: 1rem;
+ padding-top: 0.85rem;
+ border-top: 1px solid rgba(196, 163, 90, 0.3);
+ font-family: 'Noto Serif SC', serif;
+ font-size: 0.95rem;
+ color: var(--gold, #c4a35a);
+ text-align: center;
+}
+
+/* Card Component */
+.m-card {
+ background: rgba(255, 255, 255, 0.85);
+ border: 1px solid rgba(166, 28, 32, 0.12);
+ border-radius: 0.5rem;
+ padding: 1.15rem 1.1rem;
+ box-shadow: 0 4px 15px rgba(26, 21, 18, 0.05);
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.theme-tech .m-card {
+ background: rgba(28, 25, 31, 0.85);
+ border-color: rgba(196, 163, 90, 0.22);
+ color: #f3ece4;
+}
+
+.m-card h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.1rem;
+ margin: 0;
+ color: var(--brand, #a61c20);
+}
+
+.theme-tech .m-card h3 {
+ color: var(--gold, #c4a35a);
+}
+
+.m-card p {
+ font-size: 0.9rem;
+ line-height: 1.6;
+ color: var(--ink-soft, #5c534c);
+ margin: 0;
+}
+
+.theme-tech .m-card p {
+ color: rgba(243, 236, 228, 0.75);
+}
+
+/* Button & CTAs */
+.m-btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 2.75rem;
+ padding: 0.6rem 1.25rem;
+ border-radius: 0.3rem;
+ font-size: 0.92rem;
+ font-weight: 500;
+ text-decoration: none;
+ transition: all 0.25s ease;
+}
+
+.m-btn-primary {
+ background: var(--brand, #a61c20);
+ color: #ffffff;
+ box-shadow: 0 4px 12px rgba(166, 28, 32, 0.3);
+}
+
+.m-btn-outline {
+ border: 1px solid var(--brand, #a61c20);
+ color: var(--brand, #a61c20);
+ background: transparent;
+}
+
+.theme-tech .m-btn-outline {
+ border-color: var(--gold, #c4a35a);
+ color: var(--gold, #c4a35a);
+}
+
+/* Mobile Hero Component */
+.m-hero {
+ text-align: center;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 1rem;
+ padding: 1.5rem 0 1rem;
+}
+
+.m-hero-sub {
+ font-size: 0.88rem;
+ color: var(--gold, #c4a35a);
+ letter-spacing: 0.15em;
+ font-weight: 600;
+}
+
+.m-hero-title {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(1.8rem, 7vw, 2.5rem);
+ color: var(--brand, #a61c20);
+ margin: 0;
+ line-height: 1.25;
+}
+
+.m-hero-img {
+ width: min(12rem, 50vw);
+ height: auto;
+ margin: 0.5rem auto;
+ filter: drop-shadow(0 10px 20px rgba(0, 0, 0, 0.15));
+}
+
+/* Specific Cards & Rings */
+.m-rings-row {
+ display: flex;
+ justify-content: center;
+ gap: 1rem;
+ margin: 0.85rem 0;
+}
+
+.m-ring-item {
+ width: 3.2rem;
+ height: 3.2rem;
+ border-radius: 50%;
+ border: 1px solid var(--gold, #c4a35a);
+ background: rgba(166, 28, 32, 0.08);
+ display: grid;
+ place-items: center;
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.15rem;
+ font-weight: 600;
+ color: var(--brand, #a61c20);
+}
+
+.theme-tech .m-ring-item {
+ background: rgba(196, 163, 90, 0.12);
+ color: var(--gold, #c4a35a);
+}
+
+.m-poem-box {
+ background: linear-gradient(135deg, rgba(166, 28, 32, 0.06), rgba(196, 163, 90, 0.08));
+ border: 1px solid rgba(196, 163, 90, 0.3);
+ padding: 1.25rem 1rem;
+ border-radius: 0.4rem;
+ text-align: center;
+}
+
+.m-poem-quote {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.15rem;
+ line-height: 1.7;
+ color: var(--brand, #a61c20);
+ margin: 0 0 0.5rem;
+}
+
+.theme-tech .m-poem-quote {
+ color: var(--gold, #c4a35a);
+}
+
+.m-poem-source {
+ font-size: 0.82rem;
+ color: var(--ink-soft, #5c534c);
+}
+
+.theme-tech .m-poem-source {
+ color: rgba(243, 236, 228, 0.65);
+}
+
+.m-big-num {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 3.8rem;
+ font-weight: 700;
+ color: var(--brand, #a61c20);
+ line-height: 1;
+ text-shadow: 0 2px 10px rgba(166, 28, 32, 0.2);
+ text-align: center;
+ margin: 0.3rem 0;
+}
+
+.theme-tech .m-big-num {
+ color: var(--gold, #c4a35a);
+}
+
+.m-qr-card {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ padding: 1.5rem 1rem;
+ gap: 1rem;
+}
+
+.m-qr-img {
+ width: 10rem;
+ height: 10rem;
+ padding: 0.5rem;
+ background: #ffffff;
+ border-radius: 0.4rem;
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
+}
+
+.m-qr-img img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+.m-deep-links {
+ margin-top: 1.25rem;
+ padding-top: 1rem;
+ border-top: 1px solid rgba(166, 28, 32, 0.12);
+ display: grid;
+ gap: 0.65rem;
+}
+
+.m-deep-card {
+ padding: 0.85rem 0.95rem;
+ background: rgba(255, 255, 255, 0.6);
+ border-left: 3px solid var(--gold, #c4a35a);
+ border-radius: 0.2rem;
+ text-decoration: none;
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+}
+
+.m-deep-card strong {
+ font-size: 0.95rem;
+ color: var(--brand, #a61c20);
+}
+
+.m-deep-card span {
+ font-size: 0.82rem;
+ color: var(--ink-soft, #5c534c);
+}
diff --git a/src/mobile/MobileViews.tsx b/src/mobile/MobileViews.tsx
new file mode 100644
index 0000000..5ea4c72
--- /dev/null
+++ b/src/mobile/MobileViews.tsx
@@ -0,0 +1,468 @@
+import { Link, Outlet, useLocation } from 'react-router-dom'
+import {
+ brand,
+ contact,
+ haoke,
+ hero,
+ images,
+ partner,
+ poetry,
+ qingxiang,
+ subsidy,
+ system,
+ tech,
+ techDetail,
+} from '../content/site'
+import './MobileViews.css'
+
+export function MobileViews() {
+ const location = useLocation()
+ const path = location.pathname
+
+ switch (path) {
+ case '/':
+ return
+ case '/brand':
+ return
+ case '/poetry':
+ return
+ case '/qingxiang':
+ return
+ case '/haoke':
+ return
+ case '/system':
+ return
+ case '/tech':
+ return
+ case '/subsidy':
+ return
+ case '/partner':
+ return
+ case '/tech-detail':
+ return
+ case '/contact':
+ return
+ default:
+ return
+ }
+}
+
+/* 1. Mobile Home View */
+function MobileHomeView() {
+ return (
+
+
+
{hero.subBrand} · 移动全景
+
{hero.brand}
+
+ {hero.title}
+
+

+
+ {hero.subtitle}
+
+
+
+ {hero.primaryCta.label}
+
+
+ {hero.secondaryCta.label}
+
+
+
+
+ )
+}
+
+/* 2. Mobile Brand View */
+function MobileBrandView() {
+ return (
+
+
+
{brand.eyebrow}
+
{brand.title}
+
{brand.lead}
+
+
+
+ {brand.cards.map((card, idx) => (
+
+
0{idx + 1}
+
{card.title}
+
{card.text}
+
+ ))}
+
+
+
+
故里文明印记
+

+
+ {brand.timeline.map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+
+
{brand.closing}
+
+
+
深度阅读
+ {brand.deepReads.map((dp) => (
+
+
{dp.label} ›
+
{dp.blurb}
+
+ ))}
+
+
+ )
+}
+
+/* 3. Mobile Poetry View */
+function MobilePoetryView() {
+ return (
+
+
+
{poetry.eyebrow}
+
{poetry.title}
+
{poetry.lead}
+
+
+
+
“{poetry.mainPoem.quote}”
+
—— {poetry.mainPoem.source}
+
+
+
+ {poetry.poems.map((p) => (
+
+
“{p.quote}”
+
—— {p.source}
+
+ ))}
+
+
+
{poetry.closing}
+
+
+
诗酒专题
+ {poetry.deepReads.map((dp) => (
+
+
{dp.label} ›
+
{dp.blurb}
+
+ ))}
+
+
+ )
+}
+
+/* 4. Mobile Qingxiang View */
+function MobileQingxiangView() {
+ return (
+
+
+
{qingxiang.eyebrow}
+
{qingxiang.title}
+
{qingxiang.lead}
+
+
+
+

+
+ {qingxiang.styleWords.map((word) => (
+
+ {word}
+
+ ))}
+
+
+
+
+ {qingxiang.positions.map((pos) => (
+
+
{pos.title}
+
{pos.text}
+
+ ))}
+
+
+
+
品鉴仪式 · 抿咂呵
+
+ {qingxiang.tasting.map((t) => (
+
+ {t.title}:
+ {t.text}
+
+ ))}
+
+
+
+
{qingxiang.mission}
+
+
+
清香宝典专题
+ {qingxiang.deepReads.map((dp) => (
+
+
{dp.label} ›
+
{dp.blurb}
+
+ ))}
+
+
+ )
+}
+
+/* 5. Mobile Haoke View */
+function MobileHaokeView() {
+ return (
+
+
+
{haoke.eyebrow}
+
{haoke.title}
+
{haoke.lead}
+
+
+
+
核心商业机制
+
+ {haoke.oneLiner}
+
+
+
+
+ {haoke.points.map((pt) => (
+
+
{pt.title}
+
{pt.text}
+
+ ))}
+
+
+
{haoke.closing}
+
+ )
+}
+
+/* 6. Mobile System View */
+function MobileSystemView() {
+ return (
+
+
+
{system.eyebrow}
+
{system.title}
+
{system.lead}
+
+
+
+ {system.ends.map((end) => (
+
+
{end.title}
+
{end.role}
+
+ 核心能力:{end.caps}
+
+
+ ))}
+
+
+
+
+
{system.closing}
+
+ )
+}
+
+/* 7. Mobile Tech View */
+function MobileTechView() {
+ return (
+
+
+
{tech.eyebrow}
+
{tech.title}
+
{tech.lead}
+
+
+
+ {tech.points.map((pt) => (
+
+
{pt.title}
+
{pt.text}
+
+ ))}
+
+
+
{tech.claim}
+
+
+
+ {tech.cta.label}
+
+
+
+ )
+}
+
+/* 8. Mobile Subsidy View */
+function MobileSubsidyView() {
+ return (
+
+
+
{subsidy.eyebrow}
+
{subsidy.title}
+
+ {subsidy.subtitle}
+
+
{subsidy.lead}
+
+
+
+
专项补贴规模
+
{subsidy.number}
+
{subsidy.numberNote}
+
+
+
+ {subsidy.points.map((pt) => (
+
+
{pt.title}
+
{pt.text}
+
+ ))}
+
+
+
{subsidy.closing}
+
+ )
+}
+
+/* 9. Mobile Partner View */
+function MobilePartnerView() {
+ return (
+
+
+
{partner.eyebrow}
+
{partner.title}
+
{partner.lead}
+
+
+
+ {partner.points.map((pt) => (
+
+
{pt.title}
+
{pt.text}
+
+ ))}
+
+
+
+
+ {partner.primaryCta.label}
+
+
+ {partner.secondaryCta.label}
+
+
+
+ )
+}
+
+/* 10. Mobile Tech Detail View */
+function MobileTechDetailView() {
+ return (
+
+
+
{techDetail.eyebrow}
+
{techDetail.title}
+
{techDetail.lead}
+
+
+
+ {techDetail.ports.map((port) => (
+
+
{port.title}
+
{port.text}
+
+ ))}
+
+
+
{techDetail.closing}
+
+ )
+}
+
+/* 11. Mobile Contact View */
+function MobileContactView() {
+ return (
+
+
+
{contact.eyebrow}
+
{contact.title}
+
{contact.lead}
+
+
+
+
+

+
+
+
+
+ )
+}
diff --git a/src/pages/Brand.css b/src/pages/Brand.css
new file mode 100644
index 0000000..0a225f3
--- /dev/null
+++ b/src/pages/Brand.css
@@ -0,0 +1,102 @@
+.brand-layout {
+ display: grid;
+ grid-template-columns: 1.2fr 0.8fr;
+ gap: 2.5rem;
+ margin-top: 1rem;
+}
+
+.brand-timeline {
+ position: relative;
+ display: grid;
+ gap: 1.75rem;
+ padding-left: 1.5rem;
+}
+
+.brand-line {
+ position: absolute;
+ left: 0.35rem;
+ top: 0.4rem;
+ bottom: 0.4rem;
+ width: 1px;
+ background: var(--brand);
+ transform-origin: top;
+ animation: drawLineY 1.2s var(--ease) both;
+}
+
+.brand-item {
+ position: relative;
+}
+
+.brand-item::before {
+ content: '';
+ position: absolute;
+ left: -1.32rem;
+ top: 0.45rem;
+ width: 0.55rem;
+ height: 0.55rem;
+ border-radius: 50%;
+ background: var(--brand);
+}
+
+.brand-num {
+ display: block;
+ color: var(--gold);
+ letter-spacing: 0.14em;
+ font-size: 0.85rem;
+ margin-bottom: 0.35rem;
+}
+
+.brand-vtitle {
+ writing-mode: vertical-rl;
+ float: left;
+ margin: 0 0.85rem 0 0;
+ color: var(--brand);
+ font-size: 1.35rem;
+ letter-spacing: 0.2em;
+ height: 7rem;
+}
+
+.brand-aside {
+ display: grid;
+ gap: 1rem;
+ align-content: start;
+}
+
+.brand-photo img {
+ aspect-ratio: 4 / 3;
+ object-fit: cover;
+ width: 100%;
+}
+
+.brand-photo.secondary img {
+ aspect-ratio: 16 / 10;
+}
+
+.brand-era {
+ list-style: none;
+ margin: 2.5rem 0 0;
+ padding: 0;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem 1.5rem;
+ border-top: 1px solid var(--gold-soft);
+ padding-top: 1.5rem;
+}
+
+.brand-era li {
+ color: var(--brand);
+ font-size: 1.05rem;
+}
+
+@media (max-width: 48rem) {
+ .brand-layout {
+ grid-template-columns: 1fr;
+ }
+
+ .brand-vtitle {
+ writing-mode: horizontal-tb;
+ float: none;
+ height: auto;
+ margin: 0 0 0.5rem;
+ }
+}
diff --git a/src/pages/Brand.tsx b/src/pages/Brand.tsx
new file mode 100644
index 0000000..4612ed4
--- /dev/null
+++ b/src/pages/Brand.tsx
@@ -0,0 +1,68 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { brand, images } from '../content/site'
+import './Brand.css'
+
+export function BrandPage() {
+ return (
+
+
+
+ {brand.eyebrow}
+ {brand.title}
+ {brand.lead}
+
+
+
+
+
+ {brand.cards.map((card, i) => (
+
+ {String(i + 1).padStart(2, '0')}
+ {card.title}
+ {card.text}
+
+ ))}
+
+
+
+
+

+
+
+

+
+
+
+
+
+
+ {brand.timeline.map((item) => (
+ -
+ {item}
+
+ ))}
+
+ {brand.closing}
+
+
+
+
+ 深度阅读
+ 从典籍、故里、祠祀到酒厂,按篇章走进品牌传承全文脉络。
+
+
+ {brand.deepReads.map((item, i) => (
+
+
+ {item.label}
+ {item.blurb}
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/src/pages/Contact.css b/src/pages/Contact.css
new file mode 100644
index 0000000..5959595
--- /dev/null
+++ b/src/pages/Contact.css
@@ -0,0 +1,77 @@
+.contact-ceremony {
+ text-align: center;
+}
+
+.contact-lead {
+ margin-inline: auto;
+ text-align: center;
+}
+
+.contact-meet {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 3rem;
+ align-items: center;
+ justify-items: center;
+ margin-top: 2.5rem;
+ max-width: 42rem;
+ margin-inline: auto;
+}
+
+.contact-info {
+ text-align: center;
+ display: grid;
+ gap: 0.35rem;
+ justify-items: center;
+}
+
+.role {
+ margin: 0;
+ color: var(--gold);
+ letter-spacing: 0.14em;
+ font-size: 0.95rem;
+}
+
+.name {
+ margin: 0;
+ font-size: clamp(2rem, 1.6rem + 1.2vw, 2.75rem);
+ letter-spacing: 0.12em;
+}
+
+.phone {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 2.75rem;
+ margin-top: 0.5rem;
+ font-size: 1.35rem;
+ color: var(--gold);
+ letter-spacing: 0.08em;
+ border-bottom: 1px solid rgba(196, 163, 90, 0.35);
+}
+
+.qr-block {
+ text-align: center;
+}
+
+.qr-image {
+ width: min(14rem, 70vw);
+ margin: 0 auto 0.85rem;
+ padding: 0.65rem;
+ background: #fff;
+ border-radius: 0.2rem;
+ box-shadow: var(--shadow-soft);
+}
+
+.qr-block p {
+ margin: 0;
+ color: var(--ink-soft);
+ font-size: 0.9rem;
+}
+
+@media (max-width: 48rem) {
+ .contact-meet {
+ grid-template-columns: 1fr;
+ gap: 2rem;
+ }
+}
diff --git a/src/pages/Contact.tsx b/src/pages/Contact.tsx
new file mode 100644
index 0000000..872161c
--- /dev/null
+++ b/src/pages/Contact.tsx
@@ -0,0 +1,32 @@
+import { Reveal } from '../components/Reveal'
+import { contact, images } from '../content/site'
+import './Contact.css'
+
+export function ContactPage() {
+ return (
+
+
+
+ {contact.eyebrow}
+ {contact.title}
+ {contact.lead}
+
+
+
+
+
+ )
+}
diff --git a/src/pages/DeepPage.tsx b/src/pages/DeepPage.tsx
new file mode 100644
index 0000000..67eab42
--- /dev/null
+++ b/src/pages/DeepPage.tsx
@@ -0,0 +1,22 @@
+import { useParams, Navigate, useLocation } from 'react-router-dom'
+import { DeepLayout } from '../components/DeepLayout'
+import { deepPageMap } from '../content/nav'
+import { useStageMode } from '../hooks/useStageMode'
+import { resolveStageRedirect } from '../content/deep/assets'
+
+export function DeepPage() {
+ const { slug } = useParams()
+ const location = useLocation()
+ const isStage = useStageMode()
+ const section = location.pathname.split('/').filter(Boolean)[0]
+ const path = `/${section}/${slug}`
+
+ if (isStage) {
+ const main = resolveStageRedirect(path) ?? '/'
+ return
+ }
+
+ const page = deepPageMap[path]
+ if (!page) return
+ return
+}
diff --git a/src/pages/Haoke.css b/src/pages/Haoke.css
new file mode 100644
index 0000000..08a74ec
--- /dev/null
+++ b/src/pages/Haoke.css
@@ -0,0 +1,58 @@
+.haoke-page {
+ position: relative;
+}
+
+.haoke-banner {
+ position: absolute;
+ inset: 0 0 auto 0;
+ height: 14rem;
+ background-size: cover;
+ background-position: center;
+ opacity: 0.18;
+ mask-image: linear-gradient(180deg, #000, transparent);
+}
+
+.haoke-slogan {
+ margin: 1.5rem 0 2.5rem;
+ font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.4rem);
+ color: var(--brand);
+ line-height: 1.45;
+ max-width: 40rem;
+}
+
+.haoke-steps {
+ display: grid;
+ gap: 0;
+ border-left: 1px solid rgba(166, 28, 32, 0.25);
+ margin-left: 0.55rem;
+ padding-left: 1.5rem;
+}
+
+.haoke-step {
+ position: relative;
+ display: grid;
+ grid-template-columns: auto 1fr;
+ gap: 0.85rem;
+ padding: 1.15rem 0 1.5rem;
+}
+
+.haoke-dot {
+ position: absolute;
+ left: -1.85rem;
+ top: 1.45rem;
+ width: 0.7rem;
+ height: 0.7rem;
+ border-radius: 50%;
+ background: var(--brand);
+ box-shadow: 0 0 0 0.35rem rgba(166, 28, 32, 0.12);
+}
+
+.haoke-step h3 {
+ margin: 0 0 0.4rem;
+ color: var(--brand);
+}
+
+.haoke-step p {
+ margin: 0;
+ color: var(--ink-soft);
+}
diff --git a/src/pages/Haoke.tsx b/src/pages/Haoke.tsx
new file mode 100644
index 0000000..4c62f15
--- /dev/null
+++ b/src/pages/Haoke.tsx
@@ -0,0 +1,44 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { haoke, images } from '../content/site'
+import './Haoke.css'
+
+export function HaokePage() {
+ return (
+
+
+
+
+ {haoke.eyebrow}
+ {haoke.title}
+ {haoke.lead}
+
+
+
+ {haoke.oneLiner}
+
+
+
+ {haoke.points.map((point, i) => (
+
+
+
+
{point.title}
+
{point.text}
+
+
+ ))}
+
+
+
+ {haoke.closing}
+
+
+ 了解三端协同
+
+
+
+
+
+ )
+}
diff --git a/src/pages/Home.css b/src/pages/Home.css
new file mode 100644
index 0000000..75cc63c
--- /dev/null
+++ b/src/pages/Home.css
@@ -0,0 +1,118 @@
+.hero {
+ position: relative;
+ min-height: calc(100vh - var(--nav-h));
+ display: flex;
+ align-items: center;
+ overflow: hidden;
+ padding: 3rem 0 4rem;
+}
+
+.hero-bg {
+ position: absolute;
+ inset: 0;
+ background-size: cover;
+ background-position: center;
+ transform: scale(1.05);
+ animation: softZoom 1.4s var(--ease) both;
+}
+
+.hero-mask {
+ position: absolute;
+ inset: 0;
+ background: linear-gradient(
+ 105deg,
+ rgba(247, 242, 234, 0.94) 0%,
+ rgba(247, 242, 234, 0.82) 42%,
+ rgba(247, 242, 234, 0.35) 70%,
+ rgba(18, 16, 20, 0.25) 100%
+ );
+}
+
+.hero-grid {
+ position: relative;
+ z-index: 1;
+ display: grid;
+ grid-template-columns: 1.15fr 0.85fr;
+ gap: 2.5rem;
+ align-items: center;
+}
+
+.hero-brand {
+ margin: 0;
+ font-size: clamp(2.4rem, 2rem + 2vw, 4rem);
+ color: var(--brand);
+ letter-spacing: 0.12em;
+}
+
+.hero-subbrand {
+ margin: 0.35rem 0 1.25rem;
+ color: var(--ink-soft);
+ letter-spacing: 0.35em;
+ font-size: 0.95rem;
+}
+
+.hero-title {
+ margin: 0 0 1rem;
+ font-size: clamp(1.8rem, 1.4rem + 1.4vw, 2.8rem);
+}
+
+.hero-subtitle {
+ margin: 0;
+ max-width: 34rem;
+ color: var(--ink-soft);
+ font-size: 1.05rem;
+}
+
+.hero-visual {
+ justify-self: center;
+ width: min(22rem, 100%);
+ filter: drop-shadow(0 1.5rem 2.5rem rgba(26, 21, 18, 0.25));
+}
+
+.hero-visual img {
+ width: 100%;
+ height: auto;
+ object-fit: contain;
+}
+
+.modules {
+ background: linear-gradient(180deg, transparent, rgba(166, 28, 32, 0.04));
+}
+
+.module-rail {
+ display: flex;
+ gap: 1rem;
+ overflow-x: auto;
+ padding-bottom: 0.75rem;
+ scroll-snap-type: x mandatory;
+}
+
+.module-rail-item {
+ flex: 0 0 auto;
+ width: min(14rem, 72vw);
+ min-height: 9rem;
+ padding: 1.2rem 1.1rem;
+ border-left: 2px solid var(--brand);
+ background: rgba(255, 255, 255, 0.55);
+ scroll-snap-align: start;
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+ transition: transform 0.35s var(--ease), background 0.35s var(--ease);
+}
+
+.module-rail-item:hover {
+ transform: translateY(-0.25rem);
+ background: #fff;
+}
+
+.module-index {
+ font-size: 0.78rem;
+ letter-spacing: 0.14em;
+ color: var(--brand);
+}
+
+.module-desc {
+ color: var(--ink-soft);
+ font-size: 0.88rem;
+}
diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx
new file mode 100644
index 0000000..cb5d86a
--- /dev/null
+++ b/src/pages/Home.tsx
@@ -0,0 +1,61 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { hero, images, moduleEntries } from '../content/site'
+import './Home.css'
+
+export function HomePage() {
+ return (
+ <>
+
+
+
+
+
+
{hero.brand}
+
+ {hero.subBrand}
+
+
+ {hero.title}
+
+
+ {hero.subtitle}
+
+
+
+ {hero.primaryCta.label}
+
+
+ {hero.secondaryCta.label}
+
+
+
+
+

+
+
+
+
+
+
+
+ 探索杜康好客
+ 十大篇章
+ 从酒祖文脉到好客闭环,循定稿结构逐章走进。
+
+
+ {moduleEntries.map((item, index) => (
+
+
+ {String(index + 1).padStart(2, '0')}
+ {item.label}
+ {item.desc}
+
+
+ ))}
+
+
+
+ >
+ )
+}
diff --git a/src/pages/Partner.css b/src/pages/Partner.css
new file mode 100644
index 0000000..7869e29
--- /dev/null
+++ b/src/pages/Partner.css
@@ -0,0 +1,54 @@
+.partner-layout {
+ display: grid;
+ grid-template-columns: 1.2fr 0.8fr;
+ gap: 2.5rem;
+ align-items: start;
+}
+
+.partner-list {
+ list-style: none;
+ margin: 1.5rem 0 0;
+ padding: 0;
+ display: grid;
+ gap: 0;
+}
+
+.partner-row li {
+ display: grid;
+ grid-template-columns: 3rem 1fr;
+ gap: 0.85rem;
+ padding: 1.1rem 0;
+ border-bottom: 1px solid var(--tech-line);
+}
+
+.partner-idx {
+ color: var(--gold);
+ font-size: 1.15rem;
+}
+
+.partner-row h3 {
+ margin: 0 0 0.35rem;
+ color: var(--gold);
+ font-size: 1.1rem;
+}
+
+.partner-row p {
+ margin: 0;
+ color: var(--ink-soft);
+}
+
+.partner-aside .media-frame img {
+ width: 100%;
+ aspect-ratio: 4 / 5;
+ object-fit: cover;
+}
+
+.partner-cta {
+ margin-top: 1.25rem;
+}
+
+@media (max-width: 48rem) {
+ .partner-layout {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/pages/Partner.tsx b/src/pages/Partner.tsx
new file mode 100644
index 0000000..62de2ce
--- /dev/null
+++ b/src/pages/Partner.tsx
@@ -0,0 +1,48 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { images, partner } from '../content/site'
+import './Partner.css'
+
+export function PartnerPage() {
+ return (
+
+
+
+
+ {partner.eyebrow}
+ {partner.title}
+ {partner.lead}
+
+
+
+ {partner.points.map((point, i) => (
+
+ -
+ {String(i + 1).padStart(2, '0')}
+
+
{point.title}
+
{point.text}
+
+
+
+ ))}
+
+
+
+
+
+

+
+
+
+ {partner.primaryCta.label}
+
+
+ {partner.secondaryCta.label}
+
+
+
+
+
+ )
+}
diff --git a/src/pages/Poetry.css b/src/pages/Poetry.css
new file mode 100644
index 0000000..2e984a5
--- /dev/null
+++ b/src/pages/Poetry.css
@@ -0,0 +1,87 @@
+.poetry-page {
+ position: relative;
+ overflow: hidden;
+}
+
+.poetry-bg {
+ position: absolute;
+ inset: 0;
+ background-size: cover;
+ background-position: center;
+ opacity: 0.12;
+ pointer-events: none;
+}
+
+.poetry-shell {
+ position: relative;
+ z-index: 1;
+}
+
+.poem-stage {
+ margin: 1.5rem 0 2rem;
+}
+
+.poem-main {
+ margin: 0 auto;
+ max-width: 36rem;
+ padding: 2rem 1.75rem;
+ background: rgba(255, 255, 255, 0.72);
+ border: 1px solid rgba(166, 28, 32, 0.15);
+ writing-mode: vertical-rl;
+ text-orientation: mixed;
+ min-height: 18rem;
+}
+
+.poem-main p {
+ margin: 0;
+ font-size: 1.35rem;
+ letter-spacing: 0.14em;
+ line-height: 2;
+}
+
+.poem-main cite,
+.poem-slip cite {
+ display: block;
+ margin-top: 1rem;
+ font-style: normal;
+ color: var(--ink-soft);
+ font-size: 0.9rem;
+ font-family: 'Noto Sans SC', sans-serif;
+}
+
+.poem-scatter {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1.25rem;
+ align-items: stretch;
+ margin-bottom: 2rem;
+}
+
+.poem-slip {
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ height: 100%;
+ padding: 1.25rem 1.1rem;
+ background: rgba(247, 242, 234, 0.95);
+ border-top: 2px solid var(--brand);
+ box-shadow: var(--shadow-soft);
+ box-sizing: border-box;
+}
+
+.poem-slip cite {
+ margin-top: auto;
+ padding-top: 0.85rem;
+}
+
+@media (max-width: 48rem) {
+ .poem-main {
+ writing-mode: horizontal-tb;
+ text-orientation: mixed;
+ min-height: auto;
+ }
+
+ .poem-scatter {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/pages/Poetry.tsx b/src/pages/Poetry.tsx
new file mode 100644
index 0000000..05a42c3
--- /dev/null
+++ b/src/pages/Poetry.tsx
@@ -0,0 +1,57 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { images, poetry } from '../content/site'
+import './Poetry.css'
+
+export function PoetryPage() {
+ return (
+
+
+
+
+ {poetry.eyebrow}
+ {poetry.title}
+ {poetry.lead}
+
+
+
+
+ {poetry.mainPoem.quote}
+ {poetry.mainPoem.source}
+
+
+
+
+ {poetry.poems.map((poem, i) => (
+
+ 「{poem.quote}」
+ {poem.source}
+
+ ))}
+
+
+
+ {poetry.reading}
+ {poetry.closing}
+
+
+
+
+ 深度阅读
+ 选萃与唐宋余韵,把诗酒长卷摊开细读。
+
+
+ {poetry.deepReads.map((item, i) => (
+
+
+ {item.label}
+ {item.blurb}
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/src/pages/Qingxiang.css b/src/pages/Qingxiang.css
new file mode 100644
index 0000000..cbc9df0
--- /dev/null
+++ b/src/pages/Qingxiang.css
@@ -0,0 +1,138 @@
+.qx-stage {
+ position: relative;
+ display: grid;
+ place-items: center;
+ min-height: 22rem;
+ margin: 1.5rem 0 2rem;
+}
+
+.qx-bottle {
+ width: min(14rem, 46vw);
+ z-index: 2;
+}
+
+.qx-bottle img {
+ width: 100%;
+ filter: drop-shadow(0 1rem 2rem rgba(26, 21, 18, 0.28));
+}
+
+.qx-rings {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+}
+
+.qx-ring {
+ position: absolute;
+ width: 4.2rem;
+ height: 4.2rem;
+ border: 1px solid var(--brand);
+ border-radius: 50%;
+ display: grid;
+ place-items: center;
+ background: rgba(255, 255, 255, 0.75);
+ color: var(--brand);
+ font-size: 1.5rem;
+}
+
+.ring-0 {
+ left: 8%;
+ top: 18%;
+}
+.ring-1 {
+ right: 10%;
+ top: 12%;
+}
+.ring-2 {
+ right: 18%;
+ bottom: 16%;
+}
+
+.qx-style-text {
+ text-align: center;
+ margin-inline: auto;
+}
+
+.qx-cross {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1rem;
+ max-width: 48rem;
+ margin: 0 auto 2.5rem;
+}
+
+.qx-arm {
+ padding: 1.2rem 1.1rem;
+ border: 1px solid rgba(166, 28, 32, 0.14);
+ background: rgba(255, 255, 255, 0.5);
+}
+
+.qx-arm h3 {
+ margin: 0 0 0.5rem;
+ color: var(--brand);
+ font-size: 1.1rem;
+}
+
+.qx-arm p {
+ margin: 0;
+ color: var(--ink-soft);
+ font-size: 0.95rem;
+}
+
+.arm-0 {
+ border-right: none;
+ border-bottom: none;
+}
+.arm-1 {
+ border-bottom: none;
+}
+.arm-2 {
+ border-right: none;
+}
+
+.qx-split {
+ display: grid;
+ grid-template-columns: 1.05fr 0.95fr;
+ gap: 2rem;
+ align-items: center;
+}
+
+.qx-h {
+ color: var(--brand);
+ margin: 1.2rem 0 0.45rem;
+}
+
+.qx-taste {
+ display: grid;
+ gap: 0.65rem;
+ margin: 1.25rem 0;
+}
+
+.qx-taste strong {
+ color: var(--brand);
+ margin-right: 0.65rem;
+}
+
+.qx-split .media-frame img {
+ width: 100%;
+ aspect-ratio: 16 / 10;
+ object-fit: contain;
+ background: rgba(0, 0, 0, 0.03);
+}
+
+@media (max-width: 48rem) {
+ .qx-cross,
+ .qx-split {
+ grid-template-columns: 1fr;
+ }
+
+ .qx-arm {
+ border: 1px solid rgba(166, 28, 32, 0.14) !important;
+ }
+
+ .qx-ring {
+ width: 3.2rem;
+ height: 3.2rem;
+ font-size: 1.15rem;
+ }
+}
diff --git a/src/pages/Qingxiang.tsx b/src/pages/Qingxiang.tsx
new file mode 100644
index 0000000..bc63623
--- /dev/null
+++ b/src/pages/Qingxiang.tsx
@@ -0,0 +1,85 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { images, qingxiang } from '../content/site'
+import './Qingxiang.css'
+
+export function QingxiangPage() {
+ return (
+
+
+
+ {qingxiang.eyebrow}
+ {qingxiang.title}
+ {qingxiang.lead}
+
+
+
+
+
+
+
+ {qingxiang.styleWords.map((word, i) => (
+
+ {word}
+
+ ))}
+
+
+
+
+ {qingxiang.styleText}
+
+
+
+ {qingxiang.positions.map((item, i) => (
+
+ {item.title}
+ {item.text}
+
+ ))}
+
+
+
+
+ 工艺要点
+ {qingxiang.craft}
+ 产地
+ {qingxiang.origin}
+
+ {qingxiang.tasting.map((step) => (
+
+ {step.title}
+ {step.text}
+
+ ))}
+
+ {qingxiang.mission}
+ 产品露出:{qingxiang.product}
+
+
+
+

+
+
+
+
+
+
+ 清香宝典 · 深读
+ 赛道、定位、工艺、品鉴、体验与溯源,各篇独立展开。
+
+
+ {qingxiang.deepReads.map((item, i) => (
+
+
+ {item.label}
+ {item.blurb}
+
+
+ ))}
+
+
+
+
+ )
+}
diff --git a/src/pages/Subsidy.css b/src/pages/Subsidy.css
new file mode 100644
index 0000000..0ee2f73
--- /dev/null
+++ b/src/pages/Subsidy.css
@@ -0,0 +1,59 @@
+.subsidy-center {
+ text-align: center;
+}
+
+.subsidy-sub {
+ color: var(--gold);
+ letter-spacing: 0.12em;
+ margin: 0 0 1rem;
+}
+
+.big-number {
+ font-size: clamp(4.5rem, 3rem + 6vw, 8rem);
+ color: var(--gold);
+ line-height: 1;
+ margin: 1rem 0 1rem;
+ opacity: 0;
+ transform: scale(0.8);
+ transition: opacity 0.9s var(--ease), transform 0.9s cubic-bezier(0.34, 1.4, 0.64, 1);
+}
+
+.big-number.is-in {
+ opacity: 1;
+ transform: scale(1);
+}
+
+.number-note {
+ max-width: 40rem;
+ margin-inline: auto;
+ text-align: center;
+}
+
+.subsidy-ring {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1.25rem;
+ text-align: left;
+ margin: 2rem 0;
+}
+
+.subsidy-point {
+ padding: 0 0.25rem 0 1rem;
+ border-left: 2px solid var(--gold);
+}
+
+.subsidy-point h3 {
+ margin: 0 0 0.5rem;
+ color: var(--gold);
+}
+
+.subsidy-point p {
+ margin: 0;
+ color: var(--ink-soft);
+}
+
+@media (max-width: 48rem) {
+ .subsidy-ring {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/pages/Subsidy.tsx b/src/pages/Subsidy.tsx
new file mode 100644
index 0000000..d34452c
--- /dev/null
+++ b/src/pages/Subsidy.tsx
@@ -0,0 +1,61 @@
+import { useEffect, useRef, useState } from 'react'
+import { Reveal } from '../components/Reveal'
+import { subsidy } from '../content/site'
+import './Subsidy.css'
+
+export function SubsidyPage() {
+ const [showNumber, setShowNumber] = useState(false)
+ const numberRef = useRef
(null)
+
+ useEffect(() => {
+ const node = numberRef.current
+ if (!node) return
+ const observer = new IntersectionObserver(
+ (entries) => {
+ if (entries.some((e) => e.isIntersecting)) {
+ setShowNumber(true)
+ observer.disconnect()
+ }
+ },
+ { threshold: 0.35 },
+ )
+ observer.observe(node)
+ return () => observer.disconnect()
+ }, [])
+
+ return (
+
+
+
+ {subsidy.eyebrow}
+ {subsidy.title}
+ {subsidy.subtitle}
+
+ {subsidy.lead}
+
+
+
+
+ {subsidy.number}
+
+
+ {subsidy.numberNote}
+
+
+
+ {subsidy.points.map((point, i) => (
+
+ {point.title}
+ {point.text}
+
+ ))}
+
+
+
+ {subsidy.closing}
+ {subsidy.footnote}
+
+
+
+ )
+}
diff --git a/src/pages/System.css b/src/pages/System.css
new file mode 100644
index 0000000..fe136e0
--- /dev/null
+++ b/src/pages/System.css
@@ -0,0 +1,47 @@
+.system-tri {
+ position: relative;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1.25rem;
+ margin: 2.5rem 0;
+ padding-top: 1rem;
+}
+
+.system-node {
+ padding: 1.35rem 1.2rem;
+ background: rgba(255, 255, 255, 0.6);
+ border: 1px solid rgba(166, 28, 32, 0.16);
+ min-height: 12rem;
+}
+
+.system-node h3 {
+ margin: 0 0 0.65rem;
+ color: var(--brand);
+}
+
+.system-node p {
+ margin: 0;
+ color: var(--ink-soft);
+ font-size: 0.95rem;
+}
+
+.system-node .caps {
+ margin-top: 0.9rem;
+ padding-top: 0.9rem;
+ border-top: 1px dashed rgba(166, 28, 32, 0.2);
+ font-size: 0.85rem;
+}
+
+.system-loop {
+ margin: 0;
+ padding: 1.2rem 1.4rem;
+ border: 1px dashed rgba(166, 28, 32, 0.28);
+ color: var(--brand);
+ font-size: 1.1rem;
+}
+
+@media (max-width: 48rem) {
+ .system-tri {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/pages/System.tsx b/src/pages/System.tsx
new file mode 100644
index 0000000..5648bbb
--- /dev/null
+++ b/src/pages/System.tsx
@@ -0,0 +1,38 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { system } from '../content/site'
+import './System.css'
+
+export function SystemPage() {
+ return (
+
+
+
+ {system.eyebrow}
+ {system.title}
+ {system.lead}
+
+
+
+ {system.ends.map((item, i) => (
+
+ {item.title}
+ {item.role}
+ {item.caps}
+
+ ))}
+
+
+
+ {system.loop}
+ {system.closing}
+
+
+ 科技驱动
+
+
+
+
+
+ )
+}
diff --git a/src/pages/Tech.css b/src/pages/Tech.css
new file mode 100644
index 0000000..d12d28c
--- /dev/null
+++ b/src/pages/Tech.css
@@ -0,0 +1,101 @@
+.tech-page {
+ position: relative;
+ overflow: hidden;
+}
+
+.tech-grid-bg {
+ position: absolute;
+ inset: 0;
+ background-image: linear-gradient(rgba(196, 163, 90, 0.08) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(196, 163, 90, 0.08) 1px, transparent 1px);
+ background-size: 2.5rem 2.5rem;
+ mask-image: radial-gradient(circle at center, #000 20%, transparent 75%);
+ pointer-events: none;
+}
+
+.tech-orbit {
+ position: relative;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ grid-auto-rows: 1fr;
+ gap: 1.25rem;
+ margin: 3rem 0 2rem;
+ align-items: stretch;
+}
+
+.tech-orbit > .reveal-base {
+ height: 100%;
+ min-height: 11rem;
+ display: flex;
+}
+
+.tech-core {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: 6rem;
+ height: 6rem;
+ border-radius: 50%;
+ border: 1px solid var(--gold);
+ display: grid;
+ place-items: center;
+ text-align: center;
+ color: var(--gold);
+ letter-spacing: 0.1em;
+ background: rgba(28, 25, 31, 0.9);
+ z-index: 2;
+ font-size: 0.95rem;
+ pointer-events: none;
+}
+
+.tech-sat {
+ width: 100%;
+ height: 100%;
+ min-height: 11rem;
+ padding: 1.3rem 1.2rem;
+ background: var(--tech-card);
+ border: 1px solid var(--tech-line);
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
+}
+
+.tech-sat h3 {
+ margin: 0 0 0.55rem;
+ color: var(--gold);
+ flex-shrink: 0;
+}
+
+.tech-sat p {
+ margin: 0;
+ color: var(--ink-soft);
+ font-size: 0.95rem;
+ flex: 1;
+}
+
+.sat-0,
+.sat-1,
+.sat-2,
+.sat-3 {
+ justify-self: stretch;
+ max-width: none;
+ margin-top: 0;
+}
+
+@media (max-width: 48rem) {
+ .tech-orbit {
+ grid-template-columns: 1fr;
+ grid-auto-rows: auto;
+ }
+
+ .tech-core {
+ position: static;
+ transform: none;
+ margin: 0 auto 1rem;
+ }
+
+ .tech-sat {
+ min-height: 9rem;
+ }
+}
diff --git a/src/pages/Tech.tsx b/src/pages/Tech.tsx
new file mode 100644
index 0000000..e32bccd
--- /dev/null
+++ b/src/pages/Tech.tsx
@@ -0,0 +1,40 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { tech } from '../content/site'
+import './Tech.css'
+
+export function TechPage() {
+ return (
+
+
+
+
+ {tech.eyebrow}
+ {tech.title}
+ {tech.lead}
+
+
+
+
+ 数字轨道
+
+ {tech.points.map((point, i) => (
+
+ {point.title}
+ {point.text}
+
+ ))}
+
+
+
+ {tech.claim}
+
+
+ {tech.cta.label}
+
+
+
+
+
+ )
+}
diff --git a/src/pages/TechDetail.css b/src/pages/TechDetail.css
new file mode 100644
index 0000000..d01b50e
--- /dev/null
+++ b/src/pages/TechDetail.css
@@ -0,0 +1,54 @@
+.spec-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1px;
+ background: var(--tech-line);
+ border: 1px solid var(--tech-line);
+ margin: 2rem 0;
+}
+
+.spec-card {
+ background: var(--tech-card);
+ padding: 1.5rem 1.35rem;
+ position: relative;
+}
+
+.spec-no {
+ position: absolute;
+ top: 1rem;
+ right: 1.1rem;
+ color: rgba(196, 163, 90, 0.45);
+ font-size: 1.5rem;
+}
+
+.spec-card h3 {
+ margin: 0 0 0.65rem;
+ color: var(--gold);
+}
+
+.spec-card p {
+ margin: 0;
+ color: var(--ink-soft);
+ max-width: 28rem;
+}
+
+.spec-highlights {
+ list-style: none;
+ margin: 0 0 1.5rem;
+ padding: 0;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem 1.5rem;
+}
+
+.spec-highlights li {
+ color: var(--gold);
+ border-bottom: 1px solid rgba(196, 163, 90, 0.35);
+ padding-bottom: 0.2rem;
+}
+
+@media (max-width: 48rem) {
+ .spec-grid {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/pages/TechDetail.tsx b/src/pages/TechDetail.tsx
new file mode 100644
index 0000000..2a71ca1
--- /dev/null
+++ b/src/pages/TechDetail.tsx
@@ -0,0 +1,50 @@
+import { Link } from 'react-router-dom'
+import { Reveal } from '../components/Reveal'
+import { techDetail } from '../content/site'
+import './TechDetail.css'
+
+export function TechDetailPage() {
+ return (
+
+
+
+ {techDetail.eyebrow}
+ {techDetail.title}
+ {techDetail.lead}
+
+
+
+ {techDetail.ports.map((port, i) => (
+
+ {String(i + 1).padStart(2, '0')}
+ {port.title}
+ {port.text}
+
+ ))}
+
+
+
+
+ {techDetail.highlights.map((item) => (
+ -
+ {item}
+
+ ))}
+
+ {techDetail.closing}
+ {techDetail.contactHint}
+
+
+ 联系我们
+
+
+
+
+
+ )
+}
diff --git a/src/stage/StageShell.css b/src/stage/StageShell.css
new file mode 100644
index 0000000..9153150
--- /dev/null
+++ b/src/stage/StageShell.css
@@ -0,0 +1,298 @@
+/* ===== Stage Shell Ambient Theme & HUD ===== */
+.stage-shell {
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+ height: 100dvh;
+ overflow: hidden;
+ background: #0e0c10;
+ background-image:
+ radial-gradient(circle at 15% 20%, rgba(166, 28, 32, 0.15) 0%, transparent 40%),
+ radial-gradient(circle at 85% 80%, rgba(196, 163, 90, 0.12) 0%, transparent 45%),
+ linear-gradient(180deg, #141117 0%, #0c0a0e 100%);
+ color: #f3ece4;
+ font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
+ user-select: none;
+}
+
+/* Subtle grid overlay for tech/exhibition feel */
+.stage-shell::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background-image:
+ linear-gradient(rgba(196, 163, 90, 0.03) 1px, transparent 1px),
+ linear-gradient(90deg, rgba(196, 163, 90, 0.03) 1px, transparent 1px);
+ background-size: 4rem 4rem;
+ pointer-events: none;
+ z-index: 0;
+}
+
+/* HUD Top Bar with Safe Area Top */
+.stage-hud-top {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: calc(4.5rem + env(safe-area-inset-top, 0px));
+ padding-top: env(safe-area-inset-top, 0px);
+ padding-left: 2.5rem;
+ padding-right: 2.5rem;
+ box-sizing: border-box;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ z-index: 50;
+ background: linear-gradient(180deg, rgba(14, 12, 16, 0.95) 0%, rgba(14, 12, 16, 0) 100%);
+ border-bottom: 1px solid rgba(196, 163, 90, 0.15);
+ backdrop-filter: blur(8px);
+}
+
+.stage-brand {
+ display: flex;
+ align-items: center;
+ gap: 0.85rem;
+ text-decoration: none;
+}
+
+.stage-logo {
+ width: 2.4rem;
+ height: 2.4rem;
+ object-fit: contain;
+ filter: drop-shadow(0 0 8px rgba(196, 163, 90, 0.4));
+}
+
+.stage-title {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.25rem;
+ font-weight: 600;
+ letter-spacing: 0.12em;
+ color: var(--gold, #c4a35a);
+ background: linear-gradient(135deg, #f3ece4 0%, #c4a35a 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.stage-badge {
+ padding: 0.2rem 0.6rem;
+ border-radius: 0.2rem;
+ font-size: 0.75rem;
+ letter-spacing: 0.1em;
+ background: rgba(166, 28, 32, 0.25);
+ border: 1px solid rgba(166, 28, 32, 0.5);
+ color: #f3ece4;
+}
+
+/* Slide Nav Pills */
+.stage-nav-pills {
+ display: flex;
+ align-items: center;
+ gap: 0.35rem;
+ background: rgba(28, 25, 31, 0.7);
+ padding: 0.3rem 0.5rem;
+ border-radius: 2rem;
+ border: 1px solid rgba(196, 163, 90, 0.2);
+}
+
+.stage-nav-pill {
+ padding: 0.35rem 0.85rem;
+ border-radius: 1.5rem;
+ font-size: 0.82rem;
+ color: rgba(243, 236, 228, 0.65);
+ text-decoration: none;
+ transition: all 0.3s cubic-bezier(0.22, 1, 0.36, 1);
+ white-space: nowrap;
+}
+
+.stage-nav-pill:hover {
+ color: #f3ece4;
+ background: rgba(196, 163, 90, 0.15);
+}
+
+.stage-nav-pill.is-active {
+ color: #121014;
+ background: linear-gradient(135deg, #e8d08d 0%, #c4a35a 100%);
+ font-weight: 600;
+ box-shadow: 0 0 12px rgba(196, 163, 90, 0.35);
+}
+
+/* AutoPlay Button, Exit & Index Indicator */
+.stage-hud-actions {
+ display: flex;
+ align-items: center;
+ gap: 0.85rem;
+}
+
+.stage-autoplay-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.35rem;
+ padding: 0.35rem 0.75rem;
+ border-radius: 1.5rem;
+ font-size: 0.78rem;
+ color: rgba(243, 236, 228, 0.85);
+ background: rgba(28, 25, 31, 0.8);
+ border: 1px solid rgba(196, 163, 90, 0.3);
+ cursor: pointer;
+ transition: all 0.25s ease;
+}
+
+.stage-autoplay-btn.is-playing {
+ color: var(--gold, #c4a35a);
+ border-color: rgba(196, 163, 90, 0.6);
+ background: rgba(196, 163, 90, 0.15);
+}
+
+.stage-play-icon {
+ font-size: 0.7rem;
+}
+
+.stage-index-num {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.1rem;
+ letter-spacing: 0.1em;
+ color: rgba(196, 163, 90, 0.85);
+}
+
+.stage-exit-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.4rem;
+ padding: 0.4rem 1rem;
+ border-radius: 0.2rem;
+ font-size: 0.82rem;
+ color: #f3ece4;
+ background: rgba(166, 28, 32, 0.3);
+ border: 1px solid rgba(166, 28, 32, 0.6);
+ cursor: pointer;
+ transition: all 0.25s ease;
+ text-decoration: none;
+}
+
+.stage-exit-btn:hover {
+ background: rgba(166, 28, 32, 0.7);
+ color: #ffffff;
+ box-shadow: 0 0 15px rgba(166, 28, 32, 0.4);
+}
+
+/* Main Viewport Container */
+.stage-viewport {
+ position: absolute;
+ inset: 0;
+ z-index: 10;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: calc(4.5rem + env(safe-area-inset-top, 0px)) 2.5rem calc(2.5rem + env(safe-area-inset-bottom, 0px));
+}
+
+/* Bottom Progress & Hints with Safe Area Bottom */
+.stage-hud-bottom {
+ position: absolute;
+ bottom: calc(0.85rem + env(safe-area-inset-bottom, 0px));
+ left: 0;
+ right: 0;
+ z-index: 50;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 0.4rem;
+ pointer-events: none;
+}
+
+.stage-dots {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ pointer-events: auto;
+}
+
+.stage-dot {
+ position: relative;
+ width: 0.5rem;
+ height: 0.5rem;
+ border-radius: 50%;
+ background: rgba(196, 163, 90, 0.25);
+ border: none;
+ padding: 0;
+ cursor: pointer;
+ overflow: hidden;
+ transition: all 0.3s ease;
+}
+
+.stage-dot.is-active {
+ width: 2rem;
+ border-radius: 0.25rem;
+ background: rgba(196, 163, 90, 0.3);
+}
+
+.stage-dot-progress {
+ position: absolute;
+ top: 0;
+ left: 0;
+ bottom: 0;
+ width: 100%;
+ background: linear-gradient(90deg, var(--brand, #a61c20), var(--gold, #c4a35a));
+ border-radius: 0.25rem;
+ transform-origin: left;
+ animation: stageProgressBar 6s linear forwards;
+}
+
+@keyframes stageProgressBar {
+ from {
+ transform: scaleX(0);
+ }
+ to {
+ transform: scaleX(1);
+ }
+}
+
+.stage-keyboard-hint {
+ font-size: 0.72rem;
+ color: rgba(243, 236, 228, 0.4);
+ letter-spacing: 0.08em;
+}
+
+/* Mobile Responsive Adjustments for Stage Shell */
+@media (max-width: 48rem) {
+ .stage-hud-top {
+ padding-left: 1rem;
+ padding-right: 1rem;
+ height: calc(3.6rem + env(safe-area-inset-top, 0px));
+ }
+
+ .stage-nav-pills {
+ display: none;
+ }
+
+ .stage-viewport {
+ padding-top: calc(3.6rem + env(safe-area-inset-top, 0px));
+ padding-left: 1rem;
+ padding-right: 1rem;
+ padding-bottom: calc(3rem + env(safe-area-inset-bottom, 0px));
+ }
+
+ .stage-exit-btn span:first-child {
+ display: none;
+ }
+}
+
+/* Animations */
+.stage-view-anim {
+ width: 100%;
+ height: 100%;
+ animation: stageFadeIn 0.5s cubic-bezier(0.22, 1, 0.36, 1) both;
+}
+
+@keyframes stageFadeIn {
+ from {
+ opacity: 0;
+ transform: scale(0.98) translateY(10px);
+ filter: blur(4px);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1) translateY(0);
+ filter: blur(0);
+ }
+}
diff --git a/src/stage/StageShell.tsx b/src/stage/StageShell.tsx
new file mode 100644
index 0000000..40bf1f4
--- /dev/null
+++ b/src/stage/StageShell.tsx
@@ -0,0 +1,159 @@
+import { useCallback, useEffect, useRef, useState } from 'react'
+import { Link, useLocation, useNavigate } from 'react-router-dom'
+import { stageNavItems } from '../content/nav'
+import { images } from '../content/site'
+import { StageViews } from './StageViews'
+import './StageShell.css'
+
+const AUTOPLAY_INTERVAL_MS = 6000 // 6s per slide
+
+export function StageShell() {
+ const location = useLocation()
+ const navigate = useNavigate()
+ const touchStartX = useRef(null)
+ const [isAutoPlay, setIsAutoPlay] = useState(true)
+
+ const currentIndex = stageNavItems.findIndex((item) => item.path === location.pathname)
+ const activeIdx = currentIndex >= 0 ? currentIndex : 0
+
+ const goToSlide = useCallback(
+ (idx: number) => {
+ const target = stageNavItems[idx]
+ if (!target) return
+ const url = target.path === '/' ? '/?mode=stage' : `${target.path}?mode=stage`
+ navigate(url)
+ },
+ [navigate],
+ )
+
+ const handlePrev = useCallback(() => {
+ const prevIdx = (activeIdx - 1 + stageNavItems.length) % stageNavItems.length
+ goToSlide(prevIdx)
+ }, [activeIdx, goToSlide])
+
+ const handleNext = useCallback(() => {
+ const nextIdx = (activeIdx + 1) % stageNavItems.length
+ goToSlide(nextIdx)
+ }, [activeIdx, goToSlide])
+
+ // Auto-play interval timer
+ useEffect(() => {
+ if (!isAutoPlay) return
+ const timer = setInterval(() => {
+ handleNext()
+ }, AUTOPLAY_INTERVAL_MS)
+ return () => clearInterval(timer)
+ }, [isAutoPlay, handleNext])
+
+ // Touch Swipe navigation for mobile
+ const handleTouchStart = (e: React.TouchEvent) => {
+ touchStartX.current = e.touches[0].clientX
+ }
+
+ const handleTouchEnd = (e: React.TouchEvent) => {
+ if (touchStartX.current === null) return
+ const diffX = e.changedTouches[0].clientX - touchStartX.current
+ if (diffX > 50) {
+ handlePrev()
+ } else if (diffX < -50) {
+ handleNext()
+ }
+ touchStartX.current = null
+ }
+
+ // Keyboard navigation
+ useEffect(() => {
+ const onKey = (e: KeyboardEvent) => {
+ if (e.key === 'ArrowRight' || e.key === 'ArrowDown' || e.key === 'PageDown' || e.key === ' ') {
+ e.preventDefault()
+ handleNext()
+ } else if (e.key === 'ArrowLeft' || e.key === 'ArrowUp' || e.key === 'PageUp') {
+ e.preventDefault()
+ handlePrev()
+ }
+ }
+ window.addEventListener('keydown', onKey)
+ return () => window.removeEventListener('keydown', onKey)
+ }, [handleNext, handlePrev])
+
+ const normalUrl = location.pathname === '/' ? '/' : location.pathname
+
+ return (
+
+ {/* Top HUD */}
+
+
+
+ 杜康好客
+ 全景展控
+
+
+ {/* Module Navigation Pills */}
+
+
+ {/* Index, AutoPlay Toggle & Exit */}
+
+
+
+
+ {String(activeIdx + 1).padStart(2, '0')} / {String(stageNavItems.length).padStart(2, '0')}
+
+
+
+ 退出展控
+ ✕
+
+
+
+
+ {/* Main Exhibition Viewport */}
+
+
+
+
+
+
+ {/* Bottom Progress, Dots & Hints */}
+
+
+ )
+}
diff --git a/src/stage/StageViews.css b/src/stage/StageViews.css
new file mode 100644
index 0000000..afe2a85
--- /dev/null
+++ b/src/stage/StageViews.css
@@ -0,0 +1,672 @@
+/* ===== Stage Views Presentation CSS ===== */
+
+:root {
+ --stage-brand: #a61c20;
+ --stage-gold: #c4a35a;
+ --stage-gold-light: #e8d08d;
+ --stage-glass-bg: rgba(28, 25, 31, 0.78);
+ --stage-glass-border: rgba(196, 163, 90, 0.22);
+ --stage-glow: rgba(196, 163, 90, 0.15);
+}
+
+.stage-view-container {
+ width: 100%;
+ max-width: 82rem;
+ height: 100%;
+ margin: 0 auto;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ box-sizing: border-box;
+}
+
+/* Glass Card Component */
+.stage-card {
+ background: var(--stage-glass-bg);
+ border: 1px solid var(--stage-glass-border);
+ backdrop-filter: blur(14px);
+ border-radius: 0.5rem;
+ padding: 1.5rem 1.75rem;
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.35);
+ transition: all 0.35s cubic-bezier(0.22, 1, 0.36, 1);
+ position: relative;
+ overflow: hidden;
+}
+
+.stage-card::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 2px;
+ background: linear-gradient(90deg, transparent, var(--stage-gold), transparent);
+ opacity: 0.6;
+}
+
+.stage-card:hover {
+ border-color: rgba(196, 163, 90, 0.5);
+ box-shadow: 0 12px 35px rgba(166, 28, 32, 0.25);
+ transform: translateY(-2px);
+}
+
+/* Common Header Typography */
+.stage-eyebrow {
+ font-size: clamp(0.8rem, 1vw, 0.95rem);
+ color: var(--stage-gold);
+ letter-spacing: 0.2em;
+ margin: 0 0 0.5rem;
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.stage-eyebrow::before {
+ content: '';
+ width: 1.5rem;
+ height: 2px;
+ background: var(--stage-brand);
+}
+
+.stage-page-title {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(1.8rem, 2.5vw, 2.75rem);
+ font-weight: 600;
+ margin: 0 0 0.85rem;
+ color: #ffffff;
+ letter-spacing: 0.05em;
+ background: linear-gradient(135deg, #ffffff 0%, #f3ece4 60%, var(--stage-gold-light) 100%);
+ -webkit-background-clip: text;
+ -webkit-text-fill-color: transparent;
+}
+
+.stage-lead {
+ font-size: clamp(0.95rem, 1.2vw, 1.15rem);
+ line-height: 1.6;
+ color: rgba(243, 236, 228, 0.8);
+ max-width: 52rem;
+ margin: 0 auto 1.5rem;
+ text-align: center;
+}
+
+.stage-closing {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(1rem, 1.3vw, 1.25rem);
+ color: var(--stage-gold);
+ letter-spacing: 0.1em;
+ margin-top: 1.25rem;
+ text-align: center;
+ padding-top: 0.85rem;
+ border-top: 1px solid rgba(196, 163, 90, 0.2);
+ width: 100%;
+}
+
+/* Stagger Animation Base */
+.stage-stagger {
+ opacity: 0;
+ animation: stageSlideUp 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards;
+ animation-delay: calc(var(--i, 0) * 80ms);
+}
+
+@keyframes stageSlideUp {
+ from {
+ opacity: 0;
+ transform: translateY(20px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+/* ==================== Slide 1: Home ==================== */
+.stage-home-grid {
+ display: grid;
+ grid-template-columns: 1.2fr 0.8fr;
+ gap: 3rem;
+ align-items: center;
+ width: 100%;
+}
+
+.stage-home-copy {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+ text-align: left;
+}
+
+.stage-home-brand {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(2.5rem, 4vw, 4.2rem);
+ color: var(--stage-brand);
+ margin: 0;
+ letter-spacing: 0.15em;
+ text-shadow: 0 0 20px rgba(166, 28, 32, 0.4);
+}
+
+.stage-home-sub {
+ font-size: clamp(1.2rem, 1.8vw, 1.6rem);
+ color: var(--stage-gold);
+ letter-spacing: 0.12em;
+ margin: 0.2rem 0 1rem;
+}
+
+.stage-home-title {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(2rem, 3.2vw, 3.2rem);
+ color: #ffffff;
+ margin: 0 0 1rem;
+ letter-spacing: 0.08em;
+ line-height: 1.25;
+}
+
+.stage-home-desc {
+ font-size: clamp(1rem, 1.25vw, 1.25rem);
+ color: rgba(243, 236, 228, 0.82);
+ line-height: 1.75;
+ margin-bottom: 2rem;
+ max-width: 40rem;
+}
+
+.stage-home-visual {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ position: relative;
+}
+
+.stage-home-visual img {
+ width: min(20rem, 36vh);
+ height: auto;
+ filter: drop-shadow(0 15px 30px rgba(0, 0, 0, 0.6));
+ animation: stageFloat 4s ease-in-out infinite;
+}
+
+.stage-pedestal {
+ position: absolute;
+ bottom: -1rem;
+ width: 18rem;
+ height: 2.5rem;
+ background: radial-gradient(ellipse at center, rgba(196, 163, 90, 0.4) 0%, rgba(166, 28, 32, 0.2) 50%, transparent 75%);
+ border-radius: 50%;
+ filter: blur(8px);
+}
+
+@keyframes stageFloat {
+ 0%, 100% { transform: translateY(0); }
+ 50% { transform: translateY(-12px); }
+}
+
+/* ==================== Slide 2: Brand ==================== */
+.stage-brand-grid {
+ display: grid;
+ grid-template-columns: 1.1fr 0.9fr;
+ gap: 2.5rem;
+ width: 100%;
+ align-items: center;
+}
+
+.stage-brand-cards {
+ display: grid;
+ gap: 1.1rem;
+}
+
+.stage-brand-card {
+ display: grid;
+ grid-template-columns: 3rem 1fr;
+ gap: 1rem;
+ align-items: start;
+}
+
+.stage-card-num {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.5rem;
+ color: var(--stage-gold);
+}
+
+.stage-brand-card h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.25rem;
+ color: #ffffff;
+ margin: 0 0 0.4rem;
+}
+
+.stage-brand-card p {
+ font-size: 0.95rem;
+ color: rgba(243, 236, 228, 0.75);
+ margin: 0;
+ line-height: 1.6;
+}
+
+.stage-brand-media {
+ display: flex;
+ flex-direction: column;
+ gap: 1.25rem;
+}
+
+.stage-brand-media img {
+ width: 100%;
+ max-height: 14rem;
+ object-fit: cover;
+ border-radius: 0.35rem;
+ border: 1px solid var(--stage-glass-border);
+}
+
+.stage-timeline-row {
+ display: flex;
+ justify-content: space-around;
+ width: 100%;
+ margin-top: 1.5rem;
+ padding: 1rem;
+ background: var(--stage-glass-bg);
+ border: 1px solid var(--stage-glass-border);
+ border-radius: 0.35rem;
+}
+
+.stage-timeline-item {
+ display: flex;
+ align-items: center;
+ gap: 0.65rem;
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.05rem;
+ color: var(--stage-gold-light);
+}
+
+.stage-timeline-dot {
+ width: 0.5rem;
+ height: 0.5rem;
+ background: var(--stage-brand);
+ border-radius: 50%;
+ box-shadow: 0 0 8px var(--stage-brand);
+}
+
+/* ==================== Slide 3: Poetry ==================== */
+.stage-poetry-layout {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 2.5rem;
+ align-items: center;
+ width: 100%;
+}
+
+.stage-poem-main {
+ background: linear-gradient(145deg, rgba(38, 30, 42, 0.9), rgba(28, 25, 31, 0.85));
+ border: 1px solid rgba(196, 163, 90, 0.35);
+ padding: 2.2rem;
+ border-radius: 0.5rem;
+ text-align: center;
+}
+
+.stage-poem-quote {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(1.4rem, 2vw, 2rem);
+ line-height: 1.8;
+ color: var(--stage-gold-light);
+ margin: 0 0 1rem;
+}
+
+.stage-poem-cite {
+ font-size: 0.95rem;
+ color: rgba(243, 236, 228, 0.6);
+ font-style: normal;
+}
+
+.stage-poem-slips {
+ display: grid;
+ gap: 1.1rem;
+}
+
+.stage-poem-slip p {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.1rem;
+ color: #ffffff;
+ margin: 0 0 0.4rem;
+}
+
+.stage-poem-slip cite {
+ font-size: 0.88rem;
+ color: var(--stage-gold);
+ font-style: normal;
+}
+
+/* ==================== Slide 4: Qingxiang ==================== */
+.stage-qx-grid {
+ display: grid;
+ grid-template-columns: 0.85fr 0.8fr 0.85fr;
+ gap: 1.5rem;
+ align-items: center;
+ width: 100%;
+}
+
+.stage-qx-center {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ position: relative;
+}
+
+.stage-qx-center img {
+ width: min(16rem, 30vh);
+ height: auto;
+ filter: drop-shadow(0 10px 25px rgba(0, 0, 0, 0.5));
+ animation: stageFloat 4s ease-in-out infinite;
+}
+
+.stage-qx-rings {
+ display: flex;
+ gap: 0.85rem;
+ margin-top: 1rem;
+}
+
+.stage-qx-ring {
+ width: 3.5rem;
+ height: 3.5rem;
+ border-radius: 50%;
+ border: 1px solid var(--stage-gold);
+ background: rgba(166, 28, 32, 0.2);
+ display: grid;
+ place-items: center;
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.25rem;
+ color: var(--stage-gold-light);
+ box-shadow: 0 0 15px rgba(196, 163, 90, 0.2);
+}
+
+.stage-qx-list {
+ display: grid;
+ gap: 1rem;
+}
+
+.stage-qx-item h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.15rem;
+ color: var(--stage-gold);
+ margin: 0 0 0.3rem;
+}
+
+.stage-qx-item p {
+ font-size: 0.92rem;
+ color: rgba(243, 236, 228, 0.75);
+ margin: 0;
+ line-height: 1.55;
+}
+
+/* ==================== Slide 5: Haoke ==================== */
+.stage-haoke-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 2.5rem;
+ align-items: center;
+ width: 100%;
+}
+
+.stage-haoke-slogan {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(2rem, 3vw, 3rem);
+ color: var(--stage-brand);
+ margin: 0 0 1rem;
+ text-shadow: 0 0 15px rgba(166, 28, 32, 0.3);
+}
+
+.stage-haoke-mechanism {
+ display: grid;
+ gap: 1.2rem;
+}
+
+.stage-haoke-mech-card {
+ border-left: 3px solid var(--stage-brand);
+}
+
+.stage-haoke-mech-card h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.2rem;
+ color: var(--stage-gold-light);
+ margin: 0 0 0.4rem;
+}
+
+.stage-haoke-mech-card p {
+ font-size: 0.95rem;
+ color: rgba(243, 236, 228, 0.78);
+ margin: 0;
+ line-height: 1.6;
+}
+
+/* ==================== Slide 6: System ==================== */
+.stage-system-tri {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 1.5rem;
+ width: 100%;
+ margin-bottom: 1.5rem;
+}
+
+.stage-system-card h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.3rem;
+ color: var(--stage-gold);
+ margin: 0 0 0.6rem;
+}
+
+.stage-system-card .role {
+ font-size: 0.98rem;
+ color: rgba(243, 236, 228, 0.9);
+ margin-bottom: 0.85rem;
+ line-height: 1.6;
+}
+
+.stage-system-card .caps {
+ font-size: 0.85rem;
+ color: rgba(196, 163, 90, 0.85);
+ padding-top: 0.75rem;
+ border-top: 1px dashed rgba(196, 163, 90, 0.25);
+ margin: 0;
+}
+
+.stage-loop-bar {
+ width: 100%;
+ padding: 1.2rem;
+ background: rgba(166, 28, 32, 0.15);
+ border: 1px dashed var(--stage-brand);
+ border-radius: 0.4rem;
+ text-align: center;
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.15rem;
+ color: var(--stage-gold-light);
+}
+
+/* ==================== Slide 7: Tech ==================== */
+.stage-tech-orbit {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1.5rem;
+ width: 100%;
+}
+
+.stage-sat-card h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.25rem;
+ color: var(--stage-gold);
+ margin: 0 0 0.5rem;
+}
+
+.stage-sat-card p {
+ font-size: 0.95rem;
+ color: rgba(243, 236, 228, 0.78);
+ margin: 0;
+ line-height: 1.6;
+}
+
+/* ==================== Slide 8: Subsidy ==================== */
+.stage-subsidy-layout {
+ display: grid;
+ grid-template-columns: 0.85fr 1.15fr;
+ gap: 2.5rem;
+ align-items: center;
+ width: 100%;
+}
+
+.stage-subsidy-hero {
+ text-align: center;
+ background: radial-gradient(circle at center, rgba(166, 28, 32, 0.2) 0%, rgba(28, 25, 31, 0.8) 75%);
+}
+
+.stage-big-num {
+ font-family: 'Noto Serif SC', serif;
+ font-size: clamp(4.5rem, 8vw, 7.5rem);
+ font-weight: 700;
+ color: var(--stage-brand);
+ text-shadow: 0 0 30px rgba(166, 28, 32, 0.5);
+ line-height: 1;
+ margin: 0.5rem 0;
+}
+
+.stage-subsidy-cards {
+ display: grid;
+ gap: 1.2rem;
+}
+
+.stage-subsidy-card h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.2rem;
+ color: var(--stage-gold);
+ margin: 0 0 0.4rem;
+}
+
+.stage-subsidy-card p {
+ font-size: 0.95rem;
+ color: rgba(243, 236, 228, 0.78);
+ margin: 0;
+ line-height: 1.6;
+}
+
+/* ==================== Slide 9: Partner ==================== */
+.stage-partner-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 2rem;
+ width: 100%;
+}
+
+.stage-partner-section h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.25rem;
+ color: var(--stage-brand);
+ margin: 0 0 0.85rem;
+ border-bottom: 1px solid rgba(166, 28, 32, 0.3);
+ padding-bottom: 0.4rem;
+}
+
+.stage-partner-list {
+ list-style: none;
+ padding: 0;
+ margin: 0;
+ display: grid;
+ gap: 0.85rem;
+}
+
+.stage-partner-list li {
+ display: flex;
+ align-items: flex-start;
+ gap: 0.65rem;
+ font-size: 0.95rem;
+ color: rgba(243, 236, 228, 0.85);
+ line-height: 1.55;
+}
+
+.stage-partner-list li::before {
+ content: '◆';
+ color: var(--stage-gold);
+ font-size: 0.8rem;
+ margin-top: 0.2rem;
+}
+
+/* ==================== Slide 10: Tech Detail ==================== */
+.stage-ports-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1.5rem;
+ width: 100%;
+}
+
+.stage-port-card h3 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.2rem;
+ color: var(--stage-gold);
+ margin: 0 0 0.5rem;
+}
+
+.stage-port-card p {
+ font-size: 0.92rem;
+ color: rgba(243, 236, 228, 0.78);
+ margin: 0;
+ line-height: 1.6;
+}
+
+/* ==================== Slide 11: Contact ==================== */
+.stage-contact-box {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 3.5rem;
+ padding: 2.5rem 3.5rem;
+ background: linear-gradient(135deg, rgba(38, 30, 42, 0.9), rgba(28, 25, 31, 0.85));
+ border: 1px solid var(--stage-glass-border);
+ border-radius: 0.6rem;
+ box-shadow: 0 15px 40px rgba(0, 0, 0, 0.5);
+}
+
+.stage-qr-frame {
+ width: 11rem;
+ height: 11rem;
+ padding: 0.65rem;
+ background: #ffffff;
+ border-radius: 0.4rem;
+ box-shadow: 0 0 25px rgba(196, 163, 90, 0.3);
+}
+
+.stage-qr-frame img {
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+}
+
+.stage-contact-info h2 {
+ font-family: 'Noto Serif SC', serif;
+ font-size: 2rem;
+ color: var(--stage-brand);
+ margin: 0 0 0.3rem;
+}
+
+.stage-contact-info .role {
+ font-size: 1.1rem;
+ color: var(--stage-gold);
+ margin-bottom: 1.25rem;
+}
+
+.stage-contact-info .phone {
+ font-size: 1.8rem;
+ font-weight: 700;
+ color: #ffffff;
+ letter-spacing: 0.08em;
+ margin-bottom: 0.85rem;
+}
+
+.stage-contact-info .hint {
+ font-size: 0.9rem;
+ color: rgba(243, 236, 228, 0.6);
+ margin: 0;
+}
+
+/* Responsive Scaling for Ultra-wide / Smaller Display Height */
+@media (max-height: 800px) {
+ .stage-view-container {
+ transform: scale(0.92);
+ }
+}
+
+@media (max-height: 700px) {
+ .stage-view-container {
+ transform: scale(0.82);
+ }
+}
diff --git a/src/stage/StageViews.tsx b/src/stage/StageViews.tsx
new file mode 100644
index 0000000..d40f682
--- /dev/null
+++ b/src/stage/StageViews.tsx
@@ -0,0 +1,511 @@
+import { Link } from 'react-router-dom'
+import {
+ brand,
+ contact,
+ haoke,
+ hero,
+ images,
+ partner,
+ poetry,
+ qingxiang,
+ subsidy,
+ system,
+ tech,
+ techDetail,
+} from '../content/site'
+import './StageViews.css'
+
+interface StageViewsProps {
+ activePath: string
+}
+
+export function StageViews({ activePath }: StageViewsProps) {
+ switch (activePath) {
+ case '/':
+ return
+ case '/brand':
+ return
+ case '/poetry':
+ return
+ case '/qingxiang':
+ return
+ case '/haoke':
+ return
+ case '/system':
+ return
+ case '/tech':
+ return
+ case '/subsidy':
+ return
+ case '/partner':
+ return
+ case '/tech-detail':
+ return
+ case '/contact':
+ return
+ default:
+ return
+ }
+}
+
+/* 1. Home View */
+function StageHomeView() {
+ return (
+
+
+
+
+ {hero.subBrand} · 全景展面
+
+
+ {hero.brand}
+
+
+ 杜康好客
+
+
+ {hero.title}
+
+
+ {hero.subtitle}
+
+
+
+ 进入品牌展厅 ›
+
+
+
+
+
+

+
+
+
+
+ )
+}
+
+/* 2. Brand View */
+function StageBrandView() {
+ return (
+
+
+ {brand.eyebrow}
+
+
+ {brand.title}
+
+
+ {brand.lead}
+
+
+
+
+ {brand.cards.map((card, idx) => (
+
+
0{idx + 1}
+
+
{card.title}
+
{card.text}
+
+
+ ))}
+
+
+
+

+

+
+
+
+
+ {brand.timeline.map((item) => (
+
+
+ {item}
+
+ ))}
+
+
+ )
+}
+
+/* 3. Poetry View */
+function StagePoetryView() {
+ return (
+
+
+ {poetry.eyebrow}
+
+
+ {poetry.title}
+
+
+ {poetry.lead}
+
+
+
+
+
“{poetry.mainPoem.quote}”
+
—— {poetry.mainPoem.source}
+
+
+
+ {poetry.poems.map((poem, idx) => (
+
+
“{poem.quote}”
+
—— {poem.source}
+
+ ))}
+
+
+
+
+ {poetry.closing}
+
+
+ )
+}
+
+/* 4. Qingxiang View */
+function StageQingxiangView() {
+ return (
+
+
+ {qingxiang.eyebrow}
+
+
+ {qingxiang.title}
+
+
+ {qingxiang.lead}
+
+
+
+
+ {qingxiang.positions.slice(0, 2).map((pos, idx) => (
+
+
{pos.title}
+
{pos.text}
+
+ ))}
+
+
+
+

+
+ {qingxiang.styleWords.map((word) => (
+
+ {word}
+
+ ))}
+
+
+
+
+ {qingxiang.positions.slice(2, 4).map((pos, idx) => (
+
+
{pos.title}
+
{pos.text}
+
+ ))}
+
+
+
+
+ {qingxiang.mission}
+
+
+ )
+}
+
+/* 5. Haoke View */
+function StageHaokeView() {
+ return (
+
+
+ {haoke.eyebrow}
+
+
+ {haoke.title}
+
+
+ {haoke.lead}
+
+
+
+
+
+ 核心模式机制
+
+
+ {haoke.oneLiner}
+
+
{haoke.closing}
+
+
+
+ {haoke.points.map((pt, idx) => (
+
+
{pt.title}
+
{pt.text}
+
+ ))}
+
+
+
+ )
+}
+
+/* 6. System View */
+function StageSystemView() {
+ return (
+
+
+ {system.eyebrow}
+
+
+ {system.title}
+
+
+ {system.lead}
+
+
+
+ {system.ends.map((end, idx) => (
+
+
{end.title}
+
{end.role}
+
核心能力:{end.caps}
+
+ ))}
+
+
+
+ 闭环链路:{system.loop}
+
+
+ )
+}
+
+/* 7. Tech View */
+function StageTechView() {
+ return (
+
+
+ {tech.eyebrow}
+
+
+ {tech.title}
+
+
+ {tech.lead}
+
+
+
+ {tech.points.map((pt, idx) => (
+
+
{pt.title}
+
{pt.text}
+
+ ))}
+
+
+
+ {tech.claim}
+
+
+ )
+}
+
+/* 8. Subsidy View */
+function StageSubsidyView() {
+ return (
+
+
+ {subsidy.eyebrow}
+
+
+ {subsidy.title}
+
+
+ {subsidy.lead}
+
+
+
+
+
专项资源规模
+
{subsidy.number}
+
{subsidy.numberNote}
+
+
+
+ {subsidy.points.map((pt, idx) => (
+
+
{pt.title}
+
{pt.text}
+
+ ))}
+
+
+
+
+ {subsidy.closing}
+
+
+ )
+}
+
+/* 9. Partner View */
+function StagePartnerView() {
+ return (
+
+
+ {partner.eyebrow}
+
+
+ {partner.title}
+
+
+ {partner.lead}
+
+
+
+
+
核心竞争优势
+
+ {partner.points.slice(0, 3).map((pt) => (
+ -
+ {pt.title}: {pt.text}
+
+ ))}
+
+
+
+
+
合作要求与支持
+
+ {partner.points.slice(3, 5).map((pt) => (
+ -
+ {pt.title}: {pt.text}
+
+ ))}
+
+
+
+
+
+
+ 立即联系合作 ›
+
+
+
+ )
+}
+
+/* 10. Tech Detail View */
+function StageTechDetailView() {
+ return (
+
+
+ {techDetail.eyebrow}
+
+
+ {techDetail.title}
+
+
+ {techDetail.lead}
+
+
+
+ {techDetail.ports.map((port, idx) => (
+
+
{port.title}
+
{port.text}
+
+ ))}
+
+
+
+ {techDetail.closing}
+
+
+ )
+}
+
+/* 11. Contact View */
+function StageContactView() {
+ return (
+
+
+ {contact.eyebrow}
+
+
+ {contact.title}
+
+
+ {contact.lead}
+
+
+
+
+

+
+
+
+
{contact.name}
+
{contact.role}
+
{contact.phone}
+
{contact.wechatHint}
+
+
+
+ )
+}
diff --git a/src/styles/animations.css b/src/styles/animations.css
new file mode 100644
index 0000000..39ebb88
--- /dev/null
+++ b/src/styles/animations.css
@@ -0,0 +1,291 @@
+@keyframes fadeRise {
+ from {
+ opacity: 0;
+ transform: translateY(1.25rem);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+@keyframes softZoom {
+ from {
+ opacity: 0;
+ transform: scale(1.08);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes slideLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-2rem);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes slideRight {
+ from {
+ opacity: 0;
+ transform: translateX(2rem);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes unroll {
+ from {
+ opacity: 0;
+ clip-path: inset(0 0 100% 0);
+ }
+ to {
+ opacity: 1;
+ clip-path: inset(0 0 0 0);
+ }
+}
+
+@keyframes inkBloom {
+ from {
+ opacity: 0;
+ filter: blur(8px);
+ transform: scale(0.96);
+ }
+ to {
+ opacity: 1;
+ filter: blur(0);
+ transform: scale(1);
+ }
+}
+
+@keyframes popScale {
+ from {
+ opacity: 0;
+ transform: scale(0.5);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes letterSweep {
+ from {
+ opacity: 0;
+ letter-spacing: 0.55em;
+ }
+ to {
+ opacity: 1;
+ letter-spacing: 0.08em;
+ }
+}
+
+@keyframes floatY {
+ 0%,
+ 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-0.6rem);
+ }
+}
+
+@keyframes drawLineY {
+ from {
+ transform: scaleY(0);
+ }
+ to {
+ transform: scaleY(1);
+ }
+}
+
+@keyframes expandOut {
+ from {
+ opacity: 0;
+ transform: scale(0.7);
+ }
+ to {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes diagIn {
+ from {
+ opacity: 0;
+ transform: translate(1.5rem, 1.5rem);
+ }
+ to {
+ opacity: 1;
+ transform: translate(0, 0);
+ }
+}
+
+@keyframes meetInLeft {
+ from {
+ opacity: 0;
+ transform: translateX(-3rem);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+@keyframes meetInRight {
+ from {
+ opacity: 0;
+ transform: translateX(3rem);
+ }
+ to {
+ opacity: 1;
+ transform: translateX(0);
+ }
+}
+
+.reveal-base {
+ opacity: 0;
+}
+
+.reveal-base.is-in {
+ opacity: 1;
+}
+
+.anim-rise {
+ transform: translateY(1.25rem);
+ transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
+}
+.anim-rise.is-in {
+ transform: translateY(0);
+}
+
+.anim-slide-left {
+ transform: translateX(-2rem);
+ transition: opacity 0.85s var(--ease), transform 0.85s var(--ease);
+}
+.anim-slide-left.is-in {
+ transform: translateX(0);
+}
+
+.anim-slide-right {
+ transform: translateX(2rem);
+ transition: opacity 0.85s var(--ease), transform 0.85s var(--ease);
+}
+.anim-slide-right.is-in {
+ transform: translateX(0);
+}
+
+.anim-zoom {
+ transform: scale(1.08);
+ transition: opacity 1s var(--ease), transform 1s var(--ease);
+}
+.anim-zoom.is-in {
+ transform: scale(1);
+}
+
+.anim-unroll {
+ clip-path: inset(0 0 100% 0);
+ transition: opacity 1s var(--ease), clip-path 1.1s var(--ease);
+}
+.anim-unroll.is-in {
+ clip-path: inset(0 0 0 0);
+}
+
+.anim-ink {
+ filter: blur(8px);
+ transform: scale(0.96);
+ transition: opacity 1s var(--ease), filter 1s var(--ease), transform 1s var(--ease);
+}
+.anim-ink.is-in {
+ filter: blur(0);
+ transform: scale(1);
+}
+
+.anim-pop {
+ transform: scale(0.5);
+ transition: opacity 0.6s var(--ease), transform 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
+}
+.anim-pop.is-in {
+ transform: scale(1);
+}
+
+.anim-sweep {
+ letter-spacing: 0.55em;
+ transition: opacity 1s var(--ease), letter-spacing 1.1s var(--ease);
+}
+.anim-sweep.is-in {
+ letter-spacing: 0.08em;
+}
+
+.anim-expand {
+ transform: scale(0.7);
+ transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
+}
+.anim-expand.is-in {
+ transform: translate(0) scale(1);
+}
+
+.anim-diag {
+ transform: translate(1.5rem, 1.5rem);
+ transition: opacity 0.75s var(--ease), transform 0.75s var(--ease);
+}
+.anim-diag.is-in {
+ transform: translate(0, 0);
+}
+
+.anim-meet-left {
+ transform: translateX(-3rem);
+ transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
+}
+.anim-meet-left.is-in {
+ transform: translateX(0);
+}
+
+.anim-meet-right {
+ transform: translateX(3rem);
+ transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
+}
+.anim-meet-right.is-in {
+ transform: translateX(0);
+}
+
+.hero-float {
+ animation: floatY 4.5s ease-in-out infinite;
+}
+
+.media-frame img,
+.hero-visual img {
+ transition: transform 0.8s var(--ease);
+}
+
+.media-frame:hover img {
+ transform: scale(1.03);
+}
+
+@media (prefers-reduced-motion: reduce) {
+ html {
+ scroll-behavior: auto;
+ }
+
+ .reveal-base,
+ .hero-float,
+ .media-frame img,
+ .btn,
+ .page-enter {
+ animation: none !important;
+ transition: none !important;
+ opacity: 1 !important;
+ transform: none !important;
+ filter: none !important;
+ clip-path: none !important;
+ letter-spacing: normal !important;
+ }
+}
diff --git a/src/styles/responsive.css b/src/styles/responsive.css
new file mode 100644
index 0000000..9ca798b
--- /dev/null
+++ b/src/styles/responsive.css
@@ -0,0 +1,97 @@
+@media (max-width: 48rem) {
+ :root {
+ --nav-h: 3.75rem;
+ }
+
+ .desktop-only {
+ display: none !important;
+ }
+
+ .page-section {
+ padding: 3.5rem 0 !important;
+ }
+
+ .split {
+ grid-template-columns: 1fr !important;
+ gap: 1.75rem !important;
+ }
+
+ .hero {
+ min-height: auto !important;
+ padding: 5.5rem 0 3rem !important;
+ }
+
+ .hero-grid {
+ grid-template-columns: 1fr !important;
+ gap: 2rem !important;
+ }
+
+ .hero-title {
+ font-size: 2rem !important;
+ }
+
+ .module-grid,
+ .card-grid,
+ .ends-grid,
+ .ports-grid,
+ .qx-cross,
+ .qx-split,
+ .spec-grid,
+ .subsidy-ring,
+ .partner-layout,
+ .brand-layout,
+ .contact-meet,
+ .system-tri,
+ .tech-orbit {
+ grid-template-columns: 1fr !important;
+ }
+
+ .module-rail {
+ margin-inline: -0.25rem;
+ }
+
+ .poem-main {
+ writing-mode: horizontal-tb !important;
+ text-orientation: mixed !important;
+ min-height: auto !important;
+ padding: 1.5rem !important;
+ }
+
+ .timeline {
+ flex-direction: column !important;
+ align-items: flex-start !important;
+ }
+
+ .contact-grid {
+ grid-template-columns: 1fr !important;
+ }
+
+ .nav-links {
+ display: none;
+ }
+
+ .photo-placeholder {
+ max-width: 100% !important;
+ }
+
+ .loop-line {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+}
+
+@media (min-width: 48.0625rem) {
+ .mobile-only {
+ display: none !important;
+ }
+}
+
+@media (min-width: 90rem) {
+ .page-title {
+ font-size: 3rem;
+ }
+
+ .hero-title {
+ font-size: 3.5rem !important;
+ }
+}
diff --git a/src/styles/tokens.css b/src/styles/tokens.css
new file mode 100644
index 0000000..7ac550a
--- /dev/null
+++ b/src/styles/tokens.css
@@ -0,0 +1,242 @@
+:root {
+ --brand: #a61c20;
+ --brand-deep: #7d1216;
+ --brand-soft: rgba(166, 28, 32, 0.08);
+ --gold: #c4a35a;
+ --gold-soft: rgba(196, 163, 90, 0.35);
+ --ink: #1a1512;
+ --ink-soft: #5c534c;
+ --paper: #f7f2ea;
+ --paper-deep: #ebe3d6;
+ --white: #ffffff;
+ --tech-bg: #121014;
+ --tech-card: #1c191f;
+ --tech-line: rgba(196, 163, 90, 0.28);
+ --max: min(72rem, 92vw);
+ --nav-h: 4.5rem;
+ --ease: cubic-bezier(0.22, 1, 0.36, 1);
+ --shadow-soft: 0 1.25rem 3rem rgba(26, 21, 18, 0.08);
+}
+
+/* rem base: 375px -> 14px, 1920px -> 18px */
+html {
+ font-size: clamp(0.875rem, 0.75rem + 0.35vw, 1.125rem);
+ scroll-behavior: smooth;
+}
+
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ color: var(--ink);
+ background: var(--paper);
+ font-family: 'Noto Sans SC', 'PingFang SC', 'Microsoft YaHei', sans-serif;
+ line-height: 1.7;
+ -webkit-font-smoothing: antialiased;
+}
+
+img {
+ max-width: 100%;
+ display: block;
+}
+
+a {
+ color: inherit;
+ text-decoration: none;
+}
+
+button {
+ font: inherit;
+ cursor: pointer;
+ border: none;
+ background: none;
+}
+
+h1,
+h2,
+h3,
+.serif {
+ font-family: 'Noto Serif SC', 'Songti SC', serif;
+ font-weight: 600;
+ line-height: 1.35;
+ letter-spacing: 0.02em;
+}
+
+.container {
+ width: var(--max);
+ margin-inline: auto;
+}
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 2.75rem;
+ padding: 0.7rem 1.6rem;
+ border-radius: 0.15rem;
+ transition: background 0.35s var(--ease), color 0.35s var(--ease), transform 0.35s var(--ease),
+ box-shadow 0.35s var(--ease);
+}
+
+.btn-primary {
+ background: var(--brand);
+ color: var(--white);
+}
+
+.btn-primary:hover {
+ background: var(--brand-deep);
+ transform: translateY(-0.1rem);
+ box-shadow: var(--shadow-soft);
+}
+
+.btn-ghost {
+ color: var(--brand);
+ border: 1px solid var(--brand);
+ background: transparent;
+}
+
+.btn-ghost:hover {
+ background: var(--brand-soft);
+}
+
+.btn-light {
+ background: var(--white);
+ color: var(--brand);
+}
+
+.btn-light:hover {
+ transform: translateY(-0.1rem);
+}
+
+.eyebrow {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.55rem;
+ color: var(--brand);
+ font-size: 0.875rem;
+ letter-spacing: 0.18em;
+ text-transform: none;
+ margin-bottom: 0.85rem;
+}
+
+.eyebrow::before {
+ content: '';
+ width: 1.5rem;
+ height: 1px;
+ background: var(--brand);
+}
+
+.page-title {
+ font-size: clamp(1.75rem, 1.4rem + 1.2vw, 2.75rem);
+ margin: 0 0 1rem;
+}
+
+.lead {
+ font-size: 1.05rem;
+ color: var(--ink-soft);
+ max-width: 42rem;
+ margin: 0 0 2rem;
+}
+
+.closing {
+ margin-top: 2.5rem;
+ padding-top: 1.5rem;
+ border-top: 1px solid var(--gold-soft);
+ font-family: 'Noto Serif SC', serif;
+ font-size: 1.1rem;
+ color: var(--ink);
+}
+
+.theme-tech {
+ --ink: #f3ece4;
+ --ink-soft: rgba(243, 236, 228, 0.72);
+ --paper: var(--tech-bg);
+ color: var(--ink);
+ background: var(--tech-bg);
+}
+
+.theme-tech .eyebrow,
+.theme-tech .closing {
+ color: var(--gold);
+}
+
+.theme-tech .eyebrow::before {
+ background: var(--gold);
+}
+
+.theme-tech .closing {
+ border-top-color: var(--tech-line);
+}
+
+.theme-tech .btn-ghost {
+ color: var(--gold);
+ border-color: var(--gold);
+}
+
+.theme-tech .btn-ghost:hover {
+ background: rgba(196, 163, 90, 0.12);
+}
+
+.deep-read-rail {
+ margin-top: 3rem;
+ padding-top: 2rem;
+ border-top: 1px solid rgba(166, 28, 32, 0.12);
+}
+
+.deep-read-title {
+ margin: 0 0 0.45rem;
+ color: var(--brand);
+ font-size: 1.45rem;
+ letter-spacing: 0.08em;
+}
+
+.deep-read-lead {
+ margin: 0 0 1.35rem;
+ color: var(--ink-soft);
+}
+
+.deep-read-grid {
+ display: grid;
+ grid-template-columns: repeat(2, 1fr);
+ gap: 0.85rem 1rem;
+}
+
+.poetry-deep-grid {
+ grid-template-columns: 1fr 1fr;
+}
+
+.deep-read-card {
+ display: grid;
+ gap: 0.35rem;
+ padding: 1.15rem 1.2rem;
+ background: rgba(255, 255, 255, 0.55);
+ border-left: 3px solid var(--gold);
+ transition: transform 0.25s var(--ease), background 0.25s var(--ease);
+}
+
+.deep-read-card:hover {
+ transform: translateY(-0.15rem);
+ background: rgba(255, 255, 255, 0.85);
+}
+
+.deep-read-card strong {
+ color: var(--brand);
+ font-size: 1.05rem;
+}
+
+.deep-read-card span {
+ color: var(--ink-soft);
+ font-size: 0.92rem;
+ line-height: 1.55;
+}
+
+@media (max-width: 48rem) {
+ .deep-read-grid,
+ .poetry-deep-grid {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts
new file mode 100644
index 0000000..11f02fe
--- /dev/null
+++ b/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/tsconfig.app.json b/tsconfig.app.json
new file mode 100644
index 0000000..b382d79
--- /dev/null
+++ b/tsconfig.app.json
@@ -0,0 +1,23 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+ "target": "ES2022",
+ "useDefineForClassFields": true,
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "jsx": "react-jsx",
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["src"]
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..1ffef60
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,7 @@
+{
+ "files": [],
+ "references": [
+ { "path": "./tsconfig.app.json" },
+ { "path": "./tsconfig.node.json" }
+ ]
+}
diff --git a/tsconfig.node.json b/tsconfig.node.json
new file mode 100644
index 0000000..953116c
--- /dev/null
+++ b/tsconfig.node.json
@@ -0,0 +1,21 @@
+{
+ "compilerOptions": {
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+ "target": "ES2023",
+ "lib": ["ES2023"],
+ "module": "ESNext",
+ "skipLibCheck": true,
+ "moduleResolution": "bundler",
+ "allowImportingTsExtensions": true,
+ "verbatimModuleSyntax": true,
+ "moduleDetection": "force",
+ "noEmit": true,
+ "strict": true,
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "erasableSyntaxOnly": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedSideEffectImports": true
+ },
+ "include": ["vite.config.ts"]
+}
diff --git a/vite.config.ts b/vite.config.ts
new file mode 100644
index 0000000..9ffcc67
--- /dev/null
+++ b/vite.config.ts
@@ -0,0 +1,6 @@
+import { defineConfig } from 'vite'
+import react from '@vitejs/plugin-react'
+
+export default defineConfig({
+ plugins: [react()],
+})