diff --git a/AGENTS.md b/AGENTS.md index 20d2025..77f5236 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -80,6 +80,12 @@ nginx:`root /opt/app` + `Cache-Control: no-cache` + `etag on` —— 缓存靠 ⚠️ 卡是固定高度 bento,只有 kick / weight 有位置放第二行(`.mc-extra`);给 fweight/cal/curve 加第二行前 先重排那笔高度账(`158+10+64=232`,见 `mama-box.css`)。 +12. **⚠️ 第 2 条「不造假数据」的唯一登记例外:首页「此刻,孕妈们在做什么」卡(2026-10-01 起)** + - 内容:该卡的总人数 / 今日三项 / 匿名动态在**模拟阶段**为 box_app 按规则生成的模拟数字(`GET /pulse`,确定性哈希、不落库、不进任何报表)。 + - 决策人:产品负责人(用户本人),2026-10-01 拍板「前期先用模拟数据,要显示人多;正式页先用模拟,真实数据跟上后替换」;已书面告知虚假宣传风险(《反不正当竞争法》第 8 条)。 + - 期限:**最晚 2026-12-31** 复核(≤ 3 个月);真实数据接通后在后台把 `MamaBoxConfig.pulseMode` 改成 `real`(或 `off`),无需发版。 + - 边界:**只限这张卡**。孕期盲盒的数字(已收集、本周开盒人数)一律真实;任何别处都不得引用这里的模拟数字,前端也不得在接口失败时自造数字兜底。 + ## 5. 踩坑 - **不要给别的应用追加 `#openid`**:部分应用自带 hash 路由(如 `/baobao-yingji`),追加 hash 会被当非法路由 → 页面未找到。身份一律走 query(`withIdentity()` 已封装)。 diff --git a/assets/img/0fa71babe9a2.jpg b/assets/img/0fa71babe9a2.jpg new file mode 100644 index 0000000..de856fe Binary files /dev/null and b/assets/img/0fa71babe9a2.jpg differ diff --git a/assets/img/2314f334e4d0.jpg b/assets/img/2314f334e4d0.jpg new file mode 100644 index 0000000..bbda585 Binary files /dev/null and b/assets/img/2314f334e4d0.jpg differ diff --git a/assets/img/2a88a5a7c1c3.jpg b/assets/img/2a88a5a7c1c3.jpg new file mode 100644 index 0000000..9de766d Binary files /dev/null and b/assets/img/2a88a5a7c1c3.jpg differ diff --git a/assets/img/5ae2e7f617e2.jpg b/assets/img/5ae2e7f617e2.jpg new file mode 100644 index 0000000..bf6b2ed Binary files /dev/null and b/assets/img/5ae2e7f617e2.jpg differ diff --git a/assets/img/6a17a98c52b8.jpg b/assets/img/6a17a98c52b8.jpg new file mode 100644 index 0000000..7ee30d1 Binary files /dev/null and b/assets/img/6a17a98c52b8.jpg differ diff --git a/assets/img/6bdabd9eecc5.jpg b/assets/img/6bdabd9eecc5.jpg new file mode 100644 index 0000000..599c9cb Binary files /dev/null and b/assets/img/6bdabd9eecc5.jpg differ diff --git a/assets/img/83f0ab30cf36.jpg b/assets/img/83f0ab30cf36.jpg new file mode 100644 index 0000000..9226a16 Binary files /dev/null and b/assets/img/83f0ab30cf36.jpg differ diff --git a/assets/img/affcfc61f21f.jpg b/assets/img/affcfc61f21f.jpg new file mode 100644 index 0000000..6b3caea Binary files /dev/null and b/assets/img/affcfc61f21f.jpg differ diff --git a/assets/img/b743ed8646d5.jpg b/assets/img/b743ed8646d5.jpg new file mode 100644 index 0000000..e7eb8dc Binary files /dev/null and b/assets/img/b743ed8646d5.jpg differ diff --git a/assets/img/bddd81b7315d.jpg b/assets/img/bddd81b7315d.jpg new file mode 100644 index 0000000..9f4246e Binary files /dev/null and b/assets/img/bddd81b7315d.jpg differ diff --git a/assets/img/c6e9c5612d8e.jpg b/assets/img/c6e9c5612d8e.jpg new file mode 100644 index 0000000..d03c986 Binary files /dev/null and b/assets/img/c6e9c5612d8e.jpg differ diff --git a/assets/img/cceeb3b3f4ef.jpg b/assets/img/cceeb3b3f4ef.jpg new file mode 100644 index 0000000..2976eb5 Binary files /dev/null and b/assets/img/cceeb3b3f4ef.jpg differ diff --git a/assets/img/d41305de1db9.jpg b/assets/img/d41305de1db9.jpg new file mode 100644 index 0000000..db8f395 Binary files /dev/null and b/assets/img/d41305de1db9.jpg differ diff --git a/assets/img/fe00c2bddf9b.jpg b/assets/img/fe00c2bddf9b.jpg new file mode 100644 index 0000000..24cb9af Binary files /dev/null and b/assets/img/fe00c2bddf9b.jpg differ diff --git a/index.html b/index.html index 6f3cd95..8a09d79 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,7 @@ 兜爸心选 · 孕育陪伴 - + @@ -182,6 +182,19 @@ 宫格右下角那张 .mini-card.more 是**内容**位(顺带把第三列剩下的 44px 填平,而且是个够手指点的大靶子)。两个都指向 data-sheet="open", 删掉任意一个另一个都还走得通。 --> + + +
@@ -274,6 +287,24 @@
+ +
+ +
+ 此刻 +

孕妈们在做什么

+
+
+ + 0 +
+
+
+
+ +
+
+ + 孕期盲盒 + +
+
+
+ + +
+
+ + + + +
+
+
+
还有 个盲盒等你打开 错过的周会一直保留到孕 40 周
+
+
+
+
+

我的宝宝卡册

+ 0/37 +
+
+

+
+
+
+ 规则与概率说明 +
+
+
+ +
+
diff --git a/js/app.js b/js/app.js index f024e65..0314c4e 100644 --- a/js/app.js +++ b/js/app.js @@ -3,7 +3,7 @@ (function () { 'use strict'; /* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */ - var VER = '26093025'; + var VER = '26100101'; const $ = (s, c) => (c || document).querySelector(s); const $$ = (s, c) => Array.from((c || document).querySelectorAll(s)); @@ -2735,6 +2735,7 @@ applyRemoteFootprint(); applyRemoteCardsFetch(); syncLaixinReminder(); + loadBlindbox(); // 孕期盲盒:要等 ② 的预产期落地(服务端按同一份上游视图算孕周) }); } /* ---------- 远程配置(box_app 后台下发):已下线 ---------- @@ -3086,6 +3087,388 @@ }, 2600); })(); + /* ================= 此刻,孕妈们在做什么(2026-10-01,需求文档 v1.0 第二节) ================= + GET /api/mp/public/mama-box/pulse(免登录、所有人同一份)→ { show, total, today[], feed[] }。 + · 数据是模拟还是真实由 box_app 后台 pulseMode 决定,前端不区分、不兜底造数:show=false / 接口失败 → 整卡隐藏。 + ⚠️ 模拟阶段是 AGENTS.md §4.12 登记的例外,只限这张卡;别把这里的「失败兜底」改成前端自造数字。 + · 动效:进入视口时大数字从 0 数上去;之后每 60 秒刷新一次,数字平滑涨到新值;动态每 3 秒往上翻一条。 + · 今日三项可点:kick / weight 复用首页工具卡的跳转(MINI_APP_MAP),game 去消消乐。 */ + const PULSE_ICO = { + kick: '', + weight: '', + cal: '', + game: '', + box: '' + }; + const fmtN = n => String(Math.round(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ','); + /* 数字补间:el.dataset.v 记当前值,从它平滑走到 to;首次(无 dataset.v)从 0 起 */ + function tweenNum(el, to, dur) { + if (!el) return; + const from = el.dataset.v == null ? 0 : +el.dataset.v; + el.dataset.v = to; + if (from === to || (window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches)) { el.textContent = fmtN(to); return; } + const t0 = performance.now(), d = dur || 1400; + (function step(t) { + const k = Math.min(1, (t - t0) / d), e = 1 - Math.pow(1 - k, 4); + el.textContent = fmtN(from + (to - from) * e); + if (k < 1) requestAnimationFrame(step); + })(t0); + } + (function pulseCard() { + const sec = $('#pulse'); + if (!sec) return; + let seen = false, last = null, feed = [], fi = 0, ticking = null; + const TODAY_GO = { + kick: () => { const b = $('.mini-card[data-mini="kick"]'); if (b) b.click(); }, + weight: () => { const b = $('.mini-card[data-mini="weight"]'); if (b) b.click(); }, + game: () => { if (GAME_URL) jumpApp(GAME_URL, GAME_ID); } + }; + function rowHTML(f) { + /* 「孕 29 周的妈妈 数完胎动,宝宝动了 13 次」→ 上行「孕 29 周的妈妈 · 刚刚」,下行动作;数字标色 */ + const t = String(f.text || ''), i = t.indexOf('的妈妈 '); + const who = i > 0 ? t.slice(0, i + 3) : '一位妈妈', what = i > 0 ? t.slice(i + 4) : t; + const esc = s => s.replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]); + return '
' + (PULSE_ICO[f.act] || PULSE_ICO.box) + + '' + esc(who) + ' · ' + esc(f.ago || '刚刚') + '' + + esc(what).replace(/(\d+)/g, '$1') + '
'; + } + function tick() { + const tr = $('#plTrack'); + if (!tr || feed.length < 2 || document.hidden) return; + fi = (fi + 1) % feed.length; + tr.insertAdjacentHTML('beforeend', rowHTML(feed[fi])); + tr.classList.add('is-move'); + tr.style.transform = 'translateY(-46px)'; + setTimeout(() => { + tr.classList.remove('is-move'); + if (tr.firstElementChild) tr.removeChild(tr.firstElementChild); + tr.style.transform = ''; + }, 600); + } + function paint() { + const r = last; + if (!r || !r.show) { sec.classList.add('hide'); return; } + sec.classList.remove('hide'); + const tot = $('#plTotal'); + tot.classList.toggle('hide', !r.total); + if (r.total) { + $('#plPre').textContent = r.total.prefix || ''; + $('#plSuf').textContent = r.total.suffix || ''; + if (seen) tweenNum($('#plNum'), r.total.value, 1800); + } + const td = $('#plToday'), items = r.today || []; + td.classList.toggle('hide', !items.length); + const keys = items.map(x => x.key).join(','); + if (td.dataset.keys !== keys) { + td.dataset.keys = keys; + td.innerHTML = items.map(x => '').join(''); + } + if (seen) items.forEach(x => tweenNum(td.querySelector('[data-k="' + x.key + '"] b'), x.value, 1600)); + feed = r.feed || []; + const fe = $('#plFeed'); + fe.classList.toggle('hide', !feed.length); + if (feed.length) { + fi = 0; + $('#plTrack').innerHTML = rowHTML(feed[0]); + if (!ticking) ticking = setInterval(tick, 3000); + } + } + $('#plToday').addEventListener('click', e => { + const b = e.target.closest('.pl-stat'); + if (b && TODAY_GO[b.dataset.k]) { haptic(); TODAY_GO[b.dataset.k](); } + }); + function load() { + return fetch('/api/mp/public/mama-box/pulse') + .then(r => (r.ok ? r.json() : null)) + .then(j => { if (j && j.code === 200 && j.result) { last = j.result; paint(); } }) + .catch(() => { /* 静默:保持现状(首次失败即一直隐藏) */ }); + } + /* 进入视口才开始数数,否则用户滑到这里时数字早就数完了 */ + function onSeen() { + if (seen) return; + seen = true; + paint(); + } + if ('IntersectionObserver' in window) { + const io = new IntersectionObserver(es => { if (es.some(e => e.isIntersecting)) { io.disconnect(); onSeen(); } }, { threshold: .25 }); + io.observe(sec); + } else seen = true; + load(); + setInterval(() => { if (!document.hidden) load(); }, 60000); + })(); + + /* ================= 孕期盲盒(2026-10-01,需求文档 v1.0 第三节) ================= + 接口(box_app,openid 身份): + GET /api/mp/public/mama-box/blindbox?openid= → 横幅 + 本页的全部状态(含卡册、概率表) + POST /api/mp/public/mama-box/blindbox/open?openid=&week= → 开盒(服务端抽卡/抽奖励,原子去重) + · 抽什么完全由服务端决定,前端只播动画;概率说明用接口下发的 probs,和服务端永远一致(合规:公示概率)。 + · 访客态 / 孕 4 周前 / 接口失败 → 横幅保持隐藏。未填预产期 → 横幅改成「填预产期领盲盒」引导。 + · 周卡插画在 assets/img(文件名 = md5 前 12 位),CARD_IMG 是孕周 → 文件的表;没有图的孕周用「本周水果」占位画。 */ + const CARD_IMG = { + 4: 'b743ed8646d5', 5: 'bddd81b7315d', 6: 'affcfc61f21f', 7: 'fe00c2bddf9b', 8: '2a88a5a7c1c3', 9: '6a17a98c52b8', + 10: 'c6e9c5612d8e', 11: '5ae2e7f617e2', 12: '0fa71babe9a2', 13: '6bdabd9eecc5', 15: 'd41305de1db9', 16: 'cceeb3b3f4ef', + 24: '83f0ab30cf36', 32: '2314f334e4d0' + }; + const BX_GROUPS = [ + { name: '孕早期', from: 4, to: 13, c: '#9ED8C0' }, + { name: '孕中期', from: 14, to: 27, c: '#FFB08F' }, + { name: '孕晚期', from: 28, to: 40, c: '#C9B6FF' } + ]; + let BB = null, bbSel = null, bbBusy = false, bbNew = null, giftSeq = 0; + function giftSVG() { + const g = 'gf' + (++giftSeq); + return ''; + } + const bbEsc = s => String(s == null ? '' : s).replace(/[&<>"]/g, c => ({ '&': '&', '<': '<', '>': '>', '"': '"' })[c]); + function cardImgHTML(w) { + const h = CARD_IMG[w]; + return h ? '孕 ' + w + ' 周宝宝卡' + : ''; + } + /* 卡正面。card = { week, v, size:{name,txt}, say } */ + function cardFrontHTML(c) { + const sz = c.size || babySize(c.week); + return '
' + + '✦ 闪卡No.' + String(c.week).padStart(2, '0') + '' + + '
' + cardImgHTML(c.week) + '
' + + '
孕' + c.week + '周像' + bbEsc(sz.name) + ' · ' + bbEsc(sz.txt) + '
' + + '

' + (c.say ? '“' + bbEsc(c.say) + '”' : '') + '

' + + '
'; + } + function bbApi(path, method) { + const oid = hashParam('openid'); + return fetch('/api/mp/public/mama-box/' + path + (path.indexOf('?') >= 0 ? '&' : '?') + 'openid=' + encodeURIComponent(oid), { method: method || 'GET' }) + .then(r => r.json().catch(() => null)); + } + function loadBlindbox() { + if (!hashParam('openid')) { $('#bbBanner').classList.add('hide'); return Promise.resolve(); } + return bbApi('blindbox').then(j => { + if (!j || j.code !== 200 || !j.result) return; + BB = j.result; + renderBanner(); + if (BB.backfilled > 0) setTimeout(() => toast('错过的 ' + BB.backfilled + ' 张宝宝卡已补给你,去卡册看看吧'), 1800); + if ($('#detailBox').classList.contains('show')) renderBox(); + }).catch(() => { /* 静默:横幅保持现状 */ }); + } + function renderBanner() { + const ban = $('#bbBanner'); + const set = (t, s, cta, ready, calm) => { + $('#bbTitle').textContent = t; + $('#bbSub').innerHTML = s; + $('#bbCta').textContent = cta; + ban.classList.toggle('is-ready', !!ready); + ban.classList.toggle('is-calm', !!calm); + ban.classList.remove('hide'); + }; + if (!BB || !BB.enabled || BB.notYet) { ban.classList.add('hide'); return; } + if (!$('#bbGift').firstChild) $('#bbGift').innerHTML = giftSVG(); + const badge = $('#bbBadge'); + badge.classList.add('hide'); + if (BB.needProfile) { set('每周一个宝宝盲盒', '填好预产期就能领 · 完全免费', '去填写', true); return; } + const got = '已收集 ' + BB.collected + '/' + BB.totalWeeks; + const op = BB.openable || []; + if (op.length > 1) { badge.textContent = op.length; badge.classList.remove('hide'); } + if (BB.ended) set('我的孕期卡册', got + ' 张宝宝卡,永久保留', '去看看', false, true); + else if (!BB.currentOpened && op.indexOf(BB.week) >= 0) set('孕 ' + BB.week + ' 周的盲盒到了', got + ' · 免费开启', '去开启', true); + else if (op.length) set('还有 ' + op.length + ' 个盲盒没开', '错过的周也能补开 · ' + got, '去补开', true); + else set(BB.nextIn ? '下一个盲盒 ' + BB.nextIn + ' 天后到' : '本周盲盒已开启', got + (BB.flash ? ' · 闪卡 ' + BB.flash + ' 张' : ''), '看卡册', false, true); + } + $('#bbBanner').addEventListener('click', () => { + if (!BB) return; + if (BB.needProfile) { openModal('modalProfile'); return; } + bbSel = null; + renderBox(); + openPanel('detailBox'); + }); + function renderBox() { + if (!BB || BB.needProfile) return; + const op = BB.openable || []; + if (op.indexOf(bbSel) < 0) bbSel = op.indexOf(BB.week) >= 0 ? BB.week : (op[0] || null); + const st = $('#bxStage'), box = $('#bxBox'), mini = $('#bxMini'), btn = $('#bxOpen'); + st.classList.remove('is-shaking', 'is-popping'); + btn.disabled = false; + if (bbSel) { + st.classList.remove('is-done'); + $('#bxWeek').textContent = '孕 ' + bbSel + ' 周 · 宝宝盲盒'; + $('#bxHint').textContent = bbSel === BB.week ? '宝宝给你准备了这周的小惊喜' : '这是你错过的一周,现在补开'; + box.innerHTML = giftSVG(); + box.classList.remove('hide'); mini.classList.add('hide'); + btn.textContent = '开启盲盒'; btn.classList.remove('hide'); + } else { + st.classList.add('is-done'); + box.classList.add('hide'); + const cc = BB.currentCard; + $('#bxWeek').textContent = BB.ended ? '孕期卡册' : '孕 ' + BB.week + ' 周 · 已开启'; + $('#bxHint').textContent = BB.ended ? (BB.postpartum ? '宝宝出生啦,卡册会一直留着' : '孕期盲盒已经全部开完') : + (BB.nextIn ? '下一个盲盒 ' + BB.nextIn + ' 天后到' : '本周的盲盒已经开过啦'); + if (cc) { mini.innerHTML = cardFrontHTML(cc); mini.classList.remove('hide'); mini.onclick = () => showReveal(cc, null, false); } + else { mini.classList.add('hide'); box.innerHTML = giftSVG(); box.classList.remove('hide'); } + btn.classList.toggle('hide', !cc); + btn.textContent = '看看这张卡'; + } + $('#bxMisc').textContent = (BB.weekOpeners >= 50) ? '本周已有 ' + fmtN(BB.weekOpeners) + ' 位妈妈开了盲盒' : '每周一个 · 完全免费'; + /* 待补开 */ + const pend = $('#bxPend'); + pend.classList.toggle('hide', op.length < 2); + if (op.length >= 2) { + $('#bxPendN').textContent = op.length; + $('#bxChips').innerHTML = op.map(w => '').join(''); + } + /* 卡册 */ + $('#bxGot').textContent = BB.collected; + $('#bxAll').textContent = BB.totalWeeks; + requestAnimationFrame(() => { $('#bxBar').style.width = Math.round(BB.collected / BB.totalWeeks * 100) + '%'; }); + const nBack = (BB.album || []).filter(a => a.src === 'backfill').length; + $('#bxAnote').textContent = (BB.flash ? '其中闪卡 ' + BB.flash + ' 张。' : '') + (nBack ? '标「补」的是来之前的孕周,已自动补发普通款。' : ''); + const byW = {}; + (BB.album || []).forEach(a => { byW[a.week] = a; }); + $('#bxGroups').innerHTML = BX_GROUPS.map(g => { + let n = 0, cells = ''; + for (let w = g.from; w <= g.to; w++) { + const a = byW[w] || {}; + if (a.got) { + n++; + cells += ''; + } else if (op.indexOf(w) >= 0) { + cells += ''; + } else { + cells += '
?' + w + ' 周
'; + } + } + const all = g.to - g.from + 1; + return '
' + g.name + ' · ' + g.from + '–' + g.to + ' 周' + + (n === all ? '已集齐 ✓' : n + '/' + all) + '
' + cells + '
'; + }).join(''); + bbNew = null; + /* 规则与概率(接口下发) */ + const pb = BB.probs || {}; + const bar = x => '
' + bbEsc(x.name) + '' + x.p + '%
'; + $('#bxRulesBody').innerHTML = '

宝宝卡(每盒必出 1 张)

' + (pb.card || []).map(bar).join('') + + '

随机奖励(每盒 1 份)

' + (pb.reward || []).map(bar).join('') + + '

规则

' + + '

孕期盲盒完全免费,不出售、不需要任何付费。

'; + } + $('#bxChips').addEventListener('click', e => { + const b = e.target.closest('.bx-chip'); + if (!b || bbBusy) return; + bbSel = +b.dataset.w; haptic(); renderBox(); + $('#detailBox .detail-body').scrollTo({ top: 0, behavior: 'smooth' }); + }); + $('#bxGroups').addEventListener('click', e => { + const t = e.target.closest('.bx-t'); + if (!t || bbBusy) return; + if (t.dataset.op) { bbSel = +t.dataset.op; renderBox(); $('#detailBox .detail-body').scrollTo({ top: 0, behavior: 'smooth' }); return; } + const w = +t.dataset.w; + if (!w) return; + /* 卡册里点开一张:大小来自本地 SIZE 表,发育说明取孕期小知识的「宝宝这周」 */ + const c = { week: w, v: t.dataset.v, size: babySize(w), say: '' }; + showReveal(c, null, false); + loadTips().then(() => { const x = TIPS && TIPS[w]; if (x && x.baby && $('#bxReveal').classList.contains('show')) $('#bxDev').textContent = x.baby; }); + }); + $('#bxRuleBtn').addEventListener('click', () => { + const d = $('#bxRules'); d.open = true; + d.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }); + $('#bxBox').addEventListener('click', () => openBox()); + $('#bxOpen').addEventListener('click', () => { + if (bbSel) openBox(); + else if (BB && BB.currentCard) showReveal(BB.currentCard, null, false); + }); + function openBox() { + if (bbBusy || !bbSel) return; + bbBusy = true; haptic(); + const st = $('#bxStage'), btn = $('#bxOpen'), w = bbSel, t0 = Date.now(); + st.classList.add('is-shaking'); + btn.disabled = true; btn.textContent = '正在打开…'; + bbApi('blindbox/open?week=' + w, 'POST').catch(() => null).then(j => { + /* 至少晃 1.3 秒:接口太快时动画一闪而过,没有「拆」的感觉 */ + setTimeout(() => { + st.classList.remove('is-shaking'); + if (!j || j.code !== 200 || !j.result) { + bbBusy = false; btn.disabled = false; btn.textContent = '开启盲盒'; + toast((j && j.message) || '网络开小差了,再试一次'); + if (j && j.code === 400) loadBlindbox(); + return; + } + st.classList.add('is-popping'); + if (navigator.vibrate) navigator.vibrate([12, 40, 18]); + const r = j.result; + bbNew = r.card.week; + setTimeout(() => { showReveal(r.card, r.reward, true); bbBusy = false; }, 420); + loadBlindbox(); // 后台刷新横幅与卡册(翻卡层盖着,用户看不到重绘) + }, Math.max(0, 1300 - (Date.now() - t0))); + }); + } + function burst(flash) { + const box = $('#bxConfetti'); + const C = flash ? ['#FFD66E', '#FF9AD5', '#9ADFFF', '#fff', '#C9B6FF'] : ['#FFD66E', '#FF8A66', '#FFB3C1', '#fff', '#9ED8C0']; + let h = ''; + for (let i = 0; i < (flash ? 44 : 30); i++) { + const a = Math.random() * Math.PI * 2, d = 90 + Math.random() * 170; + h += ''; + } + box.innerHTML = h; + setTimeout(() => { box.innerHTML = ''; }, 1800); + } + /* 翻卡层。fresh=true:刚开出来(彩纸 + 翻面 + 奖励);false:从卡册/舞台查看(直接正面) */ + function showReveal(card, reward, fresh) { + const rv = $('#bxReveal'), c3 = $('#bxCard3d'), flash = card.v === 'flash'; + $('#bxFront').innerHTML = cardFrontHTML(card); + if (!$('#bxBack').firstChild) $('#bxBack').innerHTML = giftSVG() + '兜爸心选 · 孕期盲盒'; + $('#bxTag').textContent = fresh ? (flash ? '✦ 哇!开出了闪卡 ✦' : '新卡 · 孕 ' + card.week + ' 周') : (flash ? '✦ 闪卡 · 孕 ' + card.week + ' 周' : '孕 ' + card.week + ' 周'); + $('#bxDev').textContent = card.dev || ''; + let rw = ''; + if (reward) { + const tj = reward.type === 'taijiao'; + rw = '
' + (tj ? '🎵' : '💌') + '' + (tj ? '胎教时间 · ' + bbEsc(reward.kind || '') : '宝宝对你说') + + '随机奖励
' + (reward.title ? '
' + bbEsc(reward.title) + '
' : '') + + '

' + bbEsc(reward.text) + '

'; + } + $('#bxReward').innerHTML = rw; + $('#bxKeep').textContent = fresh ? '收进卡册' : '好的'; + rv.classList.remove('is-done', 'is-burst'); + rv.classList.toggle('is-flash', flash); + c3.classList.remove('is-flip', 'no-anim'); + void c3.offsetWidth; + rv.classList.add('show'); + rv.setAttribute('aria-hidden', 'false'); + rv.scrollTop = 0; + if (fresh) { + rv.classList.add('is-burst'); + burst(flash); + setTimeout(() => c3.classList.add('is-flip'), 260); + setTimeout(() => rv.classList.add('is-done'), 1100); + } else { + c3.classList.add('no-anim', 'is-flip'); + rv.classList.add('is-done'); + } + } + function hideReveal() { + const rv = $('#bxReveal'); + rv.classList.remove('show', 'is-done', 'is-burst'); + rv.setAttribute('aria-hidden', 'true'); + } + $('#bxKeep').addEventListener('click', () => { hideReveal(); renderBox(); }); + $('#detailBox .dt-back').addEventListener('click', hideReveal); + /* ================= 孕期小知识(2026-09-30) ================= 内容 assets/data/preg-tips.json:[{week, baby, mom, eat, care, days:[{t,d}×7]}],孕 4–40 周。 第几天 = state.day(0–6),和首页「孕 N 周 D 天」同一口径。 diff --git a/mama-box.css b/mama-box.css index 392b64e..cd10469 100644 --- a/mama-box.css +++ b/mama-box.css @@ -2371,3 +2371,323 @@ a.skip:focus{left:16px;top:16px} font-size:12px;padding:6px 12px;border-radius:9px; } .idn-retry:active{transform:scale(.95)} + +/* ===================================================================== + 孕期盲盒 · 首页细横幅(2026-10-01,需求文档 v1.0 三-7) + 一条奶油粉→薰衣草的渐变细卡:左边会晃的礼盒,中间两行字,右边胶囊。 + ⚠️ 它是 #homeBody 第一个孩子;隐藏时把首块上间距还给工具托盘(下面两条)。 + 动效:礼盒每 3 秒晃一下(只在「有盒子可开」时)+ 一道斜光每 5 秒扫过;减少动态效果时全关。 + ===================================================================== */ +.bb-banner{position:relative;display:flex;align-items:center;gap:11px;width:100%;min-height:66px; + padding:9px 12px 9px 9px;border:1px solid rgba(255,255,255,.9);border-radius:22px;overflow:hidden; + text-align:left;font:inherit;color:#5A2E22; + background:linear-gradient(105deg,#FFF0E2 0%,#FFE3E6 55%,#F1E5FF 100%); + box-shadow:0 10px 24px rgba(226,120,108,.16)} +.bb-banner.hide{display:none} +.home-body>.bb-banner.hide+.tool-dock{margin-top:21px} +.home-body.no-letter>.bb-banner.hide+.tool-dock{margin-top:30px} +.bb-banner:active{transform:scale(.985)} +.bb-gift{position:relative;flex:0 0 auto;width:50px;height:50px;border-radius:16px;display:grid;place-items:center; + background:radial-gradient(circle at 50% 60%,#fff 0,rgba(255,255,255,.55) 55%,rgba(255,255,255,0) 72%)} +.bb-gift svg{width:44px;height:44px;overflow:visible} +.bb-banner.is-ready .bb-gift svg{animation:bbWiggle 3s ease-in-out infinite;transform-origin:50% 90%} +@keyframes bbWiggle{0%,62%,100%{transform:rotate(0) scale(1)}66%{transform:rotate(-10deg) scale(1.06)} + 71%{transform:rotate(9deg) scale(1.08)}76%{transform:rotate(-6deg)}81%{transform:rotate(4deg)}86%{transform:rotate(0)}} +.bb-badge{position:absolute;left:44px;top:6px;min-width:18px;height:18px;padding:0 5px;border-radius:9px; + font-size:11px;font-weight:800;line-height:18px;text-align:center;color:#fff; + background:linear-gradient(135deg,#FF5B6E,#F4593C);box-shadow:0 0 0 2px #fff;animation:bbBadge 1.6s ease-in-out infinite} +.bb-badge.hide{display:none} +@keyframes bbBadge{0%,100%{transform:scale(1)}50%{transform:scale(1.15)}} +.bb-txt{flex:1;min-width:0;display:flex;flex-direction:column;gap:3px} +.bb-t{font-size:15.5px;font-weight:800;letter-spacing:-.2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.bb-s{font-size:11.5px;color:#9A6F62;white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.bb-s b{color:var(--brand);font-weight:800} +.bb-cta{flex:0 0 auto;border-radius:999px;padding:7px 13px;font-size:12.5px;font-weight:800;color:#fff; + background:linear-gradient(135deg,#FF7A5C,#F4593C);box-shadow:0 6px 14px rgba(244,89,60,.3)} +.bb-banner.is-calm .bb-cta{color:var(--brand);background:#fff;box-shadow:0 3px 10px rgba(226,120,108,.16)} +.bb-shine{position:absolute;top:-20%;left:-40%;width:28%;height:140%;pointer-events:none; + background:linear-gradient(90deg,rgba(255,255,255,0),rgba(255,255,255,.75),rgba(255,255,255,0)); + transform:skewX(-20deg);animation:bbShine 5s ease-in-out infinite} +@keyframes bbShine{0%,70%{left:-40%}100%{left:130%}} + +/* 礼盒 SVG(横幅和舞台共用一份画法,见 app.js giftSVG):盖子单独一组,开盒时飞起来 */ +.gift .g-lid{transform-box:fill-box;transform-origin:50% 100%} +.gift .g-body{transform-box:fill-box;transform-origin:50% 100%} + +/* ===================================================================== + 此刻,孕妈们在做什么(2026-10-01,需求文档 v1.0 第二节) + 外壳是首页公共白色通栏。里面三层: + ① 头像叠 + 一个会滚动累加的大数字(进入视口时从 0 数上去,之后每分钟刷新平滑涨) + ② 今日三项:三张浅色小卡,点击进入对应工具 + ③ 匿名动态:一行,每 3 秒往上翻一条 + 背景两团慢慢漂的柔光 + 左上「此刻」红点呼吸,表达「正在发生」。 + ===================================================================== */ +.pulse{margin:16px -26px 0;padding:16px 26px 18px;border-radius:26px;position:relative;z-index:0;overflow:hidden; + background:linear-gradient(160deg,#fff 0%,#fff 45%,#FFF6F0 100%)} +.pulse.hide{display:none} +.pl-orb{position:absolute;z-index:-1;border-radius:50%;filter:blur(18px);opacity:.55;pointer-events:none} +.pl-orb.o1{width:150px;height:150px;right:-40px;top:-50px;background:#FFD9CB;animation:plDrift 9s ease-in-out infinite} +.pl-orb.o2{width:120px;height:120px;left:-30px;bottom:-50px;background:#F1E2FF;animation:plDrift 11s ease-in-out infinite reverse} +@keyframes plDrift{0%,100%{transform:translate(0,0)}50%{transform:translate(-18px,14px)}} +.pl-head{display:flex;align-items:center;gap:8px} +.pl-live{display:inline-flex;align-items:center;gap:5px;font-size:11.5px;font-weight:800;color:#fff; + background:linear-gradient(135deg,#FF6B57,#F4593C);border-radius:999px;padding:3px 9px 3px 7px} +.pl-live i{position:relative;width:6px;height:6px;border-radius:50%;background:#fff} +.pl-live i::after{content:"";position:absolute;inset:-3px;border-radius:50%;border:1.5px solid #fff;animation:plPing 1.6s ease-out infinite} +@keyframes plPing{from{transform:scale(.6);opacity:1}to{transform:scale(1.9);opacity:0}} +.pl-title{font-size:16.5px;font-weight:800;letter-spacing:-.2px} +.pl-total{display:flex;align-items:center;gap:10px;margin-top:14px} +.pl-total.hide,.pl-today.hide,.pl-feed.hide{display:none} +.pl-avs{display:flex;flex:0 0 auto} +.pl-avs i{width:28px;height:28px;border-radius:50%;border:2px solid #fff;margin-left:-9px;box-shadow:0 2px 6px rgba(200,120,100,.18); + background:#FFC9B6 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 28 28'%3E%3Ccircle cx='14' cy='11' r='5' fill='%23fff'/%3E%3Cpath d='M5 26c1-6 5-9 9-9s8 3 9 9z' fill='%23fff'/%3E%3C/svg%3E") center/100% no-repeat} +.pl-avs i:first-child{margin-left:0} +.pl-avs i:nth-child(2){background-color:#C9B6FF} +.pl-avs i:nth-child(3){background-color:#FFD98A} +.pl-avs i:nth-child(4){background-color:#9ED8C0} +.pl-tt{display:flex;flex-wrap:wrap;align-items:baseline;font-size:13px;color:var(--ink-2);line-height:1.3} +#plSuf{flex-basis:100%;margin-top:1px;font-size:12.5px} +.pl-tt b{display:inline-block;margin:0 3px;font-size:24px;font-weight:900;letter-spacing:-.5px;color:var(--brand); + font-variant-numeric:tabular-nums;vertical-align:-2px} +.pl-today{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-top:14px} +.pl-stat{position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:2px;border:0;border-radius:16px; + padding:10px 10px 9px;text-align:left;font:inherit;overflow:hidden;transition:transform .16s} +.pl-stat:active{transform:scale(.96)} +.pl-stat .ps-ico{width:22px;height:22px;border-radius:8px;display:grid;place-items:center;color:#fff;margin-bottom:4px} +.pl-stat .ps-ico svg{width:14px;height:14px} +.pl-stat b{font-size:18px;font-weight:900;letter-spacing:-.4px;font-variant-numeric:tabular-nums} +.pl-stat span{font-size:11px;line-height:1.35;color:var(--ink-2)} +.pl-stat i.go{position:absolute;right:8px;top:10px;font-style:normal;font-size:13px;opacity:.45} +.pl-stat.k-kick{background:#FFF0EA}.pl-stat.k-kick b{color:#E5583C}.pl-stat.k-kick .ps-ico{background:#F4795C} +.pl-stat.k-weight{background:#EEF6EC}.pl-stat.k-weight b{color:#4E8A43}.pl-stat.k-weight .ps-ico{background:#6EAE5E} +.pl-stat.k-game{background:#FFF0F5}.pl-stat.k-game b{color:#D9487A}.pl-stat.k-game .ps-ico{background:#F07AA0} +.pl-feed{position:relative;margin-top:12px;height:46px;overflow:hidden;border-radius:14px;background:#FBF6F2; + -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 18%,#000 82%,transparent 100%); + mask-image:linear-gradient(180deg,transparent 0,#000 18%,#000 82%,transparent 100%)} +.pl-track{will-change:transform} +.pl-track.is-move{transition:transform .55s var(--ease)} +.pl-row{display:flex;align-items:center;gap:9px;height:46px;padding:0 12px} +.pl-row .pr-ico{flex:0 0 auto;width:26px;height:26px;border-radius:50%;display:grid;place-items:center;color:#fff} +.pl-row .pr-ico svg{width:14px;height:14px} +.pl-row .pr-t{flex:1;min-width:0;display:flex;flex-direction:column;gap:1px} +.pl-row .pr-who{font-size:10.5px;color:var(--muted)} +.pl-row .pr-do{font-size:12.5px;font-weight:700;color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis} +.pl-row .pr-do em{font-style:normal;color:var(--brand)} +.a-kick .pr-ico{background:#F4795C}.a-weight .pr-ico{background:#6EAE5E}.a-cal .pr-ico{background:#5B8FD6} +.a-game .pr-ico{background:#F07AA0}.a-box .pr-ico{background:linear-gradient(135deg,#FFB547,#FF7A5C)} + +/* ===================================================================== + 孕期盲盒 · 详情页(2026-10-01) + ① 舞台:珊瑚→玫瑰渐变 + 缓慢旋转的光芒 + 闪烁星星,礼盒悬浮呼吸; + 点「开启」→ 礼盒越晃越厉害(等接口,最少 1.3 秒)→ 盖子飞起、白光一闪 → 翻卡层。 + ② 翻卡层:暗色幕 + 彩纸迸开 + 卡片从背面 3D 翻到正面;闪卡多一层流动的镭射光。 + ③ 卡册:按孕早/中/晚期三组,已集的显示插画,可开的在发光,没到的是问号。 + ===================================================================== */ +.bx .detail-body{padding:16px 16px 40px} +.bx-rule-btn{width:auto;font-size:13px;font-weight:700;color:var(--brand)} +.bx-stage{position:relative;z-index:0;overflow:hidden;border-radius:28px;padding:22px 18px 20px;text-align:center;color:#fff; + background:linear-gradient(165deg,#FFB08F 0%,#FF8B76 42%,#E9688C 100%);box-shadow:0 16px 36px rgba(233,104,120,.3)} +.bx-rays{position:absolute;z-index:-1;left:50%;top:46%;width:520px;height:520px;margin:-260px 0 0 -260px; + background:repeating-conic-gradient(rgba(255,255,255,.16) 0 9deg,rgba(255,255,255,0) 9deg 22.5deg); + -webkit-mask-image:radial-gradient(circle,#000 0,rgba(0,0,0,.6) 30%,transparent 62%); + mask-image:radial-gradient(circle,#000 0,rgba(0,0,0,.6) 30%,transparent 62%); + animation:bxSpin 24s linear infinite} +@keyframes bxSpin{to{transform:rotate(360deg)}} +.bx-sp{position:absolute;z-index:-1;color:#FFF3C4;animation:gpTw 2.2s ease-in-out infinite;opacity:0} +.bx-sp.s1{left:16%;top:30%;font-size:14px}.bx-sp.s2{right:14%;top:24%;font-size:11px;animation-delay:.5s} +.bx-sp.s3{left:22%;top:62%;font-size:9px;animation-delay:1.1s}.bx-sp.s4{right:20%;top:58%;font-size:13px;animation-delay:1.6s} +.bx-week{font-size:13px;font-weight:800;letter-spacing:1px;display:inline-block;padding:4px 12px;border-radius:999px; + background:rgba(255,255,255,.22);backdrop-filter:blur(4px)} +.bx-hint{margin-top:8px;font-size:18px;font-weight:900;letter-spacing:.2px;text-shadow:0 2px 0 rgba(190,70,80,.25)} +.bx-box{display:block;margin:6px auto 0;width:168px;height:168px;border:0;background:none;padding:0; + animation:bxFloat 3.2s ease-in-out infinite} +.bx-box.hide,.bx-mini.hide,.bx-open.hide{display:none} +.bx-box svg{width:100%;height:100%;overflow:visible;filter:drop-shadow(0 12px 14px rgba(160,50,60,.28))} +@keyframes bxFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}} +.bx-shadow{display:block;width:96px;height:12px;margin:-8px auto 0;border-radius:50%;background:rgba(120,30,40,.18); + animation:bxShadow 3.2s ease-in-out infinite} +@keyframes bxShadow{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(.82);opacity:.7}} +.bx-stage.is-done .bx-shadow{display:none} +.bx-stage.is-shaking .bx-box{animation:bxShake .32s ease-in-out infinite} +.bx-stage.is-shaking .bx-box svg{animation:bxSwell 1.3s ease-in forwards} +@keyframes bxShake{0%,100%{transform:rotate(0)}25%{transform:rotate(-7deg) translateY(-2px)}75%{transform:rotate(7deg) translateY(-2px)}} +@keyframes bxSwell{to{transform:scale(1.12)}} +.bx-stage.is-popping .g-lid{animation:bxLid .5s cubic-bezier(.2,.8,.3,1) forwards} +.bx-stage.is-popping .g-body{animation:bxBody .5s ease-out forwards} +@keyframes bxLid{to{transform:translate(18px,-90px) rotate(28deg);opacity:0}} +@keyframes bxBody{40%{transform:scaleY(.86) scaleX(1.08)}100%{transform:scale(.7);opacity:0}} +.bx-open{position:relative;margin-top:14px;min-width:178px;border-radius:999px;padding:13px 26px;font-size:16px;font-weight:900; + letter-spacing:1px;color:#E5503C;background:#fff;box-shadow:0 10px 22px rgba(170,50,50,.25),0 0 0 0 rgba(255,255,255,.6); + animation:bxCta 2s ease-out infinite} +.bx-open:active{transform:scale(.96)} +.bx-open[disabled]{opacity:.85;animation:none} +@keyframes bxCta{0%{box-shadow:0 10px 22px rgba(170,50,50,.25),0 0 0 0 rgba(255,255,255,.55)} + 80%,100%{box-shadow:0 10px 22px rgba(170,50,50,.25),0 0 0 16px rgba(255,255,255,0)}} +.bx-stage.is-done .bx-open{animation:none;background:rgba(255,255,255,.95)} +.bx-misc{margin-top:12px;font-size:12px;color:rgba(255,255,255,.9)} +.bx-misc:empty{display:none} +.bx-mini{display:block;margin:12px auto 4px;width:150px;border:0;background:none;padding:0;transform:rotate(-4deg); + animation:bxFloat 3.6s ease-in-out infinite} +.bx-mini .cf{transform-origin:top left;width:248px;transform:scale(.605);margin-bottom:-130px;height:330px} + +.bx-pend{margin-top:14px;padding:13px 14px;border-radius:20px;background:#fff;box-shadow:var(--sh)} +.bx-pend.hide{display:none} +.bx-pend-t{font-size:14px;font-weight:800} +.bx-pend-t b{color:var(--brand)} +.bx-pend-t em{display:block;margin-top:2px;font-style:normal;font-size:11.5px;font-weight:500;color:var(--muted)} +.bx-chips{display:flex;flex-wrap:wrap;gap:7px;margin-top:10px} +.bx-chip{border-radius:999px;padding:6px 12px;font-size:12.5px;font-weight:700;color:#C4553E;background:#FFF0EA; + border:1.5px solid transparent;transition:all .2s var(--ease)} +.bx-chip.on{color:#fff;background:linear-gradient(135deg,#FF7A5C,#F4593C);box-shadow:0 4px 10px rgba(244,89,60,.28)} + +.bx-album{margin-top:16px;padding:16px 14px 6px;border-radius:24px;background:#fff;box-shadow:var(--sh)} +.bx-ah{display:flex;align-items:baseline} +.bx-ah h3{font-size:16.5px;font-weight:800} +.bx-cnt{margin-left:auto;font-size:13px;color:var(--muted)} +.bx-cnt b{font-size:20px;font-weight:900;color:var(--brand)} +.bx-prog{margin-top:9px;height:8px;border-radius:4px;background:#F5ECE6;overflow:hidden} +.bx-prog i{display:block;height:100%;width:0;border-radius:4px;background:linear-gradient(90deg,#FFB08F,#F4593C,#E9688C); + transition:width 1s var(--ease)} +.bx-anote{margin-top:7px;font-size:11.5px;color:var(--muted)} +.bx-anote:empty{display:none} +.bx-grp{margin-top:14px} +.bx-gh{display:flex;align-items:center;gap:6px;font-size:13px;font-weight:800;color:var(--ink);margin-bottom:8px} +.bx-gh i{width:6px;height:6px;border-radius:50%;background:var(--c,#F4593C)} +.bx-gh span{margin-left:auto;font-size:11.5px;font-weight:600;color:var(--muted)} +.bx-gh.full span{color:var(--brand)} +.bx-grid{display:grid;grid-template-columns:repeat(5,1fr);gap:7px} +.bx-t{position:relative;aspect-ratio:3/4;border-radius:11px;overflow:hidden;border:0;padding:0;font:inherit; + background:#F7F0EB;display:grid;place-items:center} +.bx-t .wk{position:absolute;left:0;right:0;bottom:0;text-align:center;padding:10px 0 3px;font-size:10.5px;font-weight:800;color:#fff; + background:linear-gradient(180deg,rgba(0,0,0,0),rgba(90,40,30,.55))} +.bx-t img{width:100%;height:100%;object-fit:cover} +.bx-t .fb{font-size:26px} +.bx-t.got{box-shadow:0 3px 8px rgba(190,120,96,.18)} +.bx-t.lock{border:1.5px dashed #EADBD2;color:#C9B4A8;font-size:18px;font-weight:900} +.bx-t.lock .wk{color:#B9A296;background:none} +.bx-t.can{background:linear-gradient(160deg,#FFE1D4,#FFC2B6);border:0;animation:bxCan 1.8s ease-in-out infinite} +.bx-t.can svg{width:30px;height:30px;overflow:visible} +.bx-t.can .wk{color:#C4553E;background:none} +@keyframes bxCan{0%,100%{box-shadow:0 0 0 0 rgba(244,89,60,.0)}50%{box-shadow:0 0 0 3px rgba(244,89,60,.35)}} +.bx-t.flash::after{content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none; + box-shadow:inset 0 0 0 2px #FFD66E; + background:linear-gradient(115deg,transparent 25%,rgba(255,255,255,.65) 45%,rgba(255,200,240,.4) 52%,transparent 70%) 0 0/250% 250%; + mix-blend-mode:screen;animation:bxHolo 2.6s linear infinite} +.bx-t .bf{position:absolute;right:3px;top:3px;font-size:9px;font-weight:800;color:#fff;background:rgba(0,0,0,.25);border-radius:5px;padding:0 3px} +.bx-t.is-new{animation:bxNewT 1s cubic-bezier(.3,1.6,.5,1)} +@keyframes bxNewT{0%{transform:scale(.4);opacity:0}60%{transform:scale(1.15)}100%{transform:none;opacity:1}} + +.bx-rules{margin-top:16px;border-radius:20px;background:#fff;box-shadow:var(--sh);overflow:hidden} +.bx-rules summary{list-style:none;display:flex;align-items:center;padding:14px 16px;font-size:14px;font-weight:800} +.bx-rules summary::-webkit-details-marker{display:none} +.bx-rules summary::after{content:"›";margin-left:auto;color:var(--muted);font-size:18px;transition:transform .25s} +.bx-rules[open] summary::after{transform:rotate(90deg)} +.bx-rules-b{padding:0 16px 16px;font-size:12.5px;color:var(--ink-2);line-height:1.7} +.bx-rules-b h4{margin:10px 0 6px;font-size:12.5px;font-weight:800;color:var(--ink)} +.bx-pr{display:flex;align-items:center;gap:8px;margin:5px 0} +.bx-pr span{flex:0 0 auto;width:118px;font-size:12px} +.bx-pr i{flex:1;height:6px;border-radius:3px;background:#F5ECE6;position:relative;overflow:hidden} +.bx-pr i::after{content:"";position:absolute;left:0;top:0;bottom:0;width:var(--w);border-radius:3px;background:linear-gradient(90deg,#FFB08F,#F4593C)} +.bx-pr b{flex:0 0 auto;width:34px;text-align:right;font-size:12px;color:var(--brand)} +.bx-rules-b ul{margin:4px 0 0;padding-left:16px} +.bx-rules-b .free{margin-top:10px;padding:8px 10px;border-radius:10px;background:#FFF5EF;color:#B5543F;font-weight:700;font-size:12px} + +/* ---------- 翻卡层 ---------- */ +.bx-reveal{position:absolute;inset:0;z-index:6;display:flex;align-items:flex-start;justify-content:center;overflow-y:auto; + background:rgba(48,22,26,.82);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px); + opacity:0;pointer-events:none;transition:opacity .35s} +.bx-reveal.show{opacity:1;pointer-events:auto} +.bx-glow{position:absolute;left:50%;top:260px;width:460px;height:460px;margin:-230px 0 0 -230px;pointer-events:none; + background:repeating-conic-gradient(rgba(255,214,150,.18) 0 8deg,rgba(255,214,150,0) 8deg 20deg); + -webkit-mask-image:radial-gradient(circle,#000 0,transparent 65%);mask-image:radial-gradient(circle,#000 0,transparent 65%); + animation:bxSpin 30s linear infinite;opacity:0;transition:opacity .6s .4s} +.bx-reveal.show .bx-glow{opacity:1} +.bx-reveal.is-flash .bx-glow{background:repeating-conic-gradient(rgba(255,150,220,.2) 0 8deg,rgba(150,220,255,.12) 8deg 14deg,rgba(255,240,150,0) 14deg 20deg)} +.bx-flash{position:absolute;left:50%;top:260px;width:120px;height:120px;margin:-60px 0 0 -60px;border-radius:50%; + background:radial-gradient(circle,#fff 0,rgba(255,255,255,.8) 40%,rgba(255,255,255,0) 70%);opacity:0;pointer-events:none} +.bx-reveal.is-burst .bx-flash{animation:bxFlash .8s ease-out} +@keyframes bxFlash{0%{transform:scale(.2);opacity:1}100%{transform:scale(7);opacity:0}} +.bx-confetti{position:absolute;left:50%;top:260px;width:0;height:0;pointer-events:none} +.bx-confetti i{position:absolute;left:0;top:0;width:8px;height:12px;border-radius:2px;background:var(--c);opacity:0; + animation:bxBurst 1.4s cubic-bezier(.15,.7,.3,1) var(--d) forwards} +.bx-confetti i.r{border-radius:50%;width:8px;height:8px} +@keyframes bxBurst{0%{transform:translate(0,0) rotate(0) scale(.6);opacity:1} + 70%{opacity:1}100%{transform:translate(var(--x),var(--y)) rotate(var(--r)) scale(1);opacity:0}} +.bx-rv-in{position:relative;display:flex;flex-direction:column;align-items:center;width:100%;padding:calc(env(safe-area-inset-top) + 64px) 24px 40px} +.bx-tag{font-size:14px;font-weight:900;letter-spacing:1px;color:#FFE8B8;opacity:0;transform:translateY(-8px);transition:all .4s .5s} +.bx-reveal.show .bx-tag{opacity:1;transform:none} +.bx-reveal.is-flash .bx-tag{background:linear-gradient(90deg,#FFD66E,#FF9AD5,#9ADFFF,#FFD66E) 0 0/300% 100%; + -webkit-background-clip:text;background-clip:text;color:transparent;font-size:16px;animation:bxHolo 2.4s linear infinite} +.bx-persp{margin-top:14px;perspective:1100px} +.bx-card3d{position:relative;width:248px;height:330px;transform-style:preserve-3d; + transform:rotateY(180deg) scale(.35) translateY(90px);transition:transform 1.05s cubic-bezier(.2,.9,.25,1.12)} +.bx-card3d.is-flip{transform:rotateY(0) scale(1)} +.bx-card3d.no-anim{transition:none} +.bx-face{position:absolute;inset:0;-webkit-backface-visibility:hidden;backface-visibility:hidden;border-radius:20px} +.bx-back{transform:rotateY(180deg);overflow:hidden;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px; + background:radial-gradient(circle at 50% 42%,#FFB08F 0,#F4593C 60%,#D9466E 100%);border:4px solid #FFE3D6;color:#fff} +.bx-back::before{content:"";position:absolute;inset:10px;border-radius:14px;border:1.5px dashed rgba(255,255,255,.5)} +.bx-back::after{content:"";position:absolute;inset:0;opacity:.25; + background:radial-gradient(circle,#fff 1.4px,transparent 1.6px) 0 0/18px 18px} +.bx-back svg{width:96px;height:96px;overflow:visible} +.bx-back b{position:relative;z-index:1;font-size:13px;letter-spacing:3px} +.bx-dev{max-width:290px;margin-top:16px;font-size:13px;line-height:1.7;color:rgba(255,255,255,.88);text-align:center; + opacity:0;transform:translateY(10px);transition:all .45s} +.bx-dev:empty{display:none} +.bx-reveal.is-done .bx-dev{opacity:1;transform:none;transition-delay:.15s} +.bx-reward{width:100%;max-width:300px;margin-top:14px;opacity:0;transform:translateY(24px) scale(.96);transition:all .5s var(--ease)} +.bx-reward:empty{display:none} +.bx-reveal.is-done .bx-reward{opacity:1;transform:none;transition-delay:.3s} +.bx-rw{position:relative;border-radius:20px;padding:14px 16px 15px;background:#FFFBF6;color:var(--ink);text-align:left; + box-shadow:0 14px 30px rgba(0,0,0,.25)} +.bx-rw::before{content:"";position:absolute;left:18px;top:-7px;width:14px;height:14px;transform:rotate(45deg);background:#FFFBF6} +.bx-rw-h{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:800;color:#C4553E} +.bx-rw-h i{width:26px;height:26px;border-radius:9px;display:grid;place-items:center;font-style:normal;font-size:15px; + background:linear-gradient(135deg,#FFE0D2,#FFC8BA)} +.bx-rw-h em{margin-left:auto;font-style:normal;font-size:10.5px;font-weight:700;color:#B8A094;background:#F6EEE8;border-radius:999px;padding:2px 8px} +.bx-rw-t{margin-top:9px;font-size:15px;font-weight:800} +.bx-rw-p{margin-top:5px;font-size:13.5px;line-height:1.75;color:#5E4A42;font-family:var(--font-kid)} +.bx-keep{margin-top:20px;min-width:200px;border-radius:999px;padding:13px 28px;font-size:15.5px;font-weight:900;color:#fff; + background:linear-gradient(135deg,#FF7A5C,#F4593C);box-shadow:0 10px 24px rgba(244,89,60,.4); + opacity:0;transform:translateY(16px);transition:all .45s} +.bx-reveal.is-done .bx-keep{opacity:1;transform:none;transition-delay:.55s} +.bx-keep:active{transform:scale(.96)} + +/* ---------- 宝宝卡正面 ---------- */ +.cf{position:relative;width:248px;height:330px;border-radius:20px;padding:10px;overflow:hidden;background:#FFFDF9; + box-shadow:0 18px 40px rgba(0,0,0,.28);text-align:left;color:var(--ink)} +.cf-img{position:relative;width:228px;height:200px;border-radius:14px;overflow:hidden;background:#FFF4EC;display:grid;place-items:center} +.cf-img img{width:100%;height:100%;object-fit:cover} +.cf-fb{display:grid;place-items:center;width:100%;height:100%;font-size:86px; + background:radial-gradient(circle at 50% 45%,#fff 0,#FFEADF 55%,#FFD9CB 100%)} +.cf-row{display:flex;align-items:baseline;margin-top:10px;padding:0 4px} +.cf-wk{font-size:13px;font-weight:800;color:var(--muted)} +.cf-wk b{font-size:24px;font-weight:900;color:var(--brand);margin:0 2px;letter-spacing:-.5px} +.cf-size{margin-left:auto;font-size:12px;color:var(--ink-2)} +.cf-size b{color:var(--ink);font-weight:800} +.cf-say{margin-top:6px;padding:8px 10px;border-radius:12px;background:#FFF3EC;font-size:13px;line-height:1.55;color:#7A4A3B; + font-family:var(--font-kid);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden} +.cf-say:empty{display:none} +.cf-no{position:absolute;right:16px;top:16px;font-size:10px;font-weight:800;letter-spacing:.5px;color:#fff; + background:rgba(90,46,34,.35);border-radius:999px;padding:2px 8px;backdrop-filter:blur(2px)} +.cf-tag{display:none} +.cf.v-flash{background:linear-gradient(135deg,#FFE7A3,#FFC6E5 35%,#C6E8FF 65%,#FFE7A3);padding:12px} +.cf.v-flash .cf-img{width:224px;height:196px} +.cf.v-flash .cf-row,.cf.v-flash .cf-say{position:relative} +.cf.v-flash .cf-say{background:rgba(255,255,255,.75)} +.cf.v-flash .cf-tag{display:block;position:absolute;left:16px;top:16px;z-index:2;font-size:11px;font-weight:900;color:#8A5A00; + background:linear-gradient(90deg,#FFE58A,#FFF6D0,#FFD66E);border-radius:999px;padding:2px 9px;box-shadow:0 2px 6px rgba(160,110,0,.3)} +.cf-holo{position:absolute;inset:0;pointer-events:none;z-index:1;border-radius:inherit;display:none; + background:linear-gradient(115deg,transparent 20%,rgba(255,255,255,.7) 38%,rgba(255,170,230,.45) 46%,rgba(150,225,255,.45) 54%,transparent 72%) 0 0/250% 250%; + mix-blend-mode:soft-light;animation:bxHolo 3s linear infinite} +.cf.v-flash .cf-holo{display:block} +@keyframes bxHolo{from{background-position:120% 0}to{background-position:-120% 0}} + +@media (prefers-reduced-motion:reduce){ + .bb-banner .bb-gift svg,.bb-badge,.bb-shine,.pl-orb,.pl-live i::after,.bx-rays,.bx-sp,.bx-box,.bx-shadow,.bx-open,.bx-mini, + .bx-t.can,.bx-t.flash::after,.bx-glow,.cf-holo{animation:none!important} + .bb-shine{display:none} + .bx-card3d{transition-duration:.2s} +} +.bx-t .fb{display:grid;place-items:center;width:100%;height:100%;background:radial-gradient(circle at 50% 40%,#fff 0,#FFE6DA 70%)} +.bx-keep,.bx-open{text-align:center}