Browse Source

initialize file

chenhg 4 months ago
commit
65135e0773

+ 25 - 0
App.tsx

@@ -0,0 +1,25 @@
+
+import React from 'react';
+import { HashRouter as Router, Routes, Route } from 'react-router-dom';
+import Home from '@/src/pages/Home';
+import Ships from '@/src/pages/Ships';
+import { LanguageProvider } from '@/src/contexts/LanguageContext';
+import { ThemeProvider } from '@/src/contexts/ThemeContext';
+
+const App: React.FC = () => {
+  return (
+    <ThemeProvider>
+      <LanguageProvider>
+        <Router>
+          <Routes>
+            <Route path="/" element={<Home />} />
+            <Route path="/ships" element={<Ships />} />
+            <Route path="*" element={<Home />} />
+          </Routes>
+        </Router>
+      </LanguageProvider>
+    </ThemeProvider>
+  );
+};
+
+export default App;

+ 20 - 0
README.md

@@ -0,0 +1,20 @@
+<div align="center">
+<img width="1200" height="475" alt="GHBanner" src="https://github.com/user-attachments/assets/0aa67016-6eaf-458a-adb2-6e31a0763ed6" />
+</div>
+
+# Run and deploy your AI Studio app
+
+This contains everything you need to run your app locally.
+
+View your app in AI Studio: https://ai.studio/apps/drive/1XWAbVSC2xBYeYi0zaCrmk9V7AHFwgc_V
+
+## Run Locally
+
+**Prerequisites:**  Node.js
+
+
+1. Install dependencies:
+   `npm install`
+2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key
+3. Run the app:
+   `npm run dev`

+ 410 - 0
constants.ts

@@ -0,0 +1,410 @@
+
+
+import { MenuItem, CruiseShip, Itinerary, Feature, Language } from './types';
+
+export const PHONE_NUMBER = "400-696-0666";
+
+// Placeholder images for the carousel (Simulating the user provided images)
+// 1. Interior Dining/Lounge
+// 2. Deck with Umbrellas
+// 3. Ship/Lock/Gorge view
+export const HERO_IMAGES = [
+  "https://images.unsplash.com/photo-1578474843222-9593bc88d8b0?q=80&w=1920&auto=format&fit=crop", // Dining/Lounge feel
+  "https://images.unsplash.com/photo-1599640845513-2627a35c602a?q=80&w=1920&auto=format&fit=crop", // Deck/Ocean feel
+  "https://images.unsplash.com/photo-1534055691060-0382f646016c?q=80&w=1920&auto=format&fit=crop"  // Gorge/River feel
+];
+
+export const CONTENT = {
+  zh: {
+    nav: {
+      book: "立即预订",
+      contact: "联系客服",
+      menu: [
+        { title: "首页", link: "#/" },
+        { 
+          title: "长江行游轮", 
+          link: "#/ships",
+          submenu: [
+            { title: "长江行系列", link: "#/ships?section=series" },
+            { title: "长江行·揽月", link: "#/ships?section=lanyue" },
+            { title: "长江行·极光", link: "#/ships?section=aurora" },
+          ]
+        },
+        { 
+          title: "精选航线", 
+          link: "#/itineraries",
+          submenu: [
+            { title: "航线行程", link: "#itinerary-routes" },
+            { title: "产品亮点", link: "#itinerary-highlights" },
+            { title: "游轮航期", link: "#itinerary-schedules" },
+          ]
+        },
+        { 
+          title: "游轮体验", 
+          link: "#experience",
+          submenu: [
+            { title: "品质服务", link: "#exp-service" },
+            { title: "星级餐饮", link: "#exp-dining" },
+            { title: "丰富活动", link: "#exp-activities" },
+          ]
+        },
+        { 
+          title: "游轮空间", 
+          link: "#spaces",
+          submenu: [
+            { title: "设施设备", link: "#space-facilities" },
+            { title: "房型介绍", link: "#space-rooms" },
+            { title: "尊享礼遇", link: "#space-vip" },
+          ]
+        },
+        { 
+          title: "出行攻略", 
+          link: "#guide",
+          submenu: [
+            { title: "出行指南", link: "#guide-tips" },
+            { title: "常见问题", link: "#guide-faq" },
+          ]
+        },
+        { 
+          title: "关于我们", 
+          link: "#about",
+          submenu: [
+            { title: "企业简介", link: "#about-intro" },
+            { title: "官方媒体", link: "#about-media" },
+          ]
+        },
+      ]
+    },
+    hero: {
+      welcome: "欢迎来到长江行游轮",
+      title_italic: "驻足",
+      title_normal: "即是旅途",
+      subtitle: "体验长江之上的奢华与服务,超乎您的想象。水上圣殿,静候君临。",
+      btn_find: "探索航线",
+      btn_ships: "游轮舰队"
+    },
+    home: {
+      featured_sub: "精选航程",
+      featured_title: "甄选航线",
+      view_all: "查看所有航线",
+      days: "天",
+      details: "查看详情",
+      dining_title: "卓越美食",
+      dining_desc: "品味米其林灵感菜单,由世界级名厨精选当地最新鲜食材烹饪。每一餐都是一次味蕾的旅行。",
+      dining_btn: "探索餐饮",
+      fleet_sub: "游轮舰队",
+      fleet_title: "移动的水上圣殿",
+      discover_ship: "探索游轮",
+      exp_sub: "游轮体验",
+      exp_title: "沉浸于",
+      exp_title_italic: "非凡时刻",
+      exp_desc: "从您登船的那一刻起,每一个细节都旨在提供舒适、优雅与冒险的完美融合。",
+    },
+    footer: {
+      desc: "重塑长江之旅。奢华与传承的完美交融。",
+      hotline: "24/7 预订热线",
+      privacy: "隐私政策",
+      terms: "条款与条件",
+      cookies: "Cookie 政策",
+      rights: "版权所有。"
+    },
+    shipsPage: {
+      hero_sub: "游轮舰队",
+      hero_desc: "重新定义长江之上的奢华之旅。",
+      now_sailing: "现已启航",
+      coming_soon: "2026 即将启幕",
+      flagship_label: "旗舰荣耀",
+      stats_tons: "17,000 吨",
+      stats_stars: "五星级奢华",
+      stats_ratio: "船员配比",
+      view_itineraries: "查看航线",
+      future_label: "游轮未来式",
+      waitlist_btn: "加入候补名单",
+      intro: {
+        title: "何谓“长江行”?",
+        desc: "长江,这条奔腾不息的巨龙,早已超越地理范畴,深深融入中华民族的文化基因与精神血脉。她是“唯见长江天际流”的壮阔无垠,是“不尽长江滚滚来”的时空浩荡,更是“大江东去,浪淘尽,千古风流人物”的历史回响。“长江行”不止是一次航行,更是一场三重境界的深度体验。",
+        points: [
+          {
+            title: "行·贯古今",
+            desc: "循黄金水道,溯千年文脉。让孤帆远影的诗意、金戈铁马的雄浑、桨声灯影的风华,在船舷两侧流淌成活的史诗。"
+          },
+          {
+            title: "行·见天地",
+            desc: "跨省际壮游,揽山河入怀。夔门之险、荆江九曲、沃野平畴、都市风华...万里气象与地域精粹皆成窗外交响。"
+          },
+          {
+            title: "行·致未来",
+            desc: "以匠心远见,雕时代典范。这是面向未来的智慧航行,一次对至臻体验与永续之美的承诺。"
+          }
+        ]
+      },
+      lanyue: {
+        title: "长江行·揽月",
+        desc: "“长江行·揽月”作为长江行系列游轮首艘旗舰,是目前国内尺寸最大的绿色低碳环保型度假游轮之一,也是三峡旅游集团继“长江三峡1”“西陵峡·和悦”“西陵峡·和谐”“宜昌远影”之后推出的新一代新能源游轮旗舰。"
+      },
+      aurora: {
+        title: "长江行·极光",
+        desc: "2026首航,敬请期待。"
+      }
+    },
+    ships: [
+      {
+        id: "lanyue",
+        name: "长江行·揽月",
+        description: "体验长江之上的奢华巅峰。优雅的避世圣所。",
+        image: "https://picsum.photos/800/600?random=1"
+      },
+      {
+        id: "jiguang",
+        name: "长江行·极光",
+        description: "现代设计与传统待客之道的融合。无与伦比的观景体验。",
+        image: "https://picsum.photos/800/600?random=2"
+      },
+      {
+        id: "star",
+        name: "长江行·星河",
+        description: "树立内河游轮新标杆。私密、尊贵、难忘。",
+        image: "https://picsum.photos/800/600?random=3"
+      }
+    ],
+    itineraries: [
+      {
+        id: "1",
+        title: "三峡探索之旅",
+        days: 4,
+        price: "¥3,999 起",
+        image: "https://picsum.photos/600/400?random=10",
+        route: "重庆 - 宜昌"
+      },
+      {
+        id: "2",
+        title: "长江人文之旅",
+        days: 5,
+        price: "¥4,599 起",
+        image: "https://picsum.photos/600/400?random=11",
+        route: "宜昌 - 重庆"
+      },
+      {
+        id: "3",
+        title: "大江东去长航线",
+        days: 8,
+        price: "¥8,888 起",
+        image: "https://picsum.photos/600/400?random=12",
+        route: "重庆 - 上海"
+      }
+    ],
+    features: [
+      {
+        title: "管家服务",
+        description: "每间套房均配备24小时私人管家服务。",
+        icon: "user"
+      },
+      {
+        title: "米其林餐饮",
+        description: "由世界知名主厨精心策划的菜单。",
+        icon: "utensils"
+      },
+      {
+        title: "文化沉浸",
+        description: "专属岸上观光与船上讲座。",
+        icon: "map"
+      }
+    ]
+  },
+  en: {
+    nav: {
+      book: "BOOK NOW",
+      contact: "Contact",
+      menu: [
+        { title: "HOME", link: "#/" },
+        { 
+          title: "VISTA CRUISES", 
+          link: "#/ships",
+          submenu: [
+            { title: "The Vista Series", link: "#/ships?section=series" },
+            { title: "Vista Lanyue", link: "#/ships?section=lanyue" },
+            { title: "Vista Aurora", link: "#/ships?section=aurora" },
+          ]
+        },
+        { 
+          title: "VOYAGES", 
+          link: "#/itineraries",
+          submenu: [
+            { title: "Itineraries", link: "#itinerary-routes" },
+            { title: "Highlights", link: "#itinerary-highlights" },
+            { title: "Schedules", link: "#itinerary-schedules" },
+          ]
+        },
+        { 
+          title: "EXPERIENCE", 
+          link: "#experience",
+          submenu: [
+            { title: "Service", link: "#exp-service" },
+            { title: "Dining", link: "#exp-dining" },
+            { title: "Activities", link: "#exp-activities" },
+          ]
+        },
+        { 
+          title: "SPACES", 
+          link: "#spaces",
+          submenu: [
+            { title: "Facilities", link: "#space-facilities" },
+            { title: "Suites", link: "#space-rooms" },
+            { title: "Privileges", link: "#space-vip" },
+          ]
+        },
+        { 
+          title: "GUIDE", 
+          link: "#guide",
+          submenu: [
+            { title: "Travel Tips", link: "#guide-tips" },
+            { title: "FAQ", link: "#guide-faq" },
+          ]
+        },
+        { 
+          title: "ABOUT US", 
+          link: "#about",
+          submenu: [
+            { title: "Introduction", link: "#about-intro" },
+            { title: "Media", link: "#about-media" },
+          ]
+        },
+      ]
+    },
+    hero: {
+      welcome: "Welcome to Vista Cruises",
+      title_italic: "Stay &",
+      title_normal: "Journey",
+      subtitle: "Experience the majestic Yangtze River with a level of luxury and service never before imagined. Your sanctuary on the water awaits.",
+      btn_find: "Find a Cruise",
+      btn_ships: "Our Ships"
+    },
+    home: {
+      featured_sub: "Featured Voyages",
+      featured_title: "Curated Itineraries",
+      view_all: "View All Voyages",
+      days: "Days",
+      details: "Details",
+      dining_title: "Culinary Excellence",
+      dining_desc: "Savor the flavors of the region with our Michelin-inspired menus, crafted by world-class chefs using the freshest local ingredients. Every meal is a journey in itself.",
+      dining_btn: "Explore Dining",
+      fleet_sub: "The Fleet",
+      fleet_title: "Our Floating Sanctuaries",
+      discover_ship: "Discover Ship",
+      exp_sub: "The Experience",
+      exp_title: "Immerse Yourself in",
+      exp_title_italic: "The Extraordinary",
+      exp_desc: "From the moment you step onboard, every detail is curated to provide a seamless blend of comfort, elegance, and adventure. Watch our journey unfold.",
+    },
+    footer: {
+      desc: "Reimagining the Yangtze experience. Where luxury meets heritage in perfect harmony.",
+      hotline: "24/7 Reservation Hotline",
+      privacy: "Privacy Policy",
+      terms: "Terms & Conditions",
+      cookies: "Cookie Policy",
+      rights: "Vista Cruises. All Rights Reserved."
+    },
+    shipsPage: {
+      hero_sub: "The Fleet",
+      hero_desc: "Redefining luxury on the Yangtze River.",
+      now_sailing: "Now Sailing",
+      coming_soon: "Coming 2026",
+      flagship_label: "The Flagship",
+      stats_tons: "17,000 Tons",
+      stats_stars: "5-Star Luxury",
+      stats_ratio: "Crew Ratio",
+      view_itineraries: "View Itineraries",
+      future_label: "Future of Cruising",
+      waitlist_btn: "Join Waitlist",
+      intro: {
+        title: "The Vista Philosophy",
+        desc: "The Yangtze, a dragon that never sleeps, transcends geography to become the spiritual bloodline of a nation. Vista Cruises offers not just a journey, but a three-fold experience of depth and discovery.",
+        points: [
+          {
+            title: "History",
+            desc: "Tracing thousands of years of culture along the golden waterway."
+          },
+          {
+            title: "Nature",
+            desc: "Embracing the majestic mountains and rivers, from the dangerous Kuimen to the fertile plains."
+          },
+          {
+            title: "Future",
+            desc: "A commitment to ultimate experience and sustainable beauty with craftsmanship and foresight."
+          }
+        ]
+      },
+      lanyue: {
+        title: "Vista Lanyue",
+        desc: "As the first flagship of the Vista series, Lanyue is one of the largest green, low-carbon vacation cruises in China. It represents the new generation of new energy flagships."
+      },
+      aurora: {
+        title: "Vista Aurora",
+        desc: "Maiden Voyage 2026. Stay tuned."
+      }
+    },
+    ships: [
+      {
+        id: "lanyue",
+        name: "Vista Lanyue",
+        description: "Experience the epitome of luxury on the Yangtze with the Lanyue. A sanctuary of elegance.",
+        image: "https://picsum.photos/800/600?random=1"
+      },
+      {
+        id: "jiguang",
+        name: "Vista Aurora",
+        description: "Modern design meets traditional hospitality. The Jiguang offers an unparalleled viewing experience.",
+        image: "https://picsum.photos/800/600?random=2"
+      },
+      {
+        id: "star",
+        name: "Vista Star",
+        description: "A new standard for river cruising. Intimate, exclusive, and unforgettable.",
+        image: "https://picsum.photos/800/600?random=3"
+      }
+    ],
+    itineraries: [
+      {
+        id: "1",
+        title: "Three Gorges Discovery",
+        days: 4,
+        price: "Fr. ¥3,999",
+        image: "https://picsum.photos/600/400?random=10",
+        route: "Chongqing - Yichang"
+      },
+      {
+        id: "2",
+        title: "Yangtze Heritage",
+        days: 5,
+        price: "Fr. ¥4,599",
+        image: "https://picsum.photos/600/400?random=11",
+        route: "Yichang - Chongqing"
+      },
+      {
+        id: "3",
+        title: "Grand River Voyage",
+        days: 8,
+        price: "Fr. ¥8,888",
+        image: "https://picsum.photos/600/400?random=12",
+        route: "Chongqing - Shanghai"
+      }
+    ],
+    features: [
+      {
+        title: "Butler Service",
+        description: "24-hour personal butler service for every suite.",
+        icon: "user"
+      },
+      {
+        title: "Michelin Dining",
+        description: "Curated menus by world-renowned chefs.",
+        icon: "utensils"
+      },
+      {
+        title: "Cultural Immersion",
+        description: "Exclusive shore excursions and onboard lectures.",
+        icon: "map"
+      }
+    ]
+  }
+};

+ 69 - 0
index.html

@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html lang="zh-CN">
+  <head>
+    <meta charset="UTF-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>长江行游轮 | Vista Cruises</title>
+    <script src="https://cdn.tailwindcss.com"></script>
+    <script>
+      tailwind.config = {
+        theme: {
+          extend: {
+            fontFamily: {
+              serif: ['Times New Roman', 'Times', 'serif'],
+              sans: ['Helvetica Neue', 'Helvetica', 'Arial', 'sans-serif'],
+            },
+            colors: {
+              vista: {
+                black: '#1a1a1a',
+                gold: '#c5a059',
+                goldlight: '#e0c080',
+                gray: '#F9F8F6', 
+                darkblue: '#0a1f35',
+                teal: '#009ca6'
+              }
+            },
+            animation: {
+              'fade-in-up': 'fadeInUp 1s ease-out',
+              'slow-zoom': 'slowZoom 20s linear infinite alternate',
+              'slide-in-right': 'slideInRight 0.3s ease-out'
+            },
+            keyframes: {
+              fadeInUp: {
+                '0%': { opacity: '0', transform: 'translateY(20px)' },
+                '100%': { opacity: '1', transform: 'translateY(0)' },
+              },
+              slowZoom: {
+                '0%': { transform: 'scale(1)' },
+                '100%': { transform: 'scale(1.1)' },
+              },
+              slideInRight: {
+                '0%': { transform: 'translateX(100%)' },
+                '100%': { transform: 'translateX(0)' },
+              }
+            }
+          }
+        }
+      }
+    </script>
+    <style>
+      /* Hide scrollbar for Chrome, Safari and Opera */
+      .no-scrollbar::-webkit-scrollbar {
+          display: none;
+      }
+      /* Hide scrollbar for IE, Edge and Firefox */
+      .no-scrollbar {
+          -ms-overflow-style: none;  /* IE and Edge */
+          scrollbar-width: none;  /* Firefox */
+      }
+      html {
+        scroll-behavior: smooth;
+      }
+    </style>
+
+</head>
+  <body>
+    <div id="root"></div>
+  <script type="module" src="/index.tsx"></script>
+</body>
+</html>

+ 15 - 0
index.tsx

@@ -0,0 +1,15 @@
+import React from 'react';
+import ReactDOM from 'react-dom/client';
+import App from './App';
+
+const rootElement = document.getElementById('root');
+if (!rootElement) {
+  throw new Error("Could not find root element to mount to");
+}
+
+const root = ReactDOM.createRoot(rootElement);
+root.render(
+  <React.StrictMode>
+    <App />
+  </React.StrictMode>
+);

+ 5 - 0
metadata.json

@@ -0,0 +1,5 @@
+{
+  "name": "Vista Cruises (长江行游轮)",
+  "description": "A luxury river cruise web application featuring itinerary booking, ship details, and travel guides, designed with a high-end aesthetic similar to Crystal Cruises.",
+  "requestFramePermissions": []
+}

File diff suppressed because it is too large
+ 2234 - 0
package-lock.json


+ 26 - 0
package.json

@@ -0,0 +1,26 @@
+{
+  "name": "vista-cruises-(长江行游轮)",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@types/qs": "^6.14.0",
+    "axios": "^1.13.2",
+    "lucide-react": "0.292.0",
+    "qs": "^6.14.0",
+    "react": "18.2.0",
+    "react-dom": "18.2.0",
+    "react-router-dom": "6.28.0"
+  },
+  "devDependencies": {
+    "@types/node": "^22.14.0",
+    "@vitejs/plugin-react": "^5.0.0",
+    "typescript": "~5.8.2",
+    "vite": "^6.2.0"
+  }
+}

+ 67 - 0
src/api/index.ts

@@ -0,0 +1,67 @@
+import request from '../config';
+
+export interface ArticleType {
+  id: string;
+  name: string;
+  description?: string;
+  visible?: string;
+}
+
+export interface Article {
+  id: string;
+  title: string;
+  content?: string;
+  image?: string;
+  articleType?: string;
+  publishTime?: string;
+  viewCount?: number;
+}
+
+export interface ArticlePageParams {
+  pageNum?: number;
+  pageSize?: number;
+  articleType?: string;
+  visible?: string;
+  title?: string;
+}
+
+export interface ArticlePageResult {
+  records: Article[];
+  total: number;
+  pageNum: number;
+  pageSize: number;
+}
+
+export const Api = {
+  /**
+   * 加载文章类型列表
+   */
+  loadTypeList: async (params?: any) => {
+    const visible = params?.visible;
+    if (!visible) {
+      params = { ...params, visible: '0' };
+    }
+    return await request.get<ArticleType[]>({ url: `/cms/article-type/portal/list`, params });
+  },
+
+  /**
+   * 加载文章分页列表
+   */
+  loadList: async (params?: ArticlePageParams) => {
+    return await request.get<ArticlePageResult>({ url: `/cms/article/portal/page-list`, params });
+  },
+
+  /**
+   * 加载文章详情
+   */
+  load: async (params: { id: string }) => {
+    return await request.get<Article>({ url: `/cms/article/portal/load`, params });
+  },
+
+  /**
+   * 加载后台文章分页列表(支持权限控制、多条件筛选)
+   */
+  getArticlePage: async (params?: ArticlePageParams) => {
+    return await request.get<ArticlePageResult>({ url: `/cms/article/page`, params });
+  }
+};

+ 75 - 0
src/components/Footer.tsx

@@ -0,0 +1,75 @@
+import React from 'react';
+import { CONTENT, PHONE_NUMBER } from '../constants.ts';
+import { Instagram, Facebook, Twitter, Youtube } from 'lucide-react';
+import VistaLogo from './VistaLogo.tsx';
+import { useLanguage } from '../contexts/LanguageContext.tsx';
+import { useTheme } from '../contexts/ThemeContext.tsx';
+
+const Footer: React.FC = () => {
+  const { language } = useLanguage();
+  const { customFooterLogo } = useTheme();
+  const t = CONTENT[language];
+
+  return (
+    <footer className="bg-white text-vista-darkblue pt-16 pb-12 border-t border-vista-darkblue/5">
+      <div className="max-w-7xl mx-auto px-6">
+        
+        {/* Top Section: Flex Layout for tighter control over spacing */}
+        <div className="flex flex-col lg:flex-row flex-wrap justify-center items-start gap-10 lg:gap-16 mb-12">
+            
+            {/* Logo Column */}
+            <div className="w-full lg:w-auto flex flex-col items-center lg:items-start text-center lg:text-left">
+                <div className="mb-6">
+                   <VistaLogo color="#0a1f35" src={customFooterLogo} />
+                </div>
+                {/* Description with whitespace-nowrap to prevent wrapping as requested */}
+                <p className="text-vista-darkblue/80 text-sm leading-relaxed mb-6 font-medium whitespace-nowrap">
+                    {t.footer.desc}
+                </p>
+                <div className="flex space-x-4 justify-center lg:justify-start">
+                    <Instagram size={20} className="text-vista-darkblue/80 hover:text-vista-gold cursor-pointer transition-colors" />
+                    <Facebook size={20} className="text-vista-darkblue/80 hover:text-vista-gold cursor-pointer transition-colors" />
+                    <Twitter size={20} className="text-vista-darkblue/80 hover:text-vista-gold cursor-pointer transition-colors" />
+                    <Youtube size={20} className="text-vista-darkblue/80 hover:text-vista-gold cursor-pointer transition-colors" />
+                </div>
+            </div>
+
+            {/* Menu Columns - Flex items */}
+            {t.nav.menu.slice(1, 5).map((item) => (
+                <div key={item.title} className="w-1/2 md:w-auto flex flex-col items-center min-w-[100px]">
+                    <h4 className="font-serif text-lg font-bold mb-4 border-b border-vista-darkblue/10 pb-2 inline-block text-center whitespace-nowrap">{item.title}</h4>
+                    <ul className="space-y-2 text-center">
+                        {item.submenu?.map((sub) => (
+                            <li key={sub.title}>
+                                <a href={sub.link} className="text-sm text-vista-darkblue/80 hover:text-vista-gold transition-colors font-medium whitespace-nowrap">
+                                    {sub.title}
+                                </a>
+                            </li>
+                        ))}
+                    </ul>
+                </div>
+            ))}
+        </div>
+
+        {/* Contact Strip */}
+        <div className="border-t border-vista-darkblue/10 pt-8 flex flex-col md:flex-row justify-between items-center">
+             <div className="mb-6 md:mb-0 text-center md:text-left">
+                 <p className="text-xs tracking-widest text-vista-darkblue/70 uppercase mb-2 font-bold">{t.footer.hotline}</p>
+                 <a href={`tel:${PHONE_NUMBER}`} className="text-3xl md:text-4xl font-serif font-bold text-vista-darkblue hover:text-vista-gold transition-colors">
+                     {PHONE_NUMBER}
+                 </a>
+             </div>
+             
+             <div className="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-8 text-xs text-vista-darkblue/60 font-medium items-center">
+                 <a href="#" className="hover:text-vista-darkblue transition-colors">{t.footer.privacy}</a>
+                 <a href="#" className="hover:text-vista-darkblue transition-colors">{t.footer.terms}</a>
+                 <a href="#" className="hover:text-vista-darkblue transition-colors">{t.footer.cookies}</a>
+                 <span>© {new Date().getFullYear()} {t.footer.rights}</span>
+             </div>
+        </div>
+      </div>
+    </footer>
+  );
+};
+
+export default Footer;

+ 333 - 0
src/components/Navbar.tsx

@@ -0,0 +1,333 @@
+import React, { useState, useEffect, useRef } from 'react';
+import { CONTENT } from '../constants.ts';
+import { Menu, X, ChevronDown, Search } from 'lucide-react';
+import VistaLogo from './VistaLogo.tsx';
+import { useLanguage } from '../contexts/LanguageContext.tsx';
+import { Link, useLocation, useNavigate } from 'react-router-dom';
+import SearchOverlay from './SearchOverlay.tsx';
+
+const Navbar: React.FC = () => {
+  const [scrolled, setScrolled] = useState(false);
+  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
+  const [activeMenuIndex, setActiveMenuIndex] = useState<number | null>(null);
+  const [searchOpen, setSearchOpen] = useState(false);
+  const menuRef = useRef<HTMLDivElement>(null);
+  
+  const { language, toggleLanguage } = useLanguage();
+  const location = useLocation();
+  const navigate = useNavigate();
+  
+  const t = CONTENT[language].nav;
+
+  // Handle Scroll Effect
+  useEffect(() => {
+    const handleScroll = () => {
+      const isScrolled = window.scrollY > 50;
+      setScrolled(isScrolled);
+      if (isScrolled) {
+        setActiveMenuIndex(null);
+      }
+    };
+    window.addEventListener('scroll', handleScroll);
+    return () => window.removeEventListener('scroll', handleScroll);
+  }, []);
+
+  // Handle Click Outside
+  useEffect(() => {
+    const handleClickOutside = (event: MouseEvent) => {
+      if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
+        setActiveMenuIndex(null);
+      }
+    };
+
+    document.addEventListener('mousedown', handleClickOutside);
+    return () => {
+      document.removeEventListener('mousedown', handleClickOutside);
+    };
+  }, []);
+
+  // Prevent Body Scroll when Mobile Menu Open
+  useEffect(() => {
+    if (mobileMenuOpen) {
+      document.body.style.overflow = 'hidden';
+    } else {
+      document.body.style.overflow = 'auto';
+    }
+  }, [mobileMenuOpen]);
+
+  // Handle Navigation Click (Smart Routing)
+  const handleNavClick = (e: React.MouseEvent, link: string) => {
+    if (link.startsWith('#') && !link.startsWith('#/')) {
+        e.preventDefault();
+        const anchorId = link.substring(1);
+
+        if (location.pathname !== '/') {
+            navigate('/');
+            setTimeout(() => {
+                const el = document.getElementById(anchorId);
+                if (el) el.scrollIntoView({ behavior: 'smooth' });
+            }, 100);
+        } else {
+            const el = document.getElementById(anchorId);
+            if (el) el.scrollIntoView({ behavior: 'smooth' });
+        }
+        setActiveMenuIndex(null);
+    } 
+    else if (link.startsWith('#/')) {
+       setActiveMenuIndex(null);
+    }
+  };
+
+  const getLinkTo = (link: string) => {
+      if (link.startsWith('#/')) return link.substring(1);
+      if (link.startsWith('#')) return '/';
+      return link;
+  };
+
+  return (
+    <>
+      <nav 
+        className={`fixed w-full z-50 transition-all duration-500 ease-in-out ${
+          scrolled 
+            ? 'bg-white shadow-md py-3' 
+            : 'bg-gradient-to-b from-black/60 to-transparent py-6'
+        }`}
+      >
+        <div className="max-w-7xl mx-auto px-6 flex justify-between items-center relative">
+          {/* Logo Area */}
+          <div onClick={() => { navigate('/'); setActiveMenuIndex(null); }} className="cursor-pointer z-50 relative">
+             <VistaLogo color={scrolled ? '#c5a059' : '#ffffff'} />
+          </div>
+
+          {/* Desktop Menu */}
+          <div className="hidden lg:flex space-x-8 items-center h-full" ref={menuRef}>
+            {t.menu.map((item, index) => (
+              <div 
+                key={index} 
+                className="relative group h-full flex items-center py-4"
+                onMouseEnter={() => item.submenu && setActiveMenuIndex(index)}
+                onMouseLeave={() => setActiveMenuIndex(null)}
+              >
+                {/* Main Menu Item */}
+                {item.link.startsWith('#/') ? (
+                   <Link 
+                      to={getLinkTo(item.link)}
+                      className={`text-sm font-bold uppercase tracking-widest pb-1 border-b-2 cursor-pointer flex items-center gap-1 transition-colors ${
+                        scrolled 
+                          ? 'text-vista-darkblue hover:text-vista-gold' 
+                          : 'text-white hover:text-vista-gold'
+                      } ${
+                        activeMenuIndex === index 
+                        ? 'text-vista-gold border-vista-gold' 
+                        : 'border-transparent'
+                      }`}
+                   >
+                     {item.title}
+                     {item.submenu && <ChevronDown size={14} className={`transition-transform duration-300 ${activeMenuIndex === index ? 'rotate-180' : ''}`} />}
+                   </Link>
+                ) : (
+                   <a 
+                    href={item.link}
+                    onClick={(e) => handleNavClick(e, item.link)}
+                    className={`text-sm font-bold uppercase tracking-widest pb-1 border-b-2 cursor-pointer flex items-center gap-1 transition-colors ${
+                        scrolled 
+                          ? 'text-vista-darkblue hover:text-vista-gold' 
+                          : 'text-white hover:text-vista-gold'
+                      } ${
+                        activeMenuIndex === index 
+                        ? 'text-vista-gold border-vista-gold' 
+                        : 'border-transparent'
+                    }`}
+                  >
+                    {item.title}
+                    {item.submenu && <ChevronDown size={14} className={`transition-transform duration-300 ${activeMenuIndex === index ? 'rotate-180' : ''}`} />}
+                  </a>
+                )}
+                
+                {/* Desktop Dropdown */}
+                {item.submenu && (
+                  <div className={`absolute left-1/2 -translate-x-1/2 top-full w-56 pt-2 transition-all duration-300 transform origin-top ${
+                      activeMenuIndex === index 
+                      ? 'opacity-100 visible translate-y-0' 
+                      : 'opacity-0 invisible -translate-y-2 pointer-events-none'
+                  }`}>
+                    <div className="bg-white shadow-xl rounded-sm border-t-2 border-vista-gold">
+                      <div className="py-2">
+                        {item.submenu.map((sub, subIndex) => (
+                          <React.Fragment key={subIndex}>
+                           {sub.link.startsWith('#/') ? (
+                               <Link
+                                 to={getLinkTo(sub.link)}
+                                 onClick={() => setActiveMenuIndex(null)}
+                                 className="block px-6 py-3 text-sm text-vista-darkblue/80 hover:bg-vista-darkblue/5 hover:text-vista-gold font-sans transition-colors border-l-2 border-transparent hover:border-vista-gold"
+                               >
+                                 {sub.title}
+                               </Link>
+                           ) : (
+                               <a 
+                                 href={sub.link}
+                                 onClick={(e) => handleNavClick(e, sub.link)}
+                                 className="block px-6 py-3 text-sm text-vista-darkblue/80 hover:bg-vista-darkblue/5 hover:text-vista-gold font-sans transition-colors border-l-2 border-transparent hover:border-vista-gold"
+                               >
+                                 {sub.title}
+                               </a>
+                           )}
+                          </React.Fragment>
+                        ))}
+                      </div>
+                    </div>
+                  </div>
+                )}
+              </div>
+            ))}
+          </div>
+
+          {/* Right Action Area (Desktop) */}
+          <div className="hidden lg:flex items-center space-x-6">
+               <button 
+                  onClick={() => setSearchOpen(true)} 
+                  className={`transition-colors hover:text-vista-gold ${scrolled ? 'text-vista-darkblue' : 'text-white'}`}
+                  aria-label="Search"
+               >
+                  <Search size={20} />
+               </button>
+
+               <button 
+                  onClick={toggleLanguage} 
+                  className={`text-xs font-bold uppercase tracking-widest transition-colors ${scrolled ? 'text-vista-darkblue hover:text-vista-gold' : 'text-white hover:text-vista-gold'}`}
+               >
+                  {language === 'zh' ? 'EN' : '中文'}
+               </button>
+
+               <a 
+                  href="#book"
+                  onClick={(e) => handleNavClick(e, '#book')}
+                  className={`px-6 py-2 border transition-all duration-300 text-xs font-bold tracking-widest uppercase ${
+                   scrolled 
+                   ? 'border-vista-darkblue text-vista-darkblue hover:bg-vista-darkblue hover:text-white' 
+                   : 'border-white text-white hover:bg-white hover:text-vista-darkblue'
+               }`}>
+                  {t.book}
+               </a>
+          </div>
+
+          {/* Mobile Toggle Button */}
+          <div className="lg:hidden flex items-center space-x-4 z-50">
+             <button 
+                  onClick={() => setSearchOpen(true)} 
+                  className={`transition-colors hover:text-vista-gold ${scrolled ? 'text-vista-darkblue' : 'text-white'}`}
+                  aria-label="Search"
+               >
+                  <Search size={22} />
+               </button>
+
+            <button 
+                  onClick={toggleLanguage} 
+                  className={`text-xs font-bold uppercase tracking-widest ${scrolled ? 'text-vista-darkblue' : 'text-white'}`}
+            >
+               {language === 'zh' ? 'EN' : 'CN'}
+            </button>
+            <button 
+              onClick={() => setMobileMenuOpen(true)} 
+              className={`transition-colors ${scrolled ? 'text-vista-darkblue' : 'text-white'}`}
+            >
+              <Menu size={28} />
+            </button>
+          </div>
+        </div>
+      </nav>
+
+      {/* Global Search Overlay */}
+      <SearchOverlay isOpen={searchOpen} onClose={() => setSearchOpen(false)} />
+
+      {/* Mobile Menu Backdrop */}
+      <div 
+         className={`fixed inset-0 z-[60] bg-black/50 backdrop-blur-sm transition-opacity duration-500 lg:hidden ${mobileMenuOpen ? 'opacity-100 visible' : 'opacity-0 invisible'}`}
+         onClick={() => setMobileMenuOpen(false)}
+      ></div>
+
+      {/* Mobile Menu Drawer - Side Slide-In (Half Width) */}
+      <div 
+        className={`fixed top-0 right-0 z-[70] h-full w-1/2 bg-vista-darkblue shadow-2xl transition-transform duration-500 transform lg:hidden flex flex-col ${
+            mobileMenuOpen ? 'translate-x-0' : 'translate-x-full'
+        }`}
+      >
+           <div className="flex justify-between items-center p-6 border-b border-white/10">
+              <span className="text-white font-serif text-lg tracking-wider">MENU</span>
+              <button 
+                onClick={() => setMobileMenuOpen(false)}
+                className="text-white hover:text-vista-gold transition-colors"
+              >
+                  <X size={28} />
+              </button>
+           </div>
+
+           <div className="flex-1 overflow-y-auto p-6 flex flex-col space-y-6">
+              {t.menu.map((item, index) => (
+                <div key={index}>
+                    {item.link.startsWith('#/') ? (
+                         <Link 
+                            to={getLinkTo(item.link)}
+                            className="text-xl font-serif text-white block hover:text-vista-gold transition-colors mb-2"
+                            onClick={() => setMobileMenuOpen(false)}
+                        >
+                            {item.title}
+                        </Link>
+                    ) : (
+                        <a 
+                            href={item.link} 
+                            onClick={(e) => { handleNavClick(e, item.link); setMobileMenuOpen(false); }}
+                            className="text-xl font-serif text-white block hover:text-vista-gold transition-colors mb-2"
+                        >
+                            {item.title}
+                        </a>
+                    )}
+                   
+                    {/* Submenu */}
+                    {item.submenu && (
+                        <div className="pl-4 border-l border-white/20 space-y-3 mt-2">
+                             {item.submenu.map((sub, idx) => (
+                                 <React.Fragment key={idx}>
+                                     {sub.link.startsWith('#/') ? (
+                                         <Link 
+                                            to={getLinkTo(sub.link)}
+                                            className="block text-sm text-white/60 hover:text-white transition-colors"
+                                            onClick={() => setMobileMenuOpen(false)}
+                                         >
+                                             {sub.title}
+                                         </Link>
+                                     ) : (
+                                          <a 
+                                            href={sub.link}
+                                            onClick={(e) => { handleNavClick(e, sub.link); setMobileMenuOpen(false); }}
+                                            className="block text-sm text-white/60 hover:text-white transition-colors"
+                                         >
+                                             {sub.title}
+                                         </a>
+                                     )}
+                                 </React.Fragment>
+                             ))}
+                        </div>
+                    )}
+                </div>
+              ))}
+           </div>
+           
+           <div className="p-6 border-t border-white/10 bg-vista-darkblue">
+                <a 
+                    href="#book" 
+                    onClick={(e) => { handleNavClick(e, '#book'); setMobileMenuOpen(false); }} 
+                    className="block w-full py-4 bg-vista-gold text-white font-bold uppercase tracking-widest text-sm text-center hover:bg-white hover:text-vista-darkblue transition-colors mb-4"
+                >
+                  {t.book}
+                </a>
+                <button onClick={() => setMobileMenuOpen(false)} className="block w-full text-white/60 text-sm hover:text-white transition-colors text-center">
+                  {t.contact}
+                </button>
+           </div>
+      </div>
+    </>
+  );
+};
+
+export default Navbar;

+ 254 - 0
src/components/SearchOverlay.tsx

@@ -0,0 +1,254 @@
+import React, { useState, useMemo, useEffect, useRef } from 'react';
+import { Search, X, ArrowRight, Compass, Ship, Utensils, Anchor, Film } from 'lucide-react';
+import { useLanguage } from '../contexts/LanguageContext.tsx';
+import { useTheme } from '../contexts/ThemeContext.tsx';
+import { CONTENT } from '../constants.ts';
+import { useNavigate } from 'react-router-dom';
+
+interface SearchOverlayProps {
+  isOpen: boolean;
+  onClose: () => void;
+}
+
+interface SearchResult {
+  title: string;
+  subtitle?: string;
+  category: 'home' | 'itinerary' | 'ships' | 'dining' | 'activity';
+  link: string;
+  matchType: 'title' | 'content';
+}
+
+const SearchOverlay: React.FC<SearchOverlayProps> = ({ isOpen, onClose }) => {
+  const { language } = useLanguage();
+  const { itineraries, ships, dining, videoSection } = useTheme();
+  const [query, setQuery] = useState('');
+  const inputRef = useRef<HTMLInputElement>(null);
+  const navigate = useNavigate();
+
+  const t = CONTENT[language];
+  const searchT = (CONTENT[language] as any).search || {
+    placeholder: "Search...",
+    no_results: "No results",
+    results_for: "Results: ",
+    categories: { home: "Home", ships: "Ships", itinerary: "Trips", dining: "Food", activity: "Fun" }
+  };
+
+  // Focus input when opened
+  useEffect(() => {
+    if (isOpen) {
+      setTimeout(() => {
+        inputRef.current?.focus();
+      }, 100);
+      // Removed body scroll lock to keep it feeling lightweight
+    } 
+  }, [isOpen]);
+
+  // Handle ESC key
+  useEffect(() => {
+    const handleEsc = (e: KeyboardEvent) => {
+      if (e.key === 'Escape') onClose();
+    };
+    window.addEventListener('keydown', handleEsc);
+    return () => window.removeEventListener('keydown', handleEsc);
+  }, [onClose]);
+
+  // Build Search Index
+  const searchIndex: SearchResult[] = useMemo(() => {
+    const index: SearchResult[] = [];
+
+    // 1. Home - Hero (Text on Image)
+    index.push({
+      title: t.hero.title_normal + ' ' + t.hero.title_italic,
+      subtitle: t.hero.subtitle,
+      category: 'home',
+      link: '/',
+      matchType: 'title'
+    });
+
+    // 2. Dynamic Itineraries (Cards / Images)
+    itineraries.forEach(item => {
+      index.push({
+        title: item.title,
+        subtitle: `${item.route} - ${item.price}`,
+        category: 'itinerary',
+        link: '/?section=featured-itineraries',
+        matchType: 'title'
+      });
+    });
+
+    // 3. Dynamic Ships (Cards / Images)
+    ships.forEach(item => {
+      index.push({
+        title: item.name,
+        subtitle: item.description,
+        category: 'ships',
+        link: '/?section=our-fleet',
+        matchType: 'title'
+      });
+    });
+
+    // 4. Dynamic Dining (Banner Image)
+    index.push({
+      title: dining.title,
+      subtitle: dining.description,
+      category: 'dining',
+      link: '/?section=dining-experience',
+      matchType: 'title'
+    });
+
+    // 5. Dynamic Video/Activity (Thumbnail)
+    index.push({
+      title: videoSection.title + ' ' + videoSection.titleItalic,
+      subtitle: videoSection.description,
+      category: 'activity',
+      link: '/?section=video-experience',
+      matchType: 'title'
+    });
+
+    // 6. Ships Page Content (Text on Images: Hero & Cards)
+    const shipPage = (t as any).shipsPage;
+    if (shipPage) {
+        // Hero
+        index.push({
+            title: shipPage.hero_sub, 
+            subtitle: shipPage.hero_desc,
+            category: 'ships',
+            link: '/ships',
+            matchType: 'title'
+        });
+        // Lanyue Image Card
+        index.push({
+            title: shipPage.lanyue.title,
+            subtitle: shipPage.lanyue.desc,
+            category: 'ships',
+            link: '/ships?section=lanyue',
+            matchType: 'title'
+        });
+        // Aurora Image Card
+        index.push({
+            title: shipPage.aurora.title,
+            subtitle: shipPage.aurora.desc,
+            category: 'ships',
+            link: '/ships?section=aurora',
+            matchType: 'title'
+        });
+        // Intro Text
+        index.push({
+            title: shipPage.intro.title,
+            subtitle: shipPage.intro.desc,
+            category: 'ships',
+            link: '/ships?section=series',
+            matchType: 'content'
+        });
+    }
+
+    return index;
+  }, [language, t, itineraries, ships, dining, videoSection]);
+
+  // Filter Results
+  const filteredResults = useMemo(() => {
+    if (!query.trim()) return [];
+    const lowerQuery = query.toLowerCase();
+    return searchIndex.filter(item => 
+      item.title.toLowerCase().includes(lowerQuery) || 
+      (item.subtitle && item.subtitle.toLowerCase().includes(lowerQuery))
+    );
+  }, [query, searchIndex]);
+
+  const handleResultClick = (link: string) => {
+    onClose();
+    if (link.startsWith('/?section=')) {
+        const sectionId = link.split('=')[1];
+        navigate('/');
+        setTimeout(() => {
+            document.getElementById(sectionId)?.scrollIntoView({ behavior: 'smooth' });
+        }, 100);
+    } else if (link.startsWith('/ships?section=')) {
+        navigate(link);
+    } else {
+        navigate(link);
+    }
+  };
+
+  if (!isOpen) return null;
+
+  return (
+    // Outer Wrapper: Fully transparent, used for positioning and click-outside detection
+    <div 
+        className="fixed inset-0 z-[100] flex justify-center items-start pt-[15vh]"
+        onClick={onClose}
+    >
+      {/* 
+        Inner Container: 
+        - Compact floating bar (capsule/rounded rect style)
+        - Background: Semi-transparent dark glass (bg-black/60) to ensure readability
+        - Dimensions: Tightly wrapped around text input (w-auto, min-width)
+      */}
+      <div 
+        className="relative w-auto min-w-[300px] md:min-w-[500px] max-w-2xl mx-4 bg-black/60 backdrop-blur-md rounded border border-white/10 shadow-2xl animate-fade-in-up px-4 py-2 flex flex-col" 
+        onClick={(e) => e.stopPropagation()}
+      >
+        <div className="flex items-center w-full">
+             <Search className="text-white/60 mr-3 shrink-0" size={18} />
+             <input 
+                ref={inputRef}
+                type="text" 
+                value={query}
+                onChange={(e) => setQuery(e.target.value)}
+                placeholder={searchT.placeholder}
+                className="flex-1 bg-transparent border-none text-xl font-serif italic text-white placeholder-white/40 focus:outline-none focus:ring-0 transition-colors tracking-wide h-10 px-0"
+            />
+             <button 
+                onClick={onClose} 
+                className="ml-3 text-white/50 hover:text-white transition-colors shrink-0"
+            >
+                <X size={18} />
+            </button>
+        </div>
+
+        {/* Results Dropdown - Floating immediately below the bar */}
+        {(query.trim() !== '') && (
+            <div className="absolute top-full left-0 right-0 mt-2 bg-vista-darkblue/95 backdrop-blur-xl rounded border border-white/10 max-h-[60vh] overflow-y-auto no-scrollbar shadow-2xl">
+                 {filteredResults.length > 0 ? (
+                    <div className="p-2">
+                        <div className="px-4 py-2 text-vista-gold text-[10px] font-bold uppercase tracking-widest border-b border-white/5">
+                            {searchT.results_for} "{query}"
+                        </div>
+                        {filteredResults.map((result, idx) => (
+                            <div 
+                                key={idx} 
+                                onClick={() => handleResultClick(result.link)}
+                                className="group flex items-center justify-between p-3 hover:bg-white/10 cursor-pointer transition-colors rounded-sm"
+                            >
+                                <div className="flex items-center gap-3">
+                                    <div className="text-white/40 group-hover:text-vista-gold transition-colors">
+                                        {result.category === 'itinerary' && <Compass size={16} />}
+                                        {result.category === 'ships' && <Ship size={16} />}
+                                        {result.category === 'dining' && <Utensils size={16} />}
+                                        {result.category === 'activity' && <Film size={16} />}
+                                        {result.category === 'home' && <Anchor size={16} />}
+                                    </div>
+                                    <div>
+                                        <h3 className="text-base font-serif text-white group-hover:text-vista-gold transition-colors">{result.title}</h3>
+                                        {result.subtitle && (
+                                            <p className="text-white/50 text-[10px] mt-0.5 line-clamp-1">{result.subtitle}</p>
+                                        )}
+                                    </div>
+                                </div>
+                                <ArrowRight className="text-white/20 group-hover:text-vista-gold w-3 h-3" />
+                            </div>
+                        ))}
+                    </div>
+                 ) : (
+                    <div className="p-6 text-center text-white/40 font-serif italic text-base">
+                        {searchT.no_results}
+                    </div>
+                 )}
+            </div>
+        )}
+      </div>
+    </div>
+  );
+};
+
+export default SearchOverlay;

+ 84 - 0
src/components/Sidebar.tsx

@@ -0,0 +1,84 @@
+import React, { useState } from 'react';
+import { Phone, MessageCircle, ArrowUp, Send, Settings } from 'lucide-react';
+import ThemeSettings from './ThemeSettings.tsx';
+import MiniProgramQRCode from '../images/Socialmedia/gzh.png';
+
+const Sidebar: React.FC = () => {
+  const [settingsOpen, setSettingsOpen] = useState(false);
+
+  const scrollToTop = () => {
+    window.scrollTo({ top: 0, behavior: 'smooth' });
+  };
+
+  return (
+    <>
+      <div className="fixed right-0 top-1/2 -translate-y-1/2 z-50 flex flex-col items-end space-y-4 pr-4">
+        {/* Customer Service */}
+        <div className="group relative flex items-center">
+          <div className="absolute right-12 bg-vista-darkblue text-white text-xs px-3 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none">
+            24*7 预定电话:400-696-0666
+          </div>
+          <button className="w-12 h-12 bg-white rounded-full shadow-lg flex items-center justify-center text-vista-darkblue hover:bg-vista-gold hover:text-white transition-colors border border-vista-darkblue/10">
+            <Phone size={20} />
+          </button>
+        </div>
+
+        {/* WeChat */}
+        <div className="group relative flex items-center">
+          <div className="absolute right-12 bg-white p-2 shadow-lg rounded opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 z-40">
+              {/* Placeholder for QR Code */}
+            <div
+                className="w-24 h-24 bg-vista-darkblue/5 flex items-center justify-center text-xs text-vista-darkblue/60">
+              公众号二维码
+              <img
+                  src="../images/Socialmedia/gzh.png"  // 修改为你的图片路径
+                  alt="公众号二维码"
+                  className="w-24 h-24 object-cover rounded"
+              />
+            </div>
+          </div>
+          <button
+              className="w-12 h-12 bg-white rounded-full shadow-lg flex items-center justify-center text-vista-darkblue hover:bg-vista-gold hover:text-white transition-colors border border-vista-darkblue/10">
+            <MessageCircle size={20} />
+          </button>
+        </div>
+
+        {/* Mini Program */}
+        <div className="group relative flex items-center">
+          <div className="absolute right-12 bg-white p-2 shadow-lg rounded opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-300 z-40">
+              <img src={MiniProgramQRCode} alt="小程序二维码" className="w-24 h-24 object-contain" />
+          </div>
+          <button className="w-12 h-12 bg-white rounded-full shadow-lg flex items-center justify-center text-vista-darkblue hover:bg-vista-gold hover:text-white transition-colors border border-vista-darkblue/10">
+            <Send size={20} />
+          </button>
+        </div>
+
+        {/* Customization Settings Trigger (New) */}
+        <div className="group relative flex items-center">
+           <div className="absolute right-12 bg-vista-darkblue text-white text-xs px-3 py-1 rounded opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap pointer-events-none">
+            自定义设置
+          </div>
+          <button 
+            onClick={() => setSettingsOpen(true)}
+            className="w-12 h-12 bg-white rounded-full shadow-lg flex items-center justify-center text-vista-darkblue hover:bg-vista-gold hover:text-white transition-colors border border-vista-darkblue/10"
+          >
+            <Settings size={20} />
+          </button>
+        </div>
+
+        {/* Back to Top */}
+        <button 
+          onClick={scrollToTop}
+          className="w-12 h-12 bg-vista-darkblue text-white rounded-full shadow-lg flex items-center justify-center hover:bg-vista-gold transition-colors"
+        >
+          <ArrowUp size={20} />
+        </button>
+      </div>
+
+      {/* Settings Modal */}
+      <ThemeSettings isOpen={settingsOpen} onClose={() => setSettingsOpen(false)} />
+    </>
+  );
+};
+
+export default Sidebar;

+ 381 - 0
src/components/ThemeSettings.tsx

@@ -0,0 +1,381 @@
+
+import React, { useRef } from 'react';
+import { X, Upload, RotateCcw, Image as ImageIcon, Layout, Film, Ship, Utensils, Video, Anchor } from 'lucide-react';
+import { useTheme } from '../contexts/ThemeContext.tsx';
+import { Itinerary, CruiseShip } from '../types.ts';
+
+interface ThemeSettingsProps {
+  isOpen: boolean;
+  onClose: () => void;
+}
+
+const ThemeSettings: React.FC<ThemeSettingsProps> = ({ isOpen, onClose }) => {
+  const { 
+    customLogo, customFooterLogo, heroImages, 
+    setCustomLogo, setCustomFooterLogo, setHeroImages, 
+    itineraries, setItineraries,
+    dining, setDining,
+    ships, setShips,
+    videoSection, setVideoSection,
+    shipsPageImages, setShipsPageImages,
+    resetTheme 
+  } = useTheme();
+
+  const logoInputRef = useRef<HTMLInputElement>(null);
+  const footerLogoInputRef = useRef<HTMLInputElement>(null);
+  const heroInputRef = useRef<HTMLInputElement>(null);
+  const diningImgRef = useRef<HTMLInputElement>(null);
+  const videoThumbRef = useRef<HTMLInputElement>(null);
+  const auroraImgRef = useRef<HTMLInputElement>(null);
+
+  // Helper to update specific item in an array
+  const updateItinerary = (index: number, field: keyof Itinerary, value: any) => {
+    const newItems = [...itineraries];
+    newItems[index] = { ...newItems[index], [field]: value };
+    setItineraries(newItems);
+  };
+
+  const updateShip = (index: number, field: keyof CruiseShip, value: any) => {
+    const newItems = [...ships];
+    newItems[index] = { ...newItems[index], [field]: value };
+    setShips(newItems);
+  };
+
+  // Helper for single image upload
+  const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>, callback: (url: string) => void) => {
+    const file = e.target.files?.[0];
+    if (file) {
+      const url = URL.createObjectURL(file);
+      callback(url);
+    }
+  };
+
+  // Helper for array image/video upload
+  const handleArrayMediaUpload = (e: React.ChangeEvent<HTMLInputElement>, index: number, type: 'itinerary_img' | 'itinerary_video' | 'ship' | 'lanyue') => {
+    const file = e.target.files?.[0];
+    if (file) {
+      const url = URL.createObjectURL(file);
+      if (type === 'itinerary_img') updateItinerary(index, 'image', url);
+      else if (type === 'itinerary_video') updateItinerary(index, 'video', url);
+      else if (type === 'ship') updateShip(index, 'image', url);
+      else if (type === 'lanyue') {
+         const newImages = [...shipsPageImages.lanyue];
+         newImages[index] = url;
+         setShipsPageImages({...shipsPageImages, lanyue: newImages});
+      }
+    }
+  };
+
+  if (!isOpen) return null;
+
+  return (
+    <div className="fixed inset-0 z-[100] bg-black/50 backdrop-blur-sm flex justify-end">
+      {/* 
+        Width optimization:
+        w-full max-w-[340px]: Ensures it takes full width only on very small screens (up to 340px),
+        but stays as a 340px sidebar on larger screens, preventing full-screen coverage.
+      */}
+      <div className="w-full max-w-[340px] bg-white h-full shadow-2xl p-6 overflow-y-auto animate-slide-in-right">
+        <div className="flex justify-between items-center mb-6 border-b border-vista-darkblue/10 pb-4">
+          <h2 className="text-xl font-serif text-vista-darkblue">自定义内容配置</h2>
+          <button onClick={onClose} className="text-vista-darkblue/40 hover:text-vista-darkblue transition-colors">
+            <X size={24} />
+          </button>
+        </div>
+
+        <div className="space-y-4 pb-20">
+          
+          {/* Section 1: Branding & Hero */}
+          <details className="group border border-vista-darkblue/10 rounded-lg overflow-hidden">
+            <summary className="flex items-center gap-2 p-4 bg-vista-darkblue/5 cursor-pointer font-bold text-vista-darkblue text-sm select-none">
+              <ImageIcon size={16} /> 品牌与轮播图
+            </summary>
+            <div className="p-4 space-y-6 bg-white border-t border-vista-darkblue/10">
+              {/* Main Logo */}
+              <div>
+                <label className="block text-xs text-vista-darkblue/60 mb-2 font-bold">网站 Logo (顶部导航)</label>
+                <div className="flex items-center gap-4">
+                  {customLogo ? (
+                    <img src={customLogo} className="h-10 w-auto object-contain border p-1" alt="Logo" />
+                  ) : <span className="text-xs text-gray-400">默认</span>}
+                  <button onClick={() => logoInputRef.current?.click()} className="text-xs bg-vista-darkblue text-white px-3 py-1 rounded hover:bg-vista-gold transition-colors">上传</button>
+                  <input type="file" ref={logoInputRef} className="hidden" accept="image/*" onChange={(e) => handleImageUpload(e, setCustomLogo)} />
+                </div>
+              </div>
+
+              {/* Footer Logo */}
+              <div>
+                <label className="block text-xs text-vista-darkblue/60 mb-2 font-bold">页脚 Logo (单独设置)</label>
+                <div className="flex items-center gap-4">
+                  {customFooterLogo ? (
+                    <img src={customFooterLogo} className="h-10 w-auto object-contain border p-1" alt="Footer Logo" />
+                  ) : <span className="text-xs text-gray-400">默认/继承</span>}
+                  <button onClick={() => footerLogoInputRef.current?.click()} className="text-xs bg-vista-darkblue text-white px-3 py-1 rounded hover:bg-vista-gold transition-colors">上传</button>
+                  <input type="file" ref={footerLogoInputRef} className="hidden" accept="image/*" onChange={(e) => handleImageUpload(e, setCustomFooterLogo)} />
+                </div>
+              </div>
+              
+              {/* Hero Images */}
+              <div>
+                <label className="block text-xs text-vista-darkblue/60 mb-2 font-bold">轮播图 ({heroImages.length})</label>
+                <div className="grid grid-cols-3 gap-2 mb-2">
+                   {heroImages.map((img, i) => (
+                     <div key={i} className="relative aspect-video">
+                       <img src={img} className="w-full h-full object-cover rounded" alt={`Hero ${i}`} />
+                       <button onClick={() => setHeroImages(heroImages.filter((_, idx) => idx !== i))} className="absolute top-0 right-0 bg-red-500 text-white p-0.5 rounded-bl hover:bg-red-600">
+                         <X size={10} />
+                       </button>
+                     </div>
+                   ))}
+                </div>
+                <input type="file" ref={heroInputRef} className="hidden" multiple accept="image/*" onChange={(e) => {
+                   if (e.target.files) {
+                     const newImgs = Array.from(e.target.files).map((f: any) => URL.createObjectURL(f));
+                     setHeroImages([...heroImages, ...newImgs]);
+                   }
+                }} />
+                <button onClick={() => heroInputRef.current?.click()} className="w-full py-2 border border-dashed border-vista-darkblue/30 text-xs text-vista-darkblue hover:border-vista-gold hover:text-vista-gold transition-colors">添加图片</button>
+              </div>
+            </div>
+          </details>
+
+          {/* New Section: Ships Page Details */}
+          <details className="group border border-vista-darkblue/10 rounded-lg overflow-hidden">
+             <summary className="flex items-center gap-2 p-4 bg-vista-darkblue/5 cursor-pointer font-bold text-vista-darkblue text-sm select-none">
+                <Anchor size={16} /> 子页面: 长江行游轮
+             </summary>
+             <div className="p-4 space-y-6 bg-white border-t border-vista-darkblue/10">
+                {/* Lanyue Images */}
+                <div>
+                   <label className="block text-xs text-vista-darkblue/60 mb-2 font-bold">长江行·揽月 (4张图)</label>
+                   <div className="grid grid-cols-2 gap-2">
+                      {shipsPageImages.lanyue.map((img, i) => (
+                         <div key={i} className="relative group/img">
+                            <img src={img} className="w-full h-20 object-cover rounded border" alt={`Lanyue ${i+1}`} />
+                             <label className="absolute inset-0 flex items-center justify-center bg-black/50 opacity-0 group-hover/img:opacity-100 transition-opacity cursor-pointer">
+                                <span className="text-xs text-white">更换</span>
+                                <input type="file" className="hidden" accept="image/*" onChange={(e) => handleArrayMediaUpload(e, i, 'lanyue')} />
+                             </label>
+                         </div>
+                      ))}
+                   </div>
+                </div>
+                {/* Aurora Image */}
+                 <div>
+                   <label className="block text-xs text-vista-darkblue/60 mb-2 font-bold">长江行·极光 (背景图)</label>
+                   <div className="relative group/aurora">
+                      <img src={shipsPageImages.aurora} className="w-full h-24 object-cover rounded border" alt="Aurora" />
+                       <label className="absolute inset-0 flex items-center justify-center bg-black/50 opacity-0 group-hover/aurora:opacity-100 transition-opacity cursor-pointer">
+                          <span className="text-xs text-white">更换</span>
+                          <input type="file" className="hidden" accept="image/*" onChange={(e) => handleImageUpload(e, (url) => setShipsPageImages({...shipsPageImages, aurora: url}))} />
+                       </label>
+                   </div>
+                </div>
+             </div>
+          </details>
+
+          {/* Section 2: Featured Itineraries (Updated for Video) */}
+          <details className="group border border-vista-darkblue/10 rounded-lg overflow-hidden">
+            <summary className="flex items-center gap-2 p-4 bg-vista-darkblue/5 cursor-pointer font-bold text-vista-darkblue text-sm select-none">
+              <Layout size={16} /> 精选航线 (视频/图片)
+            </summary>
+            <div className="p-4 space-y-6 bg-white border-t border-vista-darkblue/10">
+              {itineraries.map((item, idx) => (
+                <div key={item.id} className="border-b pb-4 last:border-0 last:pb-0">
+                  <div className="text-xs font-bold text-vista-gold mb-2">航线 {idx + 1}</div>
+                  <div className="space-y-2">
+                    <input 
+                      type="text" 
+                      value={item.title} 
+                      onChange={(e) => updateItinerary(idx, 'title', e.target.value)}
+                      className="w-full text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                      placeholder="标题"
+                    />
+                    <div className="flex gap-2">
+                      <input 
+                        type="text" 
+                        value={item.price} 
+                        onChange={(e) => updateItinerary(idx, 'price', e.target.value)}
+                        className="w-1/2 text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                        placeholder="价格"
+                      />
+                      <input 
+                        type="text" 
+                        value={item.days} 
+                        onChange={(e) => updateItinerary(idx, 'days', parseInt(e.target.value) || 0)}
+                        className="w-1/2 text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                        placeholder="天数"
+                      />
+                    </div>
+                     <input 
+                        type="text" 
+                        value={item.route} 
+                        onChange={(e) => updateItinerary(idx, 'route', e.target.value)}
+                        className="w-full text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                        placeholder="路线 (如: 重庆 - 宜昌)"
+                      />
+                    
+                    {/* Image & Video Upload */}
+                    <div className="grid grid-cols-2 gap-2 mt-2">
+                        {/* Image */}
+                        <div className="flex flex-col gap-1">
+                            <div className="h-16 bg-gray-100 rounded border overflow-hidden relative">
+                                <img src={item.image} className="w-full h-full object-cover" alt="Thumb" />
+                            </div>
+                            <label className="text-[10px] text-center bg-gray-100 py-1 rounded cursor-pointer hover:bg-gray-200">
+                                更换图片
+                                <input type="file" className="hidden" accept="image/*" onChange={(e) => handleArrayMediaUpload(e, idx, 'itinerary_img')} />
+                            </label>
+                        </div>
+                        {/* Video */}
+                        <div className="flex flex-col gap-1">
+                             <div className="h-16 bg-gray-100 rounded border overflow-hidden relative flex items-center justify-center">
+                                {item.video ? (
+                                    <video src={item.video} className="w-full h-full object-cover" />
+                                ) : (
+                                    <Video size={20} className="text-gray-400" />
+                                )}
+                            </div>
+                            <label className="text-[10px] text-center bg-gray-100 py-1 rounded cursor-pointer hover:bg-gray-200">
+                                上传/更换视频
+                                <input type="file" className="hidden" accept="video/*" onChange={(e) => handleArrayMediaUpload(e, idx, 'itinerary_video')} />
+                            </label>
+                        </div>
+                    </div>
+                  </div>
+                </div>
+              ))}
+            </div>
+          </details>
+
+          {/* Section 3: Dining */}
+           <details className="group border border-vista-darkblue/10 rounded-lg overflow-hidden">
+            <summary className="flex items-center gap-2 p-4 bg-vista-darkblue/5 cursor-pointer font-bold text-vista-darkblue text-sm select-none">
+              <Utensils size={16} /> 餐饮板块
+            </summary>
+            <div className="p-4 space-y-4 bg-white border-t border-vista-darkblue/10">
+               <input 
+                  type="text" 
+                  value={dining.title} 
+                  onChange={(e) => setDining({...dining, title: e.target.value})}
+                  className="w-full text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                  placeholder="标题"
+               />
+               <textarea 
+                  rows={3}
+                  value={dining.description} 
+                  onChange={(e) => setDining({...dining, description: e.target.value})}
+                  className="w-full text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                  placeholder="描述"
+               />
+               <input 
+                  type="text" 
+                  value={dining.buttonText} 
+                  onChange={(e) => setDining({...dining, buttonText: e.target.value})}
+                  className="w-full text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                  placeholder="按钮文字"
+               />
+                <div className="flex items-center gap-2">
+                  <span className="text-xs text-gray-500">背景图:</span>
+                  <img src={dining.image} className="w-16 h-8 object-cover rounded border" alt="Dining BG" />
+                  <button onClick={() => diningImgRef.current?.click()} className="text-xs bg-gray-200 px-2 py-1 rounded">更换</button>
+                  <input type="file" ref={diningImgRef} className="hidden" accept="image/*" onChange={(e) => handleImageUpload(e, (url) => setDining({...dining, image: url}))} />
+                </div>
+            </div>
+           </details>
+
+           {/* Section 4: Fleet / Ships */}
+           <details className="group border border-vista-darkblue/10 rounded-lg overflow-hidden">
+            <summary className="flex items-center gap-2 p-4 bg-vista-darkblue/5 cursor-pointer font-bold text-vista-darkblue text-sm select-none">
+              <Ship size={16} /> 船队介绍 (首页)
+            </summary>
+            <div className="p-4 space-y-6 bg-white border-t border-vista-darkblue/10">
+              {ships.map((ship, idx) => (
+                <div key={ship.id} className="border-b pb-4 last:border-0 last:pb-0">
+                   <div className="text-xs font-bold text-vista-gold mb-2">游轮 {idx + 1}</div>
+                   <div className="space-y-2">
+                      <input 
+                        type="text" 
+                        value={ship.name} 
+                        onChange={(e) => updateShip(idx, 'name', e.target.value)}
+                        className="w-full text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                        placeholder="船名"
+                      />
+                      <textarea 
+                        rows={2}
+                        value={ship.description} 
+                        onChange={(e) => updateShip(idx, 'description', e.target.value)}
+                        className="w-full text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                        placeholder="描述"
+                      />
+                      <div className="flex items-center gap-2">
+                        <img src={ship.image} className="w-12 h-8 object-cover rounded border" alt="Ship" />
+                        <label className="text-xs text-vista-darkblue underline cursor-pointer">
+                          更换图片
+                          <input type="file" className="hidden" accept="image/*" onChange={(e) => handleArrayMediaUpload(e, idx, 'ship')} />
+                        </label>
+                      </div>
+                   </div>
+                </div>
+              ))}
+            </div>
+           </details>
+
+           {/* Section 5: Video Section */}
+           <details className="group border border-vista-darkblue/10 rounded-lg overflow-hidden">
+            <summary className="flex items-center gap-2 p-4 bg-vista-darkblue/5 cursor-pointer font-bold text-vista-darkblue text-sm select-none">
+              <Film size={16} /> 视频板块
+            </summary>
+            <div className="p-4 space-y-4 bg-white border-t border-vista-darkblue/10">
+               <div className="flex gap-2">
+                 <input 
+                    type="text" 
+                    value={videoSection.title} 
+                    onChange={(e) => setVideoSection({...videoSection, title: e.target.value})}
+                    className="w-1/2 text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                    placeholder="标题前半部分"
+                 />
+                 <input 
+                    type="text" 
+                    value={videoSection.titleItalic} 
+                    onChange={(e) => setVideoSection({...videoSection, titleItalic: e.target.value})}
+                    className="w-1/2 text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                    placeholder="斜体标题"
+                 />
+               </div>
+               <textarea 
+                  rows={3}
+                  value={videoSection.description} 
+                  onChange={(e) => setVideoSection({...videoSection, description: e.target.value})}
+                  className="w-full text-xs border p-2 rounded focus:outline-none focus:border-vista-gold"
+                  placeholder="描述"
+               />
+                <div className="flex items-center gap-2">
+                  <span className="text-xs text-gray-500">缩略图:</span>
+                  <img src={videoSection.thumbnail} className="w-16 h-8 object-cover rounded border" alt="Video Thumb" />
+                  <button onClick={() => videoThumbRef.current?.click()} className="text-xs bg-gray-200 px-2 py-1 rounded">更换</button>
+                  <input type="file" ref={videoThumbRef} className="hidden" accept="image/*" onChange={(e) => handleImageUpload(e, (url) => setVideoSection({...videoSection, thumbnail: url}))} />
+                </div>
+            </div>
+           </details>
+        </div>
+
+        {/* Reset Button */}
+        <div className="border-t border-vista-darkblue/10 pt-6 mt-4">
+          <button 
+            onClick={() => {
+              if(window.confirm('确定要重置所有内容为默认值吗?此操作不可撤销。')) {
+                resetTheme();
+              }
+            }}
+            className="w-full py-3 text-vista-darkblue/60 text-xs font-bold uppercase tracking-widest hover:text-red-500 transition-colors flex items-center justify-center gap-2"
+          >
+            <RotateCcw size={14} /> 重置为默认内容
+          </button>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default ThemeSettings;

+ 90 - 0
src/components/VistaLogo.tsx

@@ -0,0 +1,90 @@
+import React from 'react';
+import { useTheme } from '../contexts/ThemeContext.tsx';
+
+interface VistaLogoProps {
+  theme?: 'light' | 'dark';
+  className?: string;
+  color?: string; // Allow explicit color override
+  src?: string | null; // Allow explicit image source override
+}
+
+const VistaLogo: React.FC<VistaLogoProps> = ({ theme = 'dark', className = '', color, src }) => {
+  const { customLogo } = useTheme();
+
+  // Default logic if color is not provided:
+  // light theme (dark bg) -> white text
+  // dark theme (light bg) -> gold text
+  const defaultColor = theme === 'light' ? '#ffffff' : '#c5a059';
+
+  // Use explicit color if provided, otherwise fallback to theme logic
+  const brandColor = color || defaultColor;
+
+  // Prioritize passed src (e.g., footer logo), then global customLogo
+  const activeLogoImage = src || customLogo;
+
+  // If a custom logo (or overridden src) is present, display it instead of the SVG
+  if (activeLogoImage) {
+    return (
+      <div className={`flex items-center gap-2 ${className} select-none h-[50px]`}>
+        <img src={activeLogoImage} alt="Vista Cruises" className="h-full w-auto object-contain" />
+      </div>
+    );
+  }
+
+  return (
+    <div className={`flex items-center gap-2 ${className} select-none`}>
+      {/* Icon Graphic */}
+      <div className="w-[50px] h-[50px] flex-shrink-0 relative">
+        <svg viewBox="0 0 100 80" fill="none" xmlns="http://www.w3.org/2000/svg" className="w-full h-full">
+          {/* Left Arc (Sun/Globe) */}
+          <path
+            d="M 25 70 A 35 35 0 1 1 65 10"
+            stroke={brandColor}
+            strokeWidth="7"
+            strokeLinecap="round"
+            className="transition-colors duration-300"
+          />
+
+          {/* Top Sail */}
+          <path
+            d="M 35 48 C 55 40 75 25 95 12 L 95 18 C 75 32 55 48 35 55 Z"
+            fill={brandColor}
+            className="transition-colors duration-300"
+          />
+
+          {/* Middle Sail */}
+          <path
+            d="M 28 62 C 50 52 75 38 98 25 L 98 30 C 75 45 50 60 28 68 Z"
+            fill={brandColor}
+            className="transition-colors duration-300"
+          />
+
+          {/* Bottom Hull/Wave */}
+          <path
+            d="M 5 80 C 35 65 70 50 100 45 L 92 70 C 60 75 30 85 20 90 Z"
+            fill={brandColor}
+            className="transition-colors duration-300"
+          />
+        </svg>
+      </div>
+
+      {/* Text Brand */}
+      <div className="flex flex-col items-start leading-none">
+        <span
+          className="text-[22px] font-serif font-bold tracking-wider transition-colors duration-300"
+          style={{ color: brandColor }}
+        >
+          长江行游轮
+        </span>
+        <span
+          className="text-[9px] tracking-[0.3em] font-sans mt-[2px] uppercase transition-colors duration-300"
+          style={{ color: brandColor }}
+        >
+          Vista Cruises
+        </span>
+      </div>
+    </div>
+  );
+};
+
+export default VistaLogo;

+ 30 - 0
src/config/config.ts

@@ -0,0 +1,30 @@
+interface Config {
+  base_url: string;
+  result_code: number | string;
+  default_headers: string;
+  request_timeout: number;
+}
+
+const config: Config = {
+  /**
+   * api请求基础路径
+   */
+  base_url: import.meta.env.VITE_BASE_URL + import.meta.env.VITE_API_URL,
+  /**
+   * 接口成功返回状态码
+   */
+  result_code: 200,
+
+  /**
+   * 接口请求超时时间
+   */
+  request_timeout: 60000,
+
+  /**
+   * 默认接口请求类型
+   * 可选值:application/x-www-form-urlencoded multipart/form-data
+   */
+  default_headers: 'application/json'
+};
+
+export { config };

+ 49 - 0
src/config/index.ts

@@ -0,0 +1,49 @@
+import { service } from './service.ts';
+import { config } from './config.ts';
+
+const { default_headers } = config;
+
+const request = (option: any) => {
+  const { headersType, headers, ...otherOption } = option;
+  return service({
+    ...otherOption,
+    headers: {
+      'Content-Type': headersType || default_headers,
+      ...headers
+    }
+  });
+};
+
+const requestInstance = {
+  get: async <T = any>(option: any) => {
+    const res = await request({ method: 'GET', ...option });
+    return res.data as T;
+  },
+  post: async <T = any>(option: any) => {
+    const res = await request({ method: 'POST', ...option });
+    return res.data as T;
+  },
+  postOriginal: async (option: any) => {
+    const res = await request({ method: 'POST', ...option });
+    return res;
+  },
+  delete: async <T = any>(option: any) => {
+    const res = await request({ method: 'DELETE', ...option });
+    return res.data as T;
+  },
+  put: async <T = any>(option: any) => {
+    const res = await request({ method: 'PUT', ...option });
+    return res.data as T;
+  },
+  download: async <T = any>(option: any) => {
+    const res = await request({ method: 'GET', responseType: 'blob', ...option });
+    return res as Promise<T>;
+  },
+  upload: async <T = any>(option: any) => {
+    option.headersType = 'multipart/form-data';
+    const res = await request({ method: 'POST', ...option });
+    return res as Promise<T>;
+  }
+};
+
+export default requestInstance;

+ 45 - 0
src/config/service.ts

@@ -0,0 +1,45 @@
+import axios from 'axios';
+import qs from 'qs';
+import { config } from './config.ts';
+
+const { base_url, request_timeout } = config;
+
+// 创建axios实例
+const service = axios.create({
+  baseURL: base_url, // api 的 base_url
+  timeout: request_timeout, // 请求超时时间
+  withCredentials: false, // 禁用 Cookie 等信息
+  // 自定义参数序列化函数
+  paramsSerializer: (params) => {
+    return qs.stringify(params, { allowDots: true });
+  }
+});
+
+// request拦截器
+service.interceptors.request.use(
+  (config) => {
+    const method = config.method?.toUpperCase();
+    // 防止 GET 请求缓存
+    if (method === 'GET') {
+      config.headers['Cache-Control'] = 'no-cache';
+      config.headers['Pragma'] = 'no-cache';
+    }
+    // 自定义参数序列化函数
+    else if (method === 'POST') {
+      const contentType = config.headers['Content-Type'] || config.headers['content-type'];
+      if (contentType === 'application/x-www-form-urlencoded') {
+        if (config.data && typeof config.data !== 'string') {
+          config.data = qs.stringify(config.data);
+        }
+      }
+    }
+    return config;
+  },
+  (error) => {
+    // Do something with request error
+    console.log(error); // for debug
+    return Promise.reject(error);
+  }
+);
+
+export { service };

+ 408 - 0
src/constants.ts

@@ -0,0 +1,408 @@
+import { MenuItem, CruiseShip, Itinerary, Feature, Language } from './types';
+
+export const PHONE_NUMBER = "400-696-0666";
+
+// Placeholder images for the carousel (Simulating the user provided images)
+// 1. Interior Dining/Lounge
+// 2. Deck with Umbrellas
+// 3. Ship/Lock/Gorge view
+export const HERO_IMAGES = [
+  "https://images.unsplash.com/photo-1578474843222-9593bc88d8b0?q=80&w=1920&auto=format&fit=crop", // Dining/Lounge feel
+  "https://images.unsplash.com/photo-1599640845513-2627a35c602a?q=80&w=1920&auto=format&fit=crop", // Deck/Ocean feel
+  "https://images.unsplash.com/photo-1534055691060-0382f646016c?q=80&w=1920&auto=format&fit=crop"  // Gorge/River feel
+];
+
+export const CONTENT = {
+  zh: {
+    nav: {
+      book: "立即预订",
+      contact: "联系客服",
+      menu: [
+        { title: "首页", link: "#/" },
+        { 
+          title: "长江行游轮", 
+          link: "#/ships",
+          submenu: [
+            { title: "长江行系列", link: "#/ships?section=series" },
+            { title: "长江行·揽月", link: "#/ships?section=lanyue" },
+            { title: "长江行·极光", link: "#/ships?section=aurora" },
+          ]
+        },
+        { 
+          title: "精选航线", 
+          link: "#/itineraries",
+          submenu: [
+            { title: "航线行程", link: "#itinerary-routes" },
+            { title: "产品亮点", link: "#itinerary-highlights" },
+            { title: "游轮航期", link: "#itinerary-schedules" },
+          ]
+        },
+        { 
+          title: "游轮体验", 
+          link: "#experience",
+          submenu: [
+            { title: "品质服务", link: "#exp-service" },
+            { title: "星级餐饮", link: "#exp-dining" },
+            { title: "丰富活动", link: "#exp-activities" },
+          ]
+        },
+        { 
+          title: "游轮空间", 
+          link: "#spaces",
+          submenu: [
+            { title: "设施设备", link: "#space-facilities" },
+            { title: "房型介绍", link: "#space-rooms" },
+            { title: "尊享礼遇", link: "#space-vip" },
+          ]
+        },
+        { 
+          title: "出行攻略", 
+          link: "#guide",
+          submenu: [
+            { title: "出行指南", link: "#guide-tips" },
+            { title: "常见问题", link: "#guide-faq" },
+          ]
+        },
+        { 
+          title: "关于我们", 
+          link: "#about",
+          submenu: [
+            { title: "企业简介", link: "#about-intro" },
+            { title: "官方媒体", link: "#about-media" },
+          ]
+        },
+      ]
+    },
+    hero: {
+      welcome: "欢迎来到长江行游轮",
+      title_italic: "驻足",
+      title_normal: "即是旅途",
+      subtitle: "体验长江之上的奢华与服务,超乎您的想象。水上圣殿,静候君临。",
+      btn_find: "探索航线",
+      btn_ships: "游轮舰队"
+    },
+    home: {
+      featured_sub: "精选航程",
+      featured_title: "甄选航线",
+      view_all: "查看所有航线",
+      days: "天",
+      details: "查看详情",
+      dining_title: "卓越美食",
+      dining_desc: "品味米其林灵感菜单,由世界级名厨精选当地最新鲜食材烹饪。每一餐都是一次味蕾的旅行。",
+      dining_btn: "探索餐饮",
+      fleet_sub: "游轮舰队",
+      fleet_title: "移动的水上圣殿",
+      discover_ship: "探索游轮",
+      exp_sub: "游轮体验",
+      exp_title: "沉浸于",
+      exp_title_italic: "非凡时刻",
+      exp_desc: "从您登船的那一刻起,每一个细节都旨在提供舒适、优雅与冒险的完美融合。",
+    },
+    footer: {
+      desc: "重塑长江之旅。奢华与传承的完美交融。",
+      hotline: "24/7 预订热线",
+      privacy: "隐私政策",
+      terms: "条款与条件",
+      cookies: "Cookie 政策",
+      rights: "版权所有。"
+    },
+    shipsPage: {
+      hero_sub: "游轮舰队",
+      hero_desc: "重新定义长江之上的奢华之旅。",
+      now_sailing: "现已启航",
+      coming_soon: "2026 即将启幕",
+      flagship_label: "旗舰荣耀",
+      stats_tons: "17,000 吨",
+      stats_stars: "五星级奢华",
+      stats_ratio: "船员配比",
+      view_itineraries: "查看航线",
+      future_label: "游轮未来式",
+      waitlist_btn: "加入候补名单",
+      intro: {
+        title: "何谓“长江行”?",
+        desc: "长江,这条奔腾不息的巨龙,早已超越地理范畴,深深融入中华民族的文化基因与精神血脉。她是“唯见长江天际流”的壮阔无垠,是“不尽长江滚滚来”的时空浩荡,更是“大江东去,浪淘尽,千古风流人物”的历史回响。“长江行”不止是一次航行,更是一场三重境界的深度体验。",
+        points: [
+          {
+            title: "行·贯古今",
+            desc: "循黄金水道,溯千年文脉。让孤帆远影的诗意、金戈铁马的雄浑、桨声灯影的风华,在船舷两侧流淌成活的史诗。"
+          },
+          {
+            title: "行·见天地",
+            desc: "跨省际壮游,揽山河入怀。夔门之险、荆江九曲、沃野平畴、都市风华...万里气象与地域精粹皆成窗外交响。"
+          },
+          {
+            title: "行·致未来",
+            desc: "以匠心远见,雕时代典范。这是面向未来的智慧航行,一次对至臻体验与永续之美的承诺。"
+          }
+        ]
+      },
+      lanyue: {
+        title: "长江行·揽月",
+        desc: "“长江行·揽月”作为长江行系列游轮首艘旗舰,是目前国内尺寸最大的绿色低碳环保型度假游轮之一,也是三峡旅游集团继“长江三峡1”“西陵峡·和悦”“西陵峡·和谐”“宜昌远影”之后推出的新一代新能源游轮旗舰。"
+      },
+      aurora: {
+        title: "长江行·极光",
+        desc: "2026首航,敬请期待。"
+      }
+    },
+    ships: [
+      {
+        id: "lanyue",
+        name: "长江行·揽月",
+        description: "体验长江之上的奢华巅峰。优雅的避世圣所。",
+        image: "https://picsum.photos/800/600?random=1"
+      },
+      {
+        id: "jiguang",
+        name: "长江行·极光",
+        description: "现代设计与传统待客之道的融合。无与伦比的观景体验。",
+        image: "https://picsum.photos/800/600?random=2"
+      },
+      {
+        id: "star",
+        name: "长江行·星河",
+        description: "树立内河游轮新标杆。私密、尊贵、难忘。",
+        image: "https://picsum.photos/800/600?random=3"
+      }
+    ],
+    itineraries: [
+      {
+        id: "1",
+        title: "三峡探索之旅",
+        days: 4,
+        price: "¥3,999 起",
+        image: "https://picsum.photos/600/400?random=10",
+        route: "重庆 - 宜昌"
+      },
+      {
+        id: "2",
+        title: "长江人文之旅",
+        days: 5,
+        price: "¥4,599 起",
+        image: "https://picsum.photos/600/400?random=11",
+        route: "宜昌 - 重庆"
+      },
+      {
+        id: "3",
+        title: "大江东去长航线",
+        days: 8,
+        price: "¥8,888 起",
+        image: "https://picsum.photos/600/400?random=12",
+        route: "重庆 - 上海"
+      }
+    ],
+    features: [
+      {
+        title: "管家服务",
+        description: "每间套房均配备24小时私人管家服务。",
+        icon: "user"
+      },
+      {
+        title: "米其林餐饮",
+        description: "由世界知名主厨精心策划的菜单。",
+        icon: "utensils"
+      },
+      {
+        title: "文化沉浸",
+        description: "专属岸上观光与船上讲座。",
+        icon: "map"
+      }
+    ]
+  },
+  en: {
+    nav: {
+      book: "BOOK NOW",
+      contact: "Contact",
+      menu: [
+        { title: "HOME", link: "#/" },
+        { 
+          title: "VISTA CRUISES", 
+          link: "#/ships",
+          submenu: [
+            { title: "The Vista Series", link: "#/ships?section=series" },
+            { title: "Vista Lanyue", link: "#/ships?section=lanyue" },
+            { title: "Vista Aurora", link: "#/ships?section=aurora" },
+          ]
+        },
+        { 
+          title: "VOYAGES", 
+          link: "#/itineraries",
+          submenu: [
+            { title: "Itineraries", link: "#itinerary-routes" },
+            { title: "Highlights", link: "#itinerary-highlights" },
+            { title: "Schedules", link: "#itinerary-schedules" },
+          ]
+        },
+        { 
+          title: "EXPERIENCE", 
+          link: "#experience",
+          submenu: [
+            { title: "Service", link: "#exp-service" },
+            { title: "Dining", link: "#exp-dining" },
+            { title: "Activities", link: "#exp-activities" },
+          ]
+        },
+        { 
+          title: "SPACES", 
+          link: "#spaces",
+          submenu: [
+            { title: "Facilities", link: "#space-facilities" },
+            { title: "Suites", link: "#space-rooms" },
+            { title: "Privileges", link: "#space-vip" },
+          ]
+        },
+        { 
+          title: "GUIDE", 
+          link: "#guide",
+          submenu: [
+            { title: "Travel Tips", link: "#guide-tips" },
+            { title: "FAQ", link: "#guide-faq" },
+          ]
+        },
+        { 
+          title: "ABOUT US", 
+          link: "#about",
+          submenu: [
+            { title: "Introduction", link: "#about-intro" },
+            { title: "Media", link: "#about-media" },
+          ]
+        },
+      ]
+    },
+    hero: {
+      welcome: "Welcome to Vista Cruises",
+      title_italic: "Stay &",
+      title_normal: "Journey",
+      subtitle: "Experience the majestic Yangtze River with a level of luxury and service never before imagined. Your sanctuary on the water awaits.",
+      btn_find: "Find a Cruise",
+      btn_ships: "Our Ships"
+    },
+    home: {
+      featured_sub: "Featured Voyages",
+      featured_title: "Curated Itineraries",
+      view_all: "View All Voyages",
+      days: "Days",
+      details: "Details",
+      dining_title: "Culinary Excellence",
+      dining_desc: "Savor the flavors of the region with our Michelin-inspired menus, crafted by world-class chefs using the freshest local ingredients. Every meal is a journey in itself.",
+      dining_btn: "Explore Dining",
+      fleet_sub: "The Fleet",
+      fleet_title: "Our Floating Sanctuaries",
+      discover_ship: "Discover Ship",
+      exp_sub: "The Experience",
+      exp_title: "Immerse Yourself in",
+      exp_title_italic: "The Extraordinary",
+      exp_desc: "From the moment you step onboard, every detail is curated to provide a seamless blend of comfort, elegance, and adventure. Watch our journey unfold.",
+    },
+    footer: {
+      desc: "Reimagining the Yangtze experience. Where luxury meets heritage in perfect harmony.",
+      hotline: "24/7 Reservation Hotline",
+      privacy: "Privacy Policy",
+      terms: "Terms & Conditions",
+      cookies: "Cookie Policy",
+      rights: "Vista Cruises. All Rights Reserved."
+    },
+    shipsPage: {
+      hero_sub: "The Fleet",
+      hero_desc: "Redefining luxury on the Yangtze River.",
+      now_sailing: "Now Sailing",
+      coming_soon: "Coming 2026",
+      flagship_label: "The Flagship",
+      stats_tons: "17,000 Tons",
+      stats_stars: "5-Star Luxury",
+      stats_ratio: "Crew Ratio",
+      view_itineraries: "View Itineraries",
+      future_label: "Future of Cruising",
+      waitlist_btn: "Join Waitlist",
+      intro: {
+        title: "The Vista Philosophy",
+        desc: "The Yangtze, a dragon that never sleeps, transcends geography to become the spiritual bloodline of a nation. Vista Cruises offers not just a journey, but a three-fold experience of depth and discovery.",
+        points: [
+          {
+            title: "History",
+            desc: "Tracing thousands of years of culture along the golden waterway."
+          },
+          {
+            title: "Nature",
+            desc: "Embracing the majestic mountains and rivers, from the dangerous Kuimen to the fertile plains."
+          },
+          {
+            title: "Future",
+            desc: "A commitment to ultimate experience and sustainable beauty with craftsmanship and foresight."
+          }
+        ]
+      },
+      lanyue: {
+        title: "Vista Lanyue",
+        desc: "As the first flagship of the Vista series, Lanyue is one of the largest green, low-carbon vacation cruises in China. It represents the new generation of new energy flagships."
+      },
+      aurora: {
+        title: "Vista Aurora",
+        desc: "Maiden Voyage 2026. Stay tuned."
+      }
+    },
+    ships: [
+      {
+        id: "lanyue",
+        name: "Vista Lanyue",
+        description: "Experience the epitome of luxury on the Yangtze with the Lanyue. A sanctuary of elegance.",
+        image: "https://picsum.photos/800/600?random=1"
+      },
+      {
+        id: "jiguang",
+        name: "Vista Aurora",
+        description: "Modern design meets traditional hospitality. The Jiguang offers an unparalleled viewing experience.",
+        image: "https://picsum.photos/800/600?random=2"
+      },
+      {
+        id: "star",
+        name: "Vista Star",
+        description: "A new standard for river cruising. Intimate, exclusive, and unforgettable.",
+        image: "https://picsum.photos/800/600?random=3"
+      }
+    ],
+    itineraries: [
+      {
+        id: "1",
+        title: "Three Gorges Discovery",
+        days: 4,
+        price: "Fr. ¥3,999",
+        image: "https://picsum.photos/600/400?random=10",
+        route: "Chongqing - Yichang"
+      },
+      {
+        id: "2",
+        title: "Yangtze Heritage",
+        days: 5,
+        price: "Fr. ¥4,599",
+        image: "https://picsum.photos/600/400?random=11",
+        route: "Yichang - Chongqing"
+      },
+      {
+        id: "3",
+        title: "Grand River Voyage",
+        days: 8,
+        price: "Fr. ¥8,888",
+        image: "https://picsum.photos/600/400?random=12",
+        route: "Chongqing - Shanghai"
+      }
+    ],
+    features: [
+      {
+        title: "Butler Service",
+        description: "24-hour personal butler service for every suite.",
+        icon: "user"
+      },
+      {
+        title: "Michelin Dining",
+        description: "Curated menus by world-renowned chefs.",
+        icon: "utensils"
+      },
+      {
+        title: "Cultural Immersion",
+        description: "Exclusive shore excursions and onboard lectures.",
+        icon: "map"
+      }
+    ]
+  }
+};

+ 37 - 0
src/contexts/LanguageContext.tsx

@@ -0,0 +1,37 @@
+import React, { createContext, useState, useContext, ReactNode } from 'react';
+import { Language } from '../types.ts';
+
+interface LanguageContextType {
+  language: Language;
+  setLanguage: (lang: Language) => void;
+  toggleLanguage: () => void;
+  t: (key: string) => string;
+}
+
+const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
+
+export const LanguageProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
+  const [language, setLanguage] = useState<Language>('zh');
+
+  const toggleLanguage = () => {
+    setLanguage(prev => prev === 'zh' ? 'en' : 'zh');
+  };
+
+  // Simple translation helper placeholder
+  // Real translations will be handled by selecting data objects in components
+  const t = (key: string) => key;
+
+  return (
+    <LanguageContext.Provider value={{ language, setLanguage, toggleLanguage, t }}>
+      {children}
+    </LanguageContext.Provider>
+  );
+};
+
+export const useLanguage = () => {
+  const context = useContext(LanguageContext);
+  if (!context) {
+    throw new Error('useLanguage must be used within a LanguageProvider');
+  }
+  return context;
+};

+ 194 - 0
src/contexts/ThemeContext.tsx

@@ -0,0 +1,194 @@
+
+import React, { createContext, useState, useContext, ReactNode, useEffect } from 'react';
+import { HERO_IMAGES, CONTENT } from '../constants.ts';
+import { Itinerary, CruiseShip, DiningSection, VideoSectionContent, ShipsPageImages } from '../types.ts';
+
+interface ThemeContextType {
+  customLogo: string | null;
+  customFooterLogo: string | null;
+  heroImages: string[];
+  
+  // New customizable sections
+  itineraries: Itinerary[];
+  ships: CruiseShip[];
+  dining: DiningSection;
+  videoSection: VideoSectionContent;
+  
+  // Ships Page Specific Images
+  shipsPageImages: ShipsPageImages;
+
+  // Setters
+  setCustomLogo: (url: string | null) => void;
+  setCustomFooterLogo: (url: string | null) => void;
+  setHeroImages: (images: string[]) => void;
+  setItineraries: (data: Itinerary[]) => void;
+  setShips: (data: CruiseShip[]) => void;
+  setDining: (data: DiningSection) => void;
+  setVideoSection: (data: VideoSectionContent) => void;
+  setShipsPageImages: (data: ShipsPageImages) => void;
+  
+  resetTheme: () => void;
+}
+
+const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
+
+// Initial default values from constants (ZH version as base)
+const DEFAULT_ITINERARIES: Itinerary[] = [
+  {
+    ...CONTENT.zh.itineraries[0],
+    video: "https://videos.pexels.com/video-files/3252327/3252327-uhd_2560_1440_25fps.mp4" 
+  },
+  {
+    ...CONTENT.zh.itineraries[1],
+    video: "https://videos.pexels.com/video-files/5049352/5049352-uhd_2560_1440_30fps.mp4"
+  },
+  {
+    ...CONTENT.zh.itineraries[2],
+    video: "https://videos.pexels.com/video-files/2927233/2927233-uhd_2560_1440_24fps.mp4"
+  }
+];
+
+const DEFAULT_SHIPS = CONTENT.zh.ships;
+const DEFAULT_DINING: DiningSection = {
+  title: CONTENT.zh.home.dining_title,
+  description: CONTENT.zh.home.dining_desc,
+  buttonText: CONTENT.zh.home.dining_btn,
+  image: "https://picsum.photos/1920/600?random=5"
+};
+const DEFAULT_VIDEO: VideoSectionContent = {
+  title: CONTENT.zh.home.exp_title,
+  titleItalic: CONTENT.zh.home.exp_title_italic,
+  description: CONTENT.zh.home.exp_desc,
+  thumbnail: "https://picsum.photos/800/450?random=20"
+};
+
+const DEFAULT_SHIPS_PAGE_IMAGES: ShipsPageImages = {
+  lanyue: [
+    "https://picsum.photos/600/400?random=100",
+    "https://picsum.photos/600/400?random=101",
+    "https://picsum.photos/600/400?random=102",
+    "https://picsum.photos/600/400?random=103"
+  ],
+  aurora: "https://picsum.photos/1920/800?random=200"
+};
+
+export const ThemeProvider: React.FC<{ children: ReactNode }> = ({ children }) => {
+  // --- Logo & Hero ---
+  const [customLogo, setCustomLogo] = useState<string | null>(() => {
+    return localStorage.getItem('vista_custom_logo') || null;
+  });
+
+  const [customFooterLogo, setCustomFooterLogo] = useState<string | null>(() => {
+    return localStorage.getItem('vista_custom_footer_logo') || null;
+  });
+
+  const [heroImages, setHeroImages] = useState<string[]>(() => {
+    const saved = localStorage.getItem('vista_hero_images');
+    return saved ? JSON.parse(saved) : HERO_IMAGES;
+  });
+
+  // --- Content Sections ---
+  const [itineraries, setItineraries] = useState<Itinerary[]>(() => {
+    const saved = localStorage.getItem('vista_itineraries');
+    return saved ? JSON.parse(saved) : DEFAULT_ITINERARIES;
+  });
+
+  const [ships, setShips] = useState<CruiseShip[]>(() => {
+    const saved = localStorage.getItem('vista_ships');
+    return saved ? JSON.parse(saved) : DEFAULT_SHIPS;
+  });
+
+  const [dining, setDining] = useState<DiningSection>(() => {
+    const saved = localStorage.getItem('vista_dining');
+    return saved ? JSON.parse(saved) : DEFAULT_DINING;
+  });
+
+  const [videoSection, setVideoSection] = useState<VideoSectionContent>(() => {
+    const saved = localStorage.getItem('vista_video');
+    return saved ? JSON.parse(saved) : DEFAULT_VIDEO;
+  });
+
+  const [shipsPageImages, setShipsPageImages] = useState<ShipsPageImages>(() => {
+    const saved = localStorage.getItem('vista_ships_page_images');
+    return saved ? JSON.parse(saved) : DEFAULT_SHIPS_PAGE_IMAGES;
+  });
+
+  // --- Persistence Effects ---
+  useEffect(() => {
+    if (customLogo) localStorage.setItem('vista_custom_logo', customLogo);
+    else localStorage.removeItem('vista_custom_logo');
+  }, [customLogo]);
+
+  useEffect(() => {
+    if (customFooterLogo) localStorage.setItem('vista_custom_footer_logo', customFooterLogo);
+    else localStorage.removeItem('vista_custom_footer_logo');
+  }, [customFooterLogo]);
+
+  useEffect(() => {
+    localStorage.setItem('vista_hero_images', JSON.stringify(heroImages));
+  }, [heroImages]);
+
+  useEffect(() => {
+    localStorage.setItem('vista_itineraries', JSON.stringify(itineraries));
+  }, [itineraries]);
+
+  useEffect(() => {
+    localStorage.setItem('vista_ships', JSON.stringify(ships));
+  }, [ships]);
+
+  useEffect(() => {
+    localStorage.setItem('vista_dining', JSON.stringify(dining));
+  }, [dining]);
+
+  useEffect(() => {
+    localStorage.setItem('vista_video', JSON.stringify(videoSection));
+  }, [videoSection]);
+
+  useEffect(() => {
+    localStorage.setItem('vista_ships_page_images', JSON.stringify(shipsPageImages));
+  }, [shipsPageImages]);
+
+
+  const resetTheme = () => {
+    setCustomLogo(null);
+    setCustomFooterLogo(null);
+    setHeroImages(HERO_IMAGES);
+    setItineraries(DEFAULT_ITINERARIES);
+    setShips(DEFAULT_SHIPS);
+    setDining(DEFAULT_DINING);
+    setVideoSection(DEFAULT_VIDEO);
+    setShipsPageImages(DEFAULT_SHIPS_PAGE_IMAGES);
+
+    localStorage.removeItem('vista_custom_logo');
+    localStorage.removeItem('vista_custom_footer_logo');
+    localStorage.removeItem('vista_hero_images');
+    localStorage.removeItem('vista_itineraries');
+    localStorage.removeItem('vista_ships');
+    localStorage.removeItem('vista_dining');
+    localStorage.removeItem('vista_video');
+    localStorage.removeItem('vista_ships_page_images');
+  };
+
+  return (
+    <ThemeContext.Provider value={{ 
+      customLogo, customFooterLogo, heroImages, 
+      setCustomLogo, setCustomFooterLogo, setHeroImages,
+      itineraries, setItineraries,
+      ships, setShips,
+      dining, setDining,
+      videoSection, setVideoSection,
+      shipsPageImages, setShipsPageImages,
+      resetTheme 
+    }}>
+      {children}
+    </ThemeContext.Provider>
+  );
+};
+
+export const useTheme = () => {
+  const context = useContext(ThemeContext);
+  if (!context) {
+    throw new Error('useTheme must be used within a ThemeProvider');
+  }
+  return context;
+};

BIN
src/images/Socialmedia/dy.png


BIN
src/images/Socialmedia/gzh.png


BIN
src/images/Socialmedia/xhs.png


+ 229 - 0
src/pages/Home.tsx

@@ -0,0 +1,229 @@
+import React, { useState, useEffect } from 'react';
+import Navbar from '@/src/components/Navbar.tsx';
+import Sidebar from '@/src/components/Sidebar.tsx';
+import Footer from '@/src/components/Footer.tsx';
+import { CONTENT } from '../constants.ts';
+import { ChevronRight, Play } from 'lucide-react';
+import { useLanguage } from '@/src/contexts/LanguageContext.tsx';
+import { useTheme } from '@/src/contexts/ThemeContext.tsx';
+
+const Home: React.FC = () => {
+  const [activeSlide, setActiveSlide] = useState(0);
+  const { language } = useLanguage();
+  // Fetch all customizable data from ThemeContext
+  const { heroImages, itineraries, ships, dining, videoSection } = useTheme();
+  
+  // Get static labels based on current language
+  const t = CONTENT[language];
+  const features = t.features; // Features list is still static/translatable for now
+
+  // Carousel Logic
+  useEffect(() => {
+    if (heroImages.length === 0) return;
+    const timer = setInterval(() => {
+      setActiveSlide((prev) => (prev + 1) % heroImages.length);
+    }, 5000); // Change slide every 5 seconds
+    return () => clearInterval(timer);
+  }, [heroImages.length]);
+
+  return (
+    <div className="min-h-screen bg-white font-sans text-vista-darkblue overflow-x-hidden">
+      <Navbar />
+      <Sidebar />
+
+      {/* Hero Section with Carousel */}
+      <section className="relative h-screen w-full overflow-hidden">
+        {/* Carousel Background Layer */}
+        {heroImages.map((img, index) => (
+          <div 
+            key={index}
+            className={`absolute inset-0 z-0 transition-opacity duration-1000 ease-in-out ${
+              activeSlide === index ? 'opacity-100' : 'opacity-0'
+            }`}
+          >
+            <img 
+              src={img} 
+              alt={`Slide ${index + 1}`} 
+              className="w-full h-full object-cover object-center scale-105 animate-slow-zoom" 
+            />
+            {/* Text Readability Overlay: Light black tint (30%) to contrast white text */}
+            <div className="absolute inset-0 bg-black/30 pointer-events-none"></div>
+          </div>
+        ))}
+        
+        {/* Carousel Dots */}
+        <div className="absolute bottom-10 right-10 z-20 flex space-x-3">
+          {heroImages.map((_, index) => (
+            <button 
+              key={index}
+              onClick={() => setActiveSlide(index)}
+              className={`w-2 h-2 rounded-full transition-all duration-300 ${
+                activeSlide === index ? 'bg-vista-gold w-8' : 'bg-white opacity-50 hover:opacity-100'
+              }`}
+            />
+          ))}
+        </div>
+        
+        {/* Content Layer */}
+        <div className="relative z-10 h-full flex flex-col justify-center items-center text-center px-4 drop-shadow-md">
+            <h2 className="text-white text-lg md:text-xl uppercase tracking-[0.3em] mb-4 animate-fade-in-up">{t.hero.welcome}</h2>
+            <h1 className="text-5xl md:text-7xl lg:text-8xl font-serif text-white mb-8 leading-tight italic">
+                {t.hero.title_italic} <span className="not-italic">{t.hero.title_normal}</span>
+            </h1>
+            <p className="text-white max-w-2xl text-lg md:text-xl font-light mb-10 leading-relaxed opacity-90">
+                {t.hero.subtitle}
+            </p>
+            <div className="flex space-x-6">
+                <button className="px-8 py-4 bg-vista-gold text-white font-bold tracking-widest uppercase text-sm hover:bg-white hover:text-vista-darkblue transition-all duration-300 shadow-lg">
+                    {t.hero.btn_find}
+                </button>
+                <button className="px-8 py-4 border border-white text-white font-bold tracking-widest uppercase text-sm hover:bg-white hover:text-vista-darkblue transition-all duration-300 shadow-lg">
+                    {t.hero.btn_ships}
+                </button>
+            </div>
+        </div>
+
+        {/* Scroll Indicator */}
+        <div className="absolute bottom-10 left-1/2 -translate-x-1/2 animate-bounce hidden md:block">
+            <div className="w-[1px] h-16 bg-white opacity-50"></div>
+        </div>
+      </section>
+
+      {/* Featured Itineraries & Activity Banner */}
+      <section className="py-24 bg-white">
+        <div className="max-w-7xl mx-auto px-6">
+            <div className="flex flex-col md:flex-row justify-between items-end mb-12">
+                <div>
+                    <span className="text-vista-gold uppercase tracking-widest text-sm font-bold">{t.home.featured_sub}</span>
+                    <h2 className="text-4xl font-serif text-vista-darkblue mt-3">{t.home.featured_title}</h2>
+                </div>
+                <a href="#all-trips" className="hidden md:flex items-center text-vista-darkblue font-bold text-sm tracking-widest uppercase hover:text-vista-gold transition-colors mt-4 md:mt-0">
+                    {t.home.view_all} <ChevronRight size={16} className="ml-2" />
+                </a>
+            </div>
+
+            <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
+                {itineraries.map((item) => (
+                    <div key={item.id} className="group bg-white border border-gray-100 shadow-sm hover:shadow-xl transition-all duration-300 cursor-pointer">
+                        {/* Media Container: Renders Video if available, else Image */}
+                        <div className="relative overflow-hidden h-64">
+                            {item.video ? (
+                              <video 
+                                src={item.video} 
+                                poster={item.image}
+                                className="w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-700"
+                                autoPlay
+                                muted
+                                loop
+                                playsInline
+                              />
+                            ) : (
+                              <img 
+                                src={item.image} 
+                                alt={item.title} 
+                                className="w-full h-full object-cover transform group-hover:scale-105 transition-transform duration-700" 
+                              />
+                            )}
+                            <div className="absolute top-4 left-4 bg-white px-3 py-1 text-xs font-bold tracking-widest uppercase z-10 text-vista-darkblue">
+                                {item.days} {t.home.days}
+                            </div>
+                        </div>
+                        <div className="p-8">
+                            <div className="text-xs text-vista-darkblue/50 uppercase tracking-widest mb-2">{item.route}</div>
+                            <h3 className="text-2xl font-serif text-vista-darkblue mb-4 group-hover:text-vista-gold transition-colors">{item.title}</h3>
+                            <div className="flex justify-between items-center border-t border-vista-darkblue/10 pt-4">
+                                <span className="text-vista-gold font-serif italic text-xl">{item.price}</span>
+                                <span className="text-xs font-bold uppercase tracking-widest text-vista-darkblue/50 group-hover:text-vista-darkblue">{t.home.details}</span>
+                            </div>
+                        </div>
+                    </div>
+                ))}
+            </div>
+        </div>
+      </section>
+
+      {/* Banner / Activity Highlight (Customizable Dining) */}
+      <section className="relative py-32 flex items-center justify-center">
+         <div className="absolute inset-0 z-0">
+             <img src={dining.image} alt="Dining" className="w-full h-full object-cover brightness-50" />
+         </div>
+         <div className="relative z-10 text-center max-w-4xl px-6">
+             <h2 className="text-4xl md:text-5xl font-serif text-white italic mb-6">{dining.title}</h2>
+             <p className="text-white text-lg font-light leading-relaxed mb-8">
+                 {dining.description}
+             </p>
+             <button className="px-8 py-3 border border-white text-white font-bold tracking-widest uppercase text-xs hover:bg-white hover:text-vista-darkblue transition-all">
+                 {dining.buttonText}
+             </button>
+         </div>
+      </section>
+
+      {/* Ship Gallery (Rolling Display) */}
+      <section className="py-24 bg-white overflow-hidden">
+        <div className="max-w-7xl mx-auto px-6 mb-12 text-center">
+            <span className="text-vista-gold uppercase tracking-widest text-sm font-bold">{t.home.fleet_sub}</span>
+            <h2 className="text-4xl font-serif text-vista-darkblue mt-3">{t.home.fleet_title}</h2>
+        </div>
+
+        <div className="relative w-full overflow-x-auto no-scrollbar pb-10">
+            <div className="flex space-x-6 px-6 md:px-[calc((100vw-80rem)/2)]">
+                {ships.map((ship) => (
+                    <div key={ship.id} className="min-w-[85vw] md:min-w-[600px] relative group cursor-pointer overflow-hidden">
+                        <div className="aspect-[16/9] overflow-hidden">
+                            {/* Removed grayscale, added scale transform */}
+                            <img src={ship.image} alt={ship.name} className="w-full h-full object-cover transition-transform duration-1000 ease-out group-hover:scale-105" />
+                        </div>
+                        <div className="absolute bottom-0 left-0 bg-white p-6 md:p-8 max-w-md shadow-lg transform translate-y-4 group-hover:translate-y-0 transition-transform duration-500">
+                            <h3 className="text-3xl font-serif text-vista-darkblue mb-2">{ship.name}</h3>
+                            <p className="text-vista-darkblue/70 mb-4 line-clamp-2">{ship.description}</p>
+                            <span className="text-xs font-bold uppercase tracking-widest text-vista-gold border-b border-vista-gold pb-1">{t.home.discover_ship}</span>
+                        </div>
+                    </div>
+                ))}
+            </div>
+        </div>
+      </section>
+
+      {/* Video Section (Customizable) - Background Color Changed to Teal */}
+      <section className="py-24 bg-vista-teal text-white">
+          <div className="max-w-7xl mx-auto px-6 flex flex-col md:flex-row items-center">
+              <div className="md:w-1/2 mb-10 md:mb-0 md:pr-12">
+                  <span className="text-vista-gold uppercase tracking-widest text-sm font-bold">{t.home.exp_sub}</span>
+                  <h2 className="text-4xl font-serif mt-4 mb-6 leading-tight">
+                    {videoSection.title} <br/><i className="font-serif">{videoSection.titleItalic}</i>
+                  </h2>
+                  <p className="text-white/80 font-light leading-relaxed mb-8">
+                      {videoSection.description}
+                  </p>
+                  <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
+                      {features.map((feat, idx) => (
+                          <div key={idx} className="flex items-start">
+                              <div className="w-2 h-2 bg-vista-gold mt-2 mr-3 rounded-full"></div>
+                              <div>
+                                  <h4 className="font-serif text-lg mb-1">{feat.title}</h4>
+                                  <p className="text-sm text-white/60">{feat.description}</p>
+                              </div>
+                          </div>
+                      ))}
+                  </div>
+              </div>
+              <div className="md:w-1/2 w-full relative group cursor-pointer">
+                  {/* Video Thumbnail Placeholder */}
+                  <div className="aspect-video w-full bg-black relative overflow-hidden shadow-2xl">
+                      <img src={videoSection.thumbnail} className="w-full h-full object-cover opacity-60 group-hover:opacity-40 transition-opacity" alt="Video Thumbnail" />
+                      <div className="absolute inset-0 flex items-center justify-center">
+                          <div className="w-20 h-20 rounded-full border border-white flex items-center justify-center group-hover:bg-white group-hover:text-vista-darkblue transition-all duration-300">
+                              <Play size={32} fill="currentColor" />
+                          </div>
+                      </div>
+                  </div>
+              </div>
+          </div>
+      </section>
+
+      <Footer />
+    </div>
+  );
+};
+
+export default Home;

+ 240 - 0
src/pages/Ships.tsx

@@ -0,0 +1,240 @@
+import React, { useEffect } from 'react';
+import Navbar from '@/src/components/Navbar.tsx';
+import Sidebar from '@/src/components/Sidebar.tsx';
+import Footer from '@/src/components/Footer.tsx';
+import { CONTENT } from '../constants.ts';
+import { useLanguage } from '@/src/contexts/LanguageContext.tsx';
+import { useTheme } from '@/src/contexts/ThemeContext.tsx';
+import { useLocation, Link } from 'react-router-dom';
+import { ArrowRight, Anchor, Star, ChevronRight } from 'lucide-react';
+
+const Ships: React.FC = () => {
+  const { language } = useLanguage();
+  const { shipsPageImages } = useTheme();
+  // We explicitly type 'any' here or assume CONTENT structure is updated because
+  // TS might not immediately pick up the new keys added to constants without a full types reload in this context.
+  // In a real app, types.ts should be updated, but here we access the object properties.
+  const t = CONTENT[language].shipsPage as any;
+  const location = useLocation();
+
+  // Scroll to section on load or location change
+  useEffect(() => {
+    const params = new URLSearchParams(location.search);
+    const section = params.get('section');
+
+    // Always scroll to top first
+    if (!section) {
+        window.scrollTo({ top: 0, behavior: 'smooth' });
+    } else {
+        const element = document.getElementById(section);
+        if (element) {
+            // Small delay to ensure render
+            setTimeout(() => {
+                element.scrollIntoView({ behavior: 'smooth' });
+            }, 300);
+        }
+    }
+  }, [location]);
+
+  return (
+    <div className="min-h-screen bg-white font-sans text-vista-darkblue overflow-x-hidden">
+      <Navbar />
+      <Sidebar />
+
+      {/* Hero Section */}
+      <div className="relative h-[70vh] w-full bg-vista-darkblue flex items-center justify-center overflow-hidden">
+         <div className="absolute inset-0 bg-black/40 z-10"></div>
+         <img
+            src="https://images.unsplash.com/photo-1548291616-3c0f5f743538?q=80&w=1920&auto=format&fit=crop"
+            alt="Ships Hero"
+            className="absolute inset-0 w-full h-full object-cover animate-slow-zoom"
+         />
+         <div className="relative z-20 text-center text-white px-4 animate-fade-in-up">
+             <span className="block text-sm md:text-base tracking-[0.3em] uppercase mb-4 text-vista-gold">{t.hero_sub}</span>
+             <h1 className="text-5xl md:text-8xl font-serif italic mb-6">{CONTENT[language].nav.menu[1].title}</h1>
+             <p className="max-w-xl mx-auto text-white/80 font-light text-lg">
+                 {t.hero_desc}
+             </p>
+         </div>
+      </div>
+
+      {/* Section 1: Vista Series Intro */}
+      <section id="series" className="py-24 max-w-7xl mx-auto px-6">
+         <div className="flex flex-col lg:flex-row gap-16 items-start">
+            {/* Left: Text Content */}
+            <div className="lg:w-3/5 space-y-12">
+                <div>
+                    <h2 className="text-4xl md:text-5xl font-serif text-vista-darkblue mb-8 leading-tight">
+                        {t.intro.title}
+                    </h2>
+                    <div className="w-20 h-1 bg-vista-gold mb-8"></div>
+                    <p className="text-xl font-light leading-relaxed text-vista-darkblue/80">
+                        {t.intro.desc}
+                    </p>
+                </div>
+
+                <div className="space-y-12">
+                    {t.intro.points.map((point: any, index: number) => (
+                        <div key={index} className="flex gap-8 group">
+                            <span className="text-6xl font-serif text-vista-gold/20 font-bold -mt-2 group-hover:text-vista-gold/40 transition-colors duration-500">
+                                0{index + 1}
+                            </span>
+                            <div>
+                                <h3 className="text-xl font-bold text-vista-darkblue mb-3 uppercase tracking-wider">{point.title}</h3>
+                                <p className="text-vista-darkblue/70 leading-relaxed">{point.desc}</p>
+                            </div>
+                        </div>
+                    ))}
+                </div>
+            </div>
+
+            {/* Right: Navigation Cards */}
+            <div className="lg:w-2/5 flex flex-col gap-8 sticky top-24">
+                <Link to="/ships?section=lanyue" className="group relative h-72 overflow-hidden block shadow-2xl rounded-sm">
+                    <img
+                        src={shipsPageImages.lanyue[0]}
+                        alt="Lanyue"
+                        className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
+                    />
+                    <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-90 transition-opacity"></div>
+                    <div className="absolute bottom-8 left-8 text-white">
+                        <span className="text-xs uppercase tracking-widest block mb-2 text-vista-gold">{t.now_sailing}</span>
+                        <span className="text-3xl font-serif italic flex items-center gap-4">
+                             {t.lanyue.title} <ArrowRight size={24} className="transform group-hover:translate-x-4 transition-transform text-vista-gold" />
+                        </span>
+                    </div>
+                </Link>
+
+                <Link to="/ships?section=aurora" className="group relative h-72 overflow-hidden block shadow-2xl rounded-sm">
+                     <img
+                        src={shipsPageImages.aurora}
+                        alt="Aurora"
+                        className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110 grayscale group-hover:grayscale-0"
+                    />
+                    <div className="absolute inset-0 bg-gradient-to-t from-black/80 via-transparent to-transparent opacity-90 transition-opacity"></div>
+                    <div className="absolute bottom-8 left-8 text-white">
+                        <span className="text-xs uppercase tracking-widest block mb-2 text-vista-gold">{t.coming_soon}</span>
+                         <span className="text-3xl font-serif italic flex items-center gap-4">
+                             {t.aurora.title} <ArrowRight size={24} className="transform group-hover:translate-x-4 transition-transform text-vista-gold" />
+                        </span>
+                    </div>
+                </Link>
+            </div>
+         </div>
+      </section>
+
+      {/* Divider */}
+      <div className="w-full h-px bg-vista-darkblue/10 max-w-7xl mx-auto"></div>
+
+      {/* Section 2: Vista Lanyue Details */}
+      <section id="lanyue" className="py-32 bg-vista-gray relative">
+          {/* Background Decor */}
+          <div className="absolute top-0 right-0 w-1/3 h-full bg-white/50 skew-x-12 hidden lg:block pointer-events-none"></div>
+
+          <div className="max-w-7xl mx-auto px-6 relative z-10">
+              <div className="text-center mb-20 max-w-4xl mx-auto">
+                  <div className="flex items-center justify-center gap-4 mb-6">
+                      <div className="h-px w-12 bg-vista-gold"></div>
+                      <span className="text-vista-gold uppercase tracking-widest text-sm font-bold">{t.flagship_label}</span>
+                      <div className="h-px w-12 bg-vista-gold"></div>
+                  </div>
+                  <h2 className="text-5xl md:text-7xl font-serif text-vista-darkblue mb-10">{t.lanyue.title}</h2>
+                  <p className="text-vista-darkblue/70 text-lg md:text-xl leading-relaxed font-light">
+                      {t.lanyue.desc}
+                  </p>
+
+                  <div className="mt-12 flex justify-center gap-6">
+                      <div className="flex flex-col items-center gap-2 px-6 border-r border-vista-darkblue/10">
+                          <Anchor size={24} className="text-vista-gold" />
+                          <span className="text-xs uppercase tracking-widest font-bold">{t.stats_tons}</span>
+                      </div>
+                      <div className="flex flex-col items-center gap-2 px-6 border-r border-vista-darkblue/10">
+                          <Star size={24} className="text-vista-gold" />
+                          <span className="text-xs uppercase tracking-widest font-bold">{t.stats_stars}</span>
+                      </div>
+                       <div className="flex flex-col items-center gap-2 px-6">
+                          <span className="text-xl font-serif font-bold text-vista-gold">1:1</span>
+                          <span className="text-xs uppercase tracking-widest font-bold">{t.stats_ratio}</span>
+                      </div>
+                  </div>
+              </div>
+
+              {/* Lanyue Image Grid (Masonry style) */}
+              <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-12 gap-4 auto-rows-[200px]">
+                  {/* Large Hero Image */}
+                  <div className="lg:col-span-8 lg:row-span-2 relative group overflow-hidden shadow-lg">
+                      <img
+                        src={shipsPageImages.lanyue[0]}
+                        alt="Lanyue Exterior"
+                        className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-105"
+                      />
+                  </div>
+                  {/* Smaller Detail Images */}
+                  <div className="lg:col-span-4 lg:row-span-1 relative group overflow-hidden shadow-lg">
+                       <img
+                        src={shipsPageImages.lanyue[1]}
+                        alt="Lanyue Interior"
+                        className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-105"
+                      />
+                  </div>
+                  <div className="lg:col-span-4 lg:row-span-1 relative group overflow-hidden shadow-lg">
+                       <img
+                        src={shipsPageImages.lanyue[2]}
+                        alt="Lanyue Deck"
+                        className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-105"
+                      />
+                  </div>
+                  <div className="lg:col-span-12 lg:row-span-1 relative group overflow-hidden shadow-lg md:hidden lg:block">
+                       <img
+                        src={shipsPageImages.lanyue[3]}
+                        alt="Lanyue Wide"
+                        className="w-full h-full object-cover transition-transform duration-1000 group-hover:scale-105"
+                      />
+                  </div>
+              </div>
+
+              <div className="mt-16 text-center">
+                  <Link to="/itineraries" className="inline-flex items-center gap-3 px-10 py-4 bg-vista-darkblue text-white uppercase tracking-widest text-xs font-bold hover:bg-vista-gold transition-colors duration-300">
+                      {t.view_itineraries} <ChevronRight size={16} />
+                  </Link>
+              </div>
+          </div>
+      </section>
+
+      {/* Section 3: Vista Aurora Teaser */}
+      <section id="aurora" className="relative h-screen flex items-center justify-center overflow-hidden bg-black">
+          <div className="absolute inset-0 opacity-70">
+               <img
+                 src={shipsPageImages.aurora}
+                 alt="Aurora"
+                 className="w-full h-full object-cover animate-slow-zoom"
+               />
+               <div className="absolute inset-0 bg-gradient-to-b from-black via-transparent to-black"></div>
+          </div>
+
+          <div className="relative z-10 text-center text-white px-6 max-w-4xl">
+               <span className="inline-block border border-white/30 px-4 py-1 rounded-full text-xs uppercase tracking-widest mb-8 backdrop-blur-sm">{t.future_label}</span>
+               <h2 className="text-6xl md:text-9xl font-serif italic mb-8 bg-clip-text text-transparent bg-gradient-to-r from-white via-white to-white/50">
+                   {t.aurora.title}
+               </h2>
+               <p className="text-xl md:text-2xl font-light tracking-wide mb-12 text-white/90">
+                   {t.aurora.desc}
+               </p>
+
+               <div className="flex flex-col md:flex-row items-center justify-center gap-6">
+                   <button className="px-10 py-4 bg-white text-black font-bold uppercase text-xs tracking-[0.2em] hover:bg-vista-gold hover:text-white transition-colors">
+                       {t.coming_soon}
+                   </button>
+                   <button className="px-10 py-4 border border-white/30 text-white font-bold uppercase text-xs tracking-[0.2em] hover:bg-white/10 transition-colors backdrop-blur-sm">
+                       {t.waitlist_btn}
+                   </button>
+               </div>
+          </div>
+      </section>
+
+      <Footer />
+    </div>
+  );
+};
+
+export default Ships;

+ 56 - 0
src/types.ts

@@ -0,0 +1,56 @@
+
+export interface SubMenuItem {
+  title: string;
+  link: string;
+}
+
+export interface MenuItem {
+  title: string;
+  link: string;
+  submenu?: SubMenuItem[];
+}
+
+export interface CruiseShip {
+  id: string;
+  name: string;
+  description: string;
+  image: string;
+}
+
+export interface Itinerary {
+  id: string;
+  title: string;
+  days: number;
+  price: string;
+  image: string;
+  video?: string; // Added video support
+  route: string;
+}
+
+export interface Feature {
+  title: string;
+  description: string;
+  icon: string;
+}
+
+export interface DiningSection {
+  title: string;
+  description: string;
+  buttonText: string;
+  image: string;
+}
+
+export interface VideoSectionContent {
+  title: string;
+  titleItalic: string;
+  description: string;
+  thumbnail: string;
+}
+
+// New Interface for Ships Page Images
+export interface ShipsPageImages {
+  lanyue: string[]; // Expecting 4 images
+  aurora: string;
+}
+
+export type Language = 'zh' | 'en';

+ 29 - 0
tsconfig.json

@@ -0,0 +1,29 @@
+{
+  "compilerOptions": {
+    "target": "ES2022",
+    "experimentalDecorators": true,
+    "useDefineForClassFields": false,
+    "module": "ESNext",
+    "lib": [
+      "ES2022",
+      "DOM",
+      "DOM.Iterable"
+    ],
+    "skipLibCheck": true,
+    "types": [
+      "node"
+    ],
+    "moduleResolution": "bundler",
+    "isolatedModules": true,
+    "moduleDetection": "force",
+    "allowJs": true,
+    "jsx": "react-jsx",
+    "paths": {
+      "@/*": [
+        "./*"
+      ]
+    },
+    "allowImportingTsExtensions": true,
+    "noEmit": true
+  }
+}

+ 56 - 0
types.ts

@@ -0,0 +1,56 @@
+
+export interface SubMenuItem {
+  title: string;
+  link: string;
+}
+
+export interface MenuItem {
+  title: string;
+  link: string;
+  submenu?: SubMenuItem[];
+}
+
+export interface CruiseShip {
+  id: string;
+  name: string;
+  description: string;
+  image: string;
+}
+
+export interface Itinerary {
+  id: string;
+  title: string;
+  days: number;
+  price: string;
+  image: string;
+  video?: string; // Added video support
+  route: string;
+}
+
+export interface Feature {
+  title: string;
+  description: string;
+  icon: string;
+}
+
+export interface DiningSection {
+  title: string;
+  description: string;
+  buttonText: string;
+  image: string;
+}
+
+export interface VideoSectionContent {
+  title: string;
+  titleItalic: string;
+  description: string;
+  thumbnail: string;
+}
+
+// New Interface for Ships Page Images
+export interface ShipsPageImages {
+  lanyue: string[]; // Expecting 4 images
+  aurora: string;
+}
+
+export type Language = 'zh' | 'en';

+ 26 - 0
vite-env.d.ts

@@ -0,0 +1,26 @@
+/// <reference types="vite/client" />
+
+declare module '*.png' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.jpg' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.jpeg' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.gif' {
+  const value: string;
+  export default value;
+}
+
+declare module '*.svg' {
+  const value: string;
+  export default value;
+}

+ 23 - 0
vite.config.ts

@@ -0,0 +1,23 @@
+import path from 'path';
+import { defineConfig, loadEnv } from 'vite';
+import react from '@vitejs/plugin-react';
+
+export default defineConfig(({ mode }) => {
+    const env = loadEnv(mode, '.', '');
+    return {
+      server: {
+        port: 3000,
+        host: '0.0.0.0',
+      },
+      plugins: [react()],
+      define: {
+        'process.env.API_KEY': JSON.stringify(env.GEMINI_API_KEY),
+        'process.env.GEMINI_API_KEY': JSON.stringify(env.GEMINI_API_KEY)
+      },
+      resolve: {
+        alias: {
+          '@': path.resolve(__dirname, '.'),
+        }
+      }
+    };
+});