/* ============================================================
   rail.css — Album Rail（唱片架）
   参照 React Bits Pro 的 Album Rail 参数与交互用原生 CSS/JS 复刻：
   扇形封面 · 聚焦居中 · 黑胶抽出旋转 · 刻度尺控件 · 标题随轨道移动。
   不复制其源码（Pro 组件未公开），实现为本项目自写。

   参数用 CSS 变量暴露，改这里就能调手感：
     --sleeve  封面边长        --spread 同侧相邻间距（倍封面宽）
     --stack   远端堆叠密度    --fan    扇形倾斜角
     --turn    侧向旋转角      --depth  纵深
   ============================================================ */

.rail {
  --sleeve: clamp(210px, 26vw, 330px);
  /* 舞台要容得下侧向封面旋转后的投影，否则会被裁掉顶边 */
  --stage-h: calc(var(--sleeve) * 1.42);
  --spread: 0.62;
  --stack: 0.6;
  --fan: 6deg;
  --turn: 20deg;
  --depth: 0.5;
  --dim: 0.3;
  --rail-ease: cubic-bezier(0.16, 1, 0.3, 1);

  position: relative;
  width: 100%;
}

/* ---------------- 舞台 ---------------- */

.rail__stage {
  position: relative;
  height: var(--stage-h);
  perspective: var(--persp, 1400px);
  perspective-origin: 50% 46%;
  /* 两侧封面在窄屏会溢出版心，直接裁在版心边缘，看起来才像刻意的 */
  overflow: hidden;
  user-select: none;
  cursor: grab; /* 可以拖：光标先告诉用户 */
}

/* 按住的时候换成「抓住」，松手回到 grab */
.rail.is-dragging .rail__stage {
  cursor: grabbing;
}

.rail__covers {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  transform-style: preserve-3d;
}

.rail__item {
  position: absolute;
  top: 50%;
  left: 50%;
  width: var(--sleeve);
  height: var(--sleeve);
  margin: calc(var(--sleeve) / -2) 0 0 calc(var(--sleeve) / -2);
  transform-style: preserve-3d;
  transform-origin: 50% 50%;
  transition: transform 760ms var(--rail-ease), filter 520ms var(--ease),
    opacity 520ms var(--ease);
  will-change: transform;
  /* 越靠外越沉入背景 */
  filter: brightness(calc(1 - var(--d, 0) * var(--dim) * 0.9))
    saturate(calc(1 - var(--d, 0) * var(--dim) * 0.55));
}

/* 悬停提亮：纯 CSS，不走 JS。
   旋转的黑胶会从光标下方反复划过，用 JS 的 enter/leave 会跟着闪 */
.rail__item:hover {
  filter: brightness(1) saturate(1);
}

.rail__item.is-focused {
  cursor: pointer;
}

/* 一屏之外的唱片：收起来，进出画面走淡入淡出。
   （跨半圈的换手帧由 JS 单独掐掉过渡，所以这里不用 transition: none） */
.rail__item.is-culled {
  opacity: 0;
  pointer-events: none;
}

/* ---------------- 封面 ---------------- */

.sleeve {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: 14px;
  background: linear-gradient(148deg, var(--c1, #2a2724) 0%, var(--c2, #121114) 100%);
  box-shadow: 0 26px 60px -28px rgb(0 0 0 / 0.9), inset 0 0 0 1px rgb(255 255 255 / 0.07);
  transform: translateZ(0.1px);
}

/* 唱片封面：真实素材铺满，压在文字之下 */
.sleeve__photo {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: saturate(0.92) brightness(0.9);
  opacity: 0;
  transition: opacity 0.6s ease;
}

/* 封面来自 Spotify CDN，慢的时候先露出底衬，到货再淡入 */
.sleeve__photo.is-loaded {
  opacity: 1;
}

/* 封面上的文字压一层暗底，照片再亮也读得清（用 ::after，避免盖掉纸纹的 ::before） */
.sleeve.has-photo::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgb(8 8 10 / 0.15) 0%, rgb(8 8 10 / 0.72) 68%);
}

.sleeve.has-photo > * {
  position: relative;
  z-index: 2;
}

/* 纸面颗粒：两层极淡的斜纹，远看是印刷感，近看不是噪点 */
.sleeve::before {
  content: "";
  position: absolute;
  inset: 0;
  opacity: 0.28;
  background-image: repeating-linear-gradient(
      115deg,
      rgb(255 255 255 / 0.035) 0 1px,
      transparent 1px 3px
    ),
    repeating-linear-gradient(25deg, rgb(0 0 0 / 0.05) 0 1px, transparent 1px 4px);
}

/* 右下角压暗，让文字始终压得住 */
.sleeve::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(120% 90% at 28% 18%, transparent 40%, rgb(0 0 0 / 0.5) 100%);
}

/* 封面上只留作者一行：歌名已移到下方字幕（那里点一下能复制） */
.sleeve__meta {
  position: absolute;
  z-index: 1;
  font-family: var(--font-latin);
  color: var(--c-ink, #f5f5f7);
  left: 6.5%;
  right: 6.5%;
  bottom: 6%;
  font-size: clamp(7.5px, calc(var(--sleeve, 240px) * 0.042), 10px);
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.62;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* 浅色封面（米白、沙色）用深色字 */
.sleeve.is-light .sleeve__meta {
  color: #16150f;
}

/* ---------------- 黑胶 ---------------- */

/* 两层结构：外层只负责「抽出 / 收回」的位移，内胆只负责旋转。
   两者分开后，位移可以平滑过渡，不会被旋转动画抢走 transform */
.rail__vinyl {
  position: absolute;
  inset: 0;
  z-index: -1;
  /* 纯装饰：旋转时它探出封面外侧，如果参与命中测试，
     旋转会从静止的光标下方反复划过，导致 hover 一明一暗地抖 */
  pointer-events: none;
  transform: translate3d(0, 0, -1px) scale(0.94);
  transition: transform 760ms var(--rail-ease), opacity 320ms var(--ease);
}

.rail__vinyl__disc {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  /* 纹路在上、盘体在下，否则被不透明的盘体盖住，看起来是一块死黑 */
  background: repeating-radial-gradient(
      circle at 50% 50%,
      rgb(255 255 255 / 0.07) 0 1px,
      transparent 1px 6px
    ),
    linear-gradient(122deg, rgb(255 255 255 / 0.14) 0%, transparent 26%, transparent 66%, rgb(255 255 255 / 0.05) 100%),
    radial-gradient(circle at 50% 50%, #08070a 0 33%, #050509 34% 100%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.22), 0 22px 50px -22px rgb(0 0 0 / 0.95);
}

/* 唱片中心标签：一圈圆 + 一点强调色，示意「盘面标签」 */
.rail__vinyl__disc::after {
  content: "";
  position: absolute;
  inset: 38%;
  border-radius: 50%;
  background: radial-gradient(circle, var(--c1, #2a2724) 0 58%, #1c1a22 59 100%);
  box-shadow: inset 0 0 0 1px rgb(255 255 255 / 0.18);
}

/* 非聚焦的封面不显示唱片：侧向封面的黑胶会在旋转后从边角漏出来 */
.rail__item:not(.is-focused) .rail__vinyl {
  opacity: 0;
}

.rail__vinyl__disc::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 62%;
  width: 4px;
  height: 4px;
  margin: -2px 0 0 -2px;
  border-radius: 50%;
  background: rgb(255 255 255 / 0.42);
}

/* 悬停：唱片探出一点（仅未播放时，避免和播放态打架） */
.rail__item.is-focused:not(.is-playing):hover .rail__vinyl {
  transform: translate3d(30%, 0, -1px) scale(0.94);
}

/* 播放：继续探出到 52%——位移由过渡完成，不再被动画顶掉 */
.rail__item.is-playing .rail__vinyl {
  transform: translate3d(52%, 0, -1px) scale(0.94);
}

/* 旋转只作用在内胆上。动画常驻但默认「暂停」：
   停止时它就停在当前角度，不会瞬间弹回 0°，看起来像转盘刹住 */
.rail__vinyl__disc {
  animation: vinyl-spin 1818ms linear infinite;
  animation-delay: 160ms;
  animation-play-state: paused;
}

/* 播放时继续转；起转延后一点，让唱片先滑出来再转起来 */
.rail__item.is-playing .rail__vinyl__disc {
  animation-play-state: running;
}

/* 33 RPM = 每转 60/33 ≈ 1.818 秒；只转，不碰位移 */
@keyframes vinyl-spin {
  to {
    transform: rotate(360deg);
  }
}

.rail__item.is-playing .sleeve {
  box-shadow: 0 26px 60px -26px rgb(0 0 0 / 0.95), inset 0 0 0 1px rgb(255 255 255 / 0.12);
}

/* ---------------- 标题（随轨道移动） ---------------- */

.rail__caption {
  position: relative;
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 14px;
  height: 26px;
  margin-top: 8px;
  font-family: var(--font-cn);
  transition: opacity 240ms var(--ease), transform 240ms var(--ease);
}

.rail__caption.is-swapping {
  opacity: 0;
  transform: translateY(4px);
}

.rail__title {
  font-size: 15px;
  font-weight: 500;
  color: var(--ink);
}

.rail__artist {
  font-family: var(--font-latin);
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--muted);
}

/* 歌名 / 作者：点一下就复制。复制成功后短暂显示「已复制」 */
.rail__title.is-copyable,
.rail__artist.is-copyable {
  cursor: copy;
  border-radius: 3px;
  transition: color var(--t1) var(--ease), background-color var(--t1) var(--ease);
}

.rail__title.is-copyable:hover,
.rail__artist.is-copyable:hover {
  color: var(--ember);
  background: color-mix(in srgb, var(--ember) 12%, transparent);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--ember) 12%, transparent);
}

.rail__title.is-copyable.is-copied,
.rail__artist.is-copyable.is-copied {
  color: var(--ember);
}

.rail__title.is-copied::after,
.rail__artist.is-copied::after {
  content: attr(data-text);
  margin-left: 8px;
  font-family: var(--font-cn);
  font-size: 9px;
  letter-spacing: 0.1em;
  opacity: 0.9;
}

/* 连接 Spotify（只有配了 Client ID 且未授权时才出现） */
.rail__connect {
  margin: 10px 0 0;
  padding: 7px 14px;
  border: 0;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--line);
  background: none;
  color: var(--muted);
  font-family: var(--font-cn);
  font-size: 10px;
  letter-spacing: 0.12em;
  cursor: pointer;
  transition: color var(--t1) var(--ease), box-shadow var(--t1) var(--ease);
}

.rail__connect:hover {
  color: var(--ember);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ember) 55%, transparent);
}

.rail__state {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-latin);
  font-size: 9px;
  letter-spacing: 0.2em;
  color: var(--ember);
  opacity: 0;
  transition: opacity 240ms var(--ease);
}

.rail.is-playing .rail__state {
  opacity: 1;
}

.rail__state i {
  width: 4px;
  height: 4px;
  border-radius: 50%;
  background: currentColor;
}

/* 没在播放时也要能说一句话（没有音源 / 试听结束 / 被浏览器拦了） */
.rail__state.is-shown {
  opacity: 1;
}

/* 30 秒试听进度条：只有真的出声时才出现 */
.rail__progress {
  width: min(340px, 62%);
  height: 2px;
  margin: 7px auto 0;
  background: var(--line);
  border-radius: 2px;
  overflow: hidden;
  opacity: 0;
  transition: opacity 240ms var(--ease);
}

.rail.is-playing .rail__progress {
  opacity: 1;
}

.rail__progress i {
  display: block;
  height: 100%;
  background: var(--ember);
  transform: scaleX(0);
  transform-origin: left center;
}

/* ---------------- 控件：刻度尺 + 播放 + 箭头 ---------------- */

.rail__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-top: 6px;
  padding: 0 2px;
}

.rail__ruler {
  position: relative;
  flex: 1;
  max-width: 340px;
  height: 34px;
  overflow: hidden;
  cursor: pointer;
  touch-action: pan-y;
}

.rail__ticks {
  position: absolute;
  top: 50%;
  left: 50%;
  display: flex;
  align-items: center;
  gap: var(--tick-gap, 16px);
  transform: translate(-50%, -50%);
  transition: transform 520ms var(--rail-ease);
}

.rail__tick {
  width: 1px;
  height: 9px;
  flex: none;
  background: var(--line);
  transition: height 320ms var(--ease), background-color 320ms var(--ease);
}

.rail__tick.is-active {
  height: 17px;
  background: var(--ember);
}

/* 随机播放键：放在唱片架下方居中的胶囊按钮。
   播放/暂停交给「点唱片」，这里只负责抽一张 */
.rail__shuffle-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: clamp(14px, 2.4vh, 26px);
}

.rail__shuffle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 11px 22px 11px 18px;
  border: 0;
  border-radius: 999px;
  box-shadow: inset 0 0 0 1px var(--line);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-cn);
  font-size: 12px;
  letter-spacing: 0.14em;
  cursor: pointer;
  transition: color var(--t1) var(--ease), background-color var(--t1) var(--ease),
    box-shadow var(--t1) var(--ease), transform var(--t1) var(--ease);
}

.rail__shuffle svg {
  width: 17px;
  height: 17px;
  transition: transform var(--t2) var(--ease);
}

.rail__shuffle-label {
  font-family: var(--font-cn);
  font-size: 12px;
  letter-spacing: 0.16em;
}

.rail__shuffle:hover {
  color: #101012;
  background: var(--ink);
  box-shadow: inset 0 0 0 1px var(--ink);
}

.rail__shuffle:hover svg {
  transform: translateX(2px);
}

.rail__shuffle:active {
  transform: scale(0.97);
}

.rail__shuffle-hint {
  margin: 8px 0 0;
  text-align: center;
  font-family: var(--font-cn);
  font-size: 10px;
  letter-spacing: 0.1em;
  color: var(--muted-2);
  opacity: 0.7;
}

/* 抽奖进行中：整颗点亮成 Ember，且点不了。
   注意不要给图标加旋转动画——那会被当成「点一下就一直在转」的 bug */
.rail.is-raffling .rail__shuffle {
  color: var(--ember);
  background: color-mix(in srgb, var(--ember) 16%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ember) 55%, transparent);
  pointer-events: none;
}

/* 滚轮连续翻片时把过渡压短，唱片才跟得上手；
   停手 180ms 后 JS 摘掉这个类，最后一张回到正常缓动自然收尾 */
.rail.is-sweeping .rail__item {
  transition-duration: 240ms;
}

.rail.is-sweeping .rail__ticks {
  transition-duration: 240ms;
}

/* 抽奖期间位置是每帧直接写死的（JS 已经把减速做在曲线里），
   所以 transform / filter 一律不过渡 —— 留着过渡只会让画面慢半拍、末尾发抖；
   opacity 只留很短一段：高速滚动时唱片一闪而过，
   淡出必须比它滚到环形换手点更快，否则会在半透明状态下横穿画面 */
.rail.is-raffling .rail__item {
  transition: opacity 90ms linear;
}

.rail.is-raffling .rail__ticks {
  transition: none;
}

/* 拖动 / 惯性滑行的道理和抽奖一样：位置由 JS 逐帧写死，
   transform 留着过渡只会慢半拍；只保留 opacity，让进出画面的唱片淡进淡出 */
.rail.is-dragging .rail__item,
.rail.is-gliding .rail__item {
  transition: opacity 260ms var(--ease);
}

.rail.is-dragging .rail__ticks,
.rail.is-gliding .rail__ticks {
  transition: none;
}

/* 抽奖结束的「开出」闪光：只在落定那一下亮一圈，滚动途中不闪 */
.rail.is-landed .rail__item.is-focused .sleeve {
  animation: rail-pop 520ms var(--ease);
}

@keyframes rail-pop {
  0% {
    box-shadow: 0 0 0 0 rgb(210 105 74 / 0.7), 0 26px 60px -28px rgb(0 0 0 / 0.9);
  }
  100% {
    box-shadow: 0 0 0 26px rgb(210 105 74 / 0), 0 26px 60px -28px rgb(0 0 0 / 0.9);
  }
}

/* 唱片架与下方说明之间留出呼吸 */
.rail + .note {
  margin-top: 40px;
}

/* ---------------- 移动端 ---------------- */

@media (max-width: 700px) {
  .rail {
    --sleeve: clamp(132px, 44vw, 190px);
    --stage-h: calc(var(--sleeve) * 1.5);
    --spread: 0.52;
    --turn: 18deg;
  }

  .sleeve__meta {
    font-size: 8px;
    bottom: 14px;
  }

  .rail__caption {
    gap: 10px;
    margin-top: 16px;
  }

  .rail__ticks {
    gap: 13px;
  }
}

/* 尊重系统的减少动态效果：不做旋转与位移动画 */
@media (prefers-reduced-motion: reduce) {
  .rail__item,
  .rail__ticks,
  .rail__vinyl {
    transition-duration: 1ms;
  }

  .rail__item.is-playing .rail__vinyl {
    animation: none;
  }
}
