/* ============================================================
   华生形象 v5(第六轮)—— 形象 CSS(可整体迁移到生产;演示页布局在页面内)
   渲染原则(08-22 用户目视后改):平涂底色 + 一层统一光照(顶光/高光点/腰部环境光遮蔽/右侧翻转暗)+ 壳纹 +
   同色系暖描边(不再用统一深色描边 → 去贴图感);无 glow、无外发光;动效 ease-out / ease-in-out,手势允许 ≤8% 轻微过冲;
   位移只在退场/回场剧允许(用户 08-14 评审授权),其余 ≤5px。
   第五轮(08-22 19:59 评审后):默认色板改 C 杏仁拿铁;镜框整体减薄;退场「钻土」改落花生叙事;新增书道具。
   第六轮(08-22 22:50 评审后):双臂常驻的活骨架(姿势由引擎 FK/IK 算,CSS 只管关节过渡与附加抖动);手持道具挂在手心随手走;
   思考/专注不再压眼皮;衣柜去无沿帽/罩杯式开衫/耳后铅笔,增圆领毛衣与渔夫帽。
   ============================================================ */

/* ---------- 色板 v4.2 · 默认 C 杏仁拿铁(A 焦糖杏仁与 B 燕麦奶之间:保留 A 的暖相,压低焦糖的饱和度与深度,轮廓与阴影同步变浅) ---------- */
.figure, .w4-palette {
  --w-shell: #F1E0C9;        /* 壳底色:杏仁拿铁(比 A 少一分黄,比 B 多一分暖) */
  --w-line: #8C6B55;         /* 描边:浅一阶的焦糖棕(A #8A6247 → 明度 +3、饱和 -8) */
  --w-shade: #C4A17E;        /* 光照层阴影色(配透明度;比 A 更灰更浅) */
  --w-groove: #B8977A;       /* 壳纹 */
  --w-face: #392D26;         /* 眼白层(深)与嘴线:浓缩咖啡 */
  --w-iris: #5B463B;
  --w-pupil: #2A1C16;
  --w-blush: #EE9F9C;        /* 腮红:珊瑚粉,径向渐变柔化 */
  --w-sh-lo: .21; --w-sh-mid: .31; --w-sh-ao: .44;   /* 光照层三档阴影透明度(C 比 A 各浅一档) */
  --w-spec: #2F3B4E;         /* 镜框:墨青(与站点主色 #1a365d 同族) */
  --w-spec-w: .95px;         /* 镜框线宽(08-22 减薄:1.3 → .95;判据 = 不压眼睛轮廓、高光完整) */
  --w-lens: rgba(120, 150, 190, .13);
  --w-hl: #FFFFFF;
  --w-leaf: #8FC07E; --w-leaf-line: #5F8A55; --w-stem: #6F9B63;
  --w-cloth-a: #9DB3C6;      /* 衣饰主色(按装扮覆盖) */
  --w-cloth-b: #F7F3EC;      /* 衣饰辅色(条纹/结/纽扣/罗纹) */
  --w-collar: #FBFAF6;
  --w-gold: #E3B45C;
  --w-soil: #BFA27F; --w-soil-dark: #9C7F5E; --w-soil-top: #D2B896;
  --w-board: #6E5D52; --w-wheel: #3A302C;
  --w-book: #4E5D7A; --w-page: #FBF8F1;   /* 书:藏青封面 + 米白页 */
  --w-prop: #F7F3EC; --w-prop-line: var(--w-line);
  --gx: 0px; --gy: 0px;      /* 视线 */
  --lid: 0px;                /* 上眼皮下垂量(0 开,6.2 闭) */
  --lid-skew: 0deg;          /* 上眼皮斜切(正 = 右侧压低);两眼各自取值即得镜像的斜眼 */
  --lowlid: 0px;
  --brow-o: 0; --brow-y: 0px; --brow-r: 0deg;
  --sh-r: 0deg; --el-r: 0deg; --sh-l: 0deg; --el-l: 0deg; --sh-y: 0px; --sh-yl: 0px; /* 肩/肘角 + 肩上提(左右各自) */
  --wr-r: 0deg; --wr-l: 0deg;  /* 腕角(§14.3.1):挥手时反向跟随前臂,甩出一下手腕 */
  --arm-ms: 480ms; --arm-lag: 45ms; --arm-ease: cubic-bezier(.3, 1.12, .4, 1); --arm-ease-el: cubic-bezier(.3, 1.3, .4, 1); /* 关节过渡:肘晚 45ms、过冲更大 */
  --carry-ms: 520ms;
  --hair-r: 0deg; --hair-s: 1; --hair-y: 0px;   /* --hair-y(§14.4.1):戴帽时把芽整体抬出帽顶,各帽一个值 */
  --breath-amp: .034; --breath-dur: 4.6s;
  --amp: 6deg; --dur: 600ms; --think-dur: 2.6s;
}
/* A 焦糖杏仁(第四轮推荐;用户 08-22:过于浓重,留作对照) */
.figure.pal-a, .w4-palette.pal-a {
  --w-shell: #F1DCC0; --w-line: #8A6247; --w-shade: #C99A6E; --w-groove: #B98A5E; --w-face: #3B2A22; --w-iris: #5E4437; --w-blush: #F09A98;
  --w-sh-lo: .24; --w-sh-mid: .36; --w-sh-ao: .5;
}
/* B 燕麦奶(冷一档;用户 08-22:过于素,留作对照) */
.figure.pal-b, .w4-palette.pal-b {
  --w-shell: #EFE2CF; --w-line: #7B6350; --w-shade: #B9A083; --w-groove: #AD9474; --w-face: #36302C; --w-iris: #574840; --w-blush: #EBA3A3;
  --w-sh-lo: .22; --w-sh-mid: .33; --w-sh-ao: .46;
}

@property --lid { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --lowlid { syntax: '<length>'; inherits: true; initial-value: 0px; }
@property --hair-r { syntax: '<angle>'; inherits: true; initial-value: 0deg; }
@property --hair-s { syntax: '<number>'; inherits: true; initial-value: 1; }

/* ---------- 基础绘制 ---------- */
.figure { display: block; overflow: visible; transition: --hair-r 600ms ease-out, --hair-s 600ms ease-out; }
.figure .body { fill: var(--w-shell); }
.figure .outline { fill: none; stroke: var(--w-line); stroke-width: 1.3; stroke-linejoin: round; }
/* 光照层:渐变 stop 颜色走变量,换色板整层跟着变 */
.figure .s-clear { stop-color: var(--w-shade); stop-opacity: 0; }
.figure .s-shade-lo { stop-color: var(--w-shade); stop-opacity: var(--w-sh-lo); }
.figure .s-shade-mid { stop-color: var(--w-shade); stop-opacity: var(--w-sh-mid); }
.figure .s-light-hi { stop-color: #fff; stop-opacity: .42; }
.figure .s-light-lo { stop-color: #fff; stop-opacity: .22; }
.figure .s-light-clear { stop-color: #fff; stop-opacity: 0; }
.figure .s-ao { stop-color: var(--w-shade); stop-opacity: var(--w-sh-ao); }
.figure .s-blush { stop-color: var(--w-blush); stop-opacity: .62; }
.figure .s-blush-clear { stop-color: var(--w-blush); stop-opacity: 0; }
.figure .s-contact { stop-color: #3B2A22; stop-opacity: .2; }
.figure .contact { transition: opacity 300ms ease-out; }
/* 光照层整体压到 .35(§7.3-1):留体积,不留漆面 */
.figure .lighting { opacity: .35; }
/* 线宽分两级(§7.3-5):外轮廓 1.3、内部细节 .9 —— 手绘感来自粗细差,不来自高光 */
.figure .groove { fill: none; stroke: var(--w-groove); stroke-width: .9; stroke-linecap: round; opacity: .6; }
.figure .groove.g-short { opacity: .3; stroke-width: .9; }
.figure .tick { fill: none; stroke: var(--w-groove); stroke-width: .7; stroke-linecap: round; opacity: .38; }
.figure .blush { opacity: .5; transition: opacity 320ms ease-out; }
.figure.x-blush .blush, .figure.x-happy .blush, .figure.x-grin .blush { opacity: 1; }

/* 芽(呆毛):随呼吸轻摆;开心上扬、困倦低垂、好奇抖一下。
   §14.4.1(D-206)起**永远在**:戴帽子从帽顶长出来(--hair-y 抬一点)、戴耳机被头梁压倒(58°),
   不再 display:none —— 唯一还会藏起它的是显式的 .no-hair。 */
.figure .hair .stem { fill: none; stroke: var(--w-stem); stroke-width: 1.1; stroke-linecap: round; }
.figure .hair .leaf { fill: var(--w-leaf); stroke: var(--w-leaf-line); stroke-width: .45; stroke-linejoin: round; }
@keyframes w-hair-sway { 0%, 100% { transform: translateY(var(--hair-y)) rotate(calc(var(--hair-r) - 4deg)) scale(var(--hair-s)); } 50% { transform: translateY(var(--hair-y)) rotate(calc(var(--hair-r) + 5deg)) scale(var(--hair-s)); } }
@keyframes w-hair-wiggle { 0%, 100% { transform: translateY(var(--hair-y)) rotate(0); } 25% { transform: translateY(var(--hair-y)) rotate(-16deg); } 60% { transform: translateY(var(--hair-y)) rotate(12deg); } }
.figure .hair { animation: w-hair-sway 3.8s ease-in-out infinite; }
/* 芽的命中面(§8.6):只吃指针,不画东西 */
.figure .part-hit { fill: transparent; stroke: none; }
.figure.x-happy, .figure.x-grin, .figure.x-wink { --hair-r: -14deg; --hair-s: 1.12; }
.figure.x-sleepy { --hair-r: 18deg; --hair-s: .9; }
.figure.x-think { --hair-r: 8deg; }
.figure.x-curious .hair { animation: w-hair-wiggle 700ms ease-in-out; }
.figure.no-hair .hair { display: none; }

/* ---------- 芽 × 帽子 / 耳机(§14.4.1 / §14.4.2 · D-206) ----------
 *
 * 几何事实:头顶 (24, 6.6)、芽根 (25.6, 7)、叶顶 y≈2.3;
 * 帽顶 cap 5.85 / bucket 4.0 / gradcap 板顶 4.0;耳机头梁覆盖 y 6.8–8.7 —— 正压在芽根上。
 * 所以帽子只要把芽抬一点点就露得出来,耳机则必须把它压倒。
 * g.hair 在 DOM 里画在 hats() 之后,z 序本来就在帽子之上(watson-figure.js:448-450)。
 *
 * 静止位姿(§14.4.1 表):帽类走 --hair-y(摆动照旧),耳机换一条不摆的呼吸。 */
.figure.o-cap-on { --hair-y: -0.4px; }
.figure.o-gradcap-on { --hair-y: -0.6px; }
.figure.o-bucket-on { --hair-y: -1.4px; }   /* bucket 顶最高,抬多一点让叶子露 ≥3 单位 */

/* 耳机:被头梁压倒、贴着头顶向右平躺,叶尖从头梁右侧探出。不摆动,只极慢地呼吸。 */
@keyframes w-hair-pressed { 0%, 100% { transform: rotate(58deg) scale(.92); } 50% { transform: rotate(61deg) scale(.92); } }
.figure.o-phones-on:not(.hair-don):not(.hair-doff) .hair { animation: w-hair-pressed 4s ease-in-out infinite; }

/* 戴上(接在 donning 的同一条时间线上,类由引擎在 donning 那一拍加):
 * 耳机 —— 头梁落到头顶(w-don-hat 的 70% ≈ 390ms)时才开始被压,过冲到 66° 再回落 58°。
 * 帽子 —— 先被压倒 58°,帽子落定后从帽顶顶出来,回到直立 + 该帽的 --hair-y,一次过冲 .3px。 */
@keyframes w-hair-don-phones {
  0%, 64% { transform: rotate(0deg) scale(1); }
  87% { transform: rotate(66deg) scale(.9); }
  100% { transform: rotate(58deg) scale(.92); }
}
@keyframes w-hair-don-hat {
  0%, 43.8% { transform: translateY(0) rotate(0deg) scale(1); }
  64% { transform: translateY(0) rotate(58deg) scale(.94); }
  88% { transform: translateY(calc(var(--hair-y) - .3px)) rotate(-4deg) scale(1.02); }
  100% { transform: translateY(var(--hair-y)) rotate(0deg) scale(1); }
}
.figure.o-phones-on.hair-don .hair { animation: w-hair-don-phones 610ms cubic-bezier(.3, .9, .4, 1.1) forwards; }
.figure.o-cap-on.hair-don .hair, .figure.o-bucket-on.hair-don .hair, .figure.o-gradcap-on.hair-don .hair {
  animation: w-hair-don-hat 890ms cubic-bezier(.3, .8, .3, 1) forwards;
}

/* 摘下:耳机一抬,芽两次过冲弹回来并把叶子「展开」;帽子只是随帽抬回原位,落地小抖一下。 */
@keyframes w-hair-doff-phones {
  0% { transform: rotate(58deg) scale(.92); }
  42% { transform: rotate(-12deg) scale(1.06); }
  72% { transform: rotate(5deg) scale(1.01); }
  100% { transform: rotate(0deg) scale(1); }
}
@keyframes w-hair-doff-hat {
  0% { transform: translateY(var(--hair-y)) rotate(0deg); }
  53% { transform: translateY(0) rotate(0deg); }
  76% { transform: translateY(0) rotate(-7deg); }
  100% { transform: translateY(0) rotate(0deg); }
}
.figure.o-phones-on.hair-doff .hair { animation: w-hair-doff-phones 520ms cubic-bezier(.3, .9, .4, 1.15) forwards; }
.figure.o-cap-on.hair-doff .hair, .figure.o-bucket-on.hair-doff .hair, .figure.o-gradcap-on.hair-doff .hair {
  animation: w-hair-doff-hat 380ms ease-out forwards;
}

/* 减动偏好:位姿瞬切,无过渡(§14.4.2 末条)。芽的位姿本来全写在关键帧里,而减动那条总闸
   把 .figure 里的动画一律关掉 —— 不补这两行,戴耳机时芽会直挺挺地穿过头梁。 */
@media (prefers-reduced-motion: reduce) {
  .figure .hair { transform: translateY(var(--hair-y)); }
  .figure.o-phones-on .hair { transform: rotate(58deg) scale(.92); }
}
.figure.rm .hair { transform: translateY(var(--hair-y)); }
.figure.rm.o-phones-on .hair { transform: rotate(58deg) scale(.92); }

/* 「顶出来」单独一拍:摘帽致意(§14.4.3)时芽跟着弹一次,复用戴帽那一段的后半程 */
@keyframes w-hair-pop {
  0% { transform: translateY(0) rotate(58deg) scale(.94); }
  62% { transform: translateY(calc(var(--hair-y) - .3px)) rotate(-4deg) scale(1.02); }
  100% { transform: translateY(var(--hair-y)) rotate(0deg) scale(1); }
}
.figure.hair-pop .hair { animation: w-hair-pop 320ms cubic-bezier(.3, .8, .3, 1); }

/* 头发(§8.1):四根短发丝(每侧两根、线宽 1.0、贴着头顶轮廓向外向下)+ 两侧贴头短发。
   哑光、只用轮廓棕,不参与 blink / x-* 的任何变化。
   帽子画在它下面,所以戴帽时整层跟着藏,不然发丝会盖在帽冠上。 */
.figure .hairline .hs { fill: none; stroke: var(--w-line); stroke-opacity: .75; stroke-width: 1; stroke-linecap: round; }
.figure .hairline .htuft { fill: var(--w-line); fill-opacity: .6; stroke: none; }
.figure.no-hair .hairline, .figure.o-bucket-on .hairline, .figure.o-cap-on .hairline,
.figure.o-gradcap-on .hairline, .figure.o-phones-on .hairline { display: none; }

/* 眼睛:眼白层(深)+虹膜+瞳孔+双高光;眼皮(壳色,裁在体内)从上方落下;笑眼为弧 */
.figure .eye { transition: transform 300ms ease-out; }
.figure .gaze { transform: translate(var(--gx), var(--gy)); transition: transform 260ms ease-out; }
.figure .sclera { fill: var(--w-face); }
.figure .iris { fill: var(--w-iris); }
.figure .pupil { fill: var(--w-pupil); }
.figure .hl { fill: var(--w-hl); opacity: .75; transform-box: fill-box; transform-origin: center; transition: transform 260ms ease-out; }
.figure.near .hl1 { transform: scale(1.4); }
.figure .lid, .figure .lowlid { fill: var(--w-shell); }
/* 上眼皮:下垂 + 可选斜切。斜切用 skewY 而不是 rotate —— rotate 会把眼皮的侧边一起转,
   眼皮的右(或左)缘缩进眼圈里,内眼角留一道没盖住的缺口;skewY 只斜下缘,两侧仍旧竖直,盖得干净。
   支点取眼皮自身盒中心(fill-box),两眼给相反角度就是镜像。 */
.figure .lid, .figure .lidline { transform: translateY(var(--lid)) skewY(var(--lid-skew)); transform-box: fill-box; transform-origin: center; transition: transform 170ms ease-out; }
/* 闭眼线(§8.3):眼皮是壳色的,全闭时镜片里空空的一片 —— 这道「︶」跟着眼皮落下来,睡着就读成两只闭眼。
   开合判据不写在 CSS 里(容器查询判自定义属性还不能用):引擎每次写 --lid 时同步切 .eyes-shut,阈值 4px。 */
.figure .lidline { fill: none; stroke: var(--w-line); stroke-width: 1; stroke-linecap: round; opacity: 0; transition: opacity 170ms ease-out, transform 170ms ease-out; }
.figure.eyes-shut .lidline { opacity: 1; }
.figure .lowlid { transform: translateY(calc(-1 * var(--lowlid))); transition: transform 170ms ease-out; }
.figure .arc { fill: none; stroke: var(--w-face); stroke-width: 1.8; stroke-linecap: round; display: none; }
.figure .brow { fill: none; stroke: var(--w-face); stroke-width: 1.05; stroke-linecap: round;
  opacity: var(--brow-o); transform: translateY(var(--brow-y)) rotate(var(--brow-r));
  transition: opacity 220ms ease-out, transform 260ms ease-out; }
.figure .eye-r .brow { transform: translateY(var(--brow-y)) rotate(calc(-1 * var(--brow-r))); }

/* 眨眼:只定义中间关键帧,起止取当前值(半闭眼状态下眨眼不跳) */
@keyframes w-blink { 38% { --lid: 6.4px; } 52% { --lid: 6.4px; } }
@keyframes w-blink-slow { 30% { --lid: 6.4px; } 62% { --lid: 6.4px; } }
.figure.blink .eyes { animation: w-blink 260ms ease-in-out; }
.figure.blink-slow .eyes { animation: w-blink-slow 620ms ease-in-out; }
.figure.blink-l .eye-l { animation: w-blink 260ms ease-in-out; }

/* 表情 */
.figure .m { fill: none; stroke: var(--w-face); stroke-width: 1.7; stroke-linecap: round; display: none; }
.figure .m-smile { display: inline; }
.figure .m-grin { fill: var(--w-face); stroke-width: 1.2; stroke-linejoin: round; }
.figure.x-happy .m, .figure.x-soft .m, .figure.x-focus .m, .figure.x-curious .m, .figure.x-think .m, .figure.x-grin .m { display: none; }
.figure.x-happy .m-big, .figure.x-soft .m-smile, .figure.x-focus .m-flat, .figure.x-curious .m-o, .figure.x-think .m-hmm, .figure.x-grin .m-grin { display: inline; }
.figure.x-happy .eyeball, .figure.x-happy .lid, .figure.x-happy .lidline, .figure.x-happy .lowlid,
.figure.x-soft .eyeball, .figure.x-soft .lid, .figure.x-soft .lidline, .figure.x-soft .lowlid,
.figure.x-grin .eyeball, .figure.x-grin .lid, .figure.x-grin .lidline, .figure.x-grin .lowlid { display: none; }
.figure.x-happy .arc-happy, .figure.x-grin .arc-happy { display: inline; }
.figure.x-soft .arc-soft { display: inline; }
/* 思考/专注(第六轮):眼球全露,不压上眼皮(压了像犯困)。思考 = 瞳孔上望(引擎 driftLoop)+ 一边挑眉 + 「嗯」嘴 + 瞳孔微缩(望远);
   专注 = 眉毛压低向内 + 下眼睑微提(用力的眼),眼皮只留给眨眼/困/哈欠 */
.figure.x-focus { --lowlid: .6px; --brow-o: .9; --brow-y: .5px; --brow-r: 8deg; }
.figure.x-think { --brow-o: .85; --brow-y: -.4px; --brow-r: -6deg; }
.figure.x-think .eye-r { --brow-r: 13deg; --brow-y: -1.5px; }   /* 一边挑眉 */
.figure .pupil { transform-box: fill-box; transform-origin: center; transition: transform 260ms ease-out; }
.figure.x-think .pupil { transform: scale(.88); }
.figure.x-curious .eye { transform: scale(1.15); }
.figure.x-curious { --brow-o: .85; --brow-y: -1.2px; }
.figure.x-wink .eye-r .eyeball, .figure.x-wink .eye-r .lid, .figure.x-wink .eye-r .lidline, .figure.x-wink .eye-r .lowlid { display: none; }
.figure.x-wink .eye-r .arc-happy { display: inline; }
.figure.x-sleepy { --lid: 3.5px; --lowlid: .9px; }
/* §3.26.3 两张脸,§3.35-1 重做(旧版只动眉毛和嘴的一两笔,1× 下与中性脸分不出来)。
   判据 = 在形象实际显示尺寸(88px 宽)的截图上不看文字也能分辨。仍然不加部件:只用既有的眼皮/瞳孔/虹膜/嘴/腮红。

   装生气:上眼皮斜切成外高内低(两眼镜像) + 瞳孔往中下瞪 + 短的下弯嘴 + 腮红深一档。
   眼皮基准 2.2px、斜切 17.5°,算下来内眼角遮住约 55%、外眼角约 24%(眼白直径 6.1,眼皮下缘中点在 cy-2.8) */
.figure.x-cross { --lid: 2.2px; --lid-skew: 17.5deg; --lowlid: .8px;
  --brow-o: 1; --brow-y: 1.4px; --brow-r: 19deg;
  --w-blush: #E4837F; }                                  /* 腮红深一档:鼓着气的红,不是害羞 */
.figure.x-cross .eye-r .lid { --lid-skew: -17.5deg; }    /* 右眼镜像:内眼角在左侧 */
.figure.x-cross .eye-l .pupil { transform: translate(.5px, .4px); }
.figure.x-cross .eye-r .pupil { transform: translate(-.5px, .4px); }
.figure.x-cross .m { display: none; }
/* 下弯嘴借既有的笑嘴翻过来用(不加新路径):宽 3.84、弧高 .99 ≈ 嘴宽 26% */
.figure.x-cross .m-smile { display: inline; transform: scale(.8, -.9); transform-box: fill-box; transform-origin: center; }
.figure.x-cross .blush { opacity: 1; }

/* 晕:瞳孔缩到 60% 且两眼错位(左瞳外上、右瞳内下,各约眼径 15%) + 上眼皮遮约三成 + 嘴加宽的小波浪 + 只剩一枚高光。
   虹膜跟着瞳孔一起挪(不缩),否则 88px 下只有那颗 2.4px 的瞳孔在动,错位读不出来;配引擎既有的 ±8° 衰减摇摆。
   眼皮两只**同向**斜(生气是两只对称地斜):同向 = 整张脸歪了,对称 = 眉心聚起来的怒;
   再让左眼比右眼多垂 1px —— 两眼开合不一样大是 88px 下最跑得出来的「站不稳」信号,平均遮盖仍是三成上下。
   多垂的量写成 calc(var(--lid) + n),不是另设 --lid:眨眼的关键帧写在 .eyes 上,per-eye 覆盖 --lid 会让那只眼睛不再眨。 */
.figure.x-dizzy { --lid: 1.6px; --lowlid: .7px; --brow-o: .85; --brow-y: -1.5px; --brow-r: -12deg; }
.figure.x-dizzy .eye-l .lid { transform: translateY(calc(var(--lid) + 1px)) skewY(-13deg); }
.figure.x-dizzy .eye-r .lid { transform: translateY(calc(var(--lid) - .5px)) skewY(-13deg); }
.figure.x-dizzy .eye-l .iris, .figure.x-dizzy .eye-l .hl1 { transform: translate(-.9px, -.9px); }
.figure.x-dizzy .eye-r .iris, .figure.x-dizzy .eye-r .hl1 { transform: translate(-.9px, .9px); }
.figure.x-dizzy .eye-l .pupil { transform: translate(-.9px, -.9px) scale(.6); }
.figure.x-dizzy .eye-r .pupil { transform: translate(-.9px, .9px) scale(.6); }
.figure.x-dizzy .m { display: none; }
/* 嘴:既有的「嗯…」曲线加宽到 120%,纵向不动 —— scaleY 会把描边一起拉粗,1× 下糊成一坨,读成张着的嘴 */
.figure.x-dizzy .m-hmm { display: inline; transform: scaleX(1.2); transform-box: fill-box; transform-origin: center; }

/* 呼吸(底部锚定) */
@keyframes w-breath {
  0%, 100% { transform: scale(1, 1); }
  50% { transform: scale(calc(1 + var(--breath-amp) * .38), calc(1 + var(--breath-amp))); }
}
.figure .breath { animation: w-breath var(--breath-dur) ease-in-out infinite; }
.figure.lively { --breath-amp: .048; --breath-dur: 3.4s; }

/* ---------- 眼镜:活道具(第七轮:真·手持——节点挂进手心 .carry,位姿由引擎算;以下 g-* 栖位矩阵与引擎 SPOTS 表同值) ---------- */
.figure .specs { transition: transform 600ms cubic-bezier(.3, .9, .3, 1), opacity 260ms ease-out; }
/* 手里的眼镜(第六轮):节点被引擎挪进手心的 .carry,位姿 = 引擎算出的手心坐标系内联 transform;抓起/松手那一帧 .snap 无过渡,其余随 --carry-ms 过渡(握姿微调) */
.figure .specs.carried { transition: transform var(--carry-ms) cubic-bezier(.3, .9, .3, 1), opacity 260ms ease-out; }
.figure .specs.carried.snap { transition: none; }
.figure .carry { transform-origin: 38.18px 38.2px; }
/* 手持配套:换框时手里淡出(.fading)/ 收起来·拿出来时在手里淡出·淡入(.stowing,配合引擎绕手心缩放) */
.figure .specs.fading, .figure .specs.stowing { opacity: 0; }
.figure .rim { fill: none; stroke: var(--w-spec); stroke-width: var(--w-spec-w); display: none; }
.figure .lens { fill: var(--w-lens); display: none; }
.figure .lens-reflect { fill: #fff; opacity: .26; }
.figure .temple path { fill: none; stroke: var(--w-spec); stroke-width: var(--w-spec-w); stroke-linecap: round; }
.figure .temple .hinge { fill: var(--w-spec); r: .45; }
.figure .temple { transition: transform 320ms ease-in-out; }
.figure .bridge { fill: none; stroke: var(--w-spec); stroke-width: var(--w-spec-w); stroke-linecap: round; }
.figure .pad { fill: none; stroke: var(--w-spec); stroke-width: .5; stroke-linecap: round; opacity: .5; }
.figure .lens-hl, .figure .lens-hl2 { fill: none; stroke: var(--w-hl); stroke-width: .85; stroke-linecap: round; opacity: .9; transition: opacity 260ms ease-out; }
.figure .lens-hl2 { stroke-width: .6; opacity: .6; }
.figure .sweep { stroke: var(--w-hl); stroke-width: .9; stroke-linecap: round; opacity: 0; }
/* 镜框八款:圆/椭圆/方圆 × 墨青/金丝/玳瑁 + 眉线框 + 无框(08-22 整体减薄:线宽只占镜圈与眼白间隙的三分之一,眼睛轮廓与双高光完整) */
.figure.f-round .rim-round, .figure.f-round .lens-round,
.figure.f-gold .rim-round, .figure.f-gold .lens-round,
.figure.f-tort .rim-round, .figure.f-tort .lens-round { display: inline; }
.figure.f-oval .rim-oval, .figure.f-oval .lens-oval { display: inline; }
.figure.f-sq .rim-sq, .figure.f-sq .lens-sq,
.figure.f-sqtort .rim-sq, .figure.f-sqtort .lens-sq { display: inline; }
.figure.f-brow .rim-brow, .figure.f-brow .rim-thin, .figure.f-brow .lens-round { display: inline; }
.figure.f-brow .rim-brow { stroke-width: 1.45; }
.figure.f-brow .rim-thin { stroke-width: .5; }
.figure.f-rimless .rim-thin, .figure.f-rimless .lens-round { display: inline; }
.figure.f-rimless .rim-thin { stroke-width: .5; opacity: .5; }
.figure.f-rimless { --w-lens: rgba(120, 150, 190, .2); }
.figure.f-gold { --w-spec: #B4925C; --w-spec-w: .8px; }
.figure.f-tort, .figure.f-sqtort { --w-spec: #6F4C31; --w-spec-w: 1.1px; }
.figure.f-oval .lens-hl { stroke-width: .8; }
/* 栖位:戴着 / 手里(短暂)/ 挂领口 / 收着(隐藏;用户 08-23:不用时收纳,不拿着、不放地上)/ 咬镜腿(短暂) */
.figure.g-hand .specs { transform: translate(2.4px, 10.8px) rotate(-22deg) scale(.92); }
.figure.g-hand .temple-l { transform: rotate(-118deg); }
.figure.g-hand .temple-r { transform: rotate(118deg); }
.figure.g-stow .specs { display: none; }
.figure.g-stow .temple-l { transform: rotate(-118deg); }
.figure.g-stow .temple-r { transform: rotate(118deg); }
.figure.g-collar .specs { transform: translate(.6px, 12px) rotate(7deg) scale(.72); }
.figure.g-collar .temple-l { transform: rotate(-118deg); }
.figure.g-collar .temple-r { transform: rotate(118deg); }
.figure.g-away .specs { transform: translate(-6px, 26px) rotate(-40deg) scale(.6); opacity: 0; }
.figure.g-none .specs { display: none; }
/* 眼镜单发动作(作用在 .specs 上,只在 g-on 时用) */
@keyframes w-spec-push { 0% { transform: translateY(0); } 28% { transform: translateY(1.3px); } 50% { transform: translateY(1.3px); } 74% { transform: translateY(-.8px); } 100% { transform: translateY(0); } }
@keyframes w-spec-slip { 0% { transform: translateY(0) rotate(0); } 35% { transform: translateY(1.6px) rotate(3deg); } 62% { transform: translateY(1.6px) rotate(3deg); } 82% { transform: translateY(-.6px) rotate(0); } 100% { transform: translateY(0) rotate(0); } }
@keyframes w-spec-bob { 0% { transform: translateY(0); } 50% { transform: translateY(1.1px); } 100% { transform: translateY(0); } }
@keyframes w-spec-tilt { 0% { transform: rotate(0); } 40% { transform: rotate(-5deg); } 100% { transform: rotate(0); } }
.figure.spec-push .specs { animation: w-spec-push var(--dur) ease-in-out; }
.figure.spec-slip .specs { animation: w-spec-slip var(--dur) ease-in-out; }
.figure.spec-bob .specs { animation: w-spec-bob 400ms ease-in-out; }
.figure.spec-tilt .specs { animation: w-spec-tilt 700ms ease-in-out; }
@keyframes w-glint { 0%, 100% { opacity: .9; } 45% { opacity: .2; } }
@keyframes w-flash { 0%, 100% { opacity: .9; } 40% { opacity: 1; stroke-width: 1.7; } }
@keyframes w-sweep { 0% { opacity: 0; transform: translateX(-4px); } 25% { opacity: .95; } 100% { opacity: 0; transform: translateX(7px); } }
.figure.glint .lens-hl { animation: w-glint 760ms ease-in-out; }
.figure.flash .lens-hl { animation: w-flash 680ms ease-in-out; }
.figure.sweep .sweep { animation: w-sweep 520ms ease-out; }
/* 擦镜:眼镜在右手里,左手在镜片上来回(手臂由引擎走路径);镜框随手腕小幅转 */
@keyframes w-wipe { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(-9deg); } 75% { transform: rotate(9deg); } }
.figure.wiping .carry { animation: w-wipe 620ms ease-in-out 2; }
/* 咬镜腿思考:眼镜在手里,镜腿尖抵住嘴角 */
.figure.g-bite .specs { transform: translate(-3.55px, 16.28px) rotate(-50deg) scale(.9); }   /* 第七轮重定:右手握 hingeR,右镜腿尖抵住嘴角,镜框斜挂在胸前(旧值把镜片压在嘴上) */
.figure.g-bite .temple-l { transform: rotate(-118deg); }
.figure.g-bite .temple-r { transform: rotate(8deg); }
/* 转眼镜(稀有 idle):眼镜绕着手心转一圈(挂在 .carry 上) */
@keyframes w-twirl { 0% { transform: rotate(0); } 100% { transform: rotate(360deg); } }
.figure.twirl .carry { animation: w-twirl 860ms cubic-bezier(.4, 0, .3, 1); }

/* ---------- 四肢(第六轮:双臂常驻的活骨架) ---------- */
/* 管状:外描边 + 内壳色;手心略大于管径,读成「有手」 */
.figure .limb-o { fill: none; stroke: var(--w-line); stroke-width: 3.7; stroke-linecap: round; }
/* 管状四肢的「轮廓」= 外描边露出的那一圈:(3.7-1.9)/2 = .9,与荚纹/衣领同一级内部线宽(§7.3-5)。外径 3.7 不动 → 尺寸不变 */
.figure .limb-i { fill: none; stroke: var(--w-shell); stroke-width: 1.9; stroke-linecap: round; }
.figure .hand { fill: var(--w-shell); stroke: var(--w-line); stroke-width: .95; }
/* 双臂永远在(只有迷你档整体不画);姿势角度全部来自引擎写入的内联变量(POSES 表 + IK),CSS 只管关节过渡:
   肘比肩晚 --arm-lag、过冲更大 → 上臂先走、前臂跟着甩到位,有「跟随」;回歇姿时引擎把 --arm-ms 放慢、换成无过冲缓动 */
.figure .arm { transition: transform var(--arm-ms) var(--arm-ease); }
.figure .arm .fore { transition: transform var(--arm-ms) var(--arm-ease-el) var(--arm-lag); }
.figure .arm-r { transform: translateY(var(--sh-y)) rotate(var(--sh-r)); }
.figure .arm-r .fore { transform: rotate(var(--el-r)); }
.figure .arm-l { transform: translateY(var(--sh-yl)) rotate(var(--sh-l)); }
.figure .arm-l .fore { transform: rotate(var(--el-l)); }
/* 腕关节(§14.3.1):原先 .hand-g 在 CSS 里没有任何规则,手是死的 —— 前臂摆到哪它就到哪。
   现在腕自己有一个角与一条 240ms 过渡,挥手时反向跟随前臂(见 w-wrist-follow),读出来才是「甩手」不是「转木棍」。
   手心那枚圆另给 180ms,「张手」时放大 1.12 用。 */
.figure .hand-g { transition: transform 240ms var(--arm-ease-el); }
.figure .arm-r .hand-g { transform: rotate(var(--wr-r)); }
.figure .arm-l .hand-g { transform: rotate(var(--wr-l)); }
.figure .arm .hand { transform-box: fill-box; transform-origin: center; transition: transform 180ms var(--arm-ease-el); }
.figure.no-arm-tr .arm, .figure.no-arm-tr .arm .fore, .figure.no-arm-tr .hand-g { transition: none; }
.figure .prop { display: none; }
.figure .prop path, .figure .prop rect { fill: var(--w-prop); stroke: var(--w-prop-line); stroke-width: 1.05; stroke-linejoin: round; stroke-linecap: round; }
.figure .prop .steam, .figure .prop .cup-handle, .figure .prop .box-handle, .figure .prop .box-line, .figure .prop .pencil-body, .figure .prop .pencil-tip { fill: none; }
.figure .prop .steam { opacity: .7; stroke-width: .9; }
.figure .prop .pencil-body { stroke-width: 1.6; stroke: var(--w-gold); }
.figure .prop .pencil-tip { stroke-width: 1.2; }
.figure.prop-cup .prop-cup, .figure.prop-box .prop-box, .figure.prop-pencil .prop-pencil, .figure.prop-spade .prop-spade { display: inline; }
/* 小铲(手里的与插在土里的同一画法):木柄 + 深色铲头 */
.figure .spade-handle, .figure .prop .spade-handle { fill: none; stroke: var(--w-board); stroke-width: 1.25; stroke-linecap: round; }
.figure .spade-grip, .figure .prop .spade-grip { fill: none; stroke: var(--w-board); stroke-width: 1.15; stroke-linecap: round; }
.figure .spade-blade, .figure .prop .spade-blade { fill: none; stroke: var(--w-wheel); stroke-width: 2.7; stroke-linecap: round; }
/* 手势附加动作:姿势本身在引擎 POSES 表里;这里只是「在姿势之上再动一动」的关键帧,基准角取内联变量,首尾帧与基准一致故不跳 */
/* 挥手(§14.3.2):旧版 ±25° 一摆 340ms 太硬,读成「甩胳膊」。现在幅度收成 −16/+20、每摆 380ms,
   并让手腕反向跟随、晚 60ms —— 这条才是「打招呼」与「摇摆」的分界。 */
@keyframes w-wave-soft { 0%, 100% { transform: rotate(calc(var(--el-r) - 16deg)); } 50% { transform: rotate(calc(var(--el-r) + 20deg)); } }
@keyframes w-wave-soft-l { 0%, 100% { transform: rotate(calc(var(--el-l) - 16deg)); } 50% { transform: rotate(calc(var(--el-l) + 20deg)); } }
@keyframes w-wrist-follow { 0%, 100% { transform: rotate(calc(var(--wr-r) + 10deg)); } 50% { transform: rotate(calc(var(--wr-r) - 12deg)); } }
@keyframes w-wrist-follow-l { 0%, 100% { transform: rotate(calc(var(--wr-l) + 10deg)); } 50% { transform: rotate(calc(var(--wr-l) - 12deg)); } }
.figure.a-wave .arm-r .fore { animation: w-wave-soft 380ms ease-in-out 2; }
.figure.a-wave .arm-l .fore { animation: w-wave-soft-l 380ms ease-in-out 2; }
.figure.a-wave .arm-r .hand-g { animation: w-wrist-follow 380ms ease-in-out 60ms 2; }
.figure.a-wave .arm-l .hand-g { animation: w-wrist-follow-l 380ms ease-in-out 60ms 2; }
.figure.a-cheer .arm .fore { animation: w-cheer 420ms ease-in-out 2; }
@keyframes w-cheer { 0%, 100% { transform: rotate(calc(var(--el-r) - 8deg)); } 50% { transform: rotate(calc(var(--el-r) + 11deg)); } }
.figure.a-cheer .arm-l .fore { animation-name: w-cheer-l; }
@keyframes w-cheer-l { 0%, 100% { transform: rotate(calc(var(--el-l) - 8deg)); } 50% { transform: rotate(calc(var(--el-l) + 11deg)); } }
.figure.a-scratch .arm-r .fore { animation: w-scratch 260ms ease-in-out 4; }
@keyframes w-scratch { 0%, 100% { transform: rotate(calc(var(--el-r) - 7deg)); } 50% { transform: rotate(calc(var(--el-r) + 7deg)); } }
.figure.a-rub .arm-r .fore { animation: w-rub 300ms ease-in-out 3; }
@keyframes w-rub { 0%, 100% { transform: rotate(calc(var(--el-r) - 6deg)) translateX(0); } 50% { transform: rotate(calc(var(--el-r) + 6deg)) translateX(-.5px); } }
.figure.a-clap .arm .fore { animation: w-clap 280ms ease-in-out 3; }
@keyframes w-clap { 0%, 100% { transform: rotate(calc(var(--el-r) + 14deg)); } 50% { transform: rotate(calc(var(--el-r) - 5deg)); } }
.figure.a-clap .arm-l .fore { animation-name: w-clap-l; }
/* 欢呼 / 鼓掌也带腕跟随(§14.3.2 末条),幅度已各减 20% */
.figure.a-cheer .arm-r .hand-g { animation: w-wrist-follow 420ms ease-in-out 60ms 2; }
.figure.a-cheer .arm-l .hand-g { animation: w-wrist-follow-l 420ms ease-in-out 60ms 2; }
.figure.a-clap .arm-r .hand-g { animation: w-wrist-follow 280ms ease-in-out 60ms 3; }
.figure.a-clap .arm-l .hand-g { animation: w-wrist-follow-l 280ms ease-in-out 60ms 3; }
@keyframes w-clap-l { 0%, 100% { transform: rotate(calc(var(--el-l) + 14deg)); } 50% { transform: rotate(calc(var(--el-l) - 5deg)); } }
.figure.a-brush .arm-r .fore { animation: w-brush 320ms ease-in-out 2; }
@keyframes w-brush { 0%, 100% { transform: rotate(calc(var(--el-r) - 10deg)); } 50% { transform: rotate(calc(var(--el-r) + 14deg)); } }
.figure.a-adjust-r .arm-r .fore { animation: w-adjust 340ms ease-in-out 2; }
@keyframes w-adjust { 0%, 100% { transform: rotate(calc(var(--el-r) - 8deg)); } 50% { transform: rotate(calc(var(--el-r) + 8deg)); } }
.figure.a-tug .arm-r .fore { animation: w-adjust 320ms ease-in-out 2; }
.figure.a-pat .arm-r .fore, .figure.a-fidget .arm-r .fore { animation: w-adjust 340ms ease-in-out 2; }
/* 跑:双臂屈肘前后摆(与腿同频 230ms,左右反相) */
.figure.a-run .arm-r .fore { animation: w-run-arm 230ms linear infinite; }
.figure.a-run .arm-l .fore { animation: w-run-arm-l 230ms linear infinite; }
@keyframes w-run-arm { 0%, 100% { transform: rotate(calc(var(--el-r) - 26deg)); } 50% { transform: rotate(calc(var(--el-r) + 26deg)); } }
@keyframes w-run-arm-l { 0%, 100% { transform: rotate(calc(var(--el-l) + 26deg)); } 50% { transform: rotate(calc(var(--el-l) - 26deg)); } }
/* 刨土:前臂带铲一下一上(向下刨时铲尖到地面线,向上甩时飞土);身体随之蹲一下、向前压 */
@keyframes w-dig { 0%, 100% { transform: rotate(calc(var(--el-r) + 28deg)); } 42% { transform: rotate(calc(var(--el-r) - 70deg)); } 58% { transform: rotate(calc(var(--el-r) - 62deg)); } }
.figure.digging .arm-r .fore { animation: w-dig 440ms cubic-bezier(.4, 0, .5, 1) 3; }
@keyframes w-dig-bob { 0%, 100% { transform: translateY(0) rotate(0); } 22% { transform: translateY(1.6px) rotate(7deg); } 50% { transform: translateY(-.7px) rotate(-2deg); } }
.figure.digging .act { animation: w-dig-bob 440ms ease-in-out 3; }
/* 姿势角度全部由引擎 POSES 表 + IK 写入内联变量(第七轮起 CSS 不再保存任何手势角度) */
/* 没有手势时的默认歇姿(双臂常驻) */
.figure { --sh-r: 8deg; --el-r: -4deg; --sh-l: 8deg; --el-l: -4deg; }

/* ---------- 部件级反应(§8.6:摸芽 / 摸手 / 摸眼镜) ----------
   指针命中在 watson-panel.css 里逐个开;这里只放「被摸到之后动什么」。
   三条都写在既有部件上,不新增几何:芽借 --hair-r 摆一下、手借挥手关键帧、镜片借那道高光。 */
@keyframes w-hair-nudge {
  0%, 100% { transform: rotate(var(--hair-r)) scale(var(--hair-s)); }
  30% { transform: rotate(calc(var(--hair-r) - 10deg)) scale(var(--hair-s)); }
  70% { transform: rotate(calc(var(--hair-r) + 10deg)) scale(var(--hair-s)); }
}
.figure.part-sprout .hair { animation: w-hair-nudge 500ms ease-in-out; }
.figure.part-glint .lens-hl { animation: w-glint 240ms ease-in-out; }

/* ---------- 配饰可摸(§14.4.3 · D-206) ----------
   手臂那半边(a-tug / a-pat / a-collar / a-bag-l / a-hat-r)与身体那半边(act-sway / act-straighten)
   都是既有姿势与动作,由 watson-launcher.js 调引擎;这里只放配饰自己那一下。
   每条 ≤ 1000ms、不位移整体、不开面板。 */

/* 耳机:悬停轻点一下头梁;点击「听歌」时眼半闭(--lid 由类给,不写 setVar 免得要手动还原) */
@keyframes w-phones-tap { 0%, 100% { transform: translateY(0); } 45% { transform: translateY(-.4px); } }
.figure.part-phones .o-phones .band { animation: w-phones-tap 200ms ease-in-out; }
.figure.part-listen { --lid: 2.4px; }

/* 帽子:抬帽致意(260ms 抬起 → 220ms 回),origin 与戴帽同一点 */
@keyframes w-hat-lift {
  0% { transform: none; }
  54% { transform: translateY(-3px) rotate(-8deg); }
  100% { transform: none; }
}
.figure.part-hat-lift .o-bucket, .figure.part-hat-lift .o-cap, .figure.part-hat-lift .o-gradcap {
  transform-origin: 24px 9px; animation: w-hat-lift 480ms cubic-bezier(.3, .8, .3, 1);
}

/* 学士帽流苏:绕板顶挂点摆一下 */
@keyframes w-tassel-swing { 0%, 100% { transform: rotate(0); } 32% { transform: rotate(-18deg); } 68% { transform: rotate(18deg); } }
.figure.part-tassel .tassel-g { animation: w-tassel-swing 500ms ease-in-out; }

/* 围巾:悬停尾巴小幅摆(点击那次甩得更大,走既有 fix-scarf 的 w-tail-swing) */
@keyframes w-tail-swing-s { 0%, 100% { transform: rotate(0); } 35% { transform: rotate(-8deg); } 70% { transform: rotate(5deg); } }
.figure.part-scarf .o-scarf .tail { animation: w-tail-swing-s 400ms ease-in-out; }

/* 领带 / 领结 / 领子 / 徽章:悬停让结闪一下(复用 w-glint,但不去碰镜片高光),点击把结拧一下 */
.figure.part-knot .o-tie .knot, .figure.part-knot .o-bow .knot,
.figure.part-knot .o-collar .collar-fold, .figure.part-knot .o-pin .pin-star { animation: w-glint 240ms ease-in-out; }
@keyframes w-knot-turn { 0% { transform: rotate(0); } 34% { transform: rotate(-6deg); } 70% { transform: rotate(5deg); } 100% { transform: rotate(0); } }
.figure.part-knot-turn .o-tie, .figure.part-knot-turn .o-bow { animation: w-knot-turn 320ms ease-in-out; }
.figure.part-knot-turn .o-pin .pin-star { animation: w-glint 240ms ease-in-out 2; }

/* 包:悬停整只包绕肩带挂点摆,点击拎起来一下 */
@keyframes w-strap-swing { 0%, 100% { transform: rotate(0); } 35% { transform: rotate(-5deg); } 70% { transform: rotate(5deg); } }
.figure.part-bag .o-bag { transform-origin: 15px 27px; animation: w-strap-swing 400ms ease-in-out; }
@keyframes w-bag-lift { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1.2px); } }
.figure.part-bag-lift .o-bag .bagbody { animation: w-bag-lift 520ms ease-in-out; }
/* ---------- 手的三种反应(§14.3.2 · D-205) ----------
   悬停「张手」:该侧肩 −6°、肘 −10°、腕 +8°、手心 ×1.12,全走过渡不走关键帧(240ms 进 / 240ms 回,
   由 watson-launcher 把 --arm-ms 临时压到 240ms,回程同一条)。表情不变 —— 只是手迎上来。 */
.figure.part-open-r .arm-r { transform: translateY(var(--sh-y)) rotate(calc(var(--sh-r) - 6deg)); }
.figure.part-open-r .arm-r .fore { transform: rotate(calc(var(--el-r) - 10deg)); }
.figure.part-open-r .arm-r .hand-g { transform: rotate(calc(var(--wr-r) + 8deg)); }
.figure.part-open-r .arm-r .hand { transform: scale(1.12); }
.figure.part-open-l .arm-l { transform: translateY(var(--sh-yl)) rotate(calc(var(--sh-l) - 6deg)); }
.figure.part-open-l .arm-l .fore { transform: rotate(calc(var(--el-l) - 10deg)); }
.figure.part-open-l .arm-l .hand-g { transform: rotate(calc(var(--wr-l) + 8deg)); }
.figure.part-open-l .arm-l .hand { transform: scale(1.12); }

/* 单击「打招呼」:引擎先把这一侧摆到 wave-<side>(肩肘 480ms 过渡到位),
   到位之后前臂摆两次、腕反向跟随晚 60ms —— 延迟写在动画上,不另排定时器。 */
.figure.act-hello-r .arm-r .fore { animation: w-wave-soft 380ms ease-in-out 480ms 2; }
.figure.act-hello-r .arm-r .hand-g { animation: w-wrist-follow 380ms ease-in-out 540ms 2; }
.figure.act-hello-l .arm-l .fore { animation: w-wave-soft-l 380ms ease-in-out 480ms 2; }
.figure.act-hello-l .arm-l .hand-g { animation: w-wrist-follow-l 380ms ease-in-out 540ms 2; }

/* 双击「击掌」:手举到位那一刻手心弹一下 */
@keyframes w-hand-pop { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.2); } }
.figure.act-highfive-r .arm-r .hand { animation: w-hand-pop 200ms ease-out 420ms; }
.figure.act-highfive-l .arm-l .hand { animation: w-hand-pop 200ms ease-out 420ms; }

/* 腿:只在移动/特定 idle 时出现(双臂常驻,所以永远不会「只有腿没有手」) */
.figure .legs { display: none; }
.figure .foot { fill: var(--w-shell); stroke: var(--w-line); stroke-width: .95; }
.figure.legs .legs { display: inline; }
@keyframes w-run-l { 0%, 100% { transform: rotate(-32deg); } 50% { transform: rotate(32deg); } }
@keyframes w-run-r { 0%, 100% { transform: rotate(32deg); } 50% { transform: rotate(-32deg); } }
.figure.run .leg-l { animation: w-run-l 230ms linear infinite; }
.figure.run .leg-r { animation: w-run-r 230ms linear infinite; }
.figure.legs-pop .legs { animation: w-legs-pop 260ms ease-out; }
@keyframes w-legs-pop { 0% { transform: translateY(-4px); opacity: 0; } 100% { transform: translateY(0); opacity: 1; } }
.figure.skate-stance .leg-l { transform: rotate(-14deg); }
.figure.skate-stance .leg-r { transform: rotate(18deg); }
.figure.kick .leg-r { animation: w-kick 400ms ease-in-out; }
@keyframes w-kick { 0% { transform: rotate(18deg); } 45% { transform: rotate(-54deg); } 100% { transform: rotate(18deg); } }
.figure.tap .leg-r { animation: w-tap 360ms ease-in-out 3; transform-origin: 28px 48.4px; }
@keyframes w-tap { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(-14deg); } }

/* 地面道具 */
.figure .board, .figure .mound { display: none; }
.figure.board .board { display: inline; }
.figure.mound .mound { display: inline; }
.figure .board rect { fill: var(--w-board); }
.figure .board circle { fill: var(--w-wheel); }
.figure .soil { fill: var(--w-soil); }
.figure .soil-top { fill: var(--w-soil-top); opacity: .7; }
.figure .soil-dots { stroke: var(--w-soil-dark); stroke-width: 1.1; stroke-linecap: round; }
.figure .sprout { display: none; }
.figure.sprout .sprout { display: inline; }
.figure .stem { fill: none; stroke: var(--w-stem); stroke-width: 1.2; stroke-linecap: round; }
.figure .leaf { fill: var(--w-leaf); }
.figure .ground-front .leaf { stroke: var(--w-leaf-line); stroke-width: .45; stroke-linejoin: round; }
/* 芽:冒出来(轻过冲)→ 随风摆;回场前先长高,身体顶出的一瞬缩回(它变成了头顶的芽) */
@keyframes w-sprout { 0% { transform: scale(0); } 70% { transform: scale(1.2); } 100% { transform: scale(1); } }
.figure.sprout .sprout { animation: w-sprout 520ms cubic-bezier(.3, .8, .4, 1); }
@keyframes w-sway-leaf { 0%, 100% { transform: rotate(-6deg); } 50% { transform: rotate(6deg); } }
.figure.sprout.sway-leaf .sprout { animation: w-sway-leaf 1.5s ease-in-out infinite; }
@keyframes w-sprout-grow { 0% { transform: scale(1) rotate(-4deg); } 100% { transform: scale(1.7) rotate(3deg); } }
.figure.sprout.sprout-grow .sprout { animation: w-sprout-grow 520ms ease-out forwards; }
@keyframes w-sprout-hide { 0% { transform: scale(1.7); } 100% { transform: scale(0); } }
.figure.sprout.sprout-hide .sprout { animation: w-sprout-hide 180ms ease-in forwards; }
/* 土堆:随刨土三下分三截长起来;埋入时堆高;回场前颤一颤;身体顶出时回落;最后淡出 */
.figure.mound .mound { transition: opacity 320ms ease-out; }
@keyframes w-mound-in { 0% { transform: scaleY(0); } 100% { transform: scaleY(1); } }
.figure.mound-in .mound { animation: w-mound-in 300ms ease-out; }
@keyframes w-mound-grow { 0% { transform: scaleY(0); } 26% { transform: scaleY(.06); } 36% { transform: scaleY(.42); } 60% { transform: scaleY(.46); } 70% { transform: scaleY(.78); } 92% { transform: scaleY(.82); } 100% { transform: scaleY(1); } }
.figure.mound-grow .mound { animation: w-mound-grow 1320ms linear forwards; }
@keyframes w-mound-heap { 0% { transform: scale(1, 1); } 100% { transform: scale(1.06, 1.32); } }
.figure.mound-heap .mound { animation: w-mound-heap 1000ms ease-in-out forwards; }
.figure.mound-heaped .mound { transform: scale(1.06, 1.32); }
@keyframes w-mound-bump { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08, 1.2); } }
.figure.mound-bump .mound { animation: w-mound-bump 340ms ease-in-out; }
@keyframes w-mound-bump2 { 0%, 100% { transform: scale(1.06, 1.32); } 50% { transform: scale(1.12, 1.44); } }
.figure.mound-bump2 .mound { animation: w-mound-bump2 340ms ease-in-out; }
@keyframes w-mound-quake { 0%, 100% { transform: scale(1.06, 1.32); } 20% { transform: scale(1.09, 1.38) translateX(-.5px); } 50% { transform: scale(1.05, 1.3) translateX(.6px); } 80% { transform: scale(1.08, 1.36) translateX(-.3px); } }
.figure.mound-quake .mound { animation: w-mound-quake 420ms ease-in-out; }
@keyframes w-mound-settle { 0% { transform: scale(1.06, 1.32); } 100% { transform: scale(1, 1); } }
.figure.mound-settle .mound { animation: w-mound-settle 900ms ease-out forwards; }
.figure.mound-out .mound { opacity: 0; }
/* 插在土里的小铲:刨完随手一插;淡出随土堆 */
.figure .spade-stuck { display: none; transition: opacity 320ms ease-out; }
@keyframes w-spade-in { 0% { transform: translateY(-6px) rotate(-18deg); opacity: 0; } 100% { transform: none; opacity: 1; } }
.figure.spade .spade-stuck { display: inline; animation: w-spade-in 320ms cubic-bezier(.3, .9, .3, 1); }
.figure.spade-out .spade-stuck { opacity: 0; }
/* 飞土(刨土时从铲头甩向身后)与抖落的土(顶出来后从身上掉下) */
.figure .flecks, .figure .shed { display: none; }
.figure .fk, .figure .sd { fill: var(--w-soil-dark); }
.figure.digging .flecks { display: inline; }
@keyframes w-fleck-1 { 0% { transform: translate(44px, 37px) scale(1); opacity: 0; } 6% { opacity: 1; } 68% { transform: translate(49px, 30px) scale(.5); opacity: 0; } 100% { transform: translate(49px, 30px); opacity: 0; } }
@keyframes w-fleck-2 { 0% { transform: translate(44px, 37px) scale(1); opacity: 0; } 6% { opacity: 1; } 68% { transform: translate(51px, 34px) scale(.6); opacity: 0; } 100% { transform: translate(51px, 34px); opacity: 0; } }
@keyframes w-fleck-3 { 0% { transform: translate(44px, 37px) scale(1); opacity: 0; } 6% { opacity: 1; } 68% { transform: translate(47px, 28px) scale(.55); opacity: 0; } 100% { transform: translate(47px, 28px); opacity: 0; } }
@keyframes w-fleck-4 { 0% { transform: translate(44px, 37px) scale(1); opacity: 0; } 6% { opacity: 1; } 68% { transform: translate(50px, 40px) scale(.6); opacity: 0; } 100% { transform: translate(50px, 40px); opacity: 0; } }
.figure.digging .fk1 { animation: w-fleck-1 440ms ease-out 160ms 3; }
.figure.digging .fk2 { animation: w-fleck-2 440ms ease-out 200ms 3; }
.figure.digging .fk3 { animation: w-fleck-3 440ms ease-out 180ms 3; }
.figure.digging .fk4 { animation: w-fleck-4 440ms ease-out 220ms 3; }
.figure.shed .shed { display: inline; }
@keyframes w-shed-1 { 0% { transform: translate(18px, 30px); opacity: 1; } 100% { transform: translate(15px, 49px); opacity: 0; } }
@keyframes w-shed-2 { 0% { transform: translate(30px, 28px); opacity: 1; } 100% { transform: translate(33px, 49px); opacity: 0; } }
@keyframes w-shed-3 { 0% { transform: translate(24px, 24px); opacity: 1; } 100% { transform: translate(22px, 49px); opacity: 0; } }
@keyframes w-shed-4 { 0% { transform: translate(34px, 36px); opacity: 1; } 100% { transform: translate(37px, 49px); opacity: 0; } }
.figure.shed .sd1 { animation: w-shed-1 520ms ease-in forwards; }
.figure.shed .sd2 { animation: w-shed-2 520ms ease-in 70ms forwards; }
.figure.shed .sd3 { animation: w-shed-3 520ms ease-in 120ms forwards; }
.figure.shed .sd4 { animation: w-shed-4 520ms ease-in 40ms forwards; }
/* 书:翻开(从书脊展开)→ 翻页(页叶绕书脊翻到左页)→ 合上(收回书脊) */
.figure .bookp { display: none; }
.figure.prop-book .bookp { display: inline; }
.figure .book-cover { fill: var(--w-book); stroke: var(--w-line); stroke-width: .45; stroke-opacity: .45; stroke-linejoin: round; }
.figure .book-page { fill: var(--w-page); stroke: var(--w-line); stroke-width: .35; stroke-opacity: .35; }
.figure .book-text { fill: none; stroke: var(--w-face); stroke-width: .5; stroke-linecap: round; opacity: .32; }
.figure .book-spine { fill: none; stroke: var(--w-line); stroke-width: .5; stroke-opacity: .35; }
.figure .book-leaf { fill: var(--w-page); stroke: var(--w-line); stroke-width: .35; stroke-opacity: .4; display: none; }
@keyframes w-flip { 0% { transform: scaleX(1) translateY(0); } 50% { transform: scaleX(.06) translateY(-1.4px); } 100% { transform: scaleX(-1) translateY(0); } }
.figure.flip .book-leaf { display: inline; animation: w-flip 640ms cubic-bezier(.4, 0, .4, 1) forwards; }
@keyframes w-book-open { 0% { transform: scaleX(.08); opacity: 0; } 100% { transform: none; opacity: 1; } }
.figure.book-open .bookp { animation: w-book-open 420ms cubic-bezier(.3, .9, .3, 1); }
@keyframes w-book-close { 0% { transform: scaleX(1); } 100% { transform: scaleX(.06); } }
.figure.book-close .bookp { animation: w-book-close 480ms cubic-bezier(.5, 0, .6, 1) forwards; }
@keyframes w-board-in { 0% { transform: translateX(-30px); opacity: 0; } 100% { transform: translateX(0); opacity: 1; } }
.figure.board-in .board { animation: w-board-in 300ms ease-out; }
@keyframes w-board-out { 0% { transform: translateX(0); opacity: 1; } 100% { transform: translateX(22px); opacity: 0; } }
.figure.board-out .board { animation: w-board-out 340ms ease-in forwards; }

/* ---------- 衣饰(显隐 + 穿脱动画) ---------- */
.figure .outfit { display: none; }
.figure .cloth-a { fill: var(--w-cloth-a); }
.figure .cloth-b { fill: var(--w-cloth-b); }
/* 身体类 */
.figure .bodywear .cloth-a { stroke: none; }
.figure .rib { fill: none; stroke: var(--w-cloth-b); stroke-width: 1.1; stroke-linecap: round; }
.figure .knit { fill: none; stroke: var(--w-cloth-b); stroke-width: .6; stroke-linecap: round; opacity: .35; }
.figure .placket { fill: none; stroke: var(--w-cloth-b); stroke-width: .8; opacity: .9; }
.figure .btn { fill: var(--w-cloth-b); }
.figure .pocket { fill: none; stroke: var(--w-cloth-b); stroke-width: .6; stroke-linejoin: round; opacity: .8; }
.figure .o-coat .lapel { fill: var(--w-cloth-b); stroke: var(--w-line); stroke-width: .35; stroke-opacity: .35; stroke-linejoin: round; }
.figure .o-coat .seam { fill: none; stroke: var(--w-line); stroke-width: .5; stroke-opacity: .3; }
.figure .o-coat .pocket { stroke: var(--w-line); stroke-opacity: .35; }
.figure .o-coat .pen { fill: none; stroke: var(--w-spec); stroke-width: 1; stroke-linecap: round; }
.figure .o-coat .btn { fill: var(--w-cloth-b); stroke: var(--w-line); stroke-width: .3; stroke-opacity: .4; }
.figure .o-hoodie .cord { fill: none; stroke: var(--w-cloth-b); stroke-width: .9; stroke-linecap: round; }
/* 颈饰 */
.figure .o-collar .collar { fill: var(--w-collar); stroke: var(--w-line); stroke-width: .9; stroke-opacity: .5; stroke-linejoin: round; }
.figure .o-collar .collar-fold { fill: none; stroke: var(--w-line); stroke-width: .45; stroke-opacity: .3; }
.figure .o-bow path { stroke: var(--w-line); stroke-width: .45; stroke-opacity: .35; stroke-linejoin: round; }
/* 领带(§8.2):描边比其它颈饰重一档(.6 / .55)—— 领带整体下移到腰线附近,那里衣饰主色与壳色最接近,不加重就糊 */
.figure .o-tie path { stroke: var(--w-line); stroke-width: .6; stroke-opacity: .55; stroke-linejoin: round; }
.figure .o-tie .collar-l, .figure .o-tie .collar-r { fill-opacity: .9; }
.figure .o-bow .knot { stroke: var(--w-line); stroke-width: .4; stroke-opacity: .35; }
.figure .o-scarf .band, .figure .o-scarf .tail path.cloth-a, .figure .o-scarf .knot-lump { stroke: var(--w-line); stroke-width: .5; stroke-opacity: .35; stroke-linejoin: round; }
.figure .o-scarf .band2 { fill: var(--w-line); fill-opacity: .1; stroke: none; }
.figure .o-scarf .stripe { fill: none; stroke: var(--w-cloth-b); stroke-width: 1; stroke-linecap: round; display: none; }
.figure.o-striped .o-scarf .stripe { display: inline; }
.figure .o-scarf .fringe { fill: none; stroke: var(--w-cloth-a); stroke-width: .8; stroke-linecap: round; }
.figure .o-pin .pin-disc { stroke: var(--w-gold); stroke-width: .5; }
.figure .o-pin .pin-star { fill: var(--w-gold); }
.figure .o-bag .strap { fill: none; stroke: var(--w-cloth-b); stroke-width: 1.5; stroke-linecap: round; }
.figure .o-bag .bagbody rect { stroke: var(--w-line); stroke-width: .45; stroke-opacity: .4; }
.figure .o-bag .flap { stroke: var(--w-line); stroke-width: .35; stroke-opacity: .35; }
.figure .o-bag .clasp { fill: var(--w-gold); }
/* 帽子 */
.figure .o-bucket path, .figure .o-gradcap path, .figure .o-cap path { stroke: var(--w-line); stroke-width: .5; stroke-opacity: .4; stroke-linejoin: round; }
.figure .o-cap .seam { fill: none; stroke: var(--w-line); stroke-width: .45; stroke-opacity: .25; }
.figure .o-bucket .band { stroke: none; opacity: .9; }
.figure .o-bucket .stitch { fill: none; stroke: var(--w-line); stroke-width: .4; stroke-opacity: .3; }
.figure .o-sweater .stripe { fill: none; stroke: var(--w-cloth-b); stroke-width: 1.3; stroke-linecap: round; }
.figure .o-cap .btn { stroke: var(--w-line); stroke-width: .3; }
.figure .o-phones .band { fill: none; stroke: var(--w-spec); stroke-width: 1.9; stroke-linecap: round; }
.figure .o-phones .cup { stroke: var(--w-line); stroke-width: .5; stroke-opacity: .4; }
.figure .o-phones .cushion { opacity: .8; }
.figure .o-gradcap .cap-edge { fill: none; stroke: #fff; stroke-width: .5; stroke-opacity: .25; }
.figure .o-gradcap .tassel { fill: none; stroke: var(--w-gold); stroke-width: .9; stroke-linecap: round; }
.figure .o-gradcap .tassel-knot { fill: var(--w-gold); }
/* 显隐 */
.figure.o-scarf-on .o-scarf, .figure.o-bow-on .o-bow, .figure.o-tie-on .o-tie, .figure.o-collar-on .o-collar,
.figure.o-vest-on .o-vest, .figure.o-cardigan-on .o-cardigan, .figure.o-coat-on .o-coat, .figure.o-hoodie-on .o-hoodie, .figure.o-sweater-on .o-sweater,
.figure.o-bucket-on .o-bucket, .figure.o-cap-on .o-cap, .figure.o-phones-on .o-phones, .figure.o-gradcap-on .o-gradcap,
.figure.o-bag-on .o-bag, .figure.o-pin-on .o-pin { display: inline; }
/* 穿上(每类各自的小剧) */
@keyframes w-don-scarf { 0% { transform: translateY(-8px) scaleX(.55); opacity: 0; } 55% { transform: translateY(.7px) scaleX(1.02); opacity: 1; } 100% { transform: none; opacity: 1; } }
.figure.donning .o-scarf { transform-origin: 24px 28px; animation: w-don-scarf 560ms cubic-bezier(.3, .8, .3, 1); }
@keyframes w-don-knot { 0% { transform: scale(0) rotate(-50deg); } 70% { transform: scale(1.06) rotate(4deg); } 100% { transform: scale(1) rotate(0); } }
.figure.donning .o-bow, .figure.donning .o-tie, .figure.donning .o-pin { animation: w-don-knot 500ms cubic-bezier(.3, .8, .3, 1); }
.figure.donning .o-pin { transform-origin: 17.6px 31.8px; }
/* 帽子:从右手的位置(头的右上外侧)被按到头上,落定时轻压一下(手的路径由引擎同步) */
@keyframes w-don-hat { 0% { transform: translate(7px, -4px) rotate(16deg); opacity: 0; } 18% { opacity: 1; } 70% { transform: translate(0, 1px) rotate(0); } 100% { transform: none; opacity: 1; } }
.figure.donning .o-bucket, .figure.donning .o-gradcap, .figure.donning .o-phones, .figure.donning .o-cap { transform-origin: 24px 9px; animation: w-don-hat 560ms cubic-bezier(.3, .8, .3, 1); }
@keyframes w-don-body { 0% { transform: scaleY(0); } 100% { transform: scaleY(1); } }
/* 身体类:从领口往下展开 = 两手把衣服往下拉(引擎让双手沿体侧从领口走到下摆,同一时长同一缓动) */
.figure.donning .o-vest, .figure.donning .o-cardigan, .figure.donning .o-coat, .figure.donning .o-hoodie, .figure.donning .o-sweater { transform-origin: 24px 27.4px; animation: w-don-body 520ms cubic-bezier(.4, 0, .5, 1); }
@keyframes w-don-collar { 0% { transform: scale(.4); opacity: 0; } 100% { transform: none; opacity: 1; } }
.figure.donning .o-collar { transform-origin: 24px 27px; animation: w-don-collar 400ms ease-out; }
@keyframes w-don-bag { 0% { transform: translate(-6px, -10px) rotate(-20deg); opacity: 0; } 100% { transform: none; opacity: 1; } }
.figure.donning .o-bag { transform-origin: 15px 27px; animation: w-don-bag 560ms cubic-bezier(.3, .8, .3, 1); }
/* 脱下:统一上提淡出 */
@keyframes w-doff { 0% { transform: none; opacity: 1; } 100% { transform: translateY(-8px) scale(.9); opacity: 0; } }
.figure.doffing .outfit { animation: w-doff 420ms ease-in forwards; }
/* 整理:围巾尾巴摆/领结抖/帽子歪正/包带拉 */
@keyframes w-tail-swing { 0%, 100% { transform: rotate(0); } 35% { transform: rotate(-14deg); } 70% { transform: rotate(8deg); } }
.figure.fix-scarf .o-scarf .tail { animation: w-tail-swing 680ms ease-in-out; }
@keyframes w-bow-wiggle { 0%, 100% { transform: rotate(0); } 30% { transform: rotate(-8deg); } 65% { transform: rotate(7deg); } }
.figure.fix-bow .o-bow, .figure.fix-bow .o-tie { animation: w-bow-wiggle 560ms ease-in-out; }
@keyframes w-hat-fix { 0% { transform: rotate(0); } 30% { transform: rotate(-7deg) translateX(-.7px); } 100% { transform: rotate(0); } }
.figure.fix-hat .o-bucket, .figure.fix-hat .o-gradcap, .figure.fix-hat .o-cap { transform-origin: 24px 9px; animation: w-hat-fix 600ms ease-in-out; }
@keyframes w-bag-fix { 0%, 100% { transform: translate(0, 0); } 40% { transform: translate(-.6px, -1.2px); } }
.figure.fix-bag .o-bag { animation: w-bag-fix 560ms ease-in-out; }
.figure.hat-tilt .o-bucket, .figure.hat-tilt .o-cap { transform-origin: 24px 12px; transform: rotate(-4deg); }

/* ---------- 身体单发动作(.act 层;--amp/--dur 由引擎逐次变奏注入) ---------- */
.figure .act { transform-origin: 24px 50px; }
.figure .pose { transform-origin: 24px 50px; transition: transform 520ms cubic-bezier(.3, .9, .3, 1); }
@keyframes w-sway { 0%, 100% { transform: rotate(0); } 30% { transform: rotate(var(--amp)); } 70% { transform: rotate(calc(var(--amp) * -.7)); } }
@keyframes w-wiggle { 0%, 100% { transform: rotate(0); } 20% { transform: rotate(var(--amp)); } 45% { transform: rotate(calc(var(--amp) * -1)); } 70% { transform: rotate(calc(var(--amp) * .55)); } }
@keyframes w-straighten { 0%, 100% { transform: scale(1, 1) translateY(0); } 40% { transform: scale(.98, 1.07) translateY(-1.8px); } 65% { transform: scale(.98, 1.07) translateY(-1.8px); } }
@keyframes w-squash { 0%, 100% { transform: scale(1, 1); } 45% { transform: scale(1.05, .93); } }
@keyframes w-nod { 0%, 100% { transform: translateY(0) scaleY(1); } 25% { transform: translateY(2.2px) scaleY(.965); } 50% { transform: translateY(0) scaleY(1); } 75% { transform: translateY(1.8px) scaleY(.97); } }
@keyframes w-bob { 0% { transform: translateY(0); } 40% { transform: translateY(1.8px); } 100% { transform: translateY(0); } }
@keyframes w-hop { 0%, 100% { transform: translateY(0) scale(1, 1); } 18% { transform: translateY(1.2px) scale(1.05, .93); } 50% { transform: translateY(-4.6px) scale(.97, 1.04); } 82% { transform: translateY(.8px) scale(1.03, .96); } }
@keyframes w-hop2 { 0%, 100% { transform: translateY(0); } 25% { transform: translateY(-4.2px); } 50% { transform: translateY(0); } 75% { transform: translateY(-3px); } }
@keyframes w-stretch { 0%, 100% { transform: scale(1, 1); } 40% { transform: scale(.965, 1.1) translateY(-1px); } 70% { transform: scale(.965, 1.1) translateY(-1px); } }
@keyframes w-tilt-hold { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(var(--amp)); } 75% { transform: rotate(var(--amp)); } }
@keyframes w-shake { 0%, 100% { transform: rotate(0); } 25% { transform: rotate(calc(var(--amp) * -1)); } 50% { transform: rotate(var(--amp)); } 75% { transform: rotate(calc(var(--amp) * -.6)); } }
@keyframes w-shiver { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-1px); } 40% { transform: translateX(1px); } 60% { transform: translateX(-.8px); } 80% { transform: translateX(.6px); } }
@keyframes w-peek { 0%, 100% { transform: rotate(0) translateX(0); } 40% { transform: rotate(-8deg) translateX(-2.2px); } 70% { transform: rotate(-8deg) translateX(-2.2px); } }
@keyframes w-spin { 0% { transform: rotate(0) scaleX(1); } 50% { transform: rotate(var(--amp)) scaleX(.2); } 100% { transform: rotate(0) scaleX(1); } }
.figure.act-sway .act { animation: w-sway var(--dur) ease-in-out; }
.figure.act-wiggle .act { animation: w-wiggle var(--dur) ease-in-out; }
.figure.act-straighten .act { animation: w-straighten var(--dur) ease-in-out; }
.figure.act-squash .act { animation: w-squash var(--dur) ease-in-out; }
.figure.act-nod .act { animation: w-nod var(--dur) ease-in-out; }
.figure.act-bob .act { animation: w-bob var(--dur) ease-in-out; }
.figure.act-hop .act { animation: w-hop var(--dur) ease-in-out; }
.figure.act-hop2 .act { animation: w-hop2 var(--dur) ease-in-out; }
.figure.act-stretch .act { animation: w-stretch var(--dur) ease-in-out; }
.figure.act-tilt .act { animation: w-tilt-hold var(--dur) ease-in-out; }
.figure.act-shake .act { animation: w-shake var(--dur) ease-in-out; }
.figure.act-shiver .act { animation: w-shiver var(--dur) ease-in-out; }
.figure.act-peek .act { animation: w-peek var(--dur) ease-in-out; }
.figure.act-spin .act { animation: w-spin var(--dur) ease-in-out; }
/* 姿态保持 */
.figure.pose-lean .pose { transform: rotate(7deg); }
.figure.pose-lean-l .pose { transform: rotate(-7deg); }
.figure.pose-listen .pose { transform: rotate(-5deg) translateY(-1px); }
.figure.pose-bow .pose { transform: rotate(9deg) translateY(1.4px) scaleY(.98); }
.figure.pose-slump .pose { transform: translateY(2px) scaleY(.965); }
/* 思考慢循环 */
@keyframes w-think { 0%, 100% { transform: rotate(-3.5deg); } 50% { transform: rotate(3.8deg); } }
.figure.thinking .act { animation: w-think var(--think-dur) ease-in-out infinite; }
/* 倾听:静止保持 + 呼吸更明显 */
.figure.listening { --breath-amp: .046; --breath-dur: 3.5s; }

/* ---------- 退场 / 回场(唯一允许位移的场景;作用于 .stage-root) ---------- */
.figure .stage-root { transform-origin: 24px 50px; }
@keyframes w-exit-fade { 0% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(3px) scale(.9); } }
@keyframes w-exit-run { 0% { transform: translateX(0) rotate(0); } 16% { transform: translateX(-3px) rotate(-4deg); } 30% { transform: translateX(3px) rotate(9deg); } 100% { transform: translateX(100px) rotate(9deg); } }
@keyframes w-exit-skate { 0% { transform: translateX(0) rotate(0); } 30% { transform: translateX(6px) rotate(-3deg); } 100% { transform: translateX(108px) rotate(-5deg); } }
@keyframes w-exit-sink { 0% { transform: translateY(0); } 25% { transform: translateY(-2.5px); } 100% { transform: translateY(80px); } }
@keyframes w-exit-roll { 0% { transform: translate(0, 0) rotate(0); } 50% { transform: translate(44px, -11px) rotate(190deg); } 100% { transform: translate(100px, 0) rotate(360deg); } }
@keyframes w-exit-walk { 0% { transform: translateX(0); } 100% { transform: translateX(100px); } }
@keyframes w-exit-jump { 0% { transform: translateY(0) scale(1, 1); } 18% { transform: translateY(4px) scale(1.08, .9); } 32% { transform: translateY(-6px) scale(.96, 1.06); } 100% { transform: translateY(-120px) scale(.96, 1.06); } }
@keyframes w-walk-bob { 0%, 100% { transform: translateY(0) rotate(-2deg); } 50% { transform: translateY(-1.8px) rotate(2deg); } }
/* 落花生:刨完土后直立着缓缓沉进土堆(不翻转、不倒立;.pit 挂地面裁剪,土从下往上盖) */
@keyframes w-bury { 0% { transform: translateY(0); } 10% { transform: translateY(-1.6px); } 100% { transform: translateY(50px); } }
.figure.x-bury .pose { animation: w-bury 1000ms cubic-bezier(.6, .05, .55, 1) forwards; }
.figure.x-buried .pose { transition: none; transform: translateY(50px); }
@keyframes w-rise { 0% { transform: translateY(50px); } 78% { transform: translateY(-2.2px); } 100% { transform: translateY(0); } }
.figure.x-rise .pose { animation: w-rise 900ms cubic-bezier(.3, .7, .3, 1) forwards; }
.figure.x-bury .contact, .figure.x-buried .contact, .figure.x-rise .contact { opacity: 0; }
.figure.x-exit-fade .stage-root { animation: w-exit-fade 420ms ease-in forwards; }
.figure.x-exit-run .stage-root { animation: w-exit-run 1100ms cubic-bezier(.5, 0, .85, .4) forwards; }
.figure.x-exit-skate .stage-root { animation: w-exit-skate 1150ms cubic-bezier(.45, 0, .8, .45) forwards; }
.figure.x-exit-sink .stage-root { animation: w-exit-sink 800ms cubic-bezier(.5, 0, .8, .4) forwards; }
.figure.x-exit-roll .stage-root { animation: w-exit-roll 1100ms cubic-bezier(.45, .05, .75, .5) forwards; }
.figure.x-exit-walk .stage-root { animation: w-exit-walk 1150ms cubic-bezier(.4, 0, .8, .5) forwards; }
.figure.x-exit-jump .stage-root { animation: w-exit-jump 900ms cubic-bezier(.5, 0, .9, .4) forwards; }
.figure.walk-bob .act { animation: w-walk-bob 300ms ease-in-out infinite; }
.figure.x-turn .pose { animation: w-turn 220ms ease-in-out forwards; }
@keyframes w-turn { 0% { transform: scaleX(1); } 50% { transform: scaleX(.12); } 100% { transform: scaleX(-1); } }
.figure.faced-away .pose { transform: scaleX(-1); }
.figure.gone .stage-root { opacity: 0; }
/* 回场 */
@keyframes w-enter-fade { 0% { opacity: 0; transform: translateY(4px) scale(.92); } 100% { opacity: 1; transform: none; } }
@keyframes w-enter-run { 0% { transform: translateX(100px) rotate(-9deg); } 78% { transform: translateX(-2px) rotate(-6deg); } 100% { transform: translateX(0) rotate(0); } }
@keyframes w-enter-skate { 0% { transform: translateX(108px) rotate(3deg); } 70% { transform: translateX(-2.5px) rotate(5deg); } 100% { transform: translateX(0) rotate(0); } }
@keyframes w-enter-peek { 0% { transform: translateY(80px); } 45% { transform: translateY(27px); } 60% { transform: translateY(27px); } 100% { transform: translateY(0); } }
@keyframes w-enter-roll { 0% { transform: translate(100px, 0) rotate(-360deg); } 50% { transform: translate(44px, -11px) rotate(-170deg); } 100% { transform: translate(0, 0) rotate(0); } }
@keyframes w-enter-walk { 0% { transform: translateX(100px); } 100% { transform: translateX(0); } }
@keyframes w-enter-drop { 0% { transform: translateY(-120px) scale(.96, 1.06); } 62% { transform: translateY(0) scale(.96, 1.06); } 74% { transform: translateY(0) scale(1.1, .88); } 88% { transform: translateY(-1.5px) scale(.98, 1.03); } 100% { transform: none; } }
.figure.x-enter-fade .stage-root { animation: w-enter-fade 480ms ease-out; }
.figure.x-enter-run .stage-root { animation: w-enter-run 1000ms cubic-bezier(.2, .6, .3, 1); }
.figure.x-enter-skate .stage-root { animation: w-enter-skate 1100ms cubic-bezier(.2, .6, .3, 1); }
.figure.x-enter-peek .stage-root { animation: w-enter-peek 980ms cubic-bezier(.3, .7, .3, 1); }
.figure.x-enter-roll .stage-root { animation: w-enter-roll 1100ms cubic-bezier(.3, .5, .4, 1); }
.figure.x-enter-walk .stage-root { animation: w-enter-walk 1100ms cubic-bezier(.25, .6, .35, 1); }
.figure.x-enter-drop .stage-root { animation: w-enter-drop 1000ms cubic-bezier(.4, 0, .6, 1); }
.figure.shake-off .act { animation: w-shake 560ms ease-in-out; }

/* ---------- 尺寸档与手机迷你档 ---------- */
.figure.mini .outfit:not(.o-scarf):not(.o-bucket):not(.o-cap), .figure.mini .tick, .figure.mini .g-short, .figure.mini .lens-hl2,
.figure.mini .brow, .figure.mini .pad, .figure.mini .hinge, .figure.mini .lens-reflect, .figure.mini .knit, .figure.mini .contact { display: none; }
.figure.mini .outline { stroke-width: 1.7; }
.figure.mini .groove { stroke-width: 1.1; opacity: .45; }
/* 迷你档镜框:线宽 1.8 → 1.45(≈0.7 屏幕像素),镜圈外扩到 r4.8 让镜框离眼睛更远 */
.figure.mini .rim, .figure.mini .temple path, .figure.mini .bridge { stroke-width: 1.45; }
.figure.mini .rim-round, .figure.mini .lens-round, .figure.mini .rim-thin { r: 4.8; }
.figure.mini .arm, .figure.mini .legs, .figure.mini .bookp, .figure.mini .flecks, .figure.mini .shed, .figure.mini .spade-stuck { display: none; }
.figure.mini .hair .stem { stroke-width: 1.5; }

/* ---------- reduced-motion:引擎停调度;CSS 层全部直切 ---------- */
@media (prefers-reduced-motion: reduce) {
  .figure *, .figure { animation: none !important; transition: none !important; }
}
.figure.rm *, .figure.rm { animation: none !important; transition: none !important; }
.figure.rm.x-exit-fade .stage-root, .figure.rm.x-exit-run .stage-root, .figure.rm.x-exit-skate .stage-root,
.figure.rm.x-exit-sink .stage-root, .figure.rm.x-exit-roll .stage-root, .figure.rm.x-exit-walk .stage-root, .figure.rm.x-exit-jump .stage-root,
.figure.rm.x-bury .stage-root, .figure.rm.digging .stage-root { opacity: 0; }

/* ---------- 嘀咕字幕(§3.26.4;R-31「一个小的临时效果承载感受」) ----------
 * 挂在 .watson-launcher(position:fixed)里,所以绝对定位以桌宠入口为基准;
 * 水平锚在**形象**的中线:入口盒子 = 形象本身(「问华生」标签已 absolute 出流,
 * 见 watson-panel.css),所以从右边界退回半个形象宽就是中线,标签翻到哪边都不影响。
 * 贴到视口边上时由 JS 写 --wm-shift 平移回来,气泡不出屏。
 * 材质走 §3.9 第 4 条唯一浮层材质 --dl-sh-menu,不自造阴影。 */
.watson-mutter {
    position: absolute;
    right: calc(var(--dl-watson-w, 88px) / 2);
    bottom: calc(100% + 8px);
    z-index: 1;
    max-width: 180px;
    padding: 4px 8px;
    border-radius: 10px;
    background: var(--dl-surface);
    box-shadow: var(--dl-sh-menu);
    color: var(--dl-ink);
    font-size: 12px;
    line-height: 16px;
    white-space: nowrap;
    text-overflow: ellipsis;
    overflow: hidden;
    pointer-events: none;
    opacity: 0;
    transform: translateX(calc(50% + var(--wm-shift, 0px)));
}

/* 距视口顶不足 60px 就翻到下方:气泡宁可挡住桌宠脚下,也不要被裁在屏幕外 */
.watson-mutter.is-below {
    top: calc(100% + 8px);
    bottom: auto;
}

@keyframes watson-mutter-in {
    from { opacity: 0; transform: translateX(calc(50% + var(--wm-shift, 0px))) translateY(4px); }
    to   { opacity: 1; transform: translateX(calc(50% + var(--wm-shift, 0px))) translateY(0); }
}

.watson-mutter.is-in {
    opacity: 1;
    animation: watson-mutter-in 120ms ease-out;
}

.watson-mutter.is-out {
    opacity: 0;
    transition: opacity 160ms ease-out;
}

/* 减动偏好:只淡不移(§3.26.4 末句) */
@media (prefers-reduced-motion: reduce) {
    .watson-mutter.is-in { animation: none; }
}
