/* ==========================================================================
   动效与手势样式（anim-gesture.css）
   --------------------------------------------------------------------------
   与 app.css 并列加载，作用域用 .anim-* / .card-stack / .stack-card 等独立类名，
   不改动 app.css 的既有选择器。所有动效尊重 prefers-reduced-motion。
   ========================================================================== */

/* ---------- 卡片堆叠（阶段一推荐态） ---------- */
.card-stack {
  position: relative;
  width: 100%;
  /* 所有卡都是绝对定位，容器必须给显式高度，否则会塌成 0 */
  height: 46vh;
  /* 堆叠容器本身不拦截拖拽事件，事件落在最前卡上 */
  /* specs/012 W4：飞出卡靠 transform 推到 innerWidth+200 的位置，
     绝对定位 + 变换的后代会计入祖先的可滚动溢出区 —— 实测拖动中
     documentElement.scrollWidth 从 927 涨到 1156，整页被撑开。
     用 overflow-x: clip 断掉这条传递：clip 不建立滚动容器（不像 hidden
     那样会把这里变成可滚动区、从而抢走 pointer 事件语义），
     也不影响 y 轴上背面卡的 scale/位移阴影。 */
  overflow-x: clip;
}

/* 每张卡绝对定位叠在容器里 */
.stack-card {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-4);
  text-align: center;
  background: var(--paper);
  border: 1px solid var(--line-soft);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-paper);
  /* 背后两张卡的位移由下面的 data-depth 决定 */
  transition: transform 0.28s var(--ease), box-shadow 0.28s var(--ease);
  will-change: transform;
  touch-action: none; /* 拖拽时不触发浏览器滚动手势 */
  user-select: none;
  -webkit-user-select: none;
}

/* 深度：0=最前，1/2=背景。后一张比前一张略小、略沉 */
.stack-card[data-depth='0'] { z-index: 3; transform: translate(0, 0) scale(1); }
.stack-card[data-depth='1'] { z-index: 2; transform: translate(0, 10px) scale(0.95); pointer-events: none; }
.stack-card[data-depth='2'] { z-index: 1; transform: translate(0, 20px) scale(0.9); pointer-events: none; }

/* 拖拽中：关闭过渡，transform 完全跟手 */
.stack-card.is-dragging {
  transition: none;
  cursor: grabbing;
  box-shadow: var(--shadow-lift);
}

/* 飞出中：也不补间（rAF 驱动） */
.stack-card.is-flying-left,
.stack-card.is-flying-right {
  transition: none;
  pointer-events: none;
}

/* 接受时的上移动画（与旧 .quest-lift 同族，但作用在堆叠卡上） */
.stack-card.is-lifting {
  animation: anim-card-lift 0.6s var(--ease) forwards;
}
@keyframes anim-card-lift {
  0% { opacity: 1; transform: translateY(0) scale(1); }
  100% { opacity: 0; transform: translateY(-30vh) scale(0.5); }
}

/* 卡内族标签 */
.stack-card__family {
  font-size: var(--fs-tiny);
  letter-spacing: 0.12em;
  color: var(--gold);
  text-transform: uppercase;
}

/* 落点预览：拖到阈值一半时在目标方向亮起的半透明占位 */
.landing-preview {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 2px dashed var(--line);
  border-radius: var(--r-card);
  background: color-mix(in srgb, var(--paper) 60%, transparent);
  opacity: 0;
  transition: opacity 0.15s var(--ease);
  pointer-events: none;
  z-index: 0;
}
.landing-preview[hidden] { display: none; }
.landing-preview--right { border-color: var(--pine); }
.landing-preview--left { border-color: var(--vermillion); }
.landing-preview__mark {
  font-size: var(--fs-small);
  color: var(--ink-soft);
}

/* 剩余数量计数器 */
.stack-counter {
  text-align: center;
  font-size: var(--fs-tiny);
  color: var(--ink-faint);
  margin-top: var(--sp-3);
}

/* ---------- 安全按钮按压态 ---------- */
/* safeButton 在 pointerdown 时加这个类，即时反馈 */
.is-pressed {
  transform: scale(0.97);
  transition: transform 0.08s var(--ease);
}

/* ---------- 侧滑返回时的页面 ---------- */
/* 跟手期间关闭 .view 的入场动画与过渡，否则 rise 动画会和 transform 打架 */
.view.is-swiping {
  animation: none !important;
  transition: none !important;
}

/* ---------- 底部导航 SVG 图标 ---------- */
.tab .i-svg {
  display: block;
  line-height: 1;
}

/* ==========================================================================
   本轮新增：12 项高级交互的样式层（JS 驱动的部分见 engine/gesture.js、
   completion.js、views/profile.js、views/quest.js）。
   命名统一 fx- 前缀，不覆盖 app.css / anim-views.css 的既有选择器。
   ========================================================================== */

/* ---------- 1/2. 弹簧按压 + 水波按钮 ----------
 * safeButton 用 rAF 直接写 transform，动画期间必须关掉 CSS transition，
 * 否则 CSS 补间和 spring 打架。
 */
.btn { position: relative; overflow: hidden; }
.is-spring-press { transition: none !important; }

.fx-ripple {
  position: absolute;
  left: var(--rx, 50%);
  top: var(--ry, 50%);
  width: 12px; height: 12px;
  margin: -6px 0 0 -6px;
  border-radius: 50%;
  /* 深按钮 color=纸色、浅按钮 color=墨色，波纹自动取反色 */
  background: currentColor;
  opacity: 0.22;
  transform: scale(0);
  pointer-events: none;
  z-index: 0;
  animation: fx-ripple-grow 0.5s var(--ease) forwards;
}
/* 文字/图标排在波纹之上 */
.btn > * { position: relative; z-index: 1; }
@keyframes fx-ripple-grow {
  to { transform: scale(var(--scale, 20)); opacity: 0; }
}

/* ---------- 4. 弹性开关（档案页匿名统计） ----------
 * 轨道是死的，滑块 .fx-switch__knob 的 translateX 由 spring.js 驱动
 * （低 damping 过冲落位）。这里只给轨道与滑块的静态形。
 */
.fx-switch {
  position: relative;
  flex: none;
  width: 46px; height: 28px;
  padding: 0;
  border-radius: 999px;
  background: var(--line);
  border: 1px solid var(--line);
  cursor: pointer;
  transition: background-color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.fx-switch[aria-pressed="true"] {
  background: var(--pine);
  border-color: var(--pine);
}
.fx-switch__knob {
  position: absolute;
  top: 2px; left: 2px;
  width: 22px; height: 22px;
  border-radius: 50%;
  background: var(--paper);
  box-shadow: 0 1px 3px rgba(31, 43, 36, 0.25);
  will-change: transform;
}

/* ---------- 6. 成功勾选：对勾逐笔绘制 ----------
 * 一条连续 path（先竖钩下撇、再右上挑），dashoffset 从全长收到 0。
 */
.fx-check {
  width: 52px; height: 52px;
  stroke: var(--gold);
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
  fill: none;
}
/* 完成覆盖层里：对勾绝对居中，在票根卡片浮现前画出，随后淡出 */
.fx-check-wrap {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  transition: opacity 0.3s var(--ease);
}
.fx-check__path {
  stroke-dasharray: 42;
  stroke-dashoffset: 42;
}
.fx-check.is-on .fx-check__path {
  animation: fx-check-draw 0.45s var(--ease) forwards;
}
@keyframes fx-check-draw {
  to { stroke-dashoffset: 0; }
}

/* ---------- 8. 骨架加载：尺寸与真实内容一致，微光扫过 ---------- */
.fx-skeleton {
  position: relative;
  overflow: hidden;
  background: var(--paper-3, var(--line-soft));
  border-radius: 6px;
}
.fx-skeleton::after {
  content: '';
  position: absolute; inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55), transparent);
  animation: fx-shimmer 1.2s var(--ease) infinite;
}
@keyframes fx-shimmer {
  to { transform: translateX(100%); }
}
/* 图位骨架：锁死与海报一致的比例，img 来了直接盖上去不跳 */
.fx-skeleton--img { aspect-ratio: 600 / 900; width: 100%; }
/* 数字位骨架：一行灰条，宽高贴近真实数字 */
.fx-skeleton--num { display: inline-block; width: 3.2em; height: 1em; vertical-align: baseline; }

/* ---------- 10. 堆叠补位：新顶层卡从下层位置升上来 ---------- */
.stack-card.is-entering {
  animation: fx-stack-rise 0.28s var(--ease);
}
@keyframes fx-stack-rise {
  from { transform: translateY(10px) scale(0.95); }
  to { transform: translateY(0) scale(1); }
}

/* ---------- 11. 底部抽屉：关键帧过冲落定，帮用户识别"到底了" ----------
 * .sheet 的关闭仍走 app.css 的 transform transition（下滑）；
 * 打开时用 animation 盖过 transition，冲到 -6px 再吸回 0。
 */
.sheet.is-open {
  animation: fx-sheet-rise 0.42s var(--ease) both;
}
@keyframes fx-sheet-rise {
  0%   { transform: translateY(101%); }
  85%  { transform: translateY(-6px); }
  100% { transform: translateY(0); }
}

/* ---------- 减少动效：所有动画压到 0.01s ---------- */
@media (prefers-reduced-motion: reduce) {
  .stack-card,
  .stack-card.is-dragging,
  .stack-card.is-flying-left,
  .stack-card.is-flying-right {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
  }
  .landing-preview { transition-duration: 0.01ms !important; }
  .is-pressed { transition-duration: 0.01ms !important; }

  /* 本轮新增：水波/对勾/骨架微光/抽屉过冲/堆叠升位，全部降级为静态 */
  .fx-ripple { animation: none !important; opacity: 0; }
  .fx-check__path { animation: none !important; stroke-dashoffset: 0; }
  .fx-skeleton::after { animation: none !important; opacity: 0; }
  .sheet.is-open { animation: none !important; }
  .stack-card.is-entering { animation: none !important; }
  .fx-switch { transition-duration: 0.01ms !important; }
}

/* ===== specs/018 收集库可交互叠卡（板正叠放 + 分层阴影质感） =====
   折叠：卡片板正叠成一摞（负 margin 重叠、不倾斜），整摞 inert，上面盖一个展开热区。
   展开：点热区 → 卡片从斜摞“扶正摊平”飞入各自槽位（lib-spread）。静止态平铺可读（v1.2）。
   不搬 demo 的紫蓝渐变：卡片仍用族色竖条 + 米白纸底；只借几何与柔影。 */
.lib-pile-wrap { position: relative; padding-bottom: 52px; }
/* 板正的一叠卡：最前一张完整可读，后面的卡只露下方一条纸边（内容隐藏、逐层微缩下沉），
   像桌上一摞整齐的票根。整摞可点=展开；提示 pill 落在纸叠下方留白里，不压内容。 */
.is-stacked .lib-item:first-child {
  position: relative; z-index: 4;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.55) inset,
              0 8px 18px rgba(31, 43, 36, 0.12),
              0 18px 36px rgba(31, 43, 36, 0.16);
}
.is-stacked .lib-item:not(:first-child) {
  position: absolute; left: 0; right: 0; top: 0; pointer-events: none;
  transform-origin: 50% 0;
}
.is-stacked .lib-item:not(:first-child) .lib-main,
.is-stacked .lib-item:not(:first-child) .lib-del,
.is-stacked .lib-item:not(:first-child) .lib-bar { visibility: hidden; }
.is-stacked .lib-item:nth-child(2) { transform: translateY(9px) scale(0.985); z-index: 3; }
.is-stacked .lib-item:nth-child(3) { transform: translateY(18px) scale(0.97); z-index: 2; }
.is-stacked .lib-item:nth-child(n+4) { transform: translateY(26px) scale(0.955); z-index: 1; }
.lib-pile-expand {
  position: absolute; inset: 0; z-index: 6;
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0 0 6px; border: 0; background: transparent; cursor: pointer;
}
.lib-pile-hint {
  font-size: var(--fs-tiny, 12px); color: var(--paper, #f6f2e9);
  background: var(--pine, #2f4f47); padding: 7px 16px; border-radius: 999px;
  box-shadow: 0 4px 12px rgba(31, 43, 36, 0.22);
}
.lib-pile-collapse { align-self: flex-start; margin-bottom: var(--sp-2); }

/* 展开那一次：卡片从斜摞姿态飞入槽位、逐张错峰 */
.anim-lib-spread .lib-item {
  transform-origin: 50% 0;
  animation: lib-spread 0.46s cubic-bezier(0.22, 0.61, 0.36, 1) both;
}
.anim-lib-spread .lib-item:nth-child(1) { --lift: 0px; }
.anim-lib-spread .lib-item:nth-child(2) { --lift: 74px;  animation-delay: 70ms; }
.anim-lib-spread .lib-item:nth-child(3) { --lift: 148px; animation-delay: 140ms; }
.anim-lib-spread .lib-item:nth-child(4) { --lift: 222px; animation-delay: 210ms; }
.anim-lib-spread .lib-item:nth-child(5) { --lift: 296px; animation-delay: 280ms; }
.anim-lib-spread .lib-item:nth-child(n+6) { --lift: 340px; animation-delay: 340ms; }
@keyframes lib-spread {
  0%   { transform: translateY(calc(-1 * var(--lift, 0px))); opacity: 0 }
  55%  { opacity: 1 }
  100% { transform: none; opacity: 1 }
}
@media (prefers-reduced-motion: reduce) {
  .anim-lib-spread .lib-item { animation: none }
  .is-stacked .lib-item { transition: none }
}

/* ===== specs/017 #1「完成了」按钮微交互：按下→轻弹→对勾 =====
   只做 scale 轻弹 + 换字，不做“宽度收成圆”（btn-block width:100% 会跳位，v1.2 求稳）。 */
.btn.is-success-morph {
  transform: scale(1.06);
  transition: transform 0.26s cubic-bezier(0.34, 1.56, 0.64, 1);
}
@media (prefers-reduced-motion: reduce) {
  .btn.is-success-morph { transform: none; transition: none; }
}
