feat(盲盒): 价值感改造一期前端 —— 待解锁任务 / 宝宝周记 / 集齐奖励 / 下周预告 (v26100107)

- 待解锁:错过的周 + 三个工具任务(进度、去完成跳应用、解锁按钮),卡册格子 🔒
- 翻卡层加「宝宝周记」;卡册点开带周记与宝宝的话
- 卡册顶部集齐奖励:三段 + 全集,差 N 张 / 待发放 / 已发放 / 填收货信息
- 集齐庆祝弹层 + 收货信息表单(POST /blindbox/claim)
- 本周已开时显示下周卡片剪影 + 倒计时;横幅优先提示可解锁 / 离 XX 大奖还差 N 张

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 03:15:16 +00:00
parent ec87d36db2
commit 555012f248
3 changed files with 252 additions and 9 deletions

View File

@@ -7,7 +7,7 @@
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<title>兜爸心选 · 孕育陪伴</title>
<!-- V:26100106 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<!-- V:26100107 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<link rel="stylesheet" href="mama-box.css">
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) -->
@@ -1437,6 +1437,20 @@
<button class="bx-open" id="bxOpen" type="button"></button>
<div class="bx-misc" id="bxMisc"></div>
</div>
<!-- 下周预告:下一孕周卡片的剪影 + 倒计时(营造等待感)。本周已开、且还没到 40 周时显示 -->
<div class="bx-next hide" id="bxNext">
<div class="bx-next-img" id="bxNextImg" aria-hidden="true"></div>
<div class="bx-next-t"><b id="bxNextW"></b><span id="bxNextS"></span></div>
<div class="bx-next-cd"><b id="bxNextD"></b><span>天后开启</span></div>
</div>
<!-- 待解锁(2026-10-01 价值感改造):参与之后漏开的周不再白送,完成一个孕期工具任务解锁一周。
任务是否完成由服务端查数胎动每日足迹核验(GET /blindbox 的 tasks),解锁 POST /blindbox/unlock -->
<div class="bx-lock hide" id="bxLock">
<div class="bx-lock-h"><span class="bx-lock-ico" aria-hidden="true">🔒</span>
<div><b>错过的 <em id="bxLockN"></em> 周待解锁</b><p>用一次孕期小工具,就能解锁一周补开</p></div></div>
<div class="bx-chips" id="bxLockChips"></div>
<div class="bx-tasks" id="bxTasks"></div>
</div>
<div class="bx-pend hide" id="bxPend">
<div class="bx-pend-t">还有 <b id="bxPendN"></b> 个盲盒等你打开 <em>错过的周会一直保留到孕 40 周</em></div>
<div class="bx-chips" id="bxChips"></div>
@@ -1447,6 +1461,8 @@
<span class="bx-cnt"><b id="bxGot">0</b>/<span id="bxAll">37</span></span>
</div>
<div class="bx-prog"><i id="bxBar"></i></div>
<!-- 分段集齐奖励:孕早/中/晚期各一段 + 37 张全集。达成即记账,兜豆系统上线前由工作人员人工发放 -->
<div class="bx-goal" id="bxGoal"></div>
<p class="bx-anote" id="bxAnote"></p>
<div id="bxGroups"></div>
</section>
@@ -1468,6 +1484,7 @@
</div>
</div>
<p class="bx-dev" id="bxDev"></p>
<div class="bx-diary" id="bxDiary"></div>
<div class="bx-reward" id="bxReward"></div>
<button class="bx-keep" id="bxKeep" type="button"></button>
</div>
@@ -1668,6 +1685,31 @@
① 昵称和宝宝小名并成一行 .fld-row(省 78px);
② 这个弹窗里的大头像从 92px 收到 76px(见 css 的 #modalProfile .av-big)。
再往里加字段之前,先回来把这笔账重算一遍。 -->
<!-- 盲盒 · 集齐全集的实物奖励:收货信息。POST /api/mp/public/mama-box/blindbox/claim -->
<section class="modal" id="modalBxAddr" role="dialog" aria-modal="true" aria-labelledby="modalBxAddrTitle">
<div class="modal-head">
<h3 id="modalBxAddrTitle">领取孕期纪念礼</h3>
<button class="modal-x" data-modal="close" aria-label="关闭">
<svg viewBox="0 0 24 24" width="15" height="15" aria-hidden="true"><path d="M6 6l12 12M18 6L6 18" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"/></svg>
</button>
</div>
<p class="bx-addr-tip">恭喜集齐 37 张宝宝卡!填好收货信息,工作人员会在 7 个工作日内为你寄出。</p>
<div class="fld-row">
<label class="fld"><span>收货人</span><input id="bxAddrName" type="text" maxlength="20" placeholder="姓名"></label>
<label class="fld"><span>手机号</span><input id="bxAddrPhone" type="tel" maxlength="20" placeholder="手机号"></label>
</div>
<label class="fld"><span>收货地址</span><input id="bxAddrAddr" type="text" maxlength="120" placeholder="省市区 + 详细地址"></label>
<div class="modal-foot"><button class="dl-btn main" id="bxAddrSave">提交</button></div>
</section>
<!-- 盲盒 · 分段集齐达成的庆祝弹层(内容由 app.js showPrize() 填) -->
<section class="modal bx-prize" id="modalBxPrize" role="dialog" aria-modal="true" aria-labelledby="bxPrizeT">
<div class="bx-prize-ico" aria-hidden="true">🏆</div>
<h3 id="bxPrizeT"></h3>
<p id="bxPrizeP"></p>
<div class="modal-foot"><button class="dl-btn main" id="bxPrizeOk">太好了</button></div>
</section>
<section class="modal" id="modalProfile" role="dialog" aria-modal="true" aria-labelledby="modalProfileTitle">
<div class="modal-head">
<h3 id="modalProfileTitle">我的资料</h3>

134
js/app.js
View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26100106';
var VER = '26100107';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -3281,11 +3281,24 @@
return bbApi('blindbox').then(j => {
if (!j || j.code !== 200 || !j.result) return;
BB = j.result;
(BB.newRewards || []).forEach(g => bxPrizeQ.push(g));
renderBanner();
if (BB.backfilled > 0) setTimeout(() => toast('错过的 ' + BB.backfilled + ' 张宝宝卡已补给你,去卡册看看吧'), 1800);
if ($('#detailBox').classList.contains('show')) renderBox();
if ($('#detailBox').classList.contains('show')) {
renderBox();
if (bxPrizeQ.length && !bbBusy && !$('#bxReveal').classList.contains('show')) setTimeout(showPrize, 400);
}
}).catch(() => { /* 静默:横幅保持现状 */ });
}
/* 最近的一个集齐目标:先看当前孕周所在的那段,集齐了再看全集。用于「离孕中期大奖还差 3 张」 */
function bxGoal() {
const ss = (BB && BB.stages) || [];
const cur = ss.find(x => x.key !== 'all' && BB.week >= x.from && BB.week <= x.to && !x.complete);
const g = cur || ss.find(x => x.key === 'all' && !x.complete);
return g && g.left > 0 ? g : null;
}
const BX_TASK_ICO = { kick3: '👶', weight: '⚖️', tool: '📅' };
let bxLockSel = null, bxPrizeQ = [];
function renderBanner() {
const ban = $('#bbBanner');
const set = (t, s, cta, ready, calm) => {
@@ -3309,8 +3322,13 @@
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 ? ' · 闪卡 <b>' + BB.flash + '</b> 张' : ''), '看卡册', false, true);
else if (op.length) set('还有 ' + op.length + ' 个盲盒没开', '已解锁,随时补开 · ' + got, '去补开', true);
else if ((BB.locked || []).length && BB.credits) set('可以解锁 ' + BB.credits + ' 个错过的盲盒', '你完成了孕期小任务 · ' + got, '去解锁', true);
else {
const g = bxGoal();
set(BB.nextIn ? '下一个盲盒 ' + BB.nextIn + ' 天后到' : '本周盲盒已开启',
g ? '离' + g.name + '大奖还差 <b>' + g.left + '</b> 张' : got + (BB.flash ? ' · 闪卡 <b>' + BB.flash + '</b> 张' : ''), '看卡册', false, true);
}
}
/* 盲盒入口:首页横幅 + 「我的」页卡册行(#meBoxCard)走同一条路 */
function openBoxEntry() {
@@ -3319,6 +3337,7 @@
bbSel = null;
renderBox();
openPanel('detailBox');
if (bxPrizeQ.length) setTimeout(showPrize, 600);
}
$('#bbBanner').addEventListener('click', openBoxEntry);
$('#meBoxCard').addEventListener('click', openBoxEntry);
@@ -3349,6 +3368,43 @@
btn.textContent = '看看这张卡';
}
$('#bxMisc').textContent = (BB.weekOpeners >= 50) ? '本周已有 ' + fmtN(BB.weekOpeners) + ' 位妈妈开了盲盒' : '每周一个 · 完全免费';
/* 下周预告:剪影 + 倒计时(本周已开时才露出来,免得和「开启盲盒」抢视线) */
const nx = BB.next, nxEl = $('#bxNext');
nxEl.classList.toggle('hide', !(nx && !bbSel && !BB.ended));
if (nx && !bbSel && !BB.ended) {
$('#bxNextImg').innerHTML = cardImgHTML(nx.week);
$('#bxNextW').textContent = '孕 ' + nx.week + ' 周 · 下周预告';
$('#bxNextS').textContent = nx.size ? '宝宝会长到' + nx.size.name + '那么大' : '';
$('#bxNextD').textContent = nx.inDays;
}
/* 待解锁 + 任务 */
const lk = BB.locked || [], lkEl = $('#bxLock');
lkEl.classList.toggle('hide', !lk.length);
if (lk.length) {
if (lk.indexOf(bxLockSel) < 0) bxLockSel = lk[lk.length - 1];
$('#bxLockN').textContent = lk.length;
$('#bxLockChips').innerHTML = lk.map(w => '<button class="bx-chip lk' + (w === bxLockSel ? ' on' : '') + '" data-w="' + w + '" type="button">🔒 孕 ' + w + ' 周</button>').join('');
$('#bxTasks').innerHTML = (BB.tasks || []).map(t => {
const btn = t.ready ? '<button class="bx-tk-b go" data-unlock="' + t.id + '" type="button">解锁孕 ' + bxLockSel + ' 周</button>'
: t.used ? '<span class="bx-tk-b done">' + (t.period === 'week' ? '本周已用' : '今天已用') + '</span>'
: '<button class="bx-tk-b" data-app="' + bbEsc(t.identifier) + '" type="button">去完成</button>';
const prog = t.need > 1 ? '<i class="bx-tk-p"><s style="width:' + Math.round(t.progress / t.need * 100) + '%"></s></i><em>' + t.progress + '/' + t.need + '</em>' : '';
return '<div class="bx-tk' + (t.ready ? ' is-ready' : '') + '"><span class="bx-tk-i" aria-hidden="true">' + (BX_TASK_ICO[t.id] || '✨') + '</span>' +
'<div class="bx-tk-m"><b>' + bbEsc(t.title) + '</b><span>' + bbEsc(t.desc) + '</span>' + (prog ? '<div class="bx-tk-pr">' + prog + '</div>' : '') + '</div>' + btn + '</div>';
}).join('');
}
/* 集齐奖励 */
$('#bxGoal').innerHTML = (BB.stages || []).map(g => {
const e = g.earned, pct = Math.round(g.got / g.total * 100);
const prize = (g.doudou ? g.doudou + ' 兜豆' : '') + (g.gift ? (g.doudou ? ' + ' : '') + '实物纪念礼' : '');
let tail;
if (e) tail = e.needAddr ? '<button class="bx-gl-b" data-addr="1" type="button">填收货信息</button>'
: '<span class="bx-gl-s' + (e.status === 'sent' ? ' sent' : '') + '">' + (e.status === 'sent' ? '已发放' : '待发放') + '</span>';
else if (g.complete) tail = '<span class="bx-gl-s off">补发集齐</span>';
else tail = '<span class="bx-gl-l">差 <b>' + g.left + '</b> 张</span>';
return '<div class="bx-gl' + (g.key === 'all' ? ' all' : '') + (e ? ' got' : '') + '"><div class="bx-gl-h"><b>' + g.name + '</b><em>' + prize + '</em>' + tail + '</div>' +
'<i class="bx-gl-bar"><s style="width:' + pct + '%"></s></i></div>';
}).join('');
/* 待补开 */
const pend = $('#bxPend');
pend.classList.toggle('hide', op.length < 2);
@@ -3361,7 +3417,8 @@
$('#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 ? '标「补」的是来之前的孕周,已自动补发普通款。' : '');
$('#bxAnote').textContent = (BB.flash ? '其中闪卡 ' + BB.flash + ' 张。' : '') + (nBack ? '标「补」的是来之前的孕周,已自动补发普通款。' : '') +
(lk.length ? '标 🔒 的是错过的周,完成小任务即可解锁。' : '');
const byW = {};
(BB.album || []).forEach(a => { byW[a.week] = a; });
$('#bxGroups').innerHTML = BX_GROUPS.map(g => {
@@ -3373,6 +3430,8 @@
cells += '<button class="bx-t got' + (a.v === 'flash' ? ' flash' : '') + (bbNew === w ? ' is-new' : '') + '" data-w="' + w + '" data-v="' + (a.v || 'normal') + '" type="button">' +
(CARD_IMG[w] ? '<img src="assets/img/' + CARD_IMG[w] + '.jpg" alt="" loading="lazy">' : '<span class="fb">' + babySize(w).emoji + '</span>') +
(a.src === 'backfill' ? '<span class="bf">补</span>' : '') + '<span class="wk">' + w + ' 周</span></button>';
} else if (lk.indexOf(w) >= 0) {
cells += '<button class="bx-t lockd" data-lk="' + w + '" type="button"><span class="li" aria-hidden="true">🔒</span><span class="wk">' + w + ' 周</span></button>';
} else if (op.indexOf(w) >= 0) {
cells += '<button class="bx-t can" data-op="' + w + '" type="button">' + giftSVG() + '<span class="wk">' + w + ' 周</span></button>';
} else {
@@ -3402,13 +3461,69 @@
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; }
if (t.dataset.lk) { bxLockSel = +t.dataset.lk; renderBox(); $('#bxLock').scrollIntoView({ behavior: 'smooth', block: 'center' }); return; }
const w = +t.dataset.w;
if (!w) return;
/* 卡册里点开一张:大小来自本地 SIZE 表,发育说明取孕期小知识的「宝宝这周」 */
const c = { week: w, v: t.dataset.v, size: babySize(w), say: '' };
/* 卡册里点开一张:大小来自本地 SIZE 表,发育说明取孕期小知识的「宝宝这周」;周记 / 宝宝的话由接口 album 带回 */
const a = (BB.album || []).find(x => x.week === w) || {};
const c = { week: w, v: t.dataset.v, size: babySize(w), say: a.say || '', diary: a.diary || [] };
showReveal(c, null, false);
loadTips().then(() => { const x = TIPS && TIPS[w]; if (x && x.baby && $('#bxReveal').classList.contains('show')) $('#bxDev').textContent = x.baby; });
});
$('#bxLockChips').addEventListener('click', e => {
const b = e.target.closest('.bx-chip');
if (!b || bbBusy) return;
bxLockSel = +b.dataset.w; haptic(); renderBox();
});
$('#bxTasks').addEventListener('click', e => {
const go = e.target.closest('[data-app]');
if (go) {
const app = APP_BY_ID[go.dataset.app];
if (app && app.url) jumpApp(app.url, app.identifier); else toast('这个工具暂时打不开,换一个任务试试');
return;
}
const u = e.target.closest('[data-unlock]');
if (!u || bbBusy || !bxLockSel) return;
bbBusy = true; u.disabled = true; u.textContent = '解锁中…';
const w = bxLockSel;
bbApi('blindbox/unlock?week=' + w + '&task=' + u.dataset.unlock, 'POST').catch(() => null).then(j => {
bbBusy = false;
if (!j || j.code !== 200) { toast((j && j.message) || '网络开小差了,再试一次'); loadBlindbox(); return; }
if (navigator.vibrate) navigator.vibrate([10, 30, 14]);
toast('孕 ' + w + ' 周的盲盒解锁啦,快打开看看');
bbSel = w;
loadBlindbox().then(() => { bbSel = w; renderBox(); $('#detailBox .detail-body').scrollTo({ top: 0, behavior: 'smooth' }); });
});
});
$('#bxGoal').addEventListener('click', e => { if (e.target.closest('[data-addr]')) openModal('modalBxAddr'); });
$('#bxAddrSave').addEventListener('click', () => {
const v = id => $('#' + id).value.trim();
const body = { name: v('bxAddrName'), phone: v('bxAddrPhone'), addr: v('bxAddrAddr') };
if (!body.name || !body.phone || !body.addr) { toast('请把收货人、电话、地址填完整'); return; }
if (!/^1\d{10}$/.test(body.phone)) { toast('手机号好像不对,再检查一下'); return; }
fetch('/api/mp/public/mama-box/blindbox/claim?openid=' + encodeURIComponent(hashParam('openid')), {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body)
}).then(r => r.json()).catch(() => null).then(j => {
if (!j || j.code !== 200) { toast((j && j.message) || '提交失败,再试一次'); return; }
closeModal(); toast('收到啦,工作人员会尽快寄出'); loadBlindbox();
});
});
/* 集齐庆祝:开盒 / 刷新时接口带回 newRewards(服务端只返回一次),排队逐个弹 */
function showPrize() {
const g = bxPrizeQ.shift();
if (!g) return;
$('#bxPrizeT').textContent = g.key === 'all' ? '37 张宝宝卡全部集齐!' : '集齐' + g.name + '!';
$('#bxPrizeP').textContent = (g.doudou ? g.doudou + ' 兜豆' : '') + (g.gift ? (g.doudou ? '和' : '') + g.gift : '') + '已为你记下,工作人员会在 7 个工作日内发放' +
(g.gift ? ',先填一下收货信息吧。' : '。');
$('#bxPrizeOk').textContent = g.gift ? '去填收货信息' : '太好了';
$('#bxPrizeOk').dataset.addr = g.gift ? '1' : '';
openModal('modalBxPrize');
}
$('#bxPrizeOk').addEventListener('click', () => {
const addr = $('#bxPrizeOk').dataset.addr;
closeModal();
if (addr) openModal('modalBxAddr'); else setTimeout(showPrize, 250);
});
$('#bxRuleBtn').addEventListener('click', () => {
const d = $('#bxRules'); d.open = true;
d.scrollIntoView({ behavior: 'smooth', block: 'start' });
@@ -3437,6 +3552,7 @@
st.classList.add('is-popping');
if (navigator.vibrate) navigator.vibrate([12, 40, 18]);
const r = j.result;
(r.newRewards || []).forEach(g => bxPrizeQ.push(g));
bbNew = r.card.week;
setTimeout(() => { showReveal(r.card, r.reward, true); bbBusy = false; }, 420);
loadBlindbox(); // 后台刷新横幅与卡册(翻卡层盖着,用户看不到重绘)
@@ -3462,6 +3578,8 @@
if (!$('#bxBack').firstChild) $('#bxBack').innerHTML = giftSVG() + '<b>兜爸心选 · 孕期盲盒</b>';
$('#bxTag').textContent = fresh ? (flash ? '✦ 哇!开出了闪卡 ✦' : '新卡 · 孕 ' + card.week + ' 周') : (flash ? '✦ 闪卡 · 孕 ' + card.week + ' 周' : '孕 ' + card.week + ' 周');
$('#bxDev').textContent = card.dev || '';
const dl = card.diary || [];
$('#bxDiary').innerHTML = dl.length ? '<div class="bx-dy-h">📖 宝宝周记 · 孕 ' + card.week + ' 周</div>' + dl.map(x => '<p>' + bbEsc(x) + '</p>').join('') : '';
let rw = '';
if (reward) {
const tj = reward.type === 'taijiao';
@@ -3493,7 +3611,7 @@
rv.classList.remove('show', 'is-done', 'is-burst');
rv.setAttribute('aria-hidden', 'true');
}
$('#bxKeep').addEventListener('click', () => { hideReveal(); renderBox(); });
$('#bxKeep').addEventListener('click', () => { hideReveal(); renderBox(); if (bxPrizeQ.length) setTimeout(showPrize, 350); });
$('#detailBox .dt-back').addEventListener('click', hideReveal);
/* ================= 孕期小知识(2026-09-30) =================

View File

@@ -2695,3 +2695,86 @@ a.skip:focus{left:16px;top:16px}
.pt-card b{font-size:14px}
.pt-card p{font-size:13px;line-height:1.6;margin-top:6px}
.pt-foot{font-size:11.5px}
/* ============ 孕期盲盒 · 价值感改造(2026-10-01):下周预告 / 待解锁任务 / 集齐奖励 / 宝宝周记 ============ */
/* 下周预告:卡片剪影(同一张插画压成纯色)+ 倒计时 */
.bx-next{display:flex;align-items:center;gap:12px;margin-top:14px;padding:12px 14px;border-radius:20px;background:#fff;box-shadow:var(--sh)}
.bx-next.hide{display:none}
.bx-next-img{flex:none;width:46px;height:58px;border-radius:10px;overflow:hidden;background:#F4E9E3;display:grid;place-items:center}
.bx-next-img img{width:100%;height:100%;object-fit:cover;filter:brightness(0) opacity(.18)}
.bx-next-img .cf-fb{font-size:24px;filter:grayscale(1) opacity(.35)}
.bx-next-t{flex:1;min-width:0}
.bx-next-t b{display:block;font-size:14px;font-weight:800}
.bx-next-t span{display:block;margin-top:3px;font-size:12px;color:var(--muted)}
.bx-next-cd{flex:none;text-align:center;padding:6px 10px;border-radius:14px;background:#FFF0EA}
.bx-next-cd b{display:block;font-size:20px;line-height:1.1;font-weight:900;color:var(--brand);font-variant-numeric:tabular-nums}
.bx-next-cd span{font-size:10.5px;font-weight:700;color:#C4553E}
/* 待解锁 */
.bx-lock{margin-top:14px;padding:14px;border-radius:20px;background:#fff;box-shadow:var(--sh)}
.bx-lock.hide{display:none}
.bx-lock-h{display:flex;gap:10px;align-items:flex-start}
.bx-lock-ico{flex:none;width:34px;height:34px;border-radius:12px;display:grid;place-items:center;font-size:17px;background:#F3EEFF}
.bx-lock-h b{font-size:14.5px;font-weight:800}
.bx-lock-h b em{font-style:normal;color:#7B61D9}
.bx-lock-h p{margin-top:2px;font-size:12px;color:var(--muted)}
.bx-chip.lk{color:#6A55B8;background:#F3EEFF}
.bx-chip.lk.on{color:#fff;background:linear-gradient(135deg,#9A85F0,#7B61D9);box-shadow:0 4px 10px rgba(123,97,217,.28)}
.bx-tasks{margin-top:12px;display:flex;flex-direction:column;gap:8px}
.bx-tk{display:flex;align-items:center;gap:10px;padding:10px 10px 10px 11px;border-radius:16px;background:#FAF6F3}
.bx-tk.is-ready{background:#FFF3EC;box-shadow:inset 0 0 0 1.5px #FFC9B5}
.bx-tk-i{flex:none;width:32px;height:32px;border-radius:11px;display:grid;place-items:center;font-size:16px;background:#fff}
.bx-tk-m{flex:1;min-width:0}
.bx-tk-m b{display:block;font-size:13.5px;font-weight:800}
.bx-tk-m>span{display:block;margin-top:2px;font-size:11.5px;color:var(--muted)}
.bx-tk-pr{display:flex;align-items:center;gap:6px;margin-top:5px}
.bx-tk-p{flex:1;max-width:110px;height:5px;border-radius:3px;background:#EEE3DC;overflow:hidden}
.bx-tk-p s{display:block;height:100%;border-radius:3px;background:linear-gradient(90deg,#FFB08F,#F4593C)}
.bx-tk-pr em{font-style:normal;font-size:11px;font-weight:700;color:#C4553E}
.bx-tk-b{flex:none;border-radius:999px;padding:7px 12px;font-size:12px;font-weight:800;color:#C4553E;background:#fff;box-shadow:inset 0 0 0 1.2px #FFC9B5}
.bx-tk-b.go{color:#fff;background:linear-gradient(135deg,#FF7A5C,#F4593C);box-shadow:0 4px 10px rgba(244,89,60,.28)}
.bx-tk-b.done{color:#B9A296;background:none;box-shadow:none}
.bx-tk-b:disabled{opacity:.6}
/* 卡册里的待解锁格子 */
.bx-t.lockd{background:#F6F2FF;border:1.5px dashed #CFC2F5}
.bx-t.lockd .li{font-size:17px;line-height:1}
.bx-t.lockd .wk{color:#7B61D9;background:none}
/* 集齐奖励 */
.bx-goal{margin-top:12px;display:grid;grid-template-columns:1fr 1fr;gap:8px}
.bx-goal:empty{display:none}
.bx-gl{padding:10px 11px;border-radius:14px;background:#FAF6F3}
.bx-gl.all{grid-column:1/-1;background:linear-gradient(135deg,#FFF6E2,#FFEBD9)}
.bx-gl.got{background:#FFF3EC}
.bx-gl-h{display:flex;align-items:baseline;flex-wrap:wrap;gap:4px 6px}
.bx-gl-h b{font-size:13px;font-weight:800}
.bx-gl-h em{font-style:normal;font-size:11.5px;font-weight:700;color:#C98A1E}
.bx-gl-l,.bx-gl-s{margin-left:auto;font-size:11.5px;color:var(--muted);white-space:nowrap}
.bx-gl-l b{color:var(--brand);font-size:13px}
.bx-gl-s{font-weight:800;color:#E07B2E}
.bx-gl-s.sent{color:#3EA27A}
.bx-gl-s.off{font-weight:600;color:#B9A296}
.bx-gl-b{margin-left:auto;border-radius:999px;padding:4px 10px;font-size:11.5px;font-weight:800;color:#fff;background:linear-gradient(135deg,#FF7A5C,#F4593C)}
.bx-gl-bar{display:block;margin-top:7px;height:5px;border-radius:3px;background:rgba(0,0,0,.06);overflow:hidden}
.bx-gl-bar s{display:block;height:100%;border-radius:3px;background:linear-gradient(90deg,#FFD36E,#F4593C);transition:width .6s var(--ease)}
/* 翻卡层里的宝宝周记 */
.bx-diary{width:100%;max-width:300px;margin-top:12px;padding:12px 14px;border-radius:18px;background:rgba(255,255,255,.14);text-align:left;
opacity:0;transform:translateY(16px);transition:all .45s}
.bx-diary:empty{display:none}
.bx-reveal.is-done .bx-diary{opacity:1;transform:none;transition-delay:.22s}
.bx-dy-h{font-size:12px;font-weight:800;color:#FFE3A6;margin-bottom:4px}
.bx-diary p{font-size:13.5px;line-height:1.7;color:#fff;font-family:var(--font-kid)}
/* 集齐庆祝弹层 + 收货表单 */
.bx-prize{text-align:center}
.bx-prize-ico{font-size:46px;line-height:1;margin:6px 0 8px}
.bx-prize h3{font-size:18px;font-weight:900}
.bx-prize p{margin:8px 4px 4px;font-size:14px;line-height:1.7;color:var(--ink-2)}
.bx-addr-tip{margin:-2px 0 12px;font-size:13px;line-height:1.6;color:var(--ink-2)}
/* 集齐奖励:三段一排(每格窄,名称 / 奖励 / 状态竖排),全集独占一行横排 */
.bx-goal{grid-template-columns:repeat(3,1fr);gap:7px}
.bx-gl:not(.all) .bx-gl-h{flex-direction:column;align-items:flex-start;gap:2px}
.bx-gl:not(.all) .bx-gl-l,.bx-gl:not(.all) .bx-gl-s,.bx-gl:not(.all) .bx-gl-b{margin-left:0}
.bx-gl:not(.all){padding:9px 10px}