/* ============================================================
   Textmolt System Layer — 全局横切规则（D 路 2026-07-02）
   加载顺序: 最后一个 <link>（覆盖语义靠源顺序）
   区块: 1.reduced-motion 全局层  2.焦点基线
         3.动画暂停纪律          4.警示有尽头 + 签名时刻
         5.Glass 材质工具类 + 降级
   Spec: docs/plans/FRONTEND_UIUX_OVERHAUL_2026-07-02/04_PLAN_DESIGN_SYSTEM.md
   ============================================================ */

/* ────────────────────────────────────────────────────────────
   1) Reduced Motion 全局层（§2.4 家族规则的兜底实现）
   技术要点:
   - 用 0.01ms 压缩而非 animation:none —— 保证 animationend /
     transitionend 事件仍触发，不卡 morphing/baton 等 JS 状态机。
   - iteration-count:1 终结所有 infinite。
   - 同时支持系统偏好 (media) 与手动开关 (html[data-motion="reduced"]，
     由 motionService 写入，供 C 路做设置项)。
   - [data-motion-essential] 豁免: loading 指示保留不透明度脉冲
     （reduce ≠ 无反馈，§2.4 F4）。
   ──────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  *:not([data-motion-essential]),
  *:not([data-motion-essential])::before,
  *:not([data-motion-essential])::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
html[data-motion="reduced"] *:not([data-motion-essential]),
html[data-motion="reduced"] *:not([data-motion-essential])::before,
html[data-motion="reduced"] *:not([data-motion-essential])::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}

/* essential 豁免元素: 强制退化为纯不透明度脉冲（禁位移/模糊/旋转） */
@media (prefers-reduced-motion: reduce) {
  [data-motion-essential] {
    animation-name: tm-essential-pulse !important;
    animation-duration: 1.6s !important;
    animation-timing-function: ease-in-out !important;
    animation-iteration-count: infinite !important;
  }
}
html[data-motion="reduced"] [data-motion-essential] {
  animation-name: tm-essential-pulse !important;
  animation-duration: 1.6s !important;
  animation-timing-function: ease-in-out !important;
  animation-iteration-count: infinite !important;
}
@keyframes tm-essential-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}

/* ────────────────────────────────────────────────────────────
   2) 焦点基线（§2.7 A1）— 键盘用户的品牌级体验
   :where() 降特异性到 0，任何组件级 :focus-visible 均可覆盖，
   既有 modelDropdown/settings 焦点契约不受影响。
   ──────────────────────────────────────────────────────────── */
:where(a, button, [role="button"], [role="option"], [role="menuitem"],
       input, textarea, select, [tabindex]:not([tabindex="-1"])):focus-visible {
  outline: var(--tm-focus-ring-outline);
  outline-offset: 2px;
  border-radius: var(--tm-radius-sm);
}
/* 输入类元素焦点环贴边（offset 0），避免与自身边框叠出双环 */
:where(input, textarea, select):focus-visible {
  outline-offset: 0;
}

/* ────────────────────────────────────────────────────────────
   3) 动画暂停纪律（§2.4 F1「不可见即暂停」的 CSS 半区）
   面板级容器隐藏/折叠时，其内所有动画暂停（合成器立即释放）。
   JS 半区（IntersectionObserver 停滚出视口的气泡动画）属 A 路
   增量，接口见 §4.4。
   ──────────────────────────────────────────────────────────── */
.hidden *,
[hidden] *,
[aria-hidden="true"] * {
  animation-play-state: paused !important;
}

/* ────────────────────────────────────────────────────────────
   4) 警示有尽头（§2.4 F5）+ 签名时刻支撑
   4a. 能量槽 critical: 呼吸 6 次(9s)后定格静态高亮。
       两段式: 有限次呼吸动画 → settle 动画 forwards 定格。
       覆盖 styles.css:6768 的 infinite 声明（源顺序在后，同特异性生效）。
   ──────────────────────────────────────────────────────────── */
.energy-edge-fill.critical {
  animation:
    energy-flow-critical 1.5s var(--tm-ease-linear) 6,
    energy-breathe 1.5s ease-in-out 6,
    tm-alert-settle var(--tm-dur-slow) var(--tm-ease-out) 9s forwards;
}
.compact-trigger-btn.critical {
  animation:
    compact-btn-pulse-critical 1.5s ease-in-out 6,
    tm-alert-settle var(--tm-dur-slow) var(--tm-ease-out) 9s forwards;
}
/* overhead 子层同款定格（styles.css:6504 的 critical-breathe infinite 覆盖）——
   F5「警示必有尽头」需覆盖 critical 态全部呼吸子层 */
.energy-edge.critical .energy-fill-overhead {
  animation:
    critical-breathe 1.5s ease-in-out 6,
    tm-alert-settle var(--tm-dur-slow) var(--tm-ease-out) 9s forwards;
}
@keyframes tm-alert-settle {
  to {
    opacity: 1;
    box-shadow:
      0 0 10px rgba(239, 68, 68, 0.55),
      0 0 20px rgba(249, 115, 22, 0.28);
  }
}

/* 4b. 签名时刻 #1: 模型切换天色渐变（§2.4.4）
   custom property 引用值变化会触发已声明的 transition。 */
.mesh-orb {
  transition: background 1200ms var(--tm-ease-out);
}

/* ────────────────────────────────────────────────────────────
   5) Glass 材质工具类（§2.3）— 新组件唯一合法玻璃写法
   用法: <div class="tm-glass">（标准）/ tm-glass--soft / tm-glass--heavy
   自动: 暗色切换(token 驱动) + @supports 降级(M1) + 边框内高光
   存量玻璃组件按 §3.9 阶段 2 迁移；迁移前后视觉 byte-equal。
   ──────────────────────────────────────────────────────────── */
.tm-glass,
.tm-glass--soft,
.tm-glass--heavy {
  background: var(--tm-glass-bg);
  border: 1px solid var(--tm-glass-border);
  box-shadow: var(--tm-shadow-2), var(--tm-glass-highlight);
}
.tm-glass {
  backdrop-filter: blur(var(--tm-glass-blur)) saturate(var(--tm-glass-saturate));
  -webkit-backdrop-filter: blur(var(--tm-glass-blur)) saturate(var(--tm-glass-saturate));
}
.tm-glass--soft {
  background: var(--tm-glass-bg-faint);
  backdrop-filter: blur(var(--tm-glass-blur-soft));
  -webkit-backdrop-filter: blur(var(--tm-glass-blur-soft));
}
.tm-glass--heavy {
  backdrop-filter: blur(var(--tm-glass-blur-heavy)) saturate(var(--tm-glass-saturate));
  -webkit-backdrop-filter: blur(var(--tm-glass-blur-heavy)) saturate(var(--tm-glass-saturate));
}

/* M1: 无 backdrop-filter 内核 → 实底替代，文字对比永不崩（§1.4 病灶 1 闭环） */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .tm-glass,
  .tm-glass--soft,
  .tm-glass--heavy {
    background: var(--tm-glass-fallback-bg);
  }
}

/* 存量玻璃降级急救（迁移完成后随 §3.9 阶段 2 删除本段） */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  body:not([data-bubble-theme="white"]) .message-user .message-bubble {
    background: var(--tm-glass-fallback-bg);
  }
  .header-logo-btn, .header-menu-btn, .upgrade-pill,
  .composer-glass-panel, .baton-pass-card {
    background: var(--tm-glass-fallback-bg);
  }
}

/* ────────────────────────────────────────────────────────────
   6) auth 分区暗色光学修正（B1 / dark-optical）
   语义 token 映射后仍需微调对比度的两处；落本文件以守
   styles.css 冻结令（新增规则不进 styles.css）。
   ──────────────────────────────────────────────────────────── */
/* dark-optical: 暗底下 text-secondary 边框过亮，降一档 */
:root[data-theme="dark"] .auth-otp-slot.filled {
  border-color: var(--text-muted, #94a3b8);
}
/* dark-optical: incomplete 提交钮在暗色下用暗面板底 + 弱文字（原亮灰底刺眼） */
:root[data-theme="dark"] .auth-otp-submit-btn.incomplete {
  background: var(--bg-tertiary, #2a2a2a) !important;
  color: var(--text-muted, #94a3b8) !important;
}

/* ────────────────────────────────────────────────────────────
   7) a11y 工具类（s02 A5）— off-screen 但对辅助技术可见。
   用于 live region（#chat-announcer）等仅供 SR 消费的节点。
   ──────────────────────────────────────────────────────────── */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ────────────────────────────────────────────────────────────
   区块 8 · F-C4 grain overlay（s06 / 06_FABLE5 §3 裁决 ✅直接吃）：
   一层静态 CSS 噪点消除「数码平板感」——复活 index.html 既有的
   .bg-noise 悬挂层（styles.css 存量规则 display:none，此处覆盖）。
   静态背景 tile、零动画、无 blend——低配办公机合成成本≈0。
   ──────────────────────────────────────────────────────────── */
.bg-noise {
  display: block;
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.35'/%3E%3C/svg%3E");
  background-repeat: repeat;
  opacity: 0.10;
}
:root[data-theme="dark"] .bg-noise {
  opacity: 0.16; /* 暗色氛围下 grain 略强（F-C2 单色暗的质感层） */
}

/* ────────────────────────────────────────────────────────────
   9) R2 W2 · Header veil（R-08 / F2）：滚动时给透明顶栏补一层
      token 化玻璃底，内容不再从 header 下方裸穿。
   ──────────────────────────────────────────────────────────── */
.header {
  isolation: isolate;
}
.header::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: calc(var(--tm-z-fab) - var(--tm-z-fab) - 1);
  background: var(--tm-glass-bg);
  -webkit-backdrop-filter: blur(var(--tm-glass-blur)) saturate(var(--tm-glass-saturate));
  backdrop-filter: blur(var(--tm-glass-blur)) saturate(var(--tm-glass-saturate));
  border-bottom: 1px solid var(--tm-border-default);
  opacity: 0;
  transition: opacity var(--tm-dur-fast) var(--tm-ease-out);
  pointer-events: none;
}
body.chat-scrolled .header::after {
  opacity: 1;
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .header::after {
    background: var(--tm-glass-fallback-bg);
  }
}

/* ────────────────────────────────────────────────────────────
   10) R2 W3 · Toast v2（R-05/R-06）
   统一挂载到 #toast-region，容器负责堆叠；单条 toast 不再写 top。
   ──────────────────────────────────────────────────────────── */
#toast-region {
  position: fixed;
  top: calc(var(--tm-safe-top) + var(--tm-space-5));
  left: 50%;
  transform: translateX(-50%);
  z-index: var(--tm-z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--tm-space-2);
  width: calc(100vw - (var(--tm-space-3) * 2));
  pointer-events: none;
}

.toast-notification {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--tm-space-2);
  max-width: calc(100vw - (var(--tm-space-3) * 2));
  box-sizing: border-box;
  padding: var(--tm-space-2) var(--tm-space-4) var(--tm-space-2) var(--tm-space-3);
  background: var(--tm-surface-inverse);
  color: var(--tm-text-on-inverse);
  border: 1px solid color-mix(in srgb, var(--tm-text-on-inverse) 16%, transparent);
  border-radius: var(--tm-radius-lg);
  box-shadow: var(--tm-shadow-float);
  font-size: var(--tm-text-md);
  font-weight: var(--tm-weight-medium);
  pointer-events: auto;
  opacity: 0;
  animation: toast-enter var(--tm-dur-normal) var(--tm-ease-spring) forwards;
}

.toast-notification.hiding {
  animation: toast-fade-out var(--tm-dur-fast) var(--tm-ease-in) forwards;
}

.toast-dot {
  --toast-dot-color: var(--tm-brand-blue);
  width: var(--tm-space-2);
  height: var(--tm-space-2);
  border-radius: var(--tm-radius-full);
  flex-shrink: 0;
  background: var(--toast-dot-color);
  box-shadow: 0 0 0 var(--tm-space-1) color-mix(in srgb, var(--toast-dot-color) 24%, transparent);
}
.toast-dot.text-blue,
.toast-dot.text-muted { --toast-dot-color: var(--tm-brand-blue); }
.toast-dot.text-green,
.toast-dot.text-purple { --toast-dot-color: var(--tm-status-success); }
.toast-dot.text-orange { --toast-dot-color: var(--tm-status-warning); }
.toast-dot.text-red { --toast-dot-color: var(--tm-status-danger); }

.toast-message {
  flex: 1;
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

@media (max-width: 480px) {
  .toast-message {
    display: -webkit-box;
    white-space: normal;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    line-height: var(--tm-leading-snug);
  }
}

.toast-action {
  max-width: 40vw;
  padding: var(--tm-space-1) var(--tm-space-3);
  border: 1px solid color-mix(in srgb, var(--tm-text-on-inverse) 40%, transparent);
  border-radius: var(--tm-radius-sm);
  background: transparent;
  color: var(--tm-text-on-inverse);
  font-size: var(--tm-text-xs);
  font-weight: var(--tm-weight-semibold);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
  transition:
    background var(--tm-dur-fast) var(--tm-ease-out),
    transform var(--tm-dur-instant) var(--tm-ease-out);
}
.toast-action:hover {
  background: color-mix(in srgb, var(--tm-text-on-inverse) 14%, transparent);
}
.toast-action:active {
  transform: scale(0.96);
}

.toast-close {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--tm-space-1);
  border: none;
  border-radius: var(--tm-radius-xs);
  background: transparent;
  color: color-mix(in srgb, var(--tm-text-on-inverse) 68%, transparent);
  cursor: pointer;
  transition:
    background var(--tm-dur-fast) var(--tm-ease-out),
    color var(--tm-dur-fast) var(--tm-ease-out);
}
.toast-close:hover {
  background: color-mix(in srgb, var(--tm-text-on-inverse) 12%, transparent);
  color: var(--tm-text-on-inverse);
}
.toast-close svg {
  width: var(--tm-space-3);
  height: var(--tm-space-3);
}

.toast-progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background: color-mix(in srgb, var(--tm-text-on-inverse) 28%, transparent);
  transform-origin: left;
  animation: toast-countdown var(--tm-ease-linear) forwards;
  border-radius: 0 0 var(--tm-radius-lg) var(--tm-radius-lg);
}

@keyframes toast-enter {
  from {
    opacity: 0;
    transform: translateY(calc(var(--tm-space-2) * -1)) scale(0.96);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

@keyframes toast-fade-out {
  from {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
  to {
    opacity: 0;
    transform: translateY(calc(var(--tm-space-1) * -1)) scale(0.98);
  }
}

@keyframes toast-countdown {
  from { transform: scaleX(1); }
  to { transform: scaleX(0); }
}

/* ────────────────────────────────────────────────────────────
   11) W8 · EmptyState v2
   复活首轮组件时统一切到 tokens v2；各业务面只传 variant/copy。
   ──────────────────────────────────────────────────────────── */
.empty-state {
  display: flex;
  min-height: 200px;
  padding: var(--tm-space-6) var(--tm-space-5);
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--tm-space-2);
  text-align: center;
  color: var(--tm-text-secondary);
}

.empty-state--compact {
  min-height: 96px;
  padding: var(--tm-space-4);
}

.empty-illustration {
  display: inline-flex;
  color: var(--tm-text-muted);
  opacity: 0;
  transform: scale(0.96);
  animation: tm-empty-illustration-in var(--tm-dur-normal) var(--tm-ease-out) var(--tm-dur-instant) forwards;
}

.empty-illustration svg {
  width: 80px;
  height: 80px;
}

.empty-state--compact .empty-illustration svg {
  width: 48px;
  height: 48px;
}

.empty-title {
  margin: 0;
  color: var(--tm-text-primary);
  font-size: var(--tm-text-sm);
  font-weight: var(--tm-weight-semibold);
  line-height: var(--tm-leading-tight);
  opacity: 0;
  animation: tm-empty-fade-in var(--tm-dur-normal) var(--tm-ease-out) var(--tm-dur-fast) forwards;
}

.empty-desc {
  max-width: 240px;
  margin: 0;
  color: var(--tm-text-secondary);
  font-size: var(--tm-text-sm);
  line-height: var(--tm-leading-normal);
  opacity: 0;
  animation: tm-empty-fade-in var(--tm-dur-normal) var(--tm-ease-out) var(--tm-dur-normal) forwards;
}

.empty-cta {
  margin-top: var(--tm-space-1);
  padding: var(--tm-space-2) var(--tm-space-4);
  border: none;
  border-radius: var(--tm-radius-lg);
  background: var(--tm-brand-purple);
  color: var(--tm-text-on-inverse);
  box-shadow: var(--tm-shadow-1);
  font-size: var(--tm-text-sm);
  font-weight: var(--tm-weight-semibold);
  cursor: pointer;
  opacity: 0;
  animation: tm-empty-fade-in var(--tm-dur-normal) var(--tm-ease-out) var(--tm-dur-slow) forwards;
  transition:
    transform var(--tm-dur-fast) var(--tm-ease-out),
    box-shadow var(--tm-dur-fast) var(--tm-ease-out),
    background var(--tm-dur-fast) var(--tm-ease-out);
}

.empty-cta:hover {
  background: color-mix(in srgb, var(--tm-brand-purple) 88%, var(--tm-text-primary));
  box-shadow: var(--tm-shadow-float);
  transform: translateY(calc(var(--tm-space-1) * -0.25));
}

.empty-cta:active {
  transform: translateY(0);
}

.empty-state-connection-lost .empty-cta {
  background: var(--tm-status-warning);
}

.settings-empty-state {
  min-height: 128px;
  border: 1px dashed var(--tm-border-default);
  border-radius: var(--tm-radius-lg);
  background: var(--tm-surface-1);
}

.command-empty-state {
  min-height: 88px;
  padding-block: var(--tm-space-3);
  border: 1px solid color-mix(in srgb, var(--tm-text-on-inverse) 8%, transparent);
  border-radius: var(--tm-radius-lg);
  background: color-mix(in srgb, var(--tm-surface-inverse) 84%, transparent);
}

.command-empty-state .empty-illustration svg {
  width: calc(var(--tm-space-6) + var(--tm-space-2));
  height: calc(var(--tm-space-6) + var(--tm-space-2));
}

.command-empty-state .empty-title {
  color: var(--tm-text-secondary);
  font-weight: var(--tm-weight-medium);
}

.command-empty-state .empty-desc {
  font-size: var(--tm-text-xs);
}

.cortex-empty-state {
  min-height: 144px;
  border: 1px dashed var(--tm-border-default);
  border-radius: var(--tm-radius-lg);
  background: color-mix(in srgb, var(--tm-surface-1) 84%, transparent);
}

@keyframes tm-empty-illustration-in {
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes tm-empty-fade-in {
  to { opacity: 1; }
}

html[data-motion="reduced"] .empty-illustration,
html[data-motion="reduced"] .empty-title,
html[data-motion="reduced"] .empty-desc,
html[data-motion="reduced"] .empty-cta {
  animation: none;
  opacity: 1;
  transform: none;
}

@media (prefers-reduced-motion: reduce) {
  .empty-illustration,
  .empty-title,
  .empty-desc,
  .empty-cta {
    animation: none;
    opacity: 1;
    transform: none;
  }
}
