Selaa lähdekoodia

Refactor: Rename OldHomeLayout to Letters and update footer links

bob 4 kuukautta sitten
vanhempi
sitoutus
d3704c1eca
20 muutettua tiedostoa jossa 2114 lisäystä ja 54 poistoa
  1. 18 0
      ccdw.xyz.conf
  2. 35 0
      check_nginx.sh
  3. 25 0
      check_node.sh
  4. 54 34
      deploy.sh
  5. 24 0
      explore_nginx.sh
  6. 28 0
      final_check.sh
  7. 71 0
      fix_and_deploy.sh
  8. 49 0
      fix_nginx_final.sh
  9. 3 0
      index.html
  10. 42 0
      retry_https.sh
  11. 250 0
      server/index.js
  12. 14 0
      server/package.json
  13. 606 0
      server/pnpm-lock.yaml
  14. 109 0
      setup_https_full.sh
  15. 11 4
      src/App.tsx
  16. 3 2
      src/Letters.tsx
  17. 171 0
      src/components/EmailSubscribe.tsx
  18. 26 14
      src/pages/NewHome.tsx
  19. 567 0
      src/pages/SimpleHome.tsx
  20. 8 0
      vite.config.ts

+ 18 - 0
ccdw.xyz.conf

@@ -0,0 +1,18 @@
+server {
+    listen 80;
+    server_name ccdw.xyz www.ccdw.xyz;
+    
+    access_log /www/wwwlogs/ccdw.xyz.log;
+    error_log /www/wwwlogs/ccdw.xyz.error.log;
+
+    location / {
+        proxy_pass http://127.0.0.1:3001;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade $http_upgrade;
+        proxy_set_header Connection 'upgrade';
+        proxy_set_header Host $host;
+        proxy_set_header X-Real-IP $remote_addr;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto $scheme;
+    }
+}

+ 35 - 0
check_nginx.sh

@@ -0,0 +1,35 @@
+#!/bin/bash
+SERVER_IP="47.253.147.187"
+SERVER_USER="root"
+SERVER_PASS="5H0FuZ:2s0q)Lx"
+
+# 使用 expect 自动登录并执行检查命令
+/usr/bin/expect <<EOF
+set timeout 60
+spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
+
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+
+expect "#"
+send "echo '=== SYSTEM INFO ==='\r"
+send "uname -a\r"
+
+send "echo '=== PORT 3001 (Node) ==='\r"
+send "netstat -tulpn | grep 3001\r"
+
+send "echo '=== PM2 STATUS ==='\r"
+send "pm2 list\r"
+
+send "echo '=== NGINX CONFIG TEST ==='\r"
+send "nginx -t\r"
+
+send "echo '=== FIND CCDW.XYZ CONFIG ==='\r"
+# 尝试在常见位置搜索域名配置
+send "find /etc/nginx /www/server/nginx /usr/local/nginx -name '*ccdw.xyz*' 2>/dev/null\r"
+
+send "exit\r"
+expect eof
+EOF

+ 25 - 0
check_node.sh

@@ -0,0 +1,25 @@
+#!/bin/bash
+SERVER_IP="47.253.147.187"
+SERVER_USER="root"
+SERVER_PASS="5H0FuZ:2s0q)Lx"
+
+/usr/bin/expect <<EOF
+set timeout 30
+spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
+
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+
+expect "#"
+send "echo '--- CHECKING NODEJS ---'\r"
+send "which node\r"
+send "which npm\r"
+send "ls -F /www/server/nodejs/ 2>/dev/null\r"
+send "find /www/server -name node -type f 2>/dev/null | grep bin/node\r"
+send "echo '--- CHECKING SYSTEM INFO AGAIN ---'\r"
+send "cat /etc/os-release\r"
+send "exit\r"
+expect eof
+EOF

+ 54 - 34
deploy.sh

@@ -4,38 +4,43 @@
 SERVER_IP="47.253.147.187"
 SERVER_USER="root"
 SERVER_PASS="5H0FuZ:2s0q)Lx"
-REMOTE_PARENT_DIR="/www/wwwroot/ccdw-website" # 项目父目录
+REMOTE_PARENT_DIR="/www/wwwroot/ccdw-website"
 
 # 颜色输出
 GREEN='\033[0;32m'
 NC='\033[0m'
 
-echo -e "${GREEN}开始部署流程...${NC}"
+echo -e "${GREEN}=== 开始部署流程 ===${NC}"
 
-# 1. 本地构建 (Vite Build)
-echo -e "${GREEN}正在执行本地构建 (npm run build)...${NC}"
+# 1. 前端构建
+echo -e "${GREEN}[1/5] 执行前端构建 (Vite Build)...${NC}"
 npm run build
-
 if [ $? -ne 0 ]; then
-    echo "构建失败,请检查错误信息"
+    echo "构建失败,请检查错误信息"
     exit 1
 fi
 
-# 2. 压缩构建产物
-echo -e "${GREEN}正在打包构建产物...${NC}"
-if [ -d "dist" ]; then
-    cd dist
-    # 打包所有文件到 release.tar.gz
-    tar -czf ../release.tar.gz .
-    cd ..
-    echo "打包完成: release.tar.gz"
-else
-    echo "错误: 未找到 dist 目录"
-    exit 1
-fi
+# 2. 打包文件
+echo -e "${GREEN}[2/5] 打包部署文件...${NC}"
+rm -rf .deploy_temp
+mkdir -p .deploy_temp
+# 复制前端产物
+cp -r dist .deploy_temp/
+# 复制后端代码
+cp -r server .deploy_temp/
+# 删除不需要的文件
+rm -rf .deploy_temp/server/node_modules 
+rm -rf .deploy_temp/server/.DS_Store
+rm -rf .deploy_temp/server/package-lock.json
+# 进入临时目录打包
+cd .deploy_temp
+tar -czf ../release.tar.gz .
+cd ..
+rm -rf .deploy_temp
+echo "✅ 打包完成: release.tar.gz"
 
-# 3. 上传文件 (使用 expect 自动输入密码)
-echo -e "${GREEN}正在上传文件到服务器...${NC}"
+# 3. 上传
+echo -e "${GREEN}[3/5] 上传文件到服务器...${NC}"
 /usr/bin/expect <<EOF
 set timeout 300
 spawn scp -o StrictHostKeyChecking=no release.tar.gz $SERVER_USER@$SERVER_IP:$REMOTE_PARENT_DIR/release.tar.gz
@@ -46,31 +51,46 @@ expect {
 expect eof
 EOF
 
-# 4. 服务器端解压部署
-echo -e "${GREEN}正在服务器上解压并部署...${NC}"
+# 4. 远程部署
+echo -e "${GREEN}[4/5] 服务器端部署...${NC}"
 /usr/bin/expect <<EOF
-set timeout 30
+set timeout 300
 spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
 expect {
     "password:" { send "$SERVER_PASS\r" }
     "yes/no" { send "yes\r"; exp_continue }
 }
 expect "#"
-# 进入目录
-send "mkdir -p $REMOTE_PARENT_DIR/public\r"
+
+# 准备目录
+send "mkdir -p $REMOTE_PARENT_DIR\r"
 send "cd $REMOTE_PARENT_DIR\r"
-# 解压覆盖到 public 目录
-send "tar -xzf release.tar.gz -C public\r"
-# 清理压缩包
+
+# 解压覆盖 (dist 和 server 目录)
+send "tar -xzf release.tar.gz\r"
 send "rm release.tar.gz\r"
-# 修正权限 (假设 web 用户为 www,如果不是宝塔面板可能需要调整)
-send "chown -R www:www public\r"
-send "chmod -R 755 public\r"
+
+# 安装/更新后端依赖
+send "cd server\r"
+send "echo 'Installing dependencies...'\r"
+send "npm install --omit=dev --registry=https://registry.npmmirror.com\r"
+
+# 检查 PM2 是否安装
+send "if ! command -v pm2 &> /dev/null; then npm install -g pm2; fi\r"
+
+# 重启服务
+# 如果没有运行则启动,如果运行了则重启
+send "pm2 describe ccdw-server > /dev/null && pm2 reload ccdw-server || pm2 start index.js --name ccdw-server\r"
+send "pm2 save\r"
+
 send "exit\r"
 expect eof
 EOF
 
-echo -e "${GREEN}部署完成! 请访问 https://ccdw.xyz${NC}"
-
-# 5. 清理本地压缩包
+# 5. 清理本地
 rm release.tar.gz
+
+echo -e "${GREEN}=== 部署成功! ===${NC}"
+echo -e "服务已启动在服务器的 3001 端口。"
+echo -e "Node 服务现在同时也托管了前端页面 (http://IP:3001)。"
+echo -e "如果您使用 Nginx,请配置反向代理将域名流量转发到 localhost:3001。"

+ 24 - 0
explore_nginx.sh

@@ -0,0 +1,24 @@
+#!/bin/bash
+SERVER_IP="47.253.147.187"
+SERVER_USER="root"
+SERVER_PASS="5H0FuZ:2s0q)Lx"
+
+/usr/bin/expect <<EOF
+set timeout 30
+spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
+
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+
+expect "#"
+send "ls -F /www/server/nginx/conf/\r"
+send "ls -F /www/server/nginx/conf/vhost/\r"
+send "ls -F /www/server/panel/vhost/nginx/ 2>/dev/null\r"
+send "echo '--- EXISTING CERTS ---'\r"
+send "find /www/server -name '*.pem' -o -name '*.crt' | head -n 10\r"
+send "cat /www/server/nginx/conf/nginx.conf\r"
+send "exit\r"
+expect eof
+EOF

+ 28 - 0
final_check.sh

@@ -0,0 +1,28 @@
+#!/bin/bash
+SERVER_IP="47.253.147.187"
+SERVER_USER="root"
+SERVER_PASS="5H0FuZ:2s0q)Lx"
+
+/usr/bin/expect <<EOF
+set timeout 30
+spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+expect "#"
+
+# 确保配置已移动
+send "if test -f /tmp/ccdw_full.conf && test -f /etc/letsencrypt/live/ccdw.xyz/fullchain.pem; then mv /tmp/ccdw_full.conf /www/server/panel/vhost/nginx/ccdw.xyz.conf; fi\r"
+
+# 强制重启 Nginx
+send "/etc/init.d/nginx restart\r"
+
+# 验证
+send "echo '--- VALIDATION ---'\r"
+send "curl -I https://ccdw.xyz\r"
+send "curl -I http://ccdw.xyz\r"
+
+send "exit\r"
+expect eof
+EOF

+ 71 - 0
fix_and_deploy.sh

@@ -0,0 +1,71 @@
+#!/bin/bash
+SERVER_IP="47.253.147.187"
+SERVER_USER="root"
+SERVER_PASS="5H0FuZ:2s0q)Lx"
+NODE_BIN="/www/server/nodejs/v22.16.0/bin"
+
+/usr/bin/expect <<EOF
+set timeout 180
+spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
+
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+
+expect "#"
+
+# 1. Link Node binaries
+send "echo '--- LINKING NODE BINARIES ---'\r"
+send "ln -sf $NODE_BIN/node /usr/bin/node\r"
+send "ln -sf $NODE_BIN/npm /usr/bin/npm\r"
+send "ln -sf $NODE_BIN/npx /usr/bin/npx\r"
+
+# 2. Finish backend deployment
+send "echo '--- FINISHING BACKEND DEPLOYMENT ---'\r"
+send "cd /www/wwwroot/ccdw-website/server\r"
+send "npm install --omit=dev --registry=https://registry.npmmirror.com\r"
+send "npm install -g pm2 --registry=https://registry.npmmirror.com\r"
+# Link PM2
+send "ln -sf $NODE_BIN/pm2 /usr/bin/pm2\r"
+
+# Start App
+send "echo '--- STARTING APP ---'\r"
+send "pm2 delete ccdw-server 2>/dev/null\r"
+send "pm2 start index.js --name ccdw-server\r"
+send "pm2 save\r"
+
+# 3. Create Nginx Config (HTTP)
+send "echo '--- CREATING NGINX CONFIG ---'\r"
+send "cat > /www/server/panel/vhost/nginx/ccdw.xyz.conf <<ENDCONF
+server {
+    listen 80;
+    server_name ccdw.xyz www.ccdw.xyz;
+    
+    access_log /www/wwwlogs/ccdw.xyz.log;
+    error_log /www/wwwlogs/ccdw.xyz.error.log;
+
+    location / {
+        proxy_pass http://127.0.0.1:3001;
+        proxy_set_header Host \\\$host;
+        proxy_set_header X-Real-IP \\\$remote_addr;
+        proxy_set_header X-Forwarded-For \\\$proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto \\\$scheme;
+    }
+}
+ENDCONF\r"
+
+# 4. Reload Nginx
+send "echo '--- RELOADING NGINX ---'\r"
+send "nginx -t\r"
+# 宝塔环境下 reload 命令
+send "/etc/init.d/nginx reload\r"
+
+# 5. Check URL access
+send "echo '--- CHECKING ACCESS ---'\r"
+send "curl -I http://127.0.0.1:3001\r"
+send "curl -I -H 'Host: ccdw.xyz' http://127.0.0.1\r"
+
+send "exit\r"
+expect eof
+EOF

+ 49 - 0
fix_nginx_final.sh

@@ -0,0 +1,49 @@
+#!/bin/bash
+SERVER_IP="47.253.147.187"
+SERVER_USER="root"
+SERVER_PASS="5H0FuZ:2s0q)Lx"
+
+# 1. Upload Nginx config
+/usr/bin/expect <<EOF
+set timeout 30
+spawn scp -o StrictHostKeyChecking=no ccdw.xyz.conf $SERVER_USER@$SERVER_IP:/www/server/panel/vhost/nginx/ccdw.xyz.conf
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+expect eof
+EOF
+
+# 2. SSH actions
+/usr/bin/expect <<EOF
+set timeout 60
+spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
+
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+
+expect "#"
+
+# Reload Nginx
+send "echo '--- RELOADING NGINX STATUS ---'\r"
+send "nginx -t\r"
+send "/etc/init.d/nginx reload\r"
+
+# Check Node App
+send "echo '--- CHECKING NODE APP ---'\r"
+send "cd /www/wwwroot/ccdw-website/server\r"
+# Force install production deps again just in case
+send "npm install --omit=dev --registry=https://registry.npmmirror.com\r"
+send "pm2 restart ccdw-server\r"
+send "sleep 3\r"
+send "pm2 list\r"
+send "pm2 logs ccdw-server --lines 20 --nostream\r"
+
+# Check ports again
+send "netstat -tulpn | grep 3001\r"
+
+send "exit\r"
+expect eof
+EOF

+ 3 - 0
index.html

@@ -7,6 +7,9 @@
     <meta name="description" content="CCDW - 纯粹的玩,一个探索新型工作模式和人类与智能体协作的创新社群。" />
     <meta name="keywords" content="纯粹的玩,CCDW,工作模式,智能体,人工智能,社群" />
     <title>CCDW - 纯粹的玩</title>
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
+    <link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;700&display=swap" rel="stylesheet">
   </head>
   <body>
     <div id="root"></div>

+ 42 - 0
retry_https.sh

@@ -0,0 +1,42 @@
+#!/bin/bash
+SERVER_IP="47.253.147.187"
+SERVER_USER="root"
+SERVER_PASS="5H0FuZ:2s0q)Lx"
+
+/usr/bin/expect <<EOF
+set timeout 120
+spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+expect "#"
+
+send "ls -l /etc/letsencrypt/live/ccdw.xyz/fullchain.pem\r"
+
+# 使用 test 命令代替 [] 避免 Tcl 解析错误
+send "if test -f /etc/letsencrypt/live/ccdw.xyz/fullchain.pem; then \
+    mv /tmp/ccdw_full.conf /www/server/panel/vhost/nginx/ccdw.xyz.conf; \
+    echo 'Certificate verified. Applying config...'; \
+else \
+    echo 'Certificate NOT found, trying install again...'; \
+    apt-get update && apt-get install -y certbot; \
+    /etc/init.d/nginx stop; \
+    killall nginx; \
+    certbot certonly --standalone -d ccdw.xyz -d www.ccdw.xyz --email bob.yuxinyang@gmail.com --agree-tos --non-interactive; \
+    if test -f /etc/letsencrypt/live/ccdw.xyz/fullchain.pem; then \
+        mv /tmp/ccdw_full.conf /www/server/panel/vhost/nginx/ccdw.xyz.conf; \
+    fi; \
+fi\r"
+
+send "echo '--- RESTARTING NGINX ---'\r"
+send "/etc/init.d/nginx start\r"
+send "/etc/init.d/nginx reload\r"
+send "nginx -t\r"
+
+send "echo '--- VERIFYING ---'\r"
+send "curl -I https://ccdw.xyz\r"
+
+send "exit\r"
+expect eof
+EOF

+ 250 - 0
server/index.js

@@ -0,0 +1,250 @@
+import express from 'express';
+import nodemailer from 'nodemailer';
+import cors from 'cors';
+
+const app = express();
+const PORT = process.env.PORT || 3001;
+
+// Middleware
+app.use(cors());
+app.use(express.json());
+
+// SMTP Configuration
+const smtpConfig = {
+    host: 'smtp-relay.brevo.com',
+    port: 587,
+    secure: false, // TLS
+    auth: {
+        user: '9e8314001@smtp-brevo.com',
+        pass: 'xsmtpsib-d4a7d55a9c3d79a2b4d8f5c17b0ac5e47530f0615e7e9945e9fdf97b90487c32-LXW7Hihe7Qb5J2tM'
+    }
+};
+
+// Create transporter
+const transporter = nodemailer.createTransport(smtpConfig);
+
+// Welcome email template
+const createWelcomeEmail = (customerEmail) => {
+    return {
+        from: '"CCDW" <bob.yuxinyang@gmail.com>',
+        to: customerEmail,
+        subject: '👋 欢迎来到 CCDW!我们很高兴认识您',
+        html: `
+<!DOCTYPE html>
+<html lang="zh-CN">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>欢迎来到 CCDW</title>
+</head>
+<body style="margin: 0; padding: 0; font-family: 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif; background-color: #0A192F;">
+    <table role="presentation" style="width: 100%; border-collapse: collapse;">
+        <tr>
+            <td align="center" style="padding: 40px 20px;">
+                <table role="presentation" style="max-width: 600px; width: 100%; border-collapse: collapse; background-color: #112240; border-radius: 16px; overflow: hidden; box-shadow: 0 4px 24px rgba(0,0,0,0.3);">
+                    
+                    <!-- Header -->
+                    <tr>
+                        <td style="padding: 40px 40px 20px; text-align: center; background: linear-gradient(135deg, #112240 0%, #0A192F 100%);">
+                            <h1 style="margin: 0; color: #64FFDA; font-size: 28px; font-weight: bold;">
+                                ✨ CCDW
+                            </h1>
+                            <p style="margin: 10px 0 0; color: #8892b0; font-size: 14px;">
+                                AI智能解决方案
+                            </p>
+                        </td>
+                    </tr>
+                    
+                    <!-- Main Content -->
+                    <tr>
+                        <td style="padding: 30px 40px;">
+                            <h2 style="margin: 0 0 20px; color: #e6f1ff; font-size: 22px; font-weight: 600;">
+                                您好!欢迎认识我们 👋
+                            </h2>
+                            
+                            <p style="margin: 0 0 20px; color: #a8b2d1; font-size: 16px; line-height: 1.8;">
+                                非常高兴收到您的来信!感谢您对 CCDW 的关注。
+                            </p>
+                            
+                            <p style="margin: 0 0 20px; color: #a8b2d1; font-size: 16px; line-height: 1.8;">
+                                我们是一家专注于 AI 智能化解决方案的公司,致力于帮助企业和个人提升工作效率,释放创造力。
+                            </p>
+                            
+                            <div style="background-color: #0A192F; border-left: 4px solid #64FFDA; padding: 20px; margin: 25px 0; border-radius: 0 8px 8px 0;">
+                                <p style="margin: 0; color: #64FFDA; font-size: 16px; font-weight: 600;">
+                                    🤔 请问您想了解什么呢?
+                                </p>
+                                <p style="margin: 10px 0 0; color: #8892b0; font-size: 14px; line-height: 1.6;">
+                                    无论是 AI 咨询、智能化转型、还是定制化开发需求,我们都很乐意与您交流探讨!
+                                </p>
+                            </div>
+                            
+                            <p style="margin: 0 0 20px; color: #a8b2d1; font-size: 16px; line-height: 1.8;">
+                                您可以直接回复此邮件,或通过以下方式联系我们:
+                            </p>
+                        </td>
+                    </tr>
+                    
+                    <!-- Contact Info -->
+                    <tr>
+                        <td style="padding: 0 40px 30px;">
+                            <table role="presentation" style="width: 100%; border-collapse: collapse; background-color: #0A192F; border-radius: 12px; overflow: hidden;">
+                                <tr>
+                                    <td style="padding: 25px;">
+                                        <h3 style="margin: 0 0 15px; color: #64FFDA; font-size: 16px; font-weight: 600;">
+                                            📞 联系方式
+                                        </h3>
+                                        <table role="presentation" style="border-collapse: collapse;">
+                                            <tr>
+                                                <td style="padding: 8px 0; color: #8892b0; font-size: 14px; width: 80px;">邮箱:</td>
+                                                <td style="padding: 8px 0;">
+                                                    <a href="mailto:bob@ccdw.xyz" style="color: #64FFDA; text-decoration: none; font-size: 14px;">bob@ccdw.xyz</a>
+                                                </td>
+                                            </tr>
+                                            <tr>
+                                                <td style="padding: 8px 0; color: #8892b0; font-size: 14px;">电话:</td>
+                                                <td style="padding: 8px 0;">
+                                                    <a href="tel:+8618602109196" style="color: #64FFDA; text-decoration: none; font-size: 14px;">186 0210 9196</a>
+                                                </td>
+                                            </tr>
+                                            <tr>
+                                                <td style="padding: 8px 0; color: #8892b0; font-size: 14px;">联系人:</td>
+                                                <td style="padding: 8px 0; color: #e6f1ff; font-size: 14px;">Bob</td>
+                                            </tr>
+                                        </table>
+                                    </td>
+                                </tr>
+                            </table>
+                        </td>
+                    </tr>
+                    
+                    <!-- Closing -->
+                    <tr>
+                        <td style="padding: 0 40px 40px;">
+                            <p style="margin: 0 0 15px; color: #a8b2d1; font-size: 16px; line-height: 1.8;">
+                                期待与您的交流! 🚀
+                            </p>
+                            <p style="margin: 0; color: #e6f1ff; font-size: 16px;">
+                                <strong>Bob</strong><br>
+                                <span style="color: #8892b0; font-size: 14px;">CCDW 团队</span>
+                            </p>
+                        </td>
+                    </tr>
+                    
+                    <!-- Footer -->
+                    <tr>
+                        <td style="padding: 20px 40px; background-color: #0A192F; border-top: 1px solid #233554; text-align: center;">
+                            <p style="margin: 0; color: #495670; font-size: 12px;">
+                                © 2026 CCDW - 释放AI的力量,创造无限可能
+                            </p>
+                        </td>
+                    </tr>
+                    
+                </table>
+            </td>
+        </tr>
+    </table>
+</body>
+</html>
+        `,
+        text: `
+您好!欢迎认识我们 👋
+
+非常高兴收到您的来信!感谢您对 CCDW 的关注。
+
+我们是一家专注于 AI 智能化解决方案的公司,致力于帮助企业和个人提升工作效率,释放创造力。
+
+🤔 请问您想了解什么呢?
+无论是 AI 咨询、智能化转型、还是定制化开发需求,我们都很乐意与您交流探讨!
+
+您可以直接回复此邮件,或通过以下方式联系我们:
+
+📞 联系方式
+邮箱:bob@ccdw.xyz
+电话:186 0210 9196
+联系人:Bob
+
+期待与您的交流! 🚀
+
+Bob
+CCDW 团队
+
+© 2026 CCDW - 释放AI的力量,创造无限可能
+        `
+    };
+};
+
+// Send welcome email endpoint
+app.post('/api/send-welcome-email', async (req, res) => {
+    try {
+        const { email } = req.body;
+
+        if (!email) {
+            return res.status(400).json({
+                success: false,
+                message: '请提供邮箱地址'
+            });
+        }
+
+        // Validate email format
+        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+        if (!emailRegex.test(email)) {
+            return res.status(400).json({
+                success: false,
+                message: '邮箱格式不正确'
+            });
+        }
+
+        const mailOptions = createWelcomeEmail(email);
+
+        await transporter.sendMail(mailOptions);
+
+        console.log(`✅ Welcome email sent to: ${email}`);
+
+        res.json({
+            success: true,
+            message: '欢迎邮件已发送!请查收您的邮箱。'
+        });
+
+    } catch (error) {
+        console.error('❌ Email sending failed:', error);
+        res.status(500).json({
+            success: false,
+            message: '邮件发送失败,请稍后重试。',
+            error: error.message
+        });
+    }
+});
+
+// Health check
+app.get('/api/health', (req, res) => {
+    res.json({ status: 'ok', timestamp: new Date().toISOString() });
+});
+
+// Serve static files in production
+// 假设部署结构:
+// /root
+//   /dist (前端构建产物)
+//   /server (后端代码)
+import path from 'path';
+import { fileURLToPath } from 'url';
+
+const __filename = fileURLToPath(import.meta.url);
+const __dirname = path.dirname(__filename);
+
+// 任何非 /api 开头的请求,都尝试服务静态文件
+// 注意:这应该放在 API 路由定义之后
+app.use(express.static(path.join(__dirname, '../dist')));
+
+// SPA Fallback: 所有未匹配的 GET 请求都返回 index.html
+app.get('*', (req, res) => {
+    if (req.path.startsWith('/api')) {
+        return res.status(404).json({ success: false, message: 'API endpoint not found' });
+    }
+    res.sendFile(path.join(__dirname, '../dist/index.html'));
+});
+
+// Start server
+app.listen(PORT, '0.0.0.0', () => {
+    console.log(`🚀 Email server running on port ${PORT}`);
+});

+ 14 - 0
server/package.json

@@ -0,0 +1,14 @@
+{
+    "name": "ccdw-email-server",
+    "version": "1.0.0",
+    "type": "module",
+    "scripts": {
+        "start": "node index.js",
+        "dev": "node --watch index.js"
+    },
+    "dependencies": {
+        "cors": "^2.8.5",
+        "express": "^4.21.2",
+        "nodemailer": "^6.9.16"
+    }
+}

+ 606 - 0
server/pnpm-lock.yaml

@@ -0,0 +1,606 @@
+lockfileVersion: '9.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+importers:
+
+  .:
+    dependencies:
+      cors:
+        specifier: ^2.8.5
+        version: 2.8.6
+      express:
+        specifier: ^4.21.2
+        version: 4.22.1
+      nodemailer:
+        specifier: ^6.9.16
+        version: 6.10.1
+
+packages:
+
+  accepts@1.3.8:
+    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+    engines: {node: '>= 0.6'}
+
+  array-flatten@1.1.1:
+    resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
+
+  body-parser@1.20.4:
+    resolution: {integrity: sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+  bytes@3.1.2:
+    resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+    engines: {node: '>= 0.8'}
+
+  call-bind-apply-helpers@1.0.2:
+    resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+    engines: {node: '>= 0.4'}
+
+  call-bound@1.0.4:
+    resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+    engines: {node: '>= 0.4'}
+
+  content-disposition@0.5.4:
+    resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
+    engines: {node: '>= 0.6'}
+
+  content-type@1.0.5:
+    resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==}
+    engines: {node: '>= 0.6'}
+
+  cookie-signature@1.0.7:
+    resolution: {integrity: sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==}
+
+  cookie@0.7.2:
+    resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==}
+    engines: {node: '>= 0.6'}
+
+  cors@2.8.6:
+    resolution: {integrity: sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==}
+    engines: {node: '>= 0.10'}
+
+  debug@2.6.9:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+
+  depd@2.0.0:
+    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+    engines: {node: '>= 0.8'}
+
+  destroy@1.2.0:
+    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+
+  dunder-proto@1.0.1:
+    resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+    engines: {node: '>= 0.4'}
+
+  ee-first@1.1.1:
+    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+
+  encodeurl@2.0.0:
+    resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==}
+    engines: {node: '>= 0.8'}
+
+  es-define-property@1.0.1:
+    resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+    engines: {node: '>= 0.4'}
+
+  es-errors@1.3.0:
+    resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+    engines: {node: '>= 0.4'}
+
+  es-object-atoms@1.1.1:
+    resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+    engines: {node: '>= 0.4'}
+
+  escape-html@1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+
+  etag@1.8.1:
+    resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+    engines: {node: '>= 0.6'}
+
+  express@4.22.1:
+    resolution: {integrity: sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==}
+    engines: {node: '>= 0.10.0'}
+
+  finalhandler@1.3.2:
+    resolution: {integrity: sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==}
+    engines: {node: '>= 0.8'}
+
+  forwarded@0.2.0:
+    resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+    engines: {node: '>= 0.6'}
+
+  fresh@0.5.2:
+    resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
+    engines: {node: '>= 0.6'}
+
+  function-bind@1.1.2:
+    resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+  get-intrinsic@1.3.0:
+    resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+    engines: {node: '>= 0.4'}
+
+  get-proto@1.0.1:
+    resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+    engines: {node: '>= 0.4'}
+
+  gopd@1.2.0:
+    resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+    engines: {node: '>= 0.4'}
+
+  has-symbols@1.1.0:
+    resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+    engines: {node: '>= 0.4'}
+
+  hasown@2.0.2:
+    resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
+    engines: {node: '>= 0.4'}
+
+  http-errors@2.0.1:
+    resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==}
+    engines: {node: '>= 0.8'}
+
+  iconv-lite@0.4.24:
+    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+    engines: {node: '>=0.10.0'}
+
+  inherits@2.0.4:
+    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+  ipaddr.js@1.9.1:
+    resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+    engines: {node: '>= 0.10'}
+
+  math-intrinsics@1.1.0:
+    resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+    engines: {node: '>= 0.4'}
+
+  media-typer@0.3.0:
+    resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
+    engines: {node: '>= 0.6'}
+
+  merge-descriptors@1.0.3:
+    resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==}
+
+  methods@1.1.2:
+    resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
+    engines: {node: '>= 0.6'}
+
+  mime-db@1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+
+  mime-types@2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+
+  mime@1.6.0:
+    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  ms@2.0.0:
+    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+
+  ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+  negotiator@0.6.3:
+    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+    engines: {node: '>= 0.6'}
+
+  nodemailer@6.10.1:
+    resolution: {integrity: sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA==}
+    engines: {node: '>=6.0.0'}
+
+  object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+
+  object-inspect@1.13.4:
+    resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+    engines: {node: '>= 0.4'}
+
+  on-finished@2.4.1:
+    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+    engines: {node: '>= 0.8'}
+
+  parseurl@1.3.3:
+    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+    engines: {node: '>= 0.8'}
+
+  path-to-regexp@0.1.12:
+    resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==}
+
+  proxy-addr@2.0.7:
+    resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+    engines: {node: '>= 0.10'}
+
+  qs@6.14.1:
+    resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==}
+    engines: {node: '>=0.6'}
+
+  range-parser@1.2.1:
+    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+    engines: {node: '>= 0.6'}
+
+  raw-body@2.5.3:
+    resolution: {integrity: sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==}
+    engines: {node: '>= 0.8'}
+
+  safe-buffer@5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+  safer-buffer@2.1.2:
+    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+  send@0.19.2:
+    resolution: {integrity: sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==}
+    engines: {node: '>= 0.8.0'}
+
+  serve-static@1.16.3:
+    resolution: {integrity: sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==}
+    engines: {node: '>= 0.8.0'}
+
+  setprototypeof@1.2.0:
+    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+
+  side-channel-list@1.0.0:
+    resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==}
+    engines: {node: '>= 0.4'}
+
+  side-channel-map@1.0.1:
+    resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+    engines: {node: '>= 0.4'}
+
+  side-channel-weakmap@1.0.2:
+    resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+    engines: {node: '>= 0.4'}
+
+  side-channel@1.1.0:
+    resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==}
+    engines: {node: '>= 0.4'}
+
+  statuses@2.0.2:
+    resolution: {integrity: sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==}
+    engines: {node: '>= 0.8'}
+
+  toidentifier@1.0.1:
+    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+    engines: {node: '>=0.6'}
+
+  type-is@1.6.18:
+    resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+    engines: {node: '>= 0.6'}
+
+  unpipe@1.0.0:
+    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+    engines: {node: '>= 0.8'}
+
+  utils-merge@1.0.1:
+    resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
+    engines: {node: '>= 0.4.0'}
+
+  vary@1.1.2:
+    resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+    engines: {node: '>= 0.8'}
+
+snapshots:
+
+  accepts@1.3.8:
+    dependencies:
+      mime-types: 2.1.35
+      negotiator: 0.6.3
+
+  array-flatten@1.1.1: {}
+
+  body-parser@1.20.4:
+    dependencies:
+      bytes: 3.1.2
+      content-type: 1.0.5
+      debug: 2.6.9
+      depd: 2.0.0
+      destroy: 1.2.0
+      http-errors: 2.0.1
+      iconv-lite: 0.4.24
+      on-finished: 2.4.1
+      qs: 6.14.1
+      raw-body: 2.5.3
+      type-is: 1.6.18
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  bytes@3.1.2: {}
+
+  call-bind-apply-helpers@1.0.2:
+    dependencies:
+      es-errors: 1.3.0
+      function-bind: 1.1.2
+
+  call-bound@1.0.4:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      get-intrinsic: 1.3.0
+
+  content-disposition@0.5.4:
+    dependencies:
+      safe-buffer: 5.2.1
+
+  content-type@1.0.5: {}
+
+  cookie-signature@1.0.7: {}
+
+  cookie@0.7.2: {}
+
+  cors@2.8.6:
+    dependencies:
+      object-assign: 4.1.1
+      vary: 1.1.2
+
+  debug@2.6.9:
+    dependencies:
+      ms: 2.0.0
+
+  depd@2.0.0: {}
+
+  destroy@1.2.0: {}
+
+  dunder-proto@1.0.1:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-errors: 1.3.0
+      gopd: 1.2.0
+
+  ee-first@1.1.1: {}
+
+  encodeurl@2.0.0: {}
+
+  es-define-property@1.0.1: {}
+
+  es-errors@1.3.0: {}
+
+  es-object-atoms@1.1.1:
+    dependencies:
+      es-errors: 1.3.0
+
+  escape-html@1.0.3: {}
+
+  etag@1.8.1: {}
+
+  express@4.22.1:
+    dependencies:
+      accepts: 1.3.8
+      array-flatten: 1.1.1
+      body-parser: 1.20.4
+      content-disposition: 0.5.4
+      content-type: 1.0.5
+      cookie: 0.7.2
+      cookie-signature: 1.0.7
+      debug: 2.6.9
+      depd: 2.0.0
+      encodeurl: 2.0.0
+      escape-html: 1.0.3
+      etag: 1.8.1
+      finalhandler: 1.3.2
+      fresh: 0.5.2
+      http-errors: 2.0.1
+      merge-descriptors: 1.0.3
+      methods: 1.1.2
+      on-finished: 2.4.1
+      parseurl: 1.3.3
+      path-to-regexp: 0.1.12
+      proxy-addr: 2.0.7
+      qs: 6.14.1
+      range-parser: 1.2.1
+      safe-buffer: 5.2.1
+      send: 0.19.2
+      serve-static: 1.16.3
+      setprototypeof: 1.2.0
+      statuses: 2.0.2
+      type-is: 1.6.18
+      utils-merge: 1.0.1
+      vary: 1.1.2
+    transitivePeerDependencies:
+      - supports-color
+
+  finalhandler@1.3.2:
+    dependencies:
+      debug: 2.6.9
+      encodeurl: 2.0.0
+      escape-html: 1.0.3
+      on-finished: 2.4.1
+      parseurl: 1.3.3
+      statuses: 2.0.2
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+
+  forwarded@0.2.0: {}
+
+  fresh@0.5.2: {}
+
+  function-bind@1.1.2: {}
+
+  get-intrinsic@1.3.0:
+    dependencies:
+      call-bind-apply-helpers: 1.0.2
+      es-define-property: 1.0.1
+      es-errors: 1.3.0
+      es-object-atoms: 1.1.1
+      function-bind: 1.1.2
+      get-proto: 1.0.1
+      gopd: 1.2.0
+      has-symbols: 1.1.0
+      hasown: 2.0.2
+      math-intrinsics: 1.1.0
+
+  get-proto@1.0.1:
+    dependencies:
+      dunder-proto: 1.0.1
+      es-object-atoms: 1.1.1
+
+  gopd@1.2.0: {}
+
+  has-symbols@1.1.0: {}
+
+  hasown@2.0.2:
+    dependencies:
+      function-bind: 1.1.2
+
+  http-errors@2.0.1:
+    dependencies:
+      depd: 2.0.0
+      inherits: 2.0.4
+      setprototypeof: 1.2.0
+      statuses: 2.0.2
+      toidentifier: 1.0.1
+
+  iconv-lite@0.4.24:
+    dependencies:
+      safer-buffer: 2.1.2
+
+  inherits@2.0.4: {}
+
+  ipaddr.js@1.9.1: {}
+
+  math-intrinsics@1.1.0: {}
+
+  media-typer@0.3.0: {}
+
+  merge-descriptors@1.0.3: {}
+
+  methods@1.1.2: {}
+
+  mime-db@1.52.0: {}
+
+  mime-types@2.1.35:
+    dependencies:
+      mime-db: 1.52.0
+
+  mime@1.6.0: {}
+
+  ms@2.0.0: {}
+
+  ms@2.1.3: {}
+
+  negotiator@0.6.3: {}
+
+  nodemailer@6.10.1: {}
+
+  object-assign@4.1.1: {}
+
+  object-inspect@1.13.4: {}
+
+  on-finished@2.4.1:
+    dependencies:
+      ee-first: 1.1.1
+
+  parseurl@1.3.3: {}
+
+  path-to-regexp@0.1.12: {}
+
+  proxy-addr@2.0.7:
+    dependencies:
+      forwarded: 0.2.0
+      ipaddr.js: 1.9.1
+
+  qs@6.14.1:
+    dependencies:
+      side-channel: 1.1.0
+
+  range-parser@1.2.1: {}
+
+  raw-body@2.5.3:
+    dependencies:
+      bytes: 3.1.2
+      http-errors: 2.0.1
+      iconv-lite: 0.4.24
+      unpipe: 1.0.0
+
+  safe-buffer@5.2.1: {}
+
+  safer-buffer@2.1.2: {}
+
+  send@0.19.2:
+    dependencies:
+      debug: 2.6.9
+      depd: 2.0.0
+      destroy: 1.2.0
+      encodeurl: 2.0.0
+      escape-html: 1.0.3
+      etag: 1.8.1
+      fresh: 0.5.2
+      http-errors: 2.0.1
+      mime: 1.6.0
+      ms: 2.1.3
+      on-finished: 2.4.1
+      range-parser: 1.2.1
+      statuses: 2.0.2
+    transitivePeerDependencies:
+      - supports-color
+
+  serve-static@1.16.3:
+    dependencies:
+      encodeurl: 2.0.0
+      escape-html: 1.0.3
+      parseurl: 1.3.3
+      send: 0.19.2
+    transitivePeerDependencies:
+      - supports-color
+
+  setprototypeof@1.2.0: {}
+
+  side-channel-list@1.0.0:
+    dependencies:
+      es-errors: 1.3.0
+      object-inspect: 1.13.4
+
+  side-channel-map@1.0.1:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      object-inspect: 1.13.4
+
+  side-channel-weakmap@1.0.2:
+    dependencies:
+      call-bound: 1.0.4
+      es-errors: 1.3.0
+      get-intrinsic: 1.3.0
+      object-inspect: 1.13.4
+      side-channel-map: 1.0.1
+
+  side-channel@1.1.0:
+    dependencies:
+      es-errors: 1.3.0
+      object-inspect: 1.13.4
+      side-channel-list: 1.0.0
+      side-channel-map: 1.0.1
+      side-channel-weakmap: 1.0.2
+
+  statuses@2.0.2: {}
+
+  toidentifier@1.0.1: {}
+
+  type-is@1.6.18:
+    dependencies:
+      media-typer: 0.3.0
+      mime-types: 2.1.35
+
+  unpipe@1.0.0: {}
+
+  utils-merge@1.0.1: {}
+
+  vary@1.1.2: {}

+ 109 - 0
setup_https_full.sh

@@ -0,0 +1,109 @@
+#!/bin/bash
+SERVER_IP="47.253.147.187"
+SERVER_USER="root"
+SERVER_PASS="5H0FuZ:2s0q)Lx"
+
+# 1. 编写完整的 Nginx HTTPS 配置文件 (预备)
+# 注意:证书路径暂时写死为 Let's Encrypt 标准路径
+cat > ccdw_full.conf <<EOF
+server {
+    listen 80;
+    server_name ccdw.xyz www.ccdw.xyz;
+    # 强制跳转到 HTTPS
+    return 301 https://\$host\$request_uri;
+}
+
+server {
+    listen 443 ssl;
+    server_name ccdw.xyz www.ccdw.xyz;
+
+    # 证书路径 (稍后由 certbot 生成)
+    ssl_certificate /etc/letsencrypt/live/ccdw.xyz/fullchain.pem;
+    ssl_certificate_key /etc/letsencrypt/live/ccdw.xyz/privkey.pem;
+
+    # SSL 优化配置
+    ssl_session_timeout 5m;
+    ssl_protocols TLSv1.2 TLSv1.3;
+    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
+    ssl_prefer_server_ciphers off;
+
+    # 日志
+    access_log /www/wwwlogs/ccdw.xyz.log;
+    error_log /www/wwwlogs/ccdw.xyz.error.log;
+
+    # 反向代理到 Node.js (3001)
+    location / {
+        proxy_pass http://127.0.0.1:3001;
+        proxy_http_version 1.1;
+        proxy_set_header Upgrade \$http_upgrade;
+        proxy_set_header Connection 'upgrade';
+        proxy_set_header Host \$host;
+        proxy_set_header X-Real-IP \$remote_addr;
+        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
+        proxy_set_header X-Forwarded-Proto \$scheme;
+    }
+}
+EOF
+
+# 2. 上传配置文件
+/usr/bin/expect <<EOF
+set timeout 30
+spawn scp -o StrictHostKeyChecking=no ccdw_full.conf $SERVER_USER@$SERVER_IP:/tmp/ccdw_full.conf
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+expect eof
+EOF
+
+# 3. SSH 执行:安装证书并应用配置
+/usr/bin/expect <<EOF
+set timeout 300
+spawn ssh -o StrictHostKeyChecking=no $SERVER_USER@$SERVER_IP
+
+expect {
+    "password:" { send "$SERVER_PASS\r" }
+    "yes/no" { send "yes\r"; exp_continue }
+}
+
+expect "#"
+
+send "echo '--- 1. INSTALLING CERTBOT ---'\r"
+# 尝试安装 certbot (Debian/Ubuntu)
+send "apt-get update && apt-get install -y certbot\r"
+
+send "echo '--- 2. STOPPING NGINX FOR AUTH ---'\r"
+# 停止 Nginx 以释放 80 端口供 Certbot 使用
+send "/etc/init.d/nginx stop\r"
+# 确保进程已杀掉
+send "killall nginx 2>/dev/null\r"
+
+send "echo '--- 3. REQUESTING CERTIFICATE ---'\r"
+# 申请证书
+send "certbot certonly --standalone -d ccdw.xyz -d www.ccdw.xyz --email bob.yuxinyang@gmail.com --agree-tos --non-interactive\r"
+
+send "echo '--- 4. APPLYING NGINX CONFIG ---'\r"
+# 检查证书是否生成成功
+send "if [ -f /etc/letsencrypt/live/ccdw.xyz/fullchain.pem ]; then \
+    echo 'Certificate verified. Applying config...'; \
+    mv /tmp/ccdw_full.conf /www/server/panel/vhost/nginx/ccdw.xyz.conf; \
+else \
+    echo 'ERROR: Certificate generation failed!'; \
+    rm /www/server/panel/vhost/nginx/ccdw.xyz.conf 2>/dev/null; \
+fi\r"
+
+send "echo '--- 5. RESTARTING NGINX ---'\r"
+send "/etc/init.d/nginx start\r"
+send "nginx -t\r"
+
+send "echo '--- 6. VERIFYING ---'\r"
+send "netstat -tulpn | grep nginx\r"
+# 发送一个测试请求看是否返回 200 (通过代理)
+send "curl -I https://ccdw.xyz\r"
+
+send "exit\r"
+expect eof
+EOF
+
+# 清理本地文件
+rm ccdw_full.conf

+ 11 - 4
src/App.tsx

@@ -1,13 +1,20 @@
 import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
-import NewHome from './NewHome';
-import OldHomeLayout from './OldHomeLayout';
+import Letters from './Letters';
+import NewHome from './pages/NewHome';
+import SimpleHome from './pages/SimpleHome';
 
 function App() {
     return (
         <Router>
             <Routes>
-                <Route path="/" element={<NewHome />} />
-                <Route path="/old-home" element={<OldHomeLayout />} />
+                {/* New Homepage: SimpleHome */}
+                <Route path="/" element={<SimpleHome />} />
+
+                {/* Letter to Players (Formerly the homepage) */}
+                <Route path="/intro" element={<NewHome />} />
+
+                {/* Letter to letters (Formerly /letter) */}
+                <Route path="/letters" element={<Letters />} />
             </Routes>
         </Router>
     );

+ 3 - 2
src/OldHomeLayout.tsx → src/Letters.tsx

@@ -1,7 +1,8 @@
+
 import HomePage from './pages/HomePage';
 import './App.css';
 
-function OldHomeLayout() {
+function Letters() {
   const currentYear = new Date().getFullYear();
 
   return (
@@ -26,4 +27,4 @@ function OldHomeLayout() {
   );
 }
 
-export default OldHomeLayout;
+export default Letters;

+ 171 - 0
src/components/EmailSubscribe.tsx

@@ -0,0 +1,171 @@
+import { useState } from 'react';
+import { Send, CheckCircle, Loader2, Mail, Sparkles } from 'lucide-react';
+
+interface EmailSubscribeProps {
+    onSuccess?: () => void;
+}
+
+const EmailSubscribe = ({ onSuccess }: EmailSubscribeProps) => {
+    const [email, setEmail] = useState('');
+    const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
+    const [message, setMessage] = useState('');
+
+    const handleSubmit = async (e: React.FormEvent) => {
+        e.preventDefault();
+
+        if (!email) {
+            setStatus('error');
+            setMessage('请输入您的邮箱地址');
+            return;
+        }
+
+        // Validate email format
+        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+        if (!emailRegex.test(email)) {
+            setStatus('error');
+            setMessage('请输入有效的邮箱地址');
+            return;
+        }
+
+        setStatus('loading');
+        setMessage('');
+
+        try {
+            const response = await fetch('/api/send-welcome-email', {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/json',
+                },
+                body: JSON.stringify({ email }),
+            });
+
+            const data = await response.json();
+
+            if (data.success) {
+                setStatus('success');
+                setMessage(data.message || '欢迎邮件已发送!请查收您的邮箱。');
+                setEmail('');
+                onSuccess?.();
+            } else {
+                setStatus('error');
+                setMessage(data.message || '发送失败,请稍后重试');
+            }
+        } catch (error) {
+            console.error('Error sending email:', error);
+            setStatus('error');
+            setMessage('网络错误,请检查连接后重试');
+        }
+    };
+
+    if (status === 'success') {
+        return (
+            <div className="w-full max-w-md mx-auto">
+                <div className="bg-[#112240] border border-[#64FFDA]/30 rounded-2xl p-8 text-center transform animate-pulse">
+                    <div className="w-16 h-16 bg-[#64FFDA]/20 rounded-full flex items-center justify-center mx-auto mb-4">
+                        <CheckCircle className="w-8 h-8 text-[#64FFDA]" />
+                    </div>
+                    <h3 className="text-xl font-bold text-white mb-2">邮件已发送! 🎉</h3>
+                    <p className="text-slate-400 text-sm">{message}</p>
+                    <button
+                        onClick={() => {
+                            setStatus('idle');
+                            setMessage('');
+                        }}
+                        className="mt-6 text-[#64FFDA] text-sm hover:underline"
+                    >
+                        发送另一封邮件
+                    </button>
+                </div>
+            </div>
+        );
+    }
+
+    return (
+        <div className="w-full max-w-md mx-auto">
+            <div className="bg-[#112240] border border-[#233554] rounded-2xl p-6 md:p-8 relative overflow-hidden">
+                {/* Decorative elements */}
+                <div className="absolute top-0 right-0 w-32 h-32 bg-[#64FFDA] rounded-full filter blur-[80px] opacity-10 pointer-events-none"></div>
+                <div className="absolute bottom-0 left-0 w-24 h-24 bg-purple-500 rounded-full filter blur-[60px] opacity-10 pointer-events-none"></div>
+
+                <div className="relative">
+                    {/* Header */}
+                    <div className="text-center mb-6">
+                        <div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-[#64FFDA]/10 border border-[#64FFDA]/30 text-[#64FFDA] text-xs font-medium mb-4">
+                            <Sparkles size={12} />
+                            开启您的 AI 之旅
+                        </div>
+                        <h3 className="text-xl md:text-2xl font-bold text-white mb-2">
+                            让我们保持联系 ✨
+                        </h3>
+                        <p className="text-slate-400 text-sm">
+                            留下您的邮箱,我们会发送一封欢迎信并了解您的需求
+                        </p>
+                    </div>
+
+                    {/* Form */}
+                    <form onSubmit={handleSubmit} className="space-y-4">
+                        <div className="relative">
+                            <div className="absolute inset-y-0 left-0 pl-4 flex items-center pointer-events-none">
+                                <Mail className="w-5 h-5 text-slate-500" />
+                            </div>
+                            <input
+                                type="email"
+                                value={email}
+                                onChange={(e) => {
+                                    setEmail(e.target.value);
+                                    if (status === 'error') {
+                                        setStatus('idle');
+                                        setMessage('');
+                                    }
+                                }}
+                                placeholder="请输入您的邮箱地址"
+                                className={`w-full pl-12 pr-4 py-4 bg-[#0A192F] border rounded-xl text-white placeholder-slate-500 focus:outline-none focus:ring-2 focus:ring-[#64FFDA]/50 transition-all ${status === 'error'
+                                    ? 'border-red-500/50 focus:ring-red-500/50'
+                                    : 'border-[#233554] focus:border-[#64FFDA]'
+                                    }`}
+                                disabled={status === 'loading'}
+                            />
+                        </div>
+
+                        {/* Error message */}
+                        {status === 'error' && message && (
+                            <p className="text-red-400 text-sm flex items-center gap-2">
+                                <span className="w-1.5 h-1.5 rounded-full bg-red-400"></span>
+                                {message}
+                            </p>
+                        )}
+
+                        {/* Submit button */}
+                        <button
+                            type="submit"
+                            disabled={status === 'loading'}
+                            className={`w-full py-4 rounded-xl font-bold flex items-center justify-center gap-2 transition-all ${status === 'loading'
+                                ? 'bg-[#64FFDA]/50 text-[#0A192F]/70 cursor-not-allowed'
+                                : 'bg-[#64FFDA] text-[#0A192F] hover:bg-[#64FFDA]/90 hover:shadow-lg hover:shadow-[#64FFDA]/20'
+                                }`}
+                        >
+                            {status === 'loading' ? (
+                                <>
+                                    <Loader2 className="w-5 h-5 animate-spin" />
+                                    发送中...
+                                </>
+                            ) : (
+                                <>
+                                    <Send className="w-5 h-5" />
+                                    发送欢迎邮件
+                                </>
+                            )}
+                        </button>
+                    </form>
+
+                    {/* Privacy note */}
+                    <p className="text-center text-slate-600 text-xs mt-4">
+                        我们尊重您的隐私,不会发送垃圾邮件
+                    </p>
+                </div>
+            </div>
+        </div>
+    );
+};
+
+export default EmailSubscribe;

+ 26 - 14
src/NewHome.tsx → src/pages/NewHome.tsx

@@ -10,8 +10,9 @@ import {
     Globe,
     ChevronDown
 } from 'lucide-react';
-import BreakoutGame from './components/BreakoutGame';
-import Logo from './components/Logo';
+import BreakoutGame from '../components/BreakoutGame';
+import Logo from '../components/Logo';
+import EmailSubscribe from '../components/EmailSubscribe';
 import { useTranslation } from 'react-i18next';
 
 const NewHome = () => {
@@ -327,21 +328,32 @@ const NewHome = () => {
 
             {/* CTA Section */}
             <section id="contact" className="py-20 px-6">
-                <div className="max-w-4xl mx-auto bg-gradient-to-r from-[#112240] to-[#0A192F] border border-[#233554] rounded-2xl p-8 md:p-12 text-center relative overflow-hidden">
+                <div className="max-w-4xl mx-auto bg-gradient-to-r from-[#112240] to-[#0A192F] border border-[#233554] rounded-2xl p-8 md:p-12 relative overflow-hidden">
                     <div className="absolute top-0 right-0 w-64 h-64 bg-[#64FFDA] rounded-full filter blur-[100px] opacity-5"></div>
 
-                    <h2 className="text-3xl font-bold text-white mb-4">{t('cta.title')}</h2>
-                    <p className="text-slate-400 mb-8 max-w-xl mx-auto">
-                        {t('cta.desc')}
-                    </p>
+                    <div className="grid md:grid-cols-2 gap-8 items-center">
+                        {/* Left side - Text */}
+                        <div className="text-center md:text-left">
+                            <h2 className="text-3xl font-bold text-white mb-4">{t('cta.title')}</h2>
+                            <p className="text-slate-400 mb-4">
+                                {t('cta.desc')}
+                            </p>
+                            <div className="flex flex-wrap gap-4 justify-center md:justify-start">
+                                <div className="flex items-center gap-2 text-sm text-slate-500">
+                                    <CheckCircle size={14} className="text-[#64FFDA]" />
+                                    免费咨询
+                                </div>
+                                <div className="flex items-center gap-2 text-sm text-slate-500">
+                                    <CheckCircle size={14} className="text-[#64FFDA]" />
+                                    快速响应
+                                </div>
+                            </div>
+                        </div>
 
-                    <div className="flex flex-col sm:flex-row justify-center gap-4">
-                        <button className="px-8 py-4 bg-[#64FFDA] text-[#0A192F] font-bold rounded hover:bg-[#64FFDA]/90 transition-all shadow-lg shadow-[#64FFDA]/20">
-                            {t('cta.btn_demo')}
-                        </button>
-                        <button className="px-8 py-4 border border-slate-600 text-white rounded hover:bg-white/5 transition-all">
-                            {t('cta.btn_contact')}
-                        </button>
+                        {/* Right side - Email Subscribe */}
+                        <div>
+                            <EmailSubscribe />
+                        </div>
                     </div>
                 </div>
             </section>

+ 567 - 0
src/pages/SimpleHome.tsx

@@ -0,0 +1,567 @@
+import React, { useState } from 'react';
+import {
+    Bot,
+    MapPin,
+    Cpu,
+    ArrowRight,
+    BarChart3,
+    Gamepad2,
+    Terminal,
+    Ticket,
+    Zap,
+    Menu,
+    X,
+    Sun,
+    Moon,
+    Feather,
+    BookOpen, // Used for the letter link
+    BrainCircuit
+} from 'lucide-react';
+import { useNavigate } from 'react-router-dom';
+
+// 多语言配置
+const translations = {
+    zh: {
+        nav: {
+            core: '业务板块',
+            consulting: '企业咨询',
+            tourism: '文旅运营',
+            dev: '开发者',
+            contact: '联系我们',
+        },
+        hero: {
+            quote: '"玩"意味着一种无拘无束的自由状态。',
+            quoteSub: '你会发现,每个真正懂得玩的人,他们玩的方式都是独一无二的,充满了自发的创造力与鲜明的个性。',
+            // Logic update: Focusing on the "Why" and the ecosystem
+            sectionTitle: '为了让大家能 "纯粹的玩",我们构建了三层业务生态',
+        },
+        cards: {
+            // Step 1: Liberate
+            consultingTitle: '第一步:解放时间',
+            consultingDesc: '首先,我们用AI为企业降本增效。只有把人从繁琐工作中解放出来,才有时间去玩。',
+            consultingLink: '查看企业AI方案',
+            // Step 2: Create Fun
+            tourismTitle: '第二步:创造乐趣',
+            tourismDesc: '然后,我们用技术重塑文旅体验。打卡地图与游戏化运营,让旅行回归玩的本质。',
+            tourismLink: '查看文旅案例',
+            // Step 3: Support Creation
+            devTitle: '第三步:支持创造',
+            devDesc: '最后,我们为AI一人公司提供基建。让每一个想玩的灵魂,拥有实现梦想的算力。',
+            devLink: '获取开发资源'
+        },
+        consulting: {
+            tag: 'Consulting',
+            title: '从繁杂中解放',
+            titleHighlight: '回归创造',
+            description: '我们为企业提供深度 IT 与 AI 咨询。这不是为了卷,而是为了降本提效,让团队从重复劳动中解脱出来。',
+            items: [
+                '企业私有知识库 (RAG)',
+                '自动化研报/公文撰写',
+                'AI 辅助决策系统',
+                '传统 IT 架构改造'
+            ],
+            reportTitle: '效率诊断',
+            reportDesc: 'AI 自动生成深度行业研究报告,将 3 天的撰写周期缩短至 15 分钟。'
+        },
+        tourism: {
+            tag: 'Cultural Tourism',
+            title: '打通文旅地产',
+            titleHighlight: '变现渠道',
+            description: '我们推出了一套以打卡地图、商户优惠券发放、抽奖游戏活动为核心的文旅运营解决方案。',
+            features: {
+                map: { title: '打卡地图', desc: 'LBS 定位解锁景点' },
+                coupon: { title: '优惠券', desc: '连接商户与游客' },
+                game: { title: '抽奖活动', desc: '提升游客留存率' },
+                data: { title: 'CPS分成', desc: '按效果获取收益' }
+            },
+            mockup: {
+                title: '西湖寻宝季',
+                gift: '🎁 待领取: 咖啡券',
+                btn: '扫码打卡'
+            }
+        },
+        dev: {
+            tag: 'Infrastructure',
+            title: '支持每一个',
+            titleHighlight: '超级个体',
+            description: '针对 AI 一人公司,我们提供基础设施服务,让你像玩乐高一样构建产品。',
+            features: {
+                token: { title: 'Token 分发', desc: 'OpenAI / Claude / DeepSeek 聚合接口' },
+                compute: { title: '弹性算力', desc: '高性价比 GPU 租赁' },
+                saas: { title: 'SaaS 套件', desc: '一键部署的创业脚手架' }
+            }
+        },
+        contact: {
+            title: '保持联系',
+            description: '既然来了,我会尽一切努力让你纯粹的玩。',
+            formTitle: '发送邮件',
+            placeholderName: '你的称呼',
+            placeholderEmail: '你的邮箱',
+            submit: '发送',
+            footerLetter: '阅读:给玩友们的一封信',
+            footerShareholder: '阅读:2025年5月29日给股东们的信',
+            footerNote: '"纯粹的玩"由几位股东共同创立,我作为现在的法人和大股东,拥有最终决策权。',
+            copyright: '© 2026 杭州纯粹的玩品牌科技有限公司'
+        }
+    },
+    en: {
+        nav: {
+            core: 'Sectors',
+            consulting: 'Consulting',
+            tourism: 'Tourism',
+            dev: 'Developers',
+            contact: 'Contact',
+        },
+        hero: {
+            quote: '"Play" means a state of unrestrained freedom.',
+            quoteSub: 'You will find that for everyone who truly knows how to play, their way of playing is unique, full of spontaneous creativity and distinct personality.',
+            sectionTitle: 'To enable "Pure Play" for everyone, we focus on three pillars',
+        },
+        cards: {
+            consultingTitle: 'Step 1: Liberate Time',
+            consultingDesc: 'First, we use AI to optimize enterprise workflows. Freeing people from tedious work gives them time to play.',
+            consultingLink: 'View Solutions',
+            tourismTitle: 'Step 2: Create Fun',
+            tourismDesc: 'Then, we reshape tourism with tech. Check-in maps and gamification bring the fun back to travel.',
+            tourismLink: 'View Cases',
+            devTitle: 'Step 3: Support Creation',
+            devDesc: 'Finally, we empower AI solopreneurs. Providing the infrastructure for every soul to build their dreams.',
+            devLink: 'Get Resources'
+        },
+        consulting: {
+            tag: 'Consulting',
+            title: 'Liberate from Chaos',
+            titleHighlight: 'Return to Creation',
+            description: 'We provide deep IT & AI consulting. Not to hustle harder, but to cut costs and increase efficiency, freeing teams from repetitive labor.',
+            items: [
+                'Private Knowledge Base (RAG)',
+                'Automated Research/Docs',
+                'AI Decision Support',
+                'Legacy IT Upgrade'
+            ],
+            reportTitle: 'Efficiency',
+            reportDesc: 'AI automatically generates in-depth research reports, reducing a 3-day writing cycle to 15 minutes.'
+        },
+        tourism: {
+            tag: 'Cultural Tourism',
+            title: 'Monetizing',
+            titleHighlight: 'Tourism Assets',
+            description: 'We launched a tourism operation solution centered on check-in maps, coupon distribution, and lucky draw games.',
+            features: {
+                map: { title: 'Check-in Map', desc: 'LBS unlock spots' },
+                coupon: { title: 'Coupons', desc: 'Connecting merchants' },
+                game: { title: 'Lucky Draw', desc: 'Boost retention' },
+                data: { title: 'CPS RevShare', desc: 'Earn by results' }
+            },
+            mockup: {
+                title: 'West Lake Hunt',
+                gift: '🎁 Reward: Coffee',
+                btn: 'Scan to Play'
+            }
+        },
+        dev: {
+            tag: 'Infrastructure',
+            title: 'Supporting Every',
+            titleHighlight: 'Super Individual',
+            description: 'For AI Solopreneurs, we provide infrastructure services, letting you build products like playing Lego.',
+            features: {
+                token: { title: 'Token Relay', desc: 'OpenAI / Claude / DeepSeek Aggregation' },
+                compute: { title: 'Elastic Compute', desc: 'Cost-effective GPU rental' },
+                saas: { title: 'SaaS Kit', desc: 'All-in-one scaffold' }
+            }
+        },
+        contact: {
+            title: 'Keep in Touch',
+            description: 'Since you are here, I will do my best to let you play purely.',
+            formTitle: 'Send Email',
+            placeholderName: 'Your Name',
+            placeholderEmail: 'Your Email',
+            submit: 'Send',
+            footerLetter: 'Read: A Letter to Players',
+            footerShareholder: 'Read: Letter to Shareholders (May 29, 2025)',
+            footerNote: '"Pure Play" was co-founded by several shareholders. As the current legal representative, I have the final decision-making power.',
+            copyright: '© 2026 Pure Play Technology Co., Ltd. Hangzhou.'
+        }
+    }
+};
+
+const SimpleHome = () => {
+    const [isMenuOpen, setIsMenuOpen] = useState(false);
+    // Default to Light Mode (isDark = false)
+    const [isDark, setIsDark] = useState(false);
+    const [lang, setLang] = useState<'zh' | 'en'>('zh');
+
+    const [email, setEmail] = useState('');
+    const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
+    const [message, setMessage] = useState('');
+
+    const navigate = useNavigate();
+
+    const t = translations[lang];
+
+    const scrollToSection = (id: string) => {
+        const element = document.getElementById(id);
+        if (element) {
+            element.scrollIntoView({ behavior: 'smooth' });
+            setIsMenuOpen(false);
+        }
+    };
+
+    const toggleTheme = () => setIsDark(!isDark);
+    const toggleLang = () => setLang(lang === 'zh' ? 'en' : 'zh');
+
+    const handleEmailSubmit = async () => {
+        if (!email) return;
+
+        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+        if (!emailRegex.test(email)) {
+            setStatus('error');
+            setMessage('请输入有效的邮箱地址');
+            return;
+        }
+
+        setStatus('loading');
+        setMessage('');
+
+        try {
+            const response = await fetch('/api/send-welcome-email', {
+                method: 'POST',
+                headers: {
+                    'Content-Type': 'application/json',
+                },
+                body: JSON.stringify({ email }),
+            });
+
+            const data = await response.json();
+
+            if (data.success) {
+                setStatus('success');
+                setMessage(data.message);
+                setEmail('');
+            } else {
+                setStatus('error');
+                setMessage(data.message || '发送失败,请稍后重试');
+            }
+        } catch (error) {
+            console.error('API Error:', error);
+            setStatus('error');
+            setMessage('网络错误,请稍后重试');
+        }
+    };
+
+    return (
+        <div className={`min-h-screen font-sans transition-colors duration-500 ${isDark ? 'bg-stone-900 text-stone-100' : 'bg-[#FAFAFA] text-stone-800'}`}>
+
+            {/* Navigation - Minimalist */}
+            <nav className={`fixed w-full z-50 transition-all duration-300 ${isDark ? 'bg-stone-900/90 border-b border-stone-800' : 'bg-white/90 border-b border-stone-100'} backdrop-blur-sm`}>
+                <div className="max-w-4xl mx-auto px-6 h-20 flex justify-between items-center">
+                    {/* Custom CCDW Logo */}
+                    <div className="flex items-center gap-3 cursor-pointer group" onClick={() => scrollToSection('hero')}>
+                        <div className="flex items-baseline font-bold text-3xl tracking-tighter" style={{ fontFamily: '"Nunito", sans-serif' }}>
+                            <span className="text-orange-400">CC</span>
+                            <span className="text-blue-500">D</span>
+                            <span className="text-cyan-400">W</span>
+                        </div>
+                        <div className={`h-6 w-px ${isDark ? 'bg-stone-700' : 'bg-stone-300'} mx-1`}></div>
+                        <span className={`text-sm tracking-widest ${isDark ? 'text-stone-400' : 'text-stone-500 group-hover:text-stone-800'} transition-colors`}>
+                            {lang === 'zh' ? '纯粹的玩' : 'Pure Play'}
+                        </span>
+                    </div>
+
+                    {/* Desktop Menu - Simple Links */}
+                    <div className="hidden md:flex items-center space-x-8">
+                        {[t.nav.consulting, t.nav.tourism, t.nav.dev].map((item, index) => {
+                            const ids = ['consulting', 'tourism', 'dev'];
+                            return (
+                                <button
+                                    key={index}
+                                    onClick={() => scrollToSection(ids[index])}
+                                    className={`text-sm font-medium hover:-translate-y-0.5 transition-transform ${isDark ? 'text-stone-400 hover:text-white' : 'text-stone-500 hover:text-stone-900'}`}
+                                >
+                                    {item}
+                                </button>
+                            );
+                        })}
+
+                        <div className="flex items-center gap-2 pl-4">
+                            <button onClick={toggleTheme} className={`p-2 rounded-full hover:bg-black/5 transition-colors ${isDark ? 'text-stone-400' : 'text-stone-400'}`}>
+                                {isDark ? <Sun className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
+                            </button>
+                            <button onClick={toggleLang} className={`text-xs font-bold px-2 py-1 rounded hover:bg-black/5 ${isDark ? 'text-stone-400' : 'text-stone-500'}`}>
+                                {lang === 'zh' ? 'EN' : '中'}
+                            </button>
+                        </div>
+                    </div>
+
+                    {/* Mobile Menu Toggle */}
+                    <div className="md:hidden">
+                        <button onClick={() => setIsMenuOpen(!isMenuOpen)} className={isDark ? 'text-stone-100' : 'text-stone-800'}>
+                            {isMenuOpen ? <X /> : <Menu />}
+                        </button>
+                    </div>
+                </div>
+
+                {/* Mobile Dropdown */}
+                {isMenuOpen && (
+                    <div className={`md:hidden absolute top-full left-0 w-full p-6 shadow-lg border-b ${isDark ? 'bg-stone-900 border-stone-800' : 'bg-white border-stone-100'}`}>
+                        {[t.nav.consulting, t.nav.tourism, t.nav.dev, t.nav.contact].map((item, index) => {
+                            const ids = ['consulting', 'tourism', 'dev', 'contact'];
+                            return (
+                                <button
+                                    key={index}
+                                    onClick={() => scrollToSection(ids[index])}
+                                    className={`block w-full text-left py-3 text-lg ${isDark ? 'text-stone-300' : 'text-stone-600'}`}
+                                >
+                                    {item}
+                                </button>
+                            );
+                        })}
+                    </div>
+                )}
+            </nav>
+
+            {/* Hero Section - Clean & Minimal */}
+            <section id="hero" className="pt-40 pb-20 px-6">
+                <div className="max-w-3xl mx-auto text-center mb-16">
+                    <p className={`text-xl md:text-2xl font-serif italic mb-8 leading-relaxed ${isDark ? 'text-stone-300' : 'text-stone-600'}`}>
+                        {t.hero.quote}
+                    </p>
+                    <p className={`text-sm md:text-base max-w-xl mx-auto leading-loose ${isDark ? 'text-stone-500' : 'text-stone-400'}`}>
+                        {t.hero.quoteSub}
+                    </p>
+                </div>
+
+                <div className="max-w-2xl mx-auto">
+                    <div className={`w-24 h-px mx-auto ${isDark ? 'bg-stone-800' : 'bg-stone-200'} mb-16`}></div>
+
+                    <div className="mb-12 text-center">
+                        <h2 className={`text-lg font-bold ${isDark ? 'text-stone-200' : 'text-stone-800'}`}>
+                            {t.hero.sectionTitle}
+                        </h2>
+                    </div>
+
+                    <div className="space-y-8">
+                        {/* Item 1 */}
+                        <div
+                            onClick={() => scrollToSection('consulting')}
+                            className={`group p-6 rounded-xl border transition-all cursor-pointer ${isDark ? 'bg-stone-800/50 border-stone-700 hover:border-blue-500/50' : 'bg-white border-stone-200 hover:border-blue-400 shadow-sm hover:shadow-md'}`}
+                        >
+                            <div className="flex items-center justify-between mb-2">
+                                <h3 className={`font-serif font-bold text-xl ${isDark ? 'text-stone-200' : 'text-stone-800'}`}>{t.cards.consultingTitle}</h3>
+                                <BrainCircuit className={`w-5 h-5 ${isDark ? 'text-blue-400' : 'text-blue-600'}`} />
+                            </div>
+                            <p className={`text-sm leading-relaxed mb-4 ${isDark ? 'text-stone-400' : 'text-stone-500'}`}>{t.cards.consultingDesc}</p>
+                            <span className={`text-xs font-bold uppercase tracking-wider flex items-center ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>
+                                {t.cards.consultingLink} <ArrowRight className="w-3 h-3 ml-1 group-hover:translate-x-1 transition-transform" />
+                            </span>
+                        </div>
+
+                        {/* Item 2 */}
+                        <div
+                            onClick={() => scrollToSection('tourism')}
+                            className={`group p-6 rounded-xl border transition-all cursor-pointer ${isDark ? 'bg-stone-800/50 border-stone-700 hover:border-orange-500/50' : 'bg-white border-stone-200 hover:border-orange-400 shadow-sm hover:shadow-md'}`}
+                        >
+                            <div className="flex items-center justify-between mb-2">
+                                <h3 className={`font-serif font-bold text-xl ${isDark ? 'text-stone-200' : 'text-stone-800'}`}>{t.cards.tourismTitle}</h3>
+                                <Ticket className={`w-5 h-5 ${isDark ? 'text-orange-400' : 'text-orange-500'}`} />
+                            </div>
+                            <p className={`text-sm leading-relaxed mb-4 ${isDark ? 'text-stone-400' : 'text-stone-500'}`}>{t.cards.tourismDesc}</p>
+                            <span className={`text-xs font-bold uppercase tracking-wider flex items-center ${isDark ? 'text-orange-400' : 'text-orange-600'}`}>
+                                {t.cards.tourismLink} <ArrowRight className="w-3 h-3 ml-1 group-hover:translate-x-1 transition-transform" />
+                            </span>
+                        </div>
+
+                        {/* Item 3 */}
+                        <div
+                            onClick={() => scrollToSection('dev')}
+                            className={`group p-6 rounded-xl border transition-all cursor-pointer ${isDark ? 'bg-stone-800/50 border-stone-700 hover:border-cyan-500/50' : 'bg-white border-stone-200 hover:border-cyan-400 shadow-sm hover:shadow-md'}`}
+                        >
+                            <div className="flex items-center justify-between mb-2">
+                                <h3 className={`font-serif font-bold text-xl ${isDark ? 'text-stone-200' : 'text-stone-800'}`}>{t.cards.devTitle}</h3>
+                                <Terminal className={`w-5 h-5 ${isDark ? 'text-cyan-400' : 'text-cyan-600'}`} />
+                            </div>
+                            <p className={`text-sm leading-relaxed mb-4 ${isDark ? 'text-stone-400' : 'text-stone-500'}`}>{t.cards.devDesc}</p>
+                            <span className={`text-xs font-bold uppercase tracking-wider flex items-center ${isDark ? 'text-cyan-400' : 'text-cyan-600'}`}>
+                                {t.cards.devLink} <ArrowRight className="w-3 h-3 ml-1 group-hover:translate-x-1 transition-transform" />
+                            </span>
+                        </div>
+                    </div>
+                </div>
+            </section>
+
+            {/* Detailed Sections - Minimalist Layout */}
+
+            {/* Consulting */}
+            <section id="consulting" className={`py-20 px-6 ${isDark ? 'bg-stone-900' : 'bg-white'}`}>
+                <div className="max-w-4xl mx-auto">
+                    <div className="grid md:grid-cols-2 gap-12 items-center">
+                        <div>
+                            <span className={`text-xs font-bold tracking-widest uppercase mb-4 block ${isDark ? 'text-blue-400' : 'text-blue-600'}`}>{t.consulting.tag}</span>
+                            <h2 className="text-3xl font-serif font-bold mb-6 leading-tight">
+                                {t.consulting.title} <br />
+                                <span className={`decoration-4 underline decoration-blue-200 ${isDark ? 'decoration-blue-900' : ''}`}>{t.consulting.titleHighlight}</span>
+                            </h2>
+                            <p className={`mb-8 leading-relaxed ${isDark ? 'text-stone-400' : 'text-stone-600'}`}>{t.consulting.description}</p>
+                            <ul className="space-y-3">
+                                {t.consulting.items.map((item, i) => (
+                                    <li key={i} className="flex items-center gap-3">
+                                        <div className={`w-1.5 h-1.5 rounded-full ${isDark ? 'bg-blue-400' : 'bg-blue-600'}`}></div>
+                                        <span className={`text-sm ${isDark ? 'text-stone-300' : 'text-stone-700'}`}>{item}</span>
+                                    </li>
+                                ))}
+                            </ul>
+                        </div>
+                        <div className={`p-8 rounded-lg border ${isDark ? 'bg-stone-800 border-stone-700' : 'bg-stone-50 border-stone-100'}`}>
+                            <BarChart3 className={`w-8 h-8 mb-4 ${isDark ? 'text-blue-400' : 'text-blue-600'}`} />
+                            <div className={`text-sm font-bold mb-2 ${isDark ? 'text-stone-200' : 'text-stone-800'}`}>{t.consulting.reportTitle}</div>
+                            <p className={`text-xs leading-relaxed ${isDark ? 'text-stone-400' : 'text-stone-500'}`}>{t.consulting.reportDesc}</p>
+                            <div className="mt-6 flex items-end gap-2">
+                                <div className="h-16 w-4 bg-stone-200 rounded-sm"></div>
+                                <div className="h-10 w-4 bg-stone-200 rounded-sm"></div>
+                                <div className={`h-24 w-4 rounded-sm ${isDark ? 'bg-blue-500' : 'bg-blue-600'}`}></div>
+                                <div className="h-14 w-4 bg-stone-200 rounded-sm"></div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </section>
+
+            {/* Tourism */}
+            <section id="tourism" className={`py-20 px-6 ${isDark ? 'bg-stone-800/30' : 'bg-stone-50'}`}>
+                <div className="max-w-4xl mx-auto">
+                    <div className="grid md:grid-cols-2 gap-12 items-center">
+                        <div className="order-2 md:order-1 grid grid-cols-2 gap-4">
+                            <div className={`p-4 rounded-lg border text-center ${isDark ? 'bg-stone-800 border-stone-700' : 'bg-white border-stone-200'}`}>
+                                <MapPin className={`w-6 h-6 mx-auto mb-2 ${isDark ? 'text-orange-400' : 'text-orange-500'}`} />
+                                <div className="font-bold text-sm mb-1">{t.tourism.features.map.title}</div>
+                                <div className="text-[10px] text-stone-400">{t.tourism.features.map.desc}</div>
+                            </div>
+                            <div className={`p-4 rounded-lg border text-center ${isDark ? 'bg-stone-800 border-stone-700' : 'bg-white border-stone-200'}`}>
+                                <Ticket className={`w-6 h-6 mx-auto mb-2 ${isDark ? 'text-orange-400' : 'text-orange-500'}`} />
+                                <div className="font-bold text-sm mb-1">{t.tourism.features.coupon.title}</div>
+                                <div className="text-[10px] text-stone-400">{t.tourism.features.coupon.desc}</div>
+                            </div>
+                            <div className={`p-4 rounded-lg border text-center ${isDark ? 'bg-stone-800 border-stone-700' : 'bg-white border-stone-200'}`}>
+                                <Gamepad2 className={`w-6 h-6 mx-auto mb-2 ${isDark ? 'text-orange-400' : 'text-orange-500'}`} />
+                                <div className="font-bold text-sm mb-1">{t.tourism.features.game.title}</div>
+                                <div className="text-[10px] text-stone-400">{t.tourism.features.game.desc}</div>
+                            </div>
+                            <div className={`p-4 rounded-lg border text-center ${isDark ? 'bg-stone-800 border-stone-700' : 'bg-white border-stone-200'}`}>
+                                <Bot className={`w-6 h-6 mx-auto mb-2 ${isDark ? 'text-orange-400' : 'text-orange-500'}`} />
+                                <div className="font-bold text-sm mb-1">{t.tourism.features.data.title}</div>
+                                <div className="text-[10px] text-stone-400">{t.tourism.features.data.desc}</div>
+                            </div>
+                        </div>
+                        <div className="order-1 md:order-2">
+                            <span className={`text-xs font-bold tracking-widest uppercase mb-4 block ${isDark ? 'text-orange-400' : 'text-orange-500'}`}>{t.tourism.tag}</span>
+                            <h2 className="text-3xl font-serif font-bold mb-6 leading-tight">
+                                {t.tourism.title} <br />
+                                <span className={`decoration-4 underline decoration-orange-200 ${isDark ? 'decoration-orange-900' : ''}`}>{t.tourism.titleHighlight}</span>
+                            </h2>
+                            <p className={`mb-8 leading-relaxed ${isDark ? 'text-stone-400' : 'text-stone-600'}`}>{t.tourism.description}</p>
+                        </div>
+                    </div>
+                </div>
+            </section>
+
+            {/* Dev Services */}
+            <section id="dev" className={`py-20 px-6 ${isDark ? 'bg-stone-900' : 'bg-white'}`}>
+                <div className="max-w-4xl mx-auto">
+                    <div className="text-center mb-16">
+                        <span className={`text-xs font-bold tracking-widest uppercase mb-4 block ${isDark ? 'text-cyan-400' : 'text-cyan-600'}`}>{t.dev.tag}</span>
+                        <h2 className="text-3xl font-serif font-bold mb-4">
+                            {t.dev.title} <span className={`decoration-4 underline decoration-cyan-200 ${isDark ? 'decoration-cyan-900' : ''}`}>{t.dev.titleHighlight}</span>
+                        </h2>
+                        <p className={`max-w-xl mx-auto ${isDark ? 'text-stone-400' : 'text-stone-600'}`}>{t.dev.description}</p>
+                    </div>
+
+                    <div className="grid md:grid-cols-3 gap-8">
+                        <div className={`p-6 border-t-2 ${isDark ? 'border-t-cyan-500 bg-stone-800/30' : 'border-t-cyan-500 bg-stone-50'}`}>
+                            <Zap className={`w-6 h-6 mb-4 ${isDark ? 'text-cyan-400' : 'text-cyan-600'}`} />
+                            <h3 className="font-bold mb-2">{t.dev.features.token.title}</h3>
+                            <p className="text-sm text-stone-500">{t.dev.features.token.desc}</p>
+                        </div>
+                        <div className={`p-6 border-t-2 ${isDark ? 'border-t-cyan-500 bg-stone-800/30' : 'border-t-cyan-500 bg-stone-50'}`}>
+                            <Cpu className={`w-6 h-6 mb-4 ${isDark ? 'text-cyan-400' : 'text-cyan-600'}`} />
+                            <h3 className="font-bold mb-2">{t.dev.features.compute.title}</h3>
+                            <p className="text-sm text-stone-500">{t.dev.features.compute.desc}</p>
+                        </div>
+                        <div className={`p-6 border-t-2 ${isDark ? 'border-t-cyan-500 bg-stone-800/30' : 'border-t-cyan-500 bg-stone-50'}`}>
+                            <Terminal className={`w-6 h-6 mb-4 ${isDark ? 'text-cyan-400' : 'text-cyan-600'}`} />
+                            <h3 className="font-bold mb-2">{t.dev.features.saas.title}</h3>
+                            <p className="text-sm text-stone-500">{t.dev.features.saas.desc}</p>
+                        </div>
+                    </div>
+                </div>
+            </section>
+
+            {/* Footer / Contact */}
+            <section id="contact" className={`py-24 px-6 border-t ${isDark ? 'bg-stone-900 border-stone-800' : 'bg-[#FAFAFA] border-stone-200'}`}>
+                <div className="max-w-2xl mx-auto text-center">
+                    <Feather className={`w-8 h-8 mx-auto mb-6 ${isDark ? 'text-stone-600' : 'text-stone-300'}`} />
+                    <h2 className="text-2xl font-serif font-bold mb-8">{t.contact.title}</h2>
+                    <p className={`mb-10 ${isDark ? 'text-stone-400' : 'text-stone-600'}`}>
+                        {t.contact.description}
+                    </p>
+
+                    <div className="flex flex-col sm:flex-row gap-4 mb-4">
+                        <input
+                            type="email"
+                            placeholder={t.contact.placeholderEmail}
+                            value={email}
+                            onChange={(e) => setEmail(e.target.value)}
+                            disabled={status === 'loading' || status === 'success'}
+                            className={`flex-1 px-4 py-3 rounded-lg border outline-none focus:ring-1 focus:ring-stone-400 transition-all ${isDark ? 'bg-stone-800 border-stone-700 text-stone-200' : 'bg-white border-stone-200 text-stone-800'}`}
+                        />
+                        <button
+                            onClick={handleEmailSubmit}
+                            disabled={status === 'loading' || status === 'success'}
+                            className={`px-8 py-3 rounded-lg font-bold transition-all disabled:opacity-50 disabled:cursor-not-allowed ${isDark ? 'bg-stone-100 text-stone-900 hover:bg-white' : 'bg-stone-900 text-white hover:bg-stone-800'}`}
+                        >
+                            {status === 'loading' ? '发送中...' : t.contact.submit}
+                        </button>
+                    </div>
+
+                    {/* Status Message */}
+                    {message && (
+                        <div className={`mb-12 text-sm font-medium ${status === 'success' ? 'text-green-500' : 'text-red-500'}`}>
+                            {message}
+                        </div>
+                    )}
+
+                    {!message && <div className="mb-16"></div>}
+
+                    <div className={`text-xs leading-loose pt-12 border-t ${isDark ? 'border-stone-800 text-stone-600' : 'border-stone-200 text-stone-400'}`}>
+                        {/* The Original Letter Links */}
+                        <div className="mb-4 flex flex-col items-center gap-2">
+                            {/* Letter to Players -> Letters.tsx -> /letters */}
+                            <a
+                                href="/letters"
+                                onClick={(e) => { e.preventDefault(); navigate('/letters'); }}
+                                className="inline-flex items-center gap-2 hover:underline decoration-stone-400 underline-offset-4 cursor-pointer"
+                            >
+                                <BookOpen className="w-3 h-3" />
+                                {t.contact.footerLetter}
+                            </a>
+                            {/* Shareholder Letter -> PDF Download */}
+                            <a
+                                href="/ltr/20250529ltr.pdf"
+                                target="_blank"
+                                rel="noopener noreferrer"
+                                className="inline-flex items-center gap-2 hover:underline decoration-stone-400 underline-offset-4 cursor-pointer"
+                            >
+                                <BookOpen className="w-3 h-3" />
+                                {t.contact.footerShareholder}
+                            </a>
+                        </div>
+                        <p>{t.contact.footerNote}</p>
+                        <p className="mt-4">{t.contact.copyright}</p>
+                    </div>
+                </div>
+            </section>
+
+        </div>
+    );
+};
+
+export default SimpleHome;

+ 8 - 0
vite.config.ts

@@ -4,4 +4,12 @@ import react from '@vitejs/plugin-react'
 // https://vite.dev/config/
 export default defineConfig({
   plugins: [react()],
+  server: {
+    proxy: {
+      '/api': {
+        target: 'http://localhost:3001',
+        changeOrigin: true,
+      }
+    }
+  }
 })