/* ═══════════════════════════════════════════════════════════════════
   动效层 v2 · 连续曲率 / 材质高光 / 发丝描边 / 分层视差 / 视图转场
   ───────────────────────────────────────────────────────────────────
   这一层排在 app.css 之后加载，同名选择器直接接管，不改老文件。
   全部参数集中在前 40 行，改手感只动这里。

   掉帧约束（三条铁律）：
   1. 只动 transform / opacity / filter；不碰 width/height/top/left。
   2. 所有跟随类动画（高光、磁吸、邻近）共用 JS 里的一条 rAF，
      不在每个元素上各开一条。
   3. 描边、光斑一律用伪元素 + mask，不新增 DOM 节点。
   ═══════════════════════════════════════════════════════════════════ */

:root{
  /* —— 曲线：两个"连续曲率"形状，不用线性、不用弹跳感过强的 —— */
  --c-out:cubic-bezier(.22,1,.36,1);        /* 进入：起步快、收尾长 */
  --c-in:cubic-bezier(.55,0,.68,.19);       /* 离开：起步缓、收尾干脆 */
  --c-spring:cubic-bezier(.32,.72,0,1);     /* 回弹：单次轻微过冲 */
  --c-sheet:cubic-bezier(.32,.72,0,1);      /* 面板升降 */
  --c-shake:cubic-bezier(.36,.07,.19,.97);

  /* —— 时长 —— */
  --t-hover-in:220ms;
  --t-hover-out:180ms;
  --t-press:90ms;
  --t-trace:320ms;
  --t-trace-back:140ms;
  --t-spec:200ms;          /* 高光淡出 */
  --t-sheet-in:420ms;
  --t-sheet-out:260ms;
  --t-push:340ms;
  --t-pop:300ms;
  --t-fade-out:120ms;
  --t-fade-in:220ms;
  --t-reveal:600ms;
  --t-title:0ms;           /* 标题固化是滚动的连续映射，没有独立时长 */
  --t-switch:260ms;

  /* —— 几何 —— */
  --lift-hover:2px;
  --scale-hover:1.012;
  --scale-hover-touch:1.007;   /* 触屏降级到 60% 幅度 */
  --lift-hover-touch:1px;
  --scale-press:.965;
  --scale-pre:1.02;            /* 桌面按下前的预抬升 */
  --hair:0.75px;               /* 发丝描边 */
  --hair-gap:0.5px;            /* 描边与元素之间的间距 */

  /* —— 材质 —— */
  --spec-alpha:.14;            /* 径向反光强度上限 */
  --spec-size:60%;             /* 反光半径 */
  --sh-rest:0 1px 2px rgba(35,38,52,.06);
  --sh-hover:0 16px 34px -14px rgba(35,38,52,.34);

  /* —— 视差 —— */
  --par-1:.04; --par-2:.08; --par-3:.12;
  --par-max:40px;

  /* 内容栏左右内边距，跟 .wrap 保持一致（窄屏 18 / 桌面 24） */
  --wrap-pad:24px;
}
@media (max-width:640px){
  :root{--wrap-pad:18px}
}

/* 只在这台设备真的有指针时才给悬停位移；触屏走下方「按压」分支 */
@media (hover:none){
  :root{
    --lift-hover:var(--lift-hover-touch);
    --scale-hover:var(--scale-hover-touch);
    --par-1:.02; --par-2:.04; --par-3:.06;
    --par-max:20px;
  }
}

/* ───────────────────────── 一.1 连续曲率浮起 ─────────────────────────
   进 220ms / 出 180ms：靠「默认 transition 写出场、:hover 里写进场」实现，
   纯 CSS 就能拿到双向不同时长，不用 JS 记账。
   背景色一律不动，只动位移、缩放、阴影。 */
/* 位移与缩放拆成两个变量，磁吸（--mgx/--mgy）和按压（--fxscale）叠在同一条
   transform 上，谁也不覆盖谁。这是上次「磁吸压掉悬停」那类事故的通用解法。 */
.fx-float{
  --fxlift:0px; --fxscale:1; --mgx:0px; --mgy:0px;
  transform:translate3d(var(--mgx),var(--mgy),0)
            translateY(var(--fxlift))
            scale(var(--fxscale));
  transition:transform var(--t-hover-out) var(--c-in),
             box-shadow var(--t-hover-out) var(--c-in);
  box-shadow:var(--sh-rest);
  will-change:transform;
}
@media (hover:hover){
  .fx-float:hover{
    transition-duration:var(--t-hover-in);
    transition-timing-function:var(--c-out);
    --fxlift:calc(-1 * var(--lift-hover));
    --fxscale:var(--scale-hover);
    box-shadow:var(--sh-hover);
  }
}

/* ───────────────────────── 一.2 压缩式按下 ─────────────────────────
   按下 90ms 压到 .965；抬起走一次 spring 过冲（1.02 回弹）再落回。
   过冲用 keyframes 播一次，比纯 transition 更像实体按钮。 */
.btn,.ftile,.kid,.chip,.icon-btn{transform-origin:50% 50%}
@media (hover:hover){
  .btn:active,.ftile:active,.kid:active,.chip:active,.icon-btn:active{
    --fxscale:var(--scale-press);
    --fxlift:0px;
    transition:transform var(--t-press) var(--c-out) !important;
  }
}
@keyframes fxRelease{
  0%{transform:scale(var(--scale-press))}
  55%{transform:scale(var(--scale-pre))}
  100%{transform:scale(1)}
}
.fx-release{animation:fxRelease 300ms var(--c-spring) 1}

/* 触屏：按下就是唯一反馈，给 8ms 触觉 */
@media (hover:none){
  .btn,.ftile,.kid,.chip{transition:transform var(--t-press) var(--c-in)}
  .btn:active,.ftile:active,.kid:active,.chip:active{
    --fxscale:var(--scale-press); --fxlift:0px}
}

/* ───────────────────────── 一.3 材质高光跟随 ─────────────────────────
   一层径向反光，位置由 JS 每帧 lerp 推到鼠标处。
   平时 opacity 0；悬停时 200ms 淡入，离开 200ms 淡出。
   半径 60%、强度上限 .14 —— 超过就成了廉价塑料反光。 */
.fx-spec{position:relative;isolation:isolate}
.fx-spec::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:0;
  opacity:0;transition:opacity var(--t-spec) var(--c-out);
  background:radial-gradient(var(--spec-size) var(--spec-size) at var(--mx,50%) var(--my,50%),
    rgba(255,255,255,var(--spec-alpha)),rgba(255,255,255,0) 72%);
}
.fx-spec:hover::after,.fx-spec.is-dragging::after{opacity:1}
.fx-spec > *{position:relative;z-index:1}
/* 触屏：没有"跟随"，改成按下时在触点扩散一次 */
@keyframes fxFlare{
  0%{opacity:.9;background-size:0% 0%}
  100%{opacity:0;background-size:220% 220%}
}
@media (hover:none){
  .fx-spec::after{display:none}
  .fx-spec.fx-flare::before{
    content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;z-index:0;
    background:radial-gradient(circle at var(--mx,50%) var(--my,50%),
      rgba(255,255,255,var(--spec-alpha)),transparent 70%);
    background-repeat:no-repeat;background-position:center;
    animation:fxFlare 620ms var(--c-out) 1 forwards;
  }
}

/* ───────────────────────── 一.4 发丝描边生长 ─────────────────────────
   悬停 / 聚焦时一条 0.75px 的线逆时针绕一圈（320ms）；
   点击时 140ms 反向收回。
   实现：conic-gradient 旋转 + mask 只留描边那一圈，不新增 DOM。 */
@property --fx-tr{
  syntax:"<angle>";
  inherits:false;
  initial-value:0deg;
}
.fx-trace{position:relative}
.fx-trace::before{
  content:"";position:absolute;inset:calc(-1 * var(--hair-gap));
  border-radius:inherit;pointer-events:none;z-index:2;
  padding:var(--hair);
  background:conic-gradient(from var(--fx-tr,0deg),
    transparent 0deg,rgba(0,113,227,.0) 8deg,
    rgba(0,113,227,.85) 46deg,rgba(0,113,227,.85) 54deg,
    transparent 92deg,transparent 360deg);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
  mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  mask-composite:exclude;
  opacity:0;
}
@keyframes fxTraceIn{
  from{--fx-tr:0deg;opacity:0}
  1%{opacity:1}
  to{--fx-tr:360deg;opacity:1}
}
@keyframes fxTraceOut{
  from{--fx-tr:360deg;opacity:1}
  to{--fx-tr:0deg;opacity:0}
}
@media (hover:hover){
  .fx-trace:hover::before,.fx-trace:focus-visible::before{
    animation:fxTraceIn var(--t-trace) var(--c-out) 1 forwards;
  }
  .fx-trace:active::before{
    animation:fxTraceOut var(--t-trace-back) var(--c-out) 1 forwards;
  }
}
/* 触屏没有 hover：聚焦后停 100ms 再决定要不要走完 */
@media (hover:none){
  .fx-trace:focus::before{animation:fxTraceIn var(--t-trace) var(--c-out) 100ms 1 forwards}
}

/* ───────────────────────── 二.1 分层视差 ─────────────────────────
   背景三团光按 0.04 / 0.08 / 0.12 的速率差反向位移，
   同时整层压 3% 暗、加 2px 模糊 —— 制造"内容在前、背景在后"的空间感。
   位移写 CSS 变量，由 JS 每帧注入，避免逐帧改 style 字符串。 */
.bg i{transform:translate3d(0,0,0)}
.bg.fx-par .b1{transform:translate3d(0,calc(var(--py1,0) * 1px),0)}
.bg.fx-par .b2{transform:translate3d(calc(var(--px2,0) * 1px),calc(var(--py2,0) * 1px),0)}
.bg.fx-par .b3{transform:translate3d(calc(var(--px3,0) * 1px),calc(var(--py3,0) * 1px),0)}
.bg.fx-par{filter:blur(2px) brightness(.97)}
.bg.fx-par i{transition:none}

/* ───────────────────────── 二.2 渐进显现错峰 ─────────────────────────
   一次进场 600ms、上浮 24px；组内每项延后 60ms。
   超过 5 项就改成整批同时（错峰太长会让人等）。 */
@keyframes fxRise{
  from{opacity:0;transform:translate3d(0,24px,0)}
  to{opacity:1;transform:none}
}
.fx-rise{opacity:0}
.fx-rise.in{
  animation:fxRise var(--t-reveal) var(--c-out) 1 forwards;
  animation-delay:calc(var(--i,0) * 60ms);
}
@media (max-width:640px){
  .fx-rise.in{animation-duration:520ms}
  .fx-rise.in{animation-delay:calc(var(--i,0) * 40ms)}
  @keyframes fxRiseM{from{opacity:0;transform:translate3d(0,16px,0)}to{opacity:1;transform:none}}
}

/* ───────────────────── 二.3 粘性标题（已停用）─────────────────────
   页头里除了标题还有整排筛选和操作按钮，吸顶后变成 200px+ 的遮挡区，
   把内容全挡在下面，整个撤掉了 —— 页面头跟着页面一起滚走。
   真要做的话：只钉 h1 那一条 44px 细条，把 .hr 留在正常流里。 */

/* ───────────────────────── 三.3 交叉淡入切换 ─────────────────────────
   旧的 120ms 淡出，新的延迟 80ms 进场（220ms、上浮 8px）。
   容器高度用 JS 插值，避免两屏高度差把页面顶得跳一下。 */
@keyframes fxFadeOut{from{opacity:1}to{opacity:0}}
@keyframes fxFadeIn{from{opacity:0;transform:translate3d(0,8px,0)}to{opacity:1;transform:none}}
.fx-leaving{animation:fxFadeOut var(--t-fade-out) var(--c-in) 1 forwards;pointer-events:none}
.fx-entering{animation:fxFadeIn var(--t-fade-in) var(--c-out) 80ms 1 both}
@media (max-width:640px){
  @keyframes fxFadeInM{from{opacity:0;transform:translate3d(0,4px,0)}to{opacity:1;transform:none}}
  .fx-entering{animation-name:fxFadeInM}
}

/* ───────────────────────── 三.4 方向性推入 ─────────────────────────
   进更深一层（push）340ms，回上一层（pop）300ms。
   桌面不做横向位移——内容横向滑动在大屏上很廉价——改成 8px 上浮，
   但**时长与移动端对齐**，这样手感是一套的。 */
@keyframes fxPushIn{from{opacity:0;transform:translate3d(36px,0,0)}to{opacity:1;transform:none}}
@keyframes fxPopIn{from{opacity:0;transform:translate3d(-36px,0,0)}to{opacity:1;transform:none}}
@keyframes fxPushInD{from{opacity:0;transform:translate3d(0,8px,0)}to{opacity:1;transform:none}}
.fx-push{animation:fxPushIn var(--t-push) var(--c-out) 1 both}
.fx-pop{animation:fxPopIn var(--t-pop) var(--c-out) 1 both}
@media (min-width:1180px){
  .fx-push{animation-name:fxPushInD}
  .fx-pop{animation-name:fxPushInD}
}

/* ───────────────────────── 三.2 底部面板升起 ─────────────────────────
   起 420ms / 落 260ms；背后内容同时缩到 0.94 并压暗 12%。
   下滑超过 30% 或速度 0.5px/ms 就关闭（判定在 JS）。 */
.more-sheet .ms-in{
  transform:translate3d(0,100%,0);
  transition:transform var(--t-sheet-out) var(--c-in);
  will-change:transform;
}
.more-sheet.on .ms-in{
  transform:translate3d(0,0,0);
  transition:transform var(--t-sheet-in) var(--c-sheet);
}
.ms-scrim{
  position:absolute;inset:0;background:rgba(24,18,38,.12);opacity:0;
  transition:opacity var(--t-sheet-out) var(--c-in);
}
.more-sheet.on .ms-scrim{opacity:1;transition-duration:var(--t-sheet-in)}
/* 背后内容：轻微后退 + 压暗 */
body.fx-sheet-open .main{
  transform:scale(.94);transform-origin:50% 60%;
  filter:brightness(.88);
  transition:transform var(--t-sheet-in) var(--c-sheet),filter var(--t-sheet-in) var(--c-sheet);
}
body:not(.fx-sheet-open) .main{
  transition:transform var(--t-sheet-out) var(--c-in),filter var(--t-sheet-out) var(--c-in);
}
/* 拖拽中：圆角收窄，暗示"这块能脱离" */
.more-sheet.is-drag .ms-in{border-radius:12px 12px 0 0;transition:border-radius 180ms var(--c-out)}

/* ───────────────────────── 四.2 抽屉拖拽关闭 ─────────────────────────
   1:1 跟手；往上拖有 0.9 的阻尼（越拉越沉）。 */
.more-sheet.is-drag .ms-in{transition:none}

/* ───────────────────────── 五.1 磁性吸附 ─────────────────────────
   感应区外扩 24px（JS 用一张虚拟网格判定），位移上限 8px，
   文字做 40% 的反相视差，看起来"按钮在跟手、字在滞后"。 */
/* .fx-mag 只负责写变量，不碰 transform —— transform 归 .fx-float 统一定义 */
.fx-mag{--mgcx:0px;--mgcy:0px}
.fx-mag > *{transform:translate3d(var(--mgcx),var(--mgcy),0);
  transition:transform 260ms var(--c-out)}
.fx-mag.fx-following{transition:transform 60ms linear}
.fx-mag.fx-following > *{transition:transform 60ms linear}
.fx-leaving-mag{transition:transform 520ms var(--c-spring) !important}

/* ───────────────────────── 五.3 邻近高亮 ─────────────────────────
   一套 rAF 里按指针到各元素的距离分档：最近 1.03、其次 0.9、再远 0.62。
   opacity 用 180ms 过渡，缩放由 JS 每帧 lerp 0.16。 */
.fx-near{transition:transform 180ms var(--c-out),opacity 180ms var(--c-out)}
.fx-near.fx-near-on{will-change:transform,opacity}
@media (hover:none){
  .fx-near{transition:none}
  .fx-near.fx-near-on{transform:none}
  .fx-near.fx-near-on.fx-poke{transform:scale(1.015)}
}

/* ───────────────────────── 六.1 开关三段 ─────────────────────────
   轨道 51×31、滑块 27、开色 #34C759；轨道色 200ms、滑块 260ms、图标 160ms。
   两端各留 44×44 的命中区（用 ::after 撑大，不改变视觉尺寸）。 */
.sw{
  width:51px;height:31px;border-radius:980px;padding:0;
  background:rgba(50,35,80,.16);
  transition:background-color 200ms var(--c-out),box-shadow 200ms var(--c-out);
}
.sw::after{
  top:2px;left:2px;width:27px;height:27px;
  box-shadow:0 1px 3px rgba(0,0,0,.28),0 0 0 .5px rgba(0,0,0,.04);
  transition:transform var(--t-switch) var(--c-sheet),width 180ms var(--c-out);
}
.sw.on{background:#34C759;box-shadow:0 2px 8px -4px rgba(52,199,89,.9)}
.sw.on::after{transform:translateX(20px)}
.sw:active::after{width:29px}
.sw.on:active::after{transform:translateX(18px);width:29px}
/* 44×44 命中区 */
.sw{position:relative}
.sw::before{content:"";position:absolute;left:50%;top:50%;width:44px;height:44px;
  margin:-22px 0 0 -22px;border-radius:50%}
.sw.sm{width:51px;height:31px}
.sw.sm::after{top:2px;left:2px;width:27px;height:27px}
.sw.sm.on::after{transform:translateX(20px)}

/* 开关里的图标：开 160ms 淡入 */
.sw .sw-ic{transition:opacity 160ms var(--c-out),transform 160ms var(--c-out)}

/* ───────────────────────── 六.3 内联校验 ─────────────────────────
   抖 3 次 × 60ms、±4px；边框 140ms 转红；错误文案 180ms 展开。
   只染边框和图标，不整块变红——那是报错，不是判决。 */
@keyframes fxShake{
  0%,100%{transform:translate3d(0,0,0)}
  16%{transform:translate3d(-4px,0,0)}
  33%{transform:translate3d(4px,0,0)}
  50%{transform:translate3d(-3px,0,0)}
  66%{transform:translate3d(3px,0,0)}
  83%{transform:translate3d(-1px,0,0)}
}
.fx-shake{animation:fxShake 180ms var(--c-shake) 3}
.fx-invalid,.fx-invalid:hover,.fx-invalid:focus{
  border-color:rgba(199,54,44,.75) !important;
  box-shadow:0 0 0 3px rgba(199,54,44,.1) !important;
  transition:border-color 140ms var(--c-out),box-shadow 140ms var(--c-out) !important;
}
.fx-errbox{
  overflow:hidden;max-height:0;opacity:0;
  transition:max-height 180ms var(--c-out),opacity 180ms var(--c-out),margin 180ms var(--c-out);
  margin:0;font-size:12px;color:var(--red-text);
}
.fx-errbox.on{max-height:56px;opacity:1;margin:5px 0 0}

/* ───────────────────────── 六.4 数值滚动 ─────────────────────────
   每位单独 420ms 进场、错峰 30ms；变化量大于 1 时先亮 80ms 再落。
   全部 tabular-nums，避免数字跳动把宽度带着抖。 */
.fx-num{font-variant-numeric:tabular-nums;display:inline-block}
.fx-num i{font-style:normal;display:inline-block;white-space:pre}
@keyframes fxDigitIn{
  from{opacity:0;transform:translate3d(0,-.42em,0)}
  to{opacity:1;transform:none}
}
.fx-num i{animation:fxDigitIn 420ms var(--c-out) 1 both;
  animation-delay:calc(var(--i,0) * 30ms)}
.fx-num.fx-hot{color:var(--blue)}
.fx-num.fx-hot i{transition:color 80ms linear}
@media (max-width:640px){
  .fx-num i{animation-duration:360ms;animation-delay:calc(var(--i,0) * 20ms)}
}

/* ───────────────────────── 统一降级 ─────────────────────────
   减少动态：位移/缩放/描边/视差全部关闭，只留颜色与透明度。
   这一条必须放在最后，压住上面所有规则。 */
@media (prefers-reduced-motion:reduce){
  .fx-float,.fx-float:hover,
  .btn,.btn:hover,.btn:active,.ftile,.ftile:hover,.kid,.kid:hover,
  .chip,.chip:hover,.icon-btn,.icon-btn:hover,
  .fx-mag,.fx-mag > *,.fx-near,.fx-near.fx-near-on{transform:none !important}
  .fx-trace::before{display:none !important}
  .fx-spec::after{display:none !important}
  .bg.fx-par{filter:none !important}
  .bg.fx-par i{transform:none !important}
  .fx-rise,.fx-rise.in{animation:none !important;opacity:1 !important}
  .fx-leaving,.fx-entering,.fx-push,.fx-pop,
  .fx-release,.fx-shake{animation:none !important}
  .more-sheet .ms-in,body.fx-sheet-open .main{transform:none !important;filter:none !important}
  .fx-num i{animation:none !important}
  body.fx-sheet-open .main{filter:none !important}
}

/* ───────── 统计卡（本周事件 / 材料完成 / 待办清掉）─────────
   这三张以前是"固定信息块"，鼠标划过纹丝不动；现在点一下能进对应页面，
   所以给它们和其它可点元素同一套浮起 + 高光 + 按压。 */
.ukpi.is-go b i{
  font-style:normal;font-size:.62em;font-weight:500;
  color:var(--ink3);margin-left:3px;letter-spacing:0;
}
.ukpi.is-go{
  transition:transform var(--t-hover-out) var(--c-in),
             box-shadow var(--t-hover-out) var(--c-in);
}
@media (hover:hover){
  .ukpi.is-go:hover{
    transition-duration:var(--t-hover-in);
    transition-timing-function:var(--c-out);
  }
}
.ukpi.is-go:active{--fxscale:.97}
@media (hover:none){
  .ukpi.is-go:active{transform:scale(.97)}
}

/* ───────── 二.4 章节进度（随吸顶一起停用）─────────
   进度线原本长在吸顶页头的底边上，页头不吸顶后没有承载物了。
   硬做成 viewport 顶部的固定线的话，会被 iOS 的浏览器工具栏压住，反而更糟。 */

/* ═════════ 统计卡的跳转箭头（.uarrow）═════════
   ⚠️ 这一段单独成块、放在文件最末，别塞进其它 section 里被误删。
   SVG 没有 width/height 属性时默认按容器铺满，一旦丢了尺寸就会撑成巨型箭头。 */
.ukpi.is-go{position:relative;padding-right:26px;cursor:pointer}
.ukpi.is-go .uarrow{
  position:absolute;right:11px;top:13px;
  width:9px;height:13px;flex:none;
  color:var(--ink3);opacity:.3;
  transition:opacity 200ms var(--c-out),transform 200ms var(--c-out);
  pointer-events:none;
}
.ukpi.is-go .uarrow svg{display:block;width:9px;height:13px}
@media (hover:hover){
  .ukpi.is-go:hover .uarrow{opacity:.65;transform:translateX(2px)}
}
.ukpi.is-go:active,
.ukpi.is-go.fx-pressed{
  --fxscale:.955;
  box-shadow:0 0 0 1px rgba(0,113,227,.22),0 8px 20px -14px rgba(0,113,227,.6) !important;
  background:rgba(255,255,255,.92);
  transition:transform 90ms var(--c-out),box-shadow 90ms var(--c-out),
             background-color 90ms var(--c-out) !important;
}
.ukpi.is-go:active .uarrow,
.ukpi.is-go.fx-pressed .uarrow{opacity:.85;transform:translateX(2px)}
@media (hover:none){
  .ukpi.is-go:active,
  .ukpi.is-go.fx-pressed{transform:scale(.955)}
}

/* 这一段永远放在文件最末：统计卡在「减少动态」下也不许位移 */
@media (prefers-reduced-motion:reduce){
  .ukpi.is-go,.ukpi.is-go:active,.ukpi.is-go.fx-pressed{transform:none!important}
  .ukpi.is-go:hover .uarrow,.ukpi.is-go.fx-pressed .uarrow{transform:none}
}
