Files
shaguabijia-app-server/h5/home/index.html
T
guke 452d7fef8f feat(h5): 首页「上次比价」横幅接真实 /api/v1/compare/records(4 分钟新鲜窗口)
删写死 mock(DEFAULT_LAST_COMPARE_RECORD + localStorage 读),改带 Bearer token 拉真实比价记录;取最近一条 status=success 且 created_at 在 4 分钟内才显示(对齐原生 HomeViewModel.loadLastCompare),否则/未登录/401 隐藏横幅;进页 + 回前台 visibilitychange 各拉一次;点击仍走 SGBridge.navigate('compareResult')。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 10:39:18 +08:00

10398 lines
434 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>比价APP 高保真原型 (外卖版)</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
html { background: #000; }
body { font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Helvetica Neue', sans-serif; background: #000; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
:root {
--primary: #FFD600; --primary-dark: #F5C500; --primary-light: #FFEB3B; --primary-bg: #FFFDE7;
--primary-pale: #FFFAEC;
--primary-gradient: linear-gradient(180deg, #FFE066 0%, #FFD600 100%);
--primary-gradient-soft: linear-gradient(180deg, #FFFAEC 0%, #FFEEB0 100%);
--accent-orange: #FF5722; --accent-red: #E53935;
--success: #4CAF50; --success-bg: #E8F5E9; --warning: #FFC107; --error: #F44336; --info: #2196F3;
--coin: #FFB300; --coin-bg: #FFF8E1;
--gray-900: #1A1A1A; --gray-800: #333; --gray-700: #4D4D4D; --gray-600: #666; --gray-500: #999; --gray-400: #B3B3B3; --gray-300: #CCC; --gray-200: #E5E5E5; --gray-100: #F5F5F5; --gray-50: #FAFAFA; --bg: #F5F5F5;
/* Onboarding 统一 tokenB 方案) */
--cta-radius: 25px;
--cta-height: 50px;
--cta-font-size: 17px;
--cta-font-weight: 700;
--cta-letter-spacing: 1.5px;
--cta-shadow: 0 6px 18px rgba(255,179,0,.38), inset 0 1px 0 rgba(255,255,255,.55);
--cta-shadow-active: 0 3px 10px rgba(255,179,0,.30), inset 0 1px 0 rgba(255,255,255,.55);
--shadow-card-sm: 0 2px 8px rgba(0,0,0,.06);
--shadow-card-md: 0 6px 18px rgba(0,0,0,.08);
}
.device { width: 375px; height: 812px; background: #fff; border-radius: 32px; overflow: hidden; position: relative; box-shadow: 0 20px 60px rgba(0,0,0,0.5); }
.screen { position: absolute; inset: 0; display: none; flex-direction: column; overflow-y: auto; overflow-x: hidden; -webkit-overflow-scrolling: touch; }
.screen.active { display: flex; }
.screen.noscroll { overflow-y: hidden; }
.screen::-webkit-scrollbar { display: none; }
/* 通用样式 */
.header { display: flex; align-items: center; justify-content: space-between; padding: 40px 16px 12px; background: #fff; flex-shrink: 0; }
.header-title { font-size: 17px; font-weight: 600; color: var(--gray-900); }
.back-btn { font-size: 15px; font-weight: 600; color: var(--gray-800); cursor: pointer; background: none; border: none; }
.btn { display: flex; align-items: center; justify-content: center; border: none; border-radius: 14px; font-weight: 600; cursor: pointer; transition: opacity .15s; }
.btn:active { opacity: .7; }
.btn-primary { background: var(--primary); color: #1a1a1a; }
.btn-outline { background: transparent; border: 1.5px solid var(--primary); color: #1a1a1a; }
.btn-secondary { background: var(--primary-bg); color: var(--primary); }
.btn-lg { padding: 12px 24px; font-size: 17px; width: 100%; }
.btn-md { padding: 8px 16px; font-size: 15px; }
.btn-sm { padding: 8px 16px; font-size: 13px; }
/* ===== Onboarding 统一 CTAB 方案)===== */
.btn-cta {
display: inline-flex; align-items: center; justify-content: center;
border: none; cursor: pointer;
background: var(--primary-gradient);
color: var(--gray-900);
font-family: inherit;
font-size: var(--cta-font-size);
font-weight: var(--cta-font-weight);
letter-spacing: var(--cta-letter-spacing);
border-radius: var(--cta-radius);
height: var(--cta-height);
box-shadow: var(--cta-shadow);
transition: transform .12s ease, box-shadow .2s ease, opacity .15s ease;
}
.btn-cta:active { transform: translateY(1px); box-shadow: var(--cta-shadow-active); }
.btn-cta.size-lg { width: 100%; padding: 0 24px; }
.btn-cta.size-md { padding: 0 24px; height: 44px; font-size: 15px; letter-spacing: 1px; border-radius: 24px; }
.btn-cta.size-pill { padding: 0 16px; height: 36px; font-size: 13px; letter-spacing: .5px; border-radius: 18px; }
/* 文字辅助按钮(取消、放弃等) */
.btn-text {
background: none; border: none; cursor: pointer;
color: var(--gray-500); font-size: 14px; font-weight: 500;
font-family: inherit; transition: opacity .15s;
}
.btn-text:active { opacity: .6; }
/* ===== Splash CTA Variants ===== */
.splash-cta { width: 100%; border: none; padding: 16px 0; border-radius: 14px; font-size: 17px; font-weight: 700; cursor: pointer; transition: transform .15s, box-shadow .25s; position: relative; overflow: hidden; }
.splash-cta:active { transform: scale(.97); }
/* V1 红橙渐变(当前) */
.splash-cta-v1 { background: linear-gradient(135deg, #FE2C55, #FF6B3D); color: #fff; box-shadow: 0 6px 16px rgba(255,68,68,.30); }
/* V2 黄底黑字(品牌主色) */
.splash-cta-v2 { background: #FFD600; color: #1a1a1a; box-shadow: 0 6px 16px rgba(255,214,0,.45); }
/* V3 黑底黄字(高对比) */
.splash-cta-v3 { background: #1a1a1a; color: #FFD600; box-shadow: 0 6px 16px rgba(0,0,0,.25); }
/* V4 黄→金渐变 + 光泽扫过 */
.splash-cta-v4 { background: linear-gradient(135deg, #FFEB3B 0%, #FFD600 50%, #FFA000 100%); color: #1a1a1a; box-shadow: 0 6px 20px rgba(249,168,37,.45); }
.splash-cta-v4::after { content: ""; position: absolute; top: 0; left: -75%; width: 50%; height: 100%; background: linear-gradient(120deg, transparent, rgba(255,255,255,.6), transparent); transform: skewX(-25deg); animation: ctaShine 2.6s infinite; }
@keyframes ctaShine { 0% { left: -75%; } 60%, 100% { left: 125%; } }
/* V5 玻璃质感 黄+白光 */
.splash-cta-v5 { background: linear-gradient(180deg, #FFEB3B, #FFC400); color: #1a1a1a; box-shadow: inset 0 1px 0 rgba(255,255,255,.7), inset 0 -2px 0 rgba(0,0,0,.08), 0 8px 20px rgba(255,193,7,.35); }
/* V6 描边款(克制风) */
.splash-cta-v6 { background: #fff; color: #1a1a1a; border: 2px solid #1a1a1a; box-shadow: 4px 4px 0 #FFD600; }
.splash-cta-v6:active { box-shadow: 2px 2px 0 #FFD600; transform: translate(2px, 2px); }
/* V7 红包红(喜庆款) */
.splash-cta-v7 { background: linear-gradient(135deg, #E53935 0%, #E02E24 100%); color: #FFD600; box-shadow: 0 6px 18px rgba(198,40,40,.40); text-shadow: 0 1px 2px rgba(0,0,0,.2); }
/* V8 跳动呼吸款(黄底) */
.splash-cta-v8 { background: #FFD600; color: #1a1a1a; box-shadow: 0 6px 16px rgba(255,214,0,.45); animation: ctaPulse 1.6s ease-in-out infinite; }
@keyframes ctaPulse { 0%,100% { transform: scale(1); box-shadow: 0 6px 16px rgba(255,214,0,.45);} 50% { transform: scale(1.03); box-shadow: 0 10px 24px rgba(255,214,0,.65);} }
/* V9 蜜糖渐变(黄→琥珀,温暖软渐变,无扫光)*/
.splash-cta-v9 { background: linear-gradient(180deg, #FFE066 0%, #FFB300 100%); color: #1a1a1a; box-shadow: 0 8px 22px rgba(255,140,0,.4); }
/* V10 金币描边(品牌黄 + 深金细边 + 内嵌高光,模拟铸币感)*/
.splash-cta-v10 { background: linear-gradient(180deg, #FFE066 0%, #FFD600 100%); color: #1a1a1a; border: 1.5px solid #8B6914; box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 6px 18px rgba(184,134,11,.35); }
/* ===== Coupon Subtitle Variants ===== */
.cps { font-family: inherit; }
/* V1 当前灰色基线 */
.cps-v1 { font-size: 13px;font-weight: 500;color: var(--gray-500);line-height: 1.5; }
/* V2 系列:金币胶囊家族 */
.cps-v2 { display: inline-flex;align-items: center;gap: 6px;padding: 6px 12px;border-radius: 999px;background: linear-gradient(180deg,#FFE066,#FFC400);color: #5A3A00;font-size: 12px;font-weight: 600;letter-spacing: .2px;box-shadow: inset 0 1px 0 rgba(255,255,255,.7),0 2px 6px rgba(255,179,0,.35); }
.cps-v2 .cps-coin { width: 14px;height: 14px;border-radius: 50%;background: radial-gradient(circle at 35% 30%,#FFE0B2,#FFA000);box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);display: inline-flex;align-items: center;justify-content: center;font-size: 10px;font-weight: 900;color: #7A4F00; }
/* V2b 金币翻转动效 */
.cps-v2b { display: inline-flex;align-items: center;gap: 6px;padding: 6px 12px;border-radius: 999px;background: linear-gradient(180deg,#FFE066,#FFC400);color: #5A3A00;font-size: 12px;font-weight: 600;letter-spacing: .2px;box-shadow: inset 0 1px 0 rgba(255,255,255,.7),0 2px 6px rgba(255,179,0,.35); }
.cps-v2b .cps-coin { width: 14px;height: 14px;border-radius: 50%;background: radial-gradient(circle at 35% 30%,#FFE0B2,#FFA000);box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);display: inline-flex;align-items: center;justify-content: center;font-size: 10px;font-weight: 900;color: #7A4F00;animation: coinFlip 2.4s ease-in-out infinite; }
@keyframes coinFlip { 0%,70%,100% { transform: rotateY(0); } 80% { transform: rotateY(180deg); } 90% { transform: rotateY(360deg); } }
/* V2c 双色分段 */
.cps-v2c { display: inline-flex;align-items: center;padding: 0;border-radius: 999px;background: linear-gradient(180deg,#FFE066,#FFC400);font-size: 12px;font-weight: 600;letter-spacing: .2px;box-shadow: inset 0 1px 0 rgba(255,255,255,.7),0 2px 6px rgba(255,179,0,.35);overflow: hidden; }
.cps-v2c .cps-tag { background: #1a1a1a;color: #FFD600;padding: 6px 8px;font-weight: 900;letter-spacing: .5px;display: inline-flex;align-items: center;gap: 2px; }
.cps-v2c .cps-text { padding: 6px 12px 6px 8px;color: #5A3A00; }
/* V2d 闪光描边(轻盈款) */
.cps-v2d { position: relative;display: inline-flex;align-items: center;gap: 6px;padding: 6px 12px;border-radius: 999px;background: #FFFDF5;color: #1a1a1a;font-size: 12px;font-weight: 600;letter-spacing: .2px;border: 1.5px solid transparent;background-image: linear-gradient(#FFFDF5,#FFFDF5),linear-gradient(135deg,#FFD600,#F9A825,#FFD600);background-origin: border-box;background-clip: padding-box,border-box;box-shadow: 0 2px 8px rgba(255,179,0,.2); }
.cps-v2d .cps-coin { width: 14px;height: 14px;border-radius: 50%;background: radial-gradient(circle at 35% 30%,#FFE0B2,#FFA000);box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);display: inline-flex;align-items: center;justify-content: center;font-size: 10px;font-weight: 900;color: #7A4F00; }
/* V2e 数字嵌入款 */
.cps-v2e { display: inline-flex;align-items: center;gap: 6px;padding: 4px 12px 4px 8px;border-radius: 999px;background: linear-gradient(180deg,#FFE066,#FFC400);color: #5A3A00;font-size: 12px;font-weight: 600;letter-spacing: .2px;box-shadow: inset 0 1px 0 rgba(255,255,255,.7),0 2px 6px rgba(255,179,0,.35); }
.cps-v2e .cps-amount { display: inline-flex;align-items: baseline;background: #1a1a1a;color: #FFD600;padding: 2px 8px;border-radius: 999px;font-weight: 900;font-size: 12px;letter-spacing: .3px; }
.cps-v2e .cps-amount small { font-size: 10px;font-weight: 700;opacity: .85;margin-left: 2px; }
/* V2f 双图标夹击 */
.cps-v2f { display: inline-flex;align-items: center;gap: 4px;padding: 6px 12px;border-radius: 999px;background: linear-gradient(180deg,#FFE066,#FFC400);color: #5A3A00;font-size: 12px;font-weight: 600;letter-spacing: .2px;box-shadow: inset 0 1px 0 rgba(255,255,255,.7),0 2px 6px rgba(255,179,0,.35); }
.cps-v2f .cps-coin { width: 14px;height: 14px;border-radius: 50%;background: radial-gradient(circle at 35% 30%,#FFE0B2,#FFA000);box-shadow: inset 0 -1px 0 rgba(0,0,0,.15);display: inline-flex;align-items: center;justify-content: center;font-size: 10px;font-weight: 900;color: #7A4F00; }
.cps-v2f .cps-bolt { width: 12px;height: 12px;display: inline-flex;align-items: center;justify-content: center; }
.cps-v2f .cps-bolt svg { display: block; }
/* V2g 立体浮起 */
.cps-v2g { display: inline-flex;align-items: center;gap: 6px;padding: 6px 12px;border-radius: 999px;background: linear-gradient(180deg,#FFE066 0%,#FFD600 50%,#FFA000 100%);color: #5A3A00;font-size: 12px;font-weight: 900;letter-spacing: .2px;box-shadow: inset 0 1.5px 0 rgba(255,255,255,.85),inset 0 -1.5px 0 rgba(184,134,11,.3),0 6px 14px rgba(249,168,37,.45),0 1px 2px rgba(0,0,0,.1);text-shadow: 0 1px 0 rgba(255,255,255,.4); }
.cps-v2g .cps-coin { width: 15px;height: 15px;border-radius: 50%;background: radial-gradient(circle at 35% 30%,#FFE0B2,#FFA000);box-shadow: inset 0 -1.5px 0 rgba(0,0,0,.2),0 1px 1px rgba(0,0,0,.15);display: inline-flex;align-items: center;justify-content: center;font-size: 10px;font-weight: 900;color: #7A4F00; }
.cps-v3f { display: inline-flex;align-items: center;gap: 6px;padding: 4px 12px;border-radius: 999px;background: rgba(255,179,0,.18);color: #8B6914;font-size: 12px;font-weight: 600;letter-spacing: .2px; }
.cps-v3f .cps-coin { display: inline-flex;align-items: center;justify-content: center;font-size: 15px;line-height: 1; }
/* V3 关键词高亮 */
.cps-v3 { font-size: 13px;font-weight: 500;color: var(--gray-700);line-height: 1.5; }
.cps-v3 em { font-style: normal;font-weight: 900;color: #1a1a1a;background: linear-gradient(to top,#FFD600 45%,transparent 45%);padding: 0 2px; }
/* V4 优惠券齿边小票 */
.cps-v4 { position: relative;display: inline-flex;align-items: stretch;background: #fff;border: 1.5px dashed #FFB300;border-radius: 8px;padding: 4px 12px;font-size: 12px;font-weight: 600;color: #E02E24;letter-spacing: .3px;box-shadow: 0 1px 4px rgba(255,179,0,.25); }
.cps-v4::before, .cps-v4::after { content: "";position: absolute;top: 50%;width: 8px;height: 8px;border-radius: 50%;background: #FFF3C4;transform: translateY(-50%); }
.cps-v4::before { left: -5px; }
.cps-v4::after { right: -5px; }
.cps-v4 .cps-amt { color: #1a1a1a;background: #FFD600;padding: 2px 6px;border-radius: 4px;margin: 0 4px;font-weight: 900; }
/* V5 数字驱动徽章 */
.cps-v5 { display: inline-flex;align-items: center;gap: 8px;padding: 4px 6px 4px 12px;border-radius: 999px;background: rgba(26,26,26,.06);font-size: 12px;font-weight: 600;color: var(--gray-800); }
.cps-v5 .cps-pill { display: inline-flex;align-items: baseline;gap: 2px;padding: 2px 8px;border-radius: 999px;background: #1a1a1a;color: #FFD600;font-weight: 900;font-size: 12px;letter-spacing: .3px; }
.cps-v5 .cps-pill small { font-size: 10px;font-weight: 700;opacity: .85; }
/* V6 双行节奏 */
.cps-v6 { display: flex;flex-direction: column;align-items: center;gap: 2px;line-height: 1.3; }
.cps-v6 .cps-top { font-size: 11px;font-weight: 600;color: var(--gray-500);letter-spacing: .5px;text-transform: uppercase; }
.cps-v6 .cps-bot { font-size: 13px;font-weight: 600;color: #E02E24; }
.cps-v6 .cps-bot em { font-style: normal;color: #1a1a1a;background: linear-gradient(to top,#FFD600 45%,transparent 45%);padding: 0 2px;font-weight: 900; }
/* ===== Tweaks Panel ===== */
.tweaks-panel { position: fixed; right: 20px; bottom: 20px; width: 280px; background: #fff; border-radius: 14px; box-shadow: 0 8px 32px rgba(0,0,0,.18); padding: 16px; z-index: 9998; font-family: -apple-system, sans-serif; display: none; }
.tweaks-panel.open { display: block; }
.tweaks-panel h3 { font-size: 14px; font-weight: 700; color: #1a1a1a; margin-bottom: 4px; }
.tweaks-panel .tweaks-sub { font-size: 11px; color: #999; margin-bottom: 12px; }
.tweaks-panel .tweaks-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.tweaks-opt { padding: 8px 8px; border-radius: 8px; border: 1.5px solid #e5e5e5; cursor: pointer; font-size: 12px; font-weight: 600; color: #333; background: #fff; text-align: center; transition: all .15s; }
.tweaks-opt:hover { border-color: #999; }
.tweaks-opt.active { background: #FFD600; border-color: #FFD600; color: #1a1a1a; }
.tweaks-opt small { display: block; font-size: 10px; font-weight: 500; color: #999999; margin-top: 2px; }
.tweaks-opt.active small { color: #5A3A00; }
.tweaks-close { position: absolute; top: 10px; right: 10px; width: 22px; height: 22px; border-radius: 12px; background: #f0f0f0; border: none; cursor: pointer; font-size: 13px; line-height: 1; color: #666; }
.card { background: #fff; border-radius: 14px; padding: 16px; box-shadow: 0 2px 8px rgba(0,0,0,.06); }
.section-title { font-size: 17px; font-weight: 600; color: var(--gray-900); margin-bottom: 12px; }
.badge { display: inline-flex; align-items: center; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 600; }
/* Tab bar — 4 tab + 中央 AI 领券 FAB(平 tab bar + FAB 站在上面,下半部分被白底视觉包住) */
.tab-bar { display: flex; height: 60px; background: #fff; border-top: .5px solid var(--gray-200); flex-shrink: 0; position: relative; overflow: visible; }
.tab-item { flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; cursor: pointer; position: relative; z-index: 1; }
.tab-icon { width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; }
.tab-label { font-size: 11px; color: var(--gray-800); font-weight: 400; }
.tab-item.active .tab-label { color: var(--gray-900); font-weight: 700; }
.tab-icon .i-outline { display: block; }
.tab-icon .i-filled { display: none; }
.tab-item.active .tab-icon .i-outline { display: none; }
.tab-item.active .tab-icon .i-filled { display: block; }
.welfare-tab-dot {
position: absolute;
left: 50%;
top: 9px;
width: 10px;
height: 10px;
border-radius: 50%;
background: #FF3B30;
border: 1px solid #FFFFFF;
box-shadow: 0 2px 5px rgba(255, 59, 48, .32);
transform: translateX(7px);
display: none;
pointer-events: none;
z-index: 3;
}
.tab-item.has-welfare-dot .welfare-tab-dot { display: block; }
/* 横幅轮播 */
@keyframes marqueeScroll { 0%{transform: translateX(0);} 100%{transform: translateX(-50%);} }
.marquee-wrap { overflow: hidden;border-radius: 8px;background: #FFF3E0;margin-bottom: 8px;height: 32px;display: flex;align-items: center; }
/* 用户案例翻页横幅 */
.flip-banner { height: 36px;border-radius: 18px;background: linear-gradient(90deg,#FFF3E0,#FFE0B2);margin-bottom: 12px;perspective: 800px;padding: 0 6px 0 4px; }
.flip-banner-stage { width: 100%;height: 100%;position: relative;transform-style: preserve-3d;transition: transform .55s cubic-bezier(.5,0,.4,1); }
.flip-banner.flipping .flip-banner-stage { transform: rotateX(-180deg); }
.flip-banner-face { position: absolute;inset: 0;display: flex;align-items: center;gap: 8px;backface-visibility: hidden;-webkit-backface-visibility: hidden;padding: 0 8px 0 4px;overflow: hidden; }
.flip-banner-face.back { transform: rotateX(180deg); }
.flip-banner-avatar { width: 28px;height: 28px;border-radius: 14px;display: flex;align-items: center;justify-content: center;font-size: 16px;flex-shrink: 0;box-shadow: 0 1px 3px rgba(0,0,0,.08); }
.flip-banner-text { flex: 1;font-size: 12px;font-weight: 500;color: var(--gray-700);white-space: nowrap;overflow: hidden;text-overflow: ellipsis; }
.flip-banner-text .flip-name { color: var(--gray-900);font-weight: 700;margin-right: 4px; }
.flip-banner-text em { font-style: normal;color: #FF5722;font-weight: 600;margin-left: 2px; }
.marquee-track { display: flex;white-space: nowrap;animation: marqueeScroll 20s linear infinite; }
.marquee-item { display: inline-flex;align-items: center;padding: 0 24px;font-size: 12px;font-weight: 600;color: var(--gray-700); }
/* 浮窗 */
.floating-btn { position: absolute; right: 16px; bottom: 80px; width: 56px; height: 56px; border-radius: 24px; background: var(--primary); color: #1a1a1a; display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 600; box-shadow: 0 4px 12px rgba(255,87,34,.4); cursor: pointer; z-index: 100; animation: pulse 2s infinite; }
@keyframes pulse { 0%,100% { box-shadow: 0 4px 12px rgba(255,87,34,.4); } 50% { box-shadow: 0 4px 24px rgba(255,87,34,.6); } }
@keyframes spin { to { transform: rotate(360deg); } }
@keyframes cardHint { 0%,100% { box-shadow: 0 2px 8px rgba(255,87,34,.1); } 50% { box-shadow: 0 2px 16px rgba(255,87,34,.3); } }
@keyframes splashTickerScroll { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
@keyframes splashFadeIn { from { opacity: 0; } to { opacity: 1; } }
/* ===== Splash · 全图方案:splash-full-bg 含印章+taglineHTML 仅放按钮 ===== */
.splash-illustration {
position: absolute;
top: 80px; left: 0; right: 0;
height: 575px;
background-image: url('assets/onboarding/index/full-bg.png');
background-size: 375px auto;
background-position: top center;
background-repeat: no-repeat;
background-color: transparent;
overflow: hidden;
z-index: 1;
}
.splash-illustration::before {
content: '';
position: absolute;
top: 0; left: 0; right: 0;
height: 90px;
background: linear-gradient(to top, rgba(253,250,249,0) 0%, rgba(253,250,249,.55) 55%, rgba(253,250,249,1) 100%);
pointer-events: none;
z-index: 1;
}
/* 底部渐变:仅最末端消接缝,远离"下单前先比价"避免糊字 */
.splash-illustration::after {
content: '';
position: absolute;
bottom: 0; left: 0; right: 0;
height: 14px;
background: linear-gradient(to bottom, rgba(253,250,249,0) 0%, rgba(253,250,249,1) 100%);
pointer-events: none;
}
.splash-start-btn {
position: absolute;
bottom: 60px;
left: 50%;
transform: translateX(-50%);
width: 220px;
height: 52px;
border-radius: 24px;
background: linear-gradient(180deg, #FFEB3B 0%, #FFD600 50%, #FFC400 100%);
color: #1A1A1A;
font-size: 17px;
font-weight: 700;
letter-spacing: 4px;
text-indent: 4px;
border: none;
cursor: pointer;
z-index: 10;
box-shadow: 0 8px 22px rgba(255,193,0,.45), inset 0 1px 0 rgba(255,255,255,.55), inset 0 -2px 0 rgba(180,110,0,.10);
transition: transform .12s ease, box-shadow .2s ease, opacity .15s ease;
}
.splash-start-btn:active {
transform: translateX(-50%) scale(0.97);
opacity: 0.92;
box-shadow: 0 4px 12px rgba(255,193,0,.35), inset 0 1px 0 rgba(255,255,255,.55);
}
@keyframes safetySlideUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes safetyPopIn { from { transform: scale(.92); opacity: 0; } to { transform: scale(1); opacity: 1; } }
@keyframes confettiDrop {
0% { transform: translateY(0) translateX(0) rotate(0deg) rotateY(0deg); opacity: 1; }
25% { translateX(var(--sway)); }
50% { transform: translateY(400px) translateX(calc(var(--sway) * -1)) rotate(360deg) rotateY(540deg); opacity: 1; }
75% { translateX(var(--sway)); }
100% { transform: translateY(850px) translateX(var(--sway)) rotate(720deg) rotateY(1080deg); opacity: 0; }
}
@keyframes confettiFloat {
0% { transform: translateY(0) translateX(0) rotate(0deg) rotateX(0deg) scale(1); opacity: 1; }
100% { transform: translateY(var(--dy)) translateX(var(--dx)) rotate(var(--spin)) rotateX(1080deg) scale(0.5); opacity: 0; }
}
/* 导航指示 */
.nav-hint { position: absolute; bottom: 8px; left: 50%; transform: translateX(-50%); font-size: 11px; color: var(--gray-400); background: rgba(255,255,255,.8); padding: 4px 12px; border-radius: 12px; z-index: 50; }
/* 方案切换控制面板 */
.variant-toggle { position: fixed; top: 16px; left: 16px; z-index: 9999; }
.variant-btn { width: 36px; height: 36px; border-radius: 18px; background: #333; color: #fff; border: none; font-size: 16px; cursor: pointer; box-shadow: 0 2px 8px rgba(0,0,0,.3); display: flex; align-items: center; justify-content: center; }
.variant-btn:hover { background: #666666; }
.variant-panel { display: none; position: absolute; top: 44px; left: 0; background: #fff; border-radius: 14px; box-shadow: 0 4px 24px rgba(0,0,0,.15); padding: 16px; min-width: 260px; max-height: 70vh; overflow-y: auto; }
.variant-panel.open { display: block; }
.variant-panel h3 { font-size: 14px; font-weight: 700; color: #333; margin-bottom: 12px; }
.variant-module { margin-bottom: 12px; }
.variant-module:last-child { margin-bottom: 0; }
.variant-module-label { font-size: 12px; font-weight: 600; color: #666; margin-bottom: 6px; }
.variant-options { display: flex; gap: 6px; flex-wrap: wrap; }
.variant-opt { padding: 4px 12px; border-radius: 32px; border: 1.5px solid #e5e5e5; font-size: 12px; font-weight: 600; cursor: pointer; background: #fff; color: #666; transition: all .15s; }
.variant-opt:hover { border-color: #999; }
.variant-opt.active { background: #FFD600; border-color: #FFD600; color: #1a1a1a; }
.variant-empty { font-size: 12px; color: #999; text-align: center; padding: 16px 0; }
/* 平台icon浮动动画 */
@keyframes float1 { 0%,100%{transform: translateY(0)} 50%{transform: translateY(-6px)} }
@keyframes float2 { 0%,100%{transform: translateY(0)} 50%{transform: translateY(-8px)} }
@keyframes float3 { 0%,100%{transform: translateY(0)} 50%{transform: translateY(-5px)} }
.fly-icon.fly1 { animation: float1 3s ease-in-out infinite; }
.fly-icon.fly2 { animation: float2 2.5s ease-in-out infinite .3s; }
.fly-icon.fly3 { animation: float3 3.2s ease-in-out infinite .6s; }
/* 下滑手势动画:底部开始→往上滑→底部消失 */
@keyframes swipeDown { 0%{transform: translateY(30px) rotate(-15deg);opacity: 0} 50%{transform: translateY(-30px) rotate(-15deg);opacity: 1} 100%{transform: translateY(30px) rotate(-15deg);opacity: 0} }
.swipe-hand { animation: swipeDown 2s ease-in-out infinite; }
/* 首个卡片呼吸光效 */
@keyframes breathGlow { 0%,100%{box-shadow: 0 2px 8px rgba(255,87,34,.06)} 50%{box-shadow: 0 2px 16px rgba(255,87,34,.18)} }
.platform-card-first { animation: breathGlow 2s ease-in-out infinite; border-color: #FFD600 !important; }
/* 点击手势提示 */
@keyframes tapHint { 0%,100%{transform: translate(0,0) scale(1)} 50%{transform: translate(-2px,2px) scale(.92)} }
.tap-hand { animation: tapHint 1.2s ease-in-out infinite; }
.gesture-hand-img { display: block; width: 100%; height: 100%; object-fit: contain; }
/* 加号二级高亮:贴近原按钮的双层呼吸圈 */
.demo-plus-focus {
display: none;
position: absolute;
z-index: 14;
width: 46px;
height: 46px;
border-radius: 50%;
transform: translate(-50%, -50%);
pointer-events: none;
}
.demo-plus-focus.circle { border-radius: 50%; }
.demo-plus-focus.pill { border-radius: 999px; }
.demo-plus-focus.rect { border-radius: 12px; }
.demo-plus-focus.confirm { border-radius: 4px 24px 24px 4px; }
.demo-plus-focus.clickable {
cursor: pointer;
pointer-events: auto;
}
.demo-plus-focus::before,
.demo-plus-focus::after {
content: "";
position: absolute;
inset: 5px;
border-radius: inherit;
border: 2px solid rgba(255,214,0,.62);
box-shadow: 0 0 8px rgba(255,214,0,.28);
transform-origin: 50% 50%;
animation: plusGuidePulse 1.45s ease-out infinite;
}
.demo-plus-focus::after {
inset: 3px;
border-color: rgba(255,255,255,.54);
animation-delay: .48s;
}
.demo-plus-core {
position: absolute;
left: 50%;
top: 50%;
width: calc(100% - 12px);
height: calc(100% - 12px);
transform: translate(-50%, -50%);
border-radius: inherit;
border: 2px solid #fff;
outline: 2px solid #FFD600;
box-shadow: 0 0 0 3px rgba(255,214,0,.10), 0 0 8px rgba(255,214,0,.30);
}
@keyframes plusGuidePulse {
0% { transform: scale(.76); opacity: .72; }
100% { transform: scale(1.38); opacity: 0; }
}
@keyframes plusGuideTap {
0%,100% { transform: translate(0,0) scale(1); }
50% { transform: translate(3px,-3px) scale(.93); }
}
#demoFinger.demo-plus-finger {
z-index: 14;
animation: plusGuideTap 1.18s ease-in-out infinite;
}
#demoFinger .gesture-hand-img {
filter: brightness(.94) saturate(.88);
}
#demoFinger.demo-plus-finger .gesture-hand-img {
transform: scaleX(-1) rotate(-14deg);
}
/* 平台选择卡片 hover 效果 */
.platform-card:hover { border-color: #FF5722 !important; box-shadow: 0 0 0 1px #FF5722, 0 4px 14px rgba(255,87,34,.12); }
.platform-card:active { transform: scale(.98); }
/* 开始比价按钮光晕 */
@keyframes btnGlow {
0%,100% { box-shadow: 0 0 12px 4px rgba(218,165,0,.45), 0 0 28px 8px rgba(218,165,0,.15); }
50% { box-shadow: 0 0 22px 10px rgba(218,165,0,.6), 0 0 45px 16px rgba(218,165,0,.25); }
}
@keyframes btnGlowRing {
0% { transform: translate(-50%,-50%) scale(.85); opacity: .7; }
100% { transform: translate(-50%,-50%) scale(1.5); opacity: 0; }
}
@keyframes fadeInUp {
0% { opacity: 0; transform: translateY(20px); }
100% { opacity: 1; transform: translateY(0); }
}
@keyframes welcomeRing {
0% { transform: scale(1); opacity: .6; }
100% { transform: scale(1.4); opacity: 0; }
}
/* ===== Agent toast 数字暴涨动效 ===== */
@keyframes couponNumPop {
0% { transform: scale(1); }
30% { transform: scale(1.32); }
65% { transform: scale(.92); }
100% { transform: scale(1); }
}
.coupon-num-pop { animation: couponNumPop .28s ease-out; }
@keyframes couponPlusFly {
0% { opacity: 0; transform: translate(-50%, 0) scale(.6); }
20% { opacity: 1; transform: translate(calc(-50% + var(--dx, 0px) * .25), calc(var(--dy, -8px) * .25)) scale(1.1); }
100% { opacity: 0; transform: translate(calc(-50% + var(--dx, 0px)), var(--dy, -34px)) scale(.7); }
}
.coupon-plus-particle {
position: absolute; left: 50%; top: 6px;
font-size: 13px; font-weight: 900; color: #FF3D00;
text-shadow: 0 1px 4px rgba(255,107,0,.4);
pointer-events: none;
z-index: 3;
animation: couponPlusFly .85s ease-out forwards;
}
@keyframes couponBarFlow {
0% { width: 8%; }
100% { width: 92%; }
}
@keyframes couponBarShine {
0% { transform: translateX(-100%); }
100% { transform: translateX(400%); }
}
@keyframes giftAwait {
0%, 100% {
transform: translateY(-50%) scale(1) rotate(-8deg);
filter: drop-shadow(0 2px 4px rgba(255,180,0,.55));
}
50% {
transform: translateY(-72%) scale(1.18) rotate(8deg);
filter: drop-shadow(0 4px 10px rgba(255,180,0,.95));
}
}
/* ===== 比价副文案红包动效(摇摆 + 光晕 + sparkle===== */
@keyframes redpacketBob {
0%, 100% { transform: translateY(0) rotate(-3deg); }
50% { transform: translateY(-3px) rotate(3deg); }
}
@keyframes redpacketGlow {
0%, 100% { opacity: .35; transform: scale(.85); }
50% { opacity: 1; transform: scale(1.18); }
}
@keyframes redpacketSparkleA {
0%, 100% { opacity: 0; transform: scale(.4); }
30%, 65% { opacity: 1; transform: scale(1); }
}
@keyframes redpacketSparkleB {
0%, 100% { opacity: 0; transform: scale(.4); }
20%, 55% { opacity: 1; transform: scale(1); }
}
.redpacket-bob { animation: redpacketBob 2.4s ease-in-out infinite; }
/* ===== AI 接管橘黄色流动光晕 ===== */
@property --aiGlowAngle {
syntax: '<angle>';
initial-value: 0deg;
inherits: false;
}
@keyframes aiGlowRotate { to { --aiGlowAngle: 360deg; } }
@keyframes aiGlowPulse {
0% { opacity: .7; }
50% { opacity: 1; }
100% { opacity: .7; }
}
@keyframes aiGlowBreathe {
0% { transform: scale(1); opacity: .85; }
50% { transform: scale(1.015); opacity: 1; }
100% { transform: scale(1); opacity: .85; }
}
.ai-takeover-glow {
display: none;
position: absolute;
inset: 0;
pointer-events: none;
z-index: 14;
border-radius: 32px;
}
.ai-takeover-glow.active { display: block; }
/* 外层流动光带:粗一圈 + 大模糊,形成柔和橘黄光晕 */
.ai-takeover-glow::before {
content: '';
position: absolute;
inset: 0;
padding: 8px;
border-radius: 32px;
background: conic-gradient(
from var(--aiGlowAngle, 0deg),
#FFE082 0%,
#FFC107 12%,
#FF8F00 24%,
#FF6B00 36%,
#FFB300 50%,
#FFD54F 62%,
#FF9800 75%,
#FF6B00 87%,
#FFE082 100%
);
-webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
-webkit-mask-composite: xor;
mask-composite: exclude;
filter: blur(7px);
animation: aiGlowRotate 5s linear infinite, aiGlowBreathe 3.2s ease-in-out infinite;
}
/* 内层柔光:从边缘向内渗透的橘色辉光,强化"光晕"感 */
.ai-takeover-glow::after {
content: '';
position: absolute;
inset: 0;
border-radius: 32px;
box-shadow: inset 0 0 36px rgba(255, 152, 0, .55),
inset 0 0 80px rgba(255, 107, 0, .28),
inset 0 0 140px rgba(255, 87, 34, .12);
animation: aiGlowPulse 2.6s ease-in-out infinite;
}
/* ===== 新手礼包弹窗 ===== */
.gift-overlay { position: absolute; inset: 0; background: rgba(0,0,0,.6); display: none; align-items: center; justify-content: center; z-index: 200; backdrop-filter: blur(2px); }
.gift-overlay.show { display: flex; animation: fadeIn .25s ease-out; }
.gift-card { width: 304px; background: #fff; border-radius: 24px; padding: 24px 16px; position: relative; box-shadow: 0 24px 60px rgba(0,0,0,.4); animation: giftPop .4s cubic-bezier(.34,1.56,.64,1); overflow: hidden; }
.gift-card::before { content: ''; position: absolute; top: 0; left: 0; right: 0; height: 120px; background: linear-gradient(180deg, #FFE066 0%, #FFD600 50%, transparent 100%); opacity: .6; pointer-events: none; }
.gift-close { position: absolute; top: 12px; right: 12px; width: 28px; height: 28px; border-radius: 14px; background: rgba(0,0,0,.08); display: flex; align-items: center; justify-content: center; font-size: 14px; color: #666; cursor: pointer; border: none; z-index: 2; }
.gift-confetti { font-size: 22px; text-align: center; letter-spacing: 4px; margin-bottom: 8px; position: relative; z-index: 1; }
.gift-title { font-size: 24px; font-weight: 600; color: #1a1a1a; text-align: center; margin-bottom: 4px; position: relative; z-index: 1; }
.gift-subtitle { font-size: 13px; color: var(--gray-700); text-align: center; margin-bottom: 16px; position: relative; z-index: 1; }
.gift-chest-wrap { position: relative; width: 160px; height: 160px; margin: 8px auto 16px; display: flex; align-items: center; justify-content: center; }
.gift-chest-glow { position: absolute; inset: 0; border-radius: 32px; background: radial-gradient(circle, rgba(255,214,0,.7) 0%, rgba(255,214,0,0) 65%); animation: chestGlow 2s ease-in-out infinite; }
.gift-chest-emoji { font-size: 48px; position: relative; z-index: 2; animation: chestBob 2s ease-in-out infinite; transition: transform .3s; }
.gift-card.opening .gift-chest-emoji { animation: chestShake .3s ease-out; }
/* 光线 burst */
.gift-burst { position: absolute; inset: 0; pointer-events: none; opacity: 0; }
.gift-card.opened .gift-burst { animation: burstFlash .6s ease-out; }
.gift-burst-ray { position: absolute; top: 50%; left: 50%; width: 6px; height: 80px; background: linear-gradient(180deg, rgba(255,214,0,1) 0%, rgba(255,214,0,0) 100%); transform-origin: 3px 0; }
.gift-burst-ray:nth-child(1) { transform: translate(-50%, 0) rotate(0deg); }
.gift-burst-ray:nth-child(2) { transform: translate(-50%, 0) rotate(60deg); }
.gift-burst-ray:nth-child(3) { transform: translate(-50%, 0) rotate(120deg); }
.gift-burst-ray:nth-child(4) { transform: translate(-50%, 0) rotate(180deg); }
.gift-burst-ray:nth-child(5) { transform: translate(-50%, 0) rotate(240deg); }
.gift-burst-ray:nth-child(6) { transform: translate(-50%, 0) rotate(300deg); }
.gift-amount { font-size: 48px; font-weight: 900; color: #FF5722; text-align: center; line-height: 1.1; display: none; }
.gift-amount-suffix { font-size: 18px; font-weight: 700; color: var(--gray-800); margin-left: 4px; }
.gift-amount-hint { font-size: 13px; color: var(--gray-700); text-align: center; margin: 6px 0 16px; display: none; }
.gift-card.opened .gift-stage-a { display: none; }
.gift-card.opened .gift-amount,
.gift-card.opened .gift-amount-hint { display: block; }
.gift-btn { width: 100%; height: 48px; border: none; border-radius: 24px; background: linear-gradient(180deg, #FFEB3B 0%, #FFD600 100%); color: #1a1a1a; font-size: 17px; font-weight: 700; cursor: pointer; box-shadow: 0 4px 16px rgba(255,214,0,.5); position: relative; z-index: 1; }
.gift-btn:active { opacity: .85; transform: translateY(1px); }
@keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }
@keyframes giftPop { 0% { opacity: 0; transform: scale(.7) translateY(20px); } 100% { opacity: 1; transform: scale(1) translateY(0); } }
@keyframes chestBob { 0%,100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-8px) rotate(2deg); } }
@keyframes chestGlow { 0%,100% { transform: scale(1); opacity: .6; } 50% { transform: scale(1.15); opacity: .9; } }
@keyframes chestShake { 0%,100% { transform: scale(1); } 25% { transform: scale(1.15) rotate(-8deg); } 75% { transform: scale(1.15) rotate(8deg); } }
@keyframes burstFlash { 0% { opacity: 0; transform: scale(.4); } 30% { opacity: 1; transform: scale(1.1); } 100% { opacity: 0; transform: scale(1.6); } }
/* 飞行金币 */
.fly-coin { position: absolute; left: 50%; top: 50%; font-size: 28px; line-height: 1; pointer-events: none; z-index: 250;
transform: translate(-50%, -50%);
animation: coinFly 1100ms cubic-bezier(.55,.05,.7,.4) forwards;
animation-delay: var(--d, 0ms);
--tx: 0px; --ty: 0px; --bx: 0px; --by: 0px;
filter: drop-shadow(0 2px 4px rgba(255,179,0,.6));
}
@keyframes coinFly {
0% { opacity: 0; transform: translate(-50%, -50%) scale(.4); }
15% { opacity: 1; transform: translate(calc(-50% + var(--bx)*.2), calc(-50% + var(--by)*.2)) scale(1.4); }
35% { opacity: 1; transform: translate(calc(-50% + var(--bx)), calc(-50% + var(--by))) scale(1.2); }
100% { opacity: 0; transform: translate(calc(-50% + var(--tx)), calc(-50% + var(--ty))) scale(.5); }
}
/* 福利 tab pulse + +5000 飘字 */
.tab-pulse { animation: tabPulse .6s ease-out; }
@keyframes tabPulse { 0% { transform: scale(1); } 40% { transform: scale(1.3); filter: drop-shadow(0 0 12px rgba(255,214,0,.9)); } 100% { transform: scale(1); } }
.tab-coin-badge { position: absolute; bottom: 50px; left: 50%; transform: translateX(-50%); background: linear-gradient(180deg,#FFEB3B,#FFD600); color: #1a1a1a; font-size: 13px; font-weight: 600; padding: 4px 8px; border-radius: 32px; box-shadow: 0 4px 12px rgba(255,179,0,.6); pointer-events: none; opacity: 0; z-index: 150; white-space: nowrap; }
.tab-coin-badge.show { animation: floatUp 1.2s ease-out forwards; }
@keyframes floatUp { 0% { opacity: 0; transform: translate(-50%, 0) scale(.7); } 20% { opacity: 1; transform: translate(-50%, -8px) scale(1.1); } 100% { opacity: 0; transform: translate(-50%, -32px) scale(1); } }
/* === 登录页 · E1 方案:PNG 背景 + 纯白渐变蒙版(分界 ~62%,红包区不糊) === */
/* 背景层:PNG 满屏 + 横向渐变兜底色(PNG 加载失败时不会露白) */
#login {
background: url("assets/login/index/redpacket-bg.png") center top / cover no-repeat,
linear-gradient(90deg, #FEE7B7 0%, #FDEFD0 50%, #FEE7B7 100%);
}
/* E1 渐变蒙版:28% 起、52.5% 之前完全透明(保红包+优惠券不糊),57% 处 .95,64% 处完全不透明 */
#login::before {
content: '';
position: absolute;
left: 0; right: 0; top: 28%; bottom: 0;
background: linear-gradient(180deg,
rgba(255, 255, 255, 0) 0%,
rgba(255, 255, 255, 0) 34%,
rgba(255, 255, 255, .55) 38%,
rgba(255, 255, 255, .95) 40.5%,
rgba(255, 255, 255, 1) 50%);
pointer-events: none;
z-index: 1;
}
/* 登录按钮去掉黄色光晕(全局 --cta-shadow 是黄底配黄阴影,登录页是白底要换中性阴影) */
#login .btn-cta {
box-shadow: 0 4px 14px rgba(0, 0, 0, .08), inset 0 1px 0 rgba(255, 255, 255, .55);
}
#login .btn-cta:active {
box-shadow: 0 2px 6px rgba(0, 0, 0, .06), inset 0 1px 0 rgba(255, 255, 255, .55);
}
/* 登录控件区:按钮位置沿用上一版,协议固定到底部 */
.lg-bottom { position: absolute; inset: 0; z-index: 5; }
.login-quick { position: absolute; inset: 0; display: block; }
.login-masked-phone { position: absolute; left: 0; right: 0; top: 64%; text-align: center; font-size: 28px; font-weight: 600; color: var(--gray-900); letter-spacing: .02em; line-height: 1; font-variant-numeric: tabular-nums; }
.login-main-btn { position: absolute; left: 7.5%; right: 7.5%; top: 71%; width: auto !important; height: 7.8%; min-height: 50px; font-size: 18px; }
.login-other-link { position: absolute; left: 0; right: 0; top: 81.5%; display: flex; align-items: center; justify-content: center; gap: 2px; border: 0; background: transparent; color: #999999; font-size: 13px; font-weight: 600; letter-spacing: .02em; cursor: pointer; font-family: inherit; }
.login-other-link .arrow { font-size: 15px; line-height: 1; color: inherit; transform: translateY(-.5px); }
.login-agreement { position: absolute; left: 50%; bottom: 24px; transform: translateX(-50%); z-index: 6; display: inline-flex; align-items: center; gap: 8px; border: 0; background: transparent; color: #999999; font-size: 12px; line-height: 1; white-space: nowrap; cursor: pointer; font-family: inherit; padding: 0; }
.login-agreement .check { width: 16px; height: 16px; margin-top: 2px; border-radius: 50%; border: 1.5px solid #CCCCCC; background: #fff; flex: 0 0 auto; position: relative; }
.login-agreement.checked .check { border-color: transparent; background: transparent url("data:image/svg+xml;utf8,<svg xmlns='http: //www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16' stroke='%23D85A1F' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><circle cx='8' cy='8' r='7'/><path d='M5 8.25 7 10 11 6'/></svg>") center/100% 100% no-repeat; background-origin: border-box; }
.login-agreement .link, .login-agreement-sheet-copy .link { color: #E65100; font-weight: 700; }
.login-phone-panel { position: absolute; left: 40px; right: 40px; top: 61%; z-index: 6; }
/* 祝贺页底部登录面板(方案 A:完整 banner + 白底简洁登录,celebration 屏内嵌一键登录) */
.celeb-login {
flex-shrink: 0; padding: 16px 24px 24px;
background: #fff; border-top: 1px solid var(--gray-200);
position: relative; z-index: 11; /* 高于 confettiCanvasz-index: 10 */
display: flex; flex-direction: column; align-items: stretch;
}
.celeb-login-phone {
text-align: center; font-size: 24px; font-weight: 600;
color: var(--gray-900); letter-spacing: .02em; margin-bottom: 12px;
font-variant-numeric: tabular-nums; line-height: 1;
}
.celeb-login > .btn-cta { width: 100%; }
.celeb-login-other {
display: block; width: 100%; margin-top: 12px;
border: 0; background: transparent; cursor: pointer; font-family: inherit;
text-align: center; color: #999999; font-size: 13px; font-weight: 600; letter-spacing: .02em;
}
.celeb-login-other .arrow { font-size: 14px; vertical-align: -1px; color: inherit; }
.celeb-login-agreement {
position: static !important; transform: none !important;
margin: 12px auto 0; display: inline-flex; left: auto; right: auto; bottom: auto;
align-self: center;
}
/* 入口页协议 · 底部抽屉(一键登录场景) */
.login-agreement-sheet-layer { display: none; position: absolute; inset: 0; z-index: 30; align-items: flex-end; background: rgba(0,0,0,.48); }
.login-agreement-sheet { width: 100%; padding: 24px 24px 32px; border-radius: 24px 24px 0 0; background: #FFFDF5; color: #1A1A1A; text-align: center; box-shadow: 0 -12px 30px rgba(0,0,0,.16); animation: loginSheetIn .24s cubic-bezier(.22,1,.36,1); }
@keyframes loginSheetIn { from { transform: translateY(24px); opacity: .75; } to { transform: translateY(0); opacity: 1; } }
.login-agreement-sheet-title { font-size: 17px; font-weight: 600; letter-spacing: .01em; margin-bottom: 12px; }
.login-agreement-sheet-copy { font-size: 13px; line-height: 1.6; color: #666666; margin-bottom: 16px; }
.login-agreement-sheet-primary { width: 100%; height: 50px; border: 0; border-radius: 999px; background: linear-gradient(180deg, #FFE066 0%, #FFD600 55%, #FFC400 100%); color: #5A3A00; font-size: 16px; font-weight: 700; letter-spacing: .04em; box-shadow: 0 12px 24px rgba(255, 200, 0, .28), inset 0 1px 0 rgba(255, 255, 255, .65); cursor: pointer; font-family: inherit; }
.login-agreement-sheet-secondary { margin-top: 12px; border: 0; background: transparent; color: #999999; font-size: 13px; cursor: pointer; font-family: inherit; padding: 8px 16px; }
/* 独立手机号登录页 */
#loginPhone { background: #fff; }
.lp-topbar { position: absolute; top: 44px; left: 0; right: 0; height: 44px; display: flex; align-items: center; padding: 0 8px; z-index: 10; }
.lp-back { width: 44px; height: 44px; display: flex; align-items: center; justify-content: center; font-size: 24px; color: var(--gray-900); background: none; border: none; cursor: pointer; font-family: inherit; }
.lp-title { position: absolute; left: 0; right: 0; top: 132px; text-align: center; font-size: 24px; font-weight: 600; color: var(--gray-900); letter-spacing: .04em; }
.lp-form { position: absolute; left: 32px; right: 32px; top: 220px; }
.lp-input-row { display: flex; align-items: center; height: 50px; border-bottom: 1px solid var(--gray-200); }
.lp-input-row.focused { border-bottom-color: var(--gray-900); }
.lp-cc { font-size: 17px; font-weight: 600; color: var(--gray-900); letter-spacing: .02em; display: inline-flex; align-items: center; gap: 4px; margin-right: 12px; flex-shrink: 0; }
.lp-cc .chev { font-size: 10px; color: var(--gray-700); margin-left: 2px; }
.lp-divider { width: 1px; height: 18px; background: var(--gray-200); margin-right: 16px; flex-shrink: 0; }
.lp-input { flex: 1; height: 100%; border: none; padding: 0; font-size: 17px; font-weight: 500; color: var(--gray-900); letter-spacing: .04em; font-variant-numeric: tabular-nums; outline: none; background: transparent; }
.lp-input::placeholder { color: var(--gray-400); font-weight: 400; }
.lp-cta { width: 100%; margin-top: 40px; margin-bottom: 16px; }
.lp-cta.disabled { background: linear-gradient(180deg, #FFE0B2 0%, #FFE066 100%); color: rgba(26,26,26,.45); box-shadow: none; pointer-events: none; }
.lp-agreement { display: flex; align-items: flex-start; gap: 8px; color: #999999; font-size: 12px; line-height: 1.55; padding-right: 8px; cursor: pointer; border: 0; background: transparent; text-align: left; font-family: inherit; width: 100%; }
.lp-agreement .check { width: 16px; height: 16px; border-radius: 50%; border: 1.5px solid #CCCCCC; background: #fff; flex: 0 0 auto; margin-top: 2px; position: relative; }
.lp-agreement.checked .check { border-color: transparent; background: transparent url("data:image/svg+xml;utf8,<svg xmlns='http: //www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16' stroke='%23D85A1F' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'><circle cx='8' cy='8' r='7'/><path d='M5 8.25 7 10 11 6'/></svg>") center/100% 100% no-repeat; background-origin: border-box; }
.lp-agreement .link { color: #E65100; font-weight: 600; }
/* 验证码步骤(沿用原 step2 视觉,放在新页内) */
.lp-sms-hint { font-size: 13px; color: var(--gray-500); margin-bottom: 16px; }
.lp-sms-boxes { display: flex; gap: 8px; justify-content: center; margin-bottom: 16px; }
.lp-sms-boxes input { width: 40px; height: 48px; border-radius: 12px; border: 1.5px solid var(--gray-200); text-align: center; font-size: 20px; font-weight: 700; outline: none; }
.lp-sms-actions { display: flex; justify-content: center; gap: 16px; }
.lp-sms-actions span { font-size: 13px; cursor: pointer; }
/* 手机号页协议 · 居中弹窗 */
.lp-modal-layer { display: none; position: absolute; inset: 0; z-index: 30; align-items: center; justify-content: center; padding: 0 32px; background: rgba(0,0,0,.5); }
.lp-modal-card { width: 100%; padding: 24px 24px 16px; border-radius: 18px; background: #fff; color: #1A1A1A; text-align: center; box-shadow: 0 20px 60px rgba(0,0,0,.3); animation: lpModalIn .24s cubic-bezier(.22,1,.36,1); }
@keyframes lpModalIn { from { transform: scale(.94); opacity: 0; } to { transform: scale(1); opacity: 1; } }
.lp-modal-title { font-size: 17px; font-weight: 600; letter-spacing: .01em; margin-bottom: 12px; color: var(--gray-900); }
.lp-modal-copy { font-size: 13px; line-height: 1.7; color: var(--gray-700); margin-bottom: 24px; text-align: left; }
.lp-modal-copy .link { color: #E65100; font-weight: 600; }
.lp-modal-primary { width: 100%; height: 44px; border: 0; border-radius: 24px; background: linear-gradient(180deg, #FFE066 0%, #FFD600 100%); color: var(--gray-900); font-size: 15px; font-weight: 700; letter-spacing: .04em; box-shadow: 0 4px 14px rgba(0, 0, 0, .08), inset 0 1px 0 rgba(255, 255, 255, .55); cursor: pointer; font-family: inherit; }
.lp-modal-secondary { margin-top: 4px; height: 40px; border: 0; background: transparent; color: var(--gray-500); font-size: 13px; cursor: pointer; font-family: inherit; padding: 8px 16px; }
/* 新人礼包 · 优惠券样式 */
.gift-coupon { position: relative; background: linear-gradient(90deg,#FF6B6B 0%,#FF5722 100%); border-radius: 24px; padding: 16px 16px 16px 24px; display: flex; align-items: center; gap: 12px; box-shadow: 0 6px 18px rgba(255,87,34,.35); color: #fff; overflow: visible; }
.gift-coupon::before { content: ''; position: absolute; top: 50%; left: -6px; transform: translateY(-50%); width: 12px; height: 12px; background: #FFF8E1; border-radius: 50%; }
.gift-coupon::after { content: ''; position: absolute; left: 16px; top: 12px; bottom: 12px; width: 1px; background-image: linear-gradient(180deg, rgba(255,255,255,.5) 50%, transparent 50%); background-size: 1px 6px; }
.gift-coupon .gc-icon { flex-shrink: 0; font-size: 48px; line-height: 1; filter: drop-shadow(0 2px 6px rgba(0,0,0,.2)); }
.gift-coupon .gc-title { font-size: 18px; font-weight: 900; color: #fff; margin-bottom: 4px; }
.gift-coupon .gc-sub { font-size: 12px; color: rgba(255,255,255,.9); }
/* 新人礼包 · 7 方案样式(用于 ?giftvariant= 切换) */
.gv-base { position: relative; border-radius: 24px; display: flex; align-items: center; gap: 12px; }
.gv-a { background: linear-gradient(135deg,#FFF3E0,#FFE0B2); padding: 16px; box-shadow: 0 4px 16px rgba(255,179,0,.18); }
.gv-a .gv-icon { flex-shrink: 0; font-size: 48px; line-height: 1; }
.gv-a .gv-title { font-size: 18px; font-weight: 600; color: #1a1a1a; margin-bottom: 4px; }
.gv-a .gv-sub { font-size: 12px; color: var(--gray-700); }
.gv-b { background: linear-gradient(135deg,#FFF3E0,#FFE0B2); padding: 16px; box-shadow: 0 4px 16px rgba(255,179,0,.18); }
.gv-b .gv-icon { flex-shrink: 0; font-size: 48px; line-height: 1; }
.gv-b .gv-title { font-size: 18px; font-weight: 600; color: #1a1a1a; margin-bottom: 4px; }
.gv-b .gv-sub { font-size: 12px; color: var(--gray-700); }
.gv-b .gv-stamp { position: absolute; top: -10px; right: 14px; background: linear-gradient(135deg,#FF5722,#E65100); color: #fff; font-size: 11px; font-weight: 600; padding: 6px 12px; border-radius: 12px 12px 12px 4px; box-shadow: 0 4px 10px rgba(255,87,34,.4); transform: rotate(8deg); }
.gv-c { background: linear-gradient(135deg,#FFF3E0,#FFE0B2); padding: 16px; box-shadow: 0 4px 16px rgba(255,179,0,.18); }
.gv-c .gv-icon { flex-shrink: 0; font-size: 48px; line-height: 1; }
.gv-c .gv-title { font-size: 18px; font-weight: 600; color: #1a1a1a; margin-bottom: 6px; }
.gv-c .gv-cd { display: inline-flex; align-items: center; gap: 4px; background: #1a1a1a; color: #FFD600; font-size: 11px; font-weight: 600; padding: 4px 8px; border-radius: 8px; font-variant-numeric: tabular-nums; }
.gv-c .gv-blink { animation: gvBlink 1s infinite; }
@keyframes gvBlink { 50% { opacity: .3; } }
.gv-d { background: linear-gradient(135deg,#FFE0B2 0%,#FFE066 50%,#FFE066 100%); padding: 24px 16px; box-shadow: 0 8px 24px rgba(255,87,34,.25), inset 0 1px 0 rgba(255,255,255,.6); overflow: hidden; }
.gv-d .gv-iconwrap { position: relative; flex-shrink: 0; width: 70px; height: 70px; display: flex; align-items: center; justify-content: center; }
.gv-d .gv-icon { position: relative; font-size: 48px; line-height: 1; filter: drop-shadow(0 4px 8px rgba(255,87,34,.3)); }
.gv-d .gv-text { position: relative; z-index: 1; }
.gv-d .gv-title { font-size: 20px; font-weight: 900; color: #1a1a1a; margin-bottom: 4px; letter-spacing: -.3px; }
.gv-d .gv-sub { font-size: 13px; color: #5A3A00; font-weight: 600; }
.gv-f { background: radial-gradient(circle at 50% 20%, rgba(255,255,255,.8), transparent 50%), linear-gradient(135deg,#FFF3E0,#FFE0B2); padding: 24px 16px 24px; box-shadow: 0 8px 24px rgba(255,179,0,.25); flex-direction: column; text-align: center; overflow: hidden; }
.gv-f .gv-rays { position: absolute; top: -20px; left: 50%; transform: translateX(-50%); width: 200px; height: 200px; pointer-events: none; background: conic-gradient(from 0deg, rgba(255,213,79,.3) 0deg, transparent 30deg, rgba(255,213,79,.3) 60deg, transparent 90deg, rgba(255,213,79,.3) 120deg, transparent 150deg, rgba(255,213,79,.3) 180deg, transparent 210deg, rgba(255,213,79,.3) 240deg, transparent 270deg, rgba(255,213,79,.3) 300deg, transparent 330deg); border-radius: 50%; animation: gvSpin 12s linear infinite; opacity: .6; }
@keyframes gvSpin { to { transform: translateX(-50%) rotate(360deg); } }
.gv-f .gv-iconwrap { position: relative; z-index: 1; margin-bottom: 8px; }
.gv-f .gv-icon { font-size: 48px; line-height: 1; display: inline-block; animation: gvBob 2.5s infinite ease-in-out; filter: drop-shadow(0 6px 12px rgba(255,87,34,.3)); }
@keyframes gvBob { 50% { transform: translateY(-4px); } }
@keyframes gvPop { 0% { transform: scale(.4); opacity: 0; } 60% { transform: scale(1.08); opacity: 1; } 100% { transform: scale(1); opacity: 1; } }
.gv-f .gv-title { position: relative; z-index: 1; font-size: 22px; font-weight: 900; color: #1a1a1a; margin-bottom: 4px; letter-spacing: -.3px; }
.gv-f .gv-sub { position: relative; z-index: 1; font-size: 12px; color: #5A3A00; font-weight: 600; }
.gv-g { background: linear-gradient(135deg,#FFF3E0,#FFE0B2); padding: 16px; box-shadow: 0 4px 16px rgba(255,179,0,.18); overflow: hidden; }
.gv-g .gv-iconwrap { position: relative; flex-shrink: 0; width: 56px; height: 56px; display: flex; align-items: center; justify-content: center; }
.gv-g .gv-icon { font-size: 48px; line-height: 1; }
.gv-g .gv-spark { position: absolute; width: 6px; height: 6px; background: #FFD600; border-radius: 50%; box-shadow: 0 0 8px #FFD600; }
.gv-g .gv-s1 { top: 0; right: 4px; animation: gvSparkle 1.8s infinite; }
.gv-g .gv-s2 { bottom: 4px; left: 0; animation: gvSparkle 1.8s infinite .6s; }
.gv-g .gv-s3 { top: 50%; right: -4px; animation: gvSparkle 1.8s infinite 1.2s; }
@keyframes gvSparkle { 0%,100% { transform: scale(0); opacity: 0; } 50% { transform: scale(1); opacity: 1; } }
.gv-g .gv-title { font-size: 18px; font-weight: 900; color: #1a1a1a; margin-bottom: 4px; }
.gv-g .gv-title em { font-style: normal; background: linear-gradient(90deg, #FF5722, #FFB300); -webkit-background-clip: text; background-clip: text; color: transparent; margin-left: 4px; font-weight: 900; }
.gv-g .gv-sub { font-size: 12px; color: var(--gray-700); }
body.gv-embed .variant-toggle { display: none !important; }
/* ===== Coupon prompt 变体 · 平台选择卡通用样式 ===== */
.cp-card { transition: all .2s; cursor: pointer; }
.cp-card[data-checked="false"] { opacity: .5; }
.cp-card[data-checked="false"] .cp-check { background: var(--gray-300) !important; }
.cp-card[data-checked="false"] .cp-check svg { opacity: 0; }
/* 选中态边框色按主题分 */
.cp-card-pink[data-checked="false"] { border-color: var(--gray-200) !important; }
.cp-card-red[data-checked="false"] { border-color: var(--gray-200) !important; }
.cp-card-yellow[data-checked="false"] { border-color: var(--gray-200) !important; }
.cp-card-yellow[data-checked="false"] .cp-toggle { background: var(--gray-200) !important; }
.cp-card-yellow[data-checked="false"] .cp-toggle::after { transform: translateX(0) !important; }
/* ============================================
一键领券完成 · 迭代 6 配色 B(朱红卡包风)
============================================ */
.coupon-done-k-mask {
position: absolute; inset: 0;
background: rgba(0,0,0,.55);
z-index: 28;
animation: kFadeIn .3s ease-out;
}
@keyframes kFadeIn { from { opacity: 0; } to { opacity: 1; } }
.coupon-done-k {
/* 配色 B · 朱红 */
--v6-bg-top: #FF1744;
--v6-bg-bottom: #C62828;
--v6-pocket-top: #FF5252;
--v6-pocket-bottom: #E53935;
--v6-notch: #C62828;
position: absolute; left: 50%; top: 50%;
transform: translate(-50%, -50%);
z-index: 30;
width: 296px;
background: linear-gradient(180deg, var(--v6-bg-top) 0%, var(--v6-bg-bottom) 65%, var(--v6-bg-bottom) 100%);
border-radius: 24px;
box-shadow: 0 20px 50px rgba(0,0,0,.4);
overflow: visible;
animation: kPopIn .5s cubic-bezier(.34,1.56,.64,1);
}
@keyframes kPopIn {
0% { opacity: 0; transform: translate(-50%, -50%) scale(.85); }
100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
/* 顶部居中品牌 logo(与 splash / 浮窗一致:黄色圆角方块 + 机器人 SVG + 文字) */
.coupon-done-k .coupon-done-logo {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 16px 0 2px;
position: relative;
z-index: 3;
}
.coupon-done-k .coupon-done-logo .logo-mark {
width: 32px; height: 32px;
border-radius: 8px;
background: linear-gradient(135deg, #FFD600, #FFB300);
display: flex; align-items: center; justify-content: center;
flex-shrink: 0;
box-shadow: 0 4px 10px rgba(0,0,0,.22);
}
.coupon-done-k .coupon-done-logo .logo-text {
font-size: 15px; font-weight: 900;
color: #fff;
letter-spacing: 0.8px;
text-shadow: 0 1px 2px rgba(0,0,0,.22);
}
/* sparkle 黄色星光 */
.coupon-done-k .v6-sparkle {
position: absolute;
width: 7px; height: 7px;
background: #FFE066; border-radius: 50%;
box-shadow: 0 0 8px #FFE082;
animation: kSparkleTwinkle 1.5s infinite ease-in-out;
z-index: 2;
}
.coupon-done-k .v6-sparkle.s1 { top: 14px; left: 22px; }
.coupon-done-k .v6-sparkle.s2 { top: 22px; right: 28px; animation-delay: .3s; }
.coupon-done-k .v6-sparkle.s3 { top: 60px; left: 28px; animation-delay: .6s; width: 5px; height: 5px; }
.coupon-done-k .v6-sparkle.s4 { top: 50px; right: 32px; animation-delay: .9s; width: 5px; height: 5px; }
.coupon-done-k .v6-sparkle.s5 { top: 38px; left: 50%; animation-delay: 1.1s; width: 4px; height: 4px; }
@keyframes kSparkleTwinkle {
0%, 100% { opacity: .3; transform: scale(.6); }
50% { opacity: 1; transform: scale(1.2); }
}
/* 头部双行渐变文案(票卡删除后改为页面中心展示) */
.coupon-done-k .v6-head {
text-align: center;
padding: 24px 16px 24px;
color: #fff; position: relative; z-index: 1;
}
.coupon-done-k .v6-head .lead-line {
font-size: 20px; font-weight: 600;
margin-bottom: 12px;
background: linear-gradient(180deg, #fff 30%, #FFE0B2 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
letter-spacing: 1px;
}
.coupon-done-k .v6-head .num-line {
font-size: 28px; font-weight: 900;
line-height: 1.1;
text-shadow: 0 2px 10px rgba(0,0,0,.28);
letter-spacing: 0.5px;
}
.coupon-done-k .v6-head .num-line .num {
color: #FFE066;
font-family: -apple-system, "DIN Alternate", sans-serif;
font-size: 36px;
margin-right: 2px;
}
.coupon-done-k .v6-head .num-line .rest {
background: linear-gradient(180deg, #fff 30%, #FFE066 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* 平台来源:"来自 + 两个 logo",不写平台名 */
.coupon-done-k .v6-from {
display: flex; align-items: center; justify-content: center;
gap: 8px;
padding: 6px 16px 12px;
position: relative; z-index: 1;
}
.coupon-done-k .v6-from .label {
font-size: 12px; font-weight: 600;
color: rgba(255,255,255,.85);
letter-spacing: 0.04em;
}
.coupon-done-k .v6-from .lgs { display: flex; align-items: center; gap: 6px; }
.coupon-done-k .v6-from .lg {
width: 28px; height: 28px;
border-radius: 8px; overflow: hidden;
border: 2px solid #fff;
box-shadow: 0 2px 6px rgba(0,0,0,.25);
background: #fff;
}
.coupon-done-k .v6-from .lg img { width: 100%; height: 100%; object-fit: cover; }
/* v3:标题内联式 — 把"领取自 + 平台 logo"嵌入第一行文案,密度最高 */
.coupon-done-k .v6-head .lead-line-inline {
display: inline-flex; align-items: center; justify-content: center;
flex-wrap: wrap; gap: 4px;
/* 取消渐变文字,避免与内联图片混搭显得别扭 */
background: none;
-webkit-text-fill-color: #fff;
color: #fff;
font-size: 16px; font-weight: 700;
letter-spacing: .3px;
}
.coupon-done-k .v6-head .lead-line-inline .lg-inline {
display: inline-block;
width: 28px; height: 28px;
border-radius: 8px; overflow: hidden;
box-shadow: 0 1px 4px rgba(0,0,0,.25);
vertical-align: middle;
}
.coupon-done-k .v6-head .lead-line-inline .lg-inline img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* v2:领取自上提(顶部胶囊;与领取数量绑定为同一信息块) */
.coupon-done-k .v6-from-top {
display: inline-flex; align-items: center; gap: 6px;
margin: 0 auto 12px;
padding: 4px 12px 4px 8px;
background: rgba(255,255,255,.16);
border: 1px solid rgba(255,255,255,.22);
border-radius: 999px;
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
}
.coupon-done-k .v6-from-top .label {
font-size: 11px; font-weight: 700;
color: rgba(255,255,255,.92);
letter-spacing: 0.04em;
}
.coupon-done-k .v6-from-top .lgs { gap: 4px; }
.coupon-done-k .v6-from-top .lg {
width: 18px; height: 18px;
border-width: 1.5px;
box-shadow: 0 1px 3px rgba(0,0,0,.2);
}
/* 整体 head 容器在使用 v6-from-top 时改 flex column 居中(让胶囊上居中) */
.coupon-done-k .v6-head:has(> .v6-from-top) {
display: flex; flex-direction: column; align-items: center;
padding-top: 12px;
}
.coupon-done-k .v6-head:has(> .v6-from-top) .lead-line {
margin-bottom: 8px;
}
/* 白色票卡 */
.coupon-done-k .v6-tickets {
display: flex; gap: 12px;
padding: 6px 16px 16px;
position: relative; z-index: 1;
}
.coupon-done-k .v6-tickets .pl {
flex: 1;
background: #fff;
border-radius: 8px;
padding: 12px 6px;
text-align: center;
position: relative;
box-shadow: 0 4px 12px rgba(0,0,0,.15);
}
/* 票卡左右切角 */
.coupon-done-k .v6-tickets .pl::before,
.coupon-done-k .v6-tickets .pl::after {
content: '';
position: absolute;
width: 12px; height: 12px;
background: var(--v6-bg-top);
border-radius: 50%;
top: 50%; transform: translateY(-50%);
}
.coupon-done-k .v6-tickets .pl::before { left: -6px; clip-path: inset(0 0 0 50%); }
.coupon-done-k .v6-tickets .pl::after { right: -6px; clip-path: inset(0 50% 0 0); }
.coupon-done-k .v6-tickets .pl .num {
font-size: 24px; font-weight: 900; color: #E02E24;
font-family: -apple-system, "DIN Alternate", sans-serif;
line-height: 1; letter-spacing: -0.5px;
}
.coupon-done-k .v6-tickets .pl .num small {
font-size: 11px; color: #E02E24; font-weight: 700; margin-left: 2px;
}
.coupon-done-k .v6-tickets .pl .dash {
margin: 8px 4px;
border-top: 1px dashed #FFCDD2;
}
.coupon-done-k .v6-tickets .pl .lg {
width: 32px; height: 32px;
border-radius: 8px; overflow: hidden;
background: #FFF8E1; margin: 0 auto;
box-shadow: 0 2px 5px rgba(0,0,0,.06);
}
.coupon-done-k .v6-tickets .pl .lg img { width: 100%; height: 100%; object-fit: cover; }
/* pocket 上层(浅)+ U 形凹口 */
.coupon-done-k .v6-pocket {
background: linear-gradient(180deg, var(--v6-pocket-top) 0%, var(--v6-pocket-bottom) 100%);
padding: 24px 16px 12px;
position: relative;
z-index: 1;
border-radius: 24px;
box-shadow: 0 -6px 14px rgba(0,0,0,.18);
}
.coupon-done-k .v6-pocket-notch {
position: absolute;
top: 0; left: 50%;
transform: translateX(-50%);
width: 44px; height: 16px;
background: var(--v6-notch);
border-radius: 0 0 32px 32px;
z-index: 2;
}
.coupon-done-k .v6-expiry {
text-align: center;
color: #fff;
font-size: 13px;
font-weight: 600;
margin-bottom: 12px;
margin-top: 4px;
letter-spacing: 0.3px;
position: relative; z-index: 2;
}
.coupon-done-k .v6-expiry .time-box {
display: inline-block;
background: #fff;
color: #E02E24;
padding: 2px 4px;
border-radius: 4px;
font-weight: 600;
font-size: 13px;
font-family: -apple-system, "DIN Alternate", sans-serif;
margin: 0 2px;
letter-spacing: -0.5px;
box-shadow: 0 1px 3px rgba(0,0,0,.15);
}
.coupon-done-k .v6-cta {
width: 100%;
background: linear-gradient(135deg, #FFEB3B 0%, #FFD600 50%, #FFC400 100%);
color: #1a1a1a;
border: none;
padding: 12px;
border-radius: 24px;
font-size: 15px;
font-weight: 900;
cursor: pointer;
box-shadow: inset 0 -2px 0 rgba(184,134,11,.18);
position: relative; z-index: 2;
letter-spacing: 1px;
}
.ad-reward-toast {
position: absolute;
left: 50%;
top: 46%;
z-index: 240;
width: 158px;
min-height: 140px;
padding: 20px 14px 18px;
border-radius: 18px;
background: linear-gradient(165deg, rgba(40,28,4,.94), rgba(0,0,0,.86));
color: #fff;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none;
opacity: 0;
transform: translate(-50%, -50%) scale(.7);
overflow: visible;
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
box-shadow: 0 14px 34px rgba(120,80,0,.32), inset 0 1px 0 rgba(255,230,120,.18);
}
.ad-reward-toast.visible {
opacity: 1;
animation: adRewardPop .32s cubic-bezier(.18,.9,.25,1.15) both;
}
@keyframes adRewardPop {
0% { opacity: 0; transform: translate(-50%, -50%) scale(.7); }
60% { opacity: 1; transform: translate(-50%, -50%) scale(1.08); }
100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
.ad-reward-toast img {
width: 60px;
height: 60px;
object-fit: contain;
margin-bottom: 6px;
filter: drop-shadow(0 4px 8px rgba(255,179,0,.4));
}
.ad-reward-toast-amount {
color: var(--primary);
font-family: "DIN Alternate", "Helvetica Neue", sans-serif;
font-size: 30px;
font-weight: 800;
line-height: 1;
letter-spacing: 0;
text-shadow: 0 1px 3px rgba(0,0,0,.4);
}
.ad-reward-toast.visible .ad-reward-toast-amount {
animation: adRewardNumBounce .5s .08s cubic-bezier(.18,.9,.25,1.4) both;
}
@keyframes adRewardNumBounce {
0% { transform: scale(.4); }
55% { transform: scale(1.25); }
100% { transform: scale(1); }
}
.ad-reward-toast-text {
margin-top: 8px;
color: #fff;
font-size: 14px;
font-weight: 700;
line-height: 1.2;
letter-spacing: 0;
white-space: nowrap;
}
.ad-reward-toast .spark {
position: absolute;
top: 38%;
left: 50%;
width: 8px;
height: 8px;
border-radius: 50%;
background: radial-gradient(circle at 35% 30%, #FFE680, #FFB300);
opacity: 0;
pointer-events: none;
}
.ad-reward-toast.visible .spark {
animation: adSparkBurst .6s ease-out forwards;
}
.ad-reward-toast .spark.s1 { --dx: -46px; --dy: -40px; animation-delay: .05s; }
.ad-reward-toast .spark.s2 { --dx: 48px; --dy: -34px; animation-delay: .08s; }
.ad-reward-toast .spark.s3 { --dx: -52px; --dy: 18px; animation-delay: .04s; }
.ad-reward-toast .spark.s4 { --dx: 50px; --dy: 24px; animation-delay: .1s; }
.ad-reward-toast .spark.s5 { --dx: 0px; --dy: -56px; animation-delay: .12s; width: 6px; height: 6px; }
.ad-reward-toast .spark.s6 { --dx: -30px; --dy: 46px; animation-delay: .07s; width: 6px; height: 6px; }
@keyframes adSparkBurst {
0% { opacity: 0; transform: translate(-50%, -50%) scale(.3); }
25% { opacity: 1; }
100% { opacity: 0; transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(1); }
}
/* ===== 比价结果页:全网最低价徽章(图片) + N2 chip ===== */
.card.with-ribbon { overflow: visible; }
.ribbon-vi {
position: absolute;
top: -16px; right: -6px;
width: 104px;
height: auto;
z-index: 3;
pointer-events: none;
filter: drop-shadow(0 4px 10px rgba(220,40,40,0.25));
}
/* 价格旁红 chip "比原选择省 ¥X" — 强调"省了多少",刻意放大 */
.save-chip-red {
background: linear-gradient(135deg, #FE2C55, #FF6B3D);
color: #fff;
border-radius: 8px;
padding: 4px 12px;
font-size: 15px;
font-weight: 600;
letter-spacing: 0.2px;
align-self: center;
display: inline-flex; align-items: baseline; gap: 2px;
box-shadow: 0 2px 6px rgba(0,0,0,0.15);
white-space: nowrap;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.save-chip-red .small { font-size: 12px; opacity: 1; font-weight: 700; }
.result-cta {
flex-shrink: 0;
min-height: 34px;
padding: 6px 16px;
border: none;
border-radius: 999px;
font-size: 13px;
font-weight: 700;
line-height: 1;
cursor: pointer;
background: var(--gray-100);
color: var(--gray-800);
display: inline-flex;
align-items: center;
justify-content: center;
gap: 2px;
white-space: nowrap;
}
.result-cta .arrow {
display: inline-flex;
align-items: center;
justify-content: center;
color: var(--gray-500);
font-size: 15px;
line-height: 1;
transform: translateY(-.5px);
}
/* 用户原选 chip:店名旁描边方角 */
.badge-userorig {
flex-shrink: 0;
display: inline-flex; align-items: center;
background: transparent;
border: 1px solid #CCCCCC;
border-radius: 4px;
padding: 2px 8px;
font-size: 11px; font-weight: 600;
color: #666666; letter-spacing: .3px;
white-space: nowrap;
}
/* ===== Kickoff 引导卡(首次进店)— L·I 升级版:黄 header + 米黄 body ===== */
.kickoff-card {
position: relative;
border-radius: 14px;
box-shadow: 0 14px 30px rgba(0,0,0,.32), 0 4px 14px rgba(255,179,0,.22);
}
/* 气泡上方的 journey 排版字(无卡片背景,闲鱼式:白字 + 关键词黄字 + 弧线下划) */
.kickoff-promo {
position: absolute;
bottom: 100%;
left: 0; right: 0;
margin-bottom: 24px;
text-align: center;
pointer-events: none;
}
.kickoff-promo .promo-line {
font-size: 22px;
font-weight: 900;
color: #fff;
line-height: 1.35;
letter-spacing: .5px;
-webkit-text-stroke: 0.4px currentColor; /* 在 900 之上再加描边,视觉更粗 */
text-stroke: 0.4px currentColor;
text-shadow: 0 2px 12px rgba(0,0,0,.7), 0 1px 3px rgba(0,0,0,.55), 0 0 2px rgba(0,0,0,.35);
}
.kickoff-promo .promo-accent {
position: relative;
display: inline-block;
padding: 0 2px;
color: #FFEB3B;
}
/* SVG 手绘风弧线下划,仿闲鱼那种"标注"感(按字号同比例放大) */
.kickoff-promo .promo-accent::after {
content: '';
position: absolute;
left: -2px; right: -2px;
bottom: -7px;
height: 9px;
background: url("data:image/svg+xml;utf8,<svg xmlns='http: //www.w3.org/2000/svg' viewBox='0 0 100 9' preserveAspectRatio='none'><path d='M2 5 Q 25 1 50 4.5 T 98 4.5' stroke='%23FFE34D' stroke-width='2.8' fill='none' stroke-linecap='round'/></svg>") no-repeat center / 100% 100%;
}
.kickoff-head {
background: linear-gradient(135deg, #FFEB3B 0%, #FFD600 100%);
padding: 8px 12px 8px;
display: flex; align-items: center; gap: 8px;
border-radius: 14px 14px 0 0;
}
.kickoff-dot {
width: 6px; height: 6px; border-radius: 50%;
background: #1a1a1a;
flex-shrink: 0;
}
.kickoff-title {
font-size: 14px; font-weight: 900; color: #1a1a1a;
letter-spacing: .5px; line-height: 1.2;
}
.kickoff-body {
background: #FFF8E1;
padding: 12px 12px 12px;
font-size: 13px; font-weight: 700; color: #1a1a1a;
line-height: 1.5;
border-radius: 0 0 14px 14px;
}
.kickoff-leader {
position: absolute;
bottom: -36px; left: 50%; margin-left: -1px;
width: 2px; height: 36px;
background: linear-gradient(180deg, rgba(255,214,0,0) 0%, #FFD600 30%, #FFD600 100%);
border-radius: 4px;
pointer-events: none;
}
.kickoff-leader::before {
content: '';
position: absolute; bottom: -4px; left: -4px;
width: 10px; height: 10px; border-radius: 50%;
background: #fff;
border: 2px solid #1a1a1a;
box-sizing: border-box;
box-shadow: 0 0 0 3px rgba(255,214,0,.55), 0 1px 3px rgba(0,0,0,.25);
animation: kickoffLeaderDot 1.6s ease-in-out infinite;
}
@keyframes kickoffLeaderDot {
0%, 100% { box-shadow: 0 0 0 3px rgba(255,214,0,.55), 0 1px 3px rgba(0,0,0,.25); }
50% { box-shadow: 0 0 0 6px rgba(255,214,0,.2), 0 1px 3px rgba(0,0,0,.25); }
}
/* 比价流程内引导气泡的黄色连接线(动态长度+方向,dot 落点贴住 spotlight 边缘) */
#demoGuide .demo-guide-leader {
position: absolute;
left: 50%; margin-left: -1px;
width: 2px;
border-radius: 4px;
pointer-events: none;
}
#demoGuide .demo-guide-leader.dir-down {
top: 100%;
background: linear-gradient(180deg, rgba(255,214,0,0) 0%, #FFD600 30%, #FFD600 100%);
}
#demoGuide .demo-guide-leader.dir-down::before {
content: '';
position: absolute; bottom: -4px; left: -4px;
width: 10px; height: 10px; border-radius: 50%;
background: #fff;
border: 2px solid #1a1a1a;
box-sizing: border-box;
box-shadow: 0 0 0 3px rgba(255,214,0,.55), 0 1px 3px rgba(0,0,0,.25);
animation: kickoffLeaderDot 1.6s ease-in-out infinite;
}
#demoGuide .demo-guide-leader.dir-up {
bottom: 100%;
background: linear-gradient(0deg, rgba(255,214,0,0) 0%, #FFD600 30%, #FFD600 100%);
}
#demoGuide .demo-guide-leader.dir-up::before {
content: '';
position: absolute; top: -4px; left: -4px;
width: 10px; height: 10px; border-radius: 50%;
background: #fff;
border: 2px solid #1a1a1a;
box-sizing: border-box;
box-shadow: 0 0 0 3px rgba(255,214,0,.55), 0 1px 3px rgba(0,0,0,.25);
animation: kickoffLeaderDot 1.6s ease-in-out infinite;
}
/* 一键比价入口 · 右下角 kickoff 卡(leader 偏右对齐侧贴按钮中心)*/
.start-compare-kickoff {
position: absolute;
right: 14px;
width: 310px;
bottom: 246px;
z-index: 16;
pointer-events: none;
}
.start-compare-kickoff .kickoff-leader {
left: auto;
right: 50px;
margin-left: 0;
}
/* 比价中浮窗 · 三平台并行 mini icons */
.cmp-platform-mini { position: relative; width: 24px; height: 24px; }
.cmp-platform-mini > img { width: 100%; height: 100%; border-radius: 4px; object-fit: cover; display: block; }
.cmp-platform-mini.done::after {
content: ''; position: absolute; bottom: -3px; right: -3px;
width: 12px; height: 12px; border-radius: 50%; background: #22C55E;
border: 1.5px solid #fff;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10'%3E%3Cpath d='M2 5l2 2 4-5' stroke='%23fff' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
background-size: 8px; background-repeat: no-repeat; background-position: center;
}
.cmp-platform-mini.active::after {
content: ''; position: absolute; inset: -4px;
border: 2px solid #FFD600; border-top-color: transparent;
border-radius: 50%; animation: spin 0.8s linear infinite;
}
.cmp-platform-mini.pending { opacity: 0.4; filter: grayscale(0.6); }
/* ============ 2. VIDEO 教程页 (vt-) ============ */
#video .vt-video-player {
flex-shrink: 0; height: 667px; background: #000;
position: relative; overflow: hidden;
}
#video .vt-poster {
position: absolute; inset: 0;
background: radial-gradient(ellipse at 25% 15%, rgba(255,255,255,.45) 0%, transparent 55%),
radial-gradient(ellipse at 80% 90%, rgba(255,87,34,.18) 0%, transparent 60%),
linear-gradient(160deg, #FFE57F 0%, #FFD54F 35%, #FFB300 75%, #F57F17 100%);
display: flex; align-items: center; justify-content: center;
cursor: pointer; overflow: hidden;
}
#video .vt-poster::before {
content: ''; position: absolute; inset: 0;
background: radial-gradient(circle at 18% 88%, rgba(255,255,255,.15) 0%, transparent 8%),
radial-gradient(circle at 88% 22%, rgba(255,255,255,.18) 0%, transparent 6%);
pointer-events: none;
}
#video .vt-video-player.paused .vt-poster::after,
#video .vt-video-player.initial .vt-poster::after {
content: ''; position: absolute; inset: 0;
background: rgba(0,0,0,.18); z-index: 2; pointer-events: none;
}
/* 商品对比卡 */
#video .vt-poster-compare {
display: flex; align-items: center; gap: 8px;
background: rgba(255,255,255,.95); border-radius: 18px;
padding: 12px 16px;
box-shadow: 0 14px 36px rgba(140,70,0,.22), 0 2px 6px rgba(0,0,0,.08);
z-index: 2; position: relative;
}
#video .vt-pc-card {
display: flex; flex-direction: column; align-items: center;
gap: 4px; padding: 4px 8px; min-width: 70px; position: relative;
}
#video .vt-pc-label { font-size: 10px; color: #999; font-weight: 600; letter-spacing: .3px; }
#video .vt-pc-card.win .vt-pc-label {
color: #fff; background: linear-gradient(135deg, #FF6B6B, #E53935);
padding: 2px 8px; border-radius: 4px;
box-shadow: 0 2px 4px rgba(229,57,53,.3);
}
#video .vt-pc-price {
font-size: 22px; font-weight: 800; color: #1a1a1a;
font-variant-numeric: tabular-nums; letter-spacing: -.5px;
}
#video .vt-pc-price.strike { color: #CCCCCC; text-decoration: line-through; font-size: 18px; font-weight: 600; }
#video .vt-pc-price.now { color: #E53935; font-size: 24px; }
#video .vt-pc-arrow { color: #E65100; font-size: 22px; font-weight: 600; text-shadow: 0 2px 4px rgba(255,111,0,.3); }
#video .vt-pc-save-tag {
position: absolute; top: -10px; right: -8px;
font-size: 10px; font-weight: 600; color: #fff;
background: linear-gradient(135deg, #FF6B6B, #E53935);
padding: 2px 6px; border-radius: 8px 8px 8px 4px;
box-shadow: 0 2px 6px rgba(229,57,53,.45);
transform: rotate(8deg); white-space: nowrap;
}
/* 装饰金币 */
#video .vt-coin {
position: absolute; z-index: 1; border-radius: 50%;
background: linear-gradient(135deg, #FFE066 0%, #FFC300 50%, #FFA000 100%);
display: flex; align-items: center; justify-content: center;
font-weight: 800; color: #5A3A00;
box-shadow: 0 4px 10px rgba(120,60,0,.25),
inset 0 1px 2px rgba(255,255,255,.7),
inset 0 -1px 2px rgba(139,69,19,.3);
border: 1.5px solid rgba(180,100,30,.25);
}
#video .vt-coin-tl { top: 14%; left: 10%; width: 36px; height: 36px; font-size: 17px; transform: rotate(-12deg); animation: vtFloatA 3.2s ease-in-out infinite; }
#video .vt-coin-tr { top: 22%; right: 9%; width: 28px; height: 28px; font-size: 13px; transform: rotate(18deg); animation: vtFloatB 3.6s ease-in-out infinite .3s; }
#video .vt-coin-bl { bottom: 28%; left: 14%; width: 24px; height: 24px; font-size: 12px; transform: rotate(8deg); animation: vtFloatA 3.8s ease-in-out infinite .6s; }
#video .vt-coin-br { bottom: 18%; right: 12%; width: 32px; height: 32px; font-size: 15px; transform: rotate(-15deg); animation: vtFloatB 3.4s ease-in-out infinite .9s; }
@keyframes vtFloatA { 0%,100% { transform: rotate(-12deg) translateY(0); } 50% { transform: rotate(-12deg) translateY(-8px); } }
@keyframes vtFloatB { 0%,100% { transform: rotate(18deg) translateY(0); } 50% { transform: rotate(18deg) translateY(-10px); } }
/* 闪光 */
#video .vt-sparkle {
position: absolute; z-index: 1; width: 8px; height: 8px;
background: linear-gradient(to bottom, transparent 45%, rgba(255,255,255,.9) 50%, transparent 55%),
linear-gradient(to right, transparent 45%, rgba(255,255,255,.9) 50%, transparent 55%);
transform: rotate(45deg);
animation: vtSparkle 2.4s ease-in-out infinite;
}
#video .vt-sparkle-1 { top: 38%; left: 22%; }
#video .vt-sparkle-2 { top: 56%; right: 24%; animation-delay: .6s; }
#video .vt-sparkle-3 { top: 70%; left: 38%; width: 6px; height: 6px; animation-delay: 1.2s; }
@keyframes vtSparkle { 0%,100% { opacity: 0; transform: rotate(45deg) scale(.5); } 50% { opacity: 1; transform: rotate(45deg) scale(1); } }
/* dim overlay */
#video .vt-dim-overlay {
position: absolute; inset: 0; z-index: 4;
background: rgba(0,0,0,.5); pointer-events: none;
opacity: 0; transition: opacity .35s;
}
#video .vt-video-player.ended .vt-dim-overlay { opacity: 1; }
/* center play */
#video .vt-center-play {
position: absolute; left: 50%; top: 50%;
width: 72px; height: 72px;
background: rgba(0,0,0,.5); border-radius: 50%;
display: flex; align-items: center; justify-content: center;
transform: translate(-50%, -50%); cursor: pointer;
box-shadow: 0 8px 24px rgba(0,0,0,.3);
transition: opacity .2s, background .15s; z-index: 6;
}
#video .vt-center-play svg { width: 30px; height: 30px; fill: #fff; margin-left: 2px; }
#video .vt-video-player.playing .vt-center-play { opacity: 0; pointer-events: none; }
#video .vt-video-player.paused .vt-center-play,
#video .vt-video-player.initial .vt-center-play {
opacity: 1; visibility: visible; pointer-events: auto;
}
#video .vt-video-player.ended .vt-center-play {
width: 80px; height: 80px;
background: rgba(255,255,255,.18);
border: 2px solid rgba(255,255,255,.45);
backdrop-filter: blur(8px);
}
#video .vt-video-player.ended .vt-center-play svg { width: 32px; height: 32px; margin-left: 0; }
/* controls */
#video .vt-controls {
position: absolute; left: 0; right: 0; bottom: 0;
padding: 12px 12px 16px;
background: linear-gradient(180deg, transparent, rgba(0,0,0,.7) 55%);
z-index: 3;
}
#video .vt-progress-row { display: flex; align-items: center; gap: 8px; }
#video .vt-time {
font-size: 11px; font-weight: 600; color: #fff;
min-width: 32px; text-align: center; font-variant-numeric: tabular-nums;
}
#video .vt-ctrl-pill {
width: 36px; height: 36px; border-radius: 50%;
background: rgba(255,255,255,.18);
display: flex; align-items: center; justify-content: center;
cursor: pointer; flex-shrink: 0;
transition: background .15s, transform .12s;
backdrop-filter: blur(4px);
}
#video .vt-ctrl-pill:active { transform: scale(.92); }
#video .vt-ctrl-pill svg { width: 16px; height: 16px; fill: #fff; }
/* track 24px 大命中区 */
#video .vt-progress-track {
flex: 1; height: 24px; background: transparent;
display: flex; align-items: center; position: relative; cursor: pointer;
}
#video .vt-progress-track::before {
content: ''; position: absolute; left: 0; right: 0; top: 50%;
transform: translateY(-50%); height: 4px;
background: rgba(255,255,255,.3); border-radius: 4px;
pointer-events: none;
}
#video .vt-progress-fill {
position: absolute; left: 0; top: 50%;
transform: translateY(-50%); height: 4px;
background: linear-gradient(90deg, #FFE066, #FFD600);
border-radius: 4px; pointer-events: none;
transition: width .08s linear; width: 0%;
}
#video .vt-progress-fill::after {
content: ''; position: absolute; right: -5px; top: 50%;
width: 11px; height: 11px; background: #FFD600;
border-radius: 50%; transform: translateY(-50%);
box-shadow: 0 0 0 3px rgba(255,214,0,.25);
}
/* desc */
#video .vt-desc-body {
flex: 1; display: flex; align-items: center; justify-content: center;
text-align: center; padding: 16px 24px;
}
#video .vt-desc {
font-size: 22px; font-weight: 600; color: var(--gray-700);
letter-spacing: .5px; line-height: 1.4;
}
/* CTA */
#video .vt-cta-bar { padding: 12px 16px 32px; flex-shrink: 0; }
#video .vt-cta-btn {
width: 100%; padding: 16px 0; border: none;
background: linear-gradient(135deg, #FFE066 0%, #FFD600 50%, #FFB300 100%);
color: #1a1a1a; border-radius: 14px;
font-size: 17px; font-weight: 700; cursor: pointer;
box-shadow: 0 6px 18px rgba(255,179,0,.45),
inset 0 1px 0 rgba(255,255,255,.6);
position: relative; overflow: hidden;
letter-spacing: .5px; transition: transform .15s;
}
#video .vt-cta-btn:active { transform: scale(.98); }
#video .vt-cta-btn.disabled {
background: #E5E5E5 !important;
background-image: none !important;
color: #CCCCCC !important;
box-shadow: none !important;
cursor: not-allowed; font-weight: 600; letter-spacing: 0;
}
/* ============ Home v2026-05-10 (image-asset driven) ============ */
/* 原来是 padding: 40px 0 16px,但 padding-top 会推高 sticky 的 scrollport 起点,
让 brand-strip 贴顶时在 y=84 而非 y=44,上方 40px padding 区域露出滚动卡片。
改为把 40px 挪到 .brand-strip 的 margin-topsticky 才能真正贴到 scrollport 顶部 y=44 */
#home .home-scroll { padding: 0; }
/* 顶部品牌带 wordmark —— 38px 极薄 stripsticky 贴顶(与 .feed-sort 接力,两层共同形成 header*/
#home .brand-strip {
height: 38px;
margin-top: 40px; /* 替代原 home-scroll 的 padding-top:40px */
padding: 0 16px;
display: flex;
align-items: center;
justify-content: space-between;
background: #F5F5F5; /* 同 page bg,未贴顶时无视觉变化;贴顶后挡住下方滚动内容 */
position: sticky;
top: 0;
z-index: 5;
}
#home .brand-strip-left {
display: flex;
align-items: center;
gap: 7px;
}
#home .brand-strip-logo {
width: 26px;
height: 26px;
display: block;
flex-shrink: 0;
border-radius: 6px;
}
#home .brand-strip-name {
font-size: 19px;
font-weight: 800;
color: #1a1a1a;
letter-spacing: .8px;
line-height: 1;
}
#home .brand-strip-tagline {
font-size: 12px;
font-weight: 700;
color: #666666;
letter-spacing: .2px;
}
/* 顶部 Banner —— 可滑动轮播(3 个点位) */
#home .home-banner {
position: relative;
margin: 8px 12px 8px;
border-radius: 18px;
overflow: hidden;
box-shadow: 0 6px 20px rgba(255,179,0,.18);
touch-action: pan-y;
}
#home .home-banner-track {
display: flex;
width: 200%;
transition: transform .35s cubic-bezier(.4,0,.2,1);
will-change: transform;
}
#home .home-banner-track.dragging { transition: none; }
#home .home-banner-slide {
width: 50%;
flex-shrink: 0;
cursor: pointer;
-webkit-user-select: none;
user-select: none;
}
#home .home-banner-slide img {
width: 100%;
display: block;
pointer-events: none;
-webkit-user-drag: none;
}
#home .home-banner-slide-fill {
width: 100%;
aspect-ratio: 1793 / 677;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
padding: 0 28px;
color: #fff;
box-sizing: border-box;
position: relative;
overflow: hidden;
}
#home .home-banner-slide-fill.bf-orange {
background: linear-gradient(135deg, #FF9A56 0%, #FF6B35 60%, #F4511E 100%);
}
#home .home-banner-slide-fill.bf-blue {
background: linear-gradient(135deg, #5EC9F5 0%, #2196F3 60%, #1565C0 100%);
}
#home .home-banner-slide-fill .bf-title {
font-size: 24px;
font-weight: 800;
line-height: 1.2;
letter-spacing: -.2px;
text-shadow: 0 1px 2px rgba(0,0,0,.12);
}
#home .home-banner-slide-fill .bf-pill {
margin-top: 12px;
display: inline-flex;
align-items: center;
gap: 4px;
padding: 6px 12px;
border-radius: 999px;
background: rgba(255,255,255,.92);
color: #1A1A1A;
font-size: 12px;
font-weight: 600;
}
#home .home-banner-slide-fill .bf-deco {
position: absolute;
right: -10px;
bottom: -10px;
font-size: 110px;
line-height: 1;
opacity: .85;
filter: drop-shadow(0 4px 12px rgba(0,0,0,.18));
pointer-events: none;
}
#home .home-banner-dots {
position: absolute;
right: 12px;
bottom: 10px;
display: flex;
gap: 5px;
z-index: 2;
}
#home .home-banner-dot {
width: 5px;
height: 5px;
border-radius: 50%;
background: rgba(255,255,255,.45);
transition: background .2s ease;
}
#home .home-banner-dot.active {
background: #fff;
}
/* ============ Home v2026-06-01: 节省日志横条(banner ↔ 双卡之间,左侧脉冲点 + 滚动 feed ============ */
#home .home-marquee {
margin: 0 12px;
height: 32px;
border-radius: 16px;
background: linear-gradient(135deg, #FFF8E1 0%, #FFECB3 100%);
border: 1px solid #FFE082;
display: flex;
align-items: center;
padding: 0 12px 0 40px;
gap: 10px;
overflow: hidden;
box-shadow: 0 2px 6px rgba(255, 140, 60, .08);
-webkit-tap-highlight-color: transparent;
}
#home .home-mq-dot {
flex-shrink: 0;
width: 16px; height: 16px;
display: inline-flex;
align-items: center;
justify-content: center;
color: #FF5722;
}
#home .home-mq-dot svg { display: block; width: 100%; height: 100%; }
#home .home-mq-viewport {
flex: 1;
height: 22px;
overflow: hidden;
position: relative;
}
#home .home-mq-track {
position: absolute; left: 0; top: 0; right: 0;
display: block;
will-change: transform;
}
#home .home-mq-item {
height: 22px;
display: grid;
grid-template-columns: 16px max-content;
column-gap: 9px;
align-items: center;
justify-content: start;
font-size: 12px; color: #1a1a1a;
letter-spacing: .1px;
white-space: nowrap;
overflow: hidden;
}
#home .home-mq-copy {
--home-mq-letter-gap: .1px;
--home-mq-time-user-gap: 7px;
display: grid;
grid-template-columns:
max-content
var(--home-mq-time-user-gap)
var(--home-mq-user-slot, 41px)
var(--home-mq-letter-gap)
max-content
var(--home-mq-letter-gap)
var(--home-mq-amount-slot, 48px)
var(--home-mq-letter-gap)
max-content;
align-items: baseline;
white-space: nowrap;
letter-spacing: .1px;
}
#home .home-mq-item .mq-time {
grid-column: 1;
color: #8B7355; font-weight: 700; font-size: 13px;
font-family: -apple-system, "DIN Alternate", sans-serif;
letter-spacing: .2px;
font-variant-numeric: tabular-nums;
}
#home .home-mq-item .mq-user {
grid-column: 3;
justify-self: center;
text-align: center;
color: #4D4D4D;
overflow: visible;
font-variant-numeric: tabular-nums;
}
#home .home-mq-item .mq-act {
grid-column: 5;
color: #4D4D4D;
overflow: visible;
text-overflow: clip;
}
#home .home-mq-item .mq-amt {
grid-column: 7;
justify-self: center;
color: #FF5722; font-weight: 800;
font-family: -apple-system, "DIN Alternate", sans-serif;
font-size: 13px;
letter-spacing: .1px;
font-variant-numeric: tabular-nums;
}
#home .home-mq-item .mq-unit {
grid-column: 9;
color: #4D4D4D;
}
#home .home-mq-measure {
position: absolute;
left: -9999px;
top: -9999px;
visibility: hidden;
white-space: nowrap;
font-size: 12px;
letter-spacing: .1px;
pointer-events: none;
}
#home .home-mq-measure .mq-user {
color: #4D4D4D;
font-variant-numeric: tabular-nums;
}
#home .home-mq-measure .mq-amt {
color: #FF5722;
font-weight: 800;
font-family: -apple-system, "DIN Alternate", sans-serif;
font-size: 13px;
letter-spacing: .1px;
font-variant-numeric: tabular-nums;
}
/* ============ Home v2026-06-03: 平台 3 数据板块(纯色 + 深字 + 竖线分隔,无阴影) ============ */
#home .home-3stat {
margin: 8px 12px 0;
background: #FFD600;
border-radius: 12px;
padding: 9px 6px;
display: grid;
grid-template-columns: 1fr 1fr 1fr;
box-sizing: border-box;
}
#home .h3s-card {
background: transparent;
text-align: center;
position: relative;
padding: 0 4px;
box-sizing: border-box;
}
#home .h3s-card + .h3s-card::before {
content: '';
position: absolute;
left: 0; top: 14%;
width: 1px; height: 72%;
background: linear-gradient(180deg, transparent, rgba(0,0,0,.18) 30%, rgba(0,0,0,.18) 70%, transparent);
}
#home .h3s-label {
font-size: 10px; font-weight: 700;
color: #1A1A1A; opacity: .75;
letter-spacing: .3px;
margin-bottom: 3px;
line-height: 1;
}
#home .h3s-num {
font-family: -apple-system, "DIN Alternate", sans-serif;
font-size: 20px; font-weight: 900; color: #1A1A1A;
line-height: 1; letter-spacing: -.3px;
display: block;
text-align: center;
white-space: nowrap;
font-variant-numeric: tabular-nums;
}
#home .h3s-value {
position: relative;
display: inline-block;
}
#home .h3s-num .h3s-yuan { font-size: 13px; font-weight: 800; margin-right: 1px; color: #1A1A1A; }
#home .h3s-num .h3s-unit {
position: absolute;
left: 100%;
bottom: 1px;
font-size: 11px;
font-weight: 800;
margin-left: 1px;
color: #1A1A1A;
opacity: .8;
}
/* 最近一次比价记录:结果摘要入口 */
#home .last-compare-banner {
margin: 8px 12px 0;
padding: 10px;
border-radius: 12px;
background: #FFFDF5;
border: 1px solid rgba(255,214,0,.72);
box-shadow: 0 4px 12px rgba(255,179,0,.12);
cursor: pointer;
opacity: 1;
transform: none;
transition: transform .12s ease, opacity .12s ease;
-webkit-tap-highlight-color: transparent;
}
#home .last-compare-banner:active {
transform: scale(.985);
opacity: .92;
}
#home .last-compare-banner .lcb-foot { display: none !important; }
#home .last-compare-banner .lcb-platform { display: none !important; }
#home .last-compare-banner::after {
content: '';
position: absolute;
right: 4px;
top: 50%;
transform: translateY(-50%);
width: 18px; height: 22px;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'><path d='M9 6l6 6-6 6' stroke='%231A1A1A' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'/></svg>");
background-size: contain;
background-repeat: no-repeat;
background-position: center;
pointer-events: none;
}
#home .last-compare-banner.visible {
display: block;
opacity: 1;
transform: translateY(0);
}
#home .lcb-head,
#home .lcb-body,
#home .lcb-foot,
#home .lcb-top,
#home .lcb-platform {
display: flex;
align-items: center;
min-width: 0;
}
#home .lcb-head {
justify-content: flex-start;
gap: 8px;
margin-bottom: 7px;
}
#home .lcb-body { gap: 10px; }
#home .lcb-main {
min-width: 0;
flex: 1;
display: flex;
flex-direction: column;
gap: 5px;
}
#home .lcb-top { gap: 6px; }
#home .lcb-kicker {
flex-shrink: 0;
font-size: 11px;
font-weight: 700;
color: #8B6914;
padding: 2px 7px;
border-radius: 999px;
background: rgba(255,214,0,.26);
line-height: 1.1;
}
#home .lcb-status {
display: none;
}
#home .lcb-shop {
min-width: 0;
flex: 1;
font-size: 14px;
font-weight: 800;
color: #1A1A1A;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
#home .lcb-meta {
min-width: 0;
font-size: 12px;
font-weight: 500;
color: #666;
line-height: 1.25;
}
#home .lcb-dish {
display: block;
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#home .lcb-pricebox {
width: 90px;
min-height: 56px;
flex-shrink: 0;
border-radius: 10px;
background: linear-gradient(180deg,#FFE066 0%,#FFD600 100%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 3px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.58);
}
#home .lcb-price-label {
color: #8B6914;
font-size: 10px;
font-weight: 700;
line-height: 1;
}
#home .lcb-price {
color: #FF5722;
font-size: 20px;
font-weight: 900;
line-height: 1;
white-space: nowrap;
}
#home .lcb-platform {
gap: 4px;
min-width: 0;
color: #5A3A00;
font-size: 10px;
font-weight: 700;
line-height: 1;
max-width: 82px;
}
#home .lcb-platform img {
width: 14px;
height: 14px;
border-radius: 4px;
object-fit: cover;
flex-shrink: 0;
background: #fff;
}
#home .lcb-platform-name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
#home .lcb-foot {
justify-content: flex-end;
gap: 4px;
margin-top: 7px;
}
#home .lcb-note {
color: #8B6914;
font-size: 11px;
font-weight: 700;
white-space: nowrap;
}
#home .lcb-arrow {
flex-shrink: 0;
color: #8B6914;
width: 16px;
height: 16px;
border-radius: 8px;
background: rgba(255,214,0,.22);
display: flex;
align-items: center;
justify-content: center;
}
#home .lcb-arrow svg { width: 11px; height: 11px; display: block; }
#home .last-compare-banner.lcb-v-summary .lcb-pricebox {
background: #FFF3C4;
box-shadow: none;
}
#home .last-compare-banner.lcb-v-price {
background: #FFFFFF;
border-color: rgba(26,26,26,.08);
box-shadow: 0 4px 12px rgba(0,0,0,.06);
}
#home .last-compare-banner.lcb-v-price .lcb-pricebox {
background: #1A1A1A;
color: #FFD600;
}
#home .last-compare-banner.lcb-v-price .lcb-price-label,
#home .last-compare-banner.lcb-v-price .lcb-platform {
color: #FFD600;
}
#home .last-compare-banner.lcb-v-price .lcb-price { color: #FFD600; }
#home .last-compare-banner.lcb-v-price .lcb-platform img { border: 1px solid rgba(255,255,255,.7); }
#home .last-compare-banner.lcb-v-ticket {
background: linear-gradient(180deg,#FFFDF5 0%,#FFF8E6 100%);
}
#home .last-compare-banner.lcb-v-ticket .lcb-body {
display: block;
}
#home .last-compare-banner.lcb-v-ticket .lcb-main {
gap: 6px;
}
#home .last-compare-banner.lcb-v-ticket .lcb-pricebox {
width: auto;
min-height: 30px;
margin-top: 7px;
padding: 0 9px;
flex-direction: row;
justify-content: space-between;
background: #fff;
border: 1px dashed rgba(255,179,0,.72);
box-shadow: none;
}
#home .last-compare-banner.lcb-v-ticket .lcb-price {
font-size: 17px;
}
#home .last-compare-banner.lcb-v-ticket .lcb-platform {
max-width: 120px;
color: #666;
}
#home .last-compare-banner.lcb-v-split {
min-height: 88px;
padding: 0;
overflow: hidden;
background: #fff;
border-color: #FFD600;
display: grid;
grid-template-columns: minmax(0,1fr) 98px;
}
#home .last-compare-banner.lcb-v-split .lcb-head,
#home .last-compare-banner.lcb-v-split .lcb-main,
#home .last-compare-banner.lcb-v-split .lcb-foot {
padding-left: 10px;
}
#home .last-compare-banner.lcb-v-split .lcb-head {
grid-column: 1;
margin: 9px 0 6px;
padding-right: 0;
}
#home .last-compare-banner.lcb-v-split .lcb-body {
display: contents;
}
#home .last-compare-banner.lcb-v-split .lcb-main {
grid-column: 1;
padding-right: 8px;
}
#home .last-compare-banner.lcb-v-split .lcb-pricebox {
grid-column: 2;
grid-row: 1 / span 3;
width: 98px;
min-height: 88px;
border-radius: 0;
background: linear-gradient(180deg,#FFE066 0%,#FFD600 100%);
}
#home .last-compare-banner.lcb-v-split .lcb-foot {
grid-column: 1;
justify-content: flex-start;
margin: 5px 0 9px;
}
#home .last-compare-banner.lcb-v-leftprice {
min-height: 92px;
padding: 8px;
display: grid;
grid-template-columns: 90px minmax(0,1fr);
column-gap: 10px;
align-items: stretch;
background: #FFFFFF;
border-color: #FFD600;
box-shadow: 0 5px 14px rgba(255,179,0,.16);
}
#home .last-compare-banner.lcb-v-leftprice .lcb-body {
display: contents;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-pricebox {
grid-column: 1;
grid-row: 1 / span 3;
width: 90px;
min-height: 76px;
border-radius: 10px;
background: linear-gradient(180deg,#FFE066 0%,#FFD600 100%);
}
#home .last-compare-banner.lcb-v-leftprice .lcb-price-label {
color: #8B6914;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-price {
color: #FF5722;
font-size: 19px;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-platform {
max-width: 78px;
color: #5A3A00;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-head {
grid-column: 2;
margin: 2px 0 5px;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-kicker {
padding: 0;
background: transparent;
color: #8B6914;
font-size: 11px;
font-weight: 700;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-main {
grid-column: 2;
justify-content: center;
gap: 4px;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-shop {
font-size: 14px;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-meta {
font-size: 12px;
color: #666;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-foot {
grid-column: 2;
justify-content: flex-start;
margin: 5px 0 2px;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-note {
height: 20px;
padding: 0 7px;
border-radius: 999px;
background: rgba(255,214,0,.22);
color: #8B6914;
display: inline-flex;
align-items: center;
}
#home .last-compare-banner.lcb-v-leftprice .lcb-arrow {
width: 18px;
height: 18px;
border-radius: 9px;
}
/* lcb-v-reference (= B6 final): 顶角红 chip + 左栈店名菜名 + 右栈最低价/¥57.9/查看详情 pill */
#home .last-compare-banner.lcb-v-reference {
display: grid;
grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
align-items: center;
column-gap: 12px;
row-gap: 1px;
min-height: auto;
padding: 24px 12px 8px 12px;
position: relative;
background: linear-gradient(90deg,#FFF7D8 0%,#FFFDF8 58%,#FFF2C8 100%);
border: 0;
border-radius: 14px;
box-shadow: inset 0 0 0 1px rgba(255,214,0,.50), 0 4px 12px rgba(255,179,0,.10);
overflow: hidden;
}
/* 隐藏 base banner 的右箭头 ::after */
#home .last-compare-banner.lcb-v-reference::after {
display: none !important;
}
/* body / main / pricebox 用 display: contents(左栈)或 grid 项(右栈) */
#home .last-compare-banner.lcb-v-reference .lcb-body { display: contents; }
#home .last-compare-banner.lcb-v-reference .lcb-main { display: contents; }
#home .last-compare-banner.lcb-v-reference .lcb-foot { display: none !important; }
#home .last-compare-banner.lcb-v-reference .lcb-arrow { display: none; }
#home .last-compare-banner.lcb-v-reference .lcb-platform { display: none !important; }
/* chip 「最近比价」 顶角红块带阴影 */
#home .last-compare-banner.lcb-v-reference .lcb-head {
position: absolute;
left: 12px;
top: 0;
margin: 0;
padding: 0;
z-index: 2;
display: inline-flex;
}
#home .last-compare-banner.lcb-v-reference .lcb-kicker {
height: 22px;
padding: 0 10px;
border-radius: 0 0 6px 6px;
background: #FF5722;
color: #FFFFFF;
font-size: 0;
line-height: 1;
box-shadow: 0 2px 5px rgba(255,87,34,.16);
display: inline-flex;
align-items: center;
}
#home .last-compare-banner.lcb-v-reference .lcb-kicker::before {
content: '最近比价';
font-size: 11px;
font-weight: 700;
color: #FFFFFF;
line-height: 1;
letter-spacing: .3px;
}
/* 行 1 左:店名 */
#home .last-compare-banner.lcb-v-reference .lcb-top {
grid-row: 1;
grid-column: 1;
justify-self: start;
align-self: center;
display: flex;
align-items: center;
min-width: 0;
max-width: 100%;
}
#home .last-compare-banner.lcb-v-reference .lcb-shop {
font-size: 15px;
font-weight: 800;
line-height: 1.2;
color: #1A1A1A;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100%;
letter-spacing: 0;
}
/* 行 2 左:菜名 — 对齐首页 .tc-sub 视觉权重 */
#home .last-compare-banner.lcb-v-reference .lcb-meta {
grid-row: 2;
grid-column: 1;
justify-self: start;
align-self: start;
font-size: 10px;
font-weight: 400;
color: #666;
line-height: 1.2;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
min-width: 0;
max-width: 100%;
}
/* 右栈:透明 column,最低价 + ¥57.9 + pill — 收紧版(方案 A 微调 + 上移 4px) */
#home .last-compare-banner.lcb-v-reference .lcb-pricebox {
display: flex;
flex-direction: column;
align-items: stretch;
justify-content: center;
gap: 2px;
grid-row: 1 / 3;
grid-column: 2;
justify-self: end;
align-self: center;
position: static;
width: 92px;
min-height: 0;
height: auto;
background: transparent;
border-radius: 0;
padding: 0;
box-shadow: none;
flex-shrink: 0;
line-height: 1;
transform: translateY(-4px);
}
#home .last-compare-banner.lcb-v-reference .lcb-pricebox::before {
display: none;
}
#home .last-compare-banner.lcb-v-reference .lcb-price-label {
font-size: 11px;
font-weight: 700;
color: #8B6914;
letter-spacing: .2px;
line-height: 1;
padding: 0;
background: transparent;
align-self: center;
border-radius: 0;
height: auto;
min-width: 0;
display: inline-flex;
align-items: center;
justify-content: center;
}
#home .last-compare-banner.lcb-v-reference .lcb-price {
color: #FF5722;
font-family: "DIN Alternate", "Helvetica Neue", sans-serif;
font-size: 22px;
font-weight: 800;
line-height: 1;
letter-spacing: 0;
font-variant-numeric: tabular-nums;
display: inline-flex;
align-items: baseline;
gap: 0;
align-self: center;
}
#home .last-compare-banner.lcb-v-reference .lcb-price-currency {
font-size: 15px;
font-weight: 800;
line-height: 1;
}
#home .last-compare-banner.lcb-v-reference .lcb-price-int {
font-size: 22px;
font-weight: 800;
line-height: 1;
}
#home .last-compare-banner.lcb-v-reference .lcb-price-dec {
font-size: 12px;
font-weight: 800;
line-height: 1;
}
/* 右栈底 满宽 CTA pill「查看详情 ›」— 校准到 DESIGN.md cta-primary 标准 */
#home .last-compare-banner.lcb-v-reference .lcb-detail-cta {
align-self: stretch;
width: 100%;
height: 22px;
padding: 0 8px;
border-radius: 999px;
background: linear-gradient(180deg,#FFE066 0%,#FFC400 100%);
color: #5A3A00;
font-size: 11px;
font-weight: 800;
line-height: 1;
letter-spacing: .3px;
border: none;
cursor: pointer;
white-space: nowrap;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 4px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 6px rgba(255,179,0,.35);
pointer-events: none;
}
#home .last-compare-banner.lcb-v-reference .lcb-detail-cta::after {
content: '';
display: inline-block;
width: 4px; height: 4px;
border-top: 1.7px solid currentColor;
border-right: 1.7px solid currentColor;
transform: rotate(45deg) translate(-1px, 1px);
}
#home .last-compare-banner.lcb-v-tagright .lcb-head {
left: auto;
right: 104px;
top: 8px;
}
#home .last-compare-banner.lcb-v-tagright .lcb-kicker {
height: 18px;
padding: 0 8px;
border-radius: 999px;
background: rgba(255,214,0,.32);
color: #7A5600;
box-shadow: none;
}
#home .last-compare-banner.lcb-v-tagright .lcb-main {
padding-top: 8px;
}
#home .last-compare-banner.lcb-v-topbar .lcb-head {
left: 0;
right: 96px;
top: 0;
height: 23px;
margin: 0;
justify-content: center;
background: linear-gradient(90deg, rgba(255,214,0,.18), rgba(255,214,0,.04));
border-bottom: 1px solid rgba(255,214,0,.18);
}
#home .last-compare-banner.lcb-v-topbar .lcb-kicker {
height: auto;
padding: 0;
border-radius: 0;
background: transparent;
color: #8B6914;
box-shadow: none;
font-size: 10px;
letter-spacing: .4px;
}
#home .last-compare-banner.lcb-v-topbar .lcb-main {
padding-top: 17px;
}
#home .last-compare-banner.lcb-v-pricebadge .lcb-head {
left: auto;
right: 15px;
top: 7px;
}
#home .last-compare-banner.lcb-v-pricebadge .lcb-kicker {
height: 16px;
padding: 0 6px;
border-radius: 999px;
background: rgba(255,255,255,.78);
color: #8B6914;
box-shadow: inset 0 0 0 1px rgba(255,214,0,.36);
font-size: 9px;
}
#home .last-compare-banner.lcb-v-pricebadge .lcb-price-label {
margin-top: 10px;
}
#home .last-compare-banner.lcb-v-pricebadge .lcb-main {
padding-top: 7px;
}
#home .last-compare-banner.lcb-v-mutedright .lcb-head {
left: auto;
right: 108px;
top: 11px;
}
#home .last-compare-banner.lcb-v-mutedright .lcb-kicker {
height: auto;
padding: 0;
border-radius: 0;
background: transparent;
color: #9B8C6A;
box-shadow: none;
font-size: 10px;
font-weight: 600;
letter-spacing: .2px;
}
#home .last-compare-banner.lcb-v-mutedright .lcb-main {
padding-top: 8px;
}
/* 5 类别快捷入口(无白底卡片,直接贴页面背景) */
#home .home-cats {
display: grid; grid-template-columns: repeat(5, 1fr);
margin: 0 12px;
padding: 6px 4px 4px;
background: transparent;
border-radius: 0;
box-shadow: none;
}
#home .home-cat {
display: flex; flex-direction: column; align-items: center;
cursor: pointer; transition: transform .12s;
padding: 0 2px;
}
#home .home-cat:active { transform: scale(.94); }
#home .home-cat img {
width: 48px; height: 48px; display: block; margin-bottom: 4px;
border-radius: 12px;
}
#home .home-cat-label {
font-size: 12px; font-weight: 600; color: #1a1a1a; letter-spacing: .2px;
}
/* 三联:左 双数据卡(HTML+CSS)+ 右 一键领优惠(橙色整卡) */
#home .home-tri {
display: grid; grid-template-columns: minmax(0, 1fr) 32px minmax(0, 1fr); gap: 4px;
padding: 8px 12px 8px;
align-items: stretch;
}
#home .home-tri-arrow {
align-self: center;
justify-self: center;
width: 28.8px;
height: 19.8px;
pointer-events: none;
z-index: 4;
background-image: url('assets/home/index/chevron-double-fade.png');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
#home .home-tri-arrow svg { display: none; }
#home .tri-stats {
display: flex; flex-direction: column; gap: 8px;
height: 100%;
}
#home .tri-stat {
position: relative;
flex: 1; min-height: 84px;
border-radius: 14px;
background: linear-gradient(135deg,#FFF3E0 0%,#FFE0B2 100%);
padding: 12px 12px;
cursor: pointer; transition: transform .12s;
box-shadow: 0 4px 14px rgba(255,140,60,.10);
overflow: hidden;
}
#home .tri-stat:active { transform: translateY(1px); }
#home .tri-stat-label {
font-size: 13px; font-weight: 700; color: #1a1a1a; letter-spacing: .2px;
line-height: 1;
}
#home .tri-stat-amount {
margin-top: 8px;
color: #FF5722; font-weight: 900;
display: inline-flex; align-items: baseline; gap: 0;
letter-spacing: -.5px; line-height: 1;
}
/* Price Golden Ratio: 整数 28 → ¥ 20 (×0.7), dec 15 (×0.55) + tabular-nums */
#home .tri-stat-amount { font-variant-numeric: tabular-nums; }
#home .tri-stat-amount .yuan { font-size: 20px; font-weight: 900; margin-right: 2px; }
#home .tri-stat-amount .num { font-size: 28px; line-height: 1; }
#home .tri-stat-amount .dec { font-size: 15px; font-weight: 900; margin-left: 2px; }
#home .tri-stat-art {
position: absolute; right: 6px; bottom: 4px;
width: 70px; height: 64px;
pointer-events: none;
user-select: none;
filter: drop-shadow(0 4px 6px rgba(180,80,20,.12));
}
#home .tri-stat-art img {
width: 100%; height: 100%; object-fit: contain; object-position: right bottom; display: block;
}
#home .tri-claim {
position: relative; overflow: hidden;
border-radius: 14px;
cursor: pointer; transition: transform .12s;
box-shadow: 0 8px 22px rgba(255,87,34,.22);
background: linear-gradient(140deg,#FFE066 0%,#FF5722 55%,#FF5722 100%);
padding: 12px 12px 8px;
display: flex; flex-direction: column;
color: #fff;
}
#home .tri-claim:active { transform: translateY(1px); }
#home .tri-claim-bg {
position: absolute; inset: -2px;
background-image: url('assets/home/index/coupon-claim-bg.png');
background-size: cover; background-position: center bottom;
background-repeat: no-repeat;
pointer-events: none;
}
#home .tri-claim-inner {
position: relative; z-index: 1;
display: flex; flex-direction: column; height: 100%;
}
#home .tri-claim-title {
font-size: 22px; font-weight: 900; color: #fff;
letter-spacing: .5px; line-height: 1;
text-shadow: 0 2px 6px rgba(180,50,0,.22);
}
#home .tri-claim-sub {
margin-top: 8px;
font-size: 12px; font-weight: 600; color: #fff;
opacity: .95; letter-spacing: .3px;
text-shadow: 0 1px 3px rgba(180,50,0,.18);
}
#home .tri-claim-spacer { flex: 1; min-height: 56px; }
#home .tri-claim-cta {
align-self: stretch;
background: #fff; color: #FF5722;
border-radius: 999px;
padding: 8px 12px;
font-size: 14px; font-weight: 600; letter-spacing: .3px;
display: inline-flex; align-items: center; justify-content: center; gap: 4px;
box-shadow: 0 4px 10px rgba(180,60,0,.18);
}
#home .tri-claim-cta .arrow { font-size: 14px; font-weight: 900; margin-left: 2px; }
/* "已领"态:领券完成后回到首页时替换 .tri-claim 默认态 */
#home .tri-claim-done {
background: linear-gradient(140deg,#FF6B3D 0%,#FF5722 55%,#E65100 100%);
padding: 12px 12px 8px;
}
#home .tri-claim-bg-done {
background-image: none;
background: radial-gradient(circle at 80% 18%, rgba(255,224,160,.45) 0%, rgba(255,160,80,0) 55%);
}
#home .tcd-head {
font-size: 12px; font-weight: 600; color: #fff;
display: inline-flex; align-items: center; gap: 4px;
opacity: .98; letter-spacing: .2px;
text-shadow: 0 1px 2px rgba(180,50,0,.18);
}
#home .tcd-check {
width: 14px; height: 14px; border-radius: 50%;
background: rgba(255,255,255,.95); color: #FF5722;
font-size: 10px; font-weight: 900; line-height: 14px;
display: inline-flex; align-items: center; justify-content: center;
}
#home .tcd-num {
margin-top: 6px;
display: inline-flex; align-items: baseline; gap: 4px;
text-shadow: 0 2px 6px rgba(180,50,0,.25);
}
#home .tcd-num .num {
font-size: 28px; font-weight: 900; color: #fff;
line-height: 1; letter-spacing: -1px;
font-family: -apple-system, "DIN Alternate", sans-serif;
}
#home .tcd-num .unit {
font-size: 12px; font-weight: 600; color: #FFF3E0;
letter-spacing: .2px;
}
#home .tcd-from {
align-self: stretch;
background: rgba(255,255,255,.18);
border-radius: 999px;
padding: 4px 8px 4px 8px;
font-size: 11px; font-weight: 600;
display: inline-flex; align-items: center; gap: 6px; justify-content: center;
color: #fff; letter-spacing: .2px;
backdrop-filter: blur(2px);
}
#home .tcd-from-label { opacity: .9; }
#home .tcd-logos { display: inline-flex; align-items: center; gap: 2px; }
#home .tcd-logos img {
width: 16px; height: 16px; border-radius: 4px;
border: 1.5px solid rgba(255,255,255,.95);
background: #fff; object-fit: cover;
box-shadow: 0 1px 3px rgba(120,40,0,.25);
}
/* ============ Home v2026-06-03: 今日领券 + 点外卖赚金币 双卡(结构 1:1 镜像)============ */
/* 左:今日领券(与右卡 .cta-g6 同 padding / shadow / 字号 / CTA 规格) */
#home .today-coupon {
position: relative;
height: 92px;
border-radius: 14px;
padding: 10px 12px;
background: url('assets/home/index/card-stats-bg.png') center/cover no-repeat;
box-shadow: 0 4px 14px rgba(255,140,60,.10);
display: flex;
flex-direction: column;
overflow: visible;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
#home .tc-title {
font-size: 13px;
font-weight: 700;
color: #1a1a1a;
line-height: 1.28;
letter-spacing: 0;
white-space: nowrap;
}
#home .tc-title .hl { color: #FF5722; }
#home .tc-sub {
font-size: 11px;
font-weight: 400;
color: #666;
letter-spacing: 0;
line-height: 1.3;
margin-top: 3px;
white-space: nowrap;
}
#home .tc-bottom {
margin-top: auto;
display: flex;
align-items: center;
}
#home .tc-cta {
height: 26px;
padding: 0 9px 0 11px;
border-radius: 999px;
background: linear-gradient(180deg, #FFE066 0%, #FFD600 100%);
color: #1a1a1a;
font-size: 11px;
font-weight: 800;
display: inline-flex;
align-items: center;
gap: 5px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 3px 8px rgba(255,179,0,.35);
letter-spacing: .3px;
border: none;
cursor: pointer;
white-space: nowrap;
}
#home .tc-cta::after {
content: '';
display: inline-block;
width: 4px; height: 4px;
border-top: 1.5px solid currentColor;
border-right: 1.5px solid currentColor;
transform: rotate(45deg) translate(-1px, 1px);
}
#home .tc-coupon-symbols {
position: absolute;
right: 8px;
bottom: 4px;
width: 46.2px;
height: auto;
pointer-events: none;
z-index: 2;
}
#home .tc-coupon-symbols img {
display: block;
width: 100%;
height: auto;
}
#home .tc-red-dot {
position: absolute;
top: 8px; right: 10px;
width: 8px; height: 8px;
background: #FF3D00;
border-radius: 50%;
border: 1.5px solid #fff;
box-shadow: 0 0 0 1.5px rgba(255,61,0,.18);
animation: tcDotPulse 1.6s ease-in-out infinite;
z-index: 2;
}
@keyframes tcDotPulse {
0%, 100% { box-shadow: 0 0 0 1.5px rgba(255,61,0,.18); }
50% { box-shadow: 0 0 0 5px rgba(255,61,0,0); }
}
/* 已领状态:其他视觉保持不变,仅隐藏手势提示 */
#home .today-coupon.claimed .tc-red-dot { display: none; }
/* 引导手势:未领时手指点击 CTA 的呼吸动画(与 onboarding 的 tapHint 同一套节奏) */
#home .tc-tap-hand {
position: absolute;
left: 60px;
bottom: -12px;
width: 46px;
height: 46px;
pointer-events: none;
z-index: 3;
filter:
drop-shadow(0 1px 0 #fff)
drop-shadow(1px 0 0 #fff)
drop-shadow(0 -1px 0 #fff)
drop-shadow(-1px 0 0 #fff);
animation: tcTapHint 1.2s ease-in-out infinite;
}
#home .tc-tap-hand img {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
@keyframes tcTapHint {
0%, 100% { transform: translate(0, 0) scale(1); }
50% { transform: translate(-2px, 2px) scale(.92); }
}
#home .today-coupon.claimed .tc-tap-hand { display: none; }
/* 右:比价入口 G-6c(货比三家·不踩坑 + 平台 logo 右下叠加 + CTA pill */
#home .cta-g6 {
position: relative;
height: 92px;
border-radius: 14px;
padding: 10px 12px;
background: url('assets/home/index/card-waimai-bg.png') center/cover no-repeat;
box-shadow: 0 4px 14px rgba(255,140,60,.10);
display: flex;
flex-direction: column;
overflow: visible;
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
#home .g6-title {
font-size: 13px;
font-weight: 700;
color: #1a1a1a;
line-height: 1.28;
letter-spacing: 0;
white-space: nowrap;
}
#home .g6-title .hl { color: #FF5722; }
#home .g6-sub {
font-size: 11px;
font-weight: 400;
color: #666;
letter-spacing: 0;
line-height: 1.3;
margin-top: 3px;
white-space: nowrap;
}
#home .g6-bottom {
margin-top: auto;
display: flex;
align-items: center;
}
#home .g6-cta {
height: 26px;
padding: 0 9px 0 11px;
border-radius: 999px;
background: linear-gradient(180deg, #FFE066 0%, #FFD600 100%);
color: #1a1a1a;
font-size: 11px;
font-weight: 800;
display: inline-flex;
align-items: center;
gap: 5px;
box-shadow: inset 0 1px 0 rgba(255,255,255,.55), 0 3px 8px rgba(255,179,0,.35);
letter-spacing: .3px;
border: none;
cursor: pointer;
white-space: nowrap;
}
#home .g6-cta::after {
content: '';
display: inline-block;
width: 4px;
height: 4px;
border-top: 1.5px solid currentColor;
border-right: 1.5px solid currentColor;
transform: rotate(45deg) translate(-1px, 1px);
}
#home .g6-logos {
position: absolute;
bottom: 8px;
right: 8px;
display: flex;
align-items: center;
flex-shrink: 0;
}
#home .g6-logos img {
width: 24px; height: 24px;
border-radius: 6px;
border: 1.5px solid #fff;
box-shadow: 0 2px 5px rgba(0,0,0,.10);
margin-left: -6px;
}
#home .g6-logos img:first-child { margin-left: 0; }
#home .g6-logos img:nth-child(1) { z-index: 1; transform: rotate(-14deg); }
#home .g6-logos img:nth-child(2) { z-index: 3; transform: translateY(-6px) rotate(5deg); }
#home .g6-logos img:nth-child(3) { z-index: 2; transform: rotate(16deg); }
/* 右卡引导手势:已领券后,手从左卡转移到「去点餐」CTA 上(与左卡 .tc-tap-hand 同一套呼吸节奏) */
#home .g6-tap-hand {
position: absolute;
left: 60px;
bottom: -12px;
width: 46px;
height: 46px;
pointer-events: none;
z-index: 3;
display: none;
filter:
drop-shadow(0 1px 0 #fff)
drop-shadow(1px 0 0 #fff)
drop-shadow(0 -1px 0 #fff)
drop-shadow(-1px 0 0 #fff);
animation: tcTapHint 1.2s ease-in-out infinite;
}
#home .g6-tap-hand img {
display: block;
width: 100%;
height: 100%;
object-fit: contain;
}
/* 仅在已领券状态下显示右卡的手 */
#home .cta-g6.coupon-claimed .g6-tap-hand { display: block; }
/* 引导卡呼吸描边框(橙红 #FF5722 = cta-warm,与高亮字同色):跟随引导手——
未领券 → 高亮左卡「今日大额优惠券」;已领券 → 高亮右卡「点外卖赚金币」 */
#home .today-coupon:not(.claimed),
#home .cta-g6.coupon-claimed {
animation: homeGuideBreath 2s ease-in-out infinite;
}
@keyframes homeGuideBreath {
0%, 100% { box-shadow: 0 0 0 1.5px rgba(255,87,34,.5), 0 4px 14px rgba(255,140,60,.10); }
50% { box-shadow: 0 0 0 2.5px rgba(255,87,34,1), 0 0 11px 1px rgba(255,87,34,.45), 0 4px 14px rgba(255,140,60,.10); }
}
/* 比价平台选择 · 中央模态 */
.home-picker-mask {
display: none;
position: absolute;
inset: 0;
z-index: 250;
background: rgba(20, 12, 4, .5);
backdrop-filter: blur(2px);
-webkit-backdrop-filter: blur(2px);
align-items: center;
justify-content: center;
padding: 24px;
}
.home-picker-mask.show { display: flex; animation: homePickerFade .22s ease-out; }
@keyframes homePickerFade { from { opacity: 0; } to { opacity: 1; } }
.home-picker-modal {
width: 100%;
max-width: 304px;
background: linear-gradient(180deg, #FFFAF0 0%, #FFFFFF 70%);
border-radius: 24px;
padding: 22px 18px 20px;
box-shadow: 0 24px 60px rgba(120, 80, 30, .28), 0 2px 8px rgba(0,0,0,.06), inset 0 1px 0 rgba(255,255,255,.85);
border: 1px solid rgba(255, 220, 120, .4);
animation: homePickerIn .35s cubic-bezier(.22,1,.36,1);
position: relative;
overflow: hidden;
}
.home-picker-modal::before {
content: '';
position: absolute;
top: -60px; left: 50%;
transform: translateX(-50%);
width: 220px; height: 120px;
background: radial-gradient(ellipse at center, rgba(255, 214, 0, .25), transparent 65%);
pointer-events: none;
}
@keyframes homePickerIn {
from { opacity: 0; transform: scale(.86) translateY(-6px); }
to { opacity: 1; transform: scale(1) translateY(0); }
}
.home-picker-close {
position: absolute;
top: 12px; right: 12px;
width: 28px; height: 28px;
border-radius: 14px;
background: rgba(0,0,0,.06);
display: flex; align-items: center; justify-content: center;
font-size: 13px; color: #4D4D4D;
cursor: pointer; font-weight: 700;
z-index: 2;
}
.home-picker-emoji {
width: 38px; height: 38px;
margin: 0 auto 6px;
border-radius: 12px;
background: linear-gradient(135deg, #FFE066 0%, #FFB300 100%);
box-shadow: 0 4px 10px rgba(255, 160, 0, .35), inset 0 1px 0 rgba(255,255,255,.6);
display: flex; align-items: center; justify-content: center;
font-size: 20px;
position: relative;
}
.home-picker-title {
font-size: 18px; font-weight: 900; color: #1a1a1a;
text-align: center; letter-spacing: .3px;
margin-bottom: 4px;
position: relative;
}
.home-picker-sub {
font-size: 12px; color: #4D4D4D;
text-align: center; margin-bottom: 18px;
line-height: 1.5; position: relative;
}
.home-picker-sub strong { color: #FF5722; font-weight: 800; }
.home-picker-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
position: relative;
}
.home-picker-item {
position: relative;
background: linear-gradient(180deg, #FFFFFF 0%, #FFFDF5 100%);
border: 1.5px solid #F2EBD8;
border-radius: 14px;
padding: 14px 6px 12px;
text-align: center;
cursor: pointer;
transition: transform .15s, border-color .15s, box-shadow .15s;
-webkit-tap-highlight-color: transparent;
box-shadow: 0 2px 6px rgba(120, 80, 20, .05);
}
.home-picker-item:active {
transform: scale(.94);
border-color: #FFD600;
box-shadow: 0 4px 14px rgba(255, 179, 0, .25);
}
.home-picker-item .home-picker-logo {
width: 46px; height: 46px;
border-radius: 12px;
margin: 0 auto 8px;
overflow: hidden;
box-shadow: 0 2px 8px rgba(0,0,0,.08);
}
.home-picker-item .home-picker-logo img { width: 100%; height: 100%; object-fit: cover; }
.home-picker-item .home-picker-name {
font-size: 12px; font-weight: 700; color: #1a1a1a;
line-height: 1.2; letter-spacing: .2px;
}
/* 未安装态:置灰 + 黄色 ↓ 引导安装 chip */
.home-picker-item.uninstalled {
background: #FAFAFA;
border-color: #ECECEC;
box-shadow: none;
}
.home-picker-item.uninstalled .home-picker-logo {
box-shadow: 0 1px 3px rgba(0,0,0,.04);
}
.home-picker-item.uninstalled .home-picker-logo img {
filter: grayscale(.7);
opacity: .55;
}
.home-picker-item.uninstalled .home-picker-name { color: #BBB; }
.home-picker-install-chip {
position: absolute;
top: 6px; right: 6px;
width: 18px; height: 18px;
border-radius: 50%;
background: #FFD600;
box-shadow: 0 0 0 1.5px rgba(255,179,0,.35);
display: flex; align-items: center; justify-content: center;
}
/* 推荐信息流 */
#home .home-feed-wrap {
background: #fff;
border-radius: 14px 14px 0 0;
margin: 0;
padding-bottom: 0;
overflow: clip; /* 用 clip 而非 hiddenclip 只做视觉裁剪不创建 BFCsticky 才能继续找到 .home-scroll 作为参考 */
}
#home .home-feed-head {
display: flex; align-items: center; justify-content: space-between;
padding: 12px 16px 4px;
}
/* feed 排序 tab —— 文字 + 下划线(黑字全部一致,激活态加粗 + 黄下划线 + 略大字号)
左对齐:tab 全部贴左侧,gap 28px 分隔
方向:data-dir="desc|asc" 仅作内部状态,feed 卡片重排即反馈
交互:点 tab 时若 sort 已贴顶就不再 scrollIntoView,避免 0 距离平滑滚动造成的微位移 */
#home .feed-sort {
display: flex; align-items: center; justify-content: flex-start;
gap: 28px;
background: #fff;
padding: 14px 16px 12px;
position: sticky;
top: 38px; /* 紧贴 .brand-strip 底部(38px */
z-index: 5;
}
#home .feed-sort .fs-item {
display: inline-flex; align-items: center;
cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent;
white-space: nowrap;
flex: 0 0 auto;
font-size: 14px; font-weight: 400;
color: #1A1A1A;
}
#home .feed-sort .fs-item .lbl { display: inline-block; position: relative; }
#home .feed-sort .fs-item.active { font-weight: 700; font-size: 15px; }
#home .feed-sort .fs-item.active .lbl::after {
content: ''; position: absolute;
left: 20%; right: 20%; bottom: -7px; /* 内缩 20%/20% = 下划线为文字宽度的 60%,居中 */
height: 3px; border-radius: 2px;
background: #FFD600;
}
/* 首页商品流多方案:用于 ?feedvariant=frame|tabs|combo|border|accent|cap|lift 对比 */
body.feed-v-frame #home .home-feed-wrap,
body.feed-v-combo #home .home-feed-wrap {
margin: 12px 10px 0;
border: 1px solid rgba(0,0,0,.10);
border-top-color: rgba(0,0,0,.14);
border-radius: 16px 16px 0 0;
box-shadow: 0 -1px 0 rgba(255,255,255,.85) inset, 0 5px 16px rgba(0,0,0,.05);
}
body.feed-v-frame #home .feed-sort,
body.feed-v-combo #home .feed-sort {
padding: 13px 14px 12px;
border-bottom: 1px solid rgba(0,0,0,.06);
}
body.feed-v-tabs #home .feed-sort,
body.feed-v-combo #home .feed-sort {
gap: 8px;
padding: 10px 12px;
background: #fff;
}
body.feed-v-tabs #home .feed-sort .fs-item,
body.feed-v-combo #home .feed-sort .fs-item {
height: 30px;
padding: 0 12px;
border-radius: 999px;
background: #F4F5F7;
color: #5F6368;
font-size: 13px;
font-weight: 600;
letter-spacing: .1px;
}
body.feed-v-tabs #home .feed-sort .fs-item.active,
body.feed-v-combo #home .feed-sort .fs-item.active {
background: linear-gradient(180deg, #FFE45C 0%, #FFD600 100%);
color: #1A1A1A;
font-size: 13px;
font-weight: 800;
box-shadow: inset 0 0 0 1px rgba(0,0,0,.06), 0 3px 8px rgba(255,214,0,.24);
}
body.feed-v-tabs #home .feed-sort .fs-item.active .lbl::after,
body.feed-v-combo #home .feed-sort .fs-item.active .lbl::after {
display: none;
}
body.feed-v-border #home .home-feed-wrap {
border: 1px solid rgba(0,0,0,.13);
border-top-width: 2px;
border-top-color: rgba(0,0,0,.20);
box-shadow: 0 -1px 0 rgba(255,255,255,.90) inset;
}
body.feed-v-accent #home .home-feed-wrap {
border-top: 3px solid #FFD600;
box-shadow: 0 -1px 0 rgba(255,255,255,.75) inset;
}
body.feed-v-cap #home .home-feed-wrap {
border: 1px solid rgba(255,193,7,.46);
border-top-color: rgba(255,179,0,.62);
background: #fff;
box-shadow: inset 0 1px 0 rgba(255,255,255,.92);
}
body.feed-v-cap #home .feed-sort {
background: linear-gradient(180deg, #FFF0A8 0%, #FFF8D8 58%, #fff 100%);
border-bottom: 0;
}
body.feed-v-lift #home .home-feed-wrap {
margin: 10px 8px 0;
border: 1px solid rgba(0,0,0,.12);
border-top-color: rgba(0,0,0,.20);
box-shadow: 0 -2px 0 rgba(255,255,255,.95), 0 -10px 22px rgba(0,0,0,.09);
}
#home .home-feed-title {
font-size: 17px; font-weight: 600; color: #1a1a1a; letter-spacing: -.2px;
display: inline-flex; align-items: center; gap: 4px;
}
#home .home-feed-title .feed-fire { font-size: 18px; line-height: 1; }
#home .feed-promo-tag {
background: linear-gradient(90deg, #FF5722, #FF8A4C);
color: #fff;
font-size: 11px;
font-weight: 700;
padding: 3px 8px;
border-radius: 8px;
display: inline-flex;
align-items: center;
letter-spacing: 0;
white-space: nowrap;
box-shadow: 0 1px 2px rgba(255,87,34,.22);
}
#home .feed-promo-tag .dot {
opacity: .8;
margin: 0 3px;
}
#home .home-feed {
display: flex; flex-direction: column;
padding: 0;
background: #fff;
}
#home .feed-card {
background: transparent;
border-radius: 0;
box-shadow: none;
cursor: pointer;
display: flex; flex-direction: row; align-items: stretch;
position: relative;
padding: 12px 16px;
}
#home .feed-card:active { background: #fafafa; }
#home .feed-img {
flex: 0 0 124px; width: 124px; height: 124px; aspect-ratio: 1/1;
border-radius: 10px;
overflow: hidden;
background: #fafafa;
position: relative;
}
#home .feed-img img {
width: 100%; height: 100%;
object-fit: cover;
display: block;
}
/* 品类徽章:左下角,外卖黄 #FFC300 / 团购橙 #FF5722 */
#home .feed-cat-badge {
position: absolute;
left: 5px; bottom: 5px;
padding: 1px 5px 2px;
border-radius: 3px;
font-size: 9px; font-weight: 700;
line-height: 1.3;
letter-spacing: .2px;
box-shadow: 0 1px 2px rgba(0,0,0,.18);
}
#home .feed-cat-badge.cat-waimai { background: #FFC300; color: #1A1A1A; }
#home .feed-cat-badge.cat-tuangou { background: #FF5722; color: #fff; }
#home .feed-body {
flex: 1; min-width: 0;
padding: 0 0 0 12px;
display: flex; flex-direction: column;
gap: 4px;
}
#home .feed-title {
font-size: 13px; font-weight: 500; color: #1a1a1a; line-height: 1.35;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
overflow: hidden;
padding-right: 4px;
}
#home .feed-title .shop-name { color: #FF5722; font-weight: 600; }
#home .feed-title .title-sep { color: #CCCCCC; margin: 0 2px; font-weight: 400; }
#home .feed-meta {
display: flex; align-items: center; gap: 8px;
font-size: 11px; color: #999;
padding-right: 0;
}
#home .feed-meta .meta-left {
color: #999; font-weight: 400;
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
flex: 1 1 auto; min-width: 0;
}
#home .feed-meta .meta-right {
color: #999; font-weight: 400;
white-space: nowrap; flex-shrink: 0;
}
#home .feed-pricerow {
display: flex; align-items: baseline; gap: 4px;
margin-top: auto;
padding-right: 60px;
}
/* Price Golden Ratio: 整数 22 → ¥ 15 (×0.7), dec 12 (×0.55) + tabular-nums */
#home .feed-price {
display: inline-flex; align-items: baseline; gap: 0;
color: #FF5722; font-weight: 600;
font-variant-numeric: tabular-nums;
}
#home .feed-price .yuan { font-size: 18px; font-weight: 600; margin-right: 2px; }
#home .feed-price .num { font-size: 26px; letter-spacing: -.3px; line-height: 1; font-weight: 600; }
#home .feed-price .num small { font-size: 14px; font-weight: 600; }
#home .feed-original {
font-size: 12px; color: #B0B0B0; text-decoration: line-through;
font-weight: 400;
}
#home .feed-tag {
font-size: 11px;
font-weight: 400;
line-height: 1.4;
color: #FF6B3D;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding-right: 60px;
}
#home .feed-grab {
position: absolute; right: 16px; bottom: 12px;
background: linear-gradient(135deg,#FFE066,#FFD600); color: #1A1A1A;
font-size: 15px; font-weight: 800;
width: 46px; height: 46px;
border-radius: 16px 16px 16px 4px;
display: flex; align-items: center; justify-content: center;
box-shadow: inset 0 1px 0 rgba(255,255,255,.7);
letter-spacing: 1px;
z-index: 2;
}
/* feed 末尾「已经到底了」—— 12px/400 #999,左右各一条 24×0.5px 浅灰短线 */
#home .feed-end {
display: flex; align-items: center; justify-content: center;
gap: 12px;
padding: 24px 16px 20px;
font-size: 12px; font-weight: 400;
color: #999;
letter-spacing: 1px;
}
#home .feed-end::before,
#home .feed-end::after {
content: '';
width: 24px; height: 0.5px;
background: #E5E5E5;
}
/* ===== R8 福利页设计规范(仅作用在 .scheme-r8 节点内) ===== */
.scheme-r8 {
--r8-yellow: #FFD600;
--r8-yellow-deep: #FFC107;
--r8-orange: #FF6A1A;
--r8-green: #21C45A;
--r8-bg: #FBFAF6;
--r8-card: #FFFFFF;
--r8-divider: #EEE8D7;
--r8-aux: #8A8A8A;
--r8-text: #1F1F1F;
background: var(--r8-bg) !important;
padding: 0 12px 16px !important;
}
.r8-status-bar {
height: 40px;
padding: 12px 8px 0;
display: flex;
align-items: flex-start;
justify-content: space-between;
color: #1A1A1A;
font-size: 14px;
font-weight: 700;
letter-spacing: 0;
}
.r8-status-icons {
display: inline-flex;
align-items: center;
gap: 6px;
height: 18px;
}
.r8-signal {
display: inline-flex;
align-items: flex-end;
gap: 2px;
height: 13px;
}
.r8-signal span {
width: 3px;
border-radius: 4px;
background: #1A1A1A;
}
.r8-signal span:nth-child(1) { height: 5px; }
.r8-signal span:nth-child(2) { height: 8px; }
.r8-signal span:nth-child(3) { height: 11px; }
.r8-signal span:nth-child(4) { height: 13px; }
.r8-wifi {
position: relative;
width: 16px;
height: 12px;
}
.r8-wifi::before,
.r8-wifi::after {
content: "";
position: absolute;
left: 50%;
border: 2px solid #1A1A1A;
border-left-color: transparent;
border-right-color: transparent;
border-bottom: none;
transform: translateX(-50%);
}
.r8-wifi::before {
top: 0;
width: 16px;
height: 9px;
border-radius: 14px 14px 0 0;
}
.r8-wifi::after {
top: 5px;
width: 9px;
height: 5px;
border-radius: 8px 8px 0 0;
}
.r8-wifi span {
position: absolute;
left: 50%;
bottom: 0;
width: 3px;
height: 3px;
transform: translateX(-50%);
border-radius: 50%;
background: #1A1A1A;
}
.r8-battery {
position: relative;
width: 24px;
height: 12px;
border: 1.8px solid #1A1A1A;
border-radius: 4px;
}
.r8-battery::before {
content: "";
position: absolute;
right: -4px;
top: 3px;
width: 2px;
height: 5px;
border-radius: 0 4px 4px 0;
background: #1A1A1A;
}
.r8-battery::after {
content: "";
position: absolute;
left: 2px;
top: 2px;
width: 17px;
height: 6px;
border-radius: 4px;
background: #1A1A1A;
}
.r8-page-header {
height: 50px;
padding: 4px 0 8px;
display: flex;
align-items: center;
justify-content: space-between;
}
.r8-page-title {
display: flex;
align-items: baseline;
gap: 8px;
flex: 1;
min-width: 0;
}
.r8-page-title strong {
font-size: 24px;
line-height: 1;
font-weight: 900;
color: #1A1A1A;
letter-spacing: 0;
flex-shrink: 0;
white-space: nowrap;
}
.r8-page-title .sub {
font-size: 11px;
line-height: 1;
color: #666666;
font-weight: 500;
white-space: nowrap;
flex-shrink: 0;
}
.r8-page-title .star {
color: var(--r8-yellow);
font-size: 12px;
font-weight: 900;
transform: translateY(-10px);
flex-shrink: 0;
}
.r8-reward-btn {
height: 32px;
padding: 0 8px 0 8px;
border: none;
border-radius: 18px;
background: rgba(255,255,255,.92);
color: #1A1A1A;
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 12px;
font-weight: 700;
box-shadow: 0 5px 14px rgba(173,128,18,.12);
cursor: pointer;
white-space: nowrap;
margin-left: 8px;
}
.r8-reward-btn svg {
width: 18px;
height: 18px;
flex-shrink: 0;
}
.r8-asset-card {
position: relative;
border-radius: 18px;
background: radial-gradient(ellipse 125% 86% at 4% 8%, rgba(255,255,255,.98) 0%, rgba(255,255,255,.56) 38%, rgba(255,255,255,0) 68%),
linear-gradient(155deg, #FFF5BF 0%, #FFE982 46%, #FFF9E4 100%);
min-height: 132px;
padding: 12px 16px 8px;
overflow: hidden;
border: 1px solid rgba(255,255,255,.78);
box-shadow: 0 8px 22px rgba(185,137,18,.10), inset 0 1px 0 rgba(255,255,255,.9);
}
.r8-asset-illustration {
position: absolute;
right: 6px; bottom: 17px;
width: 96px;
z-index: 2;
pointer-events: none; user-select: none;
}
.r8-corner-cut {
position: absolute; right: 0; top: 0;
width: 148px; height: 70px;
z-index: 1; pointer-events: none;
}
.r8-stars {
position: absolute; right: 98px; top: 42px;
width: 106px; height: 50px;
z-index: 1; pointer-events: none;
}
.r8-asset-header {
display: flex; align-items: baseline; gap: 8px;
position: relative; z-index: 3;
}
.r8-asset-title {
font-size: 18px; font-weight: 900; color: var(--r8-text);
letter-spacing: 0;
line-height: 1.08;
}
.r8-asset-withdraw {
font-size: 12px; color: #8B6914;
font-weight: 800; cursor: pointer;
}
.r8-asset-grid {
display: flex; align-items: flex-start; gap: 8px; margin-top: 16px;
position: relative; z-index: 3;
}
.r8-asset-col {
display: flex; flex-direction: column; gap: 6px;
flex: 0 1 auto; cursor: pointer;
}
.r8-asset-col:first-child {
width: 110px;
}
.r8-asset-col + .r8-asset-col {
min-width: 94px;
padding-left: 6px;
border-left: none;
}
.r8-asset-label-row {
display: flex; align-items: center; gap: 4px;
white-space: nowrap;
transform: translateX(-1px);
}
.r8-asset-coin-icon, .r8-asset-cash-icon-svg {
width: 17px; height: 17px; flex-shrink: 0; object-fit: contain;
margin-left: -3px;
}
.r8-asset-coin-label, .r8-asset-cash-label {
font-size: 13px; color: var(--r8-text); font-weight: 800;
display: inline-flex; align-items: center; gap: 2px;
}
.r8-asset-arrow {
font-size: 11px; color: #999999; font-weight: 600;
margin-left: 2px;
}
.r8-asset-coin-val, .r8-asset-cash-val {
font-size: 22px; font-weight: 800; color: #1A1A1A;
line-height: 1; letter-spacing: -.2px;
white-space: nowrap;
}
.r8-asset-coin-val, .r8-asset-cash-val {
color: var(--r8-orange);
}
.r8-asset-cash-val .unit {
font-size: 11px; font-weight: 800; margin-left: 2px;
}
.r8-asset-tip {
font-size: 10px; color: #999999;
margin-top: 12px;
position: relative; z-index: 3;
display: flex; align-items: center; gap: 4px;
line-height: 1.25;
white-space: nowrap;
}
.r8-info-icon {
width: 12px; height: 12px; border-radius: 50%;
background: rgba(138,138,138,.25); color: var(--r8-aux);
display: inline-flex; align-items: center; justify-content: center;
font-size: 10px; font-weight: 700; font-style: italic;
flex-shrink: 0;
}
.r8-checkin-card {
margin-top: 12px;
min-height: 58px;
background: linear-gradient(90deg, #FFE0B2 0%, #FFE066 100%);
border-radius: 18px;
padding: 6px 12px 6px 16px;
display: flex; align-items: center; gap: 8px;
box-shadow: 0 8px 18px rgba(185,137,18,.08);
cursor: pointer;
}
.r8-checkin-calendar {
width: 44px; height: 44px;
flex-shrink: 0; object-fit: contain;
}
.r8-checkin-text { flex: 1; min-width: 0; }
.r8-checkin-headline {
font-size: 16px; font-weight: 900; color: var(--r8-text);
display: flex; align-items: baseline; gap: 2px;
white-space: nowrap;
line-height: 1.08;
}
.r8-checkin-headline .num {
font-size: 22px; font-weight: 900; color: #FF5722;
margin: 0 4px;
}
.r8-checkin-sub {
font-size: 11px; color: #999999;
margin-top: 2px;
font-weight: 600;
white-space: nowrap;
display: none;
}
.r8-checkin-cta {
height: 32px;
background: #FFFFFF; border-radius: 32px;
padding: 0 12px 0 8px;
font-size: 12px; font-weight: 800; color: var(--r8-text);
border: none; cursor: pointer;
display: inline-flex; align-items: center; gap: 4px;
flex-shrink: 0; white-space: nowrap;
box-shadow: 0 4px 12px rgba(185,137,18,.08);
}
.r8-checkin-cta .check-tick {
display: inline-flex; align-items: center; justify-content: center;
width: 16px; height: 16px; border-radius: 4px;
background: var(--r8-green); color: #fff;
font-size: 11px; font-weight: 900;
}
.r8-section-title {
font-size: 17px; font-weight: 800; color: var(--r8-text);
margin: 0 0 8px;
}
.r8-task-card {
background: var(--r8-card);
border-radius: 14px;
padding: 12px 16px 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.04);
margin-top: 16px;
}
.r8-task-row {
display: flex; align-items: center; padding: 0;
border-bottom: none;
margin-top: 12px;
}
.r8-task-row:first-of-type { margin-top: 0; }
.r8-task-icon {
display: none;
}
.r8-task-info { flex: 1; min-width: 0; }
.r8-task-titlerow {
display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
}
.r8-task-title {
font-size: 15px; font-weight: 800; color: var(--r8-text);
}
.r8-task-coin {
display: inline-flex; align-items: center; gap: 0;
font-size: 12px; font-weight: 700; color: var(--r8-orange);
}
.r8-task-coin .ci {
width: 18px; height: 18px; flex-shrink: 0;
margin-right: -2px;
transform: scale(1.12);
transform-origin: center;
}
.r8-task-sub {
font-size: 12px; color: var(--r8-aux); margin-top: 2px; font-weight: 500;
}
.r8-task-cta {
background: var(--r8-yellow); color: var(--r8-text);
border: none; border-radius: 32px;
padding: 6px 12px;
font-size: 12px; font-weight: 800;
cursor: pointer; flex-shrink: 0;
}
.r8-checkin-task-row {
display: block;
margin-top: 8px;
padding: 2px 0 16px;
border-bottom: 1px solid #F0F0F0;
cursor: pointer;
transition: margin .18s ease;
}
.r8-checkin-task-main {
min-width: 0;
}
.r8-checkin-task-head {
display: flex;
align-items: flex-start;
gap: 8px;
}
.r8-checkin-task-copy {
flex: 1;
min-width: 0;
}
.r8-checkin-task-title {
color: var(--r8-text);
font-size: 15px;
line-height: 1.18;
font-weight: 800;
white-space: nowrap;
}
.r8-checkin-task-sub {
margin-top: 2px;
color: var(--r8-aux);
font-size: 12px;
line-height: 1.25;
font-weight: 500;
white-space: nowrap;
}
.r8-checkin-task-cta {
flex: 0 0 auto;
min-width: 54px;
height: auto;
border: none;
border-radius: 999px;
background: var(--r8-yellow);
color: var(--r8-text);
font-family: inherit;
padding: 6px 12px;
font-size: 12px;
font-weight: 800;
box-shadow: none;
cursor: pointer;
}
.r8-checkin-timeline {
position: relative;
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
column-gap: 2px;
margin-top: 16px;
}
.r8-checkin-timeline::before {
content: '';
position: absolute;
left: 20px;
right: 20px;
top: 32px;
height: 2px;
border-radius: 32px;
background: #E5E5E5;
}
.r8-checkin-step {
position: relative;
z-index: 1;
min-width: 0;
display: grid;
grid-template-rows: 18px 22px 15px;
justify-items: center;
align-items: center;
row-gap: 2px;
}
.r8-checkin-step-label {
min-height: 18px;
color: #999999;
font-size: 11px;
line-height: 18px;
font-weight: 650;
white-space: nowrap;
}
.r8-checkin-step-badge {
padding: 0 8px;
border-radius: 999px;
background: #fff;
color: #FFA000;
font-size: 11px;
line-height: 18px;
font-weight: 900;
box-shadow: 0 2px 8px rgba(0,0,0,.08);
white-space: nowrap;
}
.r8-checkin-step-icon {
box-sizing: border-box;
width: 22px;
height: 22px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.r8-checkin-step-icon img {
display: block;
width: 22px;
height: 22px;
object-fit: contain;
}
.r8-checkin-step.done .r8-checkin-step-icon {
background: #FFF3E0;
border: 1px solid #FFE066;
box-shadow: inset 0 1px 0 rgba(255,255,255,.72);
}
.r8-checkin-step.done .r8-checkin-step-icon img {
display: none;
}
.r8-checkin-step.done .r8-checkin-step-icon::before {
content: '✓';
color: #FFA000;
font-size: 13px;
line-height: 1;
font-weight: 900;
}
.r8-checkin-step.just-done .r8-checkin-step-icon {
animation: r8CheckDonePop .38s cubic-bezier(.22, 1.4, .36, 1);
}
.r8-checkin-step.current .r8-checkin-step-icon {
animation: r8CheckCoinPulse .42s ease-out;
}
@keyframes r8CheckDonePop {
0% { transform: scale(.68); opacity: .55; }
64% { transform: scale(1.12); opacity: 1; }
100% { transform: scale(1); opacity: 1; }
}
@keyframes r8CheckCoinPulse {
0% { transform: scale(.86); }
64% { transform: scale(1.1); }
100% { transform: scale(1); }
}
.r8-checkin-step.most .r8-checkin-step-icon img {
width: 22px;
height: 22px;
}
.r8-checkin-step-amount {
color: #999999;
font-size: 11px;
line-height: 1;
font-weight: 650;
font-variant-numeric: tabular-nums;
white-space: nowrap;
}
.r8-badge-update {
background: rgba(255,106,26,0.10); color: var(--r8-orange);
border: 1px solid rgba(255,106,26,0.35);
font-size: 10px; padding: 2px 4px; border-radius: 4px; font-weight: 700;
}
.r8-rule-entry {
margin: 16px auto 8px;
border: none;
background: transparent;
color: #999999;
font-family: inherit;
font-size: 12px;
font-weight: 500;
line-height: 18px;
display: flex;
align-items: center;
justify-content: center;
gap: 2px;
cursor: pointer;
}
.r8-rule-entry span {
font-size: 13px;
transform: translateY(-.5px);
}
.welfare-rules-screen {
background: #FAFAFA !important;
color: #1A1A1A;
}
.welfare-rules-body {
flex: 1;
overflow-y: auto;
padding: 12px 16px 24px;
}
.welfare-rules-card {
background: #fff;
border-radius: 14px;
padding: 16px 16px 16px;
box-shadow: 0 4px 14px rgba(0,0,0,.04);
}
.welfare-rules-title {
font-size: 18px;
font-weight: 600;
color: #1A1A1A;
margin-bottom: 12px;
}
.welfare-rules-list {
display: grid;
gap: 12px;
}
.welfare-rules-item {
display: grid;
grid-template-columns: 20px 1fr;
gap: 8px;
align-items: start;
}
.welfare-rules-index {
width: 20px;
height: 20px;
border-radius: 50%;
background: #FFF3C4;
color: #8B6914;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 600;
}
.welfare-rules-text {
font-size: 13px;
line-height: 20px;
font-weight: 400;
color: #666666;
}
.welfare-rules-text strong {
display: block;
margin-bottom: 2px;
font-size: 14px;
line-height: 20px;
font-weight: 700;
color: #1A1A1A;
}
.r8-record-task-block {
margin-top: 12px;
}
.r8-record-task-row {
margin-top: 0;
}
.r8-record-rewards {
position: relative;
margin-top: 8px;
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
gap: 2px;
}
.r8-record-milestone {
position: relative;
z-index: 1;
min-width: 0;
display: flex;
flex-direction: column;
align-items: center;
}
.r8-record-reward-box {
-webkit-appearance: none;
appearance: none;
width: 42px;
height: 50px;
border-radius: 8px;
background: #F0F0F0;
border: none;
font-family: inherit;
box-shadow: inset 0 1px 0 rgba(255,255,255,.75);
display: grid;
grid-template-rows: 10px 18px 13px;
align-content: center;
justify-items: center;
row-gap: 2px;
padding: 2px 2px;
position: relative;
transition: transform .12s ease, box-shadow .12s ease, background .12s ease;
}
button.r8-record-reward-box {
cursor: pointer;
-webkit-tap-highlight-color: transparent;
}
button.r8-record-reward-box:active {
transform: translateY(1px);
}
button.r8-record-reward-box:focus-visible {
outline: 2px solid rgba(255, 184, 0, .5);
outline-offset: 2px;
}
button.r8-record-reward-box.done,
button.r8-record-reward-box:disabled {
cursor: default;
pointer-events: none;
}
.r8-record-reward-box.done {
background: #FAFAFA;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
row-gap: 2px;
}
.r8-record-reward-box.active {
background: #FFF3E0;
box-shadow: inset 0 0 0 1px #FFD979;
}
.r8-record-status {
position: static;
text-align: center;
font-size: 10px;
line-height: 1;
font-weight: 800;
color: var(--r8-orange);
white-space: nowrap;
}
.r8-record-reward-box.done .r8-record-status {
font-size: 10px;
color: #999999;
}
.r8-record-reward-box.active .r8-record-status {
color: #FFA000;
}
.r8-record-reward-box.locked .r8-record-status {
color: #999999;
}
.r8-record-coin {
width: 18px;
height: 18px;
object-fit: contain;
flex-shrink: 0;
transform: scale(1.12);
transform-origin: center;
}
.r8-record-num {
font-size: 12px;
line-height: 1;
font-weight: 800;
color: var(--r8-orange);
letter-spacing: 0;
white-space: nowrap;
}
.r8-record-reward-box.done .r8-record-num {
font-size: 10px;
color: #999999;
font-weight: 750;
}
.r8-record-reward-box.active .r8-record-num {
font-size: 13px;
color: var(--r8-orange);
}
.r8-record-label {
margin-top: 4px;
font-size: 10px;
line-height: 1;
font-weight: 750;
color: #666666;
white-space: nowrap;
}
.r8-task-cta.is-done {
background: #F0F0F0;
color: #999999;
cursor: default;
}
@keyframes rewardToastIn {
0% { opacity: 0; transform: translate(-50%, -48%) scale(.86); }
68% { opacity: 1; transform: translate(-50%, -50%) scale(1.04); }
100% { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@keyframes rewardToastOut {
from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
to { opacity: 0; transform: translate(-50%, -54%) scale(.96); }
}
.reward-toast {
position: absolute;
left: 50%;
top: 48%;
width: 176px;
min-height: 164px;
border-radius: 18px;
background: rgba(56, 56, 68, .98);
box-shadow: 0 18px 42px rgba(0,0,0,.24);
z-index: 10020;
pointer-events: none;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 16px 12px 16px;
color: #FFFFFF;
text-align: center;
animation: rewardToastIn .26s cubic-bezier(.18, .9, .25, 1.15) both;
}
.reward-toast.is-leaving {
animation: rewardToastOut .22s ease both;
}
.reward-toast-icon {
width: 72px;
height: 54px;
object-fit: contain;
display: block;
margin-bottom: 8px;
filter: drop-shadow(0 7px 10px rgba(255, 169, 27, .18));
}
.reward-toast-amount {
font-size: 28px;
line-height: 1;
font-weight: 900;
letter-spacing: 0;
color: #FFFFFF;
}
.reward-toast-title {
margin-top: 16px;
font-size: 20px;
line-height: 1;
font-weight: 700;
color: rgba(255,255,255,.9);
white-space: nowrap;
}
/* ===== 收益明细 / 提现 ===== */
.income-nav {
display: flex;
align-items: center;
justify-content: space-between;
padding: 40px 16px 16px;
background: #fff;
flex-shrink: 0;
}
.income-nav-title {
position: absolute;
left: 50%;
transform: translateX(-50%);
font-size: 18px;
font-weight: 500;
color: var(--gray-900);
letter-spacing: 0;
}
.income-nav-back,
.income-nav-link {
position: relative;
z-index: 1;
border: none;
background: none;
cursor: pointer;
font-family: inherit;
}
.income-nav-back {
width: 44px;
min-width: 44px;
height: 32px;
display: inline-flex;
align-items: center;
justify-content: flex-start;
padding: 0;
text-align: left;
font-size: 24px;
line-height: 1;
font-weight: 400;
color: var(--gray-800);
}
.income-nav-link {
min-width: 72px;
text-align: right;
font-size: 14px;
font-weight: 400;
color: var(--gray-500);
}
.income-rate {
padding: 0 16px 8px;
font-size: 12px;
line-height: 18px;
font-weight: 400;
color: var(--gray-500);
flex-shrink: 0;
}
.income-summary-card {
margin: 0 16px 16px;
padding: 24px 16px;
border-radius: 18px;
background: linear-gradient(135deg, #FFF3E0 0%, #FFFDE7 100%);
flex-shrink: 0;
}
.income-summary-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 24px;
}
.income-summary-label {
font-size: 13px;
font-weight: 400;
color: var(--gray-600);
margin-bottom: 6px;
}
.income-summary-value {
font-size: 28px;
line-height: 1;
font-weight: 700;
color: #FF5722;
letter-spacing: .2px;
}
.income-chip-btn {
margin-top: 8px;
height: 28px;
padding: 0 12px;
border-radius: 999px;
border: 1.5px solid #FF5722;
background: transparent;
color: #FF5722;
font-size: 13px;
font-weight: 500;
cursor: pointer;
font-family: inherit;
}
.income-chip-btn.filled {
background: #FF5722;
color: #fff;
}
.income-tabs {
position: relative;
display: flex;
padding: 0 16px;
border-bottom: 2px solid var(--gray-200);
flex-shrink: 0;
}
.income-tab {
flex: 1;
border: none;
background: none;
padding: 16px 0 12px;
text-align: center;
font-family: inherit;
font-size: 15px;
font-weight: 400;
color: var(--gray-500);
cursor: pointer;
}
.income-tab.active {
color: #000;
font-weight: 400;
}
.income-tab-indicator {
position: absolute;
left: 16px;
bottom: -2px;
width: calc((100% - 32px) / 2);
height: 2px;
background: #1a1a1a;
transition: transform .24s ease;
}
.income-tabs[data-active="cash"] .income-tab-indicator {
transform: translateX(100%);
}
.income-record-shell {
flex: 1;
overflow: hidden;
background: #fff;
touch-action: pan-y;
user-select: none;
-webkit-user-select: none;
}
.income-record-track {
height: 100%;
display: flex;
width: 200%;
transition: transform .24s ease;
}
.income-record-track.cash-active {
transform: translateX(-50%);
}
.income-record-panel {
width: 50%;
min-width: 50%;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 0 16px 16px;
}
.income-record-row {
min-height: 72px;
display: flex;
align-items: center;
border-bottom: .5px solid var(--gray-100);
}
.income-record-main {
flex: 1;
min-width: 0;
}
.income-record-title {
font-size: 15px;
font-weight: 400;
color: #1A1A1A;
line-height: 1.3;
}
.income-record-date {
margin-top: 4px;
font-size: 13px;
font-weight: 400;
color: var(--gray-400);
}
.income-record-amount {
margin-left: 8px;
font-size: 16px;
font-weight: 400;
color: #FF5722;
white-space: nowrap;
}
.income-record-amount.cash {
color: #FE2C55;
}
.income-footer {
padding: 12px 16px 24px;
border-top: .5px solid var(--gray-200);
text-align: center;
flex-shrink: 0;
background: #fff;
}
.income-footer-note {
margin-bottom: 8px;
font-size: 14px;
font-weight: 400;
color: var(--gray-500);
}
.income-footer-note strong {
color: #FF5722;
font-weight: 500;
}
.income-footer .btn {
font-weight: 400;
}
.withdraw-screen {
background: #fff !important;
color: var(--gray-900);
}
#withdrawal .income-nav-title {
font-size: 16px;
font-weight: 500;
}
#withdrawal .income-nav {
background: transparent;
}
#withdrawal .income-nav-back {
font-size: 22px;
font-weight: 400;
}
.withdraw-nav-links {
min-width: 104px;
display: flex;
align-items: center;
justify-content: flex-end;
gap: 6px;
font-size: 12px;
font-weight: 400;
color: var(--gray-700);
}
.withdraw-nav-links button {
border: none;
background: none;
padding: 0;
font: inherit;
color: inherit;
cursor: pointer;
}
.withdraw-nav-sep {
width: 1px;
height: 12px;
background: var(--gray-300);
}
.withdraw-top {
flex-shrink: 0;
background: linear-gradient(126deg, #FFF3C4 0%, #FFFFFF 52%, #E8F5E9 100%);
border-bottom: .5px solid rgba(0,0,0,.04);
}
.withdraw-notice {
min-height: 40px;
display: flex;
align-items: center;
gap: 6px;
padding: 0 16px;
border-top: .5px solid rgba(0,0,0,.04);
border-bottom: .5px solid rgba(0,0,0,.05);
background: rgba(255,255,255,.50);
color: var(--gray-700);
font-size: 11px;
font-weight: 400;
}
.withdraw-notice-icon {
width: 14px;
height: 14px;
border-radius: 50%;
border: 1.3px solid currentColor;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
font-size: 10px;
line-height: 1;
}
.withdraw-notice-text {
flex: 1;
min-width: 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.withdraw-notice-close {
border: none;
background: none;
color: var(--gray-700);
font-size: 18px;
font-weight: 400;
line-height: 1;
cursor: pointer;
}
.withdraw-content {
flex: 1;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
padding: 24px 16px 24px;
background: #fff;
}
.withdraw-balance-card {
display: block;
text-align: center;
padding: 12px 0 32px;
margin-bottom: 0;
}
.withdraw-balance-amount {
font-size: 48px;
line-height: 1;
font-weight: 800;
color: #FFA000;
letter-spacing: 0;
}
.withdraw-balance-amount span {
font-size: 14px;
font-weight: 400;
margin-left: 4px;
}
.withdraw-balance-sub {
display: none;
}
.withdraw-method {
margin-bottom: 24px;
}
.withdraw-method-head {
display: flex;
align-items: center;
gap: 8px;
min-height: 26px;
margin-bottom: 12px;
}
.withdraw-method-title {
flex: 1;
font-size: 15px;
font-weight: 600;
color: var(--gray-900);
}
.withdraw-wechat-logo {
width: 22px;
height: 22px;
border-radius: 50%;
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: #22C55E;
}
.withdraw-wechat-logo svg {
width: 16px;
height: 16px;
display: block;
}
.withdraw-amount-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 8px;
}
.withdraw-amount {
min-height: 66px;
border: 1px solid #F0F0F0;
border-radius: 12px;
background: #FAFAFA;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
cursor: pointer;
font-family: inherit;
font-size: 18px;
font-weight: 700;
color: #1A1A1A;
box-shadow: 0 5px 14px rgba(0,0,0,.032);
}
.withdraw-amount small {
font-size: 12px;
font-weight: 400;
margin-left: 2px;
}
.withdraw-amount.active {
border-color: #FFD600;
background: #FFFDE7;
color: #1A1A1A;
box-shadow: inset 0 0 0 1px #FFD600, 0 7px 16px rgba(255,214,0,.13);
}
.wechat-bind-layer {
display: none;
position: absolute;
inset: 0;
z-index: 285;
overflow: hidden;
}
.wechat-bind-layer.open {
display: block;
}
.wechat-bind-mask {
position: absolute;
inset: 0;
background: rgba(0,0,0,.56);
}
.wechat-bind-sheet {
position: absolute;
left: 0;
right: 0;
bottom: 0;
min-height: 520px;
border-radius: 24px 24px 0 0;
background: #FAFAFA;
padding: 24px 16px 24px;
animation: exchangeSheetUp .24s ease-out;
}
.wechat-bind-close {
width: 38px;
height: 38px;
border-radius: 18px;
border: none;
background: #F0F0F0;
display: inline-flex;
align-items: center;
justify-content: center;
color: #1A1A1A;
font-size: 28px;
font-weight: 400;
line-height: 1;
cursor: pointer;
font-family: inherit;
}
.wechat-bind-amount-label {
margin-top: 4px;
text-align: center;
font-size: 17px;
font-weight: 400;
color: var(--gray-600);
}
.wechat-bind-amount {
margin-top: 8px;
text-align: center;
font-size: 48px;
line-height: 1;
font-weight: 700;
color: #1A1A1A;
}
.wechat-bind-amount small {
font-size: 18px;
font-weight: 400;
margin-left: 4px;
}
.wechat-bind-card {
margin-top: 64px;
min-height: 92px;
border-radius: 14px;
background: #fff;
display: flex;
align-items: center;
gap: 12px;
padding: 0 16px;
box-shadow: 0 6px 20px rgba(0,0,0,.03);
}
.wechat-bind-card-logo {
width: 34px;
height: 34px;
border-radius: 18px;
background: #22C55E;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.wechat-bind-card-logo svg {
width: 24px;
height: 24px;
}
.wechat-bind-card-title {
font-size: 17px;
font-weight: 600;
color: #1A1A1A;
}
.wechat-bind-card-sub {
margin-top: 6px;
font-size: 14px;
font-weight: 400;
color: var(--gray-500);
}
.wechat-bind-cta {
position: absolute;
left: 20px;
right: 20px;
bottom: 28px;
height: 56px;
border: none;
border-radius: 14px;
background: linear-gradient(90deg, #FFE066 0%, #FFD600 52%, #FFE066 100%);
color: #1A1A1A;
font-family: inherit;
font-size: 17px;
font-weight: 600;
cursor: pointer;
}
.exchange-layer {
display: none;
position: absolute;
inset: 0;
z-index: 280;
overflow: hidden;
}
.exchange-layer.open {
display: block;
}
.exchange-mask {
position: absolute;
inset: 0;
background: rgba(0,0,0,.72);
}
.exchange-sheet {
position: absolute;
left: 0;
right: 0;
bottom: 0;
background: #fff;
border-radius: 24px 24px 0 0;
padding: 24px 16px 24px;
animation: exchangeSheetUp .24s ease-out;
}
@keyframes exchangeSheetUp {
from { transform: translateY(100%); }
to { transform: translateY(0); }
}
.exchange-close {
position: absolute;
top: 18px;
right: 18px;
width: 38px;
height: 38px;
border-radius: 18px;
border: none;
background: #F0F0F0;
color: #1A1A1A;
font-size: 24px;
line-height: 1;
cursor: pointer;
font-family: inherit;
}
.exchange-title {
font-size: 20px;
font-weight: 900;
color: #1A1A1A;
margin-bottom: 8px;
}
.exchange-balance {
font-size: 15px;
font-weight: 600;
color: var(--gray-500);
margin-bottom: 24px;
}
.exchange-balance strong {
color: var(--gray-700);
font-weight: 800;
}
.exchange-convert-card {
height: 64px;
border-radius: 8px;
background: #FAFAFA;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px 0 16px;
margin-bottom: 24px;
}
.exchange-coin-side {
display: flex;
align-items: center;
gap: 8px;
}
.exchange-coin-icon {
width: 30px;
height: 30px;
border-radius: 14px;
background: radial-gradient(circle at 35% 30%, #FFE0B2, #FFB300);
display: flex;
align-items: center;
justify-content: center;
color: #8B6914;
font-weight: 900;
font-size: 16px;
}
.exchange-coin-num {
font-size: 24px;
font-weight: 900;
color: #000;
}
.exchange-cash-num {
font-size: 28px;
font-weight: 900;
color: #FE2C55;
}
.exchange-cash-num small {
font-size: 17px;
margin-left: 2px;
}
.exchange-presets {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
margin-bottom: 12px;
}
.exchange-preset {
height: 48px;
border-radius: 8px;
border: 1px solid #F0F0F0;
background: #fff;
color: var(--gray-400);
font-family: inherit;
font-size: 16px;
font-weight: 700;
cursor: pointer;
}
.exchange-preset.active {
border-color: #FF6B6B;
color: #FE2C55;
background: #FAFAFA;
}
.exchange-error {
min-height: 22px;
color: #FE2C55;
font-size: 14px;
font-weight: 700;
margin-bottom: 24px;
}
.exchange-rule {
text-align: center;
font-size: 15px;
font-weight: 600;
color: var(--gray-500);
margin-bottom: 24px;
}
.exchange-submit {
width: 100%;
height: 56px;
border: none;
border-radius: 8px;
background: #FE2C55;
color: #fff;
font-family: inherit;
font-size: 18px;
font-weight: 700;
cursor: pointer;
}
/* === 标准返回按钮 (统一 design.md - icons.back-chevron) === */
.btn-back {
width: 44px; height: 44px;
display: flex; align-items: center; justify-content: center;
background: transparent; border: none; padding: 0;
cursor: pointer; -webkit-tap-highlight-color: transparent;
flex-shrink: 0;
}
.btn-back:active { opacity: 0.5; }
.btn-back svg { width: 24px; height: 24px; display: block; }
</style>
<style>
/* === 跳转动画(原 shared/transitions.css 内嵌) === */
/* tokens.css 是空文件已废弃,未内嵌;token 由各 HTML 的 :root 单独定义 */
/**
* 7 种跳转动画
* 用法:JS 给 body 加 .transitioning-{词} class,触发对应动画
*/
/* push: 右进左出(进入下一层级) */
@keyframes nav-push-out {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(-30%); opacity: 0.6; }
}
body.transitioning-push { animation: nav-push-out 280ms ease-in forwards; }
/* pop: 左进右出(返回) */
@keyframes nav-pop-out {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(100%); opacity: 0.8; }
}
body.transitioning-pop { animation: nav-pop-out 280ms ease-in forwards; }
/* modal: 从底弹起(即将离开页面,向上让位) */
@keyframes nav-modal-out {
from { transform: translateY(0); opacity: 1; }
to { transform: translateY(-8%); opacity: 0.6; }
}
body.transitioning-modal { animation: nav-modal-out 280ms ease-in forwards; }
/* dismiss: 向下收起 */
@keyframes nav-dismiss-out {
from { transform: translateY(0); opacity: 1; }
to { transform: translateY(100%); opacity: 0.4; }
}
body.transitioning-dismiss { animation: nav-dismiss-out 280ms ease-in forwards; }
/* replace: 直接替换(淡出) */
@keyframes nav-replace-out {
from { opacity: 1; }
to { opacity: 0; }
}
body.transitioning-replace { animation: nav-replace-out 200ms ease-out forwards; }
/* fade: 淡入淡出(splash 类过渡) */
@keyframes nav-fade-out {
from { opacity: 1; }
to { opacity: 0; }
}
body.transitioning-fade { animation: nav-fade-out 400ms ease-in-out forwards; }
/* tab: 无动画(瞬切) */
body.transitioning-tab { /* no animation */ }
/* 进入动画(DOMContentLoaded 前的初始状态由 body.preanim 控制,可选) */
@keyframes nav-fade-in {
from { opacity: 0; }
to { opacity: 1; }
}
body { /* nav-fade-in disabled — tab 切换闪烁源头 */ }
</style>
<style>
html,body{background:#fff;display:block;height:100%;margin:0;cursor:auto;overflow:hidden;overscroll-behavior:none}
.device{width:100%;height:100%;border-radius:0;box-shadow:none}
.screen,.home-scroll{overscroll-behavior:none;-webkit-overflow-scrolling:touch}
#variantToggle,#debugBack{display:none!important}
.tab-bar{display:none!important} /* 底栏归原生壳:隐藏 H5 原型自带 tabbar,修双底栏 */
/* 顶部品牌带吸顶(去框后).第一版用 flex 固定头(#home overflow:hidden + .home-scroll min-height:0)
在本机 WebView 上把页面整个卡死、无法滚动,放弃。改用与已验证可用的 records 页同款「绝对定位」法:
.screen(position:absolute 定位容器)内,品牌带绝对贴顶、home-scroll 绝对占满其下并自身滚动——
不依赖 flex 高度链或 position:sticky,在本机 WebView 上最稳(对齐 records .hist-scroll 的 top/bottom 定位)。
margin-top:0 去框后无假状态栏,贴系统状态栏下(原生壳已 statusBarsPadding);feed-sort top:0 紧贴品牌带下。 */
#home{overflow:hidden}
#home .brand-strip{position:absolute; top:0; left:0; right:0; margin-top:0; z-index:5}
#home .home-scroll{position:absolute; top:38px; left:0; right:0; bottom:0}
#home .feed-sort{top:0}
</style>
</head>
<body>
<!--
@page: home
@path: app/home
@source: 2026-05-18.html
TODO: 补充 @owner / @from / @to / @notes(参照 FLOW.md
-->
<!-- 方案切换控制面板 -->
<div class="variant-toggle" id="variantToggle">
<button class="variant-btn" onclick="toggleVariantPanel()" title="方案切换"></button>
<div class="variant-panel" id="variantPanel">
<h3>方案切换</h3>
<div id="variantList"><div class="variant-empty">暂无多方案模块<br><span style="font-size: 11px;">在 variants 配置中添加模块即可</span></div></div>
</div>
</div>
<div class="device" id="app">
<!-- 调试返回按钮 -->
<div id="debugBack" onclick="debugGoBack()" style="position: absolute; left: 0px; bottom: 0px; z-index: 99999; background: rgb(0, 0, 0); color: rgb(255, 255, 255); font-size: 11px; font-weight: 700; padding: 8px 8px; border-radius: 0px 4px 0px 0px; cursor: pointer; opacity: 0.7; line-height: 1; letter-spacing: 1px;">返回</div>
<!-- ===== 11. LOGIN ===== -->
<!-- screen #login removed for split -->
<!-- 入口页协议 · 底部抽屉(device 级 overlay,可跨屏触发:login / celebration 都用) -->
<div id="agreeModal" class="login-agreement-sheet-layer">
<div class="login-agreement-sheet">
<div class="login-agreement-sheet-title">用户协议及隐私保护</div>
<div class="login-agreement-sheet-copy">我已阅读并同意<span class="link">用户协议</span><span class="link">基本功能隐私政策</span></div>
<button class="login-agreement-sheet-primary" type="button" onclick="agreeAndLogin()">同意并登录</button>
<button class="login-agreement-sheet-secondary" type="button" onclick="closeAgreeModal()">不同意</button>
</div>
</div>
<!-- 通用 Toastdevice 级,跨屏可见) -->
<div id="loginToast" style="display: none;position: absolute;left: 50%;bottom: 120px;transform: translateX(-50%);background: rgba(0,0,0,.75);color: #fff;font-size: 13px;font-weight: 500;padding: 8px 16px;border-radius: 8px;white-space: nowrap;z-index: 40;"></div>
<!-- ===== 11b. LOGIN PHONE(独立手机号登录页) ===== -->
<!-- screen #loginPhone removed for split -->
<!-- ===== HOME TAB ===== -->
<div class="screen active" id="home" style="background: var(--bg);">
<!-- 顶部品牌带 wordmark(吸顶:移出滚动区,作 .screen 固定头,不随内容滚动) -->
<div class="brand-strip">
<div class="brand-strip-left">
<img class="brand-strip-logo" src="assets/shared/logos/sb-brand.png" alt="">
<span class="brand-strip-name">傻瓜比价</span>
</div>
<span class="brand-strip-tagline">跨平台比价,用傻瓜</span>
</div>
<div class="home-scroll" style="flex: 1;overflow-y: auto;">
<!-- 顶部 Banner —— 3 个点位的可滑动轮播 -->
<div class="home-banner" id="homeBanner">
<div class="home-banner-track" id="homeBannerTrack">
<div class="home-banner-slide" data-banner-idx="0" data-nav-target="guideVideo" style="cursor: pointer;">
<img src="assets/home/index/banner-top-cropped.png" alt="如何使用傻瓜比价 · 新手指南">
</div>
<div class="home-banner-slide" data-banner-idx="1" data-nav-target="invite" style="cursor: pointer;">
<div class="home-banner-slide-fill bf-orange">
<div class="bf-title">邀好友 赚金币</div>
<div class="bf-pill">立即邀请 </div>
<div class="bf-deco">🎁</div>
</div>
</div>
</div>
<div class="home-banner-dots" id="homeBannerDots">
<span class="home-banner-dot active" data-dot-idx="0"></span>
<span class="home-banner-dot" data-dot-idx="1"></span>
</div>
</div>
<!-- v2026-06-01:节省日志横条(喇叭 + 时间戳 feed,整体居中) -->
<div class="home-marquee" id="homeMarquee">
<div class="home-mq-viewport">
<div class="home-mq-track" id="homeMarqueeTrack">
<div class="home-mq-item">
<span class="home-mq-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="M3 9.5h3l8-5v15l-8-5H3v-5z" fill="currentColor"/><path d="M17 9.5c1 .9 1 4.1 0 5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/><path d="M19.5 7c2 1.8 2 8.2 0 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/></svg></span>
<span class="home-mq-copy"><span class="mq-time">14:23:18</span><span class="mq-user" data-nick="省钱小能手">省***手</span><span class="mq-act">比价后节省</span><span class="mq-amt">88888.88</span><span class="mq-unit"></span></span>
</div>
<div class="home-mq-item">
<span class="home-mq-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="M3 9.5h3l8-5v15l-8-5H3v-5z" fill="currentColor"/><path d="M17 9.5c1 .9 1 4.1 0 5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/><path d="M19.5 7c2 1.8 2 8.2 0 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/></svg></span>
<span class="home-mq-copy"><span class="mq-time">14:22:45</span><span class="mq-user" data-uid="12902">1***2</span><span class="mq-act">比价后节省</span><span class="mq-amt">42.40</span><span class="mq-unit"></span></span>
</div>
<div class="home-mq-item">
<span class="home-mq-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="M3 9.5h3l8-5v15l-8-5H3v-5z" fill="currentColor"/><path d="M17 9.5c1 .9 1 4.1 0 5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/><path d="M19.5 7c2 1.8 2 8.2 0 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/></svg></span>
<span class="home-mq-copy"><span class="mq-time">14:20:31</span><span class="mq-user" data-nick="小确呀">小**</span><span class="mq-act">比价后节省</span><span class="mq-amt">7.90</span><span class="mq-unit"></span></span>
</div>
<div class="home-mq-item">
<span class="home-mq-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="M3 9.5h3l8-5v15l-8-5H3v-5z" fill="currentColor"/><path d="M17 9.5c1 .9 1 4.1 0 5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/><path d="M19.5 7c2 1.8 2 8.2 0 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/></svg></span>
<span class="home-mq-copy"><span class="mq-time">14:18:07</span><span class="mq-user" data-uid="77150">7***0</span><span class="mq-act">比价后节省</span><span class="mq-amt">156.30</span><span class="mq-unit"></span></span>
</div>
<div class="home-mq-item">
<span class="home-mq-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="M3 9.5h3l8-5v15l-8-5H3v-5z" fill="currentColor"/><path d="M17 9.5c1 .9 1 4.1 0 5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/><path d="M19.5 7c2 1.8 2 8.2 0 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/></svg></span>
<span class="home-mq-copy"><span class="mq-time">14:15:42</span><span class="mq-user" data-nick="团团">团**</span><span class="mq-act">比价后节省</span><span class="mq-amt">23.50</span><span class="mq-unit"></span></span>
</div>
<div class="home-mq-item">
<span class="home-mq-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="M3 9.5h3l8-5v15l-8-5H3v-5z" fill="currentColor"/><path d="M17 9.5c1 .9 1 4.1 0 5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/><path d="M19.5 7c2 1.8 2 8.2 0 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/></svg></span>
<span class="home-mq-copy"><span class="mq-time">14:11:29</span><span class="mq-user" data-uid="66180">6***0</span><span class="mq-act">比价后节省</span><span class="mq-amt">89.40</span><span class="mq-unit"></span></span>
</div>
<!-- 末尾复制首条,保证循环切回顶部无视觉跳变 -->
<div class="home-mq-item">
<span class="home-mq-dot" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="M3 9.5h3l8-5v15l-8-5H3v-5z" fill="currentColor"/><path d="M17 9.5c1 .9 1 4.1 0 5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/><path d="M19.5 7c2 1.8 2 8.2 0 10" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" fill="none"/></svg></span>
<span class="home-mq-copy"><span class="mq-time">14:23:18</span><span class="mq-user" data-nick="省钱小能手">省***手</span><span class="mq-act">比价后节省</span><span class="mq-amt">88888.88</span><span class="mq-unit"></span></span>
</div>
</div>
</div>
</div>
<!-- v2026-06-03 新增:平台 3 数据板块(单卡 3 列竖线) -->
<div class="home-3stat">
<div class="h3s-card">
<div class="h3s-label">帮助用户</div>
<div class="h3s-num"><span class="h3s-value">12847<span class="h3s-unit"></span></span></div>
</div>
<div class="h3s-card">
<div class="h3s-label">完成比价</div>
<div class="h3s-num"><span class="h3s-value">86532<span class="h3s-unit"></span></span></div>
</div>
<div class="h3s-card">
<div class="h3s-label">累计节省</div>
<div class="h3s-num"><span class="h3s-value">37621.4<span class="h3s-unit"></span></span></div>
</div>
</div>
<div class="last-compare-banner" id="lastCompareBanner" onclick="go('compresult')" aria-live="polite" style="display:none">
<div class="lcb-head">
<span class="lcb-kicker" id="lastCompareAge">上次比价</span>
<span class="lcb-status"></span>
</div>
<div class="lcb-body">
<div class="lcb-main">
<div class="lcb-top">
<span class="lcb-shop" id="lastCompareShop">窑鸡王(康营店)</span>
</div>
<div class="lcb-meta">
<span class="lcb-dish" id="lastCompareDish">招牌窑鸡整只-原味</span>
</div>
</div>
<div class="lcb-pricebox" id="lastComparePriceBox">
<div class="lcb-price-label">最低价</div>
<div class="lcb-price" id="lastComparePrice">¥57.9</div>
<div class="lcb-platform" id="lastComparePlatformWrap">
<img id="lastComparePlatformLogo" src="assets/shared/logos/taobao.png" alt="">
<span class="lcb-platform-name" id="lastComparePlatform">淘宝</span>
</div>
</div>
</div>
<div class="lcb-foot">
<span class="lcb-note" id="lastCompareNote">去查看</span>
<div class="lcb-arrow" aria-hidden="true">
<svg viewBox="0 0 24 24" fill="none">
<path d="M9 5l7 7-7 7" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
</div>
</div>
<!-- v2026-06-01 改版:左 平台累计省下 + 右 点外卖赚金币 -->
<div class="home-tri">
<!-- 左:今日领券(结构 1:1 镜像右卡 .cta-g6title + sub + bottom CTA -->
<div class="today-coupon" id="homeStatsCard" onclick="onTriClaimClick()">
<div class="tc-title">今日<span class="hl">大额优惠券</span></div>
<div class="tc-sub">AI 一键自动领</div>
<div class="tc-bottom">
<button class="tc-cta">去领取</button>
</div>
<div class="tc-coupon-symbols" aria-hidden="true">
<img src="assets/home/index/coupon-ticket-cluster.png" alt="">
</div>
<div class="tc-tap-hand" aria-hidden="true"><img src="assets/home/index/tap-hand-generated.png" alt=""></div>
</div>
<div class="home-tri-arrow" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none"><path d="M4 12h13M12 6l7 6-7 6" stroke="currentColor" stroke-width="3.4" stroke-linecap="round" stroke-linejoin="round"/></svg></div>
<!-- 右:点外卖赚金币(原货比三家文案改) → 点击弹平台选择 -->
<div class="cta-g6" id="homeCompareEntry" onclick="onHomeCompareClick()">
<div class="g6-title">点外卖,<span class="hl">赚金币</span></div>
<div class="g6-sub">快去解锁全网底价</div>
<div class="g6-bottom">
<button class="g6-cta">去点餐</button>
</div>
<div class="g6-logos">
<img src="assets/shared/logos/meituan.png" alt="">
<img src="assets/shared/logos/taobao.png" alt="">
<img src="assets/shared/logos/jd.png" alt="">
</div>
<div class="g6-tap-hand" aria-hidden="true"><img src="assets/home/index/tap-hand-generated.png" alt=""></div>
</div>
</div>
<!-- 5 类别快捷入口(暂时隐藏,元素保留,以后会加回来) -->
<div class="home-cats" style="display:none;">
<div class="home-cat">
<img src="assets/home/index/categories/hotel.png" alt="">
<span class="home-cat-label">特惠酒店</span>
</div>
<div class="home-cat">
<img src="assets/home/index/categories/movie.png" alt="">
<span class="home-cat-label">折扣电影票</span>
</div>
<div class="home-cat">
<img src="assets/home/index/categories/taxi.png" alt="">
<span class="home-cat-label">打车券</span>
</div>
<div class="home-cat">
<img src="assets/home/index/categories/express.png" alt="">
<span class="home-cat-label">寄快递</span>
</div>
<div class="home-cat" onclick="go('invite')">
<img src="assets/home/index/categories/invite-earn.png" alt="">
<span class="home-cat-label">分享赚钱</span>
</div>
</div>
<!-- 推荐信息流 -->
<div class="home-feed-wrap">
<div class="feed-sort" data-sort>
<div class="fs-item active" data-key="rec"><span class="lbl">智能推荐</span></div>
<div class="fs-item" data-key="distance"><span class="lbl">距离最近</span></div>
<div class="fs-item" data-key="sales"><span class="lbl">销量最高</span></div>
</div>
<!-- 位置权限未授权·feed 空态(未授权时替换 feed + 排序 tab;授权后由 applyLocationState 切回 feed -->
<div class="home-feed-empty" id="homeFeedEmpty" style="display: none;flex-direction: column;align-items: center;justify-content: flex-start;background: #fff;padding: 18px 24px 24px;text-align: center;">
<div style="width: 48px;height: 48px;border-radius: 50%;background: #F5F5F5;display: flex;align-items: center;justify-content: center;margin-bottom: 10px;">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" aria-hidden="true">
<path d="M12 21.5s6.5-5.4 6.5-10.5a6.5 6.5 0 1 0-13 0c0 5.1 6.5 10.5 6.5 10.5z" stroke="#CCCCCC" stroke-width="1.6" fill="#fff"/>
<circle cx="12" cy="10.8" r="2.4" stroke="#CCCCCC" stroke-width="1.6" fill="none"/>
</svg>
</div>
<div style="font-size: 14px;color: #999;line-height: 1.5;">授权地理位置,附近美食优惠马上看~</div>
<button onclick="goAuthLocation()" style="margin-top: 12px;height: 32px;padding: 0 20px;border: none;border-radius: 18px;font-size: 13px;font-weight: 600;background: linear-gradient(180deg,#FFE066 0%,#FFC400 100%);color: #1A1A1A;box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 6px rgba(255,179,0,.35);cursor: pointer;display: inline-flex;align-items: center;justify-content: center;">去授权</button>
</div>
<!-- 已授权但附近暂无推荐·feed 空态(推荐列表为空时替换 feed;点击去比价复用首页「去点餐」逻辑) -->
<div class="home-feed-empty" id="homeFeedNoDealEmpty" style="display: none;flex-direction: column;align-items: center;justify-content: flex-start;background: #fff;padding: 18px 24px 24px;text-align: center;">
<img src="assets/home/index/no-deal-search.png" alt="" style="width: 48px;height: 48px;object-fit: contain;display: block;margin-bottom: 10px;" aria-hidden="true">
<div style="font-size: 14px;color: #999;line-height: 1.5;">附近暂无优惠商品~去外卖平台解锁底价</div>
<button onclick="onHomeCompareClick()" style="margin-top: 12px;height: 32px;padding: 0 20px;border: none;border-radius: 18px;font-size: 13px;font-weight: 600;background: linear-gradient(180deg,#FFE066 0%,#FFC400 100%);color: #1A1A1A;box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 6px rgba(255,179,0,.35);cursor: pointer;display: inline-flex;align-items: center;justify-content: center;">去比价</button>
</div>
<!-- feed 首次加载中(转圈占位) -->
<div class="home-feed-empty" id="homeFeedLoading" style="display: none;flex-direction: column;align-items: center;justify-content: flex-start;background: #fff;padding: 32px 24px;text-align: center;">
<div style="font-size: 14px;color: #999;">加载中…</div>
</div>
<!-- feed 服务繁忙(degraded:上游/库失败已降级,可重试) -->
<div class="home-feed-empty" id="homeFeedDegraded" style="display: none;flex-direction: column;align-items: center;justify-content: flex-start;background: #fff;padding: 18px 24px 24px;text-align: center;">
<div style="font-size: 14px;color: #999;line-height: 1.5;">服务繁忙,下拉重试~</div>
<button onclick="HomeFeed.loadFirst()" style="margin-top: 12px;height: 32px;padding: 0 20px;border: none;border-radius: 18px;font-size: 13px;font-weight: 600;background: linear-gradient(180deg,#FFE066 0%,#FFC400 100%);color: #1A1A1A;cursor: pointer;">重试</button>
</div>
<!-- H2b: feed 卡片由 JS 按 /api/v1/meituan/feed|top-sales 动态渲染(原 mock 静态卡已移除) -->
<div class="home-feed"></div>
<div class="feed-end" style="display:none">已经到底了</div>
</div>
</div>
<div class="tab-bar">
<div class="tab-item active" onclick="go('home')"><span class="tab-icon"><svg class="i-outline" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 12l9-8 9 8"></path><path d="M5 10v9a1 1 0 001 1h3v-5h6v5h3a1 1 0 001-1v-9"></path></svg><svg class="i-filled" width="24" height="24" viewBox="0 0 24 24" fill="none"><path d="M3 12l9-8 9 8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M5 10v9a1 1 0 001 1h3v-5h6v5h3a1 1 0 001-1v-9" fill="#FFD600" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span><span class="tab-label">首页</span></div>
<div class="tab-item" id="homeTabWelfare" onclick="go('welfare')"><span class="welfare-tab-dot" aria-hidden="true"></span><span class="tab-icon" id="homeTabWelfareIcon"><svg class="i-outline" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="8" width="18" height="4" rx="1"></rect><path d="M12 8v12"></path><path d="M5 12v7a1 1 0 001 1h12a1 1 0 001-1v-7"></path><path d="M7.5 8C6 8 5 6.5 6 5.5S9 4 9.5 5.5c.4 1.2 2.5 2.5 2.5 2.5s2.1-1.3 2.5-2.5C15 4 17 4.5 18 5.5s-1 2.5-2.5 2.5"></path></svg><svg class="i-filled" width="24" height="24" viewBox="0 0 24 24" fill="none"><rect x="3" y="8" width="18" height="4" rx="1" fill="#FFD600" stroke="#1a1a1a" stroke-width="2"></rect><path d="M5 12v7a1 1 0 001 1h12a1 1 0 001-1v-7" fill="#FFD600" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path><path d="M12 8v12" stroke="#1a1a1a" stroke-width="2"></path><path d="M7.5 8C6 8 5 6.5 6 5.5S9 4 9.5 5.5c.4 1.2 2.5 2.5 2.5 2.5s2.1-1.3 2.5-2.5C15 4 17 4.5 18 5.5s-1 2.5-2.5 2.5" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span><span class="tab-label">赚钱</span></div>
<div class="tab-item" onclick="go('history')"><span class="tab-icon"><svg class="i-outline" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2.5"></rect><path d="M8 9h8"></path><path d="M8 13h8"></path><path d="M8 17h5"></path></svg><svg class="i-filled" width="24" height="24" viewBox="0 0 24 24" fill="none"><rect x="4" y="4" width="16" height="16" rx="2.5" fill="#FFD600" stroke="#1a1a1a" stroke-width="2"></rect><path d="M8 9h8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"></path><path d="M8 13h8" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"></path><path d="M8 17h5" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round"></path></svg></span><span class="tab-label">记录</span></div>
<div class="tab-item" onclick="go('profile')"><span class="tab-icon"><svg class="i-outline" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"></circle><path d="M4 20c0-4 4-6 8-6s8 2 8 6"></path></svg><svg class="i-filled" width="24" height="24" viewBox="0 0 24 24" fill="none"><circle cx="12" cy="8" r="4" fill="#FFD600" stroke="#1a1a1a" stroke-width="2"></circle><path d="M4 20c0-4 4-6 8-6s8 2 8 6" fill="#FFD600" stroke="#1a1a1a" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg></span><span class="tab-label">我的</span></div>
</div>
</div>
<!-- [ECOM-ONLY] BLIND BOX screen removed for waimai V1 -->
<!-- Original: 150+ lines of blind box full-page screen with card slider -->
<!-- ===== WELFARE TAB (红果风格) ===== -->
<!-- screen #welfare removed for split -->
<!-- ===== WELFARE RULES / 活动规则 ===== -->
<!-- screen #welfareRules removed for split -->
<!-- ===== PROFILE TAB ===== -->
<style>
/* —— 我的页 (profile) —— */
#profile {
background: radial-gradient(circle at 84% 17%, rgba(255,255,255,.82) 0 36px, transparent 37px),
radial-gradient(circle at 98% 20%, rgba(255,255,255,.66) 0 42px, transparent 43px),
linear-gradient(180deg, #FFF1D9 0%, #FFF6E8 28%, #FFF8EF 48%, #F8F8F8 70%, #F7F7F7 100%);
}
.prof-statusbar { position: absolute; left: 0; right: 0; top: 0; height: 42px; z-index: 20; display: flex; align-items: center; justify-content: space-between; padding: 8px 32px 0 32px; pointer-events: none; color: #000; }
.prof-status-time { font-size: 15px; font-weight: 600; letter-spacing: .1px; line-height: 1; font-variant-numeric: tabular-nums; }
.prof-status-icons { display: flex; align-items: center; gap: 8px; }
.prof-cell { display: flex; align-items: flex-end; gap: 2px; height: 14px; }
.prof-cell i { display: block; width: 3px; border-radius: 4px; background: #000; }
.prof-cell i:nth-child(1) { height: 5px; }
.prof-cell i:nth-child(2) { height: 8px; }
.prof-cell i:nth-child(3) { height: 11px; }
.prof-cell i:nth-child(4) { height: 14px; }
.prof-wifi { position: relative; width: 17px; height: 13px; overflow: hidden; }
.prof-wifi::before, .prof-wifi::after { content: ''; position: absolute; left: 50%; border: 3px solid #000; border-color: #000 transparent transparent transparent; border-radius: 50%; transform: translateX(-50%); }
.prof-wifi::before { width: 21px; height: 21px; top: 1px; }
.prof-wifi::after { width: 11px; height: 11px; top: 6px; }
.prof-wifi i { position: absolute; left: 50%; bottom: 0; width: 5px; height: 5px; margin-left: -2.5px; border-radius: 50%; background: #000; }
.prof-battery { position: relative; width: 23px; height: 12px; border: 1.7px solid #000; border-radius: 4px; padding: 2px; }
.prof-battery::before { content: ''; position: absolute; right: -3.7px; top: 3.1px; width: 2px; height: 5px; border-radius: 0 4px 4px 0; background: #000; }
.prof-battery i { display: block; width: 100%; height: 100%; border-radius: 4px; background: #000; }
.prof-scroll { flex: 1; overflow-y: auto; overflow-x: hidden; }
.prof-hero { position: relative; padding: 64px 16px 0; min-height: 154px; overflow: visible; }
.prof-clouds { position: absolute; left: 0; right: 0; top: 0; width: 100%; height: 174px; object-fit: cover; opacity: .72; pointer-events: none; }
.prof-piggy { position: absolute; top: 36px; right: -12px; width: 160px; height: auto; pointer-events: none; z-index: 1; filter: drop-shadow(0 10px 18px rgba(255,125,50,.16)); }
.prof-hero::after { content: ''; position: absolute; right: 160px; top: 114px; width: 9px; height: 9px; background: #FFB300; transform: rotate(45deg); box-shadow: 7px 0 0 -3px rgba(255,179,0,.75), 0 7px 0 -3px rgba(255,179,0,.75); z-index: 2; }
.prof-id { position: relative; z-index: 2; display: flex; align-items: center; gap: 12px; }
.prof-avatar { width: 74px; height: 74px; border-radius: 50%; flex-shrink: 0; background: radial-gradient(circle at 42% 32%, #FFE0B2 0%, #FFE066 43%, #FFEB3B 100%); border: 3px solid #fff; box-shadow: 0 6px 16px rgba(255,137,32,.20); display: flex; align-items: center; justify-content: center; overflow: hidden; }
.prof-avatar svg { width: 70px; height: 70px; display: block; }
.prof-meta { flex: 1; min-width: 0; padding-top: 2px; }
.prof-name { font-size: 18px; font-weight: 600; color: #000000; line-height: 1.16; letter-spacing: 0; }
.prof-phone { font-size: 13px; color: rgba(0,0,0,.58); margin-top: 6px; letter-spacing: .1px; font-variant-numeric: tabular-nums; }
.prof-badge { display: inline-flex; align-items: center; gap: 4px; margin-top: 8px; padding: 4px 8px 4px 6px; background: rgba(255,255,255,.58); border: 1px solid rgba(255,255,255,.95); border-radius: 999px; font-size: 12px; line-height: 1; font-weight: 700; color: #FF5722; box-shadow: 0 2px 8px rgba(143,84,25,.08); backdrop-filter: blur(4px); }
.prof-badge svg { width: 15px; height: 15px; }
.prof-badge svg path:first-child { fill: #FFA000; }
.prof-badge svg path:last-child { stroke: #fff; }
.prof-body-inner { position: relative; z-index: 3; padding: 12px 16px 16px; }
/* —— 累计帮省卡片 —— */
.prof-savings-card { position: relative; min-height: 244px; border-radius: 24px; padding: 24px 24px 16px; background: linear-gradient(180deg, #FF6A00 0%, #FF5722 52%, #FFA000 100%); box-shadow: 0 12px 24px rgba(255,117,31,.22), inset 0 1px 0 rgba(255,255,255,.30); overflow: hidden; color: #fff; border: 1px solid rgba(255,172,63,.55); }
.prof-savings-card::before { content: ''; position: absolute; inset: 0; background: radial-gradient(circle at 18% 26%, rgba(255,255,255,.10), transparent 18%), radial-gradient(circle at 75% 35%, rgba(255,255,255,.08), transparent 24%); pointer-events: none; }
.prof-savings-deco { position: absolute; left: -14px; bottom: -12px; width: 160px; height: 128px; object-fit: contain; object-position: left bottom; opacity: .30; pointer-events: none; }
.prof-savings-coin { position: absolute; top: auto; bottom: 112px; right: 18px; width: 44px; height: 44px; opacity: .82; pointer-events: none; transform: rotate(-22deg); }
.prof-savings-label { position: relative; z-index: 1; font-size: 15px; line-height: 1.1; font-weight: 700; color: rgba(255,255,255,.96); text-align: center; letter-spacing: .2px; }
.prof-amount-row { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; gap: 8px; height: 64px; margin: 6px 0 6px; }
.prof-amount-row::before { content: none; }
.prof-wheat { width: 46px; height: 64px; flex: 0 0 auto; opacity: .58; background-image: url('assets/profile/index/laurel.png'); background-repeat: no-repeat; background-size: 230px 77px; pointer-events: none; }
.prof-wheat.left { background-position: 17% center; }
.prof-wheat.right { background-position: 83% center; }
.prof-laurel { display: none; }
.prof-laurel.left { object-position: left center; }
.prof-laurel.right { object-position: right center; transform: scaleX(-1); }
.prof-amount { position: relative; z-index: 1; display: inline-flex; align-items: baseline; font-size: 36px; font-weight: 900; color: #fff; line-height: 1; letter-spacing: .1px; text-shadow: 0 2px 6px rgba(160,50,0,.16); font-variant-numeric: tabular-nums; }
.prof-currency { font-size: 28px; margin-right: 4px; vertical-align: 4px; }
.prof-stats-row { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; gap: 0; margin: 6px 0 12px; padding-top: 12px; border-top: 1px solid rgba(255,255,255,.35); }
.prof-stats-row > div:not(.prof-stats-div) { flex: 1; text-align: center; }
.prof-stat-num { font-size: 18px; font-weight: 900; color: #fff; line-height: 1; font-variant-numeric: tabular-nums; }
.prof-stat-key { font-size: 12px; font-weight: 600; color: rgba(255,255,255,.88); margin-top: 4px; }
.prof-stats-div { width: 1px; height: 38px; background: rgba(255,255,255,.33); }
.prof-savings-btn { position: relative; z-index: 1; display: flex; align-items: center; justify-content: center; gap: 6px; width: 142px; height: 34px; margin: 0 auto; padding: 0 16px; background: #fff; border: none; border-radius: 999px; font-size: 14px; font-weight: 600; color: #FF5722; cursor: pointer; box-shadow: 0 6px 12px rgba(214,82,0,.14); }
.prof-savings-btn span { font-size: 18px; line-height: 1; margin-left: 0; display: inline-block; transform: translateY(-1px); }
/* —— 省钱战绩卡 —— */
.prof-card { background: rgba(255,255,255,.96); border-radius: 18px; padding: 12px 12px; margin-top: 12px; box-shadow: 0 6px 18px rgba(151,93,35,.08); border: 1px solid rgba(255,255,255,.82); }
.prof-achv-title { display: flex; align-items: center; gap: 6px; font-size: 16px; font-weight: 600; color: #1A1A1A; margin-bottom: 12px; letter-spacing: 0; }
.prof-achv-title .prof-trophy { width: 23px; height: 23px; display: inline-flex; align-items: center; justify-content: center; background: transparent; border-radius: 0; font-size: 20px; filter: drop-shadow(0 3px 5px rgba(255,133,0,.22)); }
.prof-achv-title .prof-spark { color: #FFEB3B; font-size: 14px; margin-left: 2px; }
.prof-achv-row { display: flex; align-items: stretch; gap: 0; }
.prof-achv-item { position: relative; flex: 1; display: flex; align-items: flex-start; justify-content: center; gap: 0; min-width: 0; padding: 0 8px; text-align: center; }
.prof-achv-item > div { display: grid; grid-template-rows: 17px 23px 14px; justify-items: center; align-items: center; }
.prof-achv-item::after { content: ''; position: absolute; right: 0; top: 3px; bottom: 3px; width: 1px; background: #F0F0F0; }
.prof-achv-item:last-child::after { display: none; }
.prof-achv-ico { width: 34px; height: 34px; border-radius: 18px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
.prof-achv-ico.ico-orange { background: linear-gradient(180deg,#FFF3C4 0%,#FFE066 100%); }
.prof-achv-ico.ico-green { background: linear-gradient(180deg,#E8F5E9 0%,#CCCCCC 100%); }
.prof-achv-ico.ico-blue { background: linear-gradient(180deg,#E3F2FD 0%,#DDDDDD 100%); }
.prof-achv-ico svg { width: 19px; height: 19px; }
.prof-achv-item .prof-achv-key { font-size: 12px; color: #1A1A1A; font-weight: 500; line-height: 1.15; white-space: nowrap; }
.prof-achv-item .prof-achv-val { font-size: 18px; font-weight: 900; color: #FF5722; line-height: 1; margin-top: 0; font-variant-numeric: tabular-nums; }
.prof-achv-item .prof-achv-val span { font-size: 12px; font-weight: 500; color: #1A1A1A; margin-left: 2px; }
/* —— 菜单卡 —— */
.prof-menu-card { padding: 8px 12px; }
.prof-menu-item { display: flex; align-items: center; gap: 12px; padding: 6px 0; cursor: pointer; border-bottom: .5px solid #F0F0F0; }
.prof-menu-item:last-child { border-bottom: none; }
.prof-menu-ico { width: 34px; height: 34px; flex-shrink: 0; filter: drop-shadow(0 4px 6px rgba(49,99,180,.10)); }
.prof-menu-text { flex: 1; min-width: 0; }
.prof-menu-title { font-size: 14px; font-weight: 600; color: #1A1A1A; line-height: 1.2; }
.prof-menu-sub { font-size: 12px; color: #666666; margin-top: 2px; }
.prof-menu-arrow { font-size: 24px; line-height: 1; color: #999999; font-weight: 400; transform: translateY(-1px); }
/* —— 未登录 —— */
.prof-unlogged-hero { position: relative; padding: 64px 16px 24px; text-align: center; }
.prof-unlogged-hero .prof-avatar { margin: 0 auto 12px; background: linear-gradient(180deg,#F0F0F0 0%,#DDDDDD 100%); }
.prof-unlogged-cta { margin-top: 6px; background: linear-gradient(180deg,#FFD600 0%,#FFB300 100%); border: none; padding: 8px 24px; border-radius: 999px; font-size: 14px; font-weight: 700; color: #1a1a1a; cursor: pointer; box-shadow: 0 4px 12px rgba(255,180,52,.35); }
</style>
<!-- screen #profile removed for split -->
<!-- ===== INVITE FRIENDS ===== -->
<!-- screen #invite removed for split -->
<!-- ===== COMPARISON RESULT ===== -->
<!-- screen #compresult removed for split -->
<!-- ===== WITHDRAWAL / 提现(参考图二) ===== -->
<!-- screen #withdrawal removed for split -->
<div id="wechatBindSheet" class="wechat-bind-layer" aria-hidden="true">
<div class="wechat-bind-mask" onclick="closeWechatBindSheet()"></div>
<div class="wechat-bind-sheet" onclick="event.stopPropagation()">
<button class="wechat-bind-close" onclick="closeWechatBindSheet()" aria-label="关闭">×</button>
<div class="wechat-bind-amount-label">提现金额</div>
<div class="wechat-bind-amount"><span id="wechatBindAmount">0.3</span><small></small></div>
<div class="wechat-bind-card">
<span class="wechat-bind-card-logo" aria-hidden="true">
<svg viewBox="0 0 32 32" fill="none" aria-hidden="true">
<path d="M13.7 7.2C8 7.2 3.5 10.8 3.5 15.3c0 2.6 1.5 4.9 3.8 6.4l-.9 2.9 3.3-1.7c1.2.3 2.5.5 4 .5 5.6 0 10.2-3.6 10.2-8.1S19.3 7.2 13.7 7.2Z" fill="#fff"/>
<path d="M21 13.2c4.3.5 7.5 3.4 7.5 7 0 2.2-1.2 4.1-3.2 5.4l.7 2.4-2.8-1.5c-.9.2-1.9.4-3 .4-3.9 0-7.2-2.1-8.2-5.1.6.1 1.2.2 1.8.2 6.2 0 11.2-4 11.2-8.9v-.2Z" fill="#fff" opacity=".86"/>
<circle cx="10.1" cy="13.8" r="1" fill="#07C160"/>
<circle cx="16.7" cy="13.8" r="1" fill="#07C160"/>
</svg>
</span>
<div>
<div class="wechat-bind-card-title">暂未绑定微信账户</div>
<div class="wechat-bind-card-sub">绑定后才可提现,可以更换和解绑</div>
</div>
</div>
<button class="wechat-bind-cta" onclick="showPrototypeToast('原型演示:跳转微信授权绑定')">去微信绑定并提现</button>
</div>
</div>
<!-- ===== COIN HISTORY / 收益明细 ===== -->
<!-- screen #coinhistory removed for split -->
<div id="exchangeSheet" class="exchange-layer" aria-hidden="true">
<div class="exchange-mask" onclick="closeExchangeSheet()"></div>
<div class="exchange-sheet" onclick="event.stopPropagation()">
<button class="exchange-close" onclick="closeExchangeSheet()" aria-label="关闭">×</button>
<div class="exchange-title">金币兑换现金</div>
<div class="exchange-balance">金币余额:<strong id="exchangeBalanceText">28,967</strong></div>
<div class="exchange-convert-card">
<div class="exchange-coin-side">
<span class="exchange-coin-icon">¥</span>
<span class="exchange-coin-num" id="exchangeCoinAmount">28,967</span>
</div>
<div class="exchange-cash-num">= <span id="exchangeCashAmount">2.89</span><small></small></div>
</div>
<div class="exchange-presets">
<button class="exchange-preset" onclick="selectExchangeAmount(1000, this)">1000</button>
<button class="exchange-preset" onclick="selectExchangeAmount(2000, this)">2000</button>
<button class="exchange-preset" onclick="selectExchangeAmount(3000, this)">3000</button>
<button class="exchange-preset active" id="exchangePresetAll" onclick="selectExchangeAmount('all', this)">全部兑换</button>
</div>
<div class="exchange-error" id="exchangeError"></div>
<div class="exchange-rule">最低 100 金币,不足 100 的金币将原路返回</div>
<button class="exchange-submit" onclick="confirmExchange()">立即兑换</button>
</div>
</div>
<!-- ===== PURCHASE RECORDS ===== -->
<!-- screen #records removed for split -->
<!-- ===== SETTINGS ===== -->
<!-- screen #settings removed for split -->
<!-- 退出登录确认弹窗 -->
<div id="logoutConfirm" style="display: none;position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 260;background: rgba(0,0,0,.45);align-items: center;justify-content: center;padding: 40px 32px;overflow: hidden;">
<div style="background: #fff;border-radius: 18px;max-width: 295px;width: 100%;padding: 24px 24px 16px;text-align: center;">
<div style="font-size: 18px;font-weight: 700;color: var(--gray-900);margin-bottom: 8px;">退出登录</div>
<div style="font-size: 14px;color: var(--gray-500);line-height: 22px;margin-bottom: 24px;">退出后将无法继续累积金币收益</div>
<div style="display: flex;gap: 12px;">
<button onclick="closeLogoutConfirm()" style="flex: 1;padding: 12px;border-radius: 12px;border: 1px solid var(--gray-300);background: #fff;font-size: 15px;font-weight: 600;color: var(--gray-700);cursor: pointer;">取消</button>
<button onclick="doLogout()" style="flex: 1;padding: 12px;border-radius: 12px;border: none;background: var(--error);font-size: 15px;font-weight: 600;color: #fff;cursor: pointer;">确认退出</button>
</div>
</div>
</div>
<!-- 注销账号确认弹窗 -->
<div id="deleteConfirm" style="display: none;position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 260;background: rgba(0,0,0,.45);align-items: center;justify-content: center;padding: 40px 32px;overflow: hidden;">
<div style="background: #fff;border-radius: 18px;max-width: 295px;width: 100%;padding: 24px 24px 16px;text-align: center;">
<div style="font-size: 48px;margin-bottom: 12px;">⚠️</div>
<div style="font-size: 18px;font-weight: 700;color: var(--error);margin-bottom: 8px;">确认注销账号?</div>
<div style="font-size: 14px;color: var(--gray-500);line-height: 22px;margin-bottom: 8px;">注销后以下数据将<span style="color: var(--error);font-weight: 600;">永久删除且无法恢复</span></div>
<div style="text-align: left;background: var(--gray-50);border-radius: 8px;padding: 12px 16px;margin-bottom: 24px;font-size: 13px;color: var(--gray-600);line-height: 22px;">
· 全部金币余额和现金收益<br>
· 比价记录和购买战绩<br>
· 邀请好友关系和返佣收益
</div>
<div style="display: flex;gap: 12px;">
<button onclick="closeDeleteConfirm()" style="flex: 1;padding: 12px;border-radius: 12px;border: 1px solid var(--gray-300);background: #fff;font-size: 15px;font-weight: 600;color: var(--gray-700);cursor: pointer;">我再想想</button>
<button onclick="doDeleteAccount()" style="flex: 1;padding: 12px;border-radius: 12px;border: none;background: var(--error);font-size: 15px;font-weight: 600;color: #fff;cursor: pointer;">确认注销</button>
</div>
</div>
</div>
<!-- ===== FEEDBACK ===== -->
<!-- screen #feedback removed for split -->
<!-- ===== 领券流程屏(与新手引导同款,复用 demoSteps 的前 6 步:弹窗 → agentFlow → 完成 toast ===== -->
<!-- screen #comparing removed for split -->
<!-- ===== 一键领券 · 完成 toast(提到 device 级别,让结算弹窗显示在首页背景上而非 agent 流的截图上) ===== -->
<!-- ⚠️ SYNC: 与 prototypes/app/coupon/index.html 的 #phase-done 同步维护,改这里前先看那边 -->
<!-- 同步对象:本 toast 的 DOM 结构 + .coupon-done-k 系列 class 的 CSS -->
<!-- 不同步:onclick="onHomeCouponClaimDone()" 本地回调(首页特有) -->
<div id="demoCouponDoneToast" style="display: none;position: absolute;inset: 0;z-index: 200;">
<div class="coupon-done-k-mask"></div>
<div class="coupon-done-k">
<div class="coupon-done-logo">
<span class="logo-mark">
<span style="font-size: 12px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span>
</span>
</div>
<div class="v6-sparkle s1"></div>
<div class="v6-sparkle s2"></div>
<div class="v6-sparkle s3"></div>
<div class="v6-sparkle s4"></div>
<div class="v6-head">
<div class="lead-line lead-line-inline">今日已从<span id="couponDoneFromLogos" style="display:contents;"></span>帮您领取</div>
<div class="num-line"><span class="rest" style="margin-right: 6px;"></span><span class="num" id="demoCouponDoneCount">0</span><span class="rest">张优惠券</span></div>
</div>
<div class="v6-pocket">
<div class="v6-pocket-notch"></div>
<div class="v6-expiry">
<span class="time-box" id="cdH">23</span>:<span class="time-box" id="cdM">58</span>:<span class="time-box" id="cdS">27</span> 后失效
</div>
<button class="v6-cta" onclick="onHomeCouponClaimDone()">开心收下,立即使用</button>
</div>
</div>
</div>
<!-- ===== 一键领券 · 平台选择弹窗(device 级 overlay,显示在首页之上) ===== -->
<!-- ⚠️ SYNC: 与 prototypes/app/coupon/index.html 的 #phase-prompt 同步维护,改这里前先看那边 -->
<!-- 同步对象:本弹窗的 DOM 结构 + #couponPlatformGrid 渲染逻辑 -->
<!-- 不同步:本弹窗外层有 mask 关闭与 X 按钮(首页可关闭);标准模块 fade 进入无 mask 关闭 -->
<div id="demoCouponPrompt" style="display: none;position: absolute;inset: 0;z-index: 18;background: rgba(0,0,0,.55);align-items: center;justify-content: center;padding: 24px;" onclick="onCouponPromptMaskClick(event)">
<div id="demoCouponPromptCard" style="width: 100%;max-width: 330px;animation: fadeInUp .45s cubic-bezier(.34,1.56,.64,1);position: relative;">
<div style="background: linear-gradient(180deg,#FF7567 0%,#F44C43 56%,#ED3F36 100%);border-radius: 24px;padding: 32px 16px 24px;width: 100%;box-shadow: 0 16px 48px rgba(220,60,50,.36);border: 1px solid rgba(255,255,255,.2);text-align: center;position: relative;">
<div onclick="closeHomeCouponClaim()" style="position: absolute;top: 12px;right: 12px;width: 28px;height: 28px;border-radius: 14px;background: rgba(255,255,255,.2);display: flex;align-items: center;justify-content: center;cursor: pointer;font-size: 14px;color: #fff;font-weight: 600;line-height: 1;z-index: 2;"></div>
<img src="assets/welfare/index/coin-icon.png" alt="" style="position: absolute;top: 12px;right: 48px;width: 34px;height: 34px;z-index: 2;filter: drop-shadow(0 2px 4px rgba(150,40,0,.3));">
<span style="position:absolute;width:7px;height:7px;background:#FFE066;border-radius:50%;box-shadow:0 0 8px #FFE082;animation:kSparkleTwinkle 1.5s infinite ease-in-out;z-index:2;top:16px;left:22px;"></span>
<span style="position:absolute;width:5px;height:5px;background:#FFE066;border-radius:50%;box-shadow:0 0 7px #FFE082;animation:kSparkleTwinkle 1.5s infinite ease-in-out;animation-delay:.5s;z-index:2;top:58px;left:16px;"></span>
<span style="position:absolute;width:4px;height:4px;background:#FFE066;border-radius:50%;box-shadow:0 0 6px #FFE082;animation:kSparkleTwinkle 1.5s infinite ease-in-out;animation-delay:.9s;z-index:2;top:100px;left:30px;"></span>
<span style="position:absolute;width:6px;height:6px;background:#FFE066;border-radius:50%;box-shadow:0 0 8px #FFE082;animation:kSparkleTwinkle 1.5s infinite ease-in-out;animation-delay:.3s;z-index:2;top:60px;right:24px;"></span>
<span style="position:absolute;width:4px;height:4px;background:#FFE066;border-radius:50%;box-shadow:0 0 6px #FFE082;animation:kSparkleTwinkle 1.5s infinite ease-in-out;animation-delay:.7s;z-index:2;top:104px;right:30px;"></span>
<img src="assets/shared/logos/sb-brand.png" alt="傻瓜比价" style="width: 52px;height: 52px;border-radius: 12px;display: block;margin: 0 auto 16px;box-shadow: 0 4px 14px rgba(0,0,0,.25);">
<div style="font-size: 18px;font-weight: 900;color: #fff;line-height: 1.4;margin-bottom: 8px;text-shadow: 0 1px 2px rgba(120,10,5,.3);">您仍未领取今日大额优惠券</div>
<div style="display: inline-flex;align-items: center;gap: 6px;background: #fff;color: #ED3F36;font-size: 12px;font-weight: 800;padding: 4px 12px;border-radius: 32px;margin-bottom: 12px;box-shadow: 0 2px 8px rgba(120,10,5,.25);">
<svg width="14" height="12" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M2 7.2 V10.8 A1.4 1.4 0 0 0 3.4 12.2 H5.4 L11.2 16.4 A0.8 0.8 0 0 0 12.5 15.7 V2.3 A0.8 0.8 0 0 0 11.2 1.6 L5.4 5.8 H3.4 A1.4 1.4 0 0 0 2 7.2 Z" fill="#ED3F36"/><path d="M14.6 4.4 C16 6 16 12 14.6 13.6" stroke="#ED3F36" stroke-width="1.4" stroke-linecap="round" fill="none"/><path d="M16.8 2.6 C19.2 5.2 19.2 12.8 16.8 15.4" stroke="#ED3F36" stroke-width="1.4" stroke-linecap="round" fill="none"/><circle cx="6.2" cy="14.4" r="1.5" fill="#ED3F36"/></svg>
先领券,再比价
</div>
<div style="background: #fff;border-radius: 18px;padding: 12px 8px 12px;margin-bottom: 8px;box-shadow: 0 4px 12px rgba(120,10,5,.2);">
<div id="couponPlatformGrid" style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;"></div>
</div>
<div style="font-size: 11px;color: #FFE2D8;font-weight: 500;letter-spacing: .1px;line-height: 1.2;margin: 0 4px 12px 0;text-align: right;">将在以上平台领取</div>
<div style="position: relative;">
<button class="btn btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;background: linear-gradient(180deg,#FFE27A 0%,#FFC400 100%);color: #1a1a1a;font-weight: 900;box-shadow: inset 0 1px 0 rgba(255,255,255,.75), 0 8px 20px rgba(0,0,0,.28);border: none;" onclick="onClaimCouponConfirm()">一键自动领取</button>
</div>
</div>
</div>
</div>
<!-- F-2 改版:比价平台选择 · 中央模态(device 级 overlay -->
<div class="home-picker-mask" id="homePickerMask">
<div class="home-picker-modal">
<div class="home-picker-close" onclick="closeHomePicker()"></div>
<div class="home-picker-emoji">🍱</div>
<div class="home-picker-title">请选择您常用的平台</div>
<div class="home-picker-sub">进入平台选好商品后,<strong>AI 一键替你比多家</strong></div>
<div class="home-picker-grid" id="homePickerGrid"><!-- 内容由 renderHomePickerPlatforms() 动态渲染 --></div>
</div>
</div>
<!-- ===== 签到弹窗 ===== -->
<style>
@keyframes checkinPopIn { 0%{transform: translateY(18px) scale(.96);opacity: 0;} 100%{transform: translateY(0) scale(1);opacity: 1;} }
.checkin-popup-layer {
display: none;
position: absolute;
inset: 0;
z-index: 200;
background: rgba(0,0,0,.55);
backdrop-filter: blur(1px);
align-items: center;
justify-content: center;
padding: 0 16px;
overflow: hidden;
}
.checkin-popup-card {
width: 100%;
border-radius: 18px;
padding: 16px 12px 12px;
background: linear-gradient(180deg, #FFE0B2 0%, #FFFDE7 30%, #FFFFFF 72%);
box-shadow: 0 20px 40px rgba(0,0,0,.12);
position: relative;
overflow: visible;
animation: checkinPopIn .26s cubic-bezier(.22,1,.36,1);
}
.checkin-close {
position: absolute;
left: 50%;
bottom: -52px;
width: 34px;
height: 34px;
transform: translateX(-50%);
border: none;
border-radius: 50%;
background: rgba(255,255,255,.58);
color: rgba(0,0,0,.46);
box-shadow: 0 6px 18px rgba(0,0,0,.08);
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
line-height: 1;
cursor: pointer;
z-index: 4;
}
.checkin-banner {
position: relative;
z-index: 2;
width: 206px;
height: 16px;
margin: -4px auto 8px;
border-radius: 999px;
background: rgba(255,255,255,.92);
box-shadow: 0 4px 14px rgba(255,174,0,.14);
border: 1px solid rgba(255,184,0,.24);
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
color: #7A4F00;
font-size: 10px;
font-weight: 600;
white-space: nowrap;
}
.checkin-banner .star {
width: 9px;
height: 9px;
background: #FFB300;
clip-path: polygon(50% 0%, 62% 37%, 100% 50%, 62% 63%, 50% 100%, 38% 63%, 0% 50%, 38% 37%);
flex-shrink: 0;
}
.checkin-title {
position: relative;
z-index: 2;
margin: 0;
text-align: center;
color: #1A1A1A;
font-size: 20px;
line-height: 1.1;
font-weight: 900;
letter-spacing: 0;
}
.checkin-sub {
position: relative;
z-index: 2;
margin-top: 8px;
text-align: center;
color: #666;
font-size: 11px;
line-height: 1.2;
font-weight: 500;
white-space: nowrap;
}
.checkin-sub b {
color: #FF6A00;
font-weight: 600;
}
.checkin-rewards {
position: relative;
z-index: 2;
margin-top: 12px;
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 6px;
}
.checkin-reward-card {
position: relative;
min-height: 64px;
border-radius: 8px;
background: rgba(255,255,255,.88);
border: 1px solid #F0F0F0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 6px;
padding: 8px 2px 6px;
color: #1A1A1A;
font-weight: 600;
box-shadow: inset 0 1px 0 rgba(255,255,255,.75);
}
.checkin-reward-card.claimed {
background: rgba(247,247,247,.96);
border-color: #E5E5E5;
box-shadow: none;
color: #999999;
}
.checkin-reward-card.current {
border-color: #FFB300;
box-shadow: 0 8px 15px rgba(255,184,0,.14);
background: rgba(255,255,255,.96);
}
.checkin-reward-icon {
min-height: 26px;
display: flex;
align-items: center;
justify-content: center;
color: #999999;
font-size: 10px;
line-height: 1;
font-weight: 850;
white-space: nowrap;
}
.checkin-reward-card img {
width: 29px;
height: 29px;
object-fit: contain;
display: block;
}
.checkin-reward-card .icon-chest {
width: 36px;
height: 33px;
}
.checkin-reward-amount {
color: #1A1A1A;
font-size: 12px;
line-height: 1;
font-weight: 850;
}
.checkin-reward-card.claimed .checkin-reward-amount {
color: #999999;
}
.checkin-progress {
margin: 12px 0 0;
position: relative;
height: 42px;
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 6px;
align-items: start;
}
.checkin-progress-lines {
position: absolute;
top: 4.5px;
left: 0;
right: 0;
height: 3px;
pointer-events: none;
z-index: 0;
}
.checkin-segment {
position: absolute;
top: 0;
height: 3px;
border-radius: 32px;
}
.checkin-segment.solid {
background: var(--line-color);
}
.checkin-segment.dashed {
background-repeat: no-repeat;
}
.checkin-node {
position: relative;
z-index: 1;
display: flex;
flex-direction: column;
align-items: center;
gap: 8px;
color: #666;
font-size: 10px;
line-height: 1;
white-space: nowrap;
}
.checkin-dot {
box-sizing: border-box;
width: 12px;
height: 12px;
border-radius: 50%;
background: #FFFFFF;
border: 2px solid #DDDDDD;
}
.checkin-node.done .checkin-dot {
background: #FFB300;
border-color: #FFB300;
}
.checkin-node.current .checkin-dot {
background: #FFFFFF;
border: 2.5px solid #FFB300;
}
.checkin-popup-cta {
position: relative;
z-index: 2;
margin: 8px auto 0;
width: 252px;
height: 40px;
border: none;
border-radius: 24px;
background: linear-gradient(180deg, #FFE066 0%, #FFB300 100%);
color: #1A1A1A;
font-size: 17px;
font-weight: 900;
box-shadow: 0 14px 28px rgba(255,184,0,.22);
display: flex;
align-items: center;
justify-content: center;
gap: 4px;
cursor: pointer;
font-family: inherit;
}
.checkin-popup-cta img {
width: 18px;
height: 18px;
object-fit: contain;
display: block;
}
.checkin-popup-cta b {
color: #FF6A00;
font-size: 22px;
line-height: 1;
}
</style>
<div id="checkinPopup" class="checkin-popup-layer">
<div class="checkin-popup-card" onclick="event.stopPropagation()">
<button class="checkin-close" onclick="closeCheckinPopup()" aria-label="关闭">×</button>
<div class="checkin-banner" id="checkinBanner"><span class="star"></span>连续签到14天 最高可领2888金币</div>
<h2 class="checkin-title" id="checkinPopupTitle">签到领取今日金币</h2>
<div class="checkin-sub" id="checkinPopupSub">已连续签到 <b>2</b> 天,今天可领 <b>220</b> 金币</div>
<div class="checkin-rewards" id="checkinRewards" aria-label="连续签到奖励">
<div class="checkin-reward-card claimed">
<div class="checkin-reward-icon">已领取</div>
<div class="checkin-reward-amount">180</div>
</div>
<div class="checkin-reward-card claimed">
<div class="checkin-reward-icon">已领取</div>
<div class="checkin-reward-amount">380</div>
</div>
<div class="checkin-reward-card current">
<div class="checkin-reward-icon"><img src="assets/welfare/index/checkin-coin-single.png" alt=""></div>
<div class="checkin-reward-amount">220</div>
</div>
<div class="checkin-reward-card">
<div class="checkin-reward-icon"><img src="assets/welfare/index/checkin-coin-single.png" alt=""></div>
<div class="checkin-reward-amount">260</div>
</div>
<div class="checkin-reward-card">
<div class="checkin-reward-icon"><img src="assets/welfare/index/checkin-coins-600.png" alt=""></div>
<div class="checkin-reward-amount">888</div>
</div>
</div>
<div class="checkin-progress" id="checkinProgress" aria-label="签到进度">
<div class="checkin-progress-lines" id="checkinProgressLines"></div>
<div class="checkin-node done" data-day="1"><span class="checkin-dot"></span><span>第1天</span></div>
<div class="checkin-node done" data-day="2"><span class="checkin-dot"></span><span>第2天</span></div>
<div class="checkin-node current" data-day="3"><span class="checkin-dot"></span><span>今天</span></div>
<div class="checkin-node" data-day="4"><span class="checkin-dot"></span><span>明天</span></div>
<div class="checkin-node" data-day="7"><span class="checkin-dot"></span><span>第7天</span></div>
</div>
<button id="checkinBtn" class="checkin-popup-cta" onclick="doCheckin()">签到领 <img src="assets/welfare/index/checkin-coin-single.png" alt=""><b>220</b></button>
<button id="checkinReminderBtn" class="checkin-popup-cta" onclick="doCheckinReminder()" style="display: none;">打开签到提醒</button>
</div>
</div>
<!-- ===== 签到提醒权限弹窗 ===== -->
<div id="calendarPermPopup" style="display: none;position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 250;background: rgba(0,0,0,.55);align-items: center;justify-content: center;padding: 40px 24px;overflow: hidden;">
<div style="background: #fff;border-radius: 18px;max-width: 327px;width: 100%;padding: 24px 24px;position: relative;">
<div onclick="closeCalendarPerm()" style="position: absolute;top: 14px;right: 14px;width: 28px;height: 28px;border-radius: 14px;background: var(--gray-100);display: flex;align-items: center;justify-content: center;cursor: pointer;font-size: 14px;color: var(--gray-500);"></div>
<div style="text-align: center;">
<div style="font-size: 20px;font-weight: 700;color: var(--gray-900);margin-bottom: 8px;">日历权限未开启</div>
<div style="font-size: 14px;color: var(--gray-500);line-height: 1.6;">开启提醒失败,请前往设置-傻瓜比价打开日历权限后重试</div>
</div>
<button onclick="closeCalendarPerm()" style="width: 100%;margin-top: 16px;background: linear-gradient(180deg,#FFE066 0%,#FFC400 100%);color: #1A1A1A;border: none;padding: 12px;border-radius: 24px;font-size: 17px;font-weight: 700;cursor: pointer;box-shadow: inset 0 1px 0 rgba(255,255,255,.7), 0 2px 6px rgba(255,179,0,.35);">去开启</button>
</div>
</div>
<!-- 页面导航提示 -->
<!-- ===== 全部权限开启完成 · 祝贺弹窗(A/B/C 内容动态注入) ===== -->
<div id="celebrationModal" onclick="closeCelebrationModal()" style="display: none;position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 260;background: rgba(0,0,0,.55);align-items: center;justify-content: center;padding: 0 24px;overflow: hidden;">
<div onclick="event.stopPropagation()" id="celebrationModalCard" style="background: #fff;border-radius: 24px;width: 100%;max-width: 327px;padding: 24px 24px 24px;position: relative;animation: safetyPopIn .3s cubic-bezier(.22,1,.36,1);"></div>
</div>
<!-- ===== 权限未开启提示弹窗(居中卡片式,复用于无障碍/悬浮窗) ===== -->
<div id="safetyModal" onclick="closeSafetyModal()" style="display: none;position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 250;background: rgba(0,0,0,.55);align-items: center;justify-content: center;padding: 0 24px;overflow: hidden;">
<div onclick="event.stopPropagation()" style="background: #fff;border-radius: 24px;width: 100%;max-width: 327px;padding: 24px 24px 24px;position: relative;animation: safetyPopIn .3s cubic-bezier(.22,1,.36,1);">
<!-- 关闭按钮 -->
<button onclick="closeSafetyModal()" aria-label="关闭" style="position: absolute;top: 14px;right: 14px;width: 28px;height: 28px;border-radius: 14px;background: #f0f0f0;border: none;cursor: pointer;font-size: 16px;line-height: 1;color: #666;display: flex;align-items: center;justify-content: center;padding: 0;z-index: 2;">×</button>
<!-- 警示图标 -->
<div style="width: 60px;height: 60px;border-radius: 32px;background: #FFF3E0;display: flex;align-items: center;justify-content: center;margin: 0 auto 12px;font-size: 28px;">⚠️</div>
<!-- 标题 -->
<div id="safetyModalTitle" style="font-size: 20px;font-weight: 800;color: var(--gray-900);text-align: center;margin-bottom: 8px;">比价功能未开启</div>
<div style="font-size: 13px; color: rgb(0, 0, 0); line-height: 20px; text-align: center; margin-bottom: 16px; white-space: nowrap;">
<span id="safetyModalBody">在「已安装的应用」中找到「傻瓜比价」并开启</span><span id="safetyModalPermName" style="display: none;">无障碍</span>
</div>
<!-- 安全承诺区(压缩版) -->
<div id="safetyPromiseBlock" style="background: var(--gray-100);border-radius: 14px;padding: 12px 12px 12px;margin-bottom: 16px;">
<div style="font-size: 14px;font-weight: 700;color: var(--gray-700);margin-bottom: 8px;">我们的安全承诺</div>
<ul style="list-style: disc;padding-left: 16px;margin: 0;font-size: 12px;color: var(--gray-500);line-height: 19px;">
<li style="margin-bottom: 6px;">仅在您开启比价时启用,用完即停</li>
<li style="margin-bottom: 6px;">全程端侧加密,全方位守护您的隐私</li>
<li>您随时可在设置中关闭</li>
</ul>
</div>
<!-- 底部按钮 -->
<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;" onclick="goToPermFromSafetyModal()">去开启</button>
</div>
</div>
<!-- ===== 系统询问弹窗:位置权限(Android 系统弹窗风格,对齐 onboarding-v2 permLocationPopup ===== -->
<div id="locSysPopup" onclick="closeLocSysPopup('dismiss')" style="display: none;position: absolute;inset: 0px;z-index: 280;background: rgba(0,0,0,.45);align-items: flex-end;justify-content: center;padding: 40px 12px 16px;overflow: hidden;">
<div onclick="event.stopPropagation()" style="background: #fff;border-radius: 24px;max-width: 460px;width: 100%;padding: 24px 24px 8px;text-align: left;">
<div style="font-size: 16px;font-weight: 600;color: #1A1A1A;margin-bottom: 12px;">允许"傻瓜比价"获取此设备的位置信息?</div>
<div style="font-size: 14px;color: #666666;line-height: 20px;margin-bottom: 16px;">用于获取附近美食优惠和配送范围,为你推荐更精准的比价结果。</div>
<div style="display: flex;flex-direction: column;gap: 0;padding-top: 4px;border-top: 1px solid #F5F5F5;">
<button onclick="closeLocSysPopup('whileUsing')" style="background: none;border: none;border-bottom: 1px solid #F5F5F5;padding: 12px 0;font-size: 14px;font-weight: 600;color: #1a73e8;cursor: pointer;text-align: left;">仅使用期间允许</button>
<button onclick="closeLocSysPopup('once')" style="background: none;border: none;border-bottom: 1px solid #F5F5F5;padding: 12px 0;font-size: 14px;font-weight: 600;color: #1a73e8;cursor: pointer;text-align: left;">允许本次使用</button>
<button onclick="closeLocSysPopup('denied')" style="background: none;border: none;padding: 12px 0;font-size: 14px;font-weight: 600;color: #1a73e8;cursor: pointer;text-align: left;">禁止</button>
</div>
</div>
</div>
<!-- ===== 反馈底部半屏面板 ===== -->
<style>
@keyframes sheetUp { from { transform: translateY(100%); } to { transform: translateY(0); } }
.feedback-sheet { animation: sheetUp .3s ease-out; }
.fb-platform.selected { border-color: #FF5722 !important; background: #FFF3E0; color: #FF5722; }
</style>
<div id="feedbackSheet" style="display: none;position: absolute;top: 0;left: 0;right: 0;bottom: 0;z-index: 260;overflow: hidden;">
<!-- 遮罩 -->
<div onclick="closeFeedbackSheet()" style="position: absolute;inset: 0;background: rgba(0,0,0,.4);"></div>
<!-- 面板 -->
<div class="feedback-sheet" style="position: absolute;bottom: 0;left: 0;right: 0;background: #fff;border-radius: 18px 18px 0 0;max-height: 75%;overflow-y: auto;">
<!-- 拖动条 -->
<div style="padding: 12px 0 0;text-align: center;"><div style="width: 40px;height: 4px;border-radius: 4px;background: var(--gray-300);margin: 0 auto;"></div></div>
<!-- 标题 -->
<div style="padding: 16px 16px 12px;">
<div style="font-size: 18px;font-weight: 700;">比价结果反馈</div>
<div style="font-size: 13px;color: var(--gray-500);margin-top: 4px;">审核通过可获得 <span style="color: #FF5722;font-weight: 600;">2,000 金币</span> 奖励</div>
</div>
<!-- 步骤1:选择反馈类型 -->
<div id="fbStep1" style="padding: 0 16px 16px;">
<div style="font-size: 14px;font-weight: 600;color: var(--gray-700);margin-bottom: 12px;">选择反馈类型</div>
<div onclick="selectFbType('lower_price')" style="display: flex;align-items: center;gap: 12px;padding: 16px;border: 1.5px solid var(--gray-200);border-radius: 14px;cursor: pointer;margin-bottom: 8px;transition: all .15s;" id="fbTypeLower">
<div style="width: 44px;height: 44px;border-radius: 12px;background: linear-gradient(135deg,#FFF3E0,#FFE0B2);display: flex;align-items: center;justify-content: center;font-size: 22px;flex-shrink: 0;">💰</div>
<div><div style="font-size: 15px;font-weight: 600;">我发现了更低价格</div><div style="font-size: 12px;color: var(--gray-500);margin-top: 2px;">在其他平台找到了同款更便宜的</div></div>
<span style="margin-left: auto;font-size: 18px;color: var(--gray-400);"></span>
</div>
<div onclick="selectFbType('wrong_match')" style="display: flex;align-items: center;gap: 12px;padding: 16px;border: 1.5px solid var(--gray-200);border-radius: 14px;cursor: pointer;transition: all .15s;" id="fbTypeWrong">
<div style="width: 44px;height: 44px;border-radius: 12px;background: linear-gradient(135deg,#E3F2FD,#E3F2FD);display: flex;align-items: center;justify-content: center;font-size: 22px;flex-shrink: 0;"></div>
<div><div style="font-size: 15px;font-weight: 600;">外卖不是同一家</div><div style="font-size: 12px;color: var(--gray-500);margin-top: 2px;">比价结果中有外卖匹配错误</div></div>
<span style="margin-left: auto;font-size: 18px;color: var(--gray-400);"></span>
</div>
</div>
<!-- 步骤2A:更低价格表单 -->
<div id="fbFormLower" style="display: none;padding: 0 16px 24px;">
<div style="display: flex;align-items: center;gap: 8px;margin-bottom: 16px;">
<button class="btn-back" onclick="backToFbStep1()" aria-label="返回上一步"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 6L9 12L15 18" stroke="#1A1A1A" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
<div style="font-size: 15px;font-weight: 600;">我发现了更低价格</div>
</div>
<div style="font-size: 14px;font-weight: 600;color: var(--gray-700);margin-bottom: 8px;">粘贴外卖链接</div>
<input type="text" placeholder="粘贴更低价外卖的链接" style="width: 100%;border: 1.5px solid var(--gray-200);border-radius: 12px;padding: 12px;font-size: 14px;outline: none;margin-bottom: 16px;">
<div style="font-size: 14px;font-weight: 600;color: var(--gray-700);margin-bottom: 8px;">上传截图 <span style="font-weight: 400;color: var(--gray-400);">(含价格信息)</span></div>
<div style="display: flex;gap: 8px;margin-bottom: 16px;">
<div style="width: 80px;height: 80px;border: 1.5px dashed var(--gray-300);border-radius: 12px;display: flex;align-items: center;justify-content: center;font-size: 24px;color: var(--gray-400);cursor: pointer;">+</div>
<div style="width: 80px;height: 80px;border: 1.5px dashed var(--gray-300);border-radius: 12px;display: flex;align-items: center;justify-content: center;font-size: 24px;color: var(--gray-400);cursor: pointer;">+</div>
</div>
<button onclick="submitFeedback()" style="width: 100%;background: var(--primary);border: none;padding: 12px;border-radius: 14px;font-size: 16px;font-weight: 700;color: #1a1a1a;cursor: pointer;">提交反馈</button>
</div>
<!-- 步骤2B:非同款表单 -->
<div id="fbFormWrong" style="display: none;padding: 0 16px 24px;">
<div style="display: flex;align-items: center;gap: 8px;margin-bottom: 16px;">
<button class="btn-back" onclick="backToFbStep1()" aria-label="返回上一步"><svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M15 6L9 12L15 18" stroke="#1A1A1A" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/></svg></button>
<div style="font-size: 15px;font-weight: 600;">外卖不是同一家</div>
</div>
<div style="font-size: 14px;font-weight: 600;color: var(--gray-700);margin-bottom: 8px;">哪个平台的结果有误?</div>
<div id="fbPlatformList" style="display: flex;flex-wrap: wrap;gap: 8px;margin-bottom: 16px;"></div>
<div style="font-size: 14px;font-weight: 600;color: var(--gray-700);margin-bottom: 8px;">上传截图或描述问题</div>
<div style="display: flex;gap: 8px;margin-bottom: 12px;">
<div style="width: 80px;height: 80px;border: 1.5px dashed var(--gray-300);border-radius: 12px;display: flex;align-items: center;justify-content: center;font-size: 24px;color: var(--gray-400);cursor: pointer;">+</div>
</div>
<textarea placeholder="描述哪里不同(可选)" style="width: 100%;border: 1.5px solid var(--gray-200);border-radius: 12px;padding: 12px;font-size: 14px;outline: none;height: 60px;resize: none;font-family: inherit;margin-bottom: 16px;"></textarea>
<button onclick="submitFeedback()" style="width: 100%;background: var(--primary);border: none;padding: 12px;border-radius: 14px;font-size: 16px;font-weight: 700;color: #1a1a1a;cursor: pointer;">提交反馈</button>
</div>
<!-- 提交成功 -->
<div id="fbSuccess" style="display: none;padding: 24px 16px 32px;text-align: center;">
<div style="font-size: 48px;margin-bottom: 12px;"></div>
<div style="font-size: 18px;font-weight: 700;margin-bottom: 4px;">反馈已提交</div>
<div style="font-size: 14px;color: var(--gray-500);line-height: 22px;">系统正在验证,审核通过后<br><span style="color: #FF5722;font-weight: 600;">2,000 金币</span> 将自动到账</div>
<div style="font-size: 12px;color: var(--gray-400);margin-top: 12px;">审核结果将通过推送通知告知您</div>
<button onclick="closeFeedbackSheet()" style="margin-top: 16px;width: 100%;background: var(--primary);border: none;padding: 12px;border-radius: 14px;font-size: 16px;font-weight: 700;color: #1a1a1a;cursor: pointer;">知道了</button>
</div>
</div>
</div>
<div class="nav-hint" id="navHint" style="display: none;"></div>
<!-- 结果页预览浮层 -->
<div id="resultPreviewOverlay" onclick="hideResultPreview()" style="display: none;position: absolute;inset: 0;z-index: 300;background: #fff;cursor: pointer;">
<img id="resultPreviewImg" src="" style="width: 100%;height: 100%;object-fit: cover;object-position: top;">
</div>
<!-- ===== 新手礼包弹窗 ===== -->
<div class="gift-overlay" id="welcomeGiftPopup">
<div class="gift-card" id="welcomeGiftCard">
<button class="gift-close" onclick="closeWelcomeGift(true)"></button>
<div class="gift-stage-a">
<div class="gift-confetti">🎉🎊✨🎊🎉</div>
<div class="gift-title">新手大礼包</div>
<div class="gift-subtitle">专享新客福利,开启领取</div>
</div>
<div class="gift-chest-wrap">
<div class="gift-chest-glow"></div>
<div class="gift-burst">
<div class="gift-burst-ray"></div>
<div class="gift-burst-ray"></div>
<div class="gift-burst-ray"></div>
<div class="gift-burst-ray"></div>
<div class="gift-burst-ray"></div>
<div class="gift-burst-ray"></div>
</div>
<div class="gift-chest-emoji" id="giftChestEmoji">🎁</div>
</div>
<div class="gift-amount" id="giftAmount">+5,000<span class="gift-amount-suffix">金币</span></div>
<div class="gift-amount-hint">已存入「福利」,随时可提现</div>
<button class="gift-btn" id="giftMainBtn" onclick="openGiftBox()">✨ 开启礼包</button>
</div>
</div>
<!-- 福利 tab 上方 +5000 飘字(device 级,z-index 高于 overlay -->
<div class="tab-coin-badge" id="welfareTabBadge" style="z-index: 260;">+5,000 金币</div>
</div>
<script>
function showResultPreview(imgSrc) {
if (!imgSrc) return;
document.getElementById('resultPreviewImg').src = imgSrc;
document.getElementById('resultPreviewOverlay').style.display = 'block';
}
function hideResultPreview() {
document.getElementById('resultPreviewOverlay').style.display = 'none';
}
// 结果数据
// [WAIMAI-ONLY] 外卖比价结果数据 — 真实窑鸡王订单
const results = [
{
status: 'found',
platform: '淘宝',
platformIcon: 'assets/shared/logos/taobao.png',
shop: '窑鸡王(康营店)',
delivery: '商家自配',
lowest: true,
items: [
{ name: '招牌窑鸡★★ 整只-原味', spec: '原味 × 1', price: 69.9, original: 99.9 }
],
fees: [
{ label: '打包费', amount: 5 },
{ label: '配送费', amount: 5, original: 12, note: '惊喜减7元' }
],
discounts: [
{ label: '店铺活动/券', amount: 1 },
{ label: '平台红包(超级吃货卡)', amount: 21 }
],
totalPrice: 57.9,
totalSaved: 59,
bonuses: [{ label: '下单返豆', detail: '返110吃货豆' }],
resultImage: 'assets/onboarding/index/platform-flows/taobao/result.jpg'
},
{
status: 'found',
platform: '美团',
platformIcon: 'assets/shared/logos/meituan.png',
shop: '窑鸡王(康营店)',
delivery: '商家自配',
lowest: false,
items: [
{ name: '招牌窑鸡(整只)', spec: '1只, 招牌整只原味 × 1', price: 69.9 }
],
fees: [
{ label: '打包费', amount: 5 },
{ label: '配送费', amount: 5.8, original: 12, note: '活动减6.2元' }
],
discounts: [
{ label: '美团红包(神券包)', amount: 18 }
],
totalPrice: 62.7,
totalSaved: 24.2,
bonuses: [],
resultImage: 'assets/onboarding/index/platform-flows/meituan/result.jpg'
},
{
status: 'not-found',
platform: '京东',
platformIcon: 'assets/shared/logos/jd.png',
reason: '未找到店铺'
},
{
status: 'not-installed',
platform: '拼多多',
platformIcon: '', chipText: '拼', chipColor: '#E02E24'
}
];
// --- 同价 corner case 备用数据(取消注释可切换演示)---
// results[1].status = 'same-price';
// results[1].totalPrice = 57.9;
function renderFoundCard(r, idx, ctx) {
const isLowest = r.lowest;
const isUserOrig = ctx && ctx.userOrigId && r._id === ctx.userOrigId;
const borderColor = isLowest ? 'var(--primary)' : 'var(--gray-200)';
const cardBorder = isLowest ? '2px' : '1.5px';
const cardClass = isLowest ? 'card with-ribbon lowest' : 'card';
// 标识:最低价 → M2 斜挂红 ribbon(右上角);用户原选 → 描边方角 chip(店名旁 inline
const lowestRibbon = isLowest ? '<img class="ribbon-vi" src="assets/shared/badges/lowest-price.png" alt="全网最低">' : '';
const userOrigChip = isUserOrig ? '<span class="badge-userorig">原选择</span>' : '';
// 收集所有 AI 帮领的优惠(配送费减免 + 优惠券 + 店铺活动)
const aiDiscountItems = [];
r.fees.forEach(f => {
if (f.note) aiDiscountItems.push({ label: f.note, amount: f.original - f.amount });
});
r.discounts.forEach(d => {
aiDiscountItems.push({ label: d.label, amount: d.amount });
});
// AI 优惠标签
const aiTagsHtml = aiDiscountItems.map(d =>
`<span style="display: inline-flex;align-items: center;gap: 2px;background: #E8F5E9;border-radius: 4px;padding: 2px 8px;font-size: 11px;color: #2E7D32;white-space: nowrap;">${d.label} <b>-¥${d.amount}</b></span>`
).join('');
// N2 chip:仅最低价 + 有正向价差时显示
let chipHtml = '';
if (isLowest && ctx && ctx.savings > 0) {
chipHtml = `<span class="save-chip-red"><span class="small">比原选择省</span>¥${ctx.savings}</span>`;
}
// CTA:新手引导里弱化购买入口,所有卡片统一使用"查看"浅灰次级胶囊
const ctaHtml = `<button class="result-cta" onclick="event.stopPropagation();showResultPreview('${r.resultImage || ''}')">查看<span class="arrow"></span></button>`;
return `
<div class="${cardClass}" style="background: #fff;border-radius: 14px;padding: 12px 16px;border:${cardBorder} solid ${borderColor};position: relative;">
${lowestRibbon}
<!-- 头部:平台 logo + 店铺名 + 原选择 chip -->
<div style="display: flex;align-items: center;gap: 6px;${isLowest?'padding-right: 92px;':''}">
<img src="${r.platformIcon}" style="width: 28px;height: 28px;border-radius: 8px;flex-shrink: 0;" onerror="this.style.display='none'">
<span style="font-size: 14px;font-weight: 600;color: var(--gray-800);min-width: 0;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">${r.shop}</span>
${userOrigChip}
</div>
<!-- 傻瓜比价 AI 优惠 -->
<div style="margin-top: 8px;">
<div style="font-size: 12px;color: var(--gray-500);margin-bottom: 6px;">🤖 傻瓜比价已帮您领取以下优惠:</div>
<div style="display: flex;flex-wrap: wrap;gap: 4px;">
${aiTagsHtml}
</div>
</div>
<!-- 价格 + chip + CTA -->
<div style="display: flex;justify-content: space-between;align-items: center;margin-top: 12px;gap: 8px;">
<div style="display: flex;align-items: baseline;gap: 6px;flex-wrap: wrap;">
<span style="font-size: 13px;font-weight: 700;color: #FF5722;">¥</span>
<span style="font-size: 24px;font-weight: 800;color: #FF5722;line-height: 1;">${r.totalPrice}</span>
${chipHtml}
</div>
${ctaHtml}
</div>
</div>
`;
}
function renderNotFoundCard(r) {
// 从其他 found 结果中取店铺名
const shopName = (results.find(x => x.status === 'found') || {}).shop || '';
return `
<div style="background: #fff;border-radius: 14px;padding: 12px 16px;border: 1.5px solid var(--gray-200);position: relative;">
<!-- 头部:平台 -->
<div style="display: flex;align-items: center;gap: 8px;">
<img src="${r.platformIcon}" style="width: 28px;height: 28px;border-radius: 8px;" onerror="this.style.display='none'">
<span style="font-size: 14px;font-weight: 600;color: var(--gray-800);">${r.platform}</span>
</div>
<!-- 未找到提示 -->
<div style="margin-top: 12px;padding: 16px;background: var(--gray-50);border-radius: 8px;text-align: center;">
<div style="font-size: 13px;color: var(--gray-500);line-height: 20px;">该平台未找到「${shopName}」</div>
</div>
</div>
`;
}
/* 未安装平台卡(方案A 轻量问号):橙色双问号占位 + 未安装标 + 灰色"去安装"按钮 */
function renderNotInstalledCard(r) {
const logoHtml = r.platformIcon
? `<img src="${r.platformIcon}" style="width: 28px;height: 28px;border-radius: 8px;flex-shrink: 0;" onerror="this.style.display='none'">`
: `<span style="width: 28px;height: 28px;border-radius: 8px;flex-shrink: 0;display: inline-flex;align-items: center;justify-content: center;background: ${r.chipColor || 'var(--gray-500)'};color: #fff;font-size: 15px;font-weight: 800;">${r.chipText || ''}</span>`;
return `
<div style="background: #fff;border-radius: 14px;padding: 12px 16px;border: 1.5px solid var(--gray-200);position: relative;">
<div style="display: flex;align-items: center;gap: 6px;">
${logoHtml}
<span style="font-size: 14px;font-weight: 600;color: var(--gray-800);min-width: 0;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;">${r.platform}</span>
<span style="flex-shrink: 0;display: inline-flex;align-items: center;background: #FFF1EC;border: 1px solid rgba(255,87,34,.3);border-radius: 4px;padding: 2px 8px;font-size: 11px;font-weight: 600;color: #E0521F;white-space: nowrap;">未安装</span>
</div>
<div style="font-size: 12px;color: var(--gray-500);margin: 8px 0 6px;">🤖 就差这家没比,装上说不定更便宜</div>
<div style="display: flex;justify-content: space-between;align-items: center;margin-top: 12px;gap: 8px;">
<div style="display: flex;align-items: baseline;font-variant-numeric: tabular-nums;">
<span style="display: inline-flex;align-items: baseline;"><span style="font-size: 17px;font-weight: 800;color: #FF5722;line-height: 1;">¥</span><span style="font-size: 24px;font-weight: 800;color: #FF5722;line-height: 1;letter-spacing: -0.3px;">??</span></span>
</div>
<button class="result-cta">去安装<span class="arrow"></span></button>
</div>
</div>
`;
}
function renderResults(containerId) {
const c = document.getElementById(containerId);
// 给每条结果赋稳定 id,便于 ctx 引用
results.forEach((r, i) => { if (r._id == null) r._id = '_r' + i; });
// 计算"用户原选"= 找到的非最低里最贵那条;savings = userOrig 价 - lowest 价
const foundResults = results.filter(r => r.status === 'found' || r.status === 'same-price');
const lowestResult = results.find(r => r.lowest);
const otherFound = foundResults.filter(r => !r.lowest);
const userResult = otherFound.length ? otherFound.reduce((a, b) => a.totalPrice >= b.totalPrice ? a : b) : null;
const savings = (lowestResult && userResult && userResult.totalPrice > lowestResult.totalPrice)
? (userResult.totalPrice - lowestResult.totalPrice).toFixed(1)
: 0;
const ctx = {
userOrigId: userResult ? userResult._id : null,
savings: Number(savings),
};
// 排序:lowest → found 按价格升序 → same-price → not-found
const order = { 'found': 1, 'same-price': 2, 'not-installed': 3, 'not-found': 4 };
const sorted = [...results].sort((a, b) => {
if (a.lowest && !b.lowest) return -1;
if (!a.lowest && b.lowest) return 1;
const oa = order[a.status] || 9, ob = order[b.status] || 9;
if (oa !== ob) return oa - ob;
if (a.totalPrice && b.totalPrice) return a.totalPrice - b.totalPrice;
return 0;
});
c.innerHTML = sorted.map((r, i) => {
if (r.status === 'not-found') return renderNotFoundCard(r);
if (r.status === 'not-installed') return renderNotInstalledCard(r);
return renderFoundCard(r, i, ctx);
}).join('');
}
function renderCelebrationSavings() {
const card = document.getElementById('celebSavingsCard');
if (!card) return;
card.innerHTML = `
<img src="assets/onboarding/index/first-banner.png" alt="恭喜您完成首次比价" style="display: block;width: 100%;height: auto;">
`;
}
// 无障碍全屏引导流程
const accessibilityImages = ['assets/onboarding/index/guide-1.jpg', 'assets/onboarding/index/guide-2.jpg', 'assets/onboarding/index/guide-3.jpg', 'assets/onboarding/index/guide-4.jpg'];
let accessibilityIdx = 0;
function startAccessibilityFlow() {
accessibilityIdx = 0;
document.getElementById('accessFlowImg').src = accessibilityImages[0];
document.getElementById('accessStep4Overlay').style.display = 'none';
go('accessibility-flow');
}
function nextAccessibilityStep() {
accessibilityIdx++;
if (accessibilityIdx < accessibilityImages.length) {
document.getElementById('accessFlowImg').src = accessibilityImages[accessibilityIdx];
// 无障碍4(index=3)显示允许/拒绝覆盖层,禁用全屏点击
if (accessibilityIdx === 3) {
document.getElementById('accessStep4Overlay').style.display = 'block';
}
}
}
function onAllowClick(e) {
e.stopPropagation();
document.getElementById('accessStep4Overlay').style.display = 'none';
go('permission-step2');
}
function onRejectClick(e) {
e.stopPropagation();
document.getElementById('accessStep4Overlay').style.display = 'none';
go('permission-step1');
// 权限未开启 → 弹出提示弹窗
setTimeout(function() { openSafetyModal('无障碍'); }, 300);
}
function openSafetyModal(permName) {
document.getElementById('safetyModalPermName').textContent = permName;
document.getElementById('safetyModalTitle').textContent =
permName === '悬浮窗' ? '比价按钮未开启' : '比价功能未开启';
// 文案随权限上下文切换:无障碍 → 在「已安装的应用」中找;悬浮窗 → 直接在列表中找
var bodyEl = document.getElementById('safetyModalBody');
if (bodyEl) {
bodyEl.textContent = permName === '悬浮窗'
? '在列表中找到「傻瓜比价」并开启'
: '在「已安装的应用」中找到「傻瓜比价」并开启';
}
// 仅无障碍弹窗显示安全承诺,悬浮窗弹窗隐藏
var promise = document.getElementById('safetyPromiseBlock');
if (promise) promise.style.display = (permName === '悬浮窗') ? 'none' : 'block';
document.getElementById('safetyModal').style.display = 'flex';
}
function closeSafetyModal() {
document.getElementById('safetyModal').style.display = 'none';
}
// 「继续开启」按钮:关弹窗 + 直接进入对应权限设置页
function goToPermFromSafetyModal() {
const permName = document.getElementById('safetyModalPermName').textContent;
closeSafetyModal();
if (permName === '悬浮窗') {
startOverlayFlow();
} else {
startAccessibilityFlow();
}
}
// 悬浮窗引导流程
function startOverlayFlow() {
go('overlay-flow');
}
function onOverlayAllowClick(e) {
e.stopPropagation();
go('productselect');
openCelebrationModal();
}
// ===== 祝贺弹窗(多版本) =====
var celebrationVariants = {
// V1 「轻收尾·重期待」:承上小灰,起下大黑,行为按钮含次要引导
v1: '' +
'<div style="display: flex;align-items: center;justify-content: center;gap: 6px;font-size: 12px;color: var(--gray-500);margin-bottom: 8px;"><span style="display: inline-flex;align-items: center;justify-content: center;width: 16px;height: 16px;border-radius: 8px;background: #22C55E;color: #fff;font-size: 11px;font-weight: 700;">✓</span>全部设置已完成</div>' +
'<div style="font-size: 24px;font-weight: 800;color: var(--gray-900);text-align: center;line-height: 1.3;margin-bottom: 8px;">下一步,体验<br>傻瓜式一键比价</div>' +
'<div style="font-size: 14px;color: var(--gray-600);text-align: center;margin-bottom: 24px;">先选一个你常用的外卖平台</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;" onclick="closeCelebrationModal()">选个常用平台开始体验</button>',
// V2 「分割式」:顶部承上小条 + 中部起下标题 + 底部行为引导,节奏分明
v2: '' +
'<div style="text-align: center;margin-bottom: 12px;"><span style="display: inline-flex;align-items: center;gap: 6px;padding: 4px 12px;border-radius: 32px;background: #E8F5E9;color: #2E7D32;font-size: 12px;font-weight: 600;">✓ 已完成全部设置</span></div>' +
'<div style="font-size: 22px;font-weight: 800;color: var(--gray-900);text-align: center;line-height: 1.35;margin-bottom: 16px;">来体验傻瓜式<br>一键比价</div>' +
'<div style="height: 1px;background: var(--gray-200);margin-bottom: 16px;"></div>' +
'<div style="font-size: 13px;color: var(--gray-500);text-align: center;margin-bottom: 8px;">第一步</div>' +
'<div style="font-size: 15px;font-weight: 700;color: var(--gray-900);text-align: center;margin-bottom: 16px;">选择一个常用的外卖平台</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;" onclick="closeCelebrationModal()">选个常用平台开始体验</button>',
// V3 「叙事流」:递进式三行,承上→起下→行为,逐级加重(采用 V5 的绿圆勾徽章)
v3: '' +
'<div style="display: flex;align-items: center;gap: 8px;margin-bottom: 16px;">' +
'<span style="display: inline-flex;align-items: center;justify-content: center;width: 20px;height: 20px;border-radius: 50%;background: #22C55E;box-shadow: 0 0 0 3px rgba(34,197,94,.18);flex-shrink: 0;"><svg width="11" height="11" viewBox="0 0 10 10"><path d="M2.2 5.2l2 2 3.8-4.6" stroke="#fff" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span>' +
'<span style="font-size: 13px;color: var(--gray-700);font-weight: 600;">设置已完成</span>' +
'</div>' +
'<div style="background: linear-gradient(135deg,#FFFDE7,#FFF8E1);border-radius: 14px;padding: 16px 16px;margin-bottom: 16px;text-align: center;">' +
'<div style="font-size: 12px;color: #F9A825;font-weight: 600;margin-bottom: 6px;">下一步</div>' +
'<div style="font-size: 18px;font-weight: 800;color: var(--gray-900);">用傻瓜比价,找全网底价</div>' +
'</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;width: 100%;" onclick="closeCelebrationModal()">选外卖平台开始体验</button>',
// V4 「行为优先」:承上完全收为一行小提示,起下并入按钮上方副文,主舞台让给行为
v4: '' +
'<div style="font-size: 11px;color: var(--gray-500);letter-spacing: 1px;text-align: center;margin-bottom: 8px;text-transform: uppercase;">SETUP COMPLETE</div>' +
'<div style="font-size: 24px;font-weight: 800;color: var(--gray-900);text-align: center;line-height: 1.25;margin-bottom: 8px;">选一个常用<br>外卖平台</div>' +
'<div style="font-size: 13px;color: var(--gray-600);text-align: center;margin-bottom: 24px;">即可开启傻瓜式一键比价</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;" onclick="closeCelebrationModal()">现在选择</button>',
// V5 「徽章+行动卡」:左对齐绿圆勾徽章承上,黄色行动卡承载"下一步 + 体验傻瓜式一键比价"
v5: '' +
'<div style="display: flex;align-items: center;gap: 8px;margin-bottom: 16px;">' +
'<span style="display: inline-flex;align-items: center;justify-content: center;width: 20px;height: 20px;border-radius: 50%;background: #22C55E;box-shadow: 0 0 0 3px rgba(34,197,94,.18);flex-shrink: 0;"><svg width="11" height="11" viewBox="0 0 10 10"><path d="M2.2 5.2l2 2 3.8-4.6" stroke="#fff" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span>' +
'<span style="font-size: 13px;color: var(--gray-700);font-weight: 600;">设置已完成</span>' +
'</div>' +
'<div style="position: relative;background: linear-gradient(135deg,#FFFDE7,#FFF3C4);border-radius: 14px;padding: 24px 16px 16px;margin: 12px 0 16px;border: 1.5px solid #FFE082;text-align: center;">' +
'<div style="position: absolute;top: -12px;left: 50%;transform: translateX(-50%);font-size: 11px;color: #F57F17;font-weight: 800;background: #fff;padding: 4px 12px;border-radius: 32px;letter-spacing: .6px;border: 1.5px solid #FFE082;white-space: nowrap;">下一步</div>' +
'<div style="font-size: 20px;font-weight: 800;color: var(--gray-900);line-height: 1.3;">用傻瓜比价,找全网底价</div>' +
'</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;width: 100%;" onclick="closeCelebrationModal()">选外卖平台开始体验</button>',
// V6 「进度条」:完成段绿+待办段黄色脉动,强化推进感
v6: '' +
'<div style="display: flex;align-items: center;gap: 8px;margin-bottom: 12px;">' +
'<span style="display: inline-flex;align-items: center;justify-content: center;width: 20px;height: 20px;border-radius: 50%;background: #22C55E;box-shadow: 0 0 0 3px rgba(34,197,94,.18);flex-shrink: 0;"><svg width="11" height="11" viewBox="0 0 10 10"><path d="M2.2 5.2l2 2 3.8-4.6" stroke="#fff" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span>' +
'<span style="font-size: 13px;color: var(--gray-700);font-weight: 600;">设置已完成</span>' +
'</div>' +
'<div style="display: flex;align-items: center;gap: 6px;margin-bottom: 24px;">' +
'<div style="flex: 1;height: 6px;border-radius: 4px;background: #22C55E;"></div>' +
'<div style="flex: 1;height: 6px;border-radius: 4px;background: linear-gradient(90deg,#FFD600,#FFB300);box-shadow: 0 0 10px rgba(255,214,0,.55);animation: gvBob 1.6s ease-in-out infinite;"></div>' +
'</div>' +
'<div style="font-size: 13px;color: #F57F17;font-weight: 800;text-align: center;margin-bottom: 6px;letter-spacing: .5px;">最后一步</div>' +
'<div style="font-size: 22px;font-weight: 800;color: var(--gray-900);text-align: center;line-height: 1.3;margin-bottom: 24px;">选个常用平台</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;width: 100%;" onclick="closeCelebrationModal()">立即选择</button>',
// V7 「数字步骤」:① 已完成 → ② 当前 双步骤连线,明确流程位置
v7: '' +
'<div style="display: flex;align-items: center;justify-content: center;gap: 8px;margin-bottom: 24px;">' +
'<div style="display: flex;align-items: center;gap: 6px;opacity: .85;">' +
'<span style="display: inline-flex;align-items: center;justify-content: center;width: 20px;height: 20px;border-radius: 50%;background: #22C55E;flex-shrink: 0;"><svg width="11" height="11" viewBox="0 0 10 10"><path d="M2.2 5.2l2 2 3.8-4.6" stroke="#fff" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span>' +
'<span style="font-size: 12px;color: var(--gray-500);font-weight: 600;">设置完成</span>' +
'</div>' +
'<div style="width: 20px;height: 2px;background: var(--gray-200);border-radius: 4px;"></div>' +
'<div style="display: flex;align-items: center;gap: 6px;">' +
'<span style="display: inline-flex;align-items: center;justify-content: center;width: 20px;height: 20px;border-radius: 50%;background: var(--primary);color: #1a1a1a;font-size: 12px;font-weight: 800;flex-shrink: 0;box-shadow: 0 0 0 3px rgba(255,214,0,.25);">2</span>' +
'<span style="font-size: 12px;color: var(--gray-900);font-weight: 700;">选平台</span>' +
'</div>' +
'</div>' +
'<div style="font-size: 22px;font-weight: 800;color: var(--gray-900);text-align: center;line-height: 1.3;margin-bottom: 8px;">选个常用外卖平台</div>' +
'<div style="font-size: 13px;color: var(--gray-600);text-align: center;margin-bottom: 24px;">即可开启傻瓜式一键比价</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;display: flex;align-items: center;justify-content: center;gap: 6px;width: 100%;" onclick="closeCelebrationModal()">选择平台<span style="font-size: 18px;line-height: 1;">→</span></button>',
// V8 「极简专注」:单行小绿勾承上,舞台全部让给主标题
v8: '' +
'<div style="display: flex;align-items: center;gap: 8px;margin-bottom: 24px;">' +
'<span style="display: inline-flex;align-items: center;justify-content: center;width: 20px;height: 20px;border-radius: 50%;background: #22C55E;box-shadow: 0 0 0 3px rgba(34,197,94,.18);flex-shrink: 0;"><svg width="11" height="11" viewBox="0 0 10 10"><path d="M2.2 5.2l2 2 3.8-4.6" stroke="#fff" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span>' +
'<span style="font-size: 13px;color: var(--gray-600);font-weight: 500;">设置已完成</span>' +
'</div>' +
'<div style="font-size: 24px;font-weight: 800;color: var(--gray-900);text-align: center;line-height: 1.25;margin-bottom: 8px;letter-spacing: -.3px;">选个常用平台</div>' +
'<div style="font-size: 14px;color: var(--gray-600);text-align: center;margin-bottom: 24px;line-height: 1.5;">现在体验<span style="font-weight: 700;color: var(--gray-900);background: linear-gradient(to top,var(--primary) 40%,transparent 40%);padding: 0 2px;">傻瓜式一键比价</span></div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;width: 100%;" onclick="closeCelebrationModal()">开始体验</button>',
// V9 「iOS 极简·一图一字一按钮」:80px 大圆勾 + 大字标题 + 单按钮,零嵌套盒子
v9: '' +
'<div style="display: flex;justify-content: center;margin: 6px 0 16px;">' +
'<div style="width: 80px;height: 80px;border-radius: 50%;background: linear-gradient(135deg,#34D399,#10B981);display: flex;align-items: center;justify-content: center;box-shadow: 0 10px 28px rgba(16,185,129,.35), 0 0 0 8px rgba(16,185,129,.10);animation: gvPop .5s cubic-bezier(.34,1.56,.64,1) both;">' +
'<svg width="42" height="42" viewBox="0 0 42 42" fill="none">' +
'<path d="M11 21.5 L18 28.5 L31.5 14.5" stroke="#fff" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>' +
'</svg>' +
'</div>' +
'</div>' +
'<div style="font-size: 24px;font-weight: 800;color: var(--gray-900);text-align: center;line-height: 1.3;margin-bottom: 8px;letter-spacing: -.3px;">就差最后一步</div>' +
'<div style="font-size: 14px;color: var(--gray-600);text-align: center;line-height: 1.55;margin-bottom: 24px;">选个常用平台 · 立刻体验傻瓜式一键比价</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;font-weight: 700;" onclick="closeCelebrationModal()">选外卖平台开始体验</button>',
// V10 「Stripe / Linear 风」:左对齐、小绿勾 + 大字 + 黑底按钮,冷峻 SaaS 调性
v10: '' +
'<div style="display: flex;align-items: center;gap: 8px;margin-bottom: 24px;">' +
'<span style="display: inline-flex;align-items: center;justify-content: center;width: 18px;height: 18px;border-radius: 50%;background: #10B981;flex-shrink: 0;"><svg width="10" height="10" viewBox="0 0 10 10"><path d="M2.2 5.2l2 2 3.8-4.6" stroke="#fff" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span>' +
'<span style="font-size: 12px;color: var(--gray-500);font-weight: 500;letter-spacing: .3px;">Setup complete</span>' +
'</div>' +
'<div style="font-size: 28px;font-weight: 800;color: var(--gray-900);line-height: 1.2;margin-bottom: 8px;letter-spacing: -.6px;">选个常用<br>外卖平台</div>' +
'<div style="font-size: 14px;color: var(--gray-600);line-height: 1.55;margin-bottom: 24px;">即可开启傻瓜式一键比价 — AI 会自动跨平台比价、领券、下单。</div>' +
'<button style="background: #1A1A1A;color: #fff;border: none;border-radius: 14px;font-size: 15px;height: 50px;width: 100%;font-weight: 600;display: flex;align-items: center;justify-content: center;gap: 8px;cursor: pointer;" onclick="closeCelebrationModal()">' +
'<span>开始体验</span>' +
'<svg width="14" height="14" viewBox="0 0 14 14" fill="none"><path d="M3 7h8m0 0l-3.5-3.5M11 7l-3.5 3.5" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
'</button>',
// V11 「沉浸彩屏 hero」:负 margin 把顶部 bleed 成黄色渐变 hero,礼包大图标占主舞台,电商喜庆调性
v11: '' +
'<div style="margin: -28px -22px 16px;border-radius: 24px 24px 0 0;background: linear-gradient(180deg,#FFE066 0%,#FFF3C4 55%,#fff 100%);padding: 32px 24px 16px;text-align: center;position: relative;overflow: hidden;">' +
'<div style="position: absolute;top: -30px;left: -30px;width: 120px;height: 120px;border-radius: 50%;background: radial-gradient(circle,rgba(255,255,255,.6),transparent 70%);"></div>' +
'<div style="position: absolute;bottom: -40px;right: -30px;width: 140px;height: 140px;border-radius: 50%;background: radial-gradient(circle,rgba(255,179,0,.25),transparent 70%);"></div>' +
'<div style="width: 96px;height: 96px;margin: 0 auto 12px;position: relative;animation: gvPop .55s cubic-bezier(.34,1.56,.64,1) both;">' +
'<div style="position: absolute;inset: 0;border-radius: 50%;background: radial-gradient(circle,rgba(255,255,255,.85) 0%,rgba(255,255,255,0) 70%);"></div>' +
'<div style="position: absolute;inset: 6px;border-radius: 50%;background: linear-gradient(135deg,#FFD600 0%,#FF9800 100%);display: flex;align-items: center;justify-content: center;box-shadow: 0 14px 32px rgba(255,152,0,.45), inset 0 -6px 14px rgba(0,0,0,.08);">' +
'<span style="font-size: 46px;line-height: 1;filter: drop-shadow(0 2px 4px rgba(0,0,0,.15));">🎁</span>' +
'</div>' +
'</div>' +
'<div style="display: inline-block;font-size: 11px;font-weight: 800;color: #D84315;letter-spacing: 1.2px;background: #fff;padding: 4px 12px;border-radius: 32px;margin-bottom: 8px;box-shadow: 0 2px 6px rgba(216,67,21,.18);">SETUP COMPLETE</div>' +
'<div style="font-size: 24px;font-weight: 900;color: var(--gray-900);line-height: 1.25;letter-spacing: -.3px;">傻瓜比价 · 准备就绪</div>' +
'</div>' +
'<div style="font-size: 14px;color: var(--gray-600);text-align: center;line-height: 1.55;margin-bottom: 16px;">选个常用平台 · 立刻领券一键比价</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 52px;width: 100%;font-weight: 700;box-shadow: 0 8px 22px rgba(255,179,0,.42);display: flex;align-items: center;justify-content: center;gap: 6px;" onclick="closeCelebrationModal()">' +
'<span>选外卖平台开始体验</span><span style="font-size: 18px;line-height: 1;">→</span>' +
'</button>',
// V12 「步骤推进 2/2」:顶部细进度条 100% + 双步骤指示,强化"最后一步"流程感
v12: '' +
'<div style="display: flex;align-items: center;gap: 8px;margin-bottom: 8px;">' +
'<div style="font-size: 11px;font-weight: 700;color: var(--gray-500);letter-spacing: .4px;">2 / 2</div>' +
'<div style="flex: 1;height: 5px;border-radius: 4px;background: var(--gray-200);overflow: hidden;display: flex;">' +
'<div style="flex: 1;background: #10B981;border-radius: 4px 0 0 4px;"></div>' +
'<div style="flex: 1;background: linear-gradient(90deg,var(--primary),#FFB300);border-radius: 0 4px 4px 0;box-shadow: 0 0 8px rgba(255,179,0,.5);animation: gvBob 1.6s ease-in-out infinite;"></div>' +
'</div>' +
'</div>' +
'<div style="display: flex;align-items: center;gap: 6px;margin-bottom: 24px;font-size: 11px;">' +
'<span style="display: inline-flex;align-items: center;justify-content: center;width: 13px;height: 13px;border-radius: 50%;background: #10B981;flex-shrink: 0;"><svg width="8" height="8" viewBox="0 0 10 10"><path d="M2.2 5.2l2 2 3.8-4.6" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span>' +
'<span style="color: var(--gray-500);font-weight: 500;">设置完成</span>' +
'<span style="color: var(--gray-300);margin: 0 2px;"></span>' +
'<span style="color: var(--coin);font-weight: 800;letter-spacing: .2px;">现在选平台</span>' +
'</div>' +
'<div style="font-size: 24px;font-weight: 800;color: var(--gray-900);text-align: center;line-height: 1.25;margin-bottom: 8px;letter-spacing: -.3px;">就差最后一步</div>' +
'<div style="font-size: 14px;color: var(--gray-600);text-align: center;line-height: 1.55;margin-bottom: 24px;">选个常用平台开启<span style="font-weight: 700;color: var(--gray-900);">傻瓜式一键比价</span></div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;font-weight: 700;display: flex;align-items: center;justify-content: center;gap: 6px;" onclick="closeCelebrationModal()">' +
'<span>立即选择</span><span style="font-size: 18px;line-height: 1;">→</span>' +
'</button>'
};
var currentCelebrationVariant = 'v1';
var celebrationModalHTML = celebrationVariants[currentCelebrationVariant];
function openCelebrationModal() {
document.getElementById('celebrationModalCard').innerHTML = celebrationVariants[currentCelebrationVariant] || celebrationVariants.v1;
document.getElementById('celebrationModal').style.display = 'flex';
if (typeof fireConfetti === 'function') {
fireConfetti();
}
}
function closeCelebrationModal() {
document.getElementById('celebrationModal').style.display = 'none';
}
function onOverlayRejectClick(e) {
e.stopPropagation();
go('permission-step2');
// 权限未开启 → 弹出提示弹窗
setTimeout(function() { openSafetyModal('悬浮窗'); }, 300);
}
// 礼花彩纸特效 — 从顶部散落的彩色纸片
function fireConfetti() {
var container = document.getElementById('app');
var colors = ['#FFD600', '#FF5722', '#4CAF50', '#2196F3', '#E91E63', '#FF9800', '#9C27B0', '#00BCD4'];
for (var i = 0; i < 60; i++) {
var particle = document.createElement('div');
var color = colors[Math.floor(Math.random() * colors.length)];
var w = Math.random() * 6 + 4;
var h = Math.random() * 14 + 8;
var left = Math.random() * 100;
var dx = (Math.random() - 0.5) * 100;
var dy = 700 + Math.random() * 200;
var spin = (Math.random() - 0.5) * 1440;
var delay = Math.random() * 0.6;
var duration = Math.random() * 1.5 + 1.5;
particle.style.cssText = 'position:absolute;width:' + w + 'px;height:' + h + 'px;background:' + color + ';border-radius:1px;left:' + left + '%;top:-20px;z-index:9999;pointer-events:none;opacity:1;animation:confettiFloat ' + duration + 's ease-in ' + delay + 's forwards;--dx:' + dx + 'px;--dy:' + dy + 'px;--spin:' + spin + 'deg;';
container.appendChild(particle);
(function(p, t) { setTimeout(function() { p.remove(); }, t); })(particle, (duration + delay) * 1000 + 100);
}
}
// 权限检测 → 两个都开启才能进入下一步
function checkPermAndGo() {
var accDone = document.getElementById('accessibilityBtn').disabled;
var ovlDone = document.getElementById('overlayBtn').disabled;
if (accDone && ovlDone) {
go('productselect');
} else {
showToast('请先完成前置步骤再继续');
}
}
// Toast 提示
function showToast(msg) {
var existing = document.getElementById('globalToast');
if (existing) existing.remove();
var toast = document.createElement('div');
toast.id = 'globalToast';
toast.textContent = msg;
toast.style.cssText = 'position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);background:rgba(0,0,0,0.75);color:#fff;padding:10px 20px;border-radius:20px;font-size:14px;z-index:9999;pointer-events:none;opacity:1;transition:opacity 0.3s;white-space:nowrap;';
document.getElementById('app').appendChild(toast);
setTimeout(function() { toast.style.opacity = '0'; }, 1500);
setTimeout(function() { toast.remove(); }, 1800);
}
// 登录状态
let isLoggedIn = (typeof localStorage !== 'undefined' && localStorage.getItem('proto_isLoggedIn') !== 'false');
let loginFrom = (typeof localStorage !== 'undefined' && localStorage.getItem('proto_loginFrom')) || 'home';
let welfareVisited = (typeof localStorage !== 'undefined' && localStorage.getItem('proto_welfareVisited') === 'true');
let profileVisited = (typeof localStorage !== 'undefined' && localStorage.getItem('proto_profileVisited') === 'true');
// 新手礼包流程
let welcomeGiftShown = (typeof localStorage !== 'undefined' && localStorage.getItem('proto_welcomeGiftShown') === 'true');
let welcomeGiftClaimed = false;
let welfareCoinBalance = 28967;
let welfareCashBalance = 2.89;
let recordComparisonRewardClaimed = false;
let messageReminderRewardClaimed = false;
let videoRewardClaimed = false;
// 导航
// ===== 方案切换系统 =====
// 配置:在这里注册需要多方案对比的模块
// 格式:screenId: { current: 'a', options: { a: '方案名', b: '方案名' } }
// 方案A的screen id = 原id(如 productselect
// 方案B的screen id = 原id--b(如 productselect--b
// 方案C的screen id = 原id--c(如 productselect--c
const variants = {
'home-feed': {
current: 'cap',
inPlace: true,
options: {
base: '默认',
frame: '方案1 外框',
tabs: '方案2 Tab',
combo: '方案3 组合',
border: '边框1 描边',
accent: '边框2 黄线',
cap: '边框3 帽檐',
lift: '边框4 阴影'
},
onChange: applyHomeFeedVariant
},
'last-compare': {
current: 'reference',
inPlace: true,
options: {
reference: '默认左标',
tagright: '方案1 右上',
topbar: '方案2 顶栏',
pricebadge: '方案3 价格区',
mutedright: '方案4 弱化右侧'
},
onChange: applyLastCompareVariant
}
};
function applyHomeFeedVariant(variant) {
const key = variants['home-feed'].options[variant] ? variant : 'base';
document.body.classList.remove('feed-v-frame', 'feed-v-tabs', 'feed-v-combo', 'feed-v-border', 'feed-v-accent', 'feed-v-cap', 'feed-v-lift');
if (key !== 'base') document.body.classList.add('feed-v-' + key);
variants['home-feed'].current = key;
}
function applyLastCompareVariant(variant) {
const key = variants['last-compare'].options[variant] ? variant : 'reference';
const banner = document.getElementById('lastCompareBanner');
if (banner) {
banner.classList.remove('lcb-v-reference', 'lcb-v-tagright', 'lcb-v-topbar', 'lcb-v-pricebadge', 'lcb-v-mutedright');
banner.classList.add('lcb-v-reference');
if (key !== 'reference') banner.classList.add('lcb-v-' + key);
}
variants['last-compare'].current = key;
}
function resolveScreenId(id) {
if (variants[id] && !variants[id].inPlace) {
const v = variants[id].current;
return v === 'a' ? id : id + '--' + v;
}
return id;
}
function switchVariant(moduleId, variant) {
if (!variants[moduleId]) return;
variants[moduleId].current = variant;
renderVariantPanel();
if (typeof variants[moduleId].onChange === 'function') {
variants[moduleId].onChange(variant);
}
if (variants[moduleId].inPlace) return;
// 如果当前正在看这个模块的某个方案,立即切换
const allScreens = document.querySelectorAll('.screen');
let isViewing = false;
allScreens.forEach(s => {
if (s.classList.contains('active') && (s.id === moduleId || s.id.startsWith(moduleId + '--'))) {
isViewing = true;
}
});
if (isViewing) go(moduleId);
}
function toggleVariantPanel() {
const p = document.getElementById('variantPanel');
p.classList.toggle('open');
if (p.classList.contains('open')) renderVariantPanel();
}
function renderVariantPanel() {
const list = document.getElementById('variantList');
const keys = Object.keys(variants);
if (keys.length === 0) {
list.innerHTML = '<div class="variant-empty">暂无多方案模块<br><span style="font-size: 11px;">在 variants 配置中添加模块即可</span></div>';
return;
}
list.innerHTML = keys.map(id => {
const v = variants[id];
const opts = Object.entries(v.options).map(([key, label]) =>
`<div class="variant-opt ${v.current===key?'active':''}" onclick="switchVariant('${id}','${key}')">${label}</div>`
).join('');
return `<div class="variant-module"><div class="variant-module-label">${id}</div><div class="variant-options">${opts}</div></div>`;
}).join('');
}
// 页面加载时初始化面板
document.addEventListener('DOMContentLoaded', renderVariantPanel);
document.addEventListener('DOMContentLoaded', function () {
try {
const feedVariant = new URLSearchParams(location.search).get('feedvariant') || variants['home-feed'].current;
applyHomeFeedVariant(feedVariant);
const lastCompareVariant = new URLSearchParams(location.search).get('lastcompare') || variants['last-compare'].current;
applyLastCompareVariant(lastCompareVariant);
renderVariantPanel();
} catch (e) {}
});
// 调试:URL hash 直跳(如 #home / #welfare / #profile
document.addEventListener('DOMContentLoaded', function() {
var h = (location.hash || '').replace(/^#/, '');
if (h && document.getElementById(h)) {
setTimeout(function(){ try { go(h); } catch(e){} }, 80);
}
});
// ===== 用户案例翻页横幅 =====
var FLIP_AVATARS = ['🦊','🐱','🐶','🐻','🐼','🐨','🐯','🦁','🐮','🐷','🐸','🐵','🐔','🐧','🦆','🦅','🦉','🐺','🐗','🦄','🐝','🦋','🐞','🦜','🦩','🐢','🐙','🦀','🐠','🐬','🐳','🦈','🌸','🌻','🌺','🌷','🍎','🍑','🍓','🍉','🍊','🥭','🍇','🥝','🌶','🌮','🍔','🍕','🍜','🥟'];
var FLIP_BG = ['#FFE0B2','#FFCCBC','#FFAB91','#F8BBD0','#E1BEE7','#C5CAE9','#B3E5FC','#B2EBF2','#B2DFDB','#C8E6C9','#DCEDC8','#F0F4C3','#FFF59D','#FFE082','#FFCC80','#FFAB91','#BCAAA4','#CFD8DC','#FFCDD2','#D1C4E9'];
var FLIP_NAMES = [
'省钱小达人','抠门大魔王','优惠券猎人','比价老司机','折扣专家',
'拼单狂魔','薅羊毛达人','一只小鹿','花猫不喝酒','天天吃辣条',
'想变有钱人','哈哈哈是我','安静的咸鱼','奶茶续命中','干饭人本人',
'云上漫步者','向日葵不晒','糖醋排骨饭','北漂十年','打工累了',
'不熬夜星人','早睡早起队','宇宙小行星','深夜放毒','贪吃的橘子',
'永远不胖派','减肥进行时','咖啡因爱好者','下班想躺平','键盘侠本侠',
'佛系青年','努力上进鸡','摸鱼小能手','加班狗一只','周一不想动',
'周五最快乐','双十一战神','凑单小能手','满减狂魔','省一块是一块',
'外卖红包党','比价才下单','看完三家再买','不冲动消费','理智剁手党',
'存钱罐已满','月光警告','钱包瘦身中','大脑买买买','手刹车住'
];
var FLIP_VERBS = [
'通过比价省了','点外卖省了','刚省了','单笔省了','下单时省了',
'比价后省了','用券省了','薅到了','这单省了'
];
function buildFlipCases() {
var cases = [];
for (var i = 0; i < FLIP_NAMES.length; i++) {
var amount = (Math.random() * 27 + 3).toFixed(1); // 3.0 - 30.0
cases.push({
avatar: FLIP_AVATARS[i % FLIP_AVATARS.length],
bg: FLIP_BG[i % FLIP_BG.length],
name: FLIP_NAMES[i],
verb: FLIP_VERBS[i % FLIP_VERBS.length],
amount: amount,
});
}
// 洗牌一下避免按顺序露馅
for (var j = cases.length - 1; j > 0; j--) {
var k = Math.floor(Math.random() * (j + 1));
var t = cases[j]; cases[j] = cases[k]; cases[k] = t;
}
return cases;
}
function flipCaseHTML(c) {
return '<span class="flip-banner-avatar" style="background: ' + c.bg + '">' + c.avatar + '</span>'
+ '<span class="flip-banner-text"><span class="flip-name">' + c.name + '</span>' + c.verb + '<em>¥' + c.amount + '</em></span>';
}
var flipCases = [];
var flipIdx = 0;
var flipping = false;
function startFlipBanner() {
var banner = document.getElementById('flipBanner');
var front = document.getElementById('flipBannerFront');
var back = document.getElementById('flipBannerBack');
if (!banner || !front || !back) return;
flipCases = buildFlipCases();
front.innerHTML = flipCaseHTML(flipCases[0]);
back.innerHTML = flipCaseHTML(flipCases[1 % flipCases.length]);
setInterval(function() {
if (flipping) return;
flipping = true;
banner.classList.add('flipping');
setTimeout(function() {
flipIdx = (flipIdx + 1) % flipCases.length;
// 翻转完成后,把当前 back 内容搬到 front,瞬时复位(无 transition
front.innerHTML = flipCaseHTML(flipCases[flipIdx]);
var stage = document.getElementById('flipBannerStage');
stage.style.transition = 'none';
banner.classList.remove('flipping');
void stage.offsetWidth; // 强制重排
stage.style.transition = '';
back.innerHTML = flipCaseHTML(flipCases[(flipIdx + 1) % flipCases.length]);
flipping = false;
}, 560);
}, 2800);
}
document.addEventListener('DOMContentLoaded', startFlipBanner);
// ===== feed 排序 tab(智能推荐/距离最近/销量最高)→ 接真实数据(H2b) =====
// 点未激活 tab → 切高亮 + 拉真实数据(原 asc/desc 方向翻转是 mock 残留、后端不支持方向, 已删)
document.addEventListener('click', function (e) {
var item = e.target.closest('#home .feed-sort .fs-item');
if (!item || item.classList.contains('active')) return;
var group = item.closest('.feed-sort');
group.querySelectorAll('.fs-item').forEach(function (o) { o.classList.remove('active'); });
item.classList.add('active');
// 未授权定位:只切高亮、不打接口(对齐原生 selectTabWithoutLoad);授权后由 applyLocationState 拉
if (window.HomeFeed) {
if (HomeFeed.locationGranted()) HomeFeed.switchTab(item.dataset.key);
else HomeFeed.selectTabWithoutLoad(item.dataset.key);
}
// 自动滚到顶部:仅当 sort 还未贴顶时才滚(贴顶位置 device y=44 + sticky top=38 = 82)
var stickY = 82;
if (group.getBoundingClientRect().top > stickY + 2) {
group.scrollIntoView({ block: 'start', behavior: 'smooth' });
}
});
// ===== 顶部 Banner 轮播(3 个点位,左右滑动 + 自动播放) =====
function initHomeBannerSwiper() {
var banner = document.getElementById('homeBanner');
var track = document.getElementById('homeBannerTrack');
var dotsWrap = document.getElementById('homeBannerDots');
if (!banner || !track || !dotsWrap) return;
var total = 2;
var idx = 0;
var autoTimer = null;
var startX = 0, startY = 0, deltaX = 0, dragging = false, locked = false, abandoned = false;
var width = 0;
var pointerId = null;
var moved = false; // 本次按下是否发生了拖动(用于区分「点击」与「滑动」)
function go(i) {
idx = (i + total) % total;
width = banner.clientWidth;
track.style.transform = 'translateX(' + (-idx * width) + 'px)';
var dots = dotsWrap.querySelectorAll('.home-banner-dot');
for (var k = 0; k < dots.length; k++) {
dots[k].classList.toggle('active', k === idx);
}
}
function next() { go(idx + 1); }
function startAuto() {
stopAuto();
autoTimer = setInterval(next, 4000);
}
function stopAuto() {
if (autoTimer) { clearInterval(autoTimer); autoTimer = null; }
}
// Pointer Events 同时覆盖触摸 + 鼠标拖拽
banner.addEventListener('pointerdown', function(e) {
if (e.pointerType === 'mouse' && e.button !== 0) return;
stopAuto();
width = banner.clientWidth;
startX = e.clientX;
startY = e.clientY;
deltaX = 0;
dragging = true;
locked = false;
abandoned = false;
moved = false;
pointerId = e.pointerId;
track.classList.add('dragging');
try { banner.setPointerCapture(e.pointerId); } catch (err) {}
});
banner.addEventListener('pointermove', function(e) {
if (!dragging || e.pointerId !== pointerId) return;
var dx = e.clientX - startX;
var dy = e.clientY - startY;
if (!locked) {
if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 6) {
locked = true;
} else if (Math.abs(dy) > 6) {
// 纵向滚动:放弃这次拖拽
abandoned = true;
dragging = false;
track.classList.remove('dragging');
track.style.transform = 'translateX(' + (-idx * width) + 'px)';
try { banner.releasePointerCapture(e.pointerId); } catch (err) {}
startAuto();
return;
}
}
if (locked) {
// 阻止页面纵向滚动(pointermove 默认 passive: false 可 preventDefault
if (e.cancelable) e.preventDefault();
if (Math.abs(dx) > 6) moved = true;
deltaX = dx;
if ((idx === 0 && dx > 0) || (idx === total - 1 && dx < 0)) {
deltaX = dx * 0.35;
}
track.style.transform = 'translateX(' + (-idx * width + deltaX) + 'px)';
}
});
function endDrag(e) {
if (e && e.pointerId !== pointerId && pointerId !== null) return;
if (abandoned) { pointerId = null; return; }
if (!dragging) { startAuto(); return; }
dragging = false;
track.classList.remove('dragging');
var threshold = Math.max(30, width * 0.15);
if (deltaX < -threshold && idx < total - 1) go(idx + 1);
else if (deltaX > threshold && idx > 0) go(idx - 1);
else go(idx);
try { if (e) banner.releasePointerCapture(e.pointerId); } catch (err) {}
pointerId = null;
startAuto();
}
banner.addEventListener('pointerup', endDrag);
banner.addEventListener('pointercancel', endDrag);
// 点击 banner 跳转(监听挂在 banner 上:setPointerCapture 会让 click 派发到
// #homeBanner 而非具体 slide,所以按当前 idx 取目标。
// 区分「点击」与「滑动」:发生过拖动则不跳;点圆点也不跳)
var slides = track.querySelectorAll('.home-banner-slide');
banner.addEventListener('click', function(e) {
if (moved) return;
if (e.target.closest && e.target.closest('.home-banner-dots')) return;
var active = slides[idx];
if (!active) return;
var target = active.getAttribute('data-nav-target');
if (target) window.go(target);
});
// 点击点位跳转
dotsWrap.addEventListener('click', function(e) {
var t = e.target;
if (!t || !t.getAttribute) return;
var i = t.getAttribute('data-dot-idx');
if (i === null) return;
stopAuto();
go(parseInt(i, 10));
startAuto();
});
window.addEventListener('resize', function() { go(idx); });
go(0);
startAuto();
}
document.addEventListener('DOMContentLoaded', initHomeBannerSwiper);
// 页面历史栈(同时驱动左下角调试按钮和页面内的「← 返回」按钮)
var screenHistory = ['splash'];
// 统一返回:pop 一格回到上一步,不再 push
function goBack() {
if (screenHistory.length <= 1) return; // 已经在起点,无效
screenHistory.pop();
var prev = screenHistory[screenHistory.length - 1];
_activateScreen(prev, /*pushHistory=*/false);
}
// 调试按钮(左下角)走同一逻辑
function debugGoBack() { goBack(); }
// 仅切换 DOM active 态(不触发登录拦截、不入栈),供 goBack 复用
function _activateScreen(resolvedId, pushHistory) {
document.querySelectorAll('.screen').forEach(function(s) { s.classList.remove('active'); });
document.getElementById(resolvedId).classList.add('active');
if (pushHistory && screenHistory[screenHistory.length - 1] !== resolvedId) {
screenHistory.push(resolvedId);
}
}
function go(id) {
// 未登录 + 首次进入福利/我的 → 先跳登录页
if (!isLoggedIn && id === 'welfare' && !welfareVisited) {
welfareVisited = true; try{localStorage.setItem('proto_welfareVisited','true');}catch(e){}
loginFrom='welfare'; try{localStorage.setItem('proto_loginFrom','welfare');}catch(e){}
id = 'login';
}
if (!isLoggedIn && id === 'profile' && !profileVisited) {
profileVisited = true; try{localStorage.setItem('proto_profileVisited','true');}catch(e){}
loginFrom='profile'; try{localStorage.setItem('proto_loginFrom','profile');}catch(e){}
id = 'login';
}
// 方案切换:解析实际 screen id
var resolvedId = resolveScreenId(id);
_activateScreen(resolvedId, /*pushHistory=*/true);
// 切换福利页登录态
if (id === 'welfare') {
document.getElementById('welfareUnlogged').style.display = isLoggedIn ? 'none' : 'block';
document.getElementById('welfareLogged').style.display = isLoggedIn ? 'block' : 'none';
updateWelfareCheckinTask();
if (isLoggedIn) {
animateWelfareAssetIntro();
}
if (isLoggedIn && !checkinDone && !checkinShown && !checkinPending) {
checkinPending = true;
setTimeout(function() {
checkinPending = false;
var active = document.querySelector('.screen.active');
if (isLoggedIn && !checkinDone && !checkinShown && active && active.id === 'welfare') {
checkinShown = true; try{localStorage.setItem('proto_checkinShown','true');}catch(e){}
showCheckinPopup();
}
}, 500);
}
}
// 切换我的页登录态
if (id === 'profile') {
document.getElementById('profileUnlogged').style.display = isLoggedIn ? 'none' : 'block';
document.getElementById('profileLogged').style.display = isLoggedIn ? 'block' : 'none';
}
if (id === 'productselect') { startPlatformScan(); }
if (id === 'celebResults' || id === 'celebration') { renderCelebrationSavings(); renderResults('celebResults'); startConfetti(); }
if (id === 'compresult') { renderResults('compResultList'); }
if (id === 'comparing') { startComparison(); }
if (id === 'home') {
if (typeof renderTriClaim === 'function') renderTriClaim();
if (typeof renderLastCompareBanner === 'function') renderLastCompareBanner();
}
}
// 登录页返回 → 走统一的 goBack(pop 历史栈)
function loginGoBack() { goBack(); }
// ===== 签到弹窗 =====
const CHECKIN_MAX_DAY = 14;
const CHECKIN_REWARD_RULES = [
{ day: 1, amount: 180 },
{ day: 2, amount: 380 },
{ day: 3, amount: 220 },
{ day: 4, amount: 260 },
{ day: 5, amount: 320 },
{ day: 6, amount: 420 },
{ day: 7, amount: 888 },
{ day: 8, amount: 360 },
{ day: 9, amount: 460 },
{ day: 10, amount: 520 },
{ day: 11, amount: 680 },
{ day: 12, amount: 1088 },
{ day: 13, amount: 1688 },
{ day: 14, amount: 2888 },
];
const CHECKIN_REWARD_ASSETS = {
coin: 'assets/welfare/index/checkin-coin-single.png',
coins: 'assets/welfare/index/checkin-coins-600.png',
bigCoins: 'assets/welfare/index/checkin-coins-3888.png',
chest: 'assets/welfare/index/checkin-chest-8888.png'
};
let checkinShown = (typeof localStorage !== 'undefined' && localStorage.getItem('proto_checkinShown') === 'true');
let checkinPending = false;
let checkinDone = false;
let checkinCompletedBeforeToday = 2;
function getCheckinTodayDay() {
return Math.min(checkinCompletedBeforeToday + 1, CHECKIN_MAX_DAY);
}
function getCheckinCurrentStreak() {
return Math.min(checkinCompletedBeforeToday + (checkinDone ? 1 : 0), CHECKIN_MAX_DAY);
}
function getCheckinReward(day) {
var found = CHECKIN_REWARD_RULES.find(function(item) { return item.day === day; });
return found ? found.amount : 0;
}
function getCheckinMaxReward() {
return CHECKIN_REWARD_RULES.reduce(function(max, item) {
return Math.max(max, item.amount);
}, 0);
}
function getCheckinRewardAsset(day) {
var amount = getCheckinReward(day);
if (day === CHECKIN_MAX_DAY) return CHECKIN_REWARD_ASSETS.chest;
if (amount >= 1000) return CHECKIN_REWARD_ASSETS.bigCoins;
if (amount >= 680) return CHECKIN_REWARD_ASSETS.coins;
return CHECKIN_REWARD_ASSETS.coin;
}
function getCheckinLabel(day) {
var today = getCheckinTodayDay();
if (day === today) return '今天';
if (day === today + 1) return '明天';
return '第' + day + '天';
}
function getCheckinVisibleDays() {
var today = getCheckinTodayDay();
var days = [];
var start = Math.max(1, today - 2);
for (var day = start; day <= today; day++) days.push(day);
if (today < CHECKIN_MAX_DAY) days.push(today + 1);
[7, 14].some(function(milestone) {
if (milestone > today + 1) {
days.push(milestone);
return true;
}
return false;
});
for (var next = today + 2; next <= CHECKIN_MAX_DAY && days.length < 5; next++) {
days.push(next);
}
days = Array.from(new Set(days)).sort(function(a, b) { return a - b; });
while (days.length < 5 && days[0] > 1) {
days.unshift(days[0] - 1);
}
return days.slice(-5);
}
function getCheckinStep(day) {
var today = getCheckinTodayDay();
var completed = getCheckinCurrentStreak();
var isToday = day === today;
var isTomorrow = day === today + 1;
var isDone = day <= completed;
var isNextTarget = (!checkinDone && isToday) || (checkinDone && isTomorrow);
return {
day: day,
amount: getCheckinReward(day),
label: getCheckinLabel(day),
isDone: isDone,
isToday: isToday,
isTomorrow: isTomorrow,
isCurrent: isNextTarget,
isJustDone: checkinDone && isToday,
isMilestone: day === 7 || day === CHECKIN_MAX_DAY,
img: getCheckinRewardAsset(day)
};
}
function getCheckinSteps() {
return getCheckinVisibleDays().map(getCheckinStep);
}
function renderWelfareCheckinTimeline() {
var timeline = document.getElementById('r8CheckinTimeline');
if (!timeline) return;
timeline.innerHTML = getCheckinSteps().map(function(step) {
var classes = ['r8-checkin-step'];
if (step.isDone) classes.push('done');
if (step.isCurrent) classes.push('current');
if (step.isJustDone) classes.push('just-done');
if (step.day === CHECKIN_MAX_DAY) classes.push('most');
var top = step.isCurrent
? '<div class="r8-checkin-step-badge">' + (step.isTomorrow ? '明天' : '今天领') + '</div>'
: '<div class="r8-checkin-step-label">' + step.label + '</div>';
var icon = step.isDone
? '<div class="r8-checkin-step-icon"></div>'
: '<div class="r8-checkin-step-icon"><img src="' + step.img + '" alt=""></div>';
return '<div class="' + classes.join(' ') + '">' + top + icon + '<div class="r8-checkin-step-amount">' + step.amount + '</div></div>';
}).join('');
}
function renderCheckinPopupRewards() {
var rewards = document.getElementById('checkinRewards');
if (!rewards) return;
rewards.innerHTML = getCheckinSteps().map(function(step) {
var classes = ['checkin-reward-card'];
if (step.isDone) classes.push('claimed');
if (!step.isDone && step.isToday && !checkinDone) classes.push('current');
var icon = step.isDone
? '<div class="checkin-reward-icon">已领取</div>'
: '<div class="checkin-reward-icon"><img' + (step.day === CHECKIN_MAX_DAY ? ' class="icon-chest"' : '') + ' src="' + step.img + '" alt=""></div>';
return '<div class="' + classes.join(' ') + '">' + icon + '<div class="checkin-reward-amount">' + step.amount + '</div></div>';
}).join('');
}
function renderCheckinPopupProgress() {
var progress = document.getElementById('checkinProgress');
if (!progress) return;
progress.innerHTML = '<div class="checkin-progress-lines" id="checkinProgressLines"></div>' + getCheckinSteps().map(function(step) {
var classes = ['checkin-node'];
if (step.isDone) classes.push('done');
if (step.isCurrent) classes.push('current');
return '<div class="' + classes.join(' ') + '" data-day="' + step.day + '"><span class="checkin-dot"></span><span>' + step.label + '</span></div>';
}).join('');
}
function updateCheckinPopupCopy() {
var banner = document.getElementById('checkinBanner');
var title = document.getElementById('checkinPopupTitle');
var sub = document.getElementById('checkinPopupSub');
var btn = document.getElementById('checkinBtn');
var reminderBtn = document.getElementById('checkinReminderBtn');
var today = getCheckinTodayDay();
var streak = getCheckinCurrentStreak();
var todayReward = getCheckinReward(today);
var nextDay = Math.min(today + 1, CHECKIN_MAX_DAY);
var nextReward = getCheckinReward(nextDay);
if (banner) banner.innerHTML = '<span class="star"></span>连续签到14天 最高可领' + getCheckinMaxReward() + '金币';
if (title) title.textContent = checkinDone ? '今日金币已领取' : '签到领取今日金币';
if (sub) {
if (checkinDone && today >= CHECKIN_MAX_DAY) {
sub.innerHTML = '已完成 <b>14</b> 天签到,今日大奖已领取';
} else if (checkinDone) {
sub.innerHTML = '已连续签到 <b>' + streak + '</b> 天,明天可领 <b>' + nextReward + '</b> 金币';
} else {
sub.innerHTML = '已连续签到 <b>' + streak + '</b> 天,今天可领 <b>' + todayReward + '</b> 金币';
}
}
if (btn) {
btn.innerHTML = '签到领 <img src="' + CHECKIN_REWARD_ASSETS.coin + '" alt=""><b>' + todayReward + '</b>';
btn.style.display = checkinDone ? 'none' : 'flex';
}
if (reminderBtn) reminderBtn.style.display = checkinDone ? 'flex' : 'none';
}
function renderCheckinPopup() {
updateCheckinPopupCopy();
renderCheckinPopupRewards();
renderCheckinPopupProgress();
requestAnimationFrame(renderCheckinProgressLines);
}
function updateWelfareCheckinTask() {
var taskCard = document.getElementById('r8TaskCard');
var row = document.getElementById('r8CheckinTaskRow');
var recordBlock = document.getElementById('r8RecordTaskBlock');
var title = document.getElementById('r8CheckinTaskTitle');
var sub = document.getElementById('r8CheckinTaskSub');
var cta = document.getElementById('r8CheckinTaskCta');
var headline = document.getElementById('r8CheckinHeadline');
var cardCta = document.getElementById('r8CheckinCardCta');
if (!taskCard || !row || !recordBlock) return;
var today = getCheckinTodayDay();
var streak = getCheckinCurrentStreak();
var todayReward = getCheckinReward(today);
var nextDay = Math.min(today + 1, CHECKIN_MAX_DAY);
var nextReward = getCheckinReward(nextDay);
row.classList.toggle('is-done', checkinDone);
taskCard.insertBefore(row, recordBlock);
if (title) title.textContent = '新人签到领金币';
if (sub) {
sub.textContent = checkinDone
? (today >= CHECKIN_MAX_DAY ? '已完成14天签到,今日大奖已领取' : '已连续签到 ' + streak + ' 天,明天可领 ' + nextReward + ' 金币')
: '已连续签到 ' + streak + ' 天,今日可领 ' + todayReward + ' 金币';
}
if (cta) cta.textContent = checkinDone ? '去查看' : '去签到';
renderWelfareCheckinTimeline();
if (checkinDone) {
if (headline) headline.innerHTML = '已连续签到<span class="num">' + streak + '</span>天';
if (cardCta) cardCta.innerHTML = '<span class="check-tick">✓</span>今日已签';
} else {
if (headline) headline.textContent = '今日签到领金币';
if (cardCta) cardCta.textContent = '去签到';
}
}
function openCheckinTask() {
showCheckinPopup();
}
function renderCheckinProgressLines() {
var progress = document.getElementById('checkinProgress');
var lines = document.getElementById('checkinProgressLines');
if (!progress || !lines) return;
var nodes = Array.prototype.slice.call(progress.querySelectorAll('.checkin-node'));
if (nodes.length < 2) return;
lines.innerHTML = '';
var progressRect = progress.getBoundingClientRect();
var completed = getCheckinCurrentStreak();
var targetDay = checkinDone ? Math.min(getCheckinTodayDay() + 1, CHECKIN_MAX_DAY) : getCheckinTodayDay();
nodes.forEach(function(node, index) {
var next = nodes[index + 1];
if (!next) return;
var dot = node.querySelector('.checkin-dot');
var nextDot = next.querySelector('.checkin-dot');
if (!dot || !nextDot) return;
var dotRect = dot.getBoundingClientRect();
var nextDotRect = nextDot.getBoundingClientRect();
var x1Center = dotRect.left + dotRect.width / 2 - progressRect.left;
var x2Center = nextDotRect.left + nextDotRect.width / 2 - progressRect.left;
var dotRadius = dotRect.width / 2;
var nextDotRadius = nextDotRect.width / 2;
var day = Number(node.dataset.day || 0);
var nextDay = Number(next.dataset.day || 0);
var isDashed = nextDay - day > 1;
var x1 = isDashed ? x1Center + dotRadius : x1Center;
var x2 = isDashed ? x2Center - nextDotRadius : x2Center;
var width = Math.max(0, x2 - x1);
var color = (nextDay <= completed || nextDay === targetDay) ? '#FFB800' : '#D9D9D9';
var segment = document.createElement('span');
segment.className = 'checkin-segment ' + (isDashed ? 'dashed' : 'solid');
segment.style.left = x1 + 'px';
segment.style.width = width + 'px';
segment.style.setProperty('--line-color', color);
if (isDashed) {
var dashCount = 4;
var gap = Math.max(4, Math.min(5, width / 10));
var dash = Math.max(4, (width - gap * (dashCount + 1)) / dashCount);
for (var i = 0; i < dashCount; i++) {
var dashEl = document.createElement('i');
dashEl.style.cssText = 'position:absolute;top:0;height:3px;border-radius:99px;background:' + color + ';left:' + (gap + i * (dash + gap)) + 'px;width:' + dash + 'px;';
segment.appendChild(dashEl);
}
}
lines.appendChild(segment);
});
}
window.addEventListener('resize', function() {
var popup = document.getElementById('checkinPopup');
if (popup && getComputedStyle(popup).display !== 'none') renderCheckinProgressLines();
});
function showCheckinPopup() {
var popup = document.getElementById('checkinPopup');
if (!popup) return;
popup.style.display = 'flex';
renderCheckinPopup();
}
function closeCheckinPopup() {
document.getElementById('checkinPopup').style.display = 'none';
}
function doCheckin() {
if (checkinDone) return;
var todayReward = getCheckinReward(getCheckinTodayDay());
checkinDone = true;
updateWelfareCheckinTask();
renderCheckinPopup();
claimTaskReward('签到奖励', todayReward);
}
function doCheckinReminder() {
// 模拟权限弹窗
document.getElementById('calendarPermPopup').style.display = 'flex';
}
function closeCalendarPerm() {
document.getElementById('calendarPermPopup').style.display = 'none';
closeCheckinPopup();
}
// ===== 新手礼包 + 优惠券流程 =====
function showWelcomeGiftPopup() {
document.getElementById('welcomeGiftPopup').classList.add('show');
}
function openGiftBox() {
if (welcomeGiftClaimed) return;
var card = document.getElementById('welcomeGiftCard');
var chest = document.getElementById('giftChestEmoji');
var btn = document.getElementById('giftMainBtn');
// 阶段 A → B:宝箱开盖
card.classList.add('opening');
setTimeout(function() {
chest.textContent = '📦';
card.classList.remove('opening');
card.classList.add('opened');
flyCoinsToWelfareTab();
// 福利 tab 数字滚动 + badge + pulse
setTimeout(function() {
var tabItem = document.getElementById('homeTabWelfare');
var tabIcon = document.getElementById('homeTabWelfareIcon');
if (tabItem) {
// 临时提升 z-index 让 tab 浮在 overlay 之上
tabItem.style.position = 'relative';
tabItem.style.zIndex = '260';
}
if (tabIcon) {
tabIcon.classList.remove('tab-pulse');
// 触发重排以重启动画
void tabIcon.offsetWidth;
tabIcon.classList.add('tab-pulse');
}
var badge = document.getElementById('welfareTabBadge');
badge.classList.remove('show');
void badge.offsetWidth;
badge.classList.add('show');
// 余额在当前 mock 基数上累加新手礼包。
var coinStart = welfareCoinBalance;
var coinEnd = coinStart + 5000;
var cashEl = document.getElementById('welfareCashBalance');
var cashStart = parseFloat((cashEl && cashEl.textContent || '0').replace(/,/g, '')) || 0;
var cashEnd = cashStart + 0.5;
welfareCoinBalance = coinEnd;
welfareCashBalance = cashEnd;
animateNumber(document.getElementById('welfareCoinBalance'), coinStart, coinEnd, 1200, function(v) {
return v.toLocaleString();
});
animateNumber(cashEl, cashStart, cashEnd, 1200, function(v) {
return v.toFixed(2);
});
}, 600);
// 主按钮切换
btn.textContent = '开心收下,立即使用';
btn.onclick = claimGift;
}, 300);
}
function flyCoinsToWelfareTab() {
var card = document.getElementById('welcomeGiftCard');
var device = document.getElementById('app');
var tabIcon = document.getElementById('homeTabWelfareIcon');
if (!card || !device || !tabIcon) return;
var deviceRect = device.getBoundingClientRect();
var cardRect = card.getBoundingClientRect();
var tabRect = tabIcon.getBoundingClientRect();
// 宝箱中心相对于 device 的坐标
var startX = (cardRect.left + cardRect.width / 2) - deviceRect.left;
var startY = (cardRect.top + cardRect.height / 2) - deviceRect.top;
// 福利 tab 图标中心
var endX = (tabRect.left + tabRect.width / 2) - deviceRect.left;
var endY = (tabRect.top + tabRect.height / 2) - deviceRect.top;
var COIN_COUNT = 16;
for (var i = 0; i < COIN_COUNT; i++) {
var coin = document.createElement('div');
coin.className = 'fly-coin';
coin.textContent = '🪙';
// 起点定位(绝对定位 in device)
coin.style.left = startX + 'px';
coin.style.top = startY + 'px';
// burst 散射点(爆开瞬间)
var angle = (Math.PI * 2 * i) / COIN_COUNT + Math.random() * 0.4;
var burstDist = 60 + Math.random() * 60;
var bx = Math.cos(angle) * burstDist;
var by = Math.sin(angle) * burstDist - 30; // 略偏上
// 终点偏移(相对起点)
var tx = endX - startX + (Math.random() - 0.5) * 16;
var ty = endY - startY + (Math.random() - 0.5) * 8;
coin.style.setProperty('--bx', bx + 'px');
coin.style.setProperty('--by', by + 'px');
coin.style.setProperty('--tx', tx + 'px');
coin.style.setProperty('--ty', ty + 'px');
coin.style.setProperty('--d', (i * 18) + 'ms');
device.appendChild(coin);
// 动画结束后移除
coin.addEventListener('animationend', function() {
this.remove();
});
}
}
function animateWelfareAssetIntro() {
animateNumber(document.getElementById('welfareCoinBalance'), 0, welfareCoinBalance, 760, function(v) {
return Math.round(v).toLocaleString();
});
animateNumber(document.getElementById('welfareCashBalance'), 0, welfareCashBalance, 760, function(v) {
return v.toFixed(2);
});
}
function claimTaskReward(title, amount) {
var start = welfareCoinBalance;
welfareCoinBalance += amount;
animateNumber(document.getElementById('welfareCoinBalance'), start, welfareCoinBalance, amount >= 1000 ? 860 : 680, function(v) {
return Math.round(v).toLocaleString();
});
showRewardToast(title, amount);
}
function showRewardToast(title, amount) {
var existing = document.getElementById('rewardToast');
if (existing) existing.remove();
var toast = document.createElement('div');
toast.id = 'rewardToast';
toast.className = 'reward-toast';
var icon = document.createElement('img');
icon.className = 'reward-toast-icon';
icon.src = 'assets/welfare/index/checkin-coins-600.png';
icon.alt = '';
var amountEl = document.createElement('div');
amountEl.className = 'reward-toast-amount';
amountEl.textContent = '+' + Math.round(amount).toLocaleString();
var titleEl = document.createElement('div');
titleEl.className = 'reward-toast-title';
titleEl.textContent = title;
toast.appendChild(icon);
toast.appendChild(amountEl);
toast.appendChild(titleEl);
document.getElementById('app').appendChild(toast);
setTimeout(function() {
toast.classList.add('is-leaving');
}, 1450);
setTimeout(function() {
if (toast.parentNode) toast.remove();
}, 1700);
}
function claimVideoReward() {
if (videoRewardClaimed) return;
videoRewardClaimed = true;
claimTaskReward('任务奖励', 1859);
var cta = document.getElementById('r8VideoTaskCta');
if (cta) {
cta.textContent = '已领取';
cta.classList.add('is-done');
cta.disabled = true;
}
}
function claimMessageReminderReward() {
if (messageReminderRewardClaimed) return;
messageReminderRewardClaimed = true;
claimTaskReward('任务奖励', 10000);
var cta = document.getElementById('r8MessageTaskCta');
var sub = document.getElementById('r8MessageTaskSub');
if (sub) sub.textContent = '消息提醒已开启';
if (cta) {
cta.textContent = '已开启';
cta.classList.add('is-done');
cta.disabled = true;
}
}
function claimRecordComparisonReward() {
if (recordComparisonRewardClaimed) return;
recordComparisonRewardClaimed = true;
var activeBox = document.querySelector('#r8RecordTaskBlock .r8-record-reward-box.active');
if (activeBox) {
activeBox.classList.remove('active');
activeBox.classList.add('done');
var status = activeBox.querySelector('.r8-record-status');
if (status) status.textContent = '已领取';
var coin = activeBox.querySelector('.r8-record-coin');
if (coin) coin.remove();
if (activeBox.tagName === 'BUTTON') {
activeBox.disabled = true;
activeBox.setAttribute('aria-label', '第2次记录比价奖励已领取180金币');
}
}
claimTaskReward('比价奖励', 180);
}
function animateNumber(el, from, to, duration, formatter) {
if (!el) return;
el.__animateNumberToken = (el.__animateNumberToken || 0) + 1;
var token = el.__animateNumberToken;
var start = performance.now();
function frame(now) {
if (token !== el.__animateNumberToken) return;
var t = Math.min(1, (now - start) / duration);
var eased = 1 - Math.pow(1 - t, 3); // ease-out cubic
var v = from + (to - from) * eased;
el.textContent = formatter ? formatter(v) : Math.round(v).toString();
if (t < 1) requestAnimationFrame(frame);
else el.textContent = formatter ? formatter(to) : to.toString();
}
requestAnimationFrame(frame);
}
function claimGift() {
welcomeGiftClaimed = true;
closeWelcomeGift(false);
}
function closeWelcomeGift(skipped) {
document.getElementById('welcomeGiftPopup').classList.remove('show');
// 还原 tab 的 z-index
var tabItem = document.getElementById('homeTabWelfare');
if (tabItem) {
tabItem.style.position = '';
tabItem.style.zIndex = '';
}
if (skipped) {
// 重置卡片为初始状态以备重弹(但目前 welcomeGiftShown 已 true,不会重弹)
var card = document.getElementById('welcomeGiftCard');
card.classList.remove('opened');
document.getElementById('giftChestEmoji').textContent = '🎁';
var btn = document.getElementById('giftMainBtn');
btn.textContent = '✨ 开启礼包';
btn.onclick = openGiftBox;
}
}
// ===== 登录流程 =====
let pendingLoginAction = null; // 记录待执行的登录动作
function showToast(msg) {
var t = document.getElementById('loginToast');
t.textContent = msg;
t.style.display = 'block';
setTimeout(function(){ t.style.display = 'none'; }, 2000);
}
function toggleAgreeCheck() {
var chk = document.getElementById('agreeCheck');
chk.classList.toggle('checked');
var hint = document.getElementById('agreeHint');
if (hint) hint.style.display = chk.classList.contains('checked') ? 'none' : 'block';
}
function toggleAgreeCheckCeleb() {
var chk = document.getElementById('agreeCheckCeleb');
chk.classList.toggle('checked');
}
function isAgreedCeleb() {
return document.getElementById('agreeCheckCeleb').classList.contains('checked');
}
function celebOnboardingLogin() {
if (!isAgreedCeleb()) { showAgreeModal('celeb-quick'); return; }
doLogin();
}
function togglePhoneAgree() {
var chk = document.getElementById('agreeCheckPhone');
chk.classList.toggle('checked');
}
function isAgreed() {
return document.getElementById('agreeCheck').classList.contains('checked');
}
function isPhoneAgreed() {
return document.getElementById('agreeCheckPhone').classList.contains('checked');
}
function showAgreeModal(action) {
pendingLoginAction = action;
document.getElementById('agreeModal').style.display = 'flex';
}
function closeAgreeModal() {
document.getElementById('agreeModal').style.display = 'none';
pendingLoginAction = null;
}
function agreeAndLogin() {
// 入口页(一键登录):勾选入口页协议并继续
var chk = document.getElementById('agreeCheck');
if (chk && !chk.classList.contains('checked')) chk.classList.add('checked');
// 祝贺页内嵌登录的协议同步勾选
var chkCeleb = document.getElementById('agreeCheckCeleb');
if (chkCeleb && !chkCeleb.classList.contains('checked')) chkCeleb.classList.add('checked');
document.getElementById('agreeModal').style.display = 'none';
if (pendingLoginAction === 'quick' || pendingLoginAction === 'celeb-quick') {
doLogin();
} else if (pendingLoginAction === 'phone') {
doPhoneLogin();
}
pendingLoginAction = null;
}
function showAgreePhoneModal() {
document.getElementById('agreePhoneModal').style.display = 'flex';
}
function closeAgreePhoneModal() {
document.getElementById('agreePhoneModal').style.display = 'none';
}
function agreeAndContinuePhone() {
var chkP = document.getElementById('agreeCheckPhone');
if (chkP && !chkP.classList.contains('checked')) chkP.classList.add('checked');
document.getElementById('agreePhoneModal').style.display = 'none';
doSendSms();
}
function doLogin() {
isLoggedIn = true; try{localStorage.setItem('proto_isLoggedIn','true');}catch(e){}
var target = loginFrom || 'home';
go(target);
// 主路径登录(不是从福利/我的回流的) + 首次 → 弹新手礼包
if (!welcomeGiftShown && target === 'home') {
welcomeGiftShown = true; try{localStorage.setItem('proto_welcomeGiftShown','true');}catch(e){}
setTimeout(showWelcomeGiftPopup, 400);
}
}
function onboardingLogin() {
if (!isAgreed()) { showAgreeModal('quick'); return; }
doLogin();
}
function showPhoneLogin() {
// 进入独立手机号登录页(Step1)
document.getElementById('lpStep1').style.display = 'block';
document.getElementById('lpStep2').style.display = 'none';
go('loginPhone');
setTimeout(function(){
var inp = document.getElementById('phoneInput');
if (inp) inp.focus();
}, 80);
}
function showQuickLogin() {
// 用于其他位置返回入口页(保留兼容)
if (smsTimer) { clearInterval(smsTimer); smsTimer = null; }
go('login');
}
function updatePhoneCta() {
var inp = document.getElementById('phoneInput');
var cta = document.getElementById('lpCta');
if (!inp || !cta) return;
if (inp.value.length >= 11) cta.classList.remove('disabled');
else cta.classList.add('disabled');
}
function backToPhoneStep1() {
document.getElementById('lpStep2').style.display = 'none';
document.getElementById('lpStep1').style.display = 'block';
if (smsTimer) { clearInterval(smsTimer); smsTimer = null; }
}
let smsTimer = null;
function doSendSms() {
var phone = document.getElementById('phoneInput').value;
if (phone.length < 11) { showToast('请输入正确的手机号'); return; }
// 显示手机号提示(脱敏)
var masked = phone.substring(0,3) + '****' + phone.substring(7);
document.getElementById('smsPhoneHint').textContent = '验证码已发送至 ' + masked;
// 切换到 Step2
document.getElementById('lpStep1').style.display = 'none';
document.getElementById('lpStep2').style.display = 'block';
// 清空验证码输入框
var boxes = document.getElementById('smsCodeBoxes').querySelectorAll('input');
boxes.forEach(function(b){ b.value = ''; });
boxes[0].focus();
// 倒计时
var btn = document.getElementById('smsBtn');
var sec = 60;
btn.style.color = 'var(--gray-400)';
btn.style.cursor = 'default';
btn.onclick = null;
btn.textContent = sec + 's 后重发';
smsTimer = setInterval(function(){
sec--;
if (sec <= 0) {
clearInterval(smsTimer);
smsTimer = null;
btn.style.color = '#FFD600';
btn.style.cursor = 'pointer';
btn.textContent = '重新发送';
btn.onclick = function(){ doSendSms(); };
} else {
btn.textContent = sec + 's 后重发';
}
}, 1000);
}
function sendSmsCode() {
// 手机号页有独立的协议勾选状态 + 独立居中弹窗
if (!isPhoneAgreed()) { showAgreePhoneModal(); return; }
doSendSms();
}
function smsCodeComplete() {
// 6位验证码全部填完,自动登录
var boxes = document.getElementById('smsCodeBoxes').querySelectorAll('input');
var code = '';
boxes.forEach(function(b){ code += b.value; });
if (code.length === 6) {
isLoggedIn = true; try{localStorage.setItem('proto_isLoggedIn','true');}catch(e){}
go(loginFrom || 'home');
}
}
// ===== 平台选择流程 =====
// 全量平台列表,外卖版优先;同系优先外卖版,有外卖版则隐藏主站版
const allApps = [
{ id: 'meituan-waimai', name: '美团', icon: 'assets/shared/logos/meituan.png', color: '#FF6B00', bg: '#FFF3E0', desc: '外卖·美食·买菜', installed: true, replaces: 'meituan' },
{ id: 'taobao-shanguang', name: '淘宝', icon: 'assets/shared/logos/taobao.png', color: '#FF6A00', bg: '#FFF3E0', desc: '外卖·超市·买药', installed: true, replaces: 'taobao' },
{ id: 'jd-waimai', name: '京东', icon: 'assets/shared/logos/jd.png', color: '#E1251B', bg: '#FFEBEE', desc: '品质外卖·新人立减', installed: false, replaces: 'jd' },
{ id: 'meituan', name: '美团', icon: 'assets/shared/logos/meituan.png', color: '#FFD600', bg: '#FFFDE7', desc: '团购·到店·买菜', installed: true },
{ id: 'taobao', name: '淘宝', icon: 'assets/shared/logos/taobao.png', color: '#FF5000', bg: '#FFF3E0', desc: '网购·百亿补贴', installed: true },
{ id: 'jd', name: '京东', icon: 'assets/shared/logos/jd.png', color: '#E1251B', bg: '#FFEBEE', desc: '又好又便宜', installed: true },
];
// 外卖版是这个产品的画布:只要 allApps 里有外卖版条目,就隐藏对应主站版(不论主站是否安装)。
// 未安装的外卖版仍然在列表里以 disabled 形态展示,强调"我们能比 3 个平台,但你少了 1 个"。
function getVisibleApps() {
const replacedIds = new Set(allApps.filter(a => a.replaces).map(a => a.replaces));
return allApps.filter(a => !replacedIds.has(a.id));
}
// 进入 productselect 时渲染平台列表
let platformScanDone = false;
function startPlatformScan() {
if (platformScanDone) return;
platformScanDone = true;
renderPlatformList();
startRewardCountdown();
}
function renderPlatformList() {
const visible = getVisibleApps();
// 已装 + 未装合并到同一列表,外形对齐,用样式区分 disabled 态
document.getElementById('installedApps').innerHTML = visible.map((app, i) => {
if (app.installed) {
return '' +
'<div onclick="selectPlatform(\'' + app.id + '\')" data-pcard="' + i + '" class="platform-card" style="background: #fff;border-radius: 14px;padding: 16px 16px;cursor: pointer;display: flex;align-items: center;gap: 12px;border: 1.5px solid var(--gray-200);margin-bottom: 8px;position: relative;transition: border-color .25s,box-shadow .25s,transform .15s;">' +
'<img src="' + app.icon + '" style="width: 48px;height: 48px;border-radius: 12px;flex-shrink: 0;object-fit: cover;" />' +
'<div style="flex: 1;min-width: 0;">' +
'<div style="font-size: 20px;font-weight: 700;color: var(--gray-900);letter-spacing: -.2px;">' + app.name + '</div>' +
'</div>' +
'<svg width="20" height="20" viewBox="0 0 20 20" fill="none" style="flex-shrink: 0;color: var(--gray-300);"><path d="M7.5 4.5l5.5 5.5-5.5 5.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
'</div>';
}
return '' +
'<div class="platform-card-disabled" aria-disabled="true" onclick="onPlatformInstallClick(\'' + app.id + '\')" style="background: #FAFAFA;border-radius: 14px;padding: 16px 16px;cursor: pointer;display: flex;align-items: center;gap: 12px;border: 1.5px solid #ECECEC;margin-bottom: 8px;position: relative;">' +
'<img src="' + app.icon + '" style="width: 48px;height: 48px;border-radius: 12px;flex-shrink: 0;object-fit: cover;filter: grayscale(.7);opacity: .55;" />' +
'<div style="flex: 1;min-width: 0;">' +
'<div style="font-size: 20px;font-weight: 700;color: var(--gray-400);letter-spacing: -.2px;">' + app.name + '</div>' +
'</div>' +
'<span style="flex-shrink: 0;display: inline-flex;align-items: center;gap: 8px;">' +
'<span style="font-size: 15px;font-weight: 700;color: var(--gray-900);line-height: 1;">去安装</span>' +
'<svg width="20" height="20" viewBox="0 0 20 20" fill="none" style="color: var(--gray-300);"><path d="M7.5 4.5l5.5 5.5-5.5 5.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
'</span>' +
'</div>';
}).join('');
// 旧的次级"未安装"分区不再使用
document.getElementById('notInstalledSection').style.display = 'none';
}
// 领券浮窗 / 比价浮窗顶部的平台进度 mini icons —— 只渲染已安装的平台
// states: { 'platform-id': 'done' | 'active' | 'pending' }
function renderFloatPlatforms(containerId, states) {
const container = document.getElementById(containerId);
if (!container) return;
const installed = getVisibleApps().filter(a => a.installed);
container.innerHTML = installed.map(app => {
const state = (states && states[app.id]) || 'pending';
return '<div class="cmp-platform-mini ' + state + '"><img src="' + app.icon + '" alt="' + app.name + '"/></div>';
}).join('');
}
// 一键领券弹窗 · 3 平台卡片渲染。已装:黄边 + ✓;未装(方案 C):灰底 + 黄圆 ↓ 角标
function couponChipShortName(name) {
return name.replace(/外卖$|闪购$/, '');
}
function renderCouponPromptPlatforms() {
const grid = document.getElementById('couponPlatformGrid');
if (!grid) return;
const installedList = getInstalledPkgSet(); // 真装机态(同去点餐,替换写死的 mock app.installed)
const platforms = getVisibleApps(); // 3 张外卖版卡片
grid.innerHTML = platforms.map(app => {
const shortName = couponChipShortName(app.name);
const installed = (PICKER_PACKAGES[app.name] || []).some(function(p){ return installedList.indexOf(p) !== -1; });
if (installed) {
return '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 2px solid #FF5630;border-radius: 14px;padding: 12px 4px 8px;position: relative;">' +
'<img src="' + app.icon + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;"/>' +
'<div style="font-size: 12px;font-weight: 700;color: var(--gray-900);margin-top: 8px;">' + shortName + '</div>' +
'<span class="cp-check" style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FF5630;display: flex;align-items: center;justify-content: center;">' +
'<svg width="10" height="10" viewBox="0 0 9 9"><path d="M2 4.5l1.5 1.5 3-4" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg>' +
'</span>' +
'</div>';
}
return '<div class="cp-card-uninstalled" aria-disabled="true" style="background: #FAFAFA;border: 1.5px solid #ECECEC;border-radius: 14px;padding: 12px 4px 8px;position: relative;">' +
'<img src="' + app.icon + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;filter: grayscale(.7);opacity: .55;"/>' +
'<div style="font-size: 12px;font-weight: 700;color: #BBB;margin-top: 8px;">' + shortName + '</div>' +
'<span style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FFD600;box-shadow: 0 0 0 1.5px rgba(255,179,0,.35);display: flex;align-items: center;justify-content: center;">' +
'<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M5 1.5v5.5m0 0L2.5 4.5M5 7l2.5-2.5M2 9h6" stroke="#1a1a1a" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
'</span>' +
'</div>';
}).join('');
}
let selectedAppForBubble = null;
function selectPlatform(platformId) {
const app = allApps.find(a => a.id === platformId);
if (!app) return;
selectedAppForBubble = app;
go('comparing');
}
// 未安装平台 → 跳应用市场(原型 stub)
function onPlatformInstallClick(platformId) {
const app = allApps.find(a => a.id === platformId);
if (!app) return;
console.log('[install] 跳应用市场下载:', app.name);
}
function showWelcomeBubble() {
const mask = document.getElementById('welcomeMask');
const bubble = document.getElementById('welcomeBubble');
const title = document.getElementById('welcomeTitle');
const app = selectedAppForBubble || { name: '美团' };
title.textContent = '已为您唤起' + app.name;
mask.style.display = 'block';
bubble.style.display = 'block';
// 强制重排,让初始 opacity:0 / scale .92 生效
void bubble.offsetWidth;
requestAnimationFrame(() => {
mask.style.opacity = '1';
bubble.style.opacity = '1';
bubble.style.transform = 'translate(-50%,-50%) scale(1)';
});
setTimeout(() => {
mask.style.opacity = '0';
bubble.style.opacity = '0';
setTimeout(() => {
mask.style.display = 'none';
bubble.style.display = 'none';
}, 450);
}, 2000);
}
// 红包倒计时
let countdownInterval;
let rewardPhase = 0; // 0=首次, 1=已过期, 2=二次机会
function startRewardCountdown() {
let total = 300; // 5分钟
rewardPhase = 0;
updateRewardCard('active');
runCountdown(total);
}
function runCountdown(totalSeconds) {
let remaining = totalSeconds * 10; // 1/10秒为单位
const el = document.getElementById('rewardCountdown');
if (!el) return;
clearInterval(countdownInterval);
countdownInterval = setInterval(() => {
remaining--;
if (remaining <= 0) {
clearInterval(countdownInterval);
remaining = 0;
onCountdownEnd();
}
const totalSec = Math.floor(remaining / 10);
const tenths = remaining % 10;
const m = String(Math.floor(totalSec / 60)).padStart(2, '0');
const s = String(totalSec % 60).padStart(2, '0');
el.textContent = m + ':' + s + '.' + tenths;
}, 100);
}
function onCountdownEnd() {
if (rewardPhase === 0) {
// 首次过期 → 显示过期状态
rewardPhase = 1;
updateRewardCard('expired');
// 2秒后给二次机会
setTimeout(() => {
rewardPhase = 2;
updateRewardCard('second-chance');
runCountdown(180); // 二次机会 3分钟
}, 2000);
} else {
// 二次也过期 → 最终过期
updateRewardCard('final-expired');
}
}
function updateRewardCard(state) {
const card = document.getElementById('rewardCard');
if (!card) return;
if (state === 'active') {
card.style.background = 'linear-gradient(135deg,#FFF8E1,#FFECB3)';
card.style.opacity = '1';
card.innerHTML = `
<div style="flex-shrink: 0;font-size: 36px;line-height: 1;">🧧</div>
<div style="flex: 1;">
<div style="font-size: 14px;font-weight: 700;color: var(--gray-900);">完成首次比价领红包奖励</div>
</div>`;
} else if (state === 'expired') {
card.style.background = 'var(--gray-100)';
card.style.opacity = '.6';
card.innerHTML = `
<div style="flex-shrink: 0;font-size: 36px;line-height: 1;filter: grayscale(1);">🧧</div>
<div style="flex: 1;">
<div style="font-size: 14px;font-weight: 700;color: var(--gray-500);">红包已过期</div>
<div style="font-size: 12px;color: var(--gray-400);margin-top: 2px;">很遗憾,活动已结束</div>
</div>`;
} else if (state === 'second-chance') {
card.style.background = 'linear-gradient(135deg,#FFEBEE,#FFCDD2)';
card.style.opacity = '1';
card.innerHTML = `
<div style="flex-shrink: 0;font-size: 36px;line-height: 1;">🎁</div>
<div style="flex: 1;">
<div style="font-size: 14px;font-weight: 700;color: #E53935;">再给你一次机会!</div>
<div style="font-size: 12px;color: #FF5722;font-weight: 600;margin-top: 2px;">最后 <span id="rewardCountdown">03:00.0</span></div>
</div>`;
} else if (state === 'final-expired') {
card.style.background = 'var(--gray-100)';
card.style.opacity = '.5';
card.innerHTML = `
<div style="flex-shrink: 0;font-size: 36px;line-height: 1;filter: grayscale(1);">🧧</div>
<div style="flex: 1;">
<div style="font-size: 14px;font-weight: 600;color: var(--gray-400);">新人红包奖励已过期</div>
</div>`;
}
}
// 卡片轮流高亮
let cardRotationInterval;
function startCardRotation(count) {
let active = 0;
function highlight() {
document.querySelectorAll('[data-pcard]').forEach((el, i) => {
if (i === active) {
el.style.borderColor = '#FF5722';
el.style.boxShadow = '0 0 0 1px #FF5722';
el.querySelector('.tap-hand-icon').style.opacity = '1';
el.querySelector('.tap-hand-icon').classList.add('tap-hand');
} else {
el.style.borderColor = 'var(--gray-200)';
el.style.boxShadow = 'none';
el.querySelector('.tap-hand-icon').style.opacity = '0';
el.querySelector('.tap-hand-icon').classList.remove('tap-hand');
}
});
active = (active + 1) % count;
}
highlight();
clearInterval(cardRotationInterval);
cardRotationInterval = setInterval(highlight, 2100);
}
// 省钱数字滚动动画(已弃用但保留兼容)
function animateSavingCounter() {
const el = document.getElementById('savingCounter');
const target = 12847;
const duration = 1500;
const start = performance.now();
function tick(now) {
const p = Math.min((now - start) / duration, 1);
const ease = 1 - Math.pow(1 - p, 3);
el.textContent = Math.floor(target * ease).toLocaleString();
if (p < 1) requestAnimationFrame(tick);
}
requestAnimationFrame(tick);
}
// 商品点击 → 过渡提示 → 进入比价(兼容旧调用)
function startProductCompare() {
selectPlatform('meituan-waimai');
}
// ===== 反馈面板 =====
function openFeedbackSheet() {
const sheet = document.getElementById('feedbackSheet');
// 重置到步骤1
document.getElementById('fbStep1').style.display = 'block';
document.getElementById('fbFormLower').style.display = 'none';
document.getElementById('fbFormWrong').style.display = 'none';
document.getElementById('fbSuccess').style.display = 'none';
// 动态生成平台选项(仅显示 found/same-price 的平台)
const pl = document.getElementById('fbPlatformList');
if (pl) {
pl.innerHTML = results.filter(r => r.status !== 'not-found').map(r =>
`<div class="fb-platform" onclick="this.classList.toggle('selected')" style="padding: 8px 16px;border: 1.5px solid var(--gray-200);border-radius: 32px;font-size: 13px;font-weight: 600;cursor: pointer;display: flex;align-items: center;gap: 4px;"><img src="${r.platformIcon}" style="width: 16px;height: 16px;border-radius: 4px;"> ${r.platform}</div>`
).join('');
}
sheet.style.display = 'block';
}
function closeFeedbackSheet() {
document.getElementById('feedbackSheet').style.display = 'none';
}
function selectFbType(type) {
document.getElementById('fbStep1').style.display = 'none';
if (type === 'lower_price') {
document.getElementById('fbFormLower').style.display = 'block';
} else {
document.getElementById('fbFormWrong').style.display = 'block';
}
}
function backToFbStep1() {
document.getElementById('fbFormLower').style.display = 'none';
document.getElementById('fbFormWrong').style.display = 'none';
document.getElementById('fbStep1').style.display = 'block';
}
function submitFeedback() {
document.getElementById('fbFormLower').style.display = 'none';
document.getElementById('fbFormWrong').style.display = 'none';
document.getElementById('fbSuccess').style.display = 'block';
}
// 提现金额选择
function selectWd(el) {
document.querySelectorAll('.wd-opt').forEach(e => { e.style.borderColor='var(--gray-200)'; e.style.background='#fff'; });
el.style.borderColor='#FF5722'; el.style.background='#FFF3E0';
}
function showPrototypeToast(msg) {
var existing = document.getElementById('prototypeToast');
if (existing) existing.remove();
var toast = document.createElement('div');
toast.id = 'prototypeToast';
toast.textContent = msg;
toast.style.cssText = 'position:absolute;left:50%;bottom:118px;transform:translateX(-50%);max-width:310px;background:rgba(0,0,0,.78);color:#fff;font-size:13px;font-weight:600;line-height:18px;padding:10px 16px;border-radius:12px;z-index:9999;text-align:center;pointer-events:none;opacity:1;transition:opacity .25s;white-space:nowrap;';
document.getElementById('app').appendChild(toast);
setTimeout(function() { toast.style.opacity = '0'; }, 1500);
setTimeout(function() { if (toast.parentNode) toast.remove(); }, 1800);
}
function selectWithdrawAmount(el) {
document.querySelectorAll('.withdraw-amount').forEach(function(item) {
item.classList.remove('active');
});
el.classList.add('active');
openWechatBindSheet(el.dataset.amount || el.textContent.replace('元', '').trim());
}
function openWechatBindSheet(amount) {
var layer = document.getElementById('wechatBindSheet');
var amountEl = document.getElementById('wechatBindAmount');
if (amountEl) amountEl.textContent = amount || '0.3';
if (!layer) return;
layer.classList.add('open');
layer.setAttribute('aria-hidden', 'false');
}
function closeWechatBindSheet() {
var layer = document.getElementById('wechatBindSheet');
if (!layer) return;
layer.classList.remove('open');
layer.setAttribute('aria-hidden', 'true');
}
var incomeCurrentTab = 'coin';
function openIncomeHistory(tab) {
go('coinhistory');
setIncomeTab(tab);
}
function setIncomeTab(tab) {
incomeCurrentTab = tab === 'cash' ? 'cash' : 'coin';
var tabs = document.getElementById('incomeTabs');
var coinTab = document.getElementById('incomeTabCoin');
var cashTab = document.getElementById('incomeTabCash');
var track = document.getElementById('incomeRecordTrack');
var note = document.getElementById('incomeFooterNote');
var btn = document.getElementById('incomeFooterBtn');
if (tabs) tabs.dataset.active = incomeCurrentTab;
if (coinTab) coinTab.classList.toggle('active', incomeCurrentTab === 'coin');
if (cashTab) cashTab.classList.toggle('active', incomeCurrentTab === 'cash');
if (track) track.classList.toggle('cash-active', incomeCurrentTab === 'cash');
if (note) {
note.innerHTML = incomeCurrentTab === 'coin'
? '累计收益 <strong>2.18 元</strong>'
: '累计到账 <strong>2.36 元</strong>';
}
if (btn) btn.textContent = incomeCurrentTab === 'coin' ? '立即提现' : '去提现';
}
function initIncomeSwipe() {
var shell = document.getElementById('incomeRecordShell');
if (!shell) return;
var startX = 0;
var startY = 0;
var tracking = false;
function begin(x, y) {
startX = x;
startY = y;
tracking = true;
}
function finish(x, y) {
if (!tracking) return;
tracking = false;
var dx = x - startX;
var dy = y - startY;
if (Math.abs(dx) < 46 || Math.abs(dx) < Math.abs(dy) * 1.1) return;
if (dx < 0 && incomeCurrentTab === 'coin') setIncomeTab('cash');
if (dx > 0 && incomeCurrentTab === 'cash') setIncomeTab('coin');
}
shell.addEventListener('touchstart', function(e) {
if (!e.touches || !e.touches[0]) return;
begin(e.touches[0].clientX, e.touches[0].clientY);
}, { passive: true });
shell.addEventListener('touchend', function(e) {
var touch = e.changedTouches && e.changedTouches[0];
if (!touch) return;
finish(touch.clientX, touch.clientY);
}, { passive: true });
shell.addEventListener('mousedown', function(e) {
begin(e.clientX, e.clientY);
});
shell.addEventListener('mouseup', function(e) {
finish(e.clientX, e.clientY);
});
}
var EXCHANGE_COIN_BALANCE = 28967;
var currentExchangeAmount = EXCHANGE_COIN_BALANCE;
function exchangeCashText(coins) {
return (Math.floor(coins / 100) / 100).toFixed(2);
}
function selectExchangeAmount(amount, el) {
currentExchangeAmount = amount === 'all' ? EXCHANGE_COIN_BALANCE : Number(amount);
document.querySelectorAll('.exchange-preset').forEach(function(item) {
item.classList.remove('active');
});
if (el) el.classList.add('active');
var coinAmount = document.getElementById('exchangeCoinAmount');
var cashAmount = document.getElementById('exchangeCashAmount');
var error = document.getElementById('exchangeError');
if (coinAmount) coinAmount.textContent = currentExchangeAmount.toLocaleString('zh-CN');
if (cashAmount) cashAmount.textContent = exchangeCashText(currentExchangeAmount);
if (error) {
if (currentExchangeAmount > EXCHANGE_COIN_BALANCE) {
error.textContent = '超过已拥有金币数';
} else if (currentExchangeAmount < 100) {
error.textContent = '最低 100 金币起兑';
} else {
error.textContent = '';
}
}
}
function openExchangeSheet() {
var layer = document.getElementById('exchangeSheet');
if (!layer) return;
layer.classList.add('open');
layer.setAttribute('aria-hidden', 'false');
selectExchangeAmount('all', document.getElementById('exchangePresetAll'));
}
function closeExchangeSheet() {
var layer = document.getElementById('exchangeSheet');
if (!layer) return;
layer.classList.remove('open');
layer.setAttribute('aria-hidden', 'true');
}
function confirmExchange() {
if (currentExchangeAmount > EXCHANGE_COIN_BALANCE || currentExchangeAmount < 100) return;
closeExchangeSheet();
showPrototypeToast('兑换申请已提交,现金到账可能会有延迟');
}
document.addEventListener('DOMContentLoaded', function() {
initIncomeSwipe();
setIncomeTab('coin');
updateWelfareCheckinTask();
});
// ===== 退出登录 / 注销账号 =====
function showLogoutConfirm() {
document.getElementById('logoutConfirm').style.display = 'flex';
}
function closeLogoutConfirm() {
document.getElementById('logoutConfirm').style.display = 'none';
}
function doLogout() {
closeLogoutConfirm();
isLoggedIn = false; try{localStorage.setItem('proto_isLoggedIn','false');}catch(e){}
welfareVisited = false; try{localStorage.setItem('proto_welfareVisited','false');}catch(e){}
profileVisited = false; try{localStorage.setItem('proto_profileVisited','false');}catch(e){}
checkinShown = false; try{localStorage.setItem('proto_checkinShown','false');}catch(e){}
go('home');
}
function showDeleteConfirm() {
document.getElementById('deleteConfirm').style.display = 'flex';
}
function closeDeleteConfirm() {
document.getElementById('deleteConfirm').style.display = 'none';
}
function doDeleteAccount() {
closeDeleteConfirm();
isLoggedIn = false; try{localStorage.setItem('proto_isLoggedIn','false');}catch(e){}
welfareVisited = false; try{localStorage.setItem('proto_welfareVisited','false');}catch(e){}
profileVisited = false; try{localStorage.setItem('proto_profileVisited','false');}catch(e){}
checkinShown = false; try{localStorage.setItem('proto_checkinShown','false');}catch(e){}
go('home');
}
// Splash 不再自动跳转,等用户点击 CTA "开始体验"
// (旧的 splashCounter 累计数字动画已移除 — Option C 设计无大数字)
// Video 模拟
// ============ Video tutorial 状态机 (vt-) ============
const vt = {
status: 'initial', // initial | playing | paused | ended
elapsed: 0, // 已播毫秒
duration: 5000, // 5s 模拟一分钟,真实视频接入后改成视频时长
intervalId: null,
hasFinished: false,
isDragging: false,
};
const VT_PLAY = '<path d="M8 5v14l11-7z"/>';
const VT_PAUSE = '<path d="M6 19h4V5H6v14zm8-14v14h4V5h-4z"/>';
const VT_REPLAY = '<path d="M12 5V1L7 6l5 5V7c3.31 0 6 2.69 6 6s-2.69 6-6 6-6-2.69-6-6H4c0 4.42 3.58 8 8 8s8-3.58 8-8-3.58-8-8-8z"/>';
function vtRender() {
const player = document.getElementById('vt-video');
if (!player) return;
const fill = document.getElementById('vt-fill');
const cur = document.getElementById('vt-current');
const cta = document.getElementById('vt-cta');
const cIco = document.getElementById('vt-center-icon');
const bIco = document.getElementById('vt-bottom-icon');
player.className = 'vt-video-player ' + vt.status;
const progress = Math.min(vt.elapsed / vt.duration, 1);
fill.style.width = (progress * 100) + '%';
// 5s 真时长 → 显示成 1 分钟视频
const totalSec = Math.floor(progress * 60);
const min = Math.floor(totalSec / 60);
const sec = totalSec % 60;
cur.textContent = min + ':' + (sec < 10 ? '0' : '') + sec;
cIco.innerHTML = (vt.status === 'ended') ? VT_REPLAY : VT_PLAY;
bIco.innerHTML = (vt.status === 'playing') ? VT_PAUSE : VT_PLAY;
if (vt.hasFinished) {
cta.classList.remove('disabled');
cta.textContent = '进入快速设置';
} else {
cta.classList.add('disabled');
cta.textContent = '看完后继续';
}
}
function vtStopTimer() {
if (vt.intervalId !== null) { clearInterval(vt.intervalId); vt.intervalId = null; }
}
function vtStartTimer() {
vtStopTimer();
vt.intervalId = setInterval(() => {
vt.elapsed += 50;
if (vt.elapsed >= vt.duration) {
vt.elapsed = vt.duration;
vt.status = 'ended';
vt.hasFinished = true;
vtStopTimer();
}
vtRender();
}, 50);
}
function vtToggle() {
if (vt.status === 'playing') {
vtStopTimer();
vt.status = 'paused';
} else if (vt.status === 'ended') {
vt.elapsed = 0;
vt.status = 'playing';
vtStartTimer();
} else {
vt.status = 'playing';
vtStartTimer();
}
vtRender();
}
function vtNext() {
if (!vt.hasFinished) return;
vtStopTimer();
go('permission-step1');
}
let vtInited = false;
function vtInit() {
if (vtInited) return;
const player = document.getElementById('vt-video');
if (!player) return;
const track = document.getElementById('vt-track');
vtInited = true;
// 1. 点视频本身切换播放/暂停(排除 controls 与 center-play 的内联 onclick
player.addEventListener('click', (e) => {
if (e.target.closest('.vt-controls')) return;
if (e.target.closest('.vt-center-play')) return;
vtToggle();
});
// 2. 进度条拖动(playing / paused 都支持)
function vtSeekFromX(clientX) {
const rect = track.getBoundingClientRect();
const ratio = Math.max(0, Math.min((clientX - rect.left) / rect.width, 1));
vt.elapsed = ratio * vt.duration;
if (ratio >= 1) {
vt.status = 'ended';
vt.hasFinished = true;
vtStopTimer();
} else if (vt.status === 'ended') {
vt.status = 'paused';
}
vtRender();
}
track.addEventListener('mousedown', (e) => {
e.preventDefault(); e.stopPropagation();
vt.isDragging = true; vtSeekFromX(e.clientX);
});
document.addEventListener('mousemove', (e) => { if (vt.isDragging) vtSeekFromX(e.clientX); });
document.addEventListener('mouseup', () => { vt.isDragging = false; });
track.addEventListener('touchstart', (e) => {
e.stopPropagation();
vt.isDragging = true; vtSeekFromX(e.touches[0].clientX);
}, {passive: true});
document.addEventListener('touchmove', (e) => { if (vt.isDragging) vtSeekFromX(e.touches[0].clientX); }, {passive: true});
document.addEventListener('touchend', () => { vt.isDragging = false; });
}
// startVideo 由"进入教程"触发:进入即自动播放
function startVideo() {
vtInit();
vt.elapsed = 0;
vt.hasFinished = false;
vtStopTimer();
vt.status = 'playing';
vtStartTimer();
vtRender();
}
// startVideo 由用户点击"进入教程"触发
// 商品选择
function selectProduct(el) {
document.querySelectorAll('.product-option').forEach(e => { e.style.borderColor='transparent'; e.style.background='var(--gray-50)'; });
el.style.borderColor='var(--primary)'; el.style.background='var(--primary-bg)';
document.getElementById('selectBtn').style.display='block';
}
// 比价模拟
// ===== 截图演示比价流程 =====
// spotlight: { top, left, width, height } 相对于375x812设备
// finger: { top, left } 手指位置
// guidePos: { top, left } 文案位置, 'above' = 聚光灯上方居中
const demoSteps = [
// ===== 一键领券前置流程 =====
{
img: 'assets/onboarding/index/platform-flows/meituan/1-home.jpg',
mode: 'couponPrompt',
},
// TODO: 替换为真实的美团神券中心 / 饿了么神券中心截图
{
img: 'assets/onboarding/index/platform-flows/meituan/1-home.jpg',
mode: 'agentFlow',
agentText: '正在打开美团神券中心...',
platforms: { 'meituan-waimai': 'active' },
},
{
img: 'assets/onboarding/index/platform-flows/meituan/1-home.jpg',
mode: 'agentFlow',
agentText: '已领取美团红包',
couponDelta: 8,
platforms: { 'meituan-waimai': 'done' },
},
{
img: 'assets/onboarding/index/platform-flows/taobao/2-coupon-center.jpg',
mode: 'agentFlow',
agentText: '正在领取淘宝神券...',
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'active' },
},
{
img: 'assets/onboarding/index/platform-flows/taobao/3-coupon-claimed.jpg',
mode: 'agentFlow',
agentText: '已领取淘宝神券',
couponDelta: 12,
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'done' },
},
{
img: 'assets/onboarding/index/platform-flows/meituan/1-home.jpg',
mode: 'couponDone',
},
// ===== 比价引导流程 =====
{
img: 'assets/onboarding/index/platform-flows/meituan/1-home.jpg',
guide: '• 点击下方店铺,并找到商品准备比价',
guideTitle: '找到待比价的商品',
kickoff: true,
spotlight: { top: 517, left: 7, width: 361, height: 125 },
finger: { top: 590, left: 312 },
},
{
img: 'assets/onboarding/index/platform-flows/meituan/2-shop-top.jpg',
guide: '下滑找到待比价的商品',
mode: 'swipe',
},
{
img: 'assets/onboarding/index/waimai-guide/add-to-cart.png',
guide: '点击「+」加入购物车',
spotlight: { top: 174, left: 70, width: 301, height: 152 },
plusFocus: { top: 295, left: 352 },
finger: { top: 306, left: 274, size: 60, plusGuide: true },
guideTop: 344,
guideLeft: 187.5,
clickTarget: 'plus',
spotlightRadius: 8,
spotlightOpacity: .62,
},
{
img: 'assets/onboarding/index/waimai-guide/missing-required.png',
guide: '点击添加必选品',
spotlight: { top: 719, left: 0, width: 375, height: 93 },
plusFocus: { top: 755, left: 311, width: 128, height: 54, shape: 'rect' },
finger: { top: 771, left: 236, size: 60, plusGuide: true },
guideTop: 664,
guideLeft: 187.5,
clickTarget: 'plus',
spotlightRadius: 0,
spotlightOpacity: .62,
},
{
img: 'assets/onboarding/index/waimai-guide/add-required-item.png',
guide: '点击「+」添加',
spotlight: { top: 306, left: 76, width: 296, height: 134 },
plusFocus: { top: 403, left: 352, width: 46, height: 46, shape: 'circle' },
finger: { top: 414, left: 274, size: 60, plusGuide: true },
guideTop: 458,
guideLeft: 187.5,
clickTarget: 'plus',
spotlightRadius: 8,
spotlightOpacity: .62,
},
{
img: 'assets/onboarding/index/platform-flows/meituan/5-shop-cart.jpg',
guide: '点击去结算',
spotlight: { top: 719, left: 0, width: 375, height: 93 },
plusFocus: { top: 790, left: 315, width: 112, height: 46, shape: 'pill' },
finger: { top: 735, left: 320, size: 60, imgTransform: 'rotate(-90deg)' },
guideTop: 660,
guideLeft: 187.5,
clickTarget: 'plus',
spotlightRadius: 0,
spotlightOpacity: .62,
},
{
img: 'assets/onboarding/index/platform-flows/meituan/6-checkout.jpg',
guide: '请选择待使用的红包',
spotlight: { top: 636, left: 14, width: 347, height: 48 },
finger: { top: 660, left: 315, size: 60, imgTransform: 'rotate(6deg)' },
guideTop: 580,
guideLeft: 187.5,
spotlightRadius: 20,
spotlightOpacity: .62,
},
{
img: 'assets/onboarding/index/platform-flows/meituan/7-coupon-select.jpg',
guide: '点击确认',
spotlight: { top: 752, left: 9, width: 354, height: 55 },
plusFocus: { top: 780, left: 312, width: 104, height: 52, shape: 'confirm' },
finger: { top: 729, left: 313, size: 60, imgTransform: 'rotate(-90deg)' },
guideTop: 696,
guideLeft: 187.5,
clickTarget: 'plus',
spotlightRadius: 24,
spotlightOpacity: .62,
},
{
img: 'assets/onboarding/index/platform-flows/meituan/8-checkout-coupon.jpg',
mode: 'startCompare',
},
// ===== 淘宝 Agent 自动比价流程 =====
// 比价阶段:美团已用作起点(done),淘宝为当前 active 平台
{
img: 'assets/onboarding/index/platform-flows/taobao/1-home.jpg',
mode: 'agentFlow',
agentText: '正在打开淘宝...',
retentionPercent: 11,
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'active' },
},
{
img: 'assets/onboarding/index/platform-flows/taobao/4-search.jpg',
mode: 'agentFlow',
agentText: '正在搜索「窑鸡王」...',
retentionPercent: 26,
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'active' },
},
{
img: 'assets/onboarding/index/platform-flows/taobao/5-shop-top.jpg',
mode: 'agentFlow',
agentText: '正在浏览菜单...',
retentionPercent: 40,
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'active' },
},
{
img: 'assets/onboarding/index/platform-flows/taobao/6-shop-item.jpg',
mode: 'agentFlow',
agentText: '搜索同款商品中...',
retentionPercent: 55,
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'active' },
},
{
img: 'assets/onboarding/index/platform-flows/taobao/7-spec.jpg',
mode: 'agentFlow',
agentText: '正在确认商品规格...',
retentionPercent: 70,
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'active' },
},
{
img: 'assets/onboarding/index/platform-flows/taobao/8-checkout.jpg',
mode: 'agentFlow',
agentText: '正在应用所有优惠...',
retentionPercent: 85,
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'active' },
},
{
img: 'assets/onboarding/index/platform-flows/taobao/result.jpg',
mode: 'agentFlow',
agentText: '正在获取淘宝最终价格',
retentionPercent: 100,
last: true,
platforms: { 'meituan-waimai': 'done', 'taobao-shanguang': 'active' },
},
];
let demoStep = 0;
let couponTotal = 0;
let pendingDemoStartStep = null;
function startComparison() {
demoStep = pendingDemoStartStep != null ? pendingDemoStartStep : 0;
pendingDemoStartStep = null;
couponTotal = 0;
const numEl = document.getElementById('demoCouponNum');
if (numEl) numEl.textContent = '0';
renderDemoStep();
}
// 距下一个 0 点倒计时(每秒更新 v6-expiry 三个 time-box
let __midnightTimer = null;
function startMidnightCountdown() {
function pad(n) { return n < 10 ? '0' + n : '' + n; }
function tick() {
var hEl = document.getElementById('cdH');
var mEl = document.getElementById('cdM');
var sEl = document.getElementById('cdS');
if (!hEl || !mEl || !sEl) { stopMidnightCountdown(); return; }
var now = new Date();
var midnight = new Date(now);
midnight.setHours(24, 0, 0, 0);
var diff = Math.max(0, Math.floor((midnight - now) / 1000));
var h = Math.floor(diff / 3600);
var m = Math.floor((diff % 3600) / 60);
var s = diff % 60;
hEl.textContent = pad(h);
mEl.textContent = pad(m);
sEl.textContent = pad(s);
}
stopMidnightCountdown();
tick();
__midnightTimer = setInterval(tick, 1000);
}
function stopMidnightCountdown() {
if (__midnightTimer) { clearInterval(__midnightTimer); __midnightTimer = null; }
}
// 高频弹跳:N 次连续 +1,每次都触发数字弹跳 + 多向粒子喷射(F2 样式)
// K 方案:从 0 缓出到目标值(用于"领券完成"弹窗的总数和分平台数)
function runKCountUp(elId, target, duration, delay) {
const el = document.getElementById(elId);
if (!el) return;
el.textContent = '0';
setTimeout(() => {
let start = null;
const step = ts => {
if (!start) start = ts;
const p = Math.min((ts - start) / duration, 1);
const eased = 1 - Math.pow(1 - p, 3);
el.textContent = Math.round(target * eased);
if (p < 1) requestAnimationFrame(step);
};
requestAnimationFrame(step);
}, delay || 0);
}
function playCouponDelta(delta) {
const numEl = document.getElementById('demoCouponNum');
const numBlock = document.getElementById('demoCouponNumBlock');
if (!numEl || !numBlock) return;
const tickInterval = 130;
for (let i = 0; i < delta; i++) {
setTimeout(() => {
couponTotal += 1;
numEl.textContent = couponTotal;
numEl.classList.remove('coupon-num-pop');
void numEl.offsetWidth;
numEl.classList.add('coupon-num-pop');
const particleCount = 1 + Math.floor(Math.random() * 2);
for (let k = 0; k < particleCount; k++) {
const angle = -Math.PI / 2 + (Math.random() - 0.5) * 1.6;
const dist = 26 + Math.random() * 18;
const p = document.createElement('div');
p.className = 'coupon-plus-particle';
p.textContent = '+1';
p.style.setProperty('--dx', Math.cos(angle) * dist + 'px');
p.style.setProperty('--dy', Math.sin(angle) * dist + 'px');
numBlock.appendChild(p);
setTimeout(() => p.remove(), 900);
}
}, i * tickInterval);
}
}
function renderDemoStep() {
const s = demoSteps[demoStep];
document.getElementById('demoScreenshot').src = s.img;
// AI 接管橘黄光晕:仅在 agentFlow 阶段(块 1 一键领券 + 块 3 自动比价)开启
const aiGlow = document.getElementById('aiTakeoverGlow');
if (aiGlow) aiGlow.classList.toggle('active', s.mode === 'agentFlow');
// 进度条已移除(原 demoProgressBar),不再做按 step 计算宽度的逻辑
const spot = document.getElementById('demoSpotlight');
const plusFocus = document.getElementById('demoPlusFocus');
const finger = document.getElementById('demoFinger');
const guide = document.getElementById('demoGuide');
const guideText = document.getElementById('demoGuideText');
const kickoff = document.getElementById('demoKickoff');
const swipeOverlay = document.getElementById('demoSwipeOverlay');
const startOverlay = document.getElementById('demoStartOverlay');
const agentOverlay = document.getElementById('demoAgentOverlay');
const couponPrompt = document.getElementById('demoCouponPrompt');
const couponDoneToast = document.getElementById('demoCouponDoneToast');
// 先全部隐藏
swipeOverlay.style.display = 'none';
startOverlay.style.display = 'none';
agentOverlay.style.display = 'none';
couponPrompt.style.display = 'none';
couponDoneToast.style.display = 'none';
kickoff.style.display = 'none';
plusFocus.style.display = 'none';
plusFocus.className = 'demo-plus-focus';
plusFocus.style.width = '46px';
plusFocus.style.height = '46px';
finger.classList.remove('demo-plus-finger');
finger.style.width = '58px';
finger.style.height = '58px';
finger.style.zIndex = '15';
const fingerImg = finger.querySelector('.gesture-hand-img');
fingerImg.style.transform = '';
// ===== 一键领券弹窗 =====
if (s.mode === 'couponPrompt') {
spot.style.display = 'none';
finger.style.display = 'none';
guide.style.display = 'none';
renderCouponPromptPlatforms();
couponPrompt.style.display = 'flex';
return;
}
// ===== 一键领券完成 toast =====
if (s.mode === 'couponDone') {
spot.style.display = 'none';
finger.style.display = 'none';
guide.style.display = 'none';
// home 模式:toast 应展示在首页背景之上,先切回 home 再弹
if (homeClaimMode) {
go('home');
}
couponDoneToast.style.display = 'block';
// 0 → N 计数动画;不自动消失,等用户点击 CTA 才推进
runKCountUp('demoCouponDoneCount', couponTotal || 20, 1500, 200);
// 启动距 0 点倒计时(每秒滴答)
startMidnightCountdown();
return;
}
if (s.mode === 'swipe') {
spot.style.display = 'none';
finger.style.display = 'none';
guide.style.display = 'none';
swipeOverlay.style.display = 'flex';
swipeOverlay.querySelector('.swipe-text').textContent = s.guide;
return;
}
if (s.mode === 'startCompare') {
spot.style.display = 'none';
finger.style.display = 'none';
guide.style.display = 'none';
startOverlay.style.display = 'block';
return;
}
if (s.mode === 'agentFlow') {
spot.style.display = 'none';
finger.style.display = 'none';
guide.style.display = 'none';
agentOverlay.style.display = 'block';
// 块 1(领券) vs 块 3(比价)切换浮窗形态
const firstSpotlightIdx = demoSteps.findIndex(d => d.spotlight || d.mode === 'swipe' || d.mode === 'startCompare');
const inCouponPhase = firstSpotlightIdx > 0 && demoStep < firstSpotlightIdx;
const agentFloat = document.getElementById('demoAgentFloat');
const comparingFloat = document.getElementById('demoComparingFloat');
if (inCouponPhase) {
// 块 1:领券阶段 → 显示已领券计数浮窗(显式 flex,避免清掉 inline display:flex
if (agentFloat) agentFloat.style.display = 'flex';
if (comparingFloat) comparingFloat.style.display = 'none';
document.getElementById('demoAgentText').textContent = s.agentText;
if (s.couponDelta) playCouponDelta(s.couponDelta);
renderFloatPlatforms('demoAgentPlatforms', s.platforms);
} else {
// 块 3:比价阶段 → 显示合并比价浮窗(无已领券计数,红包激励融入副文案动效)
if (agentFloat) agentFloat.style.display = 'none';
if (comparingFloat) comparingFloat.style.display = 'block';
document.getElementById('demoComparingText').textContent = s.agentText;
if (s.retentionPercent != null) {
const fill = document.getElementById('demoComparingFill');
if (fill) fill.style.width = s.retentionPercent + '%';
}
renderFloatPlatforms('demoComparingPlatforms', s.platforms);
}
return;
}
// 默认:聚光灯模式
spot.style.display = 'block';
spot.style.top = s.spotlight.top + 'px';
spot.style.left = s.spotlight.left + 'px';
spot.style.width = s.spotlight.width + 'px';
spot.style.height = s.spotlight.height + 'px';
spot.style.borderRadius = (s.spotlightRadius ? s.spotlightRadius + 'px' : '12px');
spot.style.boxShadow = '0 0 0 9999px rgba(0,0,0,' + (s.spotlightOpacity != null ? s.spotlightOpacity : .5) + ')';
spot.style.pointerEvents = s.clickTarget === 'plus' ? 'none' : 'auto';
spot.style.cursor = s.clickTarget === 'plus' ? 'default' : 'pointer';
if (s.plusFocus) {
plusFocus.style.display = 'block';
plusFocus.style.top = s.plusFocus.top + 'px';
plusFocus.style.left = s.plusFocus.left + 'px';
plusFocus.style.width = (s.plusFocus.width || 46) + 'px';
plusFocus.style.height = (s.plusFocus.height || 46) + 'px';
plusFocus.classList.add(s.plusFocus.shape || 'circle');
if (s.clickTarget === 'plus') plusFocus.classList.add('clickable');
}
if (s.finger) {
finger.style.display = 'block';
finger.style.top = s.finger.top + 'px';
finger.style.left = s.finger.left + 'px';
finger.style.width = (s.finger.size || 58) + 'px';
finger.style.height = (s.finger.size || 58) + 'px';
if (s.finger.plusGuide) {
finger.classList.add('demo-plus-finger');
finger.style.transform = 'none';
} else {
finger.style.transform = s.finger.mirror ? 'scaleX(-1)' : 'none';
if (s.finger.imgTransform) {
fingerImg.style.transform = s.finger.imgTransform;
}
}
} else {
finger.style.display = 'none';
}
if (s.kickoff && s.guide) {
// Kickoff 黄卡:标题 + 虚线 + 圆形箭头 + 引线
guide.style.display = 'none';
document.getElementById('demoKickoffTitle').textContent = s.guideTitle || '下一步,比出全网最低价';
document.getElementById('demoKickoffText').textContent = s.guide;
kickoff.style.display = 'block';
// 让卡片底部到引线落点正好对齐 spotlight 顶部
const cardEl = kickoff.querySelector('.kickoff-card');
const leaderH = 36;
const cardH = cardEl.offsetHeight;
kickoff.style.top = (s.spotlight.top - leaderH - cardH) + 'px';
} else if (s.guide) {
guide.style.display = 'block';
guideText.textContent = s.guide;
var bubbleTopY = null;
if (s.guideTop != null) {
guide.style.top = s.guideTop + 'px';
guide.style.left = (s.guideLeft != null ? s.guideLeft + 'px' : '50%');
guide.style.right = 'auto';
guide.style.transform = 'translateX(-50%)';
bubbleTopY = s.guideTop;
} else if (s.guidePos === 'above') {
const offset = s.guideOffset || -50;
guide.style.top = (s.spotlight.top + offset) + 'px';
bubbleTopY = s.spotlight.top + offset;
if (s.guideAlign === 'spotlight') {
var cx = s.spotlight.left + s.spotlight.width / 2;
// 渲染后检查是否超出屏幕,先设置位置
guide.style.left = cx + 'px';
guide.style.transform = 'translateX(-50%)';
// 延迟检查边界并修正
setTimeout(function() {
var rect = guide.getBoundingClientRect();
var device = document.getElementById('app').getBoundingClientRect();
if (rect.right > device.right - 8) {
guide.style.left = 'auto';
guide.style.right = '8px';
guide.style.transform = 'none';
}
}, 10);
} else {
guide.style.left = '50%';
guide.style.right = 'auto';
guide.style.transform = 'translateX(-50%)';
}
}
// 动态定位 leaderdot 中心贴在 spotlight 最近的边缘上
var leader = guide.querySelector('.demo-guide-leader');
if (leader) {
leader.classList.remove('dir-down', 'dir-up');
leader.style.height = '';
leader.style.display = '';
if (bubbleTopY != null && s.spotlight) {
var bubbleH = guideText.offsetHeight;
var bubbleBottomY = bubbleTopY + bubbleH;
var targetTop = s.spotlight.top;
var targetBottom = s.spotlight.top + s.spotlight.height;
if (bubbleBottomY <= targetTop) {
leader.classList.add('dir-down');
leader.style.height = Math.max(0, targetTop - bubbleBottomY + 1) + 'px';
} else if (bubbleTopY >= targetBottom) {
leader.classList.add('dir-up');
leader.style.height = Math.max(0, bubbleTopY - targetBottom + 1) + 'px';
} else {
leader.style.display = 'none';
}
} else {
leader.style.display = 'none';
}
}
} else {
guide.style.display = 'none';
}
}
function demoPlusHit(event) {
event.stopPropagation();
const el = event.currentTarget;
const rect = el.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
if (el.classList.contains('circle')) {
const radius = Math.min(rect.width, rect.height) / 2;
const dx = x - rect.width / 2;
const dy = y - rect.height / 2;
if ((dx * dx + dy * dy) > radius * radius) {
return;
}
}
if (el.classList.contains('pill')) {
const radius = rect.height / 2;
const leftCap = radius;
const rightCap = rect.width - radius;
const inCenter = x >= leftCap && x <= rightCap && y >= 0 && y <= rect.height;
const dx = x < leftCap ? x - leftCap : x - rightCap;
const dy = y - radius;
if (!inCenter && (dx * dx + dy * dy) > radius * radius) {
return;
}
}
if (el.classList.contains('confirm')) {
const leftRadius = 4;
const rightRadius = rect.height / 2;
const inMiddle = y >= 0 && y <= rect.height && x >= leftRadius && x <= rect.width - rightRadius;
const inLeft = x >= 0 && x < leftRadius && y >= leftRadius && y <= rect.height - leftRadius;
const inRight = x > rect.width - rightRadius && x <= rect.width && y >= 0 && y <= rect.height;
const dx = x - (rect.width - rightRadius);
const dy = y - rightRadius;
if (!inMiddle && !inLeft && (!inRight || (dx * dx + dy * dy) > rightRadius * rightRadius)) {
return;
}
}
demoNext();
}
function demoNext() {
// 离开 couponDone 弹窗时关掉倒计时
stopMidnightCountdown();
const s = demoSteps[demoStep];
// home 一键领券模式:couponDone toast CTA → 回首页并切换"已领"态
if (homeClaimMode && s && s.mode === 'couponDone') {
onHomeCouponClaimDone();
return;
}
if (s.last) {
renderCelebrationSavings();
renderResults('celebResults');
go('celebration');
return;
}
if (demoStep < demoSteps.length - 1) {
demoStep++;
renderDemoStep();
}
}
// ===== 一键领券弹窗按钮 =====
function onClaimCouponConfirm() {
// H4:确认 → 触发原生领券(权限校验→缺则 OEM 引导→前台服务领券)。[] = 全领(对齐原生 platformSelectable=false);
// 领券时用户在外卖 App,进度由原生悬浮窗展示(D2),H5 不接事件流、也不再跳原型 coupon 页。
const prompt = document.getElementById('demoCouponPrompt');
if (prompt) prompt.style.display = 'none';
if (window.SGBridge && typeof SGBridge.startCouponClaim === 'function') SGBridge.startCouponClaim([]);
}
// ===== 首页"一键领优惠"卡片:去领取 → 弹窗 → agent 领券 → 完成 → 回首页 =====
let homeClaimMode = false;
let homeCouponClaimed = false;
let homeCouponClaimSummary = null; // { total, platforms: [{ id, name, icon }] }
// 领券完成弹窗:内嵌平台 logo —— 从几个平台领的就显示几个(来源 homeCouponClaimSummary.platforms
function renderCouponDoneFromLogos() {
const el = document.getElementById('couponDoneFromLogos');
if (!el) return;
const platforms = (homeCouponClaimSummary && Array.isArray(homeCouponClaimSummary.platforms)) ? homeCouponClaimSummary.platforms : [];
el.innerHTML = platforms.map(function(p) {
return '<span class="lg-inline"><img src="' + p.icon + '" alt="' + (p.name || '') + '"></span>';
}).join('');
}
function onTriClaimClick() {
// H4:登录门槛(领券要归属用户)→ 未登录拉极光一键登录
if (window.SGBridge && typeof SGBridge.getToken === 'function' && !SGBridge.getToken()) { SGBridge.requestLogin(); return; }
// 今日已领 → 提示,不重复触发(完整「上次结算卡」回显留后续)
if (window.SGBridge && typeof SGBridge.getCouponClaimedToday === 'function' && SGBridge.getCouponClaimedToday()) {
if (typeof showToast === 'function') showToast('今日已领取~');
return;
}
// 弹领券确认弹窗:「一键自动领取」→ onClaimCouponConfirm → SGBridge.startCouponClaim
homeClaimMode = true;
couponTotal = 0;
renderCouponPromptPlatforms();
const prompt = document.getElementById('demoCouponPrompt');
if (prompt) prompt.style.display = 'flex';
}
function closeHomeCouponClaim() {
// 关闭弹窗 → 留在首页(保持未领状态)
const prompt = document.getElementById('demoCouponPrompt');
if (prompt) prompt.style.display = 'none';
homeClaimMode = false;
stopMidnightCountdown();
}
function onCouponPromptMaskClick(e) {
// 点击弹窗外侧蒙层关闭(home 模式才生效;点中卡片不关)
if (!homeClaimMode) return;
if (e.target && e.target.id === 'demoCouponPrompt') closeHomeCouponClaim();
}
function getStoredCouponAdRewardCoins() {
var reward = 0;
try {
reward = parseInt(localStorage.getItem('proto_couponAdRewardCoins') || '0', 10) || 0;
} catch(e) {}
return reward;
}
function showCouponAdRewardToast(amount) {
if (!amount || amount <= 0) return;
var existing = document.getElementById('adRewardToast');
if (existing) existing.remove();
var toast = document.createElement('div');
toast.id = 'adRewardToast';
toast.className = 'ad-reward-toast';
toast.setAttribute('aria-live', 'polite');
toast.setAttribute('aria-hidden', 'false');
for (var si = 1; si <= 6; si++) {
var spark = document.createElement('span');
spark.className = 'spark s' + si;
toast.appendChild(spark);
}
var icon = document.createElement('img');
icon.src = 'assets/welfare/index/checkin-coins-600.png';
icon.alt = '';
var amountEl = document.createElement('div');
amountEl.className = 'ad-reward-toast-amount';
amountEl.textContent = '+' + Math.round(amount);
var textEl = document.createElement('div');
textEl.className = 'ad-reward-toast-text';
textEl.textContent = '领券奖励已到账';
toast.appendChild(icon);
toast.appendChild(amountEl);
toast.appendChild(textEl);
document.getElementById('app').appendChild(toast);
requestAnimationFrame(function() { toast.classList.add('visible'); });
setTimeout(function() {
toast.classList.remove('visible');
toast.setAttribute('aria-hidden', 'true');
}, 1900);
setTimeout(function() {
if (toast.parentNode) toast.remove();
}, 2150);
}
function onHomeCouponClaimDone() {
// 用户点击结果 toast 的 CTA → 关 toast、持久化"已领"态、切首页卡片到"已领"展示
homeClaimMode = false;
homeCouponClaimed = true;
stopMidnightCountdown();
var couponAdRewardCoins = getStoredCouponAdRewardCoins();
const toast = document.getElementById('demoCouponDoneToast');
if (toast) toast.style.display = 'none';
// 如果是从 compare 跳回来的,summary 已经在 init 阶段从 localStorage 还原;否则现拼一个兜底值
if (!homeCouponClaimSummary) {
const platforms = (typeof getVisibleApps === 'function' ? getVisibleApps() : [])
.filter(a => a.installed)
.map(a => ({ id: a.id, name: a.name, icon: a.icon }));
homeCouponClaimSummary = {
total: couponTotal || 20,
platforms: platforms,
};
}
try {
localStorage.setItem('proto_homeCouponClaimed', 'true');
localStorage.setItem('proto_homeCouponClaimSummary', JSON.stringify(homeCouponClaimSummary));
localStorage.setItem('proto_home_coupon_claim_date', getTodayStr_home());
localStorage.removeItem('proto_showCouponDoneToast');
localStorage.removeItem('proto_couponAdRewardCoins');
} catch(e) {}
renderTriClaim();
showCouponAdRewardToast(couponAdRewardCoins);
// 收券后直接弹"选择常用平台"弹窗(图2),引导用户进入比价
if (typeof renderHomePickerPlatforms === 'function') renderHomePickerPlatforms();
var pickerMask = document.getElementById('homePickerMask');
if (pickerMask) pickerMask.classList.add('show');
}
// ===== 首页每日检查:权限提醒 + 领券提醒(每条每天最多 1 次)=====
function getTodayStr_home() {
var d = new Date();
return d.getFullYear() + '-' + String(d.getMonth()+1).padStart(2,'0') + '-' + String(d.getDate()).padStart(2,'0');
}
function runHomeDailyChecks() {
var today = getTodayStr_home();
// ① 权限提醒:任一权限关闭 → 弹一次 safetyModal
// 默认两个权限均为"开";只有用户/调试主动写 'off' 才视为关闭,模拟真实场景
var permRemindedToday = false;
try { permRemindedToday = localStorage.getItem('proto_home_perm_reminder_date') === today; } catch(e) {}
if (!permRemindedToday) {
var accOn = true, overlayOn = true;
try {
accOn = localStorage.getItem('proto_perm_accessibility') !== 'off';
overlayOn = localStorage.getItem('proto_perm_overlay') !== 'off';
} catch(e) {}
if (!accOn || !overlayOn) {
try { localStorage.setItem('proto_home_perm_reminder_date', today); } catch(e) {}
// 两个都关时优先提示无障碍(核心功能依赖项),让用户一次一个开
var permToShow = !accOn ? '无障碍' : '悬浮窗';
setTimeout(function() {
if (typeof openSafetyModal === 'function') openSafetyModal(permToShow);
}, 450);
return; // 权限弹窗优先,避免与领券弹窗叠层;领券推迟到明天
}
}
// ② 领券提醒:今日未领且今日未弹过 → 自动弹 onTriClaimClick 多平台领券窗
var couponRemindedToday = false;
var couponClaimedToday = false;
try {
couponRemindedToday = localStorage.getItem('proto_home_coupon_reminder_date') === today;
couponClaimedToday = localStorage.getItem('proto_home_coupon_claim_date') === today;
} catch(e) {}
if (!couponRemindedToday && !couponClaimedToday && !homeCouponClaimed) {
try { localStorage.setItem('proto_home_coupon_reminder_date', today); } catch(e) {}
setTimeout(function() {
if (typeof onTriClaimClick === 'function') onTriClaimClick();
}, 500);
}
}
// ===== LBS:仅手动点击 home「去授权」后弹系统询问 =====
const HOME_LOC_AUTH_MODE_KEY = 'proto_locationAuthMode'; // whileUsing / once / denied
const HOME_LOC_LEGACY_GRANTED_KEY = 'proto_locationGranted';
const HOME_LOC_ONCE_DATE_KEY = 'proto_location_once_date';
const HOME_LOC_SESSION_GRANTED_KEY = 'proto_location_session_granted';
const HOME_NEARBY_EMPTY_KEY = 'proto_home_nearby_empty';
function getHomeLocationMode() {
try {
var mode = localStorage.getItem(HOME_LOC_AUTH_MODE_KEY);
if (mode) return mode;
if (localStorage.getItem(HOME_LOC_LEGACY_GRANTED_KEY) === '1') return 'whileUsing';
} catch(e) {}
return '';
}
function isHomeLocationGranted() {
// H2a:优先读真·系统定位权限;无桥(浏览器无 bridge)兜底原 localStorage 模式。
try { if (window.SGBridge && typeof SGBridge.isLocationGranted === 'function') return !!SGBridge.isLocationGranted(); } catch(e) {}
var mode = getHomeLocationMode();
if (mode === 'whileUsing') return true;
if (mode === 'once') {
try {
return localStorage.getItem(HOME_LOC_ONCE_DATE_KEY) === getTodayStr_home() &&
sessionStorage.getItem(HOME_LOC_SESSION_GRANTED_KEY) === '1';
} catch(e) { return false; }
}
return false;
}
function setHomeLocationMode(mode) {
try {
if (mode === 'whileUsing') {
localStorage.setItem(HOME_LOC_AUTH_MODE_KEY, 'whileUsing');
localStorage.setItem(HOME_LOC_LEGACY_GRANTED_KEY, '1');
sessionStorage.setItem(HOME_LOC_SESSION_GRANTED_KEY, '1');
} else if (mode === 'once') {
localStorage.setItem(HOME_LOC_AUTH_MODE_KEY, 'once');
localStorage.setItem(HOME_LOC_ONCE_DATE_KEY, getTodayStr_home());
localStorage.removeItem(HOME_LOC_LEGACY_GRANTED_KEY);
sessionStorage.setItem(HOME_LOC_SESSION_GRANTED_KEY, '1');
} else if (mode === 'denied') {
localStorage.setItem(HOME_LOC_AUTH_MODE_KEY, 'denied');
localStorage.removeItem(HOME_LOC_LEGACY_GRANTED_KEY);
localStorage.removeItem(HOME_LOC_ONCE_DATE_KEY);
sessionStorage.removeItem(HOME_LOC_SESSION_GRANTED_KEY);
}
} catch(e) {}
}
function openLocSysPopup() {
var s = document.getElementById('locSysPopup');
if (s) s.style.display = 'flex';
}
function goAuthLocation() {
// H2a:接真·系统定位权限窗(永久拒绝由原生跳设置);无桥兜底原型假弹窗。
if (window.SGBridge && typeof SGBridge.requestLocationPermission === 'function') { SGBridge.requestLocationPermission(); return; }
openLocSysPopup();
}
// 回前台(定位授权窗 / 设置返回)重核授权态并刷新 feed 区(纯 H5,不依赖原生事件)。
document.addEventListener('visibilitychange', function(){ if (document.visibilityState === 'visible') { try { applyLocationState(); } catch(e){} try { renderLastCompareBanner(); } catch(e){} } });
// 「上次比价」横幅:进页拉一次 + 回前台(上面 visibilitychange)再拉(4 分钟窗口随时间过期;在外卖 App 比完价回来即出)
document.addEventListener('DOMContentLoaded', function(){ try { renderLastCompareBanner(); } catch(e){} });
function isHomeNearbyEmpty(feed) {
try {
var params = new URLSearchParams(location.search);
if (params.get('nearbyEmpty') === '1') return true;
if (params.get('nearbyEmpty') === '0') return false;
if (localStorage.getItem(HOME_NEARBY_EMPTY_KEY) === '1') return true;
} catch(e) {}
if (!feed) return false;
if (feed.dataset && feed.dataset.empty === 'true') return true;
return !feed.querySelector('.feed-card:not([hidden])');
}
function syncHomeFeedEmptyHeight(activeEmpty) {
if (!activeEmpty) return;
activeEmpty.style.minHeight = '';
requestAnimationFrame(function() {
var tab = document.querySelector('#home .tab-bar');
var need = (tab ? tab.getBoundingClientRect().top : window.innerHeight) - activeEmpty.getBoundingClientRect().top;
if (need > activeEmpty.offsetHeight) activeEmpty.style.minHeight = need + 'px';
});
}
// feed 区定位空态:未授权显示授权空态;已授权但无附近推荐显示比价空态;有推荐时切回 feed
// 默认值沿用项目 localStorage 约定:未显式授权即视为未授权 → 首屏即空态(点右下角「重置」清空 localStorage 可回到空态重看)
let locationGranted = (typeof localStorage !== 'undefined' && localStorage.getItem('proto_locationGranted') === '1');
function applyLocationState() {
locationGranted = isHomeLocationGranted();
var empty = document.getElementById('homeFeedEmpty');
var noDealEmpty = document.getElementById('homeFeedNoDealEmpty');
var feed = document.querySelector('#home .home-feed');
var sort = document.querySelector('#home .feed-sort');
if (!empty || !feed) return;
var nearbyEmpty = locationGranted && isHomeNearbyEmpty(feed);
empty.style.display = locationGranted ? 'none' : 'flex';
if (noDealEmpty) noDealEmpty.style.display = nearbyEmpty ? 'flex' : 'none';
feed.style.display = (locationGranted && !nearbyEmpty) ? '' : 'none';
if (sort) sort.style.display = ''; // 排序三按键(智能推荐/距离最近/销量最高)始终保留,不被空态替换
// 空态时去掉 feed 卡框(cap 帽檐变体)的金色描边,避免露出横/竖线
var wrap = document.querySelector('#home .home-feed-wrap');
if (wrap) wrap.style.borderColor = (locationGranted && !nearbyEmpty) ? '' : 'transparent';
// 空态时让白色 feed 区撑满到底栏,下方不留灰
if (!locationGranted) {
var fl0 = document.getElementById('homeFeedLoading'); if (fl0) fl0.style.display = 'none';
var fd0 = document.getElementById('homeFeedDegraded'); if (fd0) fd0.style.display = 'none';
syncHomeFeedEmptyHeight(empty); return;
}
empty.style.minHeight = '';
if (nearbyEmpty && noDealEmpty) syncHomeFeedEmptyHeight(noDealEmpty);
else if (noDealEmpty) noDealEmpty.style.minHeight = '';
// H2b: 已授权(未授权上面已 return)→ 首次拉 feed / 坐标变化刷新;loading/empty/degraded 由 HomeFeedStates 接管
if (window.HomeFeed) {
if (!HomeFeed.state.loadedOnce && !HomeFeed.state.loading) HomeFeed.loadFirst();
else HomeFeed.refreshIfCoordsChanged();
}
}
document.addEventListener('DOMContentLoaded', applyLocationState);
window.addEventListener('load', applyLocationState);
// 系统弹窗:仅由手动点击「去授权」呼出;遮罩只关闭;允许期间写入长期授权;允许本次只给当前会话;禁止保持未授权空态。
function closeLocSysPopup(action) {
var s = document.getElementById('locSysPopup');
if (s) s.style.display = 'none';
if (action === 'whileUsing') {
setHomeLocationMode('whileUsing');
applyLocationState();
} else if (action === 'once') {
setHomeLocationMode('once');
applyLocationState();
} else if (action === 'denied') {
setHomeLocationMode('denied');
applyLocationState();
}
}
function renderTriClaim() {
const card = document.getElementById('homeStatsCard');
if (!card) return;
const cta = card.querySelector('.tc-cta');
const sub = card.querySelector('.tc-sub');
if (homeCouponClaimed) {
card.classList.add('claimed');
if (cta) cta.textContent = '去查看';
if (sub) sub.textContent = 'AI 一键自动领';
} else {
card.classList.remove('claimed');
if (cta) cta.textContent = '去领取';
if (sub) sub.textContent = 'AI 一键自动领';
}
// 引导手势在两卡间转移:未领券 → 手在左卡「去领取」;已领券 → 手转到右卡「去点餐」
const entry = document.getElementById('homeCompareEntry');
if (entry) entry.classList.toggle('coupon-claimed', homeCouponClaimed);
}
// 「上次比价」数据改由后端 /api/v1/compare/records 实拉(见 renderLastCompareBanner);原写死 mock 已移除。
const LAST_COMPARE_VARIANTS = ['reference', 'tagright', 'topbar', 'pricebadge', 'mutedright'];
const LAST_COMPARE_PLATFORM_LOGOS = {
'淘宝': 'assets/shared/logos/taobao.png',
'美团': 'assets/shared/logos/meituan.png',
'京东': 'assets/shared/logos/jd.png',
'淘宝': 'assets/shared/logos/taobao.png',
'美团': 'assets/shared/logos/meituan.png',
'京东': 'assets/shared/logos/jd.png'
};
function getLastCompareVariant() {
const params = new URLSearchParams(location.search);
const value = params.get('lastcompare') || (variants['last-compare'] && variants['last-compare'].current) || 'reference';
return LAST_COMPARE_VARIANTS.includes(value) ? value : 'reference';
}
function renderLastCompareBanner() {
const banner = document.getElementById('lastCompareBanner');
if (!banner) return;
const token = (window.SGBridge && SGBridge.getToken && SGBridge.getToken()) || '';
if (!token) { banner.style.display = 'none'; return; } // 未登录 → 隐藏
fetch('/api/v1/compare/records?limit=5', { headers: { 'Authorization': 'Bearer ' + token } })
.then(function (r) { return r.ok ? r.json() : null; })
.then(function (d) {
const rec = pickFreshLastCompare(d && d.items);
if (!rec) { banner.style.display = 'none'; return; } // 无成功 / 超 4 分钟窗口 / 401 → 隐藏
banner.style.display = '';
fillLastCompareBanner(lastCompareToSummary(rec));
})
.catch(function () { banner.style.display = 'none'; });
}
// 取最近一条成功比价并卡 4 分钟新鲜窗口(records 按 created_at 倒序,首条成功即最新成功)。
function pickFreshLastCompare(items) {
if (!Array.isArray(items)) return null;
for (let i = 0; i < items.length; i++) {
const it = items[i];
if (it && it.status === 'success') {
const t = Date.parse(it.created_at);
if (isNaN(t)) return null;
const age = Date.now() - t; // 容 1 分钟时钟偏差(同原生 toFreshSummary)
return (age >= -60000 && age <= 4 * 60 * 1000) ? it : null;
}
}
return null;
}
// 后端 ComparisonRecordOut → 横幅摘要(对齐原生 LastCompareSummary)。
function lastCompareToSummary(rec) {
return {
shop: rec.store_name || '',
items: (rec.items || []).map(function (it) { return it && it.name; }).filter(Boolean),
bestPlatform: rec.best_platform_name || '',
bestPrice: rec.best_price_cents != null ? rec.best_price_cents / 100 : 0,
};
}
function fillLastCompareBanner(record) {
const banner = document.getElementById('lastCompareBanner');
if (!banner) return;
const shopEl = document.getElementById('lastCompareShop');
const dishEl = document.getElementById('lastCompareDish');
const ageEl = document.getElementById('lastCompareAge');
const platformWrap = document.getElementById('lastComparePlatformWrap');
const platformEl = document.getElementById('lastComparePlatform');
const platformLogo = document.getElementById('lastComparePlatformLogo');
const priceEl = document.getElementById('lastComparePrice');
const noteEl = document.getElementById('lastCompareNote');
const statusEl = banner.querySelector('.lcb-status');
const dishes = Array.isArray(record.items) && record.items.length
? record.items
: (record.item ? [record.item] : []);
const dishText = dishes.slice(0, 2).filter(Boolean).join('、');
const variant = getLastCompareVariant();
const platform = record.bestPlatform || record.sourcePlatform || '淘宝';
const price = Number(record.bestPrice || record.sourcePrice || 0);
const priceText = price ? price.toFixed(1) : '';
const priceParts = priceText ? priceText.split('.') : [];
if (shopEl) shopEl.textContent = record.shop || '上次比价店铺';
if (dishEl) dishEl.textContent = dishText || '查看比价结果';
if (ageEl) ageEl.textContent = '上次比价';
if (statusEl) statusEl.textContent = '';
if (platformEl) platformEl.textContent = platform;
if (platformLogo) platformLogo.src = LAST_COMPARE_PLATFORM_LOGOS[platform] || LAST_COMPARE_PLATFORM_LOGOS['淘宝'];
if (priceEl) {
priceEl.innerHTML = price
? '<span class="lcb-price-currency">¥</span><span class="lcb-price-int">' + priceParts[0] + '</span><span class="lcb-price-dec">.' + (priceParts[1] || '0') + '</span>'
: '';
}
if (platformWrap) platformWrap.style.display = price ? '' : 'none';
if (noteEl) noteEl.textContent = '去查看';
banner.classList.remove('lcb-v-card', 'lcb-v-platform', 'lcb-v-dish', 'lcb-v-strong', 'lcb-v-summary', 'lcb-v-price', 'lcb-v-ticket', 'lcb-v-split', 'lcb-v-leftprice', 'lcb-v-reference', 'lcb-v-tagright', 'lcb-v-topbar', 'lcb-v-pricebadge', 'lcb-v-mutedright');
banner.classList.add('lcb-v-reference');
if (variant !== 'reference') banner.classList.add('lcb-v-' + variant);
banner.classList.add('visible');
banner.setAttribute('aria-hidden', 'false');
// B6: 注入「查看详情」满宽 pill 到 pricebox 底部
const pricebox = document.getElementById('lastComparePriceBox');
if (pricebox && !pricebox.querySelector('.lcb-detail-cta')) {
const cta = document.createElement('button');
cta.className = 'lcb-detail-cta';
cta.type = 'button';
cta.tabIndex = -1;
cta.setAttribute('aria-hidden', 'true');
cta.textContent = '查看详情';
pricebox.appendChild(cta);
}
}
// ===== 一键领券弹窗 · 多版本(平台选择中部 + 视觉风格切换)=====
function toggleCouponPlatform(el) {
if (!el) return;
el.dataset.checked = el.dataset.checked === 'true' ? 'false' : 'true';
}
const couponPromptVariants = {
// V2 「快手糖果风」粉橙渐变外卡 + 白色内卡 3 列网格
v2: '' +
'<div style="background: linear-gradient(180deg,#FF6B9E 0%,#FF8FAB 55%,#FFAA8C 100%);border-radius: 24px;padding: 16px 16px 16px;width: 100%;box-shadow: 0 16px 48px rgba(255,80,120,.45);text-align: center;">' +
'<div style="width: 52px;height: 52px;border-radius: 13px;margin: 0 auto 8px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 17px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="font-size: 17px;font-weight: 900;color: #fff;line-height: 1.4;margin-bottom: 12px;text-shadow: 0 1px 2px rgba(0,0,0,.08);">您今日还有大额优惠券可领</div>' +
'<div style="background: linear-gradient(180deg,#fff 70%,#FFF1F4 100%);border-radius: 18px;padding: 12px 12px;margin-bottom: 16px;">' +
'<div style="font-size: 11px;color: var(--gray-600);font-weight: 600;margin-bottom: 12px;">已为您发现 <span style="color: #FF4081;font-weight: 800;">3</span> 个平台都有大额优惠</div>' +
'<div style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-pink" style="background: #fff;border: 2px solid #FF6B9E;border-radius: 12px;padding: 8px 4px 8px;position: relative;"><img src="' + arr[i+1] + '" style="width: 36px;height: 36px;border-radius: 8px;margin: 0 auto;display: block;"/><div style="font-size: 11px;font-weight: 700;color: var(--gray-900);margin-top: 6px;">' + arr[i] + '</div><span class="cp-check" style="position: absolute;top: 4px;right: 4px;width: 16px;height: 16px;border-radius: 50%;background: #FF4081;display: flex;align-items: center;justify-content: center;"><svg width="9" height="9" viewBox="0 0 9 9"><path d="M2 4.5l1.5 1.5 3-4" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span></div>'; }, '') +
'</div>' +
'</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;width: 100%;background: linear-gradient(180deg,#FFE066,#FFD600);color: #1a1a1a;font-weight: 900;box-shadow: 0 6px 16px rgba(255,179,0,.5);" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>',
// V3 「品牌黄横向选择卡」黄→白渐变外卡 + 3 张并排选择卡(琥珀边框态)+ 黄色品牌按钮(加深收尾保对比)
v3: '' +
'<div style="background: linear-gradient(180deg,#FFE066 0%,#FFF3C4 50%,#FFFDF5 100%);border-radius: 24px;padding: 24px 16px 16px;width: 100%;box-shadow: 0 16px 48px rgba(255,193,7,.4);text-align: center;">' +
'<div style="width: 52px;height: 52px;border-radius: 13px;margin: 0 auto 8px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 17px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="font-size: 18px;font-weight: 900;color: var(--gray-900);line-height: 1.4;margin-bottom: 12px;">您今日还有大额优惠券可领</div>' +
'<div style="background: #fff;border-radius: 18px;padding: 12px 8px;margin-bottom: 8px;box-shadow: 0 2px 12px rgba(255,193,7,.15);">' +
'<div style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 2px solid #FFB300;border-radius: 14px;padding: 12px 4px 8px;position: relative;"><img src="' + arr[i+1] + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;"/><div style="font-size: 12px;font-weight: 700;color: var(--gray-900);margin-top: 8px;">' + arr[i] + '</div><span class="cp-check" style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FFB300;display: flex;align-items: center;justify-content: center;"><svg width="10" height="10" viewBox="0 0 9 9"><path d="M2 4.5l1.5 1.5 3-4" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span></div>'; }, '') +
'</div>' +
'</div>' +
'<div style="font-size: 12px;color: var(--gray-500);font-weight: 500;margin-bottom: 12px;">将在以上平台领取</div>' +
'<button class="btn btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;background: linear-gradient(180deg,#FFD600 0%,#FFA000 100%);color: #1a1a1a;font-weight: 900;box-shadow: 0 8px 22px rgba(255,140,0,.5);border: none;" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>',
// V4 「黄色品牌风」白底外卡 + 纵向 3 行平台列表 + 黄色 toggle 开关
v4: '' +
'<div style="background: #fff;border-radius: 24px;padding: 24px 16px 16px;width: 100%;text-align: center;box-shadow: 0 16px 48px rgba(0,0,0,.3);border-top: 6px solid #FFD600;">' +
'<div style="width: 56px;height: 56px;border-radius: 14px;margin: 0 auto 12px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 18px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="font-size: 18px;font-weight: 800;color: var(--gray-900);line-height: 1.4;margin-bottom: 12px;">您今日还有大额优惠券可领</div>' +
'<div style="background: var(--gray-100);border-radius: 14px;padding: 8px;margin-bottom: 16px;display: flex;flex-direction: column;gap: 6px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 1.5px solid #FFD600;border-radius: 12px;padding: 8px 12px;display: flex;align-items: center;gap: 8px;text-align: left;"><img src="' + arr[i+1] + '" style="width: 36px;height: 36px;border-radius: 8px;flex-shrink: 0;"/><div style="flex: 1;font-size: 14px;font-weight: 700;color: var(--gray-900);">' + arr[i] + '</div><span class="cp-toggle" style="position: relative;width: 36px;height: 20px;border-radius: 8px;background: #FFD600;flex-shrink: 0;transition: background .2s;"><span style="position: absolute;top: 2px;left: 2px;width: 16px;height: 16px;border-radius: 50%;background: #fff;box-shadow: 0 1px 3px rgba(0,0,0,.2);transform: translateX(16px);transition: transform .2s;"></span></span></div>'; }, '') +
'</div>' +
'<button class="btn btn-primary btn-lg" style="border-radius: 24px;font-size: 16px;height: 48px;width: 100%;" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>',
// ===== V3 副标题处理方案 a~e(仅副标题区域不同,其他与 V3 一致)=====
// V3a 「机制副标题」副标题改讲"AI 自动 · 跨平台"机制,13px / 600 / 暗金
v3a: '' +
'<div style="background: linear-gradient(180deg,#FFE066 0%,#FFF3C4 50%,#FFFDF5 100%);border-radius: 24px;padding: 24px 16px 16px;width: 100%;box-shadow: 0 16px 48px rgba(255,193,7,.4);text-align: center;">' +
'<div style="width: 52px;height: 52px;border-radius: 13px;margin: 0 auto 8px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 17px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="font-size: 18px;font-weight: 900;color: var(--gray-900);line-height: 1.4;margin-bottom: 6px;">您今日还有大额优惠券可领</div>' +
'<div style="font-size: 13px;font-weight: 600;color: #8B6914;line-height: 1.5;margin-bottom: 12px;">AI 跨平台一键领取,无需手动切换 APP</div>' +
'<div style="background: #fff;border-radius: 18px;padding: 12px 8px;margin-bottom: 8px;box-shadow: 0 2px 12px rgba(255,193,7,.15);">' +
'<div style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 2px solid #FFB300;border-radius: 14px;padding: 12px 4px 8px;position: relative;"><img src="' + arr[i+1] + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;"/><div style="font-size: 12px;font-weight: 700;color: var(--gray-900);margin-top: 8px;">' + arr[i] + '</div><span class="cp-check" style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FFB300;display: flex;align-items: center;justify-content: center;"><svg width="10" height="10" viewBox="0 0 9 9"><path d="M2 4.5l1.5 1.5 3-4" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span></div>'; }, '') +
'</div>' +
'</div>' +
'<div style="font-size: 12px;color: var(--gray-500);font-weight: 500;margin-bottom: 12px;">将在以上平台领取</div>' +
'<button class="btn btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;background: linear-gradient(180deg,#FFD600 0%,#FFA000 100%);color: #1a1a1a;font-weight: 900;box-shadow: 0 8px 22px rgba(255,140,0,.5);border: none;" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>',
// V3b 「黄色胶囊标签」副标题文案不变,但用黄色 pill 包裹 + 🎁 emoji
v3b: '' +
'<div style="background: linear-gradient(180deg,#FFE066 0%,#FFF3C4 50%,#FFFDF5 100%);border-radius: 24px;padding: 24px 16px 16px;width: 100%;box-shadow: 0 16px 48px rgba(255,193,7,.4);text-align: center;">' +
'<div style="width: 52px;height: 52px;border-radius: 13px;margin: 0 auto 8px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 17px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="font-size: 18px;font-weight: 900;color: var(--gray-900);line-height: 1.4;margin-bottom: 8px;">您今日还有大额优惠券可领</div>' +
'<div style="display: inline-block;background: rgba(255,179,0,.18);color: #8B6914;font-size: 12px;font-weight: 700;padding: 4px 12px;border-radius: 32px;margin-bottom: 12px;">🎁 领取后立享更低好价</div>' +
'<div style="background: #fff;border-radius: 18px;padding: 12px 8px;margin-bottom: 8px;box-shadow: 0 2px 12px rgba(255,193,7,.15);">' +
'<div style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 2px solid #FFB300;border-radius: 14px;padding: 12px 4px 8px;position: relative;"><img src="' + arr[i+1] + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;"/><div style="font-size: 12px;font-weight: 700;color: var(--gray-900);margin-top: 8px;">' + arr[i] + '</div><span class="cp-check" style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FFB300;display: flex;align-items: center;justify-content: center;"><svg width="10" height="10" viewBox="0 0 9 9"><path d="M2 4.5l1.5 1.5 3-4" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span></div>'; }, '') +
'</div>' +
'</div>' +
'<div style="font-size: 12px;color: var(--gray-500);font-weight: 500;margin-bottom: 12px;">将在以上平台领取</div>' +
'<button class="btn btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;background: linear-gradient(180deg,#FFD600 0%,#FFA000 100%);color: #1a1a1a;font-weight: 900;box-shadow: 0 8px 22px rgba(255,140,0,.5);border: none;" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>',
// V3c 「特性 chip 行」三枚迷你 pill:AI 自动 · 30 秒 · 安全
v3c: '' +
'<div style="background: linear-gradient(180deg,#FFE066 0%,#FFF3C4 50%,#FFFDF5 100%);border-radius: 24px;padding: 24px 16px 16px;width: 100%;box-shadow: 0 16px 48px rgba(255,193,7,.4);text-align: center;">' +
'<div style="width: 52px;height: 52px;border-radius: 13px;margin: 0 auto 8px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 17px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="font-size: 18px;font-weight: 900;color: var(--gray-900);line-height: 1.4;margin-bottom: 8px;">您今日还有大额优惠券可领</div>' +
'<div style="display: flex;justify-content: center;gap: 6px;flex-wrap: wrap;margin-bottom: 12px;">' +
'<span style="background: rgba(255,179,0,.18);color: #8B6914;font-size: 11px;font-weight: 700;padding: 4px 8px;border-radius: 32px;">🤖 AI 自动</span>' +
'<span style="background: rgba(255,179,0,.18);color: #8B6914;font-size: 11px;font-weight: 700;padding: 4px 8px;border-radius: 32px;">⚡ 30 秒完成</span>' +
'<span style="background: rgba(255,179,0,.18);color: #8B6914;font-size: 11px;font-weight: 700;padding: 4px 8px;border-radius: 32px;">🛡️ 安全无感</span>' +
'</div>' +
'<div style="background: #fff;border-radius: 18px;padding: 12px 8px;margin-bottom: 8px;box-shadow: 0 2px 12px rgba(255,193,7,.15);">' +
'<div style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 2px solid #FFB300;border-radius: 14px;padding: 12px 4px 8px;position: relative;"><img src="' + arr[i+1] + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;"/><div style="font-size: 12px;font-weight: 700;color: var(--gray-900);margin-top: 8px;">' + arr[i] + '</div><span class="cp-check" style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FFB300;display: flex;align-items: center;justify-content: center;"><svg width="10" height="10" viewBox="0 0 9 9"><path d="M2 4.5l1.5 1.5 3-4" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span></div>'; }, '') +
'</div>' +
'</div>' +
'<div style="font-size: 12px;color: var(--gray-500);font-weight: 500;margin-bottom: 12px;">将在以上平台领取</div>' +
'<button class="btn btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;background: linear-gradient(180deg,#FFD600 0%,#FFA000 100%);color: #1a1a1a;font-weight: 900;box-shadow: 0 8px 22px rgba(255,140,0,.5);border: none;" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>',
// V3d 「顶部小标签 + 单标题」副标题取消,logo 下方加「今日福利」黄色小 pill
v3d: '' +
'<div style="background: linear-gradient(180deg,#FFE066 0%,#FFF3C4 50%,#FFFDF5 100%);border-radius: 24px;padding: 24px 16px 16px;width: 100%;box-shadow: 0 16px 48px rgba(255,193,7,.4);text-align: center;">' +
'<div style="width: 52px;height: 52px;border-radius: 13px;margin: 0 auto 8px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 17px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="display: inline-block;background: #FFD600;color: #1a1a1a;font-size: 11px;font-weight: 800;letter-spacing: .5px;padding: 2px 8px;border-radius: 32px;margin-bottom: 8px;">今日福利</div>' +
'<div style="font-size: 18px;font-weight: 900;color: var(--gray-900);line-height: 1.4;margin-bottom: 12px;">发现您有未领取的大额优惠券</div>' +
'<div style="background: #fff;border-radius: 18px;padding: 12px 8px;margin-bottom: 8px;box-shadow: 0 2px 12px rgba(255,193,7,.15);">' +
'<div style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 2px solid #FFB300;border-radius: 14px;padding: 12px 4px 8px;position: relative;"><img src="' + arr[i+1] + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;"/><div style="font-size: 12px;font-weight: 700;color: var(--gray-900);margin-top: 8px;">' + arr[i] + '</div><span class="cp-check" style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FFB300;display: flex;align-items: center;justify-content: center;"><svg width="10" height="10" viewBox="0 0 9 9"><path d="M2 4.5l1.5 1.5 3-4" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span></div>'; }, '') +
'</div>' +
'</div>' +
'<div style="font-size: 12px;color: var(--gray-500);font-weight: 500;margin-bottom: 12px;">将在以上平台领取</div>' +
'<button class="btn btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;background: linear-gradient(180deg,#FFD600 0%,#FFA000 100%);color: #1a1a1a;font-weight: 900;box-shadow: 0 8px 22px rgba(255,140,0,.5);border: none;" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>',
// V3e 「紧迫感副标题」副标题强调时效,⏰ + 暖红色,13px / 700
v3e: '' +
'<div style="background: linear-gradient(180deg,#FFE066 0%,#FFF3C4 50%,#FFFDF5 100%);border-radius: 24px;padding: 24px 16px 16px;width: 100%;box-shadow: 0 16px 48px rgba(255,193,7,.4);text-align: center;">' +
'<div style="width: 52px;height: 52px;border-radius: 13px;margin: 0 auto 8px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 17px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="font-size: 18px;font-weight: 900;color: var(--gray-900);line-height: 1.4;margin-bottom: 6px;">您今日还有大额优惠券可领</div>' +
'<div style="font-size: 13px;font-weight: 700;color: #D9534F;line-height: 1.5;margin-bottom: 12px;">⏰ 仅今日有效,错过自动失效</div>' +
'<div style="background: #fff;border-radius: 18px;padding: 12px 8px;margin-bottom: 8px;box-shadow: 0 2px 12px rgba(255,193,7,.15);">' +
'<div style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 2px solid #FFB300;border-radius: 14px;padding: 12px 4px 8px;position: relative;"><img src="' + arr[i+1] + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;"/><div style="font-size: 12px;font-weight: 700;color: var(--gray-900);margin-top: 8px;">' + arr[i] + '</div><span class="cp-check" style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FFB300;display: flex;align-items: center;justify-content: center;"><svg width="10" height="10" viewBox="0 0 9 9"><path d="M2 4.5l1.5 1.5 3-4" stroke="#fff" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" fill="none"/></svg></span></div>'; }, '') +
'</div>' +
'</div>' +
'<div style="font-size: 12px;color: var(--gray-500);font-weight: 500;margin-bottom: 12px;">将在以上平台领取</div>' +
'<button class="btn btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;background: linear-gradient(180deg,#FFD600 0%,#FFA000 100%);color: #1a1a1a;font-weight: 900;box-shadow: 0 8px 22px rgba(255,140,0,.5);border: none;" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>',
// V3f 「流程植入副标题」副标题植入产品流程:先领券 → AI 比价 → 找底价
v3f: '' +
'<div style="background: linear-gradient(180deg,#FFE066 0%,#FFF3C4 50%,#FFFDF5 100%);border-radius: 24px;padding: 24px 16px 16px;width: 100%;box-shadow: 0 16px 48px rgba(255,193,7,.4);text-align: center;">' +
'<div style="width: 52px;height: 52px;border-radius: 13px;margin: 0 auto 8px;background: #FFD600;display: flex;align-items: center;justify-content: center;box-shadow: 0 4px 14px rgba(255,179,0,.5);"><span style="font-size: 17px;font-weight: 900;color: #1a1a1a;line-height: 1.05;text-align: center;">傻瓜<br>比价</span></div>' +
'<div style="font-size: 18px;font-weight: 900;color: var(--gray-900);line-height: 1.4;margin-bottom: 8px;">您今日还有大额优惠券可领</div>' +
'<div style="display: inline-flex;align-items: center;gap: 6px;background: rgba(255,179,0,.18);color: #8B6914;font-size: 12px;font-weight: 700;padding: 4px 12px;border-radius: 32px;margin-bottom: 12px;"><svg width="14" height="12" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M2 7.2 V10.8 A1.4 1.4 0 0 0 3.4 12.2 H5.4 L11.2 16.4 A0.8 0.8 0 0 0 12.5 15.7 V2.3 A0.8 0.8 0 0 0 11.2 1.6 L5.4 5.8 H3.4 A1.4 1.4 0 0 0 2 7.2 Z" fill="#8B6914"/><path d="M14.6 4.4 C16 6 16 12 14.6 13.6" stroke="#8B6914" stroke-width="1.4" stroke-linecap="round" fill="none"/><path d="M16.8 2.6 C19.2 5.2 19.2 12.8 16.8 15.4" stroke="#8B6914" stroke-width="1.4" stroke-linecap="round" fill="none"/><circle cx="6.2" cy="14.4" r="1.5" fill="#8B6914"/></svg>先领券再比价</div>' +
'<div style="background: #fff;border-radius: 18px;padding: 12px 8px;margin-bottom: 8px;box-shadow: 0 2px 12px rgba(255,193,7,.15);">' +
'<div style="display: grid;grid-template-columns: repeat(3,1fr);gap: 8px;">' +
['美团', 'assets/shared/logos/meituan.png', '淘宝', 'assets/shared/logos/taobao.png', '京东', 'assets/shared/logos/jd.png'].reduce(function(acc, _, i, arr){ if (i % 2) return acc; return acc + '<div data-checked="true" onclick="toggleCouponPlatform(this)" class="cp-card cp-card-yellow" style="background: #fff;border: 2px solid #FFB300;border-radius: 14px;padding: 12px 4px 8px;position: relative;"><img src="' + arr[i+1] + '" style="width: 42px;height: 42px;border-radius: 8px;margin: 0 auto;display: block;"/><div style="font-size: 12px;font-weight: 700;color: var(--gray-900);margin-top: 8px;">' + arr[i] + '</div><span class="cp-check" style="position: absolute;top: 6px;right: 6px;width: 18px;height: 18px;border-radius: 50%;background: #FFB300;display: flex;align-items: center;justify-content: center;"><svg width=\"10\" height=\"10\" viewBox=\"0 0 9 9\"><path d=\"M2 4.5l1.5 1.5 3-4\" stroke=\"#fff\" stroke-width=\"1.6\" stroke-linecap=\"round\" stroke-linejoin=\"round\" fill=\"none\"/></svg></span></div>'; }, '') +
'</div>' +
'</div>' +
'<div style="font-size: 12px;color: var(--gray-500);font-weight: 500;margin-bottom: 12px;">将在以上平台领取</div>' +
'<button class="btn btn-lg" style="border-radius: 24px;font-size: 16px;height: 50px;width: 100%;background: linear-gradient(180deg,#FFD600 0%,#FFA000 100%);color: #1a1a1a;font-weight: 900;box-shadow: 0 8px 22px rgba(255,140,0,.5);border: none;" onclick="onClaimCouponConfirm()">一键自动领取</button>' +
'</div>'
};
function applyCouponPromptVariant(v) {
const card = document.getElementById('demoCouponPromptCard');
if (!card || !couponPromptVariants[v]) return;
card.style.cssText = 'width:100%;max-width:330px;animation:fadeInUp .45s cubic-bezier(.34,1.56,.64,1);';
card.innerHTML = couponPromptVariants[v];
}
function showCouponPromptForPreview() {
// 预览模式:直接强制显示弹窗,不走 demo flow
document.querySelectorAll('.screen').forEach(function(s){ s.classList.remove('active'); s.style.display = ''; });
const cmp = document.getElementById('comparing');
if (cmp) { cmp.classList.add('active'); cmp.style.display = 'flex'; }
const prompt = document.getElementById('demoCouponPrompt');
if (prompt) prompt.style.display = 'flex';
}
function onClaimCouponDecline() {
// 残忍放弃:跳过领券,直接进入比价引导第一步
document.getElementById('demoCouponPrompt').style.display = 'none';
const firstSpotlightIdx = demoSteps.findIndex(d => d.spotlight || d.mode === 'swipe' || d.mode === 'startCompare');
if (firstSpotlightIdx > 0) {
demoStep = firstSpotlightIdx;
renderDemoStep();
} else {
demoNext();
}
}
// 跳过新人体验流程 → 召回弹窗(居中卡片式)
function onSkipExperienceRequest() {
const modal = document.getElementById('skipConfirmModal');
if (modal) modal.style.display = 'flex';
}
function closeSkipConfirmModal() {
const modal = document.getElementById('skipConfirmModal');
if (modal) modal.style.display = 'none';
}
function confirmSkipExperience() {
closeSkipConfirmModal();
// 真的跳过 → 直接进入 login(不走 demo 比价 + celebration
go('login');
}
// [ECOM-ONLY] 盲盒解锁 + 滑动引擎 removed for waimai V1
// ===== 礼花爆炸特效 =====
function startConfetti() {
const canvas = document.getElementById('confettiCanvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
canvas.width = 375;
canvas.height = 812;
const colors = ['#FF5722','#FFD600','#4CAF50','#2196F3','#E91E63','#FF9800','#9C27B0','#00BCD4','#FF4081'];
const pieces = [];
const shapes = ['rect','circle','strip'];
// 从两侧底部发射两簇礼花
function burst(originX, originY, count) {
for (let i = 0; i < count; i++) {
const angle = -Math.PI/2 + (Math.random() - 0.5) * 1.2; // 向上扇形
const speed = Math.random() * 8 + 5;
pieces.push({
x: originX,
y: originY,
vx: Math.cos(angle) * speed * (originX < 187 ? 1 : -1) * (0.3 + Math.random() * 0.7),
vy: Math.sin(angle) * speed - Math.random() * 3,
w: Math.random() * 10 + 4,
h: Math.random() * 6 + 2,
color: colors[Math.floor(Math.random() * colors.length)],
rot: Math.random() * 360,
vr: (Math.random() - 0.5) * 12,
gravity: 0.12 + Math.random() * 0.06,
friction: 0.98,
opacity: 1,
shape: shapes[Math.floor(Math.random() * shapes.length)],
wobble: Math.random() * 10,
wobbleSpeed: 0.05 + Math.random() * 0.1,
});
}
}
// 左右两侧各爆一簇
burst(60, 350, 50);
burst(315, 350, 50);
// 稍后中间再爆一簇
setTimeout(() => burst(187, 300, 40), 300);
let frame = 0;
function draw() {
ctx.clearRect(0, 0, 375, 812);
let alive = 0;
pieces.forEach(p => {
if (p.opacity <= 0) return;
alive++;
p.vx *= p.friction;
p.vy *= p.friction;
p.vy += p.gravity;
p.vx += Math.sin(p.wobble) * 0.3;
p.wobble += p.wobbleSpeed;
p.x += p.vx;
p.y += p.vy;
p.rot += p.vr;
p.vr *= 0.99;
// 慢慢淡出
if (frame > 80) p.opacity = Math.max(0, p.opacity - 0.006);
ctx.save();
ctx.translate(p.x, p.y);
ctx.rotate(p.rot * Math.PI / 180);
ctx.globalAlpha = p.opacity;
ctx.fillStyle = p.color;
if (p.shape === 'circle') {
ctx.beginPath();
ctx.arc(0, 0, p.w/2, 0, Math.PI*2);
ctx.fill();
} else if (p.shape === 'strip') {
ctx.fillRect(-p.w/2, -1, p.w, 2.5);
} else {
ctx.fillRect(-p.w/2, -p.h/2, p.w, p.h);
}
ctx.restore();
});
frame++;
if (alive > 0 && frame < 400) requestAnimationFrame(draw);
else ctx.clearRect(0, 0, 375, 812);
}
draw();
}
// ===== 新人礼包方案切换(URL 参数驱动,用于 gift-variants 对比页) =====
const giftVariantTemplates = {
A: '<div class="gv-base gv-a"><div class="gv-icon">🎁</div><div><div class="gv-title">新人专享礼包</div><div class="gv-sub">完成体验立即到账</div></div></div>',
B: '<div class="gv-base gv-b"><div class="gv-stamp">价值 ¥88</div><div class="gv-icon">🎁</div><div><div class="gv-title">新人专享礼包</div><div class="gv-sub">完成体验立即到账</div></div></div>',
C: '<div class="gv-base gv-c"><div class="gv-icon">🎁</div><div><div class="gv-title">新人专享礼包</div><div class="gv-cd"><span>⏱</span><span id="gvCd">23:45<span class="gv-blink">:</span>12</span><span>后失效</span></div></div></div>',
D: '<div class="gv-base gv-d"><div class="gv-iconwrap"><div class="gv-icon">🎁</div></div><div class="gv-text"><div class="gv-title">新人专享礼包</div><div class="gv-sub">完成体验立即到账</div></div></div>',
E: '<div class="gift-coupon"><div class="gc-icon">🎁</div><div style="flex: 1;min-width: 0;"><div class="gc-title">新人专享礼包</div><div class="gc-sub">完成体验立即到账</div></div></div>',
F: '<div class="gv-base gv-f"><div class="gv-rays"></div><div class="gv-iconwrap"><div class="gv-icon">🎁</div></div><div class="gv-title">新人专享礼包</div><div class="gv-sub">完成体验立即到账</div></div>',
G: '<div class="gv-base gv-g"><div class="gv-iconwrap"><span class="gv-spark gv-s1"></span><span class="gv-spark gv-s2"></span><span class="gv-spark gv-s3"></span><div class="gv-icon">🎁</div></div><div><div class="gv-title">新人专享<em>大礼包</em></div><div class="gv-sub">完成体验立即到账</div></div></div>'
};
let __gvCdTimer = null;
function __gvStartCountdown() {
let total = 23 * 3600 + 45 * 60 + 12;
if (__gvCdTimer) clearInterval(__gvCdTimer);
const tick = () => {
total = Math.max(0, total - 1);
const h = String(Math.floor(total / 3600)).padStart(2, '0');
const m = String(Math.floor((total % 3600) / 60)).padStart(2, '0');
const s = String(total % 60).padStart(2, '0');
const el = document.getElementById('gvCd');
if (el) el.innerHTML = h + ':' + m + '<span class="gv-blink">:</span>' + s;
};
tick();
__gvCdTimer = setInterval(tick, 1000);
}
function applyGiftVariant(letter) {
const container = document.getElementById('giftCardContainer');
if (!container) return;
const key = (letter || 'E').toUpperCase();
const tpl = giftVariantTemplates[key] || giftVariantTemplates.E;
container.innerHTML = tpl;
if (key === 'C') __gvStartCountdown();
}
(function applyGiftVariantURLParams() {
try {
const params = new URLSearchParams(location.search);
const variant = params.get('giftvariant');
const celebVariant = params.get('celebrationvariant');
const celebrate = params.get('celebrate') === '1';
const couponVariant = params.get('couponvariant');
const showCoupon = params.get('showcoupon') === '1';
const ctaVariantParam = params.get('ctavariant');
const screen = params.get('screen');
const embed = params.get('embed') === '1';
const run = function() {
if (embed) document.body.classList.add('gv-embed');
if (variant) applyGiftVariant(variant);
if (celebVariant && celebrationVariants[celebVariant]) {
currentCelebrationVariant = celebVariant;
}
if (screen) { try { go(screen); } catch (e) {} }
if (celebrate) {
try { openCelebrationModal(); } catch (e) {}
}
if (couponVariant && typeof applyCouponPromptVariant === 'function') {
try { applyCouponPromptVariant(couponVariant); } catch (e) {}
}
if (showCoupon && typeof showCouponPromptForPreview === 'function') {
try { showCouponPromptForPreview(); } catch (e) {}
}
if (ctaVariantParam && typeof applyCtaVariant === 'function') {
try { applyCtaVariant(parseInt(ctaVariantParam, 10)); } catch (e) {}
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', run);
} else {
run();
}
} catch (e) {}
})();
</script>
<!-- ===== Tweaks Panel ===== -->
<div id="tweaksPanel" class="tweaks-panel">
<button class="tweaks-close" onclick="closeTweaks()">×</button>
<h3>「开启省钱之旅」按钮样式</h3>
<div class="tweaks-sub">点击切换,预览不同方案</div>
<div class="tweaks-grid">
<div class="tweaks-opt" data-v="1">红橙渐变<small>当前</small></div>
<div class="tweaks-opt" data-v="2">黄底黑字<small>品牌主色</small></div>
<div class="tweaks-opt" data-v="3">黑底黄字<small>高对比</small></div>
<div class="tweaks-opt" data-v="4">金色光泽<small>动态扫光</small></div>
<div class="tweaks-opt" data-v="5">玻璃质感<small>立体黄</small></div>
<div class="tweaks-opt" data-v="6">描边偏移<small>克制款</small></div>
<div class="tweaks-opt" data-v="7">红包喜庆<small>红+金</small></div>
<div class="tweaks-opt" data-v="8">呼吸跳动<small>动效款</small></div>
<div class="tweaks-opt" data-v="9">蜜糖渐变<small>黄→琥珀</small></div>
<div class="tweaks-opt" data-v="10">金币描边<small>深金细边</small></div>
</div>
<div style="height: 1px;background: #E5E5E5;margin: 16px 0 12px;"></div>
<h3>领券弹窗副标题样式</h3>
<div class="tweaks-sub">主标题下那行小字,强化"收益"信号</div>
<div class="tweaks-grid">
<div class="tweaks-opt sub-opt" data-sv="1">基线灰<small>当前最弱</small></div>
<div class="tweaks-opt sub-opt" data-sv="2">金币胶囊<small>品牌黄徽章</small></div>
<div class="tweaks-opt sub-opt" data-sv="3">关键词高亮<small>黄色荧光笔</small></div>
<div class="tweaks-opt sub-opt" data-sv="4">优惠券小票<small>齿边+朱红</small></div>
<div class="tweaks-opt sub-opt" data-sv="5">数字徽章<small>预计立省¥</small></div>
<div class="tweaks-opt sub-opt" data-sv="6">双行节奏<small>预告+承诺</small></div>
<div class="tweaks-opt sub-opt" data-sv="7">V2b 金币翻转<small>动效活力</small></div>
<div class="tweaks-opt sub-opt" data-sv="8">V2c 双色分段<small>黑黄拼接</small></div>
<div class="tweaks-opt sub-opt" data-sv="9">V2d 闪光描边<small>轻盈款</small></div>
<div class="tweaks-opt sub-opt" data-sv="10">V2e 数字嵌入<small>¥18+ 内嵌</small></div>
<div class="tweaks-opt sub-opt" data-sv="11">V2f 双图标<small>金币+闪电</small></div>
<div class="tweaks-opt sub-opt" data-sv="12">V2g 立体浮起<small>金属高光</small></div>
<div class="tweaks-opt sub-opt" data-sv="13">流程植入<small>领券→比价→底价</small></div>
</div>
<div style="height: 1px;background: #E5E5E5;margin: 16px 0 12px;"></div>
<h3>「完成设置」弹窗版式</h3>
<div class="tweaks-sub">点击切换,下次进入弹窗即生效</div>
<div class="tweaks-grid">
<div class="tweaks-opt celeb-opt" data-cv="v1">轻收尾·重期待<small>承上小,起下大</small></div>
<div class="tweaks-opt celeb-opt" data-cv="v2">分割式<small>三段节奏</small></div>
<div class="tweaks-opt celeb-opt" data-cv="v3">叙事流<small>递进式</small></div>
<div class="tweaks-opt celeb-opt" data-cv="v4">行为优先<small>主舞台让给行为</small></div>
<div class="tweaks-opt celeb-opt" data-cv="v5">徽章+行动卡<small>chip 强调最后一步</small></div>
<div class="tweaks-opt celeb-opt" data-cv="v6">进度条推进<small>脉动黄推进感</small></div>
<div class="tweaks-opt celeb-opt" data-cv="v7">数字步骤<small>① ✓ → ② 当前</small></div>
<div class="tweaks-opt celeb-opt" data-cv="v8">极简专注<small>大字承担引导</small></div>
</div>
<button onclick="previewCelebration()" style="margin-top: 12px;width: 100%;padding: 8px;border-radius: 8px;border: 1px solid #ddd;background: #fff;font-size: 13px;font-weight: 600;cursor: pointer;">▶ 预览当前版式</button>
</div>
<script>
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
"ctaVariant": 5,
"celebVariant": "v3",
"couponSubVariant": 13
}/*EDITMODE-END*/;
let currentCtaVariant = TWEAK_DEFAULTS.ctaVariant || 1;
let currentCouponSubVariant = TWEAK_DEFAULTS.couponSubVariant || 1;
currentCelebrationVariant = TWEAK_DEFAULTS.celebVariant || 'v1';
const couponSubMarkup = {
1: '<div class="cps cps-v1">领取后将为您解锁更低好价</div>',
2: '<div class="cps cps-v2"><span class="cps-coin">¥</span>领取后解锁今日最低价</div>',
3: '<div class="cps cps-v3">领取后将为您解锁<em>全网最低价</em></div>',
4: '<div class="cps cps-v4">已为您锁定 <span class="cps-amt">¥18+</span> 神券</div>',
5: '<div class="cps cps-v5">领取后预计立省<span class="cps-pill">¥8<small>~</small>25</span></div>',
6: '<div class="cps cps-v6"><span class="cps-top">· 预计 3 秒完成 ·</span><span class="cps-bot">领券解锁<em>今日最低价</em></span></div>',
7: '<div class="cps cps-v2b"><span class="cps-coin">¥</span>领取后解锁今日最低价</div>',
8: '<div class="cps cps-v2c"><span class="cps-tag">立省</span><span class="cps-text">解锁今日最低价</span></div>',
9: '<div class="cps cps-v2d"><span class="cps-coin">¥</span>领取后解锁今日最低价</div>',
10: '<div class="cps cps-v2e"><span class="cps-amount">¥18<small>+</small></span>解锁今日最低价</div>',
11: '<div class="cps cps-v2f"><span class="cps-coin">¥</span>领取后解锁<span class="cps-bolt"><svg width="10" height="11" viewBox="0 0 10 11"><path d="M6 0L0 6h3l-1 5 6-6H5z" fill="#1a1a1a"/></svg></span>今日最低价</div>',
12: '<div class="cps cps-v2g"><span class="cps-coin">¥</span>领取后解锁今日最低价</div>',
13: '<div class="cps cps-v3f"><span class="cps-coin"><svg width="15" height="13" viewBox="0 0 22 18" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="M2 7.2 V10.8 A1.4 1.4 0 0 0 3.4 12.2 H5.4 L11.2 16.4 A0.8 0.8 0 0 0 12.5 15.7 V2.3 A0.8 0.8 0 0 0 11.2 1.6 L5.4 5.8 H3.4 A1.4 1.4 0 0 0 2 7.2 Z" fill="#8B6914"/><path d="M14.6 4.4 C16 6 16 12 14.6 13.6" stroke="#8B6914" stroke-width="1.4" stroke-linecap="round" fill="none"/><path d="M16.8 2.6 C19.2 5.2 19.2 12.8 16.8 15.4" stroke="#8B6914" stroke-width="1.4" stroke-linecap="round" fill="none"/><circle cx="6.2" cy="14.4" r="1.5" fill="#8B6914"/></svg></span>先领券,再比价</div>'
};
function applyCouponSubVariant(v) {
currentCouponSubVariant = v;
const slot = document.getElementById('demoCouponSubtitleSlot');
if (slot) slot.innerHTML = couponSubMarkup[v] || couponSubMarkup[1];
document.querySelectorAll('#tweaksPanel .sub-opt').forEach(el => {
el.classList.toggle('active', String(el.dataset.sv) === String(v));
});
}
function applyCelebVariant(cv) {
currentCelebrationVariant = cv;
document.querySelectorAll('#tweaksPanel .celeb-opt').forEach(el => {
el.classList.toggle('active', el.dataset.cv === cv);
});
}
function previewCelebration() {
if (typeof openCelebrationModal === 'function') openCelebrationModal();
}
function applyCtaVariant(v) {
currentCtaVariant = v;
const btn = document.getElementById('splashCTA');
if (!btn) return;
btn.className = 'splash-cta splash-cta-v' + v;
document.querySelectorAll('#tweaksPanel .tweaks-opt').forEach(el => {
el.classList.toggle('active', String(el.dataset.v) === String(v));
});
}
function closeTweaks() {
document.getElementById('tweaksPanel').classList.remove('open');
try { window.parent.postMessage({type:'__edit_mode_dismissed'},'*'); } catch(e){}
}
window.addEventListener('message', (e) => {
const d = e.data || {};
if (d.type === '__activate_edit_mode') document.getElementById('tweaksPanel').classList.add('open');
if (d.type === '__deactivate_edit_mode') document.getElementById('tweaksPanel').classList.remove('open');
});
document.addEventListener('DOMContentLoaded', () => {
document.querySelectorAll('#tweaksPanel .tweaks-opt:not(.celeb-opt):not(.sub-opt)').forEach(el => {
el.addEventListener('click', () => {
const v = parseInt(el.dataset.v, 10);
applyCtaVariant(v);
try { window.parent.postMessage({type:'__edit_mode_set_keys', edits:{ctaVariant: v}}, '*'); } catch(e){}
});
});
document.querySelectorAll('#tweaksPanel .sub-opt').forEach(el => {
el.addEventListener('click', () => {
const v = parseInt(el.dataset.sv, 10);
applyCouponSubVariant(v);
try { window.parent.postMessage({type:'__edit_mode_set_keys', edits:{couponSubVariant: v}}, '*'); } catch(e){}
});
});
document.querySelectorAll('#tweaksPanel .celeb-opt').forEach(el => {
el.addEventListener('click', () => {
const cv = el.dataset.cv;
applyCelebVariant(cv);
try { window.parent.postMessage({type:'__edit_mode_set_keys', edits:{celebVariant: cv}}, '*'); } catch(e){}
});
});
applyCtaVariant(currentCtaVariant);
applyCouponSubVariant(currentCouponSubVariant);
applyCelebVariant(currentCelebrationVariant);
try { window.parent.postMessage({type:'__edit_mode_available'},'*'); } catch(e){}
});
</script>
<script>
(function() {
var ID_TO_PATH = {
"login": "app/login/inapp.html",
"loginPhone": "app/login/phone.html",
"home": "app/home",
"guideVideo": "app/home/guide-video.html",
"welfare": "app/welfare",
"welfareRules": "app/welfare/rules.html",
"profile": "app/profile",
"invite": "app/welfare/invite.html",
"compresult": "app/compare/index.html?screen=result",
"withdrawal": "app/welfare/withdrawal.html",
"coinhistory": "app/welfare/coinhistory.html",
"records": "app/profile/records.html",
"settings": "app/profile/settings.html",
"feedback": "app/profile/feedback.html",
"comparing": "app/compare",
"history": "app/history"
};
var PREFIX = '../../';
function navigate(id) {
// 抢救 inline onclick 设置的 window.loginFrom(隐式 global,不会进 let 绑定)
if (window.loginFrom && typeof window.loginFrom === 'string') {
try { localStorage.setItem('proto_loginFrom', window.loginFrom); } catch(e) {}
}
var p = ID_TO_PATH[id];
if (!p) { console.warn('[nav] unknown id:', id); return; }
location.href = PREFIX + p + (p.indexOf('.html') !== -1 ? '' : '/index.html');
}
window.go = navigate;
window._activateScreen = function(id) { navigate(id); };
window.goBack = function() { if (history.length > 1) history.back(); else navigate('home'); };
window.debugGoBack = window.goBack;
})();
</script>
<script>
// ============================================
// V3: 页面级初始化
// 替代原 go(id) 切到本屏时跑的逻辑
// ============================================
document.addEventListener('DOMContentLoaded', function() {
var isLogged = false;
try { isLogged = localStorage.getItem('proto_isLoggedIn') !== 'false'; } catch(e) {}
var activeScreen = document.querySelector('.screen.active');
if (!activeScreen) return;
var id = activeScreen.id;
// welfare 登录态切换
if (id === 'welfare') {
var wu = document.getElementById('welfareUnlogged');
var wl = document.getElementById('welfareLogged');
if (wu) wu.style.display = isLogged ? 'none' : 'block';
if (wl) wl.style.display = isLogged ? 'block' : 'none';
// 已登录 + 没显示过签到弹窗 → 弹一次
if (isLogged && typeof showCheckinPopup === 'function') {
var checkinShown = false;
try { checkinShown = localStorage.getItem('proto_checkinShown') === 'true'; } catch(e) {}
if (!checkinShown) setTimeout(showCheckinPopup, 500);
}
}
// profile 登录态切换
if (id === 'profile') {
var pu = document.getElementById('profileUnlogged');
var pl = document.getElementById('profileLogged');
if (pu) pu.style.display = isLogged ? 'none' : 'block';
if (pl) pl.style.display = isLogged ? 'block' : 'none';
}
// productselect 自动开始平台扫描
if (id === 'productselect') {
if (typeof startPlatformScan === 'function') {
try { startPlatformScan(); } catch(e) { console.warn(e); }
}
}
// celebration 动画 + 数据
if (id === 'celebration') {
if (typeof renderCelebrationSavings === 'function') try { renderCelebrationSavings(); } catch(e){}
if (typeof renderResults === 'function') try { renderResults('celebResults'); } catch(e){}
if (typeof startConfetti === 'function') try { startConfetti(); } catch(e){}
}
// comparing 自动开始比价动画
if (id === 'comparing') {
if (typeof startCompareAnimation === 'function') try { startCompareAnimation(); } catch(e){}
if (typeof startComparing === 'function') try { startComparing(); } catch(e){}
}
// 首页:从 compare 跑完领券流程跳回来时,还原"已领"态 + 弹结果 toast
if (id === 'home') {
try {
if (typeof renderLastCompareBanner === 'function') renderLastCompareBanner();
var summaryStr = localStorage.getItem('proto_homeCouponClaimSummary');
var claimedFlag = localStorage.getItem('proto_homeCouponClaimed');
var claimDate = localStorage.getItem('proto_home_coupon_claim_date');
var today_home = (typeof getTodayStr_home === 'function') ? getTodayStr_home() : '';
// 跨日重置:上次领的不是今天 → 已领态作废,让今天重新走一遍领券流程
if (claimedFlag === 'true' && claimDate && claimDate !== today_home) {
localStorage.removeItem('proto_homeCouponClaimed');
localStorage.removeItem('proto_homeCouponClaimSummary');
claimedFlag = null;
summaryStr = null;
}
if (claimedFlag === 'true' && summaryStr) {
homeCouponClaimed = true;
homeCouponClaimSummary = JSON.parse(summaryStr);
if (typeof renderTriClaim === 'function') renderTriClaim();
}
var showToast = localStorage.getItem('proto_showCouponDoneToast');
if (showToast === 'true' && summaryStr) {
localStorage.removeItem('proto_showCouponDoneToast');
var summary = JSON.parse(summaryStr);
couponTotal = summary.total || 0;
homeCouponClaimSummary = summary;
var toast = document.getElementById('demoCouponDoneToast');
if (toast) {
toast.style.display = 'block';
renderCouponDoneFromLogos();
if (typeof runKCountUp === 'function') runKCountUp('demoCouponDoneCount', couponTotal, 1500, 200);
if (typeof startMidnightCountdown === 'function') startMidnightCountdown();
}
}
if (showToast !== 'true') {
// LBS 系统弹窗只由首页「去授权」手动触发;进入 home 只跑每日检查(领券/权限提醒)
if (typeof runHomeDailyChecks === 'function') runHomeDailyChecks();
}
} catch(e) { console.warn('[home init] claim state restore failed:', e); }
}
});
</script>
<script>
// === 调试用:右下角"重置"按钮,仅 file:// 本地预览可见 ===
(function() {
if (location.protocol !== 'file:') return;
if (document.getElementById('debugReset')) return;
function mount() {
var btn = document.createElement('div');
btn.id = 'debugReset';
btn.textContent = '↻ 重置'; // ↻ 重置
btn.title = '清空 localStorage 并刷新(仅本地预览可见)';
btn.style.cssText = 'position:fixed;right:0;bottom:0;z-index:99999;background:#000;color:#fff;font-size:11px;font-weight:700;padding:8px;border-radius:4px 0 0 0;cursor:pointer;opacity:.7;line-height:1;letter-spacing:1px;font-family:inherit;';
btn.onclick = function() {
try { localStorage.clear(); } catch(e) {}
location.reload();
};
document.body.appendChild(btn);
}
if (document.body) mount();
else document.addEventListener('DOMContentLoaded', mount);
})();
// === 调试用:首页"每日检查"控制面板(权限开关 + 清今日态),仅 file:// 可见 ===
(function() {
if (location.protocol !== 'file:') return;
if (document.getElementById('debugDaily')) return;
function mount() {
var wrap = document.createElement('div');
wrap.id = 'debugDaily';
wrap.style.cssText = 'position:fixed;right:0;bottom:34px;z-index:99999;display:flex;flex-direction:column;gap:3px;align-items:flex-end;font-family:inherit;';
function permChip(label, key) {
var c = document.createElement('div');
var off = false;
try { off = localStorage.getItem(key) === 'off'; } catch(e) {}
c.textContent = label + (off ? ' ✕' : ' ✓');
c.title = '点击切换'+label+'权限状态(仅本地预览)';
c.style.cssText = 'background:'+(off ? '#c0392b' : '#222')+';color:#fff;font-size:10px;font-weight:700;padding:5px 8px;border-radius:4px 0 0 4px;cursor:pointer;opacity:.78;line-height:1;letter-spacing:.5px;';
c.onclick = function() {
try {
if (localStorage.getItem(key) === 'off') localStorage.removeItem(key);
else localStorage.setItem(key, 'off');
// 切换权限即清掉今日提醒记忆,下一次刷新可重新弹
localStorage.removeItem('proto_home_perm_reminder_date');
} catch(e) {}
location.reload();
};
return c;
}
function clearTodayChip() {
var c = document.createElement('div');
c.textContent = '清今日态';
c.title = '清空今日权限/领券弹窗记忆 + 已领卡片,模拟新一天进入首页';
c.style.cssText = 'background:#555;color:#fff;font-size:10px;font-weight:700;padding:5px 8px;border-radius:4px 0 0 4px;cursor:pointer;opacity:.78;line-height:1;letter-spacing:.5px;';
c.onclick = function() {
try {
localStorage.removeItem('proto_home_perm_reminder_date');
localStorage.removeItem('proto_home_coupon_reminder_date');
localStorage.removeItem('proto_home_coupon_claim_date');
localStorage.removeItem('proto_homeCouponClaimed');
localStorage.removeItem('proto_homeCouponClaimSummary');
} catch(e) {}
location.reload();
};
return c;
}
function nearbyEmptyChip() {
var c = document.createElement('div');
var on = false;
try { on = localStorage.getItem('proto_home_nearby_empty') === '1'; } catch(e) {}
c.textContent = '无推荐' + (on ? ' ✓' : ' ✕');
c.title = '模拟已开启位置权限但附近没有推荐商品,空态由首页状态判断出现(仅本地预览)';
c.style.cssText = 'background:'+(on ? '#222' : '#555')+';color:#fff;font-size:10px;font-weight:700;padding:5px 8px;border-radius:4px 0 0 4px;cursor:pointer;opacity:.78;line-height:1;letter-spacing:.5px;';
c.onclick = function() {
try {
if (localStorage.getItem('proto_home_nearby_empty') === '1') {
localStorage.removeItem('proto_home_nearby_empty');
} else {
// 调试态只模拟“已授权但附近无推荐”,空态仍由 applyLocationState 统一判断展示。
localStorage.setItem('proto_home_nearby_empty', '1');
localStorage.setItem('proto_locationAuthMode', 'whileUsing');
localStorage.setItem('proto_locationGranted', '1');
}
} catch(e) {}
location.reload();
};
return c;
}
wrap.appendChild(permChip('无障碍', 'proto_perm_accessibility'));
wrap.appendChild(permChip('悬浮窗', 'proto_perm_overlay'));
wrap.appendChild(nearbyEmptyChip());
wrap.appendChild(clearTodayChip());
document.body.appendChild(wrap);
}
if (document.body) mount();
else document.addEventListener('DOMContentLoaded', mount);
})();
</script>
<script>
// ===== v2026-06-01:实时节省日志横条 + 平台累计省下入口 + AI 领券 FAB =====
// 昵称脱敏:4 字保留首尾;3 字及以下保留首字;5 字及以上固定 3 星。
function maskNickname(name) {
var chars = Array.from(name || '');
var len = chars.length;
if (len === 0) return '用户**';
if (len >= 5) return chars[0] + '***' + chars[len - 1];
if (len === 4) return chars[0] + '**' + chars[len - 1];
return chars[0] + '**';
}
// 无昵称用户:固定 id 前 1 位 + 3 个星号 + id 后 1 位,例如 a***8。
function maskUserId(id) {
var normalized = String(id || '').replace(/\s/g, '');
if (!normalized) return '0***0';
return normalized.slice(0, 1) + '***' + normalized.slice(-1);
}
function formatSavingsAmount(value) {
var num = Number(String(value || '').replace(/[^\d.]/g, ''));
if (!Number.isFinite(num)) num = 0;
return num.toFixed(2);
}
function setHomeMqFixedSlots(track) {
var home = document.getElementById('home');
var host = track && track.closest ? track.closest('.home-marquee') : null;
if (!home || !host) return;
var measure = document.createElement('span');
measure.className = 'home-mq-measure';
home.appendChild(measure);
function measureText(className, text) {
var span = document.createElement('span');
span.className = className;
span.textContent = text;
measure.innerHTML = '';
measure.appendChild(span);
return (Math.ceil(span.getBoundingClientRect().width * 100) / 100) + 'px';
}
host.style.setProperty('--home-mq-user-slot', measureText('mq-user', '省***手'));
host.style.setProperty('--home-mq-amount-slot', measureText('mq-amt', '88888.88'));
measure.remove();
}
// 实时节省日志横条 —— 每 2.4s 自动上滚一条,到末尾(首条副本)后无动画归零
(function() {
var track = document.getElementById('homeMarqueeTrack');
if (!track) return;
setHomeMqFixedSlots(track);
// 两种打码混着渲染:有昵称(data-nick)走昵称脱敏,无昵称(data-uid)走 id 前后位脱敏。
Array.prototype.forEach.call(track.querySelectorAll('.mq-user'), function(el) {
if (el.dataset.nick) el.textContent = maskNickname(el.dataset.nick);
else if (el.dataset.uid) el.textContent = maskUserId(el.dataset.uid);
});
Array.prototype.forEach.call(track.querySelectorAll('.mq-amt'), function(el) {
el.textContent = formatSavingsAmount(el.textContent);
});
var items = track.children;
var n = items.length;
if (n < 2) return;
var itemH = 22;
var idx = 0;
var maxIdx = n - 1; // 末尾是首条副本,到这里要无动画归零
setInterval(function() {
idx++;
track.style.transition = 'transform .42s cubic-bezier(.22,1,.36,1)';
track.style.transform = 'translateY(' + (-idx * itemH) + 'px)';
if (idx >= maxIdx) {
setTimeout(function() {
track.style.transition = 'none';
track.style.transform = 'translateY(0)';
idx = 0;
}, 460);
}
}, 2400);
})();
// 平台累计省下入口 → 跳转占位(暂复用 profile,后续替换为战报页)
function onPlatformStatsClick() {
if (typeof go === 'function') go('profile');
}
// 平台候选包(对齐原生 HOME_PICKER_APPS):任一已装即「已装」;点击拉起把整组候选包交原生取首个已装。
var PICKER_PACKAGES = {
'美团': ['com.sankuai.meituan', 'com.sankuai.meituan.takeoutnew'],
'淘宝': ['com.taobao.taobao', 'me.ele'],
'京东': ['com.jingdong.app.mall'],
};
function getInstalledPkgSet() {
try { if (window.SGBridge && typeof SGBridge.getInstalledApps === 'function') return SGBridge.getInstalledApps() || []; } catch(e) {}
return [];
}
// 比价入口 picker:动态渲染 3 平台(真装机态:getInstalledApps 判候选包是否已装)
function renderHomePickerPlatforms() {
var grid = document.getElementById('homePickerGrid');
if (!grid || typeof getVisibleApps !== 'function') return;
var installedList = getInstalledPkgSet();
var platforms = getVisibleApps();
grid.innerHTML = platforms.map(function(app) {
var pkgs = PICKER_PACKAGES[app.name] || [];
var installed = pkgs.some(function(p){ return installedList.indexOf(p) !== -1; });
if (installed) {
return '<div class="home-picker-item" data-platform="' + app.name + '">' +
'<div class="home-picker-logo"><img src="' + app.icon + '" alt=""></div>' +
'<div class="home-picker-name">' + app.name + '</div>' +
'</div>';
}
return '<div class="home-picker-item uninstalled" data-platform="' + app.name + '" data-uninstalled="true">' +
'<div class="home-picker-logo"><img src="' + app.icon + '" alt=""></div>' +
'<div class="home-picker-name">' + app.name + '</div>' +
'<span class="home-picker-install-chip">' +
'<svg width="10" height="10" viewBox="0 0 10 10" fill="none"><path d="M5 1.5v5.5m0 0L2.5 4.5M5 7l2.5-2.5M2 9h6" stroke="#1a1a1a" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/></svg>' +
'</span>' +
'</div>';
}).join('');
}
// 比价入口 onclick → 渲染平台 + 弹 picker
function onHomeCompareClick() {
// H3:拉起外卖不需登录态;「未登录引导」连同 getAuthState 留后续登录态子项。直接渲染 + 弹平台选择。
renderHomePickerPlatforms();
var mask = document.getElementById('homePickerMask');
if (mask) mask.classList.add('show');
}
function closeHomePicker() {
var mask = document.getElementById('homePickerMask');
if (mask) mask.classList.remove('show');
}
(function() {
var mask = document.getElementById('homePickerMask');
if (!mask) return;
// 事件委托:点蒙层关闭 / 点平台触发跳转或安装引导
mask.addEventListener('click', function(e) {
if (e.target === mask) { closeHomePicker(); return; }
var item = e.target.closest('.home-picker-item');
if (!item) return;
var nameEl = item.querySelector('.home-picker-name');
var name = nameEl ? nameEl.textContent : '';
closeHomePicker();
if (item.dataset.uninstalled === 'true') {
// 未装:跳应用市场引导(原型 stub)
if (typeof showToast === 'function') showToast('请先安装「' + name + '」APP');
if (typeof onPlatformInstallClick === 'function') {
var app = (typeof allApps !== 'undefined') ? allApps.find(function(a){return a.name === name;}) : null;
if (app) onPlatformInstallClick(app.id);
}
} else {
// 已装:拉起对应平台 App(整组候选包交原生取首个已装,NEW_TASK|CLEAR_TASK 冷启首页)
var pkgs = PICKER_PACKAGES[name] || [];
if (pkgs.length && window.SGBridge && typeof SGBridge.launchApp === 'function') SGBridge.launchApp(pkgs);
}
});
})();
</script>
<script>
(function() {
function getTodayKey() {
var d = new Date();
return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
}
function updateWelfareCheckinTabDot() {
var tab = document.getElementById('homeTabWelfare');
if (!tab) return;
var done = false;
try { done = localStorage.getItem('proto_checkinDoneDate') === getTodayKey(); } catch(e) {}
tab.classList.toggle('has-welfare-dot', !done);
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', updateWelfareCheckinTabDot);
else updateWelfareCheckinTabDot();
})();
</script>
<script src="../shared/bridge.js"></script>
<script>
/* H2b 首页 feed:rec/distance→/meituan/feed,sales→/meituan/top-sales;触底翻页 + 每 tab 5min 缓存
+ loading/empty/degraded 三态 + 去重 + 切 tab 防竞态 + 未授权不打接口。对齐原生 HomeViewModel。 */
(function (global) {
'use strict';
var BEIJING = { longitude: 116.404, latitude: 39.928 };
var ENDPOINT = { rec: '/api/v1/meituan/feed', distance: '/api/v1/meituan/feed', sales: '/api/v1/meituan/top-sales' };
var CACHE_TTL = 5 * 60 * 1000;
var SCROLL_THRESHOLD = 400; // 距底 px 触发翻页
var st = { tab: 'rec', page: 0, hasNext: true, loading: false, loadedOnce: false,
items: [], seq: 0, coords: null, cache: {} };
function $feed() { return document.querySelector('#home .home-feed'); }
function $end() { return document.querySelector('#home .feed-end'); }
function $scroll() { return document.querySelector('#home .home-scroll'); }
function locationGranted() {
try { if (global.SGBridge && SGBridge.isLocationGranted) return !!SGBridge.isLocationGranted(); } catch (e) {}
return false;
}
function getCoords() {
try {
if (global.SGBridge && SGBridge.getLocation) {
var c = SGBridge.getLocation();
if (c && typeof c.longitude === 'number' && typeof c.latitude === 'number') return c;
}
} catch (e) {}
return BEIJING;
}
function esc(s) {
return String(s == null ? '' : s).replace(/[&<>"']/g, function (m) {
return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[m];
});
}
function splitPrice(p) {
var s = String(p == null ? '' : p), i = s.indexOf('.');
return i < 0 ? [s, ''] : [s.slice(0, i), s.slice(i)];
}
// CouponCard → .feed-card(1:1 照原生 FeedCard;feed-tag 只用 price_label/rank_label,不显示「比团购省」)
function renderCard(card) {
var isWaimai = card.platform === 1;
var shop = card.brand_name
? '<span class="shop-name">' + esc(card.brand_name) + '</span><span class="title-sep"></span>' : '';
var title = esc(card.name || '');
if (card.available_poi_num) title += '' + card.available_poi_num + '店通用';
var metaLeft = '';
if (card.distance_text) metaLeft += esc(card.distance_text);
if (card.poi_name) metaLeft += (metaLeft ? ' ' : '') + esc(card.poi_name);
var saleVol = card.sale_volume ? '<span class="meta-right">' + esc(card.sale_volume) + '</span>' : '';
var labels = [];
if (card.price_label && card.price_label.trim()) labels.push(card.price_label.trim());
if (card.rank_label && card.rank_label.trim()) labels.push(card.rank_label.trim());
var tag = labels.length
? '<div class="feed-tag">' + esc(labels[Math.floor(Math.random() * labels.length)]) + '</div>' : '';
var pr = splitPrice(card.sell_price);
var el = document.createElement('div');
el.className = 'feed-card';
el.dataset.sign = card.product_view_sign || '';
el.dataset.platform = card.platform;
if (card.biz_line != null) el.dataset.bizLine = card.biz_line;
el.innerHTML =
'<div class="feed-img"><img src="' + esc(card.head_image_url || '') + '" alt="">' +
'<span class="feed-cat-badge ' + (isWaimai ? 'cat-waimai">外卖' : 'cat-tuangou">团购') + '</span></div>' +
'<div class="feed-body">' +
'<div class="feed-title">' + shop + title + '</div>' +
'<div class="feed-meta"><span class="meta-left">' + metaLeft + '</span>' + saleVol + '</div>' +
tag +
'<div class="feed-pricerow">' +
'<div class="feed-price"><span class="yuan">¥</span><span class="num">' + esc(pr[0]) +
(pr[1] ? '<small>' + esc(pr[1]) + '</small>' : '') + '</span></div>' +
'<span class="feed-original">¥' + esc(card.original_price || '') + '</span>' +
'</div>' +
'</div>' +
'<div class="feed-grab">抢</div>';
return el;
}
function renderList(newItems, append) {
var feed = $feed(); if (!feed) return;
if (!append) feed.innerHTML = '';
newItems.forEach(function (c) { feed.appendChild(renderCard(c)); });
var end = $end();
if (end) end.style.display = (!st.hasNext && st.items.length) ? '' : 'none';
}
function reqBody(tab, page, coords) {
return tab === 'sales'
? { page: page }
: { longitude: coords.longitude, latitude: coords.latitude, page: page, tab: tab };
}
function loadPage(page, append) {
var tab = st.tab, coords = getCoords(), mySeq = ++st.seq;
st.coords = coords; st.loading = true;
if (global.HomeFeedStates) HomeFeedStates(append ? 'loadingMore' : 'loading');
fetch(ENDPOINT[tab], {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(reqBody(tab, page, coords)),
}).then(function (r) { return r.ok ? r.json() : null; }).then(function (d) {
if (mySeq !== st.seq) return; // 竞态:已切 tab/重载 → 丢弃旧响应
st.loading = false; st.loadedOnce = true;
if (!d) { if (global.HomeFeedStates) HomeFeedStates('degraded'); return; }
var items = d.items || [];
if (append) {
var seen = {}; st.items.forEach(function (c) { seen[c.product_view_sign] = 1; });
items = items.filter(function (c) { return c.product_view_sign && !seen[c.product_view_sign]; });
st.items = st.items.concat(items);
} else { st.items = items; }
st.page = page; st.hasNext = !!d.has_next;
st.cache[tab] = { items: st.items.slice(), page: st.page, hasNext: st.hasNext,
loadedAt: (append && st.cache[tab]) ? st.cache[tab].loadedAt : Date.now() };
renderList(append ? items : st.items, append);
if (global.HomeFeedStates) HomeFeedStates(d.status === 'degraded' ? 'degraded' : (st.items.length ? 'ok' : 'empty'));
}).catch(function () {
if (mySeq !== st.seq) return;
st.loading = false; st.loadedOnce = true;
if (global.HomeFeedStates) HomeFeedStates('degraded');
});
}
function loadFirst() { st.page = 0; st.hasNext = true; st.items = []; renderList([], false); loadPage(1, false); }
function loadNext() { if (!st.hasNext || st.loading) return; loadPage(st.page + 1, true); }
function switchTab(tab) {
if (tab === st.tab) return;
st.seq++; // 作废旧 tab 在飞的请求
st.tab = tab;
var c = st.cache[tab];
if (c && Date.now() - c.loadedAt < CACHE_TTL) { // 命中新鲜缓存 → 直接恢复,不转圈
st.items = c.items.slice(); st.page = c.page; st.hasNext = c.hasNext; st.loading = false;
renderList(st.items, false);
if (global.HomeFeedStates) HomeFeedStates(st.items.length ? 'ok' : 'empty');
} else { loadFirst(); }
}
function selectTabWithoutLoad(tab) { // 未授权:只切状态,不打接口
if (tab === st.tab) return;
st.seq++; st.tab = tab; st.page = 0; st.hasNext = true; st.items = []; renderList([], false);
}
// 坐标变化(rec/distance 依赖坐标)→ 清全部缓存并重拉当前 tab
function refreshIfCoordsChanged() {
var c = getCoords();
if (st.coords && (st.coords.longitude !== c.longitude || st.coords.latitude !== c.latitude)) {
st.cache = {};
if (locationGranted()) loadFirst();
}
}
function bindScroll() { // 触底翻页
var sc = $scroll(); if (!sc || sc._feedBound) return; sc._feedBound = true;
sc.addEventListener('scroll', function () {
if (sc.scrollTop + sc.clientHeight >= sc.scrollHeight - SCROLL_THRESHOLD) loadNext();
});
}
global.HomeFeed = {
loadFirst: loadFirst, switchTab: switchTab, selectTabWithoutLoad: selectTabWithoutLoad,
refreshIfCoordsChanged: refreshIfCoordsChanged, bindScroll: bindScroll,
locationGranted: locationGranted, state: st,
};
document.addEventListener('DOMContentLoaded', bindScroll);
})(window);
</script>
<script>
/* H2b feed 三态 + 抢:loading 首次转圈 / empty 复用「附近暂无…去比价」/ degraded 服务繁忙;
抢 → referral-link([1,3]) → openMeituan(deeplink||h5)。与 H2a applyLocationState 协作切态。 */
(function (global) {
'use strict';
function el(id) { return document.getElementById(id); }
function hide(n) { if (n) n.style.display = 'none'; }
// feed 子态机:loading/degraded 本函数管;ok/empty 交回 applyLocationState 按卡片数切 feed/附近暂无空态。
global.HomeFeedStates = function (kind) {
var feed = document.querySelector('#home .home-feed');
var loading = el('homeFeedLoading');
var degraded = el('homeFeedDegraded');
var noDeal = el('homeFeedNoDealEmpty');
var hasItems = !!(global.HomeFeed && HomeFeed.state.items.length);
if (kind === 'loading' && !hasItems) { // 首次加载中:转圈,压住空态/feed
if (loading) loading.style.display = 'flex';
hide(degraded); hide(noDeal); if (feed) feed.style.display = 'none';
return;
}
if (kind === 'degraded' && !hasItems) { // 上游失败且无数据:服务繁忙
if (degraded) degraded.style.display = 'flex';
hide(loading); hide(noDeal); if (feed) feed.style.display = 'none';
return;
}
// ok / empty / loadingMore / 有数据时的 degraded:收起 loading+degraded,交 H2a 按卡片数定 feed/附近暂无
hide(loading); hide(degraded);
try { applyLocationState(); } catch (e) {}
};
// 抢:换链(linkTypeList=[1,3])→ deeplink 优先, 退 h5 → SGBridge.openMeituan(事件委托,适配动态卡)
document.addEventListener('click', function (e) {
var grab = e.target.closest('#home .feed-card .feed-grab');
if (!grab) return;
var card = grab.closest('.feed-card');
if (!card || card._grabbing) return;
card._grabbing = true;
var prev = grab.textContent; grab.textContent = '…';
fetch('/api/v1/meituan/referral-link', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
product_view_sign: card.dataset.sign,
platform: Number(card.dataset.platform) || 1,
biz_line: card.dataset.bizLine != null ? Number(card.dataset.bizLine) : null,
link_type_list: [1, 3],
}),
}).then(function (r) { return r.ok ? r.json() : null; }).then(function (d) {
var map = (d && d.link_map) || {};
var uri = map['3'] || map['1'] || (d && d.link) || '';
if (uri && global.SGBridge && SGBridge.openMeituan) SGBridge.openMeituan(uri);
else if (global.SGBridge && SGBridge.toast) SGBridge.toast('暂时无法跳转,请稍后再试');
else console.log('[抢] openMeituan →', uri);
}).catch(function () {
if (global.SGBridge && SGBridge.toast) SGBridge.toast('暂时无法跳转,请稍后再试');
}).then(function () { grab.textContent = prev; card._grabbing = false; });
});
})(window);
</script>
<script>
/* H1→收尾:导航类 go() 已接 SGBridge.navigate(原生壳跳 教程/邀请/比价结果);去领取/去点餐/去授权仍占位(H3/H4)。
占位 = 轻量 toast + console,不触发原型多屏切换(其它 .screen 已删,原型切屏会 JS 报错)。
仅真实点击才弹 toast:pointerdown 记最近手势,过滤加载时的自动调用(如领券提醒 setTimeout(onTriClaimClick))。 */
(function(){
var lastGesture = 0;
document.addEventListener('pointerdown', function(){ lastGesture = Date.now(); }, true);
var hideTimer;
function placeholderToast(){
var el = document.getElementById('h1PlaceholderToast');
if (!el){
el = document.createElement('div');
el.id = 'h1PlaceholderToast';
el.style.cssText = 'position:fixed;left:50%;bottom:96px;transform:translateX(-50%);max-width:78%;'
+ 'background:rgba(0,0,0,.8);color:#fff;font-size:13px;font-weight:500;line-height:1.4;text-align:center;'
+ 'padding:9px 16px;border-radius:10px;z-index:9999;opacity:0;transition:opacity .18s;pointer-events:none;';
document.body.appendChild(el);
}
el.textContent = '功能开发中,敬请期待';
el.style.opacity = '1';
clearTimeout(hideTimer);
hideTimer = setTimeout(function(){ el.style.opacity = '0'; }, 1400);
}
function placeholder(label){
return function(){
try { console.log('[H1 占位] ' + label + ' — H2-H4 接桥'); } catch(e){}
if (Date.now() - lastGesture < 1200) placeholderToast(); // 仅手势触发才弹,滤掉加载时自动调用
};
}
// 导航类按钮 → SGBridge.navigate(原生壳跳真实页);其余目标本轮未接,维持占位
var NAV_ROUTES = { guideVideo: 'guideVideo', invite: 'invite', compresult: 'compareResult' };
window.go = function(target){
var route = NAV_ROUTES[target];
if (route && window.SGBridge && typeof SGBridge.navigate === 'function') { SGBridge.navigate(route); return; }
try { console.log('[H1 占位] go(' + (target == null ? '' : target) + ') — 未接线/无桥,占位'); } catch(e){}
if (Date.now() - lastGesture < 1200) placeholderToast();
};
// onTriClaimClick(去领取)已接真实领券(见上方定义),从占位移除
// onHomeCompareClick(去点餐)已接真实 picker + launchApp(见上方定义),从占位移除;去领取/去授权仍占位
// goAuthLocation(去授权)已接真·定位权限(见上方定义),从占位移除;去领取仍占位
})();
/* H1:三数据卡 + 跑马灯接同源 /api/v1/platform/*(均不鉴权;失败回落原型 mock) */
(function(){
// 三数据卡:替换静态数字(顺序 帮助用户/完成比价/累计节省)
fetch('/api/v1/platform/stats').then(function(r){return r.ok?r.json():null;}).then(function(s){
if(!s) return;
var v=document.querySelectorAll('.home-3stat .h3s-value');
if(v.length<3) return;
v[0].innerHTML=s.help_users+'<span class="h3s-unit">人</span>';
v[1].innerHTML=s.total_compares+'<span class="h3s-unit">次</span>';
v[2].innerHTML=(s.total_saved_cents/100).toFixed(1)+'<span class="h3s-unit">元</span>';
}).catch(function(){});
// 跑马灯:原地更新现有行文本(不重建 DOM→保住滚动);末行是首条副本,用 data[0] 保无缝循环
fetch('/api/v1/platform/savings-feed?limit=8').then(function(r){return r.ok?r.json():null;}).then(function(d){
if(!d||!d.items||!d.items.length) return;
var rows=document.querySelectorAll('#homeMarqueeTrack .home-mq-item');
if(!rows.length) return;
var data=d.items;
rows.forEach(function(el,i){
var src=(i===rows.length-1)?data[0]:data[i%data.length];
var t=el.querySelector('.mq-time'); if(t)t.textContent=src.time;
var u=el.querySelector('.mq-user'); if(u)u.textContent=src.masked_user;
var a=el.querySelector('.mq-amt'); if(a)a.textContent=(src.saved_amount_cents/100).toFixed(2);
});
}).catch(function(){});
})();
</script>
</body>
</html>