/* ============================================================
   Cover Flow — iPod 风格 3D 画廊组件
   ------------------------------------------------------------
   结构（由 JS 生成，无需手写）：
     .cf                    舞台，perspective 容器
       .cf-track
         .cf-card           单张卡（4:3 封面 + 倒影 + 两行文字）
           .cf-frame
           .cf-reflect > .cf-reflect-inner
           .cf-meta

   调参方式：全部走 CSS 变量，在 .cf 或任意祖先上覆盖即可。
   几何参数（--cf-gap / --cf-depth / --cf-max-rotate …）由 JS 在
   挂载与 resize 时从计算样式读取，所以改 CSS 就够，不用改 JS。
   ============================================================ */

.cf{
  /* --- 尺寸 --- */
  --cf-card-w: clamp(238px, 29vw, 408px); /* 卡片宽度（唯一的尺寸源头） */
  --cf-aspect: 0.75;                      /* 卡片 高/宽，0.75 = 4:3 */
  --cf-reflect-ratio: 0.42;               /* 倒影高度 / 封面高度，iPod 约 0.4–0.5 */
  --cf-meta-h: 86px;                      /* 卡片下方两行文字预留高度（.cf--float 下不使用） */
  --cf-pad-y: 8px;                        /* 顶部留白，防止旋转时封面被裁边 */
  --cf-radius: 10px;

  /* --- 倒影 ---
     拆成两层，各管一件事：
       filter:brightness() 决定「暗度」——倒影永远比封面暗，与背景黑白无关
       mask-image          决定「渐隐」——只管形状，不参与明暗
     合成到 opacity 上会被背景色冲淡（黑底直接乘 0），别再那么写。 */
  --cf-reflect-dim: 0.72;   /* 接触线处的倒影亮度，1 = 与封面同亮；暗色主题建议 .64 左右 */
  --cf-reflect-seam: .08;   /* 封面与倒影之间的接触阴影强度，0 = 关掉 */
  --cf-reflect-seam-color: 23,21,15;  /* 接触阴影 RGB 三元组，方便配 alpha */
  --cf-reflect-mask: linear-gradient(to bottom,
      rgba(0,0,0,1) 0%,
      rgba(0,0,0,.90) 10%,
      rgba(0,0,0,.74) 24%,
      rgba(0,0,0,.55) 40%,
      rgba(0,0,0,.37) 56%,
      rgba(0,0,0,.22) 70%,
      rgba(0,0,0,.11) 82%,
      rgba(0,0,0,.04) 91%,
      rgba(0,0,0,.008) 97%,
      rgba(0,0,0,0) 100%);

  /* --- 透视与排布 --- */
  --cf-perspective: 1300px;
  --cf-gap: 0.68;          /* 第 1 张邻居的横向间距（× 卡片宽） */
  --cf-gap-2: 0.40;        /* 第 2 张的增量 */
  --cf-gap-3: 0.30;        /* 第 3 张的增量 */
  --cf-depth: 0.72;        /* 纵深后退量（× 卡片宽），越大越像扇形 */
  --cf-depth-max: 2.6;     /* 纵深计算的最大距离 */
  --cf-max-rotate: 58deg;  /* 旋转渐近上限 */
  --cf-rotate-bias: 0.32;  /* 越小，邻居转得越快 */

  /* --- 外观 --- */
  --cf-bg: transparent;    /* 两侧淡出用的底色，跟页面背景一致 */
  --cf-fade: 9%;           /* 两侧淡出宽度 */
  --cf-fg: #17150f;
  --cf-muted: #6e685c;
  --cf-shadow: 0 0 0 1px rgba(23,21,15,.07), 0 26px 48px -22px rgba(23,21,15,.55), 0 3px 10px -3px rgba(23,21,15,.18);
  --cf-sheen: .5;          /* 封面玻璃反光强度 */
  --cf-dim: 0;             /* 压暗层不透明度，JS 按卡片离中心的距离逐帧写 */
  --cf-font: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC",
             "Hiragino Sans GB", "Microsoft YaHei", sans-serif;
  --cf-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, monospace;

  /* --- 占位封面（没有截图时自动使用） --- */
  --cf-ph-bg: #e6e1d6;
  --cf-ph-ink: rgba(23,21,15,.26);
  --cf-ph-ink-2: rgba(23,21,15,.12);

  position: relative;
  width: 100%;
  height: calc(
    var(--cf-card-w) * var(--cf-aspect) * (1 + var(--cf-reflect-ratio))
    + var(--cf-meta-h) + var(--cf-pad-y) + 22px
  );
  perspective: var(--cf-perspective);
  /* 顶部留白：卡片绕 Y 轴旋转时，近端被透视放大，顶端会跑到容器外被 overflow 裁掉。
     实测最大外溢约 3.3px（card-w 460），留 8px 足够，窄屏按比例更小。
     注意 perspective-origin 也要跟着下移，否则正中卡会变梯形。 */
  perspective-origin: 50% calc(var(--cf-pad-y) + var(--cf-card-w) * var(--cf-aspect) * 0.5);
  overflow: hidden;
  touch-action: pan-y;
  cursor: grab;
  outline: none;
  font-family: var(--cf-font);
  color: var(--cf-fg);
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}
.cf:active{ cursor: grabbing; }
.cf:focus-visible{ outline: 2px solid currentColor; outline-offset: -2px; }

/* 两侧淡出，让边缘卡片自然融进背景 */
.cf::after{
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2000;
  pointer-events: none;
  background: linear-gradient(to right,
    var(--cf-bg) 0%,
    transparent var(--cf-fade),
    transparent calc(100% - var(--cf-fade)),
    var(--cf-bg) 100%);
  opacity: .6;
}

.cf-track{
  position: absolute;
  inset: var(--cf-pad-y) 0 0;
  transform-style: preserve-3d;
}

/* --- 单张卡 --- */
.cf-card{
  position: absolute;
  top: 0;
  left: 50%;
  width: var(--cf-card-w);
  display: block;
  color: inherit;
  text-decoration: none;
  transform-style: preserve-3d;
  will-change: transform, opacity;
  backface-visibility: visible;
  cursor: pointer;
}

/* 没有 href 的纯展示卡：光标交还给舞台（grab），点击只做「转到正中」 */
.cf-card--static{ cursor: inherit; }

/* --- 封面 --- */
.cf-frame{
  position: relative;
  aspect-ratio: 1 / var(--cf-aspect);
  border-radius: var(--cf-radius);
  overflow: hidden;
  background: var(--cf-ph-bg);
  box-shadow: var(--cf-shadow);
  transform: translateZ(0);
}
.cf-frame::after{
  /* 玻璃反光，模拟 iPod 屏幕的亮面 */
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(126deg,
    rgba(255,255,255,.26) 0%,
    rgba(255,255,255,.06) 18%,
    rgba(255,255,255,0) 34%,
    rgba(255,255,255,0) 100%);
  mix-blend-mode: screen;
  opacity: var(--cf-sheen);
}
.cf-frame > img,
.cf-frame > video,
.cf-frame > canvas{
  width: 100%;
  height: 100%;
  /* fill = 拉伸铺满，不裁边也不留空（默认）。截图与画框比例接近时几乎看不出变形；
     需要裁满用 cover，需要完整显示用 contain。 */
  object-fit: var(--cf-fit, fill);
  object-position: var(--cf-fit-pos, top center);
  display: block;
}

/* --- 压暗层：远离中心的卡片变暗 ---
   为什么不用 filter:brightness()：
   1. filter 是 grouping property，会把 transform-style 的使用值强制成 flat。
      加在 .cf-card 上，.cf-flip 的 translateZ 抬起就完全失效（实测：加一个空滤镜
      opacity(1)，translateZ(400px) 的效果和 0 一模一样）。现在没炸只是因为正中卡
      的 --cf-dim 恰好是 0，属于运气。
   2. filter 改值要重新栅格化整个图层；opacity 只走合成器。
   黑色叠层 alpha = 1 - brightness，视觉上等价。 */
.cf-dim{
  position: absolute;
  inset: 0;
  z-index: 2;              /* 压在图片和玻璃反光之上 */
  pointer-events: none;
  border-radius: inherit;
  background: #000;
  opacity: var(--cf-dim, 0);
}

/* --- 倒影：封面在「湿地面」上的镜像 ---
   结构是封面的克隆，先 scaleY(-1) 翻上来再落到封面正下方。
   高度只有封面的 --cf-reflect-ratio 倍，所以看到的永远是封面下半截的镜像
   —— 这正是 iPod 上「CD 插在唱片架里」的观感。 */
.cf-reflect{
  position: relative;
  margin-top: 1px;
  height: calc(var(--cf-card-w) * var(--cf-aspect) * var(--cf-reflect-ratio));
  overflow: hidden;
  pointer-events: none;
  /* 只负责「主题暗度」这一个静态值（切亮暗时才变）。
     距离衰减不在这里做 —— 见 .cf-reflect .cf-dim。
     filter 一旦随帧改值，整个图层要重新栅格化。 */
  filter: brightness(var(--cf-reflect-dim, .72));
  -webkit-mask-image: var(--cf-reflect-mask);
  mask-image: var(--cf-reflect-mask);
  /* 翻面时倒影跟着收掉：封面离开货架，镜面里就不该还有它。
     .28s 正好在转到 90° 前淡完；回来时先等封面落地再淡回来（见下面 .is-flipped 那条）。 */
  transition: opacity .28s ease .18s;
}
/* 倒影的距离衰减：叠在 --cf-dim（和封面同一份压暗）之上再加一点，
   让远处的倒影不至于连成一条雾带。brightness(b) 等价于黑色叠层 alpha = 1-b。 */
.cf-reflect .cf-dim{
  opacity: calc(var(--cf-dim, 0) + (1 - var(--cf-refl, 1)));
}
/* 接触阴影：封面压在倒影上的那一道暗缝，没有它倒影会像飘在空中 */
.cf-reflect::after{
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 9%;
  pointer-events: none;
  background: linear-gradient(to bottom,
    rgba(var(--cf-reflect-seam-color), var(--cf-reflect-seam)) 0%,
    rgba(var(--cf-reflect-seam-color), 0) 100%);
}
.cf-reflect-inner{
  position: absolute;
  left: 0;
  bottom: 100%;             /* 先放到倒影框上方，再翻下来 */
  width: 100%;
  height: calc(var(--cf-card-w) * var(--cf-aspect));
  transform: scaleY(-1);
  transform-origin: 50% 100%;
  border-radius: var(--cf-radius);
  overflow: hidden;
  background: var(--cf-ph-bg);
}
.cf-reflect-inner > img,
.cf-reflect-inner > video,
.cf-reflect-inner > canvas{
  width: 100%;
  height: 100%;
  object-fit: var(--cf-fit, fill);
  object-position: var(--cf-fit-pos, top center);
  display: block;
}

/* --- 卡片文字：只有正中的一张显示 --- */
.cf-meta{
  padding-top: 16px;
  text-align: center;
  opacity: var(--cf-meta-op, 1);
}
.cf-meta h3{
  margin: 0;
  font-size: clamp(16px, 1.6vw, 20px);
  font-weight: 600;
  letter-spacing: -.005em;
  line-height: 1.35;
}
.cf-meta p{
  margin: 6px auto 0;
  max-width: 32ch;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--cf-muted);
}

/* ============================================================
   翻面（需要 mount 时传 flip: true）—— 按 Apple Cover Flow 的做法
   ------------------------------------------------------------
   结构：
     .cf-card.cf-card--flip
       .cf-flip            ← 这一层负责「抬起 + 翻面」，preserve-3d
         .cf-face--front   ← 封面 + 倒影
         .cf-face--back    ← 介绍 + 打开按钮，预先转 180°
       .cf-meta
   卡片本身的 3D 位置由 JS 写在 .cf-card 上，翻转写在 .cf-flip 上，两层互不干扰。

   和 Core Animation 的对应关系：
     透视         .cf 上的 perspective       ≈ 容器 layer 的 sublayerTransform.m34
     背面剔除     backface-visibility:hidden ≈ doubleSided = NO
     翻面         rotate: y 0 → 180deg       ≈ transform.rotation.y 0 → π（一条独立动画）
     抬起         translate: 0 0 z 的峰值    ≈ transform.translation.z（另一条独立动画）

   三个必须照抄的点：
   1. 只有 180°，不叠加圈数。多转一圈是二次创作，不是 Apple 的动作。
   2. 抬起走 translateZ（离开货架），不是 translateY（在平面内往上跳）。
      严格说 90° 那一瞬投影宽度仍是 0（零厚度的平面必然如此），抬 z 的作用是
      让整张卡在翻面过程中持续变大、并带出「离开货架」的位移感，
      否则看起来只是被横向压扁再弹回来。倒影同时收掉，物体离地才有重量。
   3. 旋转和抬起是两条独立的动画 —— Core Animation 里本来就是两个 keyPath。
      CSS 里靠 `rotate` / `translate` 这两个独立属性实现（不是 transform 函数），
      所以旋转可以走 transition：中途再点一次会从当前角度平滑折返，不会跳。
      都塞进 transform 就只能用一条 animation，打断时必然snap回 0° 或 180°。
   ============================================================ */
.cf-card--flip{ cursor: pointer; outline: none; }
.cf-card--flip:focus-visible{
  outline: 2px solid var(--cf-accent, currentColor);
  outline-offset: 4px;
  border-radius: var(--cf-radius);
}

.cf-flip{
  position: relative;
  transform-style: preserve-3d;
  /* 抬起量：0.18 × 卡宽（460 → 83px）。配合 perspective 1300，
     中途放大约 6.8%，正好抵消 90° 处被压成一条棱的塌陷。 */
  --cf-flip-lift: calc(var(--cf-card-w) * .18);
  --cf-flip-dur: .6s;
  rotate: y 0deg;
  /* ease-in-out 对应 kCAMediaTimingFunctionEaseInEaseOut：
     起步、加速穿过侧棱、减速落定 */
  transition: rotate var(--cf-flip-dur) cubic-bezier(.42,0,.25,1);
}
.cf-card.is-flipped .cf-flip{ rotate: y 180deg; }

/* 抬起：只动 translate，和上面的 rotate 互不覆盖。
   translate 在 rotate 之前应用，所以 z 永远是朝观众推，不随卡片转向而偏。 */
@keyframes cf-flip-lift{
  0%, 100% { translate: 0 0 0; }
  50%      { translate: 0 0 var(--cf-flip-lift); }
}
.cf-card.is-animating .cf-flip{
  animation: cf-flip-lift var(--cf-flip-dur) ease-in-out;
}
@media (prefers-reduced-motion: reduce){
  .cf-flip{ transition: none; }
  .cf-card.is-animating .cf-flip{ animation: none; }
}

.cf-face{
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
/* backface-visibility 只影响绘制，不影响命中测试：
   翻过去的那一面依然会挡住鼠标，所以这里要手动关掉它的 pointer-events */
.cf-face--front{
  position: relative;
  transform: rotateY(0deg);
  pointer-events: auto;
}
.cf-face--back{
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: calc(var(--cf-card-w) * var(--cf-aspect));
  transform: rotateY(180deg);
  pointer-events: none;
  border-radius: var(--cf-radius);
  overflow: hidden;
  background: var(--cf-back-bg, #141414);
  box-shadow: var(--cf-shadow);
  /* 字号跟着卡片宽度走，背面才能等比缩放 */
  font-size: calc(var(--cf-card-w) * 0.032);
  color: var(--cf-fg);
}
.cf-card.is-flipped .cf-face--front{ pointer-events: none; }
.cf-card.is-flipped .cf-face--back{ pointer-events: auto; }

.cf-back{
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .7em;
  height: 100%;
  padding: .9em 1.15em;
  text-align: left;
}
.cf-back-kicker{
  margin: 0;
  font-family: var(--cf-mono);
  font-size: .68em;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--cf-muted);
}
.cf-back h3{
  margin: 0;
  font-size: 1.3em;
  font-weight: 600;
  line-height: 1.28;
  letter-spacing: -.01em;
}
.cf-back-desc{
  margin: 0;
  font-size: .9em;
  line-height: 1.55;
  color: var(--cf-muted);
  /* 背面空间有限，最多三行 */
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.cf-back-btn{
  display: inline-flex;
  align-items: center;
  gap: .5em;
  align-self: flex-start;
  margin-top: .5em;
  padding: .55em 1em;
  border-radius: var(--cf-radius);
  background: var(--cf-accent, #fff);
  color: var(--cf-accent-ink, #000);
  font-family: var(--cf-font);
  font-size: .85em;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
  transition: opacity .18s, transform .18s;
}
.cf-back-btn:hover{ opacity: .88; }
.cf-back-btn:active{ transform: scale(.97); }
.cf-back-btn.is-disabled{
  background: transparent;
  border: 1px solid var(--cf-back-line, rgba(128,128,128,.35));
  color: var(--cf-muted);
  cursor: default;
}
.cf-back-arrow{ font-size: .9em; }

/* 翻开后收掉下方文字：标题和介绍已经在背面上了。
   两边的延迟是分开的：翻开立刻淡出（封面一起身就没人看字了），
   翻回来要等封面落地才淡入，否则字会先于封面出现在还在转的背面上。 */
.cf-card.is-flipped .cf-meta{ opacity: 0; transition-delay: 0s; }
.cf-meta{ transition: opacity .3s ease .2s; }
.cf-card.is-flipped .cf-reflect{ opacity: 0; transition-delay: 0s; }

/* --- 可选：文字浮在倒影上（.cf--float） ---
   默认文字排在倒影下面，会把整块往下顶。加上 .cf--float 后文字绝对定位
   进倒影区域，高度公式不再为它留 --cf-meta-h，画廊整体收紧。
   位置由 --cf-meta-anchor 控制：0 = 紧贴封面，1 = 落在倒影底部。 */
.cf--float{
  height: calc(
    var(--cf-card-w) * var(--cf-aspect) * (1 + var(--cf-reflect-ratio))
    + var(--cf-pad-y) + 18px
  );
}
.cf--float .cf-meta{
  position: absolute;
  left: 4%;
  width: 92%;
  padding-top: 0;
  top: calc(
    var(--cf-card-w) * var(--cf-aspect) *
    (1 + var(--cf-reflect-ratio) * var(--cf-meta-anchor, .1))
  );
}
/* 文字紧贴封面时会压在倒影最亮的那一段上，暗底尤其糊。
   用一层极轻的同色投影兜底，不是色块、看不出边界；不想要就设 transparent。 */
.cf--float .cf-meta h3,
.cf--float .cf-meta p{
  text-shadow:
    0 0 10px var(--cf-meta-halo, var(--cf-bg)),
    0 1px 2px var(--cf-meta-halo, var(--cf-bg));
}

/* ============================================================
   占位封面：没有截图时用一张「迷你网页」，可整体替换
   ============================================================ */
.cf-ph{
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  background: var(--cf-ph-bg);
}
.cf-reflect-inner .cf-ph{ position: absolute; inset: 0; }
.cf-ph-top{
  display: flex;
  align-items: center;
  gap: 4px;
  height: 22px;
  padding: 0 9px;
  border-bottom: 1px solid var(--cf-ph-ink-2);
  flex: none;
}
.cf-ph-top i{
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--cf-ph-ink);
}
.cf-ph-top span{
  margin-left: 7px;
  flex: 1;
  height: 6px;
  border-radius: 3px;
  background: var(--cf-ph-ink-2);
}
.cf-ph-hero{
  flex: 1.2;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 8px;
  padding: 0 9%;
  background: linear-gradient(135deg, var(--cf-c1, #d9501e), var(--cf-c2, #a83a2c));
}
.cf-ph-h{
  height: 13px;
  width: 64%;
  border-radius: 3px;
  background: rgba(255,255,255,.88);
}
.cf-ph-h2{
  height: 6px;
  width: 40%;
  border-radius: 3px;
  background: rgba(255,255,255,.5);
}
.cf-ph-body{
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 7px;
  padding: 0 9%;
}
.cf-ph-l{
  height: 6px;
  width: 100%;
  border-radius: 3px;
  background: var(--cf-ph-ink-2);
}
.cf-ph-l.is-short{ width: 58%; }
.cf-ph-blocks{
  display: flex;
  gap: 7px;
  margin-top: 3px;
}
.cf-ph-blocks b{
  flex: 1;
  height: 22px;
  border-radius: 4px;
  background: var(--cf-ph-ink-2);
}

/* ============================================================
   响应式默认值（可在宿主页面覆盖）
   ============================================================ */
@media (max-width: 860px){
  .cf{
    --cf-card-w: min(72vw, 400px);
    --cf-meta-h: 96px;
    --cf-perspective: 1000px;
  }
}

@media (prefers-reduced-motion: reduce){
  .cf-card{ will-change: auto; }
}
