精简首页预览版本并优化响应式布局
This commit is contained in:
1100
src/TravelSite.css
1100
src/TravelSite.css
File diff suppressed because it is too large
Load Diff
@@ -6,14 +6,11 @@ import {
|
||||
ArrowLeft,
|
||||
ArrowRight,
|
||||
CheckCircle,
|
||||
Globe,
|
||||
Headset,
|
||||
List,
|
||||
MagnifyingGlass,
|
||||
MapPin,
|
||||
PauseCircle,
|
||||
PlayCircle,
|
||||
UsersThree,
|
||||
X,
|
||||
} from '@phosphor-icons/react';
|
||||
import {
|
||||
@@ -1819,9 +1816,6 @@ function routeDetailPillars(safari: SafariCardItem, language: SiteLanguage = 'zh
|
||||
}
|
||||
|
||||
function selectPage(path: string) {
|
||||
if (path === '/preview/classic') return <HomePage variant="classic" />;
|
||||
if (path === '/preview/v1') return <HomePage variant="v1" />;
|
||||
if (path === '/preview/v2') return <EditorialHomePage />;
|
||||
if (path === '/safari-faqs' || path === '/faqs') return <SafariFaqsPage />;
|
||||
if (path.startsWith('/our-guides')) return <GuidesPage />;
|
||||
if (path.startsWith('/meet-the-team')) return <TeamPage />;
|
||||
@@ -1855,269 +1849,6 @@ function selectPage(path: string) {
|
||||
return <HomePage />;
|
||||
}
|
||||
|
||||
function EditorialHomePage() {
|
||||
const { content, language } = useTravelSite();
|
||||
const editorialRef = useRef<HTMLElement | null>(null);
|
||||
const home = content.home;
|
||||
const editorialExperiences = [
|
||||
{ number: '01', title: pickText('山野', 'Wild Karst', language), eyebrow: pickText('徒步 · 骑行 · 洞穴', 'Hike · Cycle · Caves', language), image: '/client-assets/karst-hike-ridge.jpg', href: '/experiences/karst-hiking/' },
|
||||
{ number: '02', title: pickText('村寨', 'Village Life', language), eyebrow: pickText('日常 · 家宴 · 手作', 'Homes · Tables · Craft', language), image: '/client-assets/villages-xijiang-dusk.jpg', href: '/experiences/indigenous-ethnic-villages/' },
|
||||
{ number: '03', title: pickText('水世界', 'Water Worlds', language), eyebrow: pickText('瀑布 · 峡谷 · 河流', 'Falls · Canyons · Rivers', language), image: '/client-assets/waterfall-huangguoshu-curtain.jpg', href: '/experiences/iconic-waterfall-clusters/' },
|
||||
{ number: '04', title: pickText('贵州味道', 'Taste Guizhou', language), eyebrow: pickText('家宴 · 市场 · 山野咖啡', 'Tables · Markets · Coffee', language), image: '/client-assets/culinary-long-table-banquet.jpg', href: '/experiences/culinary-cultural-journeys/' },
|
||||
];
|
||||
const editorialRoutes = home.safaris.cards.slice(0, 3);
|
||||
const fieldImages = [
|
||||
'/client-assets/team-field-day.webp',
|
||||
'/client-assets/culture-batik-making.jpg',
|
||||
'/client-assets/river-packraft-shore.jpg',
|
||||
'/client-assets/ancient-towns-zhenyuan-night.jpg',
|
||||
'/client-assets/culinary-long-table-banquet.jpg',
|
||||
];
|
||||
|
||||
useGSAP(
|
||||
() => {
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||
|
||||
const heroTimeline = gsap.timeline({ defaults: { ease: 'power3.out' } });
|
||||
heroTimeline
|
||||
.from('.oc-v2-hero > img', { scale: 1.13, autoAlpha: 0.4, duration: 1.8, ease: 'power2.out' })
|
||||
.from('.oc-v2-hero-copy > p', { y: 20, autoAlpha: 0, duration: 0.6 }, '-=1.18')
|
||||
.from('.oc-v2-hero h1', { y: 90, autoAlpha: 0, duration: 1.05 }, '-=.85')
|
||||
.from('.oc-v2-hero-bottom > *', { y: 32, autoAlpha: 0, duration: 0.7, stagger: 0.12 }, '-=.58')
|
||||
.from('.oc-v2-hero-scroll', { scale: 0.65, autoAlpha: 0, duration: 0.55 }, '-=.45');
|
||||
|
||||
gsap.to('.oc-v2-hero > img', {
|
||||
yPercent: 10,
|
||||
scale: 1.08,
|
||||
ease: 'none',
|
||||
scrollTrigger: { trigger: '.oc-v2-hero', start: 'top top+=90', end: 'bottom top', scrub: 0.7 },
|
||||
});
|
||||
|
||||
gsap.to('.oc-v2-hero-scroll svg', {
|
||||
y: 8,
|
||||
duration: 1.15,
|
||||
repeat: -1,
|
||||
yoyo: true,
|
||||
ease: 'sine.inOut',
|
||||
});
|
||||
|
||||
gsap.utils.toArray<HTMLElement>('.oc-v2-section-intro, .oc-v2-field-heading').forEach((heading) => {
|
||||
gsap.from(heading.children, {
|
||||
y: 58,
|
||||
autoAlpha: 0,
|
||||
duration: 0.9,
|
||||
stagger: 0.12,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: heading, start: 'top 78%', once: true },
|
||||
});
|
||||
});
|
||||
|
||||
gsap.from('.oc-v2-experience-card', {
|
||||
y: 86,
|
||||
clipPath: 'inset(0 0 100% 0)',
|
||||
duration: 1.05,
|
||||
stagger: 0.11,
|
||||
ease: 'power4.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-experience-grid', start: 'top 76%', once: true },
|
||||
});
|
||||
gsap.from('.oc-v2-experience-card > img', {
|
||||
scale: 1.18,
|
||||
duration: 1.5,
|
||||
stagger: 0.11,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-experience-grid', start: 'top 76%', once: true },
|
||||
});
|
||||
|
||||
gsap.from('.oc-v2-feature-image', {
|
||||
clipPath: 'inset(0 100% 0 0)',
|
||||
duration: 1.25,
|
||||
ease: 'power4.inOut',
|
||||
scrollTrigger: { trigger: '.oc-v2-feature', start: 'top 72%', once: true },
|
||||
});
|
||||
gsap.from('.oc-v2-feature-image img', {
|
||||
scale: 1.16,
|
||||
duration: 1.6,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-feature', start: 'top 72%', once: true },
|
||||
});
|
||||
gsap.from('.oc-v2-feature-copy > *', {
|
||||
y: 58,
|
||||
autoAlpha: 0,
|
||||
duration: 0.82,
|
||||
stagger: 0.1,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-feature-copy', start: 'top 74%', once: true },
|
||||
});
|
||||
|
||||
gsap.from('.oc-v2-route-card', {
|
||||
y: 90,
|
||||
clipPath: 'inset(100% 0 0 0)',
|
||||
duration: 1.08,
|
||||
stagger: 0.13,
|
||||
ease: 'power4.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-route-grid', start: 'top 76%', once: true },
|
||||
});
|
||||
gsap.from('.oc-v2-route-card img', {
|
||||
scale: 1.16,
|
||||
duration: 1.55,
|
||||
stagger: 0.13,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-route-grid', start: 'top 76%', once: true },
|
||||
});
|
||||
|
||||
gsap.from('.oc-v2-field-collage figure, .oc-v2-field-note', {
|
||||
y: 76,
|
||||
scale: 0.96,
|
||||
autoAlpha: 0,
|
||||
duration: 0.9,
|
||||
stagger: 0.1,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-field-collage', start: 'top 76%', once: true },
|
||||
});
|
||||
gsap.to('.oc-v2-field-collage figure.is-1 img', {
|
||||
yPercent: 10,
|
||||
scale: 1.08,
|
||||
ease: 'none',
|
||||
scrollTrigger: { trigger: '.oc-v2-field-collage', start: 'top bottom', end: 'bottom top', scrub: 0.8 },
|
||||
});
|
||||
|
||||
gsap.from('.oc-v2-local-media img:first-child', {
|
||||
clipPath: 'inset(0 0 100% 0)',
|
||||
scale: 1.1,
|
||||
duration: 1.25,
|
||||
ease: 'power4.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-local', start: 'top 72%', once: true },
|
||||
});
|
||||
gsap.from('.oc-v2-local-media img:last-child', {
|
||||
x: 70,
|
||||
y: 60,
|
||||
rotate: 2,
|
||||
autoAlpha: 0,
|
||||
duration: 0.95,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-local', start: 'top 65%', once: true },
|
||||
});
|
||||
gsap.from('.oc-v2-local-copy > *', {
|
||||
y: 52,
|
||||
autoAlpha: 0,
|
||||
duration: 0.78,
|
||||
stagger: 0.09,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-local-copy', start: 'top 74%', once: true },
|
||||
});
|
||||
|
||||
gsap.from('.oc-v2-final > div > *', {
|
||||
y: 66,
|
||||
autoAlpha: 0,
|
||||
duration: 0.9,
|
||||
stagger: 0.13,
|
||||
ease: 'power3.out',
|
||||
scrollTrigger: { trigger: '.oc-v2-final', start: 'top 66%', once: true },
|
||||
});
|
||||
gsap.fromTo('.oc-v2-final > img', { scale: 1.06 }, {
|
||||
scale: 1.16,
|
||||
ease: 'none',
|
||||
scrollTrigger: { trigger: '.oc-v2-final', start: 'top bottom', end: 'bottom top', scrub: 0.9 },
|
||||
});
|
||||
},
|
||||
{ scope: editorialRef, dependencies: [language], revertOnUpdate: true },
|
||||
);
|
||||
|
||||
return (
|
||||
<main ref={editorialRef} className="oc-home-v2" data-no-translate>
|
||||
<section className="oc-v2-hero" aria-labelledby="v2-hero-title">
|
||||
<img src={asset(home.hero.imageUrl)} alt={pickText('贵州喀斯特山野中的万趣私家旅行', 'A private WanderQ journey in the karst landscape of Guizhou', language)} />
|
||||
<div className="oc-v2-hero-shade" aria-hidden="true" />
|
||||
<div className="oc-v2-hero-copy">
|
||||
<p>{pickText('贵州,中国西南 · 私家旅行', 'Guizhou, Southwest China · Private travel', language)}</p>
|
||||
<h1 id="v2-hero-title">
|
||||
{pickText('大地,', 'Earth', language)}<br />
|
||||
<em>{pickText('邀请你走近。', 'invites you closer.', language)}</em>
|
||||
</h1>
|
||||
<div className="oc-v2-hero-bottom">
|
||||
<span>{pickText('山水不是背景,生活也不是表演。跟随真正生活在这里的人,去往贵州更深处。', 'Landscapes are not a backdrop, and local life is not a performance. Travel deeper with people who live here.', language)}</span>
|
||||
<a href="/contact/">{pickText('设计我的旅程', 'Design my journey', language)} <ArrowRight size={18} aria-hidden="true" /></a>
|
||||
</div>
|
||||
</div>
|
||||
<a className="oc-v2-hero-scroll" href="#v2-discover" aria-label={pickText('向下探索', 'Scroll to discover', language)}>
|
||||
<ArrowRight size={20} weight="light" aria-hidden="true" />
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<section className="oc-v2-discover" id="v2-discover" aria-labelledby="v2-discover-title">
|
||||
<div className="oc-v2-section-intro">
|
||||
<span>01 / {pickText('选择一种靠近方式', 'Choose how to get closer', language)}</span>
|
||||
<div>
|
||||
<h2 id="v2-discover-title">{pickText('贵州不只有一个入口。', 'There is more than one way into Guizhou.', language)}</h2>
|
||||
<p>{pickText('从喀斯特山野、村寨日常、水世界或贵州餐桌出发。每一种体验都可以成为一条私家路线的起点。', 'Begin with wild karst, village life, water worlds or the local table. Each can become the first chapter of a private route.', language)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="oc-v2-experience-grid">
|
||||
{editorialExperiences.map((item, index) => (
|
||||
<a className={`oc-v2-experience-card is-${index + 1}`} href={item.href} key={item.number}>
|
||||
<img src={item.image} alt={item.title} />
|
||||
<span>{item.number}</span>
|
||||
<div><small>{item.eyebrow}</small><h3>{item.title}</h3></div>
|
||||
<ArrowRight size={20} aria-hidden="true" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="oc-v2-feature" aria-labelledby="v2-feature-title">
|
||||
<div className="oc-v2-feature-image">
|
||||
<img src="/client-assets/brand-real-guizhou.webp" alt={pickText('贵州村寨真实生活', 'Everyday life in a Guizhou village', language)} />
|
||||
<span>{pickText('贵州 · 黔东南', 'Southeast Guizhou', language)}</span>
|
||||
</div>
|
||||
<div className="oc-v2-feature-copy">
|
||||
<span>02 / {pickText('本期故事', 'Featured story', language)}</span>
|
||||
<h2 id="v2-feature-title">{pickText('去一个地方,也理解一个地方。', 'Go somewhere. Understand it, too.', language)}</h2>
|
||||
<p>{pickText('在村寨里留下一晚,在家常饭桌上多坐一会,听懂一门手艺和一栋木楼背后的生活。我们把“看见”变成真正的相遇。', 'Stay a night in the village. Linger at the family table. Understand the life behind a craft and a timber home. We turn seeing into meeting.', language)}</p>
|
||||
<a href="/experiences/village-immersion/">{pickText('阅读村寨故事', 'Read the village story', language)} <ArrowRight size={16} aria-hidden="true" /></a>
|
||||
<div className="oc-v2-feature-facts">
|
||||
<div><b>100%</b><span>{pickText('私家定制', 'private planning', language)}</span></div>
|
||||
<div><b>24/7</b><span>{pickText('在地协调', 'local support', language)}</span></div>
|
||||
<div><b>EN / 中</b><span>{pickText('双语支持', 'bilingual support', language)}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="oc-v2-routes" aria-labelledby="v2-routes-title">
|
||||
<div className="oc-v2-section-intro is-light">
|
||||
<span>03 / {pickText('旅行灵感', 'Journey inspiration', language)}</span>
|
||||
<div><h2 id="v2-routes-title">{pickText('三条路线,都是讨论的开始。', 'Three routes. Each starts a conversation.', language)}</h2><p>{pickText('先选一个接近你的方向,再按日期、兴趣、体力和住宿偏好重新编排。', 'Choose the direction closest to you, then reshape it around dates, interests, energy and stays.', language)}</p></div>
|
||||
</div>
|
||||
<div className="oc-v2-route-grid">
|
||||
{editorialRoutes.map((route, index) => (
|
||||
<a href={resolveMergedExperienceHref(route.href)} className={`oc-v2-route-card is-${index + 1}`} key={route.id}>
|
||||
<img src={asset(route.imageUrl)} alt={oc(route.title, language)} />
|
||||
<div><span>{String(index + 1).padStart(2, '0')} · {oc(route.nights, language)}</span><h3>{oc(route.title, language)}</h3><p>{oc(route.copy, language)}</p><b>{pickText('打开路线', 'Open journey', language)} <ArrowRight size={15} aria-hidden="true" /></b></div>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="oc-v2-field" aria-labelledby="v2-field-title">
|
||||
<div className="oc-v2-field-heading"><span>04 / {pickText('来自现场', 'From the field', language)}</span><h2 id="v2-field-title">{pickText('真实旅行,没有摆拍的模板。', 'Real journeys. No staged template.', language)}</h2></div>
|
||||
<div className="oc-v2-field-collage">
|
||||
{fieldImages.map((image, index) => <figure className={`is-${index + 1}`} key={image}><img src={image} alt={pickText(`贵州旅行现场 ${index + 1}`, `Guizhou journey moment ${index + 1}`, language)} />{index === 0 ? <figcaption>{pickText('万趣本地团队,在贵州。', 'The WanderQ team, here in Guizhou.', language)}</figcaption> : null}</figure>)}
|
||||
<div className="oc-v2-field-note"><PlayCircle size={52} weight="thin" aria-hidden="true" /><p>{pickText('一顿饭、一条山路、一场手作,也可能是旅程里最久被记住的部分。', 'A meal, a mountain road or time at the craft table can become the longest-held memory.', language)}</p></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="oc-v2-local" aria-labelledby="v2-local-title">
|
||||
<div className="oc-v2-local-media"><img src="/client-assets/team-wanderq-group.webp" alt={pickText('万趣贵州本地团队', 'The Guizhou-based WanderQ team', language)} /><img src="/client-assets/team-field-day.webp" alt={pickText('万趣团队旅行现场', 'WanderQ team in the field', language)} /></div>
|
||||
<div className="oc-v2-local-copy"><span>05 / {pickText('为什么是万趣', 'Why WanderQ', language)}</span><h2 id="v2-local-title">{pickText('离现场近一点,旅程就从容一点。', 'Closer to the field. Calmer on the road.', language)}</h2><p>{pickText('我们在贵州本地规划与执行。熟悉山路、天气、水位与村寨节奏,也理解境外客人需要怎样清楚、可靠的英文旅行支持。', 'We plan and operate from Guizhou—close to the roads, weather, water and village rhythms, with clear English-language support for international guests.', language)}</p><ul><li>{pickText('一组客人,一套私家方案', 'One private plan for one party', language)}</li><li>{pickText('合规入境接待与透明报价', 'Compliant inbound reception and clear pricing', language)}</li><li>{pickText('行程期间 24 小时在地协调', '24-hour local coordination while travelling', language)}</li></ul><a href="/our-story/">{pickText('认识本地团队', 'Meet the local team', language)} <ArrowRight size={16} aria-hidden="true" /></a></div>
|
||||
</section>
|
||||
|
||||
<section className="oc-v2-final" aria-labelledby="v2-final-title">
|
||||
<img src="/client-assets/ancient-towns-zhenyuan-night.jpg" alt={pickText('贵州镇远古城夜景', 'Zhenyuan ancient town at night', language)} />
|
||||
<div><span>{pickText('下一段旅程,从一场对话开始。', 'Your next journey starts with a conversation.', language)}</span><h2 id="v2-final-title">{pickText('贵州,等你走进。', 'Guizhou is waiting.', language)}</h2><a href="/contact/">{pickText('开始规划', 'Start planning', language)} <ArrowRight size={18} aria-hidden="true" /></a></div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
function Header({
|
||||
language,
|
||||
menuOpen,
|
||||
@@ -2687,19 +2418,17 @@ function SearchOverlay({ onClose }: { onClose: () => void }) {
|
||||
);
|
||||
}
|
||||
|
||||
function HomePage({ variant = 'v1' }: { variant?: 'classic' | 'v1' }) {
|
||||
function HomePage() {
|
||||
const { content, language } = useTravelSite();
|
||||
const prototypeRef = useRef<HTMLElement | null>(null);
|
||||
const home = content.home;
|
||||
const heroSlides = useMemo(
|
||||
() => variant === 'v1'
|
||||
? [
|
||||
'/client-assets/atlas-hero-waterfall.webp',
|
||||
'/client-assets/hero-real-travelers.webp',
|
||||
'/client-assets/hero-miao-home-meal.webp',
|
||||
]
|
||||
: [home.hero.imageUrl, ...home.hero.awardUrls].filter(Boolean),
|
||||
[home.hero.awardUrls, home.hero.imageUrl, variant],
|
||||
() => [
|
||||
'/client-assets/atlas-hero-waterfall.webp',
|
||||
'/client-assets/hero-real-travelers.webp',
|
||||
'/client-assets/hero-miao-home-meal.webp',
|
||||
],
|
||||
[],
|
||||
);
|
||||
const [heroIndex, setHeroIndex] = useState(0);
|
||||
const [heroPaused, setHeroPaused] = useState(false);
|
||||
@@ -2707,7 +2436,6 @@ function HomePage({ variant = 'v1' }: { variant?: 'classic' | 'v1' }) {
|
||||
|
||||
useGSAP(
|
||||
() => {
|
||||
if (variant === 'classic') return;
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||
|
||||
const heroTimeline = gsap.timeline({ defaults: { ease: 'power3.out' } });
|
||||
@@ -2759,7 +2487,7 @@ function HomePage({ variant = 'v1' }: { variant?: 'classic' | 'v1' }) {
|
||||
scrollTrigger: { trigger: '.oc-atlas-plan', start: 'top 72%', once: true },
|
||||
});
|
||||
},
|
||||
{ scope: prototypeRef, dependencies: [language, variant], revertOnUpdate: true },
|
||||
{ scope: prototypeRef, dependencies: [language], revertOnUpdate: true },
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -2770,29 +2498,6 @@ function HomePage({ variant = 'v1' }: { variant?: 'classic' | 'v1' }) {
|
||||
return () => window.clearInterval(timer);
|
||||
}, [heroPaused, heroSlides.length]);
|
||||
|
||||
const trustSignals = [
|
||||
{
|
||||
icon: MapPin,
|
||||
title: pickText('贵州本地团队', 'Guizhou-based team', language),
|
||||
copy: pickText('熟悉山路、天气与在地节奏', 'Local knowledge of roads, weather and daily rhythms', language),
|
||||
},
|
||||
{
|
||||
icon: UsersThree,
|
||||
title: pickText('纯玩私家小团', 'Private journeys only', language),
|
||||
copy: pickText('按人数、兴趣与节奏定制', 'Designed around your people, interests and pace', language),
|
||||
},
|
||||
{
|
||||
icon: Globe,
|
||||
title: pickText('英文旅行支持', 'English trip support', language),
|
||||
copy: pickText('双语沟通与文化说明', 'Bilingual planning and cultural context', language),
|
||||
},
|
||||
{
|
||||
icon: Headset,
|
||||
title: pickText('24 小时在地支持', '24/7 on-ground support', language),
|
||||
copy: pickText('行程期间持续响应与协调', 'Responsive coordination throughout your journey', language),
|
||||
},
|
||||
];
|
||||
|
||||
const prototypeJourneySteps = [
|
||||
{
|
||||
number: '01',
|
||||
@@ -2811,134 +2516,9 @@ function HomePage({ variant = 'v1' }: { variant?: 'classic' | 'v1' }) {
|
||||
},
|
||||
];
|
||||
|
||||
const brandValues = [
|
||||
{
|
||||
number: '01',
|
||||
title: pickText('合规入境接待', 'Compliant inbound reception', language),
|
||||
copy: pickText('为境外游客提供清晰、正规的入境接待流程。', 'A clear, compliant reception process for international travelers.', language),
|
||||
},
|
||||
{
|
||||
number: '02',
|
||||
title: pickText('懂贵州,也会讲贵州', 'Local stories, not scripts', language),
|
||||
copy: pickText('讲清这里的文化与生活,不做背书式流水线导游。', 'Guizhou specialists who explain the culture, not a memorized script.', language),
|
||||
},
|
||||
{
|
||||
number: '03',
|
||||
title: pickText('熟悉山路与天气', 'Locals who know the road', language),
|
||||
copy: pickText('熟悉贵州山路、天气与现场变化,及时响应。', 'Local knowledge of mountain roads, weather and changing conditions.', language),
|
||||
},
|
||||
{
|
||||
number: '04',
|
||||
title: pickText('不赶场的真实旅行', 'A slower, more human pace', language),
|
||||
copy: pickText('拒绝赶场式打卡,把时间留给山水、村寨与真实生活。', 'No checklist travel—more time for landscapes, villages and daily life.', language),
|
||||
},
|
||||
{
|
||||
number: '05',
|
||||
title: pickText('一人一行程,价格透明', 'One journey, clearly priced', language),
|
||||
copy: pickText('按你的兴趣与节奏设计行程,清楚说明价格与服务。', 'A private itinerary shaped around you, with pricing explained clearly.', language),
|
||||
},
|
||||
];
|
||||
|
||||
const inquiryPrompts = [
|
||||
{
|
||||
number: '01',
|
||||
title: pickText('什么时候,和谁一起', 'When, and with whom', language),
|
||||
copy: pickText('大致日期和同行人数就够了,不需要现在完全确定。', 'Rough dates and party size are enough. Nothing needs to be fixed yet.', language),
|
||||
},
|
||||
{
|
||||
number: '02',
|
||||
title: pickText('你为什么想来贵州', 'Why Guizhou', language),
|
||||
copy: pickText('户外、山水、村寨、人文或美食,告诉我们你最在意的部分。', 'Adventure, landscapes, villages, culture, or food—tell us what matters most.', language),
|
||||
},
|
||||
{
|
||||
number: '03',
|
||||
title: pickText('怎样的节奏最适合你', 'How you like to travel', language),
|
||||
copy: pickText('轻松一些、充实一些,或者两者之间,再加上你的住宿期待。', 'Easy days, fuller days, or a mix—plus the kind of stays that feel right.', language),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<main ref={prototypeRef} className={variant === 'v1' ? 'oc-home-atlas' : 'oc-home-classic'} data-no-translate>
|
||||
{variant === 'classic' ? (
|
||||
<>
|
||||
<section className="oc-home-hero">
|
||||
<img
|
||||
key={activeHero}
|
||||
className="oc-hero-media"
|
||||
src={asset(activeHero)}
|
||||
alt={language === 'zh' ? '万趣贵州私家旅行现场' : 'A private WanderQ journey in Guizhou'}
|
||||
/>
|
||||
{home.hero.tripAdvisorUrl ? <img className="oc-trip" src={asset(home.hero.tripAdvisorUrl)} alt="Tripadvisor" /> : null}
|
||||
<div className="oc-hero-copy">
|
||||
<p>{oc(home.hero.kicker, language)}</p>
|
||||
<h1>{oc(home.hero.title, language)}</h1>
|
||||
<p className="oc-hero-summary">
|
||||
{pickText('越过景点清单,跟随本地人走进山水、村寨与真实生活。', 'Go beyond the sights with local people, landscapes and everyday life.', language)}
|
||||
</p>
|
||||
<a className="oc-orange-btn" href={home.hero.ctaHref}>
|
||||
{oc(home.hero.ctaLabel, language)}
|
||||
<ArrowRight size={18} aria-hidden="true" />
|
||||
</a>
|
||||
<div className="oc-hero-dots" aria-label={language === 'zh' ? '首屏图片轮播控制' : 'Hero slideshow controls'}>
|
||||
{heroSlides.map((slide, index) => (
|
||||
<button
|
||||
type="button"
|
||||
className={`oc-hero-dot${index === heroIndex ? ' is-active' : ''}`}
|
||||
aria-label={language === 'zh' ? `查看第 ${index + 1} 张首屏图` : `Show hero slide ${index + 1}`}
|
||||
aria-current={index === heroIndex ? 'true' : undefined}
|
||||
onClick={() => setHeroIndex(index)}
|
||||
key={slide}
|
||||
/>
|
||||
))}
|
||||
<button
|
||||
type="button"
|
||||
className="oc-hero-playback"
|
||||
aria-label={heroPaused ? (language === 'zh' ? '继续轮播' : 'Resume slideshow') : (language === 'zh' ? '暂停轮播' : 'Pause slideshow')}
|
||||
onClick={() => setHeroPaused((current) => !current)}
|
||||
>
|
||||
{heroPaused ? <PlayCircle size={22} weight="light" aria-hidden="true" /> : <PauseCircle size={22} weight="light" aria-hidden="true" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="oc-home-trust" aria-label={language === 'zh' ? '万趣服务保障' : 'WanderQ service assurances'}>
|
||||
<div className="oc-container oc-home-trust-grid">
|
||||
{trustSignals.map(({ icon: Icon, title, copy }) => (
|
||||
<article className="oc-home-trust-item" key={title}>
|
||||
<Icon size={25} weight="light" aria-hidden="true" />
|
||||
<div><strong>{title}</strong><span>{copy}</span></div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="oc-home-brand-story" aria-labelledby="home-brand-story-title">
|
||||
<div className="oc-container">
|
||||
<div className="oc-brand-story-layout">
|
||||
<figure className="oc-brand-story-media">
|
||||
<img src="/client-assets/brand-real-guizhou.webp" alt={language === 'zh' ? '贵州村寨中的真实生活' : 'Everyday life in a Guizhou village'} />
|
||||
</figure>
|
||||
<div className="oc-brand-story-copy">
|
||||
<span className="oc-brand-eyebrow">{language === 'zh' ? '为什么选择万趣' : 'Why WanderQ'}</span>
|
||||
<h2 id="home-brand-story-title">{pickText('越过旅行泡泡,走进真实贵州', 'Travel beyond the bubble.', language)}</h2>
|
||||
<p>{pickText('万趣的核心不是“售卖旅游产品”,而是创造能够改变人与地方关系的旅行体验。我们拒绝景点清单式打卡,让旅行者真正靠近贵州的山水、日常生活与当地人。', 'WanderQ is not here to sell a checklist of sights. We create private journeys that bring you closer to the landscapes, everyday life and people of Guizhou.', language)}</p>
|
||||
<blockquote>{pickText('“把脚步留给山风、瀑声与村寨灯火,其余的交给我们。”', '“Leave your footsteps to mountain winds, waterfalls and village lights. Leave the rest to us.”', language)}</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div className="oc-brand-values" aria-label={language === 'zh' ? '万趣核心价值' : 'WanderQ core values'}>
|
||||
{brandValues.map((value) => (
|
||||
<article className="oc-brand-value" key={value.number}>
|
||||
<span>{value.number}</span><h3>{value.title}</h3><p>{value.copy}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<section className="oc-atlas-hero" aria-labelledby="atlas-hero-title">
|
||||
<main ref={prototypeRef} className="oc-home-atlas" data-no-translate>
|
||||
<section className="oc-atlas-hero" aria-labelledby="atlas-hero-title">
|
||||
<div className="oc-atlas-hero-copy">
|
||||
<p className="oc-atlas-coordinate">26°03′–29°13′ N 103°36′–109°35′ E</p>
|
||||
<h1 id="atlas-hero-title">
|
||||
@@ -3062,11 +2642,6 @@ function HomePage({ variant = 'v1' }: { variant?: 'classic' | 'v1' }) {
|
||||
</figure>
|
||||
</section>
|
||||
|
||||
</>
|
||||
)}
|
||||
|
||||
{variant === 'v1' && (
|
||||
<>
|
||||
<section className="oc-atlas-team" aria-labelledby="atlas-team-title">
|
||||
<div className="oc-atlas-team-copy oc-atlas-reveal">
|
||||
<p>{pickText('扎根贵州 · 面向世界', 'LOCAL ROOTS, GLOBAL STANDARDS', language)}</p>
|
||||
@@ -3101,98 +2676,6 @@ function HomePage({ variant = 'v1' }: { variant?: 'classic' | 'v1' }) {
|
||||
<small>{pickText('双语沟通 · 私家定制 · 本地支持', 'Bilingual planning · Private travel · Local support', language)}</small>
|
||||
</address>
|
||||
</section>
|
||||
</>
|
||||
)}
|
||||
|
||||
<section className="oc-home-team oc-container" id="about-advantages" aria-labelledby="home-team-title">
|
||||
<div className="oc-home-team-layout">
|
||||
<div className="oc-home-team-media">
|
||||
<img
|
||||
className="oc-home-team-main-image"
|
||||
src="/client-assets/team-field-day.webp"
|
||||
alt={language === 'zh' ? '贵州户外旅行现场的万趣团队与客人' : 'WanderQ team and guests during an outdoor day in Guizhou'}
|
||||
/>
|
||||
<img
|
||||
className="oc-home-team-field-image"
|
||||
src="/client-assets/team-wanderq-group.webp"
|
||||
alt={language === 'zh' ? '万趣团队与旅行行业伙伴' : 'WanderQ team with travel-industry partners'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="oc-home-team-copy">
|
||||
<span className="oc-brand-eyebrow">{language === 'zh' ? '贵州本地团队' : 'Guizhou-based team'}</span>
|
||||
<h2 id="home-team-title">{oc(home.journey.title, language)}</h2>
|
||||
<p className="oc-home-team-intro">
|
||||
{pickText(
|
||||
'我们在贵州本地判断路线、沟通现场并照应行程。山路、天气、语言和临时变化,不需要你独自面对。',
|
||||
'We judge routes, coordinate the field, and support each journey from Guizhou—so mountain roads, weather, language, and last-minute changes are never yours to handle alone.',
|
||||
language,
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className="oc-home-team-services">
|
||||
{home.journey.cards.map((item, index) => (
|
||||
<article className="oc-home-team-service" key={item.id}>
|
||||
<span>{String(index + 1).padStart(2, '0')}</span>
|
||||
<div>
|
||||
<h3>{oc(item.title, language)}</h3>
|
||||
<p>{oc(item.copy, language)}</p>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<a className="oc-outline-btn oc-home-team-cta" href="/contact/">
|
||||
{language === 'zh' ? '和本地团队聊聊' : 'Talk with our local team'}
|
||||
<ArrowRight size={18} aria-hidden="true" />
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="oc-home-final-cta" aria-labelledby="home-final-cta-title">
|
||||
<div className="oc-container oc-home-final-cta-layout">
|
||||
<div className="oc-home-final-cta-copy">
|
||||
<span className="oc-home-final-eyebrow">{oc(content.footerCta.eyebrowLabel, language)}</span>
|
||||
<h2 id="home-final-cta-title">
|
||||
{language === 'zh' ? (
|
||||
<>从你真正想要的<br /><em>贵州开始。</em></>
|
||||
) : (
|
||||
<>Start with what<br /><em>matters to you.</em></>
|
||||
)}
|
||||
</h2>
|
||||
<p>
|
||||
{pickText(
|
||||
'大致日期就够了。告诉我们谁会同行,以及你最想感受的贵州。本地团队会把这些信息整理成一个清楚的旅行方向。',
|
||||
'Rough dates are enough. Tell us who is traveling and the part of Guizhou you most want to feel. Our local team will shape that into one clear journey direction.',
|
||||
language,
|
||||
)}
|
||||
</p>
|
||||
<a className="oc-orange-btn oc-home-final-cta-button" href={content.footerCta.href}>
|
||||
{pickText('告诉我们你的大致想法', 'Share my rough plan', language)}
|
||||
<ArrowRight size={18} aria-hidden="true" />
|
||||
</a>
|
||||
<small className="oc-home-final-cta-note">
|
||||
{pickText('不需要固定行程 · 直接和贵州本地团队沟通', 'No fixed itinerary needed · Direct conversation with our Guizhou team', language)}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div className="oc-home-inquiry-prompts" aria-label={language === 'zh' ? '咨询时可以提供的信息' : 'Helpful details for your inquiry'}>
|
||||
<span className="oc-home-inquiry-prompts-label">
|
||||
{pickText('开始时,这三件事就够了', 'Three things are enough to begin', language)}
|
||||
</span>
|
||||
{inquiryPrompts.map((prompt) => (
|
||||
<article className="oc-home-inquiry-prompt" key={prompt.number}>
|
||||
<span>{prompt.number}</span>
|
||||
<div>
|
||||
<h3>{prompt.title}</h3>
|
||||
<p>{prompt.copy}</p>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -941,23 +941,6 @@ body:has(.oc-home-atlas) .oc-footer-legal-meta {
|
||||
body:has(.oc-home-atlas) .oc-header-actions { grid-column: 2; }
|
||||
body:has(.oc-home-atlas) .oc-header-cta { display: none; }
|
||||
|
||||
.oc-atlas-hero {
|
||||
grid-template-columns: 1fr 54px;
|
||||
grid-template-rows: auto minmax(480px, 62svh) 48px;
|
||||
}
|
||||
|
||||
.oc-atlas-hero-copy {
|
||||
grid-column: 1;
|
||||
padding: 54px 38px 48px;
|
||||
}
|
||||
|
||||
.oc-atlas-hero-media { grid-column: 1; grid-row: 2; }
|
||||
.oc-atlas-stamp { grid-column: 2; grid-row: 1 / 3; }
|
||||
.oc-atlas-hero-rail { grid-row: 3; }
|
||||
.oc-atlas-hero-rail > span:nth-child(2),
|
||||
.oc-atlas-hero-rail > span:nth-child(3) { display: none; }
|
||||
.oc-atlas-hero-rail { grid-template-columns: 1fr auto; }
|
||||
|
||||
.oc-atlas-experience-stage {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
@@ -1008,6 +991,27 @@ body:has(.oc-home-atlas) .oc-footer-legal-meta {
|
||||
.oc-atlas-contact small { width: 100%; }
|
||||
}
|
||||
|
||||
@media (max-width: 700px) {
|
||||
.oc-atlas-hero {
|
||||
grid-template-columns: 1fr 54px;
|
||||
grid-template-rows: auto minmax(480px, 62svh) 48px;
|
||||
}
|
||||
|
||||
.oc-atlas-hero-copy {
|
||||
grid-column: 1;
|
||||
padding: 54px 38px 48px;
|
||||
}
|
||||
|
||||
.oc-atlas-hero-media { grid-column: 1; grid-row: 2; }
|
||||
.oc-atlas-stamp { grid-column: 2; grid-row: 1 / 3; }
|
||||
.oc-atlas-hero-rail {
|
||||
grid-row: 3;
|
||||
grid-template-columns: 1fr auto;
|
||||
}
|
||||
.oc-atlas-hero-rail > span:nth-child(2),
|
||||
.oc-atlas-hero-rail > span:nth-child(3) { display: none; }
|
||||
}
|
||||
|
||||
@media (max-width: 620px) {
|
||||
body:has(.oc-home-atlas) .oc-header { padding-inline: 18px; }
|
||||
body:has(.oc-home-atlas) .oc-logo img { width: 116px; }
|
||||
|
||||
@@ -28,10 +28,6 @@
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-outline-btn,
|
||||
.oc-home-v1 .oc-home-final-cta .oc-orange-btn {
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
/* Shared framed content receives one calm, consistent surface treatment. */
|
||||
.oc-site :is(
|
||||
@@ -55,78 +51,6 @@
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Homepage: remove the long pauses between the five core story beats. */
|
||||
.oc-home-v1 .oc-home-experiences {
|
||||
margin-top: 0;
|
||||
padding-top: clamp(56px, 4.8vw, 72px);
|
||||
padding-bottom: clamp(62px, 5.4vw, 82px);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-experiences .oc-section-title {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-experience-stack {
|
||||
gap: clamp(14px, 1.45vw, 24px);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-experience-band {
|
||||
height: 620px;
|
||||
min-height: 620px;
|
||||
grid-template-rows: 342px 278px;
|
||||
border-radius: var(--oc-card-radius-lg);
|
||||
box-shadow: var(--oc-card-shadow);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-experience-band:hover {
|
||||
box-shadow: var(--oc-card-shadow-hover);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-experiences > .oc-center {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.oc-prototype-story {
|
||||
column-gap: clamp(54px, 6vw, 92px);
|
||||
padding-top: clamp(64px, 5.2vw, 78px);
|
||||
padding-bottom: clamp(64px, 5vw, 76px);
|
||||
}
|
||||
|
||||
.oc-prototype-story-image {
|
||||
min-height: 660px;
|
||||
border-radius: var(--oc-card-radius-lg);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team {
|
||||
padding-top: clamp(68px, 5.6vw, 84px);
|
||||
padding-bottom: clamp(72px, 5.8vw, 88px);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team-layout {
|
||||
gap: clamp(46px, 5.5vw, 82px);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team-media {
|
||||
min-height: 610px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team-main-image {
|
||||
height: 540px;
|
||||
border-radius: var(--oc-card-radius-lg);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team-field-image {
|
||||
height: 210px;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-final-cta {
|
||||
padding-block: clamp(78px, 6.4vw, 98px);
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-final-cta-layout {
|
||||
gap: clamp(52px, 6vw, 88px);
|
||||
}
|
||||
|
||||
/* Experience archive: create distinct cards instead of one rigid tiled wall. */
|
||||
.oc-experience-editorial .oc-menu-page-hero {
|
||||
@@ -286,24 +210,6 @@
|
||||
}
|
||||
|
||||
@media (max-width: 820px) {
|
||||
.oc-home-v1 .oc-home-experiences {
|
||||
padding-top: 58px;
|
||||
padding-bottom: 62px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-experience-stack {
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-experience-band {
|
||||
height: auto;
|
||||
min-height: 390px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team {
|
||||
padding-top: 62px;
|
||||
padding-bottom: 68px;
|
||||
}
|
||||
|
||||
.oc-experience-series-nav {
|
||||
padding-bottom: 62px;
|
||||
@@ -336,49 +242,6 @@
|
||||
padding-inline: 21px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-experiences {
|
||||
padding-top: 50px;
|
||||
padding-bottom: 54px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-experiences .oc-section-title {
|
||||
margin-bottom: 28px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-experiences > .oc-center {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.oc-prototype-story {
|
||||
padding-top: 52px;
|
||||
padding-bottom: 58px;
|
||||
}
|
||||
|
||||
.oc-prototype-story-image {
|
||||
min-height: 410px;
|
||||
margin-bottom: 42px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team {
|
||||
padding-top: 54px;
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team-layout {
|
||||
gap: 38px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team-media {
|
||||
min-height: 410px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-team-main-image {
|
||||
height: 330px;
|
||||
}
|
||||
|
||||
.oc-home-v1 .oc-home-final-cta {
|
||||
padding-block: 62px 66px;
|
||||
}
|
||||
|
||||
.oc-experience-editorial .oc-menu-page-hero {
|
||||
padding-bottom: 80px;
|
||||
|
||||
Reference in New Issue
Block a user