/* =======================================================================
   开箱剧场 (Reveal) · 从被点击的格子"取出"一张标本卡，再交棒给解释卡
   与 app.js 的 playReveal() 配套；--reveal-total 必须等于 JS 的 REVEAL_MS。

   时间线（总 2550ms）：
     0–1050    卡片从格子的位置与大小长到居中大卡（FLIP，JS 用 WAAPI 驱动）
     0–1250    光环从格子处铺开并旋转，星屑飞散
     0–900     卡面掠光 + 卡框/文字/星级逐级浮现
     220–900   背面辉光（aura）旋转展开
     1530      交棒：解释卡开始淡入，卡片与面纱让位（620ms）
     2150      剧场收尾

   所有动画只动 transform / opacity / filter，因此全程在合成器上跑，
   不触发重排重绘——这是"丝滑"的物理原因。
   ======================================================================= */

.pm-reveal {
  --reveal-total: 2550ms;
  --card-w: 280px;
  --card-h: 380px;
  --dock-gap: 70px;
  pointer-events: auto;
  cursor: default;
}

/* ---------------- 卡片舞台：JS 在这里做 FLIP ---------------- */
.pm-reveal-stage {
  position: relative;
  width: var(--card-w);
  height: var(--card-h);
  transform-origin: center center;
  will-change: transform, opacity;
}

.pm-reveal-card {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  --face-x: 0px;
  --face-y: 0px;
  --near-x: 0px;
  --near-y: 0px;
  --lift: 0;
  /* 指针倾斜；perspective 由 .pm-reveal 提供，卡片自身再加一层做"厚度"。
     角度由 holo-foil.js 每帧阻尼写入，这里不能加 transition（会一顿一顿的）。 */
  transform: perspective(900px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg)) scale(calc(1 + var(--lift)));
  transform-style: preserve-3d;
  will-change: transform;
  user-select: none;
  -webkit-user-select: none;
}

/* ---------------- 3D 双面卡牌与实体厚度结构 (holo-card-studio 体系) ---------------- */
.pm-card-flipper {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.62s cubic-bezier(0.2, 0.8, 0.25, 1);
}
.pm-reveal-card.is-flipped .pm-card-flipper {
  transform: rotateY(180deg);
}

/* 卡牌侧边的"实体厚度"。它是一整块 translateZ(-2px) 的渐变板，
   本意只是给卡牌一圈边；但翻转时它与 .pm-card-flipper 同处一个 3D 上下文，
   浏览器会把它排到背面之上，于是整张卡背（烫金浮雕、属性表格、典藏落款）
   被这层金色渐变糊成一片。修法是把它掏空成环——中间必须不可见。

   掏空靠 mask-composite，而"支持 mask 但不支持 composite"的浏览器会把两层遮罩
   取并集 → 等于没遮 → 又变回整块板。所以这里用 @supports 明确兜底：
   拿不到镂空能力就干脆不画这块板，卡边的金线交给两个卡面自己的 border。 */
.pm-card-edge { display: none; }
@supports ((mask-composite: exclude) or (-webkit-mask-composite: xor)) {
  .pm-card-edge {
    display: block;
    position: absolute;
    inset: -1px;
    border-radius: 17px;
    background: linear-gradient(135deg, rgba(255, 230, 160, 0.55) 0%, rgba(46, 34, 16, 0.92) 38%, rgba(255, 230, 160, 0.68) 68%, rgba(30, 24, 12, 0.92) 100%);
    transform: translateZ(-2px);
    pointer-events: none;
    box-shadow: 0 20px 48px rgba(0, 0, 0, 0.7);
    padding: 2px;
    box-sizing: border-box;
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
  }
}

.pm-card-face {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border-radius: 16px;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  transform-style: preserve-3d;
  box-sizing: border-box;
  overflow: hidden;
  user-select: none;
  -webkit-user-select: none;
}
.pm-card-front {
  z-index: 2;
  transform: rotateY(0deg);
  background: radial-gradient(circle at 50% 40%, #0d1620, #04080c 85%);
  /* 分层减法：这里是卡面金边的"唯一一道实体线"。
     原先 2px / 0.85 的金框，叠上着色器自带的外圈金线与 .pm-card-bevel-rim 内圈金线
     就是四道亮边同时亮，卡框比标本还抢眼。收成 1px，让着色器的 rim 去补那道锐光。 */
  border: 1px solid rgba(255, 230, 160, 0.55);
  box-shadow:
    inset 0 0 0 1px rgba(255, 245, 210, 0.22),
    inset 0 0 34px rgba(0, 0, 0, 0.95),
    0 24px 60px -12px rgba(0, 0, 0, 0.85);
}
.pm-card-bevel-rim {
  position: absolute;
  inset: 5px;
  border: 1px solid rgba(255, 226, 155, 0.18);
  border-radius: 11px;
  pointer-events: none;
  z-index: 6;
  box-shadow: inset 0 0 10px rgba(255, 226, 155, 0.06);
}

.pm-card-front .pm-reveal-frame {
  width: 100%;
  height: 100%;
  position: relative;
  border-radius: 14px;
  overflow: hidden;
}

/* 当着色器接管深度视差与叠色时，隐藏原始平面 img 与纯色背景 */
.pm-reveal-card.has-shader-tex .pm-reveal-object {
  opacity: 0 !important;
  pointer-events: none;
}
.pm-reveal-card.has-shader-tex .pm-reveal-frame {
  background: transparent !important;
}

/* 典藏正面文字层次：带投射阴影，悬浮于 3D 标本与背景之上 */
.pm-reveal-heading,
.pm-reveal-name,
.pm-reveal-discovery {
  position: relative;
  z-index: 10;
  pointer-events: none;
  filter: drop-shadow(0 2px 5px rgba(0, 0, 0, 0.85));
}
.pm-reveal-heading {
  position: absolute;
  top: 14px;
  left: 14px;
  display: flex;
  flex-direction: column;
}
.pm-reveal-heading span {
  font-family: 'SF Mono', 'Courier New', monospace;
  font-size: 10px;
  letter-spacing: 2.5px;
  color: #ffe6a0;
  text-shadow: 0 0 8px rgba(255, 230, 160, 0.6);
}
.pm-reveal-heading b {
  font-size: 30px;
  font-weight: 800;
  color: #fffdf5;
  letter-spacing: -0.5px;
  text-shadow: 0 0 14px rgba(255, 230, 160, 0.5);
  line-height: 1.05;
}
.pm-reveal-heading small {
  font-size: 9.5px;
  color: #b8d4c5;
  letter-spacing: 1px;
  text-transform: uppercase;
  font-weight: 500;
  margin-top: 2px;
}
.pm-reveal-name {
  position: absolute;
  bottom: 14px;
  left: 14px;
  display: flex;
  flex-direction: column;
  font-size: 24px;
  font-weight: 800;
  color: #ffffff;
  text-shadow: 0 2px 14px rgba(0, 0, 0, 0.9);
}
.pm-reveal-name span {
  font-size: 9.5px;
  letter-spacing: 2px;
  color: #ffe6a0;
  font-weight: 600;
  margin-top: 1px;
}
.pm-reveal-stars {
  display: flex;
  gap: 2px;
  margin-top: 4px;
  color: #ffd466;
  font-size: 12px;
  text-shadow: 0 0 10px rgba(255, 212, 102, 0.7);
}
.pm-reveal-discovery {
  position: absolute;
  top: 12px;
  right: 14px;
  font-size: 9px;
  letter-spacing: 1.5px;
  color: #ffeaa8;
  font-weight: 600;
  background: linear-gradient(135deg, rgba(28, 42, 35, 0.92), rgba(12, 20, 16, 0.95));
  padding: 4px 9px;
  border-radius: 10px;
  border: 1px solid rgba(255, 230, 160, 0.55);
  backdrop-filter: blur(6px);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.5);
  z-index: 10;
}

.pm-card-back {
  transform: rotateY(180deg);
  z-index: 2;
  background: radial-gradient(circle at 50% 35%, #182820, #08120e 75%);
  border: 1px solid rgba(255, 226, 155, 0.5);
  box-shadow:
    inset 0 0 0 1px rgba(255, 240, 190, 0.18),
    inset 0 0 34px rgba(0, 0, 0, 0.8),
    0 18px 48px -8px rgba(0, 0, 0, 0.75);
  color: #e5ede8;
  padding: 16px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
}

/* 典藏卡背内部装饰与排版 */
.pm-card-back-inner {
  position: relative;
  width: 100%;
  height: 100%;
  border: 1px solid rgba(212, 175, 105, 0.45);
  border-radius: 8px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 14px 12px;
  box-sizing: border-box;
  background:
    radial-gradient(circle at 50% 50%, rgba(255, 230, 160, 0.04) 0%, transparent 70%),
    repeating-linear-gradient(45deg, rgba(212, 175, 105, 0.03) 0px, rgba(212, 175, 105, 0.03) 1px, transparent 1px, transparent 8px);
}
.pm-card-back-inner:before {
  content: '';
  position: absolute;
  inset: 3px;
  border: 1px dashed rgba(212, 175, 105, 0.3);
  border-radius: 6px;
  pointer-events: none;
}
.pm-card-back-crest {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 6px;
}
.pm-back-crest-sym {
  font-family: 'STKaiti', 'KaiTi', 'SimSun', serif;
  font-size: 46px;
  font-weight: 700;
  color: #ffe29b;
  text-shadow: 0 0 16px rgba(255, 226, 155, 0.45);
  line-height: 1.1;
}
.pm-back-crest-num {
  font-family: monospace;
  font-size: 11px;
  letter-spacing: 2px;
  color: rgba(255, 226, 155, 0.7);
  margin-top: 2px;
}
.pm-card-back-body {
  text-align: center;
  margin: 8px 0;
}
.pm-back-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: #ffffff;
  letter-spacing: 0.5px;
}
.pm-back-meta {
  margin: 4px 0 10px;
  font-size: 11px;
  color: #a2b8ad;
}
.pm-back-specs {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 226, 155, 0.15);
  border-radius: 6px;
  padding: 6px 4px;
}
.pm-back-specs div {
  display: flex;
  flex-direction: column;
  align-items: center;
}
.pm-back-specs small {
  font-size: 9px;
  color: rgba(255, 226, 155, 0.65);
}
.pm-back-specs b {
  font-size: 11px;
  color: #eef5f0;
  margin-top: 2px;
}
.pm-card-back-foot {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  border-top: 1px solid rgba(212, 175, 105, 0.25);
  padding-top: 6px;
}
.pm-card-back-foot span {
  font-size: 11px;
  font-weight: 600;
  color: #ffe29b;
  letter-spacing: 1px;
}
.pm-card-back-foot small {
  font-size: 8px;
  color: rgba(255, 255, 255, 0.5);
  letter-spacing: 1.5px;
  margin-top: 2px;
}

/* 三层视差：照片（中景）随倾斜轻微让位，标题与卡名（近景）朝相反方向多让一点，
   边缘（远景）不动。视线一动就能看到三层分开，卡面像有厚度的实物。 */
.pm-reveal-object {
  transform: translate3d(var(--face-x, 0px), var(--face-y, 0px), 0);
  will-change: transform;
}
.pm-reveal .pm-reveal-heading,
.pm-reveal .pm-reveal-name,
.pm-reveal-card > .pm-reveal-discovery,
.pm-card-front > .pm-reveal-discovery {
  transform: translate3d(var(--near-x, 0px), var(--near-y, 0px), 0);
  will-change: transform;
}

/* ---------------- 按钮：挂在卡片下方（赏卡模式控制台） ---------------- */
.pm-reveal-dock {
  position: absolute;
  left: 50%;
  bottom: calc(50% - (var(--card-h) / 2 + var(--dock-gap)));
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 10px;
  z-index: 35;
  pointer-events: auto;
  white-space: nowrap;
  animation: pm-dock-in 520ms 620ms cubic-bezier(.16, 1, .3, 1) both;
}
.pm-reveal-dock button {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 15px;
  font-size: 13px;
  font-weight: 500;
  color: #f0f7f3;
  background: rgba(20, 36, 28, 0.82);
  border: 1px solid rgba(255, 230, 160, 0.35);
  border-radius: 20px;
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  cursor: pointer;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.35);
  transition: all 0.2s cubic-bezier(0.16, 1, 0.3, 1);
  user-select: none;
}
.pm-reveal-dock button:hover {
  background: rgba(32, 58, 46, 0.95);
  border-color: rgba(255, 230, 160, 0.7);
  color: #ffffff;
  transform: translateY(-2px);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}
.pm-reveal-dock button:active {
  transform: translateY(0);
}
.pm-dock-icon {
  font-size: 13px;
  line-height: 1;
}
.pm-reveal-dock .pm-reveal-act-view {
  background: linear-gradient(135deg, rgba(46, 80, 62, 0.9), rgba(28, 52, 40, 0.9));
  border-color: rgba(255, 226, 155, 0.6);
  color: #ffe9b6;
}
.pm-reveal-dock .pm-reveal-act-view:hover {
  background: linear-gradient(135deg, rgba(62, 108, 84, 0.98), rgba(38, 70, 54, 0.98));
  border-color: #ffe9b6;
  color: #ffffff;
}
@keyframes pm-dock-in {
  from { opacity: 0; transform: translate(-50%, 14px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
.pm-reveal-act-sound {
  color: #ffe9b6;
  background: rgba(35, 52, 42, .78);
  border: 1px solid rgba(255, 226, 155, .45);
  backdrop-filter: blur(8px);
}
.pm-reveal-act-sound:hover { background: rgba(48, 70, 56, .9); color: #fff6de; }

/* ---------------- 提示与跳过 ---------------- */
.pm-reveal-hint {
  position: absolute;
  left: 50%;
  bottom: calc(50% - (var(--card-h) / 2 + var(--dock-gap) + 38px));
  transform: translateX(-50%);
  animation: pm-hint-in 460ms 1500ms ease both;
  font-size: 12px;
  color: rgba(255, 255, 255, 0.72);
  white-space: nowrap;
  pointer-events: none;
  z-index: 25;
}
@keyframes pm-hint-in {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}
.pm-reveal.is-handing-off .pm-reveal-hint { opacity: 0; transition: opacity 300ms ease; }

.pm-reveal-skip {
  position: absolute;
  top: 0;
  left: 0;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
  pointer-events: none;
}
.pm-reveal-skip:focus-visible {
  width: auto;
  height: auto;
  clip: auto;
  overflow: visible;
  pointer-events: auto;
  z-index: 999;
  outline: 2px solid #ffe9b6;
  outline-offset: -6px;
  background: #1e362a;
  color: #ffe29b;
}

/* ---------------- 面纱：从卡片处铺开的暗场 ---------------- */
.pm-reveal .pm-reveal-veil {
  background: radial-gradient(circle at 50% 46%, rgba(58, 92, 84, .78), rgba(8, 20, 16, .97) 62%);
  animation: pm-veil var(--reveal-total) cubic-bezier(.4, 0, .2, 1) both;
}
@keyframes pm-veil {
  0%   { opacity: 0; }
  10%  { opacity: 1; }
  55%  { opacity: 1; }
  100% { opacity: 1; }
}

/* ---------------- 光环 / 辉光：从格子处铺开 ---------------- */
.pm-reveal .pm-reveal-halo {
  animation: pm-halo var(--reveal-total) cubic-bezier(.22, .9, .24, 1) both;
}
@keyframes pm-halo {
  0%   { opacity: 0; transform: scale(.22) rotate(-40deg); }
  16%  { opacity: 1; }
  52%  { opacity: 1; transform: scale(1) rotate(0deg); }
  100% { opacity: 0; transform: scale(1.32) rotate(24deg); }
}
.pm-reveal .pm-reveal-aura {
  animation: pm-aura var(--reveal-total) cubic-bezier(.22, .9, .24, 1) both;
}
@keyframes pm-aura {
  0%   { opacity: 0; transform: scale(.3) rotate(0deg); }
  26%  { opacity: .85; }
  70%  { opacity: .6; }
  100% { opacity: 0; transform: scale(1.25) rotate(150deg); }
}
/* 光环与辉光始终居中定位于卡片正后方（天球法阵与聚光基座），
   严禁跟随点击格子偏移至边角（彻底解决右下角偏移 Bug，视觉焦点严格凝聚在卡片上） */
.pm-reveal .pm-reveal-halo,
.pm-reveal .pm-reveal-aura,
.pm-reveal.is-from-cell .pm-reveal-halo,
.pm-reveal.is-from-cell .pm-reveal-aura,
.pm-reveal.is-from-cell.is-grown .pm-reveal-halo,
.pm-reveal.is-from-cell.is-grown .pm-reveal-aura {
  translate: 0px 0px !important;
}

/* ---------------- 卡面：掠光 + 卡框 ---------------- */
.pm-reveal .pm-reveal-frame {
  animation: pm-frame-in 780ms cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes pm-frame-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
.pm-reveal .pm-reveal-frame:after {
  animation: pm-sheen 900ms 300ms cubic-bezier(.3, .7, .3, 1) both;
}
@keyframes pm-sheen {
  0%   { transform: translate(-52%); opacity: 0; }
  18%  { opacity: .9; }
  100% { transform: translate(52%); opacity: 0; }
}

/* ---------------- 文字：逐级浮现 ---------------- */
.pm-reveal .pm-reveal-heading {
  animation: pm-fade-up 520ms 380ms cubic-bezier(.16, 1, .3, 1) both;
}
.pm-reveal .pm-reveal-name {
  animation: pm-fade-up 520ms 460ms cubic-bezier(.16, 1, .3, 1) both;
}
.pm-reveal-card > .pm-reveal-discovery {
  animation: pm-fade-up 560ms 240ms cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes pm-fade-up {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* ---------------- 星屑 / 星级 ---------------- */
.pm-reveal-constellation i {
  animation: pm-spark 1100ms var(--delay) cubic-bezier(.2, .8, .3, 1) both;
}
.pm-reveal-stars i {
  animation: pm-star-awaken 480ms cubic-bezier(.16, 1, .3, 1) both;
}

/* ---------------- 交棒：卡片让位给解释卡 ---------------- */
.pm-reveal.is-handing-off .pm-reveal-stage {
  transform: scale(.9) translateY(-10px);
  opacity: 0;
  transition: transform 620ms cubic-bezier(.4, 0, 1, 1), opacity 560ms ease;
}
.pm-reveal.is-handing-off .pm-reveal-veil,
.pm-reveal.is-handing-off .pm-reveal-halo,
.pm-reveal.is-handing-off .pm-reveal-aura,
.pm-reveal.is-handing-off .pm-reveal-constellation,
.pm-reveal.is-handing-off .pm-reveal-dock {
  animation: none;
  opacity: 0;
  transition: opacity 620ms ease;
}

/* ---------------- 收回：卡片反向 FLIP 飞回原位，遮罩与法阵柔和散去 ---------------- */
.pm-reveal.is-closing {
  opacity: 0;
  pointer-events: none !important;
  transition: opacity 420ms cubic-bezier(.22, 1, .36, 1);
}
.pm-reveal.is-closing .pm-reveal-veil,
.pm-reveal.is-closing .pm-reveal-halo,
.pm-reveal.is-closing .pm-reveal-aura,
.pm-reveal.is-closing .pm-reveal-transmute-rings,
.pm-reveal.is-closing .pm-reveal-backdrop-glow,
.pm-reveal.is-closing .pm-reveal-dock,
.pm-reveal.is-closing .pm-reveal-hint {
  opacity: 0;
  transition: opacity 360ms cubic-bezier(.22, 1, .36, 1);
}

/* 解释卡入场：在卡片让位的同时从下方浮起。
   注意 root 是 .pm-world（app.js 里由 JS 创建），不是 .experience-page。 */
.pm-world.is-handoff .pm-inspector .pm-folio {
  animation: pm-handoff-in 880ms cubic-bezier(.16, 1, .3, 1) both;
}
@keyframes pm-handoff-in {
  from { opacity: 0; transform: translateY(26px) scale(.968); }
  to   { opacity: 1; transform: none; }
}

/* ---------------- 背景：整座收藏馆往后退一层 ---------------- */
/* 只作用在 .pm-room（收藏区）上：它内部没有 fixed 后代，
   所以缩放/模糊不会改变解释卡的定位参照。 */
.pm-room {
  transition: transform 680ms cubic-bezier(.22, 1, .36, 1), opacity 500ms ease,
              filter 620ms ease;
}
.pm-world.is-extracting-card .pm-room {
  transform: scale(.955) translateY(10px);
  /* 整页 filter 是这次开箱里最贵的一笔（全屏高斯 + 颜色矩阵）。
     去掉 saturate 省一整套颜色矩阵，半径收到 5px；
     压暗交给同时在场的那层暗场（.pm-reveal-veil），观感几乎不变，帧率明显回来。 */
  filter: blur(5px) brightness(.62);
  will-change: transform, filter;
}
/* 被取走的那一格：亮一下再让位 */
.pm-cell.is-extracted {
  animation: pm-cell-give 900ms cubic-bezier(.22, .9, .24, 1) both;
}
@keyframes pm-cell-give {
  0%   { transform: none; filter: none; }
  22%  { transform: scale(1.06); filter: brightness(1.5); }
  100% { transform: scale(.88); opacity: .22; filter: none; }
}
.pm-holo-card.is-extracted {
  animation: pm-cell-give-card 900ms cubic-bezier(.22, .9, .24, 1) both;
}
@keyframes pm-cell-give-card {
  0%   { opacity: 1; }
  22%  { opacity: 1; }
  100% { opacity: .18; }
}

/* 强防护：所有子元素强制占据第一行第一列，禁止撑出额外 Grid 行挤压卡片位置 */
.pm-reveal > * {
  grid-area: 1 / 1;
}

/* ---------------- 变身时空法阵与环境辉光 (Card Transmutation & Ambient Portal) ----------------
   分层减法：这两层原来是**常驻**且相当显眼（法阵 0.42 一直转，640px 金蓝雾一直呼吸），
   和卡片自己的箔面扫光抢视觉焦点，卡边都跟着发毛。
   现在它们降级为"卡片底下的一盏底座光"：法阵接近隐形，只留一点旋转的机械味；
   环境光去掉蓝色、缩小一圈，让注意力落回卡面。 */
.pm-reveal-backdrop-glow {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 480px;
  height: 480px;
  margin-top: -240px;
  margin-left: -240px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 50%, rgba(212, 175, 105, 0.17) 0%, rgba(212, 175, 105, 0.05) 46%, transparent 72%);
  pointer-events: none;
  z-index: 0;
  filter: blur(46px);
  animation: pm-glow-pulse 6s ease-in-out infinite alternate;
  will-change: transform, opacity;
}
@keyframes pm-glow-pulse {
  0% { transform: scale(0.94); opacity: 0.50; }
  100% { transform: scale(1.08); opacity: 0.78; }
}

.pm-reveal-transmute-rings {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 540px;
  height: 540px;
  margin-top: -270px;
  margin-left: -270px;
  pointer-events: none;
  z-index: 0;
  opacity: 0.13;
  animation: pm-rings-enter 1.2s cubic-bezier(0.16, 1, 0.3, 1) both;
  will-change: transform, opacity;
}
@keyframes pm-rings-enter {
  0% { transform: scale(0.4) rotate(-30deg); opacity: 0; }
  100% { transform: scale(1) rotate(0deg); opacity: 0.13; }
}
.pm-transmute-ring {
  position: absolute;
  border-radius: 50%;
  box-sizing: border-box;
}
.pm-transmute-ring-inner {
  inset: 60px;
  border: 1px solid rgba(255, 230, 160, 0.40);
  animation: pm-ring-spin-cw 45s linear infinite;
  will-change: transform;
}
.pm-transmute-ring-outer {
  inset: 0;
  border: 1px dashed rgba(212, 175, 105, 0.48);
  animation: pm-ring-spin-ccw 60s linear infinite;
  will-change: transform;
}
@keyframes pm-ring-spin-cw {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
@keyframes pm-ring-spin-ccw {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(-360deg); }
}

/* 悬浮底托阴影 (Floating Levitation Pedestal) */
.pm-card-pedestal {
  position: absolute;
  bottom: -32px;
  left: 50%;
  width: 220px;
  height: 26px;
  margin-left: -110px;
  border-radius: 50%;
  background: radial-gradient(ellipse at 50% 50%, rgba(0, 0, 0, 0.65) 0%, rgba(0, 0, 0, 0.28) 45%, transparent 75%);
  filter: blur(8px);
  pointer-events: none;
  z-index: -1;
  animation: pm-pedestal-breathe 4s ease-in-out infinite alternate;
  will-change: transform, opacity;
}
@keyframes pm-pedestal-breathe {
  0% { transform: scale(0.90); opacity: 0.45; }
  100% { transform: scale(1.10); opacity: 0.78; }
}

/* 翻转变身冲击波脉冲 (Flip Transmutation Burst) */
.pm-card-flip-burst {
  position: absolute;
  inset: -18px;
  border-radius: 26px;
  border: 2px solid rgba(255, 230, 160, 0.85);
  box-shadow: 0 0 35px rgba(212, 175, 105, 0.65), inset 0 0 25px rgba(255, 230, 160, 0.45);
  opacity: 0;
  pointer-events: none;
  z-index: 20;
  will-change: transform, opacity;
}
.pm-reveal-card.is-flip-bursting .pm-card-flip-burst {
  animation: pm-flip-burst 0.58s cubic-bezier(0.16, 1, 0.3, 1) both;
}
@keyframes pm-flip-burst {
  0% { transform: scale(0.85); opacity: 0.95; }
  60% { opacity: 0.65; }
  100% { transform: scale(1.22); opacity: 0; }
}

/* ---------------- 移动端 ---------------- */
@media (max-width: 700px) {
  .pm-reveal { --card-w: 240px; --card-h: 330px; --dock-gap: 46px; }
  .pm-reveal-dock { gap: 8px; }
  .pm-reveal-hint { font-size: 11px; }
}

/* 精简动效：完全交给 app.js 短路（直接进档案），这里只做兜底 */
@media (prefers-reduced-motion: reduce) {
  .pm-reveal,
  .pm-reveal * {
    animation: none !important;
    transition: none !important;
  }
}

/* ======================================================================
   展开 / 收回：让"点开一个元素"有连续的位移，而不是两处各自淡入淡出
   —— 参考 https://shapeof.world/explore/periodic-table 打开卡片后的过渡
   ====================================================================== */

/* 重复翻开：几何 FLIP 由 app.js 计算（以被点的那一格的圆心为缩放原点），
   这里只做合成提示，避免大卡片缩放时出现边缘毛刺。 */
.pm-folio{backface-visibility:hidden}
.pm-return-specimen{object-fit:contain;will-change:transform,opacity}

/* 开箱动画收尾：小卡落在解释卡的照片位上。
   照片位先空出来，等卡片落定的同一刻再让照片显形 —— 交接点看不出接缝。 */
.pm-world.is-morphing .pm-inspector .pm-folio{animation:pm-dossier-fade 460ms 140ms ease both}
@keyframes pm-dossier-fade{from{opacity:0}to{opacity:1}}
.pm-reveal.is-morphing.is-handing-off .pm-reveal-stage{transition:none}

/* ======================================================================
   收回：把"放回展柜"做成 macOS 最小化那种丝滑
   ----------------------------------------------------------------------
   一条时间线，三个一起往同一个点收敛的动作：

     0–50ms     标本离台（抬起一点点，由 JS 的飞行动画前 14% 负责）
     0–460ms    整张档案卡平移到目标格子并缩小到 10%
     110–440ms  伴随的透明度退场
     0–560ms    标本照片沿弧线飞回格子
     524ms      交接：格子的图显形 + 格子"接住"一下

   两处原来会"断"的地方：
     1. `.pm-inspector.is-closing` 里写死了 background:0 0，纸色底在关闭那一帧
        就没了，整页背景"啪"地露出来，而此时卡片还在慢慢淡；
     2. 内部元素各自 150ms 淡出，和飞行动画各跑各的。

   现在：卡片整体缩小 + 平移，所有内部元素不再单独淡出，全部交给一层 opacity。
   ====================================================================== */
.pm-world.is-returning .pm-inspector {
  /* 只动 opacity：纸底、细节、卡片一起走，不再各自闪没 */
  transition: opacity 330ms cubic-bezier(.4, 0, .5, 1) 110ms;
  will-change: opacity;
}
.pm-world.is-returning .pm-inspector.is-closing {
  opacity: 0;
  background: var(--paper, #f0eddf);      /* 覆盖 .is-closing 的瞬时不透明 */
}
/* 整张卡片"收"回格子：位移 = 卡片中心 → 目标格子中心，同时缩到格子那么大。
   两者朝**同一个**点收敛，所以看起来是一个动作，而不是"卡片淡掉、照片自己飞走"。
   缓动取"起步就把大半程走完"的曲线 —— 最后那一小截是稳稳塞进格子的感觉，
   而不是匀速缩小（匀速会显得像在泄气）。 */
.pm-world.is-returning .pm-inspector .pm-folio {
  transform-origin: 50% 50%;
  transition: transform 460ms cubic-bezier(.25, .8, .3, 1);
  will-change: transform;
}
.pm-world.is-returning .pm-inspector.is-closing .pm-folio {
  /* 必须显式写回 opacity:1。
     periodic-table.css 里有一条 `.pm-inspector.is-closing .pm-folio{opacity:0;...}`，
     只在关闭时把整张卡片设成透明 —— 于是"整体缩小"的变换一直在跑，却什么都看不见，
     看上去就只是"面板淡掉"。缩小的过程必须让人看见，才叫缩小。 */
  opacity: 1;
  transform: translate(var(--return-dx, 0px), var(--return-dy, 0px)) scale(var(--return-scale, .1));
}
/* 卡片以外的部分不再单独淡出——整块的 opacity 已经管了，
   留着它们就会在卡片还亮着的时候先消失，又变回"闪一下"。
   （`.pm-inline-atom` / `.pm-isotope-caption` 也被原样式单独设了 opacity:0，
     一并收回，卡片才会作为一个整体缩下去。） */
.pm-world.is-returning .pm-inspector.is-closing .pm-details,
.pm-world.is-returning .pm-inspector.is-closing .pm-close,
.pm-world.is-returning .pm-inspector.is-closing .pm-inline-atom,
.pm-world.is-returning .pm-inspector.is-closing .pm-isotope-caption {
  opacity: 1;
  transition: none;
}
/* 标本照片本体在飞行期间由 ghost 代表；这里只负责别让它出现两次。
   注意不要用 display:none —— 那会让 getBoundingClientRect 立刻归零，
   而 ghost 的起点是在同一帧之前量好的，改布局可能带出一帧抖动。 */
.pm-inspector.is-closing .pm-hero-image { visibility: hidden; }

/* 飞行中的标本要"有实体"：一张照片裸飘在半空是纸片，带投影才是物件。
   macOS 把窗口收进 Dock 时，那个小窗始终拖着自己的阴影 —— 这是"实物感"的一半来源。
   投影写在 filter 上（不是 box-shadow）：filter 先于 transform 作用，
   所以它随元素一起缩放 —— 物件变小，影子也自然变小变淡，不需要额外动画。 */
.pm-return-specimen {
  filter: drop-shadow(0 14px 22px rgba(24, 38, 30, 0.32))
          drop-shadow(0 2px 5px rgba(24, 38, 30, 0.18));
}

/* 格子"接住"：一次极小的回落，像 Dock 图标接住窗口。
   用独立的 scale 属性而不是 transform —— 卡册小卡的 transform 上挂着
   全息镭射的倾斜（rotateX/rotateY），用 transform 做关键帧会把它整个顶掉。 */
@keyframes pm-cell-catch {
  0%   { scale: 1.075; }
  52%  { scale: .988; }
  100% { scale: 1; }
}
.pm-cell[data-landing],
.pm-holo-card[data-landing] {
  animation: pm-cell-catch 300ms cubic-bezier(.2, .9, .3, 1) both;
}
/* 交接那一瞬：格子自己的图快速显形。ghost 与它在同一位置、同一尺寸，
   所以这一下只是把"普通混合"换成"multiply 混合"的色调过渡。 */
@keyframes pm-catch-img { from { opacity: .45; } to { opacity: 1; } }
.pm-cell[data-landing] img,
.pm-holo-card[data-landing] img {
  animation: pm-catch-img 200ms cubic-bezier(.3, .8, .3, 1) both;
}

@media (prefers-reduced-motion: reduce) {
  .pm-world.is-returning .pm-inspector,
  .pm-world.is-returning .pm-inspector .pm-folio { transition: none; }
  .pm-world.is-returning .pm-inspector.is-closing .pm-folio { transform: none; }
  .pm-cell[data-landing], .pm-holo-card[data-landing],
  .pm-cell[data-landing] img, .pm-holo-card[data-landing] img { animation: none; }
}

/* ---------------- 轻量弹出：已收录的元素再点一次 ----------------
   同一张标本卡、同一段落位交棒，只是去掉光环/星屑/掠光/音效，
   并把总时长压到 1.5s（JS 会把 --reveal-total 写成 1500ms）。
   这样"首页随便点哪一格都有弹出"，又不会挡着反复翻看。 */
.pm-reveal.is-quick { --reveal-total: 1500ms; }
.pm-reveal.is-quick .pm-reveal-frame { animation-duration: 520ms; }
.pm-reveal.is-quick .pm-reveal-card > .pm-reveal-discovery { animation-duration: 420ms; animation-delay: 120ms; }
.pm-reveal.is-quick .pm-reveal-heading { animation-duration: 420ms; animation-delay: 200ms; }
.pm-reveal.is-quick .pm-reveal-name { animation-duration: 420ms; animation-delay: 260ms; }
.pm-reveal.is-quick .pm-reveal-stars i { animation-duration: 400ms; }
.pm-reveal.is-quick .pm-reveal-frame:after { display: none; }      /* 掠光 */
.pm-reveal.is-quick .pm-reveal-stage { animation: none; }
