/* ============================================================
 * AcousticOrbit — 阶段 1 样式
 * 画布基准 5386×3366
 * 布局方案 v3:
 *   背景 = 独立 cover 撑满视口(可被裁切,只是环境)
 *   机身 = contain 完整可见(卷线等装饰画在 Main_Body 上,不允许裁)
 *   所有界面内文字/描边使用 cqw 单位,随机身容器一起缩放
 * ============================================================ */

:root {
  /* ---- 色彩规范(设计者 2026-07 提供,已与机身印刷橙采样核实) ---- */
  --ao-orange:        #d15d31;   /* 项目主橙 / 橙色文字 */
  --ao-orange-light:  #e87a4b;   /* 主橙提亮(渐变上沿) */
  --ao-orange-deep:   #b04a24;   /* 主橙压深(渐变下沿) */
  --ao-text-light:    #d5d7d6;   /* 屏幕白色文字 */
  --ao-track-black:   #0c0c0c;
  --ao-ink:           #0b0b0b;   /* 机身印刷黑(设计者色号,仅用于对色参考,不要直接拿来渲染动态文字) */
  --ao-text-dark:     #29292b;   /* 统一深色动态文字色(2026-07 定稿,取旧维度名/过滤器分类名两色之间);
                                     所有新增深色文字一律使用这个变量,不要再新增深色数值 */

  /* ---- 黑体字体栈(跨平台回退链) ----
     Mac: Heiti SC / STHeiti(Photoshop 的"黑体")
     Win: Microsoft YaHei;Linux/Android: Noto/思源黑体
     若需完全一致,可由设计者导出 woff2 后改用 @font-face 嵌入(见 README) */
  /* 多语言(2026-07-19 FE-V9):在简体黑体后补繁体 TC 与日文 JP 回退,
     让繁体字形/日文假名有地道字体,而非退到简体或系统默认 */
  --font-hei: "Heiti SC", "STHeiti", "PingFang SC", "PingFang TC",
              "Hiragino Sans", "Hiragino Kaku Gothic ProN", "Microsoft YaHei",
              "Noto Sans SC", "Noto Sans TC", "Noto Sans JP",
              "Noto Sans CJK SC", "Source Han Sans SC", sans-serif;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

html, body {
  width: 100%;
  height: 100%;
  background: #000;
  overflow: hidden;
  font-family: "Shree Devanagari 714", "PingFang SC", "Microsoft YaHei", sans-serif;
}

/* ---------- 背景:cover 撑满,允许裁切 ---------- */
#layer-background {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
  pointer-events: none;
}

/* ---------- 机身容器:contain,始终完整可见 ----------
 * container-type: size 使内部可用 cqw/cqh 单位,
 * 文字与描边全部随容器缩放(浏览器窗口缩放时同步变化) */
#device {
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  aspect-ratio: 5386 / 3366;
  width: min(100vw, calc(100vh * (5386 / 3366)));
  container-type: size;
}

.fill {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  user-select: none;
}

img.fill { object-fit: contain; pointer-events: none; }

/* ---------- 透视:只作用于机身 ---------- */
#tilt-wrap { perspective: 2600px; }

#stage {
  /* 不要 preserve-3d:悬停透视只旋转 #stage 整体,子元素无独立 3D 变换,
     而 preserve-3d 会让复合层子元素逃逸祖先 overflow 裁切
     (专辑转盘上半圆漏出 Speaker 区,实测踩坑) */
  transition: transform 120ms ease-out;
  will-change: transform;
}

/* 空白处放行点击穿透到 #layer-behind(机身后层部件:拉杆/线圈/插头),
   实际交互元素(直接子元素)各自恢复接收。
   注意:全画布的透明包装层(.filter-cluster / .right-screen-clip)必须
   重新置 none,否则 ID 选择器优先级会压过它们自身的穿透设置,
   透明层会吞掉整个画布的真实鼠标点击(合成点击测不出,踩过的坑) */
#layer-controls { pointer-events: none; }
#layer-controls > * { pointer-events: auto; }
#layer-controls > .filter-cluster,
#layer-controls > .right-screen-clip { pointer-events: none; }
#layer-behind { pointer-events: none; }
#layer-behind > * { pointer-events: auto; }
#layer-debug    { pointer-events: none; }
/* 插头在 #layer-static(#tilt-wrap 之下):透视容器与 #stage 必须穿透,
   否则整个画布的点击都落在它们身上,到不了后面的插头;pointer-events
   会继承,#stage 内的交互元素已各自显式 auto,不受影响 */
#tilt-wrap, #stage { pointer-events: none; }
#layer-static { pointer-events: none; }   /* 插头本体继承穿透,.plug-hit 显式 auto */

/* ---------- 通用定位元素 ---------- */
.abs { position: absolute; }

/* ============================================================
 * 维度滑轨(阶段 1:valence 样板,阶段 2 铺满 8 条)
 * 结构:
 *   .dim-slider          外包裹,定位在轨道坐标,overflow 可见
 *     .dim-track         凹槽轨道(代码绘制,含质感)
 *       .dim-fill        橙色填充(宽度 = 值比例)
 *     .dim-thumb         滑块贴图 + 数值
 *       img
 *       .dim-value       当前值(白字,叠在滑块上)
 * 质感要点:
 *   凹槽 = 顶部内阴影(深) + 底部外侧一线高光(白) + 内部近黑
 *   填充 = 橙色纵向渐变 + 顶部一丝暗压,像嵌在凹槽里发光的条
 * ============================================================ */
.dim-slider {
  /* left/top/width/height 由 JS 按轨道坐标写入 */
  touch-action: none;
}

.dim-track {
  position: absolute;
  inset: 0;
  border-radius: 100cqw;               /* 全圆角 */
  background:
    linear-gradient(180deg, #000 0%, var(--ao-track-black) 55%, #1a1a1a 100%);
  /* 凹陷质感:
     ① inset 顶部深阴影 —— 光从上来,凹槽上缘背光
     ② inset 底部一丝内亮 —— 凹槽下缘接光
     ③ 外部底缘 1 线白高光 —— 机身面板在凹槽下方的受光棱线 */
  box-shadow:
    inset 0 0.12cqw 0.22cqw rgba(0, 0, 0, 0.9),
    inset 0 -0.05cqw 0.08cqw rgba(255, 255, 255, 0.10),
    0 0.055cqw 0 rgba(255, 255, 255, 0.38),
    0 -0.03cqw 0 rgba(0, 0, 0, 0.25);
  overflow: hidden;
}

.dim-fill {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 50%;                           /* 实际宽度由 JS 写入 */
  border-radius: inherit;
  background: linear-gradient(180deg, var(--ao-orange-light) 0%, var(--ao-orange) 45%, var(--ao-orange-deep) 100%);
  /* 填充条自身也在凹槽里:顶部压一点暗,保持嵌入感 */
  box-shadow:
    inset 0 0.10cqw 0.16cqw rgba(0, 0, 0, 0.45),
    inset 0 -0.04cqw 0.06cqw rgba(255, 255, 255, 0.25);
}

.dim-thumb {
  position: absolute;
  top: 50%;
  /* 尺寸:181×82 相对轨道 1241×18 */
  width: calc(181 / 1241 * 100%);
  height: calc(82 / 18 * 100%);
  transform: translate(-50%, -50%);     /* left 由 JS 写入(中心定位) */
  cursor: grab;
  display: flex;
  align-items: center;
  justify-content: center;
}
.dim-thumb:active { cursor: grabbing; }

.dim-thumb > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  /* 微投影让滑块"浮"在凹槽上方 */
  filter: drop-shadow(0 0.09cqw 0.14cqw rgba(0, 0, 0, 0.45));
}

.dim-value {
  position: relative;
  z-index: 1;
  /* 字号由 JS 按维度位数写入(基准 0.89cqw,对应 Figma 参考框 125×48) */
  font-size: 0.89cqw;
  font-family: var(--font-hei);
  font-weight: 500;
  letter-spacing: 0.015cqw;
  color: var(--ao-text-light);
  pointer-events: none;
  font-variant-numeric: tabular-nums;
  text-shadow: 0 0.03cqw 0.06cqw rgba(0, 0, 0, 0.7);
  /* 静态垂直微调(高精度维度用,由 JS 写入 --value-nudge-y,默认 0cqw);
     与下方拖动下沉叠加,而不是互相覆盖。回退值必须带单位(0cqw 而非裸 0),
     否则 calc() 里数字与长度相加类型不合法,声明整体失效(踩过的坑) */
  transform: translateY(var(--value-nudge-y, 0cqw));
}

/* 拖动中的按压反馈:滑块轻微下沉、投影收缩(硬件手感);数值文字跟随同步下沉
   (与静态垂直微调 --value-nudge-y 叠加,而非覆盖) */
.dim-slider.dragging .dim-thumb > img {
  filter: drop-shadow(0 0.04cqw 0.07cqw rgba(0, 0, 0, 0.55));
  transform: translateY(0.03cqw);
}
.dim-slider.dragging .dim-value {
  transform: translateY(calc(var(--value-nudge-y, 0cqw) + 0.03cqw));
}

/* ---------- 维度名(text_1~8,动态叠字,参与多语言) ---------- */
.dim-label {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  font-family: var(--font-hei);
  /* 字号标定(设计者以 danceability 为样本):
     文字框 441×86,字形实占 362×56(含上中下三格)→ 字号 ≈ 56px ≈ 1.05cqw */
  font-size: 1.12cqw;
  font-weight: 300;                  /* 更细;若平台无细体则回落常规体,届时再调浅颜色 */
  /* 字形前沿距框左 45px → 不顶框头 */
  padding-left: 0.835cqw;
  color: var(--ao-text-dark);
  letter-spacing: 0.01cqw;
  pointer-events: none;
  white-space: nowrap;
}
/* 维度名文字包层:横向压缩(scaleX)吃 textfit 的 --fit-x —— 日语长名压扁
   保字高不缩号;origin 左让文字锚定框左沿。--fit-x 由容器继承下来 */
.dim-label-txt {
  display: inline-block;
  white-space: nowrap;
  transform: scaleX(var(--fit-x, 1));
  transform-origin: left center;
}
/* 中文(简/繁):8 维 + filters 分类名分散对齐撑满文字框,首末字到框两侧
   等距、字间距更大、末字贴近右侧(设计者要求,替代之前贴左的紧凑排布)。
   两侧留白由容器对称 padding 决定;span 撑满内容框、逐字分散。 */
/* 仅 8 维度名分散对齐(文字框够宽);filters 分类名框窄,分散会把 2 字挤成
   两行,故保持原样单行(不参与 justify) */
:lang(zh) .dim-label-txt {
  display: block;
  width: 100%;
  white-space: normal;
  text-align: justify;
  text-align-last: justify;
  text-justify: inter-character;
}
/* 两侧对称留白 → 分散对齐的字距(设计者:字距不要过大,收一点) */
:lang(zh) .dim-label { padding-left: 1.5cqw; padding-right: 1.5cqw; }
/* 维度权重:中文一行显示,沿用默认左对齐 + 垂直居中(框中线已对齐八旋钮) */

/* ---------- 权重读数(text_weight_value_1~8,"×"号已印刷,数字动态) ---------- */
.weight-value {
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-hei);
  font-size: 0.82cqw;                 /* 框 65×65 */
  /* 读数底色分行不同(机身印刷色块):默认白字 #d5d7d6;
     第 3、4 行底色浅 → 加 .ink 用黑字 #0b0b0b,由 JS 按行号分配 */
  color: var(--ao-text-light);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}
.weight-value.ink { color: var(--ao-text-dark); }

/* ---------- 权重旋钮 ---------- */
.weight-knob {
  cursor: grab;
  touch-action: none;
  border-radius: 50%;
  /* 立体感:右下轻微投影(扩散)+ 贴地接触影(收紧)。
     阴影在容器上,内部贴图旋转时阴影方向恒定不随转 */
  box-shadow:
    0.13cqw 0.17cqw 0.30cqw rgba(0, 0, 0, 0.75),
    0.05cqw 0.07cqw 0.12cqw rgba(0, 0, 0, 0.6);
}
.weight-knob.dragging { cursor: grabbing; }
.weight-knob > img {
  position: absolute;
  /* 贴图放大 6%,把导出留下的亮色羽化边裁到容器外(overflow:hidden 吃掉);
     实测边缘 2~3px 存在抗锯齿亮边,是"白色高光"的真正来源,与 Figma 尺寸无关 */
  inset: -6%;
  width: 112%;
  height: 112%;
  object-fit: contain;
  pointer-events: none;
  will-change: transform;
}

/* ---------- 过滤器面板(阶段 4) ---------- */
.filter-toggle {
  cursor: pointer;
}
.filter-toggle > img,
.key-btn > img,
.mode-btn > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
}

/* 按下反馈:轻微下沉(硬件手感)。
   注意:.filter-toggle 是拨杆(3 帧状态机),不是按钮,不需要按下位移。
   文字与图形一起下沉:span/img 同选择器。 */
.key-btn:active > img,
.key-btn:active > span,
.mode-btn:active > img {
  transform: translateY(2%);
}

.key-btn, .mode-btn {
  cursor: pointer;
}
.key-btn > span {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-hei);
  font-size: 0.62cqw;
  color: var(--ao-text-light);
  pointer-events: none;
}

/* 关闭的过滤器:整簇呈"未通电"质感——去饱和 + 压暗,而非透明度衰减
   (拟物化语境里,关闭的硬件面板应该看起来"实体还在但没有电",
   透明度衰减会显得像 UI 层的淡出,与整体质感冲突) */
.filter-cluster.disabled {
  filter: grayscale(0.9) brightness(0.5) contrast(0.9);
  pointer-events: none;
}
.filter-cluster { position: absolute; inset: 0; pointer-events: none; }
.filter-cluster > * { pointer-events: auto; }
.filter-cluster.disabled > * { pointer-events: none; }

.filter-slider { touch-action: none; }
.filter-thumb {
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  cursor: grab;
  display: flex;
  align-items: center;
  justify-content: center;
}
.filter-thumb:active { cursor: grabbing; }
.filter-thumb > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
  /* 模糊半径从 0.11cqw 收窄到 0.045cqw:实测(截图像素级 diff)确认贴图实体
     本身在轨道两端已经贴合(<0.5px 误差),真正让人觉得"没贴边"的是投影
     的模糊晕开范围在轨道端点处溢出到轨道外的背景上——缩小模糊半径直接
     减少这圈溢出,而不是去动数值范围或滑块定位公式 */
  filter: drop-shadow(0 0.04cqw 0.045cqw rgba(0, 0, 0, 0.45));
}
.filter-value {
  position: relative;
  z-index: 1;
  font-family: var(--font-hei);
  font-size: 0.5cqw;
  color: var(--ao-text-light);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  text-shadow: 0 0.03cqw 0.05cqw rgba(0, 0, 0, 0.7);
}
/* 拖动中:投影收缩 + 轻微下沉,文字随滑块一起下沉(与 8 维度滑轨同款手感) */
.filter-slider.dragging .filter-thumb.active > img {
  filter: drop-shadow(0 0.03cqw 0.05cqw rgba(0, 0, 0, 0.55));
  transform: translateY(0.03cqw);
}
.filter-slider.dragging .filter-thumb.active .filter-value {
  transform: translateY(0.03cqw);
}

/* ---------- 过滤器分类名标签(动态叠字,与维度名同规格) ---------- */
.filter-label {
  display: flex;
  align-items: center;
  padding-top: 0.12cqw;               /* 避免顶格,留出上边缘空隙 */
  padding-left: 0.28cqw;              /* 左边留一点空隙(约一个字母 t 的宽度),不贴框边 */
  font-family: var(--font-hei);
  font-size: 1.02cqw;                  /* 比 8 维度名称(1.12cqw)略小一丁点 */
  font-weight: 300;
  color: var(--ao-text-dark);
  pointer-events: none;
  white-space: nowrap;
  /* 文案超槽位时横向压扁(日语「スピーチ度」等),origin 左锚定 */
  transform: scaleX(var(--fit-x, 1));
  transform-origin: left center;
}

/* ---------- text_dimension_weights(板块标题,两行) ---------- */
.dim-weights-label {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  justify-content: center;
  padding: 0.15cqw 0 0.15cqw 0.2cqw;   /* 四周留出空隙,不顶格 */
  font-family: var(--font-hei);
  font-size: 1.02cqw;
  font-weight: 300;
  line-height: 1.3;
  color: var(--ao-text-dark);
  pointer-events: none;
  white-space: nowrap;
}

/* ---------- 语言切换标签(机身顶排,两态实体按钮 btn_lang_*_on/off) ---------- */
.lang-tab { cursor: pointer; }
.lang-tab img {
  display: block;
  width: 100%;
  height: auto;
  pointer-events: none;                /* 点击落在 .lang-tab 上,由 JS 切换语言 */
}
.lang-tab:active { filter: brightness(0.92); }
/* 断电:语言切换不可点(设计者要求"点了没用");JS 侧亦有兜底判断 */
body.power-off .lang-tab { pointer-events: none; }

/* ---------- Tutorial 按钮(顶排,纯橙底素材 + 代码叠白字;仅展示无交互) ---------- */
.tutorial-btn { pointer-events: none; }
.tutorial-btn img { display: block; width: 100%; height: auto; }
.tutorial-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-hei);
  font-size: 1.02cqw;
  font-weight: 400;
  color: #fff;                         /* 纯白 —— #d5d7d6 在橙底上偏灰(设计者反馈) */
  white-space: nowrap;
}

/* ============================================================
 * 搜索框 + 候选/推荐列表 + Generate 按钮(阶段 5)
 * input_search/list_candidates 区域在 Main_Body 上是纯黑镂空,没有任何
 * 预印内容——图标、占位文字、行样式、流行度条全部代码绘制
 * ============================================================ */
/* 左缩进三段(内边距 + 图标宽 + 间隙)用同一组数值,保证
   "Enter a song name"、歌名、artist 三者左边缘严格对齐:
   行内文字缩进 = 0.5 + 1.3 + 0.6 = 2.4cqw(.candidate-row 用这个值) */
.search-box {
  display: flex;
  align-items: center;
  gap: 0.6cqw;
  padding: 0 0.5cqw;
}

.search-icon-chip {
  flex: 0 0 auto;
  width: 1.3cqw;
  height: 1.3cqw;
  border-radius: 22%;
  background: rgba(255, 255, 255, 0.06);
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;                      /* 点击图标 = 触发搜索(代替回车) */
}
/* 圆圈与手柄整体缩小并上移一点(设计者反馈:原来视觉重心偏下沉) */
.search-icon-glass {
  position: relative;
  top: -0.07cqw;
  width: 40%;
  height: 40%;
  border: 0.08cqw solid var(--ao-text-light);
  border-radius: 50%;
  opacity: 0.85;
}
.search-icon-glass::after {
  content: '';
  position: absolute;
  width: 0.36cqw;
  height: 0.08cqw;
  right: -0.25cqw;
  bottom: -0.04cqw;
  background: var(--ao-text-light);
  border-radius: 1cqw;
  transform: rotate(45deg);
  opacity: 0.85;
}

.search-input {
  flex: 1 1 auto;
  height: 100%;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  font-family: var(--font-hei);
  font-weight: 300;
  font-size: 1.05cqw;
  color: var(--ao-text-light);
}
.search-input::placeholder {
  color: var(--ao-text-light);
  opacity: 0.7;
}
.search-input[readonly] { cursor: default; }

/* ---------- 超长文字跑马灯(悬停触发,单向循环) ----------
   裁切窗内两份相同文字(第二份 margin-left = 首尾间距),一直向左
   平移一个循环距离;新头对齐框头时由 keyframes 前 12% 停顿。
   一圈跑完(animationend)由 JS 复位/交替,不用 infinite */
.marquee-inner { display: inline-block; }
.marquee-copy { display: inline-block; }
.marquee-copy + .marquee-copy {
  margin-left: 2em;        /* 名字首尾之间的间距 */
  user-select: none;       /* 循环副本不可选中:避免复制歌名时粘出两遍 */
}
.marquee-on { text-overflow: clip !important; }
.marquee-on > .marquee-inner {
  animation: ao-marquee var(--marquee-dur, 8s) linear 1;
}
@keyframes ao-marquee {
  0%, 12% { transform: translateX(0); }
  100%    { transform: translateX(var(--marquee-shift, 0px)); }
}

.candidate-list {
  overflow-y: auto;
  overflow-x: hidden;
}
.candidate-list::-webkit-scrollbar { width: 0.35cqw; }
.candidate-list::-webkit-scrollbar-track { background: transparent; }
.candidate-list::-webkit-scrollbar-thumb {
  background: rgba(209, 93, 49, 0.5);
  border-radius: 1cqw;
}

.candidate-row {
  position: relative;
  height: 19.454%;                      /* 171 / 879(list_item 行高 / 列表总高) */
  display: flex;
  align-items: center;
  /* 左缩进与搜索框文字起点一致(0.5+1.3+0.6,见 .search-box 注释) */
  padding: 0 0.9cqw 0 2.4cqw;
  cursor: pointer;
  border-bottom: 0.03cqw solid rgba(255, 255, 255, 0.08);
  transition: background 80ms ease-out;
}
.candidate-row:last-child { border-bottom: none; }
.candidate-row:hover { background: rgba(255, 255, 255, 0.05); }
.candidate-row.selected {
  background: linear-gradient(90deg, rgba(209, 93, 49, 0.42), rgba(209, 93, 49, 0.16));
}

.candidate-left {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.18cqw;
}
.candidate-name {
  font-family: var(--font-hei);
  font-weight: 400;
  font-size: 1.0cqw;
  color: var(--ao-text-light);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.candidate-artist {
  font-family: var(--font-hei);
  font-weight: 300;
  font-size: 0.72cqw;
  color: var(--ao-text-light);
  opacity: 0.65;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.candidate-popularity {
  flex: 0 0 29.09%;                     /* 313 / 1076(popularity 块宽 / 列表总宽) */
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.5cqw;
}
.candidate-popularity-num {
  font-family: var(--font-hei);
  font-weight: 500;
  font-size: 0.82cqw;
  color: var(--ao-text-light);
  font-variant-numeric: tabular-nums;
}
.candidate-popularity-bar {
  display: flex;
  align-items: flex-end;
  gap: 0.14cqw;
  height: 0.9cqw;
}
.candidate-popularity-bar i {
  display: block;
  width: 0.22cqw;
  height: 100%;
  border-radius: 0.06cqw;
  background: rgba(255, 255, 255, 0.14);
  font-style: normal;
}
.candidate-popularity-bar i.on {
  background: linear-gradient(180deg, var(--ao-orange-light), var(--ao-orange));
}

/* ---------- Generate 按钮 ---------- */
.generate-btn {
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
/* 贴图状态(设计者定稿):常态永远用 disabled 贴图,只有按住的瞬间
   切到 active 贴图(物理按键手感);未选中候选时点击仅逻辑上无效,
   贴图表现不变化(两张贴图叠放,:active 时切换透明度) */
.generate-btn > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
}
.generate-btn > img.pressed-img { opacity: 0; }
.generate-btn:active > img.pressed-img { opacity: 1; }
.generate-btn:active > img { transform: translateY(2%); }
.generate-label {
  position: relative;
  z-index: 1;
  text-align: center;
  font-family: var(--font-hei);
  font-weight: 400;
  font-size: 0.82cqw;
  letter-spacing: 0.01cqw;
  white-space: nowrap;
  color: #fff;                          /* 橙色按钮上的白字(参照设计者 exemplar 图) */
  pointer-events: none;
}
.generate-btn:active .generate-label { transform: translateY(2%); }

/* ---------- Search / Spotify 跳转按钮(阶段 7) ----------
   贴图状态与 Generate 按钮同一套原理(设计者定稿):常态永远 disabled
   贴图,只有按住瞬间切 active 贴图;未选中推荐时点击仅逻辑无效,
   外观不变。Spotify 贴图自带印字;Search 贴图纯橙,文字代码叠加 */
.link-btn {
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.link-btn > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  pointer-events: none;
}
.link-btn > img.pressed-img { opacity: 0; }
.link-btn:active > img.pressed-img { opacity: 1; }
.link-btn:active > img { transform: translateY(2%); }
.link-btn:active .link-btn-label { transform: translateY(2%); }
.link-btn-label {
  position: relative;
  z-index: 1;
  font-family: var(--font-hei);
  font-weight: 400;
  font-size: 0.82cqw;
  letter-spacing: 0.01cqw;
  white-space: nowrap;
  color: var(--ao-text-dark);           /* 橙色帧上的深色印字,与机身印刷风格一致 */
  pointer-events: none;
}

/* ============================================================
 * 中屏展示区(阶段 6):箭头两侧屏幕 + distance + 差值雷达图
 * 屏幕区域是 Main_Body 上的纯黑镂空,文字/进度条/图表全部代码绘制
 * ============================================================ */
.mid-screen {
  display: flex;
  align-items: center;
  padding: 0 0.6cqw;
  overflow: hidden;
  pointer-events: auto;      /* 悬停触发长歌名滚动(阶段 7.4) */
}
/* 左屏居右、右屏居左:两个歌名都贴近中间的箭头,避免离得太远 */
.mid-screen.align-right { justify-content: flex-end; }
.mid-screen.align-left  { justify-content: flex-start; }
.mid-screen-text {
  font-family: var(--font-hei);
  font-weight: 300;
  font-size: 0.95cqw;
  color: var(--ao-text-light);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* 加载态(3.5):黑底上滑过一条橙色进度条;加载中隐藏文字 */
.mid-screen-loading {
  display: none;
  position: absolute;
  left: 4%;
  right: 4%;
  top: 38%;
  bottom: 38%;
  border-radius: 100cqw;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.06);
}
.mid-screen-loading::after {
  content: '';
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 34%;
  border-radius: inherit;
  background: linear-gradient(90deg, transparent, var(--ao-orange), var(--ao-orange-light));
  animation: ao-load-slide 900ms linear infinite;
}
@keyframes ao-load-slide {
  from { transform: translateX(-100%); }
  to   { transform: translateX(300%); }
}
.mid-screen.loading .mid-screen-text { visibility: hidden; }
.mid-screen.loading .mid-screen-loading { display: block; }

/* distance 读数屏(水平居中;垂直贴近文字框下沿,字号与维度名(1.12cqw)相当) */
.distance-screen {
  gap: 0.5cqw;
  justify-content: center;
  align-items: flex-end;
  padding-bottom: 0.12cqw;
}
.distance-label {
  font-family: var(--font-hei);
  font-weight: 300;
  font-size: 1.1cqw;
  color: var(--ao-text-light);
  visibility: hidden;
}
.distance-screen.active .distance-label { visibility: visible; }
/* 断电熄屏:.active 给 .distance-label 的显式 visible 特异性会盖过父级的
   visibility:hidden,导致"距离:"熄屏后仍残留——这里显式把子标签也熄掉 */
body.power-off .distance-screen .distance-label,
body.power-off .distance-screen .distance-value { visibility: hidden; }
.distance-value {
  font-family: var(--font-hei);
  font-weight: 400;
  font-size: 1.1cqw;
  color: var(--ao-orange);
  font-variant-numeric: tabular-nums;
}

/* 差值雷达图 */
.radar-wrap { pointer-events: auto; }
.radar-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* ---------- 调试红框 ---------- */
.debug-box {
  position: absolute;
  border: 0.04cqw solid rgba(255, 0, 0, 0.9);
  background: rgba(255, 0, 0, 0.08);
}
.debug-box > span {
  position: absolute;
  top: -1.3em;
  left: 0;
  font-size: 0.28cqw;                    /* cqw:随机身缩放,浏览器缩放不再错位 */
  line-height: 1.25;
  color: #fff;
  background: rgba(200, 0, 0, 0.85);
  padding: 0 0.1cqw;
  white-space: nowrap;
  border-radius: 0.06cqw;
}

/* ---------- 开发工具条 ---------- */
#dev-bar {
  position: fixed;
  left: 10px;
  bottom: 10px;
  z-index: 99;
  display: flex;
  gap: 8px;
  align-items: center;
  font-size: 12px;
  color: #ccc;
}
#dev-bar button {
  padding: 4px 10px;
  border: 1px solid #555;
  border-radius: 4px;
  background: #222;
  color: #ddd;
  cursor: pointer;
}
#dev-bar button:hover { background: #333; }
#dev-bar button.on { background: var(--ao-orange); color: #000; border-color: var(--ao-orange); }

/* ---------- 像素量尺(开发工具) ---------- */
#measure-overlay {
  position: fixed;
  inset: 0;
  z-index: 90;                    /* 低于 #dev-bar(99),量尺开着也能点按钮关掉 */
  cursor: crosshair;
  background: rgba(0, 0, 0, 0.04);
  touch-action: none;
}
#measure-box {
  position: fixed;
  border: 1px solid var(--ao-orange);
  background: rgba(209, 93, 49, 0.14);
  pointer-events: none;
}
#measure-tag {
  position: fixed;
  z-index: 91;
  padding: 4px 7px;
  background: rgba(12, 12, 12, 0.9);
  color: #fff;
  font: 12px/1.4 ui-monospace, "SF Mono", Menlo, monospace;
  white-space: nowrap;
  border-radius: 3px;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  #stage { transition: none; }
}

/* ============================================================
 * 机身大改(2026-07-17):字体打包 / 线圈电源 / 拉杆 / Preview /
 * 插头 / 专辑转盘 / 断电态。部件贴图铺满各自定位框(1:1 素材)
 * ============================================================ */

/* 机身印刷字体打包进项目:iPad 等无此字体的设备不再回退到苹方 */
@font-face {
  font-family: "Shree Devanagari 714";
  src: url("../assets/fonts/Shree714.woff2") format("woff2");
  font-display: swap;
}

.coil img, .lever-plate img, .lever-conn img,
.preview-btn img, .plug img {
  width: 100%; height: 100%; display: block;
}

/* ---------- 线圈电源:先垂直拔出插进去的那截(--coil-lift),再横移;
   旋转发生在远离插口的半程,近插口半程必须直线插拔 ---------- */
.coil { cursor: pointer; }
.coil.off { animation: coil-off 0.7s cubic-bezier(0.35, 0.6, 0.35, 1) both; }
.coil.re-on { animation: coil-on 0.7s cubic-bezier(0.35, 0.6, 0.35, 1) both; }
@keyframes coil-off {
  0%   { transform: translate(0, 0) rotate(0deg); }
  45%  { transform: translate(0, var(--coil-lift)) rotate(0deg); }
  100% { transform: translate(var(--coil-dx), var(--coil-dy)) rotate(var(--coil-deg)); }
}
@keyframes coil-on {
  0%   { transform: translate(var(--coil-dx), var(--coil-dy)) rotate(var(--coil-deg)); }
  55%  { transform: translate(0, var(--coil-lift)) rotate(0deg); }
  100% { transform: translate(0, 0) rotate(0deg); }
}

/* ---------- Generate 拉杆:拉下快、弹回慢(渐变过渡) ---------- */
.lever-plate { cursor: pointer; transition: transform 0.6s cubic-bezier(0.25, 0.7, 0.35, 1); }
.lever-plate.pulled { transform: translateY(var(--lever-dy)); transition-duration: 0.15s; }
.lever-conn  { transition: transform 0.6s cubic-bezier(0.25, 0.7, 0.35, 1); }
.lever-conn.pulled { transform: rotate(var(--conn-deg)); transition-duration: 0.15s; }

/* ---------- Preview 侧按钮:按下左移,位移型状态 ---------- */
.preview-btn { cursor: pointer; transition: transform 0.3s cubic-bezier(0.3, 0.7, 0.35, 1); }
.preview-btn.pushed { transform: translateX(var(--push-dx)); }
.preview-btn.shake { animation: btn-shake 0.4s; }
@keyframes btn-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4%); } 75% { transform: translateX(4%); }
}

/* ---------- 三平台插头(2026-07-19 新素材,全程无旋转):默认竖直垂下,
   插入=先到插口正下方更低处对齐、停顿、再纯竖直插入 ---------- */
.plug {
  cursor: pointer;
  transition: transform 0.25s ease-out;
}
.plug.disabled { cursor: default; }
/* 命中区只留插头头部:素材大部分是透明电线,三个插头元素互相叠压,
   透明区不能抢点击 */
.plug .plug-hit {
  position: absolute;
  left: 0; top: 0;
  width: 36%; height: 54%;
  pointer-events: auto;
}
.plug:not(.inserted):not(.extracting):not(.disabled):hover {
  transform: translateY(var(--lift-dy));
}
/* 两段式:先斜向到达插入位正下方的暂存点(与插口竖直对齐、y 更靠下),
   停顿一小下,再纯竖直插入——最后一段一定是直上,和插电线同理。
   暂存点 = translate(ins-dx, ins-dy + stage-drop),x 与插入位相同 */
.plug.inserted { animation: plug-in 0.6s cubic-bezier(0.4, 0, 0.2, 1) both; }
.plug.extracting { animation: plug-out 0.5s cubic-bezier(0.4, 0, 0.2, 1) both; }
@keyframes plug-in {
  0%   { transform: translate(0, 0); }
  50%  { transform: translate(var(--ins-dx), calc(var(--ins-dy) + var(--stage-drop))); }
  64%  { transform: translate(var(--ins-dx), calc(var(--ins-dy) + var(--stage-drop))); }
  100% { transform: translate(var(--ins-dx), var(--ins-dy)); }
}
@keyframes plug-out {
  0%   { transform: translate(var(--ins-dx), var(--ins-dy)); }
  42%  { transform: translate(var(--ins-dx), calc(var(--ins-dy) + var(--stage-drop))); }
  100% { transform: translate(0, 0); }
}

/* ---------- 刻字:浅灰 + 内阴影,"刻在机身上"(60pt ≈ 1.11cqw) ---------- */
.engraved {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.114cqw;
  color: #575b5d;    /* 设计者反馈原 #85888a 太浅,加深保对比 */
  letter-spacing: 0.02em;
  text-shadow:
    0 -0.05em  0.04em rgba(15, 15, 15, 0.5),
    0  0.055em 0.04em rgba(255, 255, 255, 0.9);
  pointer-events: none;
  user-select: none;
  white-space: nowrap;
}
/* 拉杆:竖排自下而上;Preview:竖排自上而下(参考图方向) */
.lever-label { writing-mode: vertical-rl; transform: rotate(180deg); }
.preview-label { writing-mode: vertical-rl; }
/* CJK 在 vertical-rl 下本就竖排正立(自上而下),再 rotate(180) 会字头朝下——
   简/繁/日 去掉这个 180° 翻转,保持字正立(拉丁文才需要翻转成自下而上) */
:lang(zh) .lever-label, :lang(ja) .lever-label { transform: none; }
/* 简繁日「生成推荐」「试听」字距略加大(设计者要求,竖排下沿字轴方向拉开) */
:lang(zh) .lever-label, :lang(ja) .lever-label,
:lang(zh) .preview-label, :lang(ja) .preview-label { letter-spacing: 0.14em; }
/* 生成推荐 简繁 再加约 50 画布px 字距(设计者要求;字号≈60px,0.14em≈8.4px
   → 目标≈58px≈1.08cqw)。日语、试听不变 */
:lang(zh) .lever-label { letter-spacing: 1.08cqw; }
/* 英/法 Generate 竖排(带 180° 翻转)整体再往左上挪一丁点,居中到拉杆上。
   translate 在旋转外层生效(屏幕系):-x 左移、-y 上移 */
:lang(en) .lever-label, :lang(fr) .lever-label {
  transform: translate(0.2cqw, -0.45cqw) rotate(180deg);
}

/* ---------- 专辑转盘:正圆下半(屏幕区),播放时旋转 + 橙色进度弧 ---------- */
.album-disc {
  opacity: 0;
  transition: opacity 0.5s;
  pointer-events: none;
}
.album-disc.visible { opacity: 1; }
.album-art {
  position: absolute; inset: 3%;
  width: 94%; height: 94%;
  border-radius: 50%;
  object-fit: cover;
  background: #101010;
  box-shadow: 0 0 0 0.35cqw #0c0c0c;   /* 唱片外圈黑边 */
}
.album-disc.spinning .album-art { animation: disc-spin 4s linear infinite; }
@keyframes disc-spin { from { transform: rotate(0); } to { transform: rotate(360deg); } }
.album-progress {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  overflow: visible;
}
.album-progress path {
  stroke: #d15d31;
  stroke-width: 2.2;
  stroke-linecap: round;
}

/* ---------- 右屏裁切容器:Preview 播放时内容下移让位 ---------- */
.right-screen-clip { overflow: hidden; pointer-events: none; }
.right-screen-clip > * { pointer-events: auto; }
.right-screen-clip .distance-screen,
.right-screen-clip .radar-wrap {
  transition: transform 0.6s cubic-bezier(0.3, 0.6, 0.3, 1), opacity 0.6s;
}
.right-screen-clip.disc-mode .distance-screen,
.right-screen-clip.disc-mode .radar-wrap {
  transform: translateY(var(--disc-shift));
}

/* ---------- 断电态:三屏无字,控件暗淡(复用 filters 未开启的观感) ---------- */
body.power-off .mid-screen-text,
body.power-off .mid-screen-loading,
body.power-off .distance-screen,
body.power-off .radar-canvas,
body.power-off .candidate-list,
body.power-off .search-input,
body.power-off .search-icon-chip,
body.power-off .album-disc { visibility: hidden; }

body.power-off .dim-slider,
body.power-off .dim-label,
body.power-off .weight-knob,
body.power-off .weight-value,
body.power-off .filter-toggle,
body.power-off .filter-cluster,
body.power-off .filter-label,
body.power-off .plug {
  filter: grayscale(0.9) brightness(0.5) contrast(0.9);
}
/* 以下均为实体硬件,断电后形态不变(不变暗)——设计者反复强调:
   Generate 拉杆本体+连接件+刻字、维度权重印字、Preview 侧钮。
   拉杆刻字 .lever-label 是 .lever-plate 子元素,父级不加滤镜即随之不变。 */

/* 跳闸/恢复期间给控件加位移过渡(平时拖动是即时跟手,不能有过渡) */
body.tripping .dim-thumb { transition: left 0.45s ease-in; }
body.tripping .dim-fill { transition: width 0.45s ease-in, left 0.45s ease-in; }
body.tripping .filter-thumb { transition: left 0.45s ease-in; }
body.tripping .weight-knob img { transition: transform 0.5s ease-in; }
