今日脚印改版(撕页日历+小脚丫时间线+宝宝便签),消消乐卡压矮

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 16:22:55 +00:00
parent da995ae540
commit 3f115bd37f
3 changed files with 145 additions and 83 deletions

View File

@@ -7,7 +7,7 @@
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes"> <meta name="apple-mobile-web-app-capable" content="yes">
<title>兜爸心选 · 孕育陪伴</title> <title>兜爸心选 · 孕育陪伴</title>
<!-- V:26100138 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 --> <!-- V:26100139 缓存版本标记:改 css/js 时同步 bump 该值(与 js/app.js 的 VER 同源),静态资源按 mtime 走 ETag 自动刷新 -->
<link rel="stylesheet" href="mama-box.css"> <link rel="stylesheet" href="mama-box.css">
<script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script> <script src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
<!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) --> <!-- 共享助手:授权/跳商城/位置上报(分享卡片同步,见 docs/h5-auth-standard.md 第四节) -->
@@ -380,83 +380,36 @@
⚠️ .steps 仍是整卡可点的 role="button",里面**不能**再出现 <button>。 ⚠️ .steps 仍是整卡可点的 role="button",里面**不能**再出现 <button>。
所以「查看全部 ›」挂在 .tl-head 上、AI 气泡是 .steps 的兄弟节点, 所以「查看全部 ›」挂在 .tl-head 上、AI 气泡是 .steps 的兄弟节点,
两者都在 .steps 外面。 --> 两者都在 .steps 外面。 -->
<section class="today-line"> <!-- 今日脚印 2026-10-01 改版(用户:「对标其他模块,UI 要漂亮一些,内容文字也要有创意」):
<div class="tl-head"> · 头部左边一张撕页日历(日 / 月·周几),右边标题 + 一句小诗;
<span class="tl-h-when">今天</span> · 每条记录前的圆点换成**一左一右交替的小脚丫**,沿虚线往下走 —— 「脚印」两个字画出来;
<span class="tl-h-dot"></span> · 每条记录是一张小卡:大字内容(数字加粗上色)+ 时间 + 来源小标签;
<h2 class="tl-h-title" id="tlTitle">宝宝的今日脚印</h2> · 没记录时(guide)是一行脚丫插画 + 接口给的引导语(童趣字体)+ 带 logo 的应用芯片;
<button class="tl-h-all" data-goto="archive">查看全部 ›</button> · AI 小结做成「宝宝写给妈妈的一张便签」:信纸横线 + 宝宝口吻 + 落款小名。
⚠️ 「AI」角标必须保留(唯一的机器生成提示,见 syncBabyWords 那段注释)。
JS 依赖的 id / class(.today-line / #tlSteps / .step 系列 / #tlGuide* / .ai-bubble / .ab-say / #abTag / #tlTitle)都沿用。 -->
<section class="today-line fp2">
<div class="fp-head">
<div class="fp-cal" aria-hidden="true"><i class="fc-ring"></i><b id="fpCalD">--</b><span id="fpCalM">--</span></div>
<div class="fp-ht">
<h2 class="tl-h-title" id="tlTitle">宝宝的今日脚印</h2>
<p class="fp-poem">你记下的每一笔,都是它长大的样子</p>
</div>
<button class="fp-all" data-goto="archive">全部<i>›</i></button>
</div> </div>
<!-- 这句是首页 slogan 挪走之后接手的那一句(用户点名要放这儿)。 <div class="steps fp-steps ripple" id="tlSteps" data-goto="archive" role="button" tabindex="0" aria-label="宝宝的今日脚印,查看完整档案">
⚠️ 全站现在只有这一处写着「把每次记录变成孩子一生的记忆」,
顶上 slogan 已经换成「每次记录都是一生记忆」了,别再抄回去 ——
两处贴着同一句话,等于把这句说废了。 -->
<p class="tl-h-note">把每次记录变成孩子一生的记忆</p>
<!-- 整张卡都能点。上面那颗「查看全部 ›」正好当成「这张能点」的提示
(整卡可点最大的毛病就是看不出来能点)。 -->
<!-- ⚠️ aria-label 里也有小名,所以它跟 #tlTitle 一起交给 syncBabyWords()
刷。标题跟着小名走、读屏却一直念旧名字,是最难被发现的那种不同步 ——
眼睛看不见它。 -->
<!-- ⚠️ 这里原来躺着 4 条**写死的演示行**(胎动 12 次 / 体重 58.2kg / 产检提醒 / 睡前散步)。
它们只在「接口读不到」时露出来 —— 也就是说,上游一抖动,页面就开始编数据。
2026-09-22 按产品口径删掉,换成一条中性占位;四种显示模式由下方 JS 的 fpApply() 决定:
· 有档案 + 拿到事件 → 渲染真实事件(覆盖 #tlSteps)
· 有档案 + 今天没记 → 「还没有足迹」
· 有档案 + 接口读不到 → **整块不显示**(不编数据)
· 无档案(上游查无此人)→ 显示 #tlGuide 引导占位
首次渲染时 `#tlSteps` 里是这条「正在获取…」,不是假数据;
JS 的 pendingFootprintStep() 会建一份**同构**的行,两处改文案要一起改。 -->
<div class="steps ripple" id="tlSteps" data-goto="archive" role="button" tabindex="0"
aria-label="宝宝的今日脚印,查看完整档案">
<div class="step"><span class="st-time">今天</span><span class="st-dot"></span><span class="st-txt">正在获取今天的脚印…</span><span class="st-from"></span></div> <div class="step"><span class="st-time">今天</span><span class="st-dot"></span><span class="st-txt">正在获取今天的脚印…</span><span class="st-from"></span></div>
</div> </div>
<!-- 引导占位(fpApply('guide') 才显示,此时 #tlSteps 收起): <div class="tl-guide fp-guide" id="tlGuide" style="display:none">
文案与应用**都来自接口** —— lead 是上游自己的引导语(如「宝妈,先从这几件事记起吧。」), <span class="fg-walk" aria-hidden="true"><i class="fw0"><svg viewBox="0 0 16 22" width="12" height="16" aria-hidden="true"><ellipse cx="8.4" cy="13.6" rx="4.6" ry="6.6" fill="currentColor"/><circle cx="3.6" cy="4.6" r="1.7" fill="currentColor"/><circle cx="6.6" cy="2.6" r="1.6" fill="currentColor"/><circle cx="9.8" cy="2.4" r="1.5" fill="currentColor"/><circle cx="12.6" cy="3.8" r="1.3" fill="currentColor"/></svg></i><i class="fw1"><svg viewBox="0 0 16 22" width="12" height="16" aria-hidden="true"><ellipse cx="8.4" cy="13.6" rx="4.6" ry="6.6" fill="currentColor"/><circle cx="3.6" cy="4.6" r="1.7" fill="currentColor"/><circle cx="6.6" cy="2.6" r="1.6" fill="currentColor"/><circle cx="9.8" cy="2.4" r="1.5" fill="currentColor"/><circle cx="12.6" cy="3.8" r="1.3" fill="currentColor"/></svg></i><i class="fw2"><svg viewBox="0 0 16 22" width="12" height="16" aria-hidden="true"><ellipse cx="8.4" cy="13.6" rx="4.6" ry="6.6" fill="currentColor"/><circle cx="3.6" cy="4.6" r="1.7" fill="currentColor"/><circle cx="6.6" cy="2.6" r="1.6" fill="currentColor"/><circle cx="9.8" cy="2.4" r="1.5" fill="currentColor"/><circle cx="12.6" cy="3.8" r="1.3" fill="currentColor"/></svg></i><i class="fw3"><svg viewBox="0 0 16 22" width="12" height="16" aria-hidden="true"><ellipse cx="8.4" cy="13.6" rx="4.6" ry="6.6" fill="currentColor"/><circle cx="3.6" cy="4.6" r="1.7" fill="currentColor"/><circle cx="6.6" cy="2.6" r="1.6" fill="currentColor"/><circle cx="9.8" cy="2.4" r="1.5" fill="currentColor"/><circle cx="12.6" cy="3.8" r="1.3" fill="currentColor"/></svg></i><i class="fw4"><svg viewBox="0 0 16 22" width="12" height="16" aria-hidden="true"><ellipse cx="8.4" cy="13.6" rx="4.6" ry="6.6" fill="currentColor"/><circle cx="3.6" cy="4.6" r="1.7" fill="currentColor"/><circle cx="6.6" cy="2.6" r="1.6" fill="currentColor"/><circle cx="9.8" cy="2.4" r="1.5" fill="currentColor"/><circle cx="12.6" cy="3.8" r="1.3" fill="currentColor"/></svg></i></span>
unused[] 是今天没用过的应用,能对上 App 表的会渲染成可点芯片(见 js 的 renderGuide)。 <span class="tl-guide-t" id="tlGuideTitle">还没有你的脚印</span>
⚠️ 版式**沿用下面 AI 气泡那套语言**(左列 + 圆点 + 带尖角的气泡),只是底色用白 —— <span class="tl-guide-s" id="tlGuideSub">去上面的工具记一笔,这里就会长出你今天的脚印</span>
用户反馈原来那张横跨整块的白卡「放在这有点突兀」;现在它与时间轴共用同一条竖线。 <span class="tl-guide-apps" id="tlGuideApps"></span>
左边的空 .ab-badge 只是**占位**,宽度 = --tl-time,好让圆点落在竖线上(与 AI 行同列)。 -->
<div class="tl-guide" id="tlGuide" style="display:none">
<span class="ab-badge"></span>
<span class="ab-dot"></span>
<span class="ab-body">
<span class="tl-guide-bubble">
<span class="tl-guide-t" id="tlGuideTitle">还没有你的脚印</span>
<span class="tl-guide-s" id="tlGuideSub">去上面的工具记一笔,这里就会长出你今天的脚印</span>
<span class="tl-guide-apps" id="tlGuideApps"></span>
</span>
</span>
</div> </div>
<!-- 这一天的收尾节点。三个直接子元素刻意和 .step 一样排 <button class="ai-bubble fp-note ripple" data-open="detailAI">
(42px 的左格 + 圆点 + 剩下的),点才落得进同一条竖线里。 <span class="fn-top"><span class="ab-badge">AI</span><b class="ab-tag" id="abTag">宝宝今天想说</b></span>
模块名「AI今日小结」降级成气泡里的一枚小标签, <span class="ab-say"></span>
说明和「往期小结 ›」并成气泡底下的一行落款 —— <span class="fn-foot"><span class="fn-sign" id="fpSign">—— 宝宝</span><em>往期小结 ›</em></span>
320px 视口下这三段挤在一行是塞不下的,所以标签得进气泡。 -->
<button class="ai-bubble ripple" data-open="detailAI">
<span class="ab-badge">AI</span>
<span class="ab-dot"></span>
<span class="ab-body">
<span class="ab-bubble">
<b class="ab-tag" id="abTag">宝宝今天想说</b>
<!-- 这句是 app.js 里 AI_LOGS[0].txt 的手写副本(这里要 <b> 和 <br> 排版,
没法直接共用那个字符串)。那边改口吻,这里必须跟着改,
否则首页气泡说「小家伙」、点进去小结说「我」,像两个人在写。
✅ 2026-09-23 已接真源:句子的数据来自 box_app 的 /footprint/today
(上游数胎动平台的用户面接口 /user/daily/footprint 的 `digest.text`),
由 fpRender() 写入;`digest.warn` 为真时整句标红(.ai-warn)。
⚠️ 但 AI 小结面板(详情页 renderAI → AI_LOGS)里那份仍是**演示文案**,
与这里是两套数据 —— 面板要一并用真数据,得先有 mood/stats/建议的字段。 -->
<!-- ⚠️ 文案由 js 的 fpRender() 从接口 `digest.text` 写入(今日脚印接口给的真文案)。
原来这里写死「妈妈,今天你数到我动了 3 次…」,任何用户都看到 —— 已删。
接口没给 digest(今天没活动 / 读不到)时整颗气泡由 .no-ai 收起,不留空壳。 -->
<span class="ab-say"></span>
</span>
<!-- 「AI自动汇总你每日记录」按用户要求删了,这行现在只剩右边的入口。
✅ 位置不用额外处理:.ab-foot em 本来就写着 margin-left:auto,
左边那截没了它照样贴右。(原来那条 .ab-foot i 的样式已成
死规则,一并删了。) -->
<span class="ab-foot"><em>往期小结 ›</em></span>
</span>
</button> </button>
</section> </section>
@@ -478,7 +431,7 @@
<span class="gp-new">新游戏</span> <span class="gp-new">新游戏</span>
<span class="gp-text"> <span class="gp-text">
<span class="gp-name">宝宝消消乐</span> <span class="gp-name">宝宝消消乐</span>
<span class="gp-sub">奶瓶、尿不湿、小衣服……<br>三个一样就消掉,边玩边备齐待产包</span> <span class="gp-sub">三个一样就消掉,边玩边备齐待产包</span>
<span class="gp-cta">开始玩 <svg viewBox="0 0 24 24" width="12" height="12" aria-hidden="true"><path d="M8 5l8 7-8 7z" fill="currentColor"/></svg></span> <span class="gp-cta">开始玩 <svg viewBox="0 0 24 24" width="12" height="12" aria-hidden="true"><path d="M8 5l8 7-8 7z" fill="currentColor"/></svg></span>
</span> </span>
<span class="gp-board" id="gpBoard" aria-hidden="true"></span> <span class="gp-board" id="gpBoard" aria-hidden="true"></span>

View File

@@ -3,7 +3,7 @@
(function () { (function () {
'use strict'; 'use strict';
/* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */ /* 页面版本号(YYMMDD+序号):显示在「关于兜爸心选」页底部;bump 时同步 head 注释与 ?v= */
var VER = '26100138'; var VER = '26100139';
const $ = (s, c) => (c || document).querySelector(s); const $ = (s, c) => (c || document).querySelector(s);
const $$ = (s, c) => Array.from((c || document).querySelectorAll(s)); const $$ = (s, c) => Array.from((c || document).querySelectorAll(s));
@@ -1373,6 +1373,7 @@
$('#dockTitle').textContent = babyWord('的孕育工具'); $('#dockTitle').textContent = babyWord('的孕育工具');
$('#abTag').textContent = babyWord('今天想说'); $('#abTag').textContent = babyWord('今天想说');
$('#tlTitle').textContent = babyWord('的今日脚印'); $('#tlTitle').textContent = babyWord('的今日脚印');
const _sg = $('#fpSign'); if (_sg) _sg.textContent = '—— 爱你的' + (state.babyName || '宝宝');
/* 整条时间轴自己是个 role="button",它的 aria-label 里也写着模块名。 /* 整条时间轴自己是个 role="button",它的 aria-label 里也写着模块名。
只刷可见的 #tlTitle、把这句留在原地,读屏用户会一直听见上一个小名 —— 只刷可见的 #tlTitle、把这句留在原地,读屏用户会一直听见上一个小名 ——
而这种不同步在屏幕上根本看不出来,只能靠这行和它的断言守着。 */ 而这种不同步在屏幕上根本看不出来,只能靠这行和它的断言守着。 */
@@ -4033,6 +4034,12 @@
function fpApply(mode, r) { _fpMode = mode; _fpData = r || null; fpRender(); if (plHook) plHook(); } function fpApply(mode, r) { _fpMode = mode; _fpData = r || null; fpRender(); if (plHook) plHook(); }
function fpRender() { function fpRender() {
const tl = document.querySelector('.today-line'), steps = $('#tlSteps'), guide = $('#tlGuide'); const tl = document.querySelector('.today-line'), steps = $('#tlSteps'), guide = $('#tlGuide');
const _cd = $('#fpCalD');
if (_cd) {
const n = new Date();
_cd.textContent = (n.getDate() < 10 ? '0' : '') + n.getDate();
$('#fpCalM').textContent = (n.getMonth() + 1) + '月 · 周' + '日一二三四五六'[n.getDay()];
}
if (!tl || !steps) return; if (!tl || !steps) return;
const mode = _fpMode, r = _fpData || {}; const mode = _fpMode, r = _fpData || {};
if (mode === 'hide') { tl.style.display = 'none'; return; } if (mode === 'hide') { tl.style.display = 'none'; return; }
@@ -4085,7 +4092,9 @@
const b = document.createElement('button'); const b = document.createElement('button');
b.type = 'button'; b.type = 'button';
b.className = 'tl-chip ripple'; b.className = 'tl-chip ripple';
b.textContent = u.appName || app.name || ident; const logo = (app && (app.logo || app.icon)) || '';
if (logo) { const im = document.createElement('img'); im.src = logo; im.alt = ''; im.loading = 'lazy'; b.appendChild(im); }
b.append(u.appName || (app && app.name) || ident);
b.addEventListener('click', () => jumpApp(href, (app && app.identifier) || ident || '')); b.addEventListener('click', () => jumpApp(href, (app && app.identifier) || ident || ''));
box.appendChild(b); box.appendChild(b);
} else if (u.appName) { } else if (u.appName) {
@@ -4097,8 +4106,8 @@
}); });
if (sub) { if (sub) {
sub.textContent = box.childElementCount sub.textContent = box.childElementCount
? '从下面这些记一笔,今天的脚印就会长出来' ? '挑一个记一笔,今天的第一只小脚丫就会印上来'
: '去上面的工具记一笔,这里就会长出你今天的脚印'; : '去上面的工具记一笔,今天的第一只小脚丫就会印上来';
} }
} }
@@ -4108,8 +4117,10 @@
const time = document.createElement('span'); time.className = 'st-time'; time.textContent = '今天'; const time = document.createElement('span'); time.className = 'st-time'; time.textContent = '今天';
const dot = document.createElement('span'); dot.className = 'st-dot'; const dot = document.createElement('span'); dot.className = 'st-dot';
const txt = document.createElement('span'); txt.className = 'st-txt'; txt.textContent = '正在获取今天的脚印…'; const txt = document.createElement('span'); txt.className = 'st-txt'; txt.textContent = '正在获取今天的脚印…';
const from = document.createElement('span'); from.className = 'st-from'; dot.innerHTML = FP_FOOT;
d.append(time, dot, txt, from); const card = document.createElement('span'); card.className = 'st-card';
card.append(txt);
d.append(dot, card);
return d; return d;
} }
@@ -4156,10 +4167,19 @@
txt.textContent = String(e.label || ''); txt.textContent = String(e.label || '');
} }
const from = document.createElement('span'); from.className = 'st-from'; const from = document.createElement('span'); from.className = 'st-from';
from.textContent = e.app ? '来源 · ' + e.app : ''; from.textContent = e.app || '';
d.append(time, dot, txt, from); /* 2026-10-01 脚印改版:圆点里放一只小脚丫(左右交替靠 CSS :nth-child 翻转),
文案 + 时间 + 来源包进一张小卡 .st-card。SVG 是写死的常量,不含上游字段。 */
dot.innerHTML = FP_FOOT;
const card = document.createElement('span'); card.className = 'st-card';
const meta = document.createElement('span'); meta.className = 'st-meta';
meta.append(time, from);
if (e.detail) { const dt = document.createElement('span'); dt.className = 'st-det'; dt.textContent = e.detail; meta.append(dt); }
card.append(txt, meta);
d.append(dot, card);
return d; return d;
} }
const FP_FOOT = '<svg viewBox="0 0 16 22" width="12" height="16" aria-hidden="true"><ellipse cx="8.4" cy="13.6" rx="4.6" ry="6.6" fill="currentColor"/><circle cx="3.6" cy="4.6" r="1.7" fill="currentColor"/><circle cx="6.6" cy="2.6" r="1.6" fill="currentColor"/><circle cx="9.8" cy="2.4" r="1.5" fill="currentColor"/><circle cx="12.6" cy="3.8" r="1.3" fill="currentColor"/></svg>';
/* 无足迹占位行:同构的一行,读屏和眼睛都不会把空卡当成坏卡。 */ /* 无足迹占位行:同构的一行,读屏和眼睛都不会把空卡当成坏卡。 */

View File

@@ -3076,3 +3076,92 @@ a.skip:focus{left:16px;top:16px}
.seg-b{text-align:center;white-space:nowrap} .seg-b{text-align:center;white-space:nowrap}
.stp-b{width:48px;min-width:48px;display:grid;place-items:center;padding:0} .stp-b{width:48px;min-width:48px;display:grid;place-items:center;padding:0}
.stp-ctl input{width:0} .stp-ctl input{width:0}
/* ================= 宝宝消消乐卡压矮(2026-10-01 用户:「高度可以降一降」)================= */
.gp-card{padding:11px 12px 11px 16px;border-radius:20px}
.gp-text{padding-top:10px}
.gp-name{font-size:18px}
.gp-sub{margin-top:3px;font-size:11.5px;line-height:1.45;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}
.gp-cta{margin-top:7px;padding:4px 12px;font-size:12px}
.gp-board{grid-template-columns:repeat(3,22px);gap:3px;padding:5px;border-radius:12px}
.gp-board i{width:22px;height:22px;border-radius:7px;font-size:13px}
.gp-card::before{width:120px;height:120px;bottom:-56px}
/* ================= 今日脚印 2026-10-01 改版(.fp2)=================
一张奶油色的「手账」:撕页日历 + 一左一右的小脚丫沿虚线往下走 + 宝宝写给妈妈的便签。
⚠️ 写在文件末尾,靠 .today-line.fp2 覆盖上面那套旧的时间轴列宽变量(--tl-*)布局。 */
.today-line.fp2{padding:16px 16px 16px;
background-color:#fff;
background-image:
radial-gradient(160px 110px at 100% 0%, rgba(255,214,190,.45), rgba(255,214,190,0) 70%),
radial-gradient(140px 120px at 0% 100%, rgba(255,226,236,.55), rgba(255,226,236,0) 70%),
linear-gradient(180deg,#FFFCF8,#FFF6EF)}
.today-line.fp2::before{display:none}
.fp-head{display:flex;align-items:center;gap:12px}
.fp-cal{position:relative;flex:0 0 56px;height:60px;border-radius:14px;overflow:hidden;background:#fff;
box-shadow:0 6px 14px rgba(200,110,80,.16),inset 0 0 0 1px #F6E3DA;display:flex;flex-direction:column;align-items:center;justify-content:flex-end;padding-bottom:6px}
.fp-cal::before{content:"";position:absolute;left:0;right:0;top:0;height:17px;background:linear-gradient(135deg,#FF8E6E,#F4593C)}
.fc-ring{position:absolute;top:-3px;left:14px;right:14px;height:9px;
background:radial-gradient(circle,#fff 2.2px,transparent 2.6px) 0 50%/14px 9px repeat-x}
.fp-cal b{font-size:23px;font-weight:900;line-height:1;color:#3A2C27;letter-spacing:-.5px;font-variant-numeric:tabular-nums}
.fp-cal span{font-size:9.5px;font-weight:700;color:#A08C82;margin-top:3px;white-space:nowrap}
.fp-ht{flex:1;min-width:0}
.fp2 .tl-h-title{font-size:18px;font-weight:900;color:var(--ink);letter-spacing:-.3px}
.fp-poem{margin-top:4px;font-size:12px;color:#A57F70;font-family:var(--font-kid);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fp-all{flex:0 0 auto;align-self:flex-start;margin-top:4px;display:flex;align-items:center;gap:1px;font-size:12.5px;font-weight:700;color:var(--muted)}
.fp-all i{font-style:normal;font-size:15px;line-height:1}
/* 脚印行:左边一只小脚丫(交替左右、交替颜色),沿一条虚线往下;右边一张小卡 */
.fp2 .fp-steps{position:relative;margin-top:14px;padding:0}
.fp2 .fp-steps::before{content:"";position:absolute;left:13px;top:16px;bottom:16px;border-left:2px dashed #F3D6CA}
.fp2 .step{display:flex;align-items:center;gap:10px;padding:5px 0;font-size:14px}
.fp2 .st-dot{flex:0 0 28px;width:28px;height:28px;border-radius:50%;display:grid;place-items:center;
background:#FFEDE6;color:#F4704F;box-shadow:0 0 0 3px #FFF8F3;z-index:1}
.fp2 .step:nth-child(odd) .st-dot svg{transform:scaleX(-1) rotate(-8deg)}
.fp2 .step:nth-child(even) .st-dot svg{transform:rotate(8deg) translateY(1px)}
.fp2 .step:nth-child(3n+2) .st-dot{background:#FDE8F0;color:#D9577A}
.fp2 .step:nth-child(3n) .st-dot{background:#FFF3D6;color:#E0A126}
.st-card{flex:1;min-width:0;display:flex;flex-direction:column;gap:4px;padding:10px 12px;border-radius:14px;background:rgba(255,255,255,.9);
box-shadow:0 3px 10px rgba(190,120,95,.08),inset 0 0 0 1px #F7EAE3}
.fp2 .st-txt{font-size:14.5px;font-weight:700;color:#3A2C27;white-space:normal;line-height:1.45}
.fp2 .st-txt b{font-size:17px;font-weight:900;color:var(--brand);margin:0 2px;font-variant-numeric:tabular-nums}
.st-meta{display:flex;align-items:center;gap:6px;min-width:0}
.fp2 .st-time{flex:0 0 auto;font-size:11.5px;font-weight:700;color:#B09A8F}
.fp2 .st-from{margin:0;font-size:10.5px;font-weight:700;color:#C0704F;background:#FFF0E8;border-radius:99px;padding:1px 8px}
.fp2 .st-from:empty{display:none}
.st-det{font-size:11px;color:#B09A8F;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* 还没脚印:一行小脚丫走过来 + 引导语(童趣字体)+ 带 logo 的应用芯片 */
.fp2 .fp-guide{display:block;margin-top:14px;padding:14px 14px 16px;border-radius:18px;text-align:center;
background:rgba(255,255,255,.85);box-shadow:inset 0 0 0 1.5px #F6E1D8;border:0}
.fp2 .fp-guide::before,.fp2 .fp-guide::after{display:none}
.fg-walk{display:flex;justify-content:center;align-items:flex-end;gap:12px;height:30px;color:#F6B39D}
.fg-walk i{display:block;line-height:0}
.fg-walk i:nth-child(odd){transform:translateY(-8px) scaleX(-1) rotate(-12deg)}
.fg-walk i:nth-child(even){transform:rotate(12deg)}
.fg-walk .fw0{opacity:.3}.fg-walk .fw1{opacity:.45}.fg-walk .fw2{opacity:.6}.fg-walk .fw3{opacity:.8}.fg-walk .fw4{color:var(--brand);animation:fpStep 1.6s ease-in-out infinite}
@keyframes fpStep{0%,100%{transform:rotate(12deg)}50%{transform:rotate(12deg) translateY(-3px) scale(1.12)}}
.fp2 .tl-guide-t{display:block;margin-top:10px;font-family:var(--font-kid);font-size:16.5px;font-weight:700;line-height:1.55;color:#4A3530}
.fp2 .tl-guide-s{display:block;margin-top:4px;font-size:12px;color:#A08C82}
.fp2 .tl-guide-apps{justify-content:center;gap:7px;margin-top:12px}
.fp2 .tl-chip{display:inline-flex;align-items:center;gap:5px;padding:5px 11px 5px 5px;border-radius:99px;border:0;
font-size:12.5px;font-weight:700;color:#4A3530;background:#fff;box-shadow:0 2px 8px rgba(190,110,85,.14),inset 0 0 0 1px #F6E1D8}
.fp2 .tl-chip img{width:20px;height:20px;border-radius:6px;object-fit:cover}
.fp2 .tl-chip.is-plain{padding-left:11px}
@media (prefers-reduced-motion:reduce){.fg-walk .fw4{animation:none}}
/* 宝宝的便签:信纸横线 + 胶带 + 落款 */
.fp2 .fp-note{position:relative;display:block;width:100%;margin-top:14px;padding:16px 16px 12px;text-align:left;border-radius:6px 18px 18px 18px;
overflow:visible;background:#FFFDF7;box-shadow:0 8px 20px rgba(200,120,90,.14),inset 0 0 0 1px #F5E4DA}
.fp2 .fp-note::after{content:"";position:absolute;left:50%;top:-8px;width:64px;height:16px;margin-left:-32px;transform:rotate(-3deg);
background:rgba(255,190,160,.55);border-radius:2px;box-shadow:0 1px 2px rgba(0,0,0,.04)}
.fn-top{display:flex;align-items:center;gap:7px}
.fp2 .ab-badge{flex:0 0 auto;margin:0;padding:2px 6px;border-radius:6px;font-size:10px;font-weight:900;color:#fff;
background:linear-gradient(135deg,#FF8E6E,#F4593C);letter-spacing:.5px}
.fp2 .ab-tag{margin:0;font-size:12.5px;font-weight:800;color:#C0573C;opacity:1}
.fp2 .ab-say{display:block;margin-top:8px;font-family:var(--font-kid);font-size:16.5px;font-weight:700;line-height:30px;color:#4A3530;
background-image:repeating-linear-gradient(180deg,transparent 0,transparent 29px,rgba(232,180,160,.38) 29px,rgba(232,180,160,.38) 30px)}
.fn-foot{display:flex;align-items:center;gap:8px;margin-top:6px}
.fn-sign{font-family:var(--font-kid);font-size:13px;color:#C0704F}
.fn-foot em{margin-left:auto;font-style:normal;font-size:12px;font-weight:700;color:var(--brand)}
.today-line.fp2.no-ai .fp-note{display:none}