/* ============================================================================
 *  L-api 交互增强层  ·  l-api-effects.css
 * ----------------------------------------------------------------------------
 *  配套文件：l-api-effects.js（只负责「打标 / 写变量」）、l-api-skin.css（配色）
 *
 *  ▍设计原则（照抄 SOFT LAB 的架构，但换了用途）
 *    1. JS 不碰动画，只负责给元素加类名、往元素上写 CSS 变量；
 *       所有位移/缩放/透明度一律由 CSS transition / @keyframes 执行。
 *       → 掉帧时可被合成器接管，且断 JS 后页面依然完整可用。
 *    2. 颜色一律从皮肤变量派生（--primary / --card / --foreground …），
 *       不写死任何色值 → 亮暗两套主题自动跟随，改主色不用改本文件。
 *    3. 只用 scale / translate / rotate / opacity 这些「独立变换属性」，
 *       不用 transform 复合属性 —— 避免冲掉应用自己写的 transform（Tailwind 里到处都是）。
 *    4. 选择器一律包在 :where() 里（特异性 0），应用自己的样式永远优先。
 *       本文件是「兜底 + 增强」，不是覆盖。
 *    5. 任何位移/缩放都必须能在 prefers-reduced-motion: reduce 下退化成
 *       「只有颜色和透明度变化」。
 *
 *  ▍时长 / 缓动令牌
 *    借自参考站点的 9 档时长 + 6 条曲线，但砍掉了 600ms 以上的三档 ——
 *    那是展示站用的，工具站超过 400ms 就开始「拖」。
 * ========================================================================= */

/* ============================ 0 · 令牌 ============================ */
:root {
  --mo-dur-instant: 0.1s;
  --mo-dur-quick:   0.2s;
  --mo-dur-fast:    0.3s;
  --mo-dur-base:    0.4s;

  --mo-ease-standard: cubic-bezier(0.4, 0, 0.2, 1);   /* 通用 */
  --mo-ease-decel:    cubic-bezier(0.22, 1, 0.36, 1);  /* 进场减速 */
  --mo-ease-spring:   cubic-bezier(0.34, 1.56, 0.64, 1); /* 会过冲，回弹用 */
  --mo-ease-draw:     cubic-bezier(0.34, 0, 0.06, 1);  /* 描线用 */

  /* 派生色：全部来自皮肤，本文件不出现任何十六进制色值 */
  --mo-wash:  color-mix(in oklab, var(--primary) 7%, transparent);
  --mo-lift:  0 6px 20px -8px color-mix(in oklab, var(--foreground) 26%, transparent);
  --mo-glow:  0 0 0 3px color-mix(in oklab, var(--ring, var(--primary)) 30%, transparent);
}

/* ==================== 1 · 按压回弹（Press Scale） ====================
 * 参考：B-07 Press Scale
 * 场景：所有按钮 / 可点元素。最便宜、收益最广的一条。
 * 实现：hover 微抬，:active 缩到 0.97，松手由 spring 曲线弹回（多弹一下）。
 * 注意：用独立属性 scale，不写 transform —— 不会和 Tailwind 的
 *       translate-* / rotate-* 打架。
 *
 * ⚠️ 为什么目标是 .lapi-press 而不是 button / [role="button"]：
 *    有的组件库把 role="button" 挂在整个卡片容器上，纯 CSS 分不清
 *    「按钮」和「一大块可点区域」，一 hover 整块放大就废了。
 *    所以由 JS 按尺寸（≤360 × ≤88）打标，CSS 只管动画。
 *
 * ⚠️ 基础态刻意不写 `scale: 1` —— 只要 scale 不是 none，
 *    元素就会成为 fixed/absolute 后代的包含块，按钮里如果有弹出层会被带偏。
 *    只在 hover / active 时给值，离开时回到 none（按规范 none 等价于 1，
 *    过渡依然平滑）。 */
.lapi-press {
  transition:
    scale var(--mo-dur-quick) var(--mo-ease-spring),
    box-shadow var(--mo-dur-quick) var(--mo-ease-standard),
    background-color var(--mo-dur-quick) var(--mo-ease-standard);
}
.lapi-press:not([disabled]):not([aria-disabled="true"]):hover {
  scale: 1.015;
}
.lapi-press:not([disabled]):not([aria-disabled="true"]):active {
  scale: 0.97;
  transition-duration: var(--mo-dur-instant);
}

/* ==================== 2 · 焦点环（Focus Ring） ====================
 * 参考：参考站点没有这一条 —— 但它才是工具站最该先做的一条。
 * 场景：键盘 Tab 导航。低特异性，应用自带的焦点样式永远优先。 */
:where(button, a, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--ring, var(--primary));
  outline-offset: 2px;
  box-shadow: var(--mo-glow);
}

/* ==================== 3 · 表格行指示（Row Marker） ====================
 * 参考：B-01 Fill Sweep 的「弱化 + 静态化」版
 * 场景：渠道表 / 令牌表 / 日志表。原效果是色块从左铺满，在表格里太吵；
 *       改成左侧 3px 主色内阴影 + 底色微亮，用来「锁定当前行」。
 * 实现：inset box-shadow，不用伪元素 —— tr 上加 position:relative
 *       在部分浏览器会破坏表格布局，这个坑绕开。 */
:where(table tbody tr, [role="row"]):not(.lapi-no-mo) {
  transition:
    box-shadow var(--mo-dur-fast) var(--mo-ease-standard),
    background-color var(--mo-dur-fast) var(--mo-ease-standard);
}
:where(table tbody tr, [role="row"]):not(.lapi-no-mo):hover {
  box-shadow: inset 3px 0 0 0 var(--primary);
  background-color: var(--mo-wash);
}

/* ==================== 4 · 卡片上浮（Card Lift） ====================
 * 场景：概览页统计卡、渠道卡。
 * 由 JS 打标 .lapi-card 后生效（JS 会做尺寸/内容校验，
 * 避免把整块内容面板也当成卡片一起抬起来）。 */
.lapi-card {
  transition:
    translate var(--mo-dur-fast) var(--mo-ease-decel),
    box-shadow var(--mo-dur-fast) var(--mo-ease-decel);
}
.lapi-card:hover {
  translate: 0 -2px;
  box-shadow: var(--mo-lift);
}

/* ==================== 5 · 忙碌扫光（Busy Sheen） ====================
 * 参考：B-14 Success Morph 的「处理中」段
 * 场景：点「测试渠道」「保存」「重载」之后按钮被禁用，用户不知道要等多久。
 * 关键设计：不拦截业务逻辑，只在 JS 判定「刚被点过 → 现在变 disabled」时
 *          才加 .lapi-busy。所以表单里天然禁用的按钮不会一直闪。 */
.lapi-busy {
  position: relative;
  overflow: hidden;
  cursor: progress;
}
.lapi-busy::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(
    100deg,
    transparent 25%,
    color-mix(in oklab, var(--primary-foreground) 32%, transparent) 50%,
    transparent 75%
  );
  translate: -130% 0;
  animation: lapi-sheen 1.3s var(--mo-ease-standard) infinite;
}
@keyframes lapi-sheen {
  to { translate: 130% 0; }
}

/* ==================== 6 · 复制成功（Copy → Check） ====================
 * 参考：B-14 Success Morph
 * 场景：复制 API Key / 令牌 / 模型名。全站最高频操作，最值得做。
 * 实现：JS 在按钮上叠一层 .lapi-check，对勾靠 stroke-dashoffset 描出来。 */
.lapi-check {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: inherit;
  background: var(--success, var(--primary));
  color: var(--success-foreground, var(--primary-foreground));
  pointer-events: none;
  animation: lapi-check-in var(--mo-dur-base) var(--mo-ease-spring);
  z-index: 2;
}
.lapi-check svg {
  width: 1.05em;
  height: 1.05em;
}
.lapi-check svg path {
  stroke-dasharray: 26;
  stroke-dashoffset: 26;
  animation: lapi-draw var(--mo-dur-base) var(--mo-ease-draw) 0.06s forwards;
}
@keyframes lapi-check-in {
  from { opacity: 0; scale: 0.86; }
  to   { opacity: 1; scale: 1; }
}
@keyframes lapi-draw {
  to { stroke-dashoffset: 0; }
}

/* ==================== 7 · 数字滚动（Count Up） ====================
 * 场景：概览页「今日调用 / 消耗额度 / 渠道数」。
 * 参考站点里没有这一类 —— 它是展示站，缺的正是仪表盘动效，
 * 这也是你比它更该补的一块（详见方案文档 §3.4）。 */
.lapi-count {
  display: inline-block;
  font-variant-numeric: tabular-nums; /* 关键：等宽数字，滚动时不抖 */
  transition: color var(--mo-dur-base) var(--mo-ease-standard);
}
.lapi-count.is-rolling {
  color: var(--primary);
}

/* ==================== 8 · 骨架淡入（Skeleton Fade） ====================
 * 场景：表格/图表数据到位时，避免「刷」地一下硬切。 */
.lapi-fade-in {
  animation: lapi-fade var(--mo-dur-base) var(--mo-ease-decel);
}
@keyframes lapi-fade {
  from { opacity: 0; translate: 0 4px; }
  to   { opacity: 1; translate: 0 0; }
}

/* ==================== 9 · 破坏性操作蓄力（Hold to Confirm） ====================
 * 参考：B-08 Elastic Stretch（按住蓄力、松手弹出去）
 * 场景：删除渠道 / 删除令牌。把「一下就删掉」改成「按住 0.9 秒」。
 * ⚠️ 这是本文件里唯一会改变操作语义的一条，默认关闭，
 *    必须给元素加 .lapi-hold 才生效，且 click 通路保持不变（长按是额外路径，
 *    不是唯一路径）—— 否则会直接破坏键盘可访问性。
 *
 * 实现：蓄力条用 background-image 铺在按钮自己的背景色之上、文字之下，
 *      靠 background-size 从 0% 走到 100%。
 *      ⚠️ 不能用伪元素 + z-index:-1 —— 那会被按钮自身的背景色盖住，
 *         而且是静默失效（看起来「没生效」，其实是被挡住了）。
 * 用法：JS 给目标加 .lapi-hold，pointerdown 起计时 900ms 后触发原生 click。
 *
 * ⚠️ 选择器写成 .lapi-hold.lapi-hold（特异性 0,2,0）而不是 .lapi-hold ——
 *    应用自己的 `.b-danger { background: ... }` 这类**简写属性**会把
 *    background-image / background-size 一起重置，特异性相同的情况下
 *    后出现的简写会赢，蓄力条就静默消失了。这里沿用皮肤文件的 `:root:root` 同款手法。 */
.lapi-hold.lapi-hold {
  --hold-fill: color-mix(in oklab, var(--destructive) 82%, transparent);
  background-image: linear-gradient(var(--hold-fill), var(--hold-fill));
  background-repeat: no-repeat;
  background-position: left center;
  background-size: 0% 100%;
}
.lapi-hold.lapi-hold.is-charging {
  background-size: 100% 100%;
  transition: background-size 0.9s linear;
  animation: lapi-charge var(--mo-dur-base) var(--mo-ease-spring);
}
.lapi-hold.lapi-hold.is-held {
  background-size: 0% 100%;
  transition: none;
}
@keyframes lapi-charge {
  0%   { scale: 1; }
  45%  { scale: 0.965; }
  100% { scale: 1; }
}

/* ==================== 10 · 可访问性降级 ====================
 * 参考站点也在做同一件事（其 CSS 里有 --mo-magnet-max: 0px 的降级分支）。
 * 规则：只保留颜色/透明度变化，全部位移与缩放归零。 */
@media (prefers-reduced-motion: reduce) {
  .lapi-press,
  .lapi-card,
  .lapi-hold.lapi-hold {
    transition-duration: 0.01ms !important;
    animation: none !important;
  }
  .lapi-press:hover,
  .lapi-press:active,
  .lapi-card:hover {
    scale: 1;
    translate: 0 0;
  }
  .lapi-hold.lapi-hold.is-charging {
    transition: none;
  }
  .lapi-busy::after {
    animation: none;
    translate: 0 0;
    opacity: 0.25;
  }
  .lapi-check svg path {
    stroke-dashoffset: 0;
    animation: none;
  }
  .lapi-fade-in,
  .lapi-check {
    animation: none;
  }
}

/* ==================== 11 · 校准模式（?lapi-inspect=1） ====================
 * 上线前用它确认「JS 到底标中了哪些元素」。
 * 打开方式：https://9178.tyu.me/?lapi-inspect=1 */
html[data-lapi-inspect] .lapi-card { outline: 2px dashed #f59e0b; outline-offset: 2px; }
html[data-lapi-inspect] .lapi-count { outline: 2px dashed #8b5cf6; outline-offset: 2px; }
html[data-lapi-inspect] .lapi-busy { outline: 2px solid #10b981; outline-offset: 2px; }
html[data-lapi-inspect] .lapi-press { outline: 2px dashed #ec4899; outline-offset: 2px; }
html[data-lapi-inspect] :where(table tbody tr, [role="row"]) { outline: 1px dotted #0ea5e9; }
html[data-lapi-inspect] :where(button, [role="button"], [role="tab"]) { outline: 1px dotted #94a3b8; }
html[data-lapi-inspect]::after {
  content: "LAPI 动效校准模式 · 橙=卡片 紫=数字 绿=忙碌 粉=按压 蓝=行 灰=按钮";
  position: fixed;
  left: 50%;
  bottom: 12px;
  translate: -50% 0;
  z-index: 99999;
  padding: 6px 12px;
  border-radius: 999px;
  font: 12px/1.4 ui-monospace, Consolas, monospace;
  background: #111827;
  color: #f9fafb;
  pointer-events: none;
}
