悄悄话全屏页四改:一屏一句竖滑沉浸式、逐字亮起、光晕随句变色、双击抱抱、全部抽屉 (v26100168)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 06:54:02 +00:00
parent a9581b64a1
commit e70e4cb91b
3 changed files with 198 additions and 88 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:26100167 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<!-- V:26100168 缓存版本标记:改 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 第四节) -->
@@ -1766,23 +1766,25 @@
层级 z 110:盖住 tabbar(60),低于说一句弹窗(modal 120+),所以在这页里点「说一句」弹窗能盖在上面。 -->
<section class="wh-page" id="whPage" role="dialog" aria-modal="true" aria-labelledby="whpTitle" aria-hidden="true">
<div class="whp-sky" aria-hidden="true"><i class="s1"></i><i class="s2"></i><i class="s3"></i><i class="moon"></i></div>
<div class="whp-scroll" id="whpScroll">
<header class="whp-top">
<button class="whp-back" id="whpBack" type="button" aria-label="返回">
<svg viewBox="0 0 24 24" width="20" height="20"><path d="M15 5l-7 7 7 7" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<span class="whp-tag" id="whpTag"></span>
</header>
<h2 class="whp-title" id="whpTitle">悄悄话</h2>
<p class="whp-sub">不留名字,只留一句话。<br>听到了,就给她一个抱抱。</p>
<!-- 2026-10-02 三改(用户:「多人提交时看得不清晰……配色没问题,创意呈现上调整」):
上 = 星图:每句悄悄话一颗星,抱抱越多越大越亮,连成今晚的星座;点星星 → 中间聚焦卡换成那一句(也会自动轮播)。
下 = 全部悄悄话:单列清单,一行一句,人再多也清楚。 -->
<div class="whp-sky2" id="whpSky" role="group" aria-label="今晚的星星,每颗是一句悄悄话"></div>
<div class="whp-focus" id="whpFocus" aria-live="polite"></div>
<div class="whp-lh"><b>全部悄悄话</b><span id="whpCount"></span></div>
<div class="whp-list" id="whpList"></div>
<p class="whp-end" id="whpEnd"></p>
<!-- 2026-10-02 四改(用户:「色调可以,整体设计再调整,看其他孕妈的内容要做得炫酷一点」):
改成「一屏一句」的竖滑沉浸式(像刷短视频):封面 = 大标题 + 今晚星座;往上滑每屏一句大字,
逐字亮起;背后两团光晕随每句换色;右侧抱抱大按钮,双击屏幕也能抱抱(爱心在手指处炸开);
顶部一排进度条;右上「全部」拉出清单抽屉,点哪句跳哪句。配色沿用深夜星河。 -->
<div class="whv-aura" id="whvAura" aria-hidden="true"><i></i><i></i></div>
<header class="whv-top">
<button class="whp-back" id="whpBack" type="button" aria-label="返回">
<svg viewBox="0 0 24 24" width="20" height="20"><path d="M15 5l-7 7 7 7" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
<div class="whv-bars" id="whvBars" aria-hidden="true"></div>
<button class="whv-all" id="whvAll" type="button">全部</button>
</header>
<div class="whv" id="whv"></div>
<h2 class="sr-only" id="whpTitle">同孕周悄悄话</h2>
<div class="whd" id="whDrawer" aria-hidden="true">
<div class="whd-panel" role="dialog" aria-label="全部悄悄话">
<div class="whd-head"><b>全部悄悄话</b><span id="whpCount"></span><button type="button" class="whd-x" id="whdX" aria-label="收起"><svg viewBox="0 0 24 24" width="14" height="14"><path d="M6 6l12 12M18 6L6 18" stroke="currentColor" stroke-width="2.4" stroke-linecap="round"/></svg></button></div>
<div class="whp-list" id="whpList"></div>
</div>
</div>
<div class="whp-bar">
<button class="whp-say" id="whSay" type="button"><span>说一句悄悄话</span></button>

174
js/app.js
View File

@@ -3,7 +3,7 @@
(function () {
'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26100167';
var VER = '26100168';
const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -3341,101 +3341,137 @@
return '<button class="whp-hug' + (it.hugged ? ' on' : '') + '" type="button" data-hug="' + esc(it.id) + '" aria-label="给她一个抱抱">' +
HEART + '<b>' + (it.hugs || '') + '</b></button>';
}
/* 星图 + 聚焦卡 + 清单(三改)。星星位置按 id 哈希定在网格里再抖一抖,同一句每次出现在同一处 */
let fi = 0, focT = null, focHold = false;
/* 四改:一屏一句的竖滑沉浸页。slide 0 = 封面(标题 + 星座),slide k = 第 k 句。 */
const RM = !!(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches);
const AURA = [['#6B4AA8', '#C2667A'], ['#3F4FA6', '#8E5BB0'], ['#9A4F86', '#E0907A'], ['#365C9E', '#7A58AE'], ['#7D3F8C', '#D27A9A']];
const hsh = s => { let h = 7; for (const c of String(s)) h = (h * 31 + c.charCodeAt(0)) >>> 0; return h; };
function paintSky(l) {
let cur = 0, io = null, vl = [];
/* 沉浸页先看别人的,自己说的放最后(金色收尾) */
const vlist = () => list().filter(x => !x.mine).concat(list().filter(x => x.mine));
const whv = $('#whv');
function skySvg(l) {
const n = l.length, cols = n > 12 ? 5 : 4, rows = Math.max(2, Math.ceil(n / cols));
const pts = l.map((it, i) => {
const h = hsh(it.id), c = i % cols, r = Math.floor(i / cols);
/* 蛇形排列,连线不来回横穿 */
const cc = r % 2 ? cols - 1 - c : c;
const x = ((cc + .5 + ((h % 100) / 100 - .5) * .62) / cols) * 100;
const y = ((r + .5 + (((h >> 8) % 100) / 100 - .5) * .55) / rows) * 100;
const sz = it.mine ? 13 : Math.round(5 + Math.min(it.hugs || 0, 40) / 40 * 11);
return { x, y, sz, it, i };
const h = hsh(it.id), c = i % cols, r = Math.floor(i / cols), cc = r % 2 ? cols - 1 - c : c;
return { i, it, x: ((cc + .5 + ((h % 100) / 100 - .5) * .62) / cols) * 100, y: ((r + .5 + (((h >> 8) % 100) / 100 - .5) * .55) / rows) * 100,
sz: it.mine ? 12 : Math.round(5 + Math.min(it.hugs || 0, 40) / 40 * 10) };
});
$('#whpSky').innerHTML =
'<svg class="whs-lines" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true"><polyline points="' +
return '<svg class="whs-lines" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true"><polyline points="' +
pts.map(p => p.x.toFixed(1) + ',' + p.y.toFixed(1)).join(' ') + '"/></svg>' +
pts.map(p => '<button type="button" class="whs-star' + (p.it.mine ? ' mine' : '') + (p.i === fi ? ' on' : '') +
'" data-fi="' + p.i + '" style="left:' + p.x.toFixed(1) + '%;top:' + p.y.toFixed(1) + '%;--s:' + p.sz + 'px;--tw:' + ((hsh(p.it.id) % 30) / 10).toFixed(1) + 's" aria-label="' +
esc(p.it.text) + '"><i></i></button>').join('');
pts.map(p => '<button type="button" class="whs-star' + (p.it.mine ? ' mine' : '') + '" data-go="' + (p.i + 1) + '" style="left:' + p.x.toFixed(1) + '%;top:' + p.y.toFixed(1) +
'%;--s:' + p.sz + 'px;--tw:' + ((hsh(p.it.id) % 30) / 10).toFixed(1) + 's" aria-label="' + esc(p.it.text) + '"><i></i></button>').join('');
}
function paintFocus(anim) {
const l = list(), it = l[fi];
const box = $('#whpFocus');
if (!it) { box.innerHTML = ''; return; }
box.innerHTML =
'<div class="whf-top"><span class="whf-no">' + (it.mine ? '<em>我说的</em>' : '') + 'No.' + (fi + 1) + ' / ' + l.length + '</span>' +
'<span class="whf-nav"><button type="button" data-fn="-1" aria-label="上一句">‹</button><button type="button" data-fn="1" aria-label="下一句">›</button></span></div>' +
'<p class="whf-q' + (anim ? ' in' : '') + '">' + esc(it.text) + '</p>' +
'<div class="whp-meta"><span>孕 ' + it.week + ' 周 · ' + esc(it.ago) + '</span>' + hugBtn(it) + '</div>';
$$('.whs-star', $('#whpSky')).forEach(s => s.classList.toggle('on', +s.dataset.fi === fi));
}
function focusTo(i, user) {
const n = list().length;
if (!n) return;
fi = (i + n) % n;
if (user) focHold = true; // 用户自己点过就不再自动轮播,免得正要抱抱时换走
paintFocus(true);
function slide(it, i, n) {
const t = String(it.text || ''), len = [...t].length;
const fs = len <= 9 ? 'xl' : len <= 18 ? 'l' : 'm';
const chars = [...t].map((c, k) => '<span style="--c:' + k + '">' + esc(c) + '</span>').join('');
const rail = it.mine
? '<div class="whv-got">' + HEART + '<b>' + (it.hugs || 0) + '</b><small>收到的抱抱</small></div>'
: '<button class="whv-hug whp-hug' + (it.hugged ? ' on' : '') + '" type="button" data-hug="' + esc(it.id) + '" aria-label="给她一个抱抱">' + HEART + '<b>' + (it.hugs || '') + '</b></button><small class="whv-hl">抱抱</small>';
return '<article class="whv-s' + (it.mine ? ' mine' : '') + '" data-i="' + (i + 1) + '">' +
'<div class="whv-in">' +
'<div class="whv-no"><b>' + String(i + 1).padStart(2, '0') + '</b><span>/ ' + String(n).padStart(2, '0') + '</span>' + (it.mine ? '<em>我说的</em>' : '') + '</div>' +
'<p class="whv-q ' + fs + '">' + chars + '</p>' +
'<div class="whv-meta">孕 ' + it.week + ' 周的妈妈 · ' + esc(it.ago) + '</div>' +
(i === 0 && !it.mine ? '<div class="whv-tip">双击屏幕,也能给她一个抱抱</div>' : '') +
'</div><div class="whv-rail">' + rail + '</div></article>';
}
function paintList(l) {
$('#whpCount').textContent = l.length + ' 句 · 最新在上';
$('#whpCount').textContent = l.length + ' 句 · 我说的在最后';
$('#whpList').innerHTML = l.map((it, i) => {
const lv = it.mine ? 'mine' : it.hugs >= 25 ? 'l3' : it.hugs >= 10 ? 'l2' : 'l1';
return '<div class="whl ' + lv + '"><button type="button" class="whl-t" data-fi="' + i + '">' +
return '<div class="whl ' + lv + '"><button type="button" class="whl-t" data-go="' + (i + 1) + '">' +
(it.mine ? '<em>我说的</em>' : '') + esc(it.text) + '</button>' +
'<div class="whp-meta"><span>孕 ' + it.week + ' 周 · ' + esc(it.ago) + '</span>' + hugBtn(it) + '</div></div>';
}).join('');
}
function setCur(i) {
cur = i;
const n = list().length;
$$('.whv-bars i', page).forEach((b, k) => { b.classList.toggle('done', k < i - 1); b.classList.toggle('on', k === i - 1); });
$('#whvBars').classList.toggle('hide', i === 0);
const it = vl[i - 1];
const pal = it && it.mine ? ['#A8723A', '#E0A86A'] : i === 0 ? ['#5A3B72', '#4A2C55'] : AURA[(i - 1) % AURA.length];
const au = $$('#whvAura i');
au[0].style.backgroundColor = pal[0]; au[1].style.backgroundColor = pal[1];
$$('.whs-star', page).forEach(s => s.classList.toggle('on', +s.dataset.go === i));
}
function paintPage() {
const l = list();
$('#whpTag').textContent = data ? '孕 ' + data.week + ' 周 · 前后一周的妈妈' : '';
/* 北京时间 18 点后 / 5 点前叫「今晚」,其余「今天」 */
const l = vl = vlist(), n = l.length;
const bh = (new Date().getUTCHours() + 8) % 24, night = bh >= 18 || bh < 5;
$('#whpTitle').innerHTML = (night ? '今晚' : '今天') + ',<b>' + l.length + '</b> 句悄悄话';
$('#whpEnd').textContent = night ? '— 都在这里了,晚安 —' : '— 都在这里了 —';
/* 第一次聚焦:抱抱最多的那句(不选自己的) */
fi = 0;
l.forEach((x, i) => { if (!x.mine && x.hugs > (l[fi].mine ? -1 : l[fi].hugs)) fi = i; });
paintSky(l);
paintFocus(false);
whv.innerHTML =
'<article class="whv-s whv-cover" data-i="0"><div class="whv-in">' +
'<span class="whp-tag">' + (data ? '孕 ' + data.week + ' 周 · 前后一周的妈妈' : '') + '</span>' +
'<div class="whv-ct">' + (night ? '今晚' : '今天') + ',<br><b>' + n + '</b> 句悄悄话</div>' +
'<p class="whp-sub">不留名字,只留一句话。听到了,就给她一个抱抱。</p>' +
'<div class="whp-sky2" id="whpSky" role="group" aria-label="今晚的星星,每颗是一句悄悄话">' + skySvg(l) + '</div>' +
'<div class="whv-down" aria-hidden="true"><i></i>上滑,听她们说</div></div></article>' +
l.map((it, i) => slide(it, i, n)).join('') +
'<article class="whv-s whv-end" data-i="' + (n + 1) + '"><div class="whv-in"><div class="whv-ct sm">' +
(night ? '都在这里了,<br>晚安。' : '都在这里了。') + '</div><p class="whp-sub">你也说一句吧,她们会听到的。</p></div></article>';
$('#whvBars').innerHTML = l.map(() => '<i></i>').join('');
paintList(l);
$('#whpLeft').textContent = !data ? '' : !oid() || !data.canPost ? '填好预产期,就能说一句' : data.left ? '今天还能说 ' + data.left + ' 句' : '今天的 3 句说完啦,明天再来';
if (io) io.disconnect();
if ('IntersectionObserver' in window) {
io = new IntersectionObserver(es => es.forEach(e => {
e.target.classList.toggle('on', e.isIntersecting);
if (e.isIntersecting) setCur(+e.target.dataset.i);
}), { root: whv, threshold: .6 });
$$('.whv-s', whv).forEach(s => io.observe(s));
} else $$('.whv-s', whv).forEach(s => s.classList.add('on'));
setCur(0);
}
function goTo(i, smooth) {
const s = $('.whv-s[data-i="' + i + '"]', whv);
if (s) whv.scrollTo({ top: s.offsetTop, behavior: smooth && !RM ? 'smooth' : 'auto' });
}
function drawer(on) {
$('#whDrawer').classList.toggle('show', on);
$('#whDrawer').setAttribute('aria-hidden', on ? 'false' : 'true');
}
$('#whvAll').addEventListener('click', () => { haptic(); drawer(true); });
$('#whdX').addEventListener('click', () => drawer(false));
$('#whDrawer').addEventListener('click', e => { if (e.target.id === 'whDrawer') drawer(false); });
page.addEventListener('click', e => {
const st = e.target.closest('[data-fi]');
if (st) {
haptic();
focusTo(+st.dataset.fi, true);
if (st.classList.contains('whl-t')) $('#whpScroll').scrollTo({ top: $('#whpSky').offsetTop - 70, behavior: 'smooth' });
return;
}
const nb = e.target.closest('[data-fn]');
if (nb) { haptic(); focusTo(fi + +nb.dataset.fn, true); }
const g = e.target.closest('[data-go]');
if (!g) return;
haptic();
drawer(false);
goTo(+g.dataset.go, true);
});
/* 聚焦卡左右滑切换 */
let tx = null;
$('#whpFocus').addEventListener('touchstart', e => { tx = e.touches[0].clientX; }, { passive: true });
$('#whpFocus').addEventListener('touchend', e => {
if (tx == null) return;
const dx = e.changedTouches[0].clientX - tx; tx = null;
if (Math.abs(dx) > 40) focusTo(fi + (dx < 0 ? 1 : -1), true);
/* 双击屏幕 = 抱抱:大爱心在手指处炸开,再走下面同一条抱抱逻辑 */
let lastTap = 0;
whv.addEventListener('click', e => {
if (e.target.closest('button')) return;
const now = Date.now();
if (now - lastTap > 320) { lastTap = now; return; }
lastTap = 0;
const s = e.target.closest('.whv-s');
const hb = s && $('.whv-hug', s);
if (!hb) return;
if (!RM) {
const big = document.createElement('i');
big.className = 'whv-big';
big.innerHTML = HEART;
const r = page.getBoundingClientRect();
big.style.left = (e.clientX - r.left) + 'px';
big.style.top = (e.clientY - r.top) + 'px';
page.appendChild(big);
setTimeout(() => big.remove(), 900);
}
if (!hb.classList.contains('on')) hb.click();
});
function openPage() {
paintPage();
page.classList.add('show');
page.setAttribute('aria-hidden', 'false');
$('#whpScroll').scrollTop = 0;
whv.scrollTop = 0;
drawer(false);
haptic();
focHold = false;
clearInterval(focT);
if (!(window.matchMedia && matchMedia('(prefers-reduced-motion: reduce)').matches))
focT = setInterval(() => { if (!focHold && !document.hidden && list().length > 1) focusTo(fi + 1); }, 6000);
}
function closePage() {
clearInterval(focT);
drawer(false);
page.classList.remove('show');
page.setAttribute('aria-hidden', 'true');
}
@@ -3491,7 +3527,6 @@
const o = oid();
if (!o) { toast('登录后就能给她抱抱啦'); return; }
if (b.classList.contains('on')) { toast('你已经抱过她啦'); return; }
focHold = true;
const id = b.dataset.hug;
const it = list().find(x => x.id === id);
haptic();
@@ -3556,8 +3591,7 @@
paintEntry();
paintPage();
closeModal();
fi = 0; focHold = true; paintFocus(true);
$('#whpScroll').scrollTo({ top: $('#whpSky').offsetTop - 70, behavior: 'smooth' });
setTimeout(() => goTo(vl.findIndex(x => x.id === it.id) + 1, true), 80);
toast('发出去啦,同孕周妈妈会给你抱抱的');
})
.catch(() => toast('网络不太好,再试一次'))

View File

@@ -3790,3 +3790,77 @@ a.skip:focus{left:16px;top:16px}
.td-row{gap:12px}
.td-row:active .td-li{background:#D5EEE1}
.td-li svg{width:22px;height:22px}
/* ===== 2026-10-02 悄悄话全屏页 · 四改:一屏一句竖滑沉浸式 ===== */
.whv-aura{position:absolute;inset:0;pointer-events:none;overflow:hidden}
.whv-aura i{position:absolute;width:120vw;height:120vw;max-width:560px;max-height:560px;border-radius:50%;filter:blur(70px);opacity:.55;
transition:background-color 1.2s ease;animation:whvDrift 14s ease-in-out infinite alternate}
.whv-aura i:first-child{left:-35%;top:8%}
.whv-aura i:last-child{right:-40%;bottom:6%;animation-delay:-7s;opacity:.45}
@keyframes whvDrift{from{transform:translate(0,0) scale(1)}to{transform:translate(12%,-8%) scale(1.15)}}
.wh-page .whp-sky i.moon{opacity:.5}
.whv-top{position:absolute;left:0;right:0;top:0;z-index:3;display:flex;align-items:center;gap:10px;padding:calc(12px + env(safe-area-inset-top)) 16px 10px;
background:linear-gradient(rgba(20,14,40,.55),transparent)}
.whv-bars{flex:1;display:flex;gap:3px;transition:opacity .3s}
.whv-bars.hide{opacity:0}
.whv-bars i{flex:1;height:3px;border-radius:2px;background:rgba(255,255,255,.18);overflow:hidden;position:relative}
.whv-bars i.done{background:rgba(255,255,255,.7)}
.whv-bars i.on{background:linear-gradient(90deg,#FFD9A8,#FF9E8A);box-shadow:0 0 6px rgba(255,190,150,.8)}
.whv-all{margin-left:auto;height:30px;padding:0 13px;border-radius:99px;background:rgba(255,255,255,.12);color:#fff;font-size:12.5px;font-weight:700;backdrop-filter:blur(6px)}
.whv{position:absolute;inset:0;z-index:1;overflow-y:auto;scroll-snap-type:y mandatory;overscroll-behavior:contain;scrollbar-width:none;-webkit-overflow-scrolling:touch}
.whv::-webkit-scrollbar{display:none}
.whv-s{position:relative;height:100%;scroll-snap-align:start;scroll-snap-stop:always;display:flex;align-items:center;
padding:70px 84px 130px 28px;box-sizing:border-box;user-select:none;-webkit-user-select:none}
.whv-in{width:100%}
.whv-cover,.whv-end{padding-right:28px}
.whv-cover{align-items:flex-start;padding-top:calc(64px + env(safe-area-inset-top))}
.whv-ct{margin-top:18px;font-size:34px;line-height:1.25;font-weight:900;letter-spacing:1px}
.whv-ct b{font-size:64px;font-family:"DIN Alternate","DIN",Helvetica,Arial,sans-serif;background:linear-gradient(120deg,#FFE2B8,#FF9E8A);-webkit-background-clip:text;background-clip:text;color:transparent}
.whv-ct.sm{font-size:28px}
.whv-cover .whp-sky2{height:200px;margin:14px -8px 0}
.whv-down{display:flex;flex-direction:column;align-items:center;gap:6px;margin-top:6px;font-size:12px;color:rgba(255,255,255,.6);letter-spacing:2px}
.whv-down i{width:18px;height:18px;border:solid rgba(255,255,255,.7);border-width:2px 2px 0 0;transform:rotate(-45deg);animation:whvUp 1.6s ease-in-out infinite}
@keyframes whvUp{0%,100%{transform:translateY(4px) rotate(-45deg);opacity:.4}50%{transform:translateY(-4px) rotate(-45deg);opacity:1}}
.whv-no{display:flex;align-items:baseline;gap:6px;font-family:"DIN Alternate","DIN",Helvetica,Arial,sans-serif;color:rgba(255,255,255,.45);letter-spacing:1px}
.whv-no b{font-size:22px;color:rgba(255,226,190,.9)}
.whv-no span{font-size:13px}
.whv-no em{margin-left:6px;font-style:normal;font-family:inherit;font-size:11.5px;font-weight:800;padding:3px 9px;border-radius:99px;background:#FFD596;color:#5A3510;letter-spacing:0}
.whv-q{margin:18px 0 0;font-weight:900;line-height:1.35;letter-spacing:1px;text-shadow:0 2px 24px rgba(0,0,0,.25)}
.whv-q.xl{font-size:40px}.whv-q.l{font-size:32px}.whv-q.m{font-size:26px}
.whv-q span{display:inline-block;opacity:0;filter:blur(8px);transform:translateY(10px)}
.whv-s.on .whv-q span{animation:whvCh .6s cubic-bezier(.2,.8,.2,1) forwards;animation-delay:calc(var(--c) * 55ms + 120ms)}
@keyframes whvCh{to{opacity:1;filter:blur(0);transform:none}}
.whv-s.mine .whv-q{background:linear-gradient(120deg,#FFF1D6,#FFC48A);-webkit-background-clip:text;background-clip:text;color:transparent;text-shadow:none}
.whv-meta{margin-top:18px;font-size:13px;color:rgba(255,255,255,.6);opacity:0;transition:opacity .6s .5s}
.whv-tip{display:inline-block;margin-top:22px;font-size:11.5px;padding:5px 11px;border-radius:99px;background:rgba(255,255,255,.08);color:rgba(255,255,255,.6);opacity:0;transition:opacity .6s 1s}
.whv-s.on .whv-meta,.whv-s.on .whv-tip{opacity:1}
.whv-rail{position:absolute;right:16px;bottom:150px;display:flex;flex-direction:column;align-items:center;gap:6px}
.whv-hug.whp-hug{flex-direction:column;justify-content:center;width:58px;height:58px;padding:0;gap:0;border-radius:50%;background:rgba(255,255,255,.12);backdrop-filter:blur(8px);box-shadow:inset 0 0 0 1px rgba(255,255,255,.15)}
.whv-hug.whp-hug svg{width:26px;height:26px}
.whv-hug.whp-hug b{font-size:11.5px;line-height:1.1}
.whv-hug.whp-hug.on{box-shadow:0 6px 22px rgba(255,111,145,.5)}
.whv-hl,.whv-got small{font-size:11px;color:rgba(255,255,255,.55)}
.whv-got{display:flex;flex-direction:column;align-items:center;gap:2px;color:#FFD596}
.whv-got svg{width:30px;height:30px;fill:#FFD596;filter:drop-shadow(0 0 10px rgba(255,200,120,.8))}
.whv-got b{font-size:18px;font-family:"DIN Alternate","DIN",Helvetica,Arial,sans-serif}
.whv-big{position:absolute;z-index:5;width:96px;height:96px;margin:-48px 0 0 -48px;pointer-events:none;animation:whvBig .9s cubic-bezier(.2,.8,.2,1) forwards}
.whv-big svg{width:100%;height:100%;fill:#FF6F91;filter:drop-shadow(0 0 18px rgba(255,111,145,.8))}
@keyframes whvBig{0%{transform:scale(.2) rotate(-12deg);opacity:0}25%{transform:scale(1.15) rotate(6deg);opacity:1}55%{transform:scale(1)}100%{transform:translateY(-70px) scale(1.3);opacity:0}}
.whd{position:absolute;inset:0;z-index:6;background:rgba(10,6,24,.5);opacity:0;visibility:hidden;transition:opacity .3s,visibility .3s}
.whd.show{opacity:1;visibility:visible}
.whd-panel{position:absolute;left:0;right:0;bottom:0;max-height:72%;overflow-y:auto;padding:18px 20px calc(20px + env(safe-area-inset-bottom));border-radius:22px 22px 0 0;
background:linear-gradient(#2A1F48,#1E1634);box-shadow:0 -10px 40px rgba(0,0,0,.4);transform:translateY(100%);transition:transform .35s cubic-bezier(.2,.8,.2,1)}
.whd.show .whd-panel{transform:none}
.whd-head{display:flex;align-items:baseline;gap:10px;margin-bottom:4px}
.whd-head b{font-size:17px}
.whd-head span{font-size:12px;color:rgba(255,255,255,.5)}
.whd-x{margin-left:auto;width:30px;height:30px;border-radius:50%;background:rgba(255,255,255,.1);color:#fff;font-size:13px}
.wh-page .whp-bar{z-index:2}
@media (prefers-reduced-motion:reduce){
.whv-aura i,.whv-down i{animation:none}
.whv-q span{opacity:1;filter:none;transform:none}
.whv-s.on .whv-q span{animation:none}
}
.whv-s.mine .whv-q{background:none;-webkit-background-clip:border-box;background-clip:border-box;color:#FFDDAE;text-shadow:0 0 22px rgba(255,190,120,.55)}
.whd-x{display:grid;place-items:center}
.whv-ct b{font-size:60px;margin-right:4px}