/* ==========================================================================
   DeckDorm — Hero showcase (four product lines, 2×2)
   --------------------------------------------------------------------------
   Source of truth: viewer-demo/hero-binder-b.html. Generated by
   _build_hero_css.py; edit the prototype first, then re-run the builder.

   The rules below neutralize the old flip-tile skin in main.css. That skin is
   still in main.css (it is simply unused now that hero.php no longer prints
   .dd-hero__deck), but .dd-tile is a shared class name, so the tile has to be
   reset explicitly:
     · min-height 176px  → 0   (the prototype sizes each stage itself)
     · overflow hidden   → visible (the deck box lid swings out of the tile)
     · dark gradient bg  → none (the prototype draws real 3D objects)
     · hover translateY  → none (hover is spent on the animation, not a lift)
   ========================================================================== */

.dd-showcase .dd-tile {
	min-height: 0;
	border: 0;
	border-radius: 14px;
	overflow: visible;
	box-shadow: none;
	color: inherit;
	isolation: auto;
	background: transparent;
	-webkit-tap-highlight-color: transparent;
}

.dd-showcase .dd-tile:hover,
.dd-showcase .dd-tile:focus-visible {
	transform: none;
	border-color: transparent;
	box-shadow: none;
}

.dd-showcase .dd-tile--binder,
.dd-showcase .dd-tile--sleeves,
.dd-showcase .dd-tile--toploader,
.dd-showcase .dd-tile--deckbox {
	background: none;
}

/* 2026-11-11 — 排版整体放大（Carl：最大化后内容偏小、四周空白太多）。
   三个开关一起动，页面的观感才是一致的：
     · 容器 1200 → min(1520px, 90%)，只作用于 hero 的 `.dd-hero__inner`，
       不碰全局 `.dd-container`（下方 Shop / Why 区仍走原宽度）；
     · 字号改成 clamp(下限, 一次函数, 上限)，曲线按三个实测点标定：
         1280 → 标题 54／正文 17.5；1440 → 58.5／18.1；1920 → 72／20
       ⚠️ 下限必须压住 1024 的单列，上限只在大屏生效 —— 旧版
       `clamp(36px,5.4vw,60px)` 到 1920 仍锁在 60，正是「最大化了还是小」的原因；
     · 上下留白 104/96 → clamp(56px,4.6vw,88px)。 */
.dd-hero__inner{position:relative;z-index:1;display:grid;
  grid-template-columns:minmax(0,1.06fr) minmax(0,.94fr);
  gap:clamp(36px,3.2vw,76px);align-items:center;min-height:0;width:min(1660px,92%)}
/* 2026-11-12（Carl）：左栏文字整体再往右让一点 —— 贴着容器左沿读起来太顶，
   与页头的 logo 也咬得太死。只给文字栏加左内边距，右栏四宫格与列宽都不动；
   1024 以下折成单列时归零，否则窄屏会白损一截行宽。
   同日二次：Carl「向中间靠近一点」，1.5vw/28 → 2.6vw/48。
   同日三次：Carl 按截图指定落点（文字左沿约在视口 12.5% 处）→ 8.3vw/160。
   上限 160 是压着算过的：1920 时左列可用仍有 676px，标题照旧两行 + 金色一行，
   不会碎成四行；副文案 660 的上限也没被挤到。 */
.dd-hero__copy{padding-left:clamp(0px,8.3vw,160px)}
.dd-hero .dd-eyebrow{font-family:var(--dd-font);font-size:clamp(11px,calc(9px + .235vw),13.5px);
  font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--color-accent-strong);
  margin-bottom:clamp(12px,1.05vw,20px)}
/* 2026-11-12（Carl）：标题再大一档。2.9vw/78 → 3.3vw/88，1440 约 59.8→65.5、
   1920 约 73.7→81.4（+10%）。下限 34 不动，手机端不变。
   ⚠️ 上限必须同步抬，否则大屏照旧锁死在小字号 —— 这是 11-11 那次的同一个坑。 */
.dd-hero__title{font-family:var(--dd-font);font-size:clamp(34px,calc(18px + 3.3vw),88px);
  font-weight:800;letter-spacing:-.03em;color:var(--color-hero-heading);
  margin-bottom:clamp(16px,1.35vw,26px);line-height:1.04;text-wrap:balance}
.dd-hero__title span{display:block;color:var(--color-hero-accent,#a8822f)}
/* 2026-11-12（Carl）：副文案原本做过两端对齐（justify + inter-word），
   但右端齐平的代价是词间被撕开——1440 下 "Card  binders,  sleeves," 那种大缝，
   Carl 判定「为了对齐放弃美观」⇒ **改回左对齐，不再 justify**。
   只保留 hyphens:none（英文自动断字会把单词劈开，比参差的右端更难看）。
   max-width 仍 36vw：再放宽（试过 40vw）不会多装词，只是让末行更孤单。 */
.dd-hero__sub{font-size:clamp(15px,calc(12.5px + .39vw),20px);line-height:1.62;
  color:var(--color-hero-muted);max-width:clamp(460px,36vw,660px);
  hyphens:none;
  margin-bottom:clamp(24px,2.1vw,38px)}
.dd-hero__actions{display:flex;flex-wrap:wrap;gap:clamp(12px,.95vw,16px)}
.dd-hero .dd-btn{display:inline-flex;align-items:center;gap:8px;font-family:var(--dd-font);font-weight:600;
  font-size:clamp(14px,calc(11px + .3125vw),17px);
  padding:clamp(12px,.95vw,16px) clamp(22px,1.6vw,32px);border-radius:999px;
  text-decoration:none;transition:transform .18s ease,box-shadow .22s ease,background .2s ease,color .2s ease}

/* 主按钮（2026-11-11 二次改版）。第一版去掉了果冻渐变，做成素面实心金；
   Carl 现在要求「和右上角 Request Sample 一样的款式，底色再浅一档」。
   Request Sample 就是主题里的 .dd-btn--gold：实心填充 + 2px 同色边 + 999 胶囊
   + 金色柔光投影。这里沿用它的**结构**，只把填充从 --color-accent(#c9a24b)
   提亮两档到 #e7cf9b，字色落到深金棕，边框比填充再深一档 —— 浅金落在奶油色
   hero 上，靠边框才有边界，否则整枚按钮会化进背景。

   2026-11-12（Carl）：按钮「没有发光、hover 反而变暗」。两个原因：
   ① 原来只有一条裸 box-shadow，负 spread 收到 -14px，光全塌在按钮底下，
      而且这枚按钮压根没带 `.dd-btn--glow` —— 那层呼吸光是 Request Sample 独占的；
   ② hover 把填充压到 #dfc389，越动越暗。
   现在常态自带金色呼吸光（3.4s，外扩 12→26px），hover 提亮到 #f3dfae 并把光
   铺到 22/44px 两层 —— 手感是「亮起来」而不是「暗下去」。
   ⚠️ animation 会盖住普通声明里的 box-shadow，所以 hover 必须写 `animation:none`，
      否则下面那组更亮的光不生效；过渡交给 `.dd-btn` 上的 transition，切换不跳。 */
.dd-hero .dd-btn--gold{position:relative;background:#e7cf9b;color:#43300a;
  border:2px solid #d5b978;
  animation:dd-hero-gold-pulse 3.4s ease-in-out infinite}
/* 光色比底色更亮、更淡（#f6d696 / #fbe8bf）。金调 rgba(226,190,110,.3) 试过，
   在奶油底上跟背景同色，等于没发光 —— 浅底要「暖白」才透得出来。 */
@keyframes dd-hero-gold-pulse{
  0%,100%{box-shadow:inset 0 1px 0 rgba(255,255,255,.55),
          0 8px 22px -14px rgba(168,130,47,.7),
          0 0 14px rgba(246,214,150,.70),0 0 30px rgba(251,232,191,.35)}
  50%    {box-shadow:inset 0 1px 0 rgba(255,255,255,.65),
          0 10px 26px -14px rgba(168,130,47,.78),
          0 0 22px rgba(246,214,150,.90),0 0 46px rgba(251,232,191,.55)}
}
.dd-hero .dd-btn--gold::after{content:"\2192";font-size:.92em;line-height:1;
  transition:transform .22s ease;display:inline-block}
.dd-hero .dd-btn--gold:hover{background:#f6e3b4;border-color:#e0c68a;transform:translateY(-2px);
  animation:none;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.75),
             0 12px 28px -14px rgba(168,130,47,.8),
             0 0 26px rgba(250,224,170,.95),0 0 56px rgba(251,232,191,.6)}
.dd-hero .dd-btn--gold:hover::after{transform:translateX(4px)}
/* 键盘走到这里要给同一份反馈，否则只有鼠标能看见发光。 */
.dd-hero .dd-btn--gold:focus-visible{background:#f6e3b4;border-color:#e0c68a;
  animation:none;outline:none;
  box-shadow:inset 0 1px 0 rgba(255,255,255,.75),
             0 0 0 3px rgba(250,224,170,.55),
             0 0 26px rgba(250,224,170,.95)}
@media (prefers-reduced-motion:reduce){
  .dd-hero .dd-btn--gold{animation:none;
    box-shadow:inset 0 1px 0 rgba(255,255,255,.6),
               0 8px 22px -14px rgba(168,130,47,.7),
               0 0 18px rgba(246,214,150,.8)}
}
/* 次按钮跟着对齐几何：同 2px 边、同族阴影，两枚胶囊才是一对。 */
.dd-hero .dd-btn--solid{background:#161a26;color:#fff;border:2px solid #161a26;
  box-shadow:0 8px 22px -14px rgba(21,24,40,.75),0 2px 4px rgba(21,24,40,.1)}
.dd-hero .dd-btn--solid:hover{transform:translateY(-2px);
  box-shadow:0 12px 26px -14px rgba(21,24,40,.85),0 2px 4px rgba(21,24,40,.12)}

/* ============================================================
   右侧：四条产品线 2×2 四宫格
   ============================================================ */
.dd-hero__visual{display:flex;align-items:center;justify-content:center;min-height:460px}
.dd-showcase{
  --bw:98px;       /* 单本卡册合上时的宽度，四宫格所有尺寸都由它派生 */
  --bh:130px;
  --dep:5.5px;
  --fan:13.5deg;   /* 扇形张开角（收紧一点＝同样宽度里三本更大） */
  --spread:calc(var(--bw) * .20);  /* 底部捏合点的横向错位 */
  --lift:calc(var(--bh) * .068);   /* 把两侧那本压下去，免得它的上角从中间那本顶上冒出来 */
  /* 行距 124 / 列距 8：列距是 11-05 上线版原值；行距必须放开到 124，
     因为卡夹的入卡轨迹原封不动地保留着「斜 23° 整张卡悬在格子上方」——
     放大后这张卡的包围盒从 101px 长到 154px，58px 的行距装不下，会直接
     压住上排「Binders & Albums」（实测 120 时只剩 2px 余量，字体高一像素
     就压上，故取 124）。这个数值就是它头顶的净空，改小先跑 _grid_overlap.py。
     宁可两行离远一点，也不动动画轨迹。
     ⚠️ 列写 minmax(0,1fr) 而不是 1fr：卡册扇形比一列宽，1fr 会被
     min-content 撑成不等宽的两列，四格就不对称了 */
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:124px 8px;width:100%;
  max-width:490px;
}
.dd-tile{
  display:flex;flex-direction:column;align-items:center;gap:7px;
  padding:4px 4px 4px;border-radius:14px;text-decoration:none;
  transition:background .3s ease;
}
.dd-tile:hover{background:rgba(255,255,255,.34)}
.dd-tile:focus-visible{outline:2px solid rgba(201,162,75,.85);outline-offset:4px}
/* 舞台：每格固定高度，四个产品底边对齐 */
.dd-tile__stage{
  display:flex;align-items:flex-end;justify-content:center;
  /* 内容高按最高的一款（卡夹/卡盒 133）取，四款视觉底边才对得齐 */
  height:calc(var(--bh) * 1.47);
  /* 底部留出地面阴影 / 台面线溢出的空间（卡夹放大后要 ~20px）。
     2026-11-11 Carl：四宫格上下两排离得太开，图标往里收一点。行距 124 动不得
     （卡夹那张卡掠过上排标签只剩 5px 净空，实测见 _hero_geo.py），所以收的是
     标签上下的死空 —— 舞台底 20→11、格内 padding 6/10→4/4、图文间距 10→7。
     这几处都在飞行卡片的路径之外，净空只会变大不会变小。 */
  padding-bottom:11px;
}
.dd-tile__name{
  position:relative;z-index:6;   /* 下排的卡/盒盖会掠过上排名字，文字必须在最上层 */
  /* 18px = 与左侧正文 .dd-hero__sub 完全同尺寸（Carl：产品名要和灰字一样大） */
  font-family:var(--dd-font);font-weight:700;font-size:18px;line-height:1.25;
  letter-spacing:.005em;color:var(--color-accent-strong);text-align:center;
  text-shadow:0 1px 0 #eddbaf,0 2px 7px rgba(93,60,45,.26);
}


/* ============================================================
   ② 卡套 —— 与 sleeve-preview.html 完全同源
   结构 / 规则 / 时间曲线一字未改；全部像素乘 --k 等比缩放，
   --k 是唯一尺寸开关：预览宽 262px × .3664 = 96px。
   ============================================================ */
.dv-stage{
  --k:.41;
  --cw:calc(262px * var(--k));
  --ch:calc(366px * var(--k));
  --gap:calc(3.5px * var(--k));
  --tilt:-8deg;
  position:relative;display:block;cursor:pointer;outline:none;
  width:calc(var(--cw) + var(--gap) * 2);
  height:calc(var(--ch) + var(--gap) + 6px * var(--k));
}

/* 地面阴影 */
.dv-ground{
  position:absolute;left:50%;bottom:calc(-48px * var(--k));
  width:calc(var(--cw) * 1.20);height:calc(64px * var(--k));
  transform:translateX(-50%) rotate(var(--tilt)) translateY(calc(10px * var(--k)));
  border-radius:50%;
  background:radial-gradient(50% 50% at 50% 50%,rgba(93,60,45,.28),rgba(93,60,45,0) 72%);
  animation:dv-breathe 4.6s ease-in-out infinite;
}

/* 倾斜 + 待机漂浮分层，避免 transform 互相覆盖 */
.dv-tilt{transform:rotate(var(--tilt))}
.dv-float{animation:dv-float 4.6s ease-in-out infinite;will-change:transform}
.dd-tile:hover .dv-float,.dv-stage.is-in .dv-float{animation-play-state:paused}

/* ---------- 卡套本体 ---------- */
.dv-sleeve{
  position:relative;
  width:calc(var(--cw) + var(--gap) * 2);
  height:calc(var(--ch) + var(--gap) + 6px * var(--k));   /* 底边封边 gap，顶部开口 6px */
  /* 四角略有差异 = 膜被撑开时的松紧不均，不是工业切出来的直角 */
  border-radius:calc(13px * var(--k)) calc(15px * var(--k))
                calc(5px * var(--k))  calc(5.5px * var(--k));
  /* 切边：透明 PP 的边在光里会折出一条冷色亮线 */
  border:calc(1.2px * var(--k)) solid rgba(112,142,174,.46);
  /* 极淡冷色底：不是白叠白（会变玻璃砖）*/
  background:rgba(220,236,252,.02);
  box-shadow:
    0 0 calc(6px * var(--k)) rgba(112,142,174,.22),
    0 calc(26px * var(--k)) calc(46px * var(--k)) calc(-26px * var(--k)) rgba(93,60,45,.46),
    0 calc(7px * var(--k)) calc(17px * var(--k)) calc(-9px * var(--k)) rgba(93,60,45,.16);
  transition:transform .5s cubic-bezier(.32,.74,.26,1),box-shadow .5s ease;
}
.dd-tile:hover .dv-sleeve,.dv-stage.is-in .dv-sleeve{
  transform:scale(1.018,1.008);
  box-shadow:
    0 0 calc(8px * var(--k)) rgba(112,142,174,.26),
    0 calc(30px * var(--k)) calc(54px * var(--k)) calc(-28px * var(--k)) rgba(93,60,45,.50),
    0 calc(8px * var(--k)) calc(19px * var(--k)) calc(-9px * var(--k)) rgba(93,60,45,.18);
}

/* 背膜：给口袋一点体积，能透过它看到背景 */
.dv-sleeve__back{
  position:absolute;inset:0;border-radius:inherit;
  background:
    linear-gradient(118deg,rgba(255,255,255,.05) 0%,rgba(255,255,255,.01) 24%,rgba(255,255,255,.006) 62%,rgba(206,228,246,.03) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(255,255,255,.40),
    inset 0 0 calc(22px * var(--k)) calc(-10px * var(--k)) rgba(122,152,182,.34);
}

/* ---------- 卡牌：初始在套外上方 → 悬停后精准滑入 ---------- */
.dv-card{
  position:absolute;
  left:var(--gap);bottom:var(--gap);
  width:var(--cw);height:var(--ch);
  border-radius:calc(4px * var(--k));
  opacity:0;
  /* 起点：底端正好贴在套口上方 8px —— 完全在套外 */
  transform:translateY(calc(-100% - 8px * var(--k))) scale(.972);
  transform-origin:50% 100%;
  box-shadow:
    0 calc(20px * var(--k)) calc(32px * var(--k)) calc(-14px * var(--k)) rgba(21,24,40,.52),
    0 calc(4px * var(--k)) calc(9px * var(--k)) calc(-3px * var(--k)) rgba(21,24,40,.26),
    inset 0 0 0 1px rgba(255,255,255,.13);
  /* 离开时：先滑出，再淡出 */
  transition:
    transform .54s cubic-bezier(.42,.02,.62,1),
    opacity .2s ease .3s,
    box-shadow .45s ease;
}
.dv-card svg{display:block;width:100%;height:100%;border-radius:inherit}

.dd-tile:hover .dv-card,
.dv-stage.is-in .dv-card{
  opacity:1;
  transform:translateY(0) scale(1);
  box-shadow:
    0 0 0 rgba(21,24,40,0),
    0 0 0 rgba(21,24,40,0),
    inset 0 0 0 1px rgba(255,255,255,.06);
  /* 进入时：先淡入（卡牌出现在套上方），0.16s 后再向下插 */
  transition:
    transform .7s cubic-bezier(.2,.76,.26,1) .16s,
    opacity .18s ease,
    box-shadow .5s ease .16s;
}

/* ---------- 前膜：中心几乎全透，靠封边 + 斜向高光表现 PP 质感 ---------- */
.dv-sleeve__front{
  position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  overflow:hidden;
  background:
    /* 上缘吃天光（冷）／下缘吃桌面（暖）*/
    linear-gradient(180deg,
      rgba(158,192,226,.22) 0%,
      rgba(176,204,232,.06) 4%,
      rgba(255,255,255,.005) 11%,
      rgba(255,255,255,0) 89%,
      rgba(198,164,136,.05) 96%,
      rgba(190,150,118,.12) 100%),
    /* 右上角一小片被兜住的高光 */
    radial-gradient(36% 24% at 87% -3%,
      rgba(255,255,255,.20) 0%,rgba(255,255,255,.06) 36%,rgba(255,255,255,0) 66%),
    /* 右侧一片斜向反光：局部软片，不是贯穿整块的一条 */
    radial-gradient(46% 30% at 101% 38%,
      rgba(255,255,255,.12) 0%,rgba(255,255,255,.035) 46%,rgba(255,255,255,0) 74%),
    /* 左下角回光 */
    radial-gradient(26% 18% at 3% 104%,
      rgba(255,255,255,.07) 0%,rgba(255,255,255,0) 72%);
  -webkit-backdrop-filter:saturate(.95);
  backdrop-filter:saturate(.95);
  box-shadow:
    /* 内圈一道冷色描边：膜和背景的分界 */
    inset 0 0 0 1px rgba(116,146,178,.26),
    inset 0 1px 0 rgba(255,255,255,.46),
    inset 0 -1px 0 rgba(255,255,255,.22),
    inset 0 0 calc(34px * var(--k)) calc(-18px * var(--k)) rgba(102,130,158,.20);
}
/* 封边不是白色，是「透过两层膜的背景」*/
.dv-sleeve::after{
  content:"";position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  box-shadow:inset 0 0 0 var(--gap) rgba(112,136,160,.155);
}

/* 左右封边：压合出来的渐变线——上淡、中亮、下淡 */
.dv-sleeve__front::before,
.dv-sleeve__front::after{
  content:"";position:absolute;top:0;bottom:0;width:calc(1.5px * var(--k));
  background:linear-gradient(180deg,
    rgba(255,255,255,.02) 0%,
    rgba(255,255,255,.34) 14%,
    rgba(132,160,188,.30) 48%,
    rgba(255,255,255,.28) 76%,
    rgba(255,255,255,.02) 100%);
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 7%,#000 93%,transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 7%,#000 93%,transparent 100%);
}
.dv-sleeve__front::before{left:0}
.dv-sleeve__front::after{right:0}

/* 顶部开口：外层膜比内层膜短一截，中间露出一道缝 */
.dv-sleeve__mouth{
  position:absolute;
  left:calc(16px * var(--k));right:calc(16px * var(--k));top:calc(8px * var(--k));
  height:calc(3px * var(--k));pointer-events:none;
  border-radius:46% 46% 54% 54% / 60% 60% 100% 100%;
  filter:blur(calc(.55px * var(--k)));
  background:linear-gradient(90deg,
    rgba(255,255,255,0) 0%,
    rgba(255,255,255,.18) 9%,
    rgba(128,152,174,.34) 34%,
    rgba(108,134,158,.46) 50%,
    rgba(128,152,174,.34) 66%,
    rgba(255,255,255,.18) 91%,
    rgba(255,255,255,0) 100%);
}
.dv-sleeve__mouth::before{
  content:"";position:absolute;left:1px;right:1px;
  top:calc(-3px * var(--k));height:calc(3px * var(--k));
  background:linear-gradient(180deg,rgba(255,255,255,.16),rgba(255,255,255,.005));
  border-radius:50% 50% 46% 46% / 100% 100% 60% 60%;
}
.dv-sleeve__mouth::after{
  content:"";position:absolute;left:0;right:0;
  top:calc(3px * var(--k));height:calc(3px * var(--k));
  background:linear-gradient(180deg,rgba(255,255,255,.18),rgba(255,255,255,0));
  border-radius:2px;
}

/* 插好后扫过一道光，表示「到位」*/
.dv-sleeve__sheen{
  position:absolute;inset:0;border-radius:inherit;pointer-events:none;opacity:0;
  background:linear-gradient(104deg,
    rgba(255,255,255,0) 34%,
    rgba(255,255,255,.62) 48%,
    rgba(255,255,255,.92) 51%,
    rgba(255,255,255,.55) 54%,
    rgba(255,255,255,0) 68%);
  background-size:280% 100%;
  background-position:160% 0;
}
.dd-tile:hover .dv-sleeve__sheen,
.dv-stage.is-in .dv-sleeve__sheen{
  animation:dv-sheen .86s cubic-bezier(.3,.6,.3,1) .7s both;
}

/* 硬板只会平移；薄薄膜会被气流带着轻轻摆一下 */
@keyframes dv-float{
  0%,100%{transform:translateY(-5px) rotate(-.32deg)}
  50%{transform:translateY(5px) rotate(.32deg)}
}
@keyframes dv-breathe{
  0%,100%{opacity:.72;transform:translateX(-50%) rotate(var(--tilt)) translateY(calc(12px * var(--k))) scale(1)}
  50%{opacity:1;transform:translateX(-50%) rotate(var(--tilt)) translateY(calc(12px * var(--k))) scale(1.06)}
}
@keyframes dv-sheen{
  0%{opacity:0;background-position:160% 0}
  18%{opacity:1}
  70%{opacity:.85}
  100%{opacity:0;background-position:-60% 0}
}
/* 触屏没有 hover：直接呈现装好卡的样子 */
@media (hover:none){
  .dv-card{opacity:1;transform:translateY(0) scale(1)}
}
@media (prefers-reduced-motion:reduce){
  .dv-float,.dv-ground{animation:none}
  .dv-card{transition:opacity .2s ease}
  .dv-sleeve__sheen{display:none}
}

.dd-trio{
  position:relative;display:block;
  width:calc(var(--bw) * 2.4);
  /* 只取单本高：扇形下角靠 --lift 补偿，不再在盒子里留 23% 余量，
     否则视觉底边会比另外三款高出 23px */
  height:var(--bh);
  cursor:pointer;
}

/* 整摞当一只手抓着的牌，一起上下漂浮 */
.dd-trio__fan{
  position:absolute;left:50%;top:0;
  width:var(--bw);height:var(--bh);
  margin-left:calc(var(--bw) / -2);
  animation:dd-float 4.2s ease-in-out infinite;
}

.dd-slot{
  position:absolute;inset:0;
  perspective:800px;perspective-origin:50% 46%;
  transform-origin:50% 100%;          /* 扇形轴心 = 底部捏合点 */
  transition:transform .62s cubic-bezier(.34,.86,.3,1),opacity .4s ease,filter .4s ease;
}
/* 左张倒向左、右张倒向右、中间那张压在最上 —— 就是手里扇开的一把牌 */
.dd-slot--l{transform:translate(calc(var(--spread) * -1),var(--lift)) rotate(calc(var(--fan) * -1));z-index:1}
.dd-slot--c{transform:translate(0,0) rotate(0deg);z-index:3}
.dd-slot--r{transform:translate(var(--spread),var(--lift)) rotate(var(--fan));z-index:2}

/* 合上时卡片居中：书体总宽 2×bw，平移 -bw 后封面正好落在 0..bw */
.dd-binder{position:absolute;inset:0;
  --sm1:#2a2a31;--sm2:#191920;--sm3:#0d0d12;--sm4:#08080c}
.dd-binder__float{position:absolute;inset:0}

/* 整摞共用一片地面阴影，比每本各一个更像「叠在一起」 */
.dd-trio__shadow{
  position:absolute;left:50%;bottom:-4px;width:calc(var(--bw) * 1.72);height:calc(var(--bw) * .11);
  border-radius:50%;transform:translateX(-50%);
  background:radial-gradient(50% 50% at 50% 50%,rgba(93,60,45,.28),rgba(93,60,45,0) 72%);
  animation:dd-breathe 4.2s ease-in-out infinite;
}

.dd-binder__book{
  position:absolute;left:0;top:0;
  width:calc(var(--bw) * 2);height:100%;
  transform-style:preserve-3d;
  /* 旋转中心放在「封面」的中心（书体局部 1.5bw 处），否则开合会把册子甩偏 */
  transform-origin:calc(var(--bw) * 1.5) 50%;
  transform:translateX(calc(var(--bw) * -1));
  transition:transform .66s cubic-bezier(.32,.74,.26,1);
}

.dd-binder__page{position:absolute;left:50%;top:0;width:50%;height:100%;backface-visibility:hidden;
  border-radius:6% / 4.65%;overflow:hidden}
.dd-binder__page svg,.dd-binder__face svg{display:block;width:100%;height:100%}

.dd-binder__cover{
  position:absolute;left:50%;top:0;width:50%;height:100%;
  transform-origin:left center;transform-style:preserve-3d;
  transform:rotateY(0deg);
  transition:transform .66s cubic-bezier(.32,.74,.26,1);
}

.dd-binder__gusset{
  position:absolute;left:100%;top:4%;bottom:4%;width:var(--dep);
  transform-origin:left center;transform:rotateY(90deg);
  background:linear-gradient(90deg,var(--sm1) 0%,var(--sm2) 42%,var(--sm3) 78%,var(--sm4) 100%);
  border-radius:3px 6px 6px 3px;
  transition:opacity .3s ease .18s;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 3px,#000 calc(100% - 3px),transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 3px,#000 calc(100% - 3px),transparent 100%);
}

.dd-binder__face{position:absolute;inset:0;backface-visibility:hidden;border-radius:6% / 4.65%}
.dd-binder__face--front{
  overflow:hidden;
  background:linear-gradient(158deg,var(--cv1) 0%,var(--cv2) 54%,var(--cv3) 86%,var(--cv4) 100%);
  box-shadow:
    inset 0 0 0 1px rgba(93,60,45,.13),
    inset 2px 0 0 rgba(255,255,255,.16),
    inset -4px 0 9px -7px rgba(93,60,45,.34),
    inset 0 -6px 11px -9px rgba(93,60,45,.20),
    0 8px 16px -13px rgba(93,60,45,.55);
}
/* 最上面那本要往后面的两本上落一点影子，层次才出得来 */
.dd-slot--c .dd-binder__face--front{
  box-shadow:
    inset 0 0 0 1px rgba(93,60,45,.13),
    inset 2px 0 0 rgba(255,255,255,.22),
    inset -4px 0 9px -7px rgba(93,60,45,.34),
    inset 0 -6px 11px -9px rgba(93,60,45,.20),
    0 14px 28px -18px rgba(60,40,30,.55),
    0 8px 16px -13px rgba(93,60,45,.55);
}
.dd-binder__gloss{position:absolute;inset:-6%;pointer-events:none;
  background:linear-gradient(114deg,
    rgba(255,255,255,.42) 0%,
    rgba(255,255,255,.10) 20%,
    rgba(255,255,255,0) 34%,
    rgba(255,255,255,.16) 62%,
    rgba(255,255,255,0) 78%)}
.dd-binder__seam{position:absolute;top:0;bottom:0;right:0;width:2.7%;
  background:linear-gradient(90deg,var(--sm1) 0%,var(--sm2) 55%,var(--sm3) 100%);
  box-shadow:-4px 0 8px -6px rgba(0,0,0,.6)}
/* 拉链轨道：包边上的一条细深线，不然整条边只是一条色块 */
.dd-binder__seam::after{content:"";position:absolute;left:54%;top:2%;bottom:2%;width:1px;
  background:rgba(12,12,16,.5);box-shadow:1px 0 0 rgba(255,255,255,.14)}
.dd-binder__pull{position:absolute;right:.4%;top:32.5%;width:4.6%;height:4.6%;
  border-radius:1.5px;
  background:linear-gradient(148deg,#b6b6c0 0%,#7b7a86 42%,#3f3e48 100%);
  box-shadow:0 1px 2px rgba(0,0,0,.55),inset 0 1px 0 rgba(255,255,255,.55)}
.dd-binder__pull::before{content:"";position:absolute;left:50%;top:26%;width:1px;bottom:26%;
  background:rgba(10,10,14,.7);transform:translateX(-50%)}
.dd-binder__pull::after{content:"";position:absolute;left:32%;top:100%;width:36%;height:150%;
  border-radius:1px 1px 3px 3px;
  background:linear-gradient(160deg,#a8a7b2,#5c5b66 55%,#2b2a33);
  box-shadow:inset 0 1px 0 rgba(255,255,255,.45),0 2px 4px rgba(0,0,0,.4)}
.dd-binder__face--back{transform:rotateY(180deg);overflow:hidden;
  box-shadow:0 8px 18px -10px rgba(93,60,45,.45)}

/* 三款配色（--cv*=正面，--sm*=拉链侧边厚度） */
.dd-binder--white{--cv1:#ffffff;--cv2:#fbf9f4;--cv3:#efeade;--cv4:#ddd6c7;
  --sm1:#f4f0e7;--sm2:#dfd9cb;--sm3:#c7c0ae;--sm4:#aea695}
.dd-binder--red{--cv1:#ec7a89;--cv2:#c22f3e;--cv3:#a01830;--cv4:#77101f;
  --sm1:#8e2434;--sm2:#6c1523;--sm3:#4a0d18;--sm4:#330710}
.dd-binder--blue{--cv1:#6f9cf4;--cv2:#2f6fe4;--cv3:#1b3f8f;--cv4:#12306b;
  --sm1:#28498f;--sm2:#1a3670;--sm3:#102353;--sm4:#0a183c}

/* 后面两本压一档亮度后退，视觉焦点留给中间那本 */
.dd-slot--l .dd-binder,.dd-slot--r .dd-binder{filter:brightness(.97) saturate(.97)}
/* 左边那本翻过来朝外，拉链就在它露出来的那一侧 —— 不然只剩一块红板 */
.dd-slot--l .dd-binder__face--front{transform:scaleX(-1)}

/* ===== 悬停：中间这本打开，左右两张顺着扇形继续甩开并淡出 ===== */
.dd-tile:hover .dd-trio .dd-slot--c .dd-binder__cover,
.dd-trio.is-open .dd-slot--c .dd-binder__cover{transform:rotateY(-180deg)}
.dd-tile:hover .dd-trio .dd-slot--c .dd-binder__gusset,
.dd-trio.is-open .dd-slot--c .dd-binder__gusset{opacity:0;transition:opacity .16s ease}

.dd-trio.is-open .dd-slot--l{
  transform:translate(calc(var(--bw) * -.62),calc(var(--lift) * .4)) rotate(calc(var(--fan) * -2.2)) scale(.82);
  opacity:0;filter:blur(3px);
  transition:transform .6s cubic-bezier(.4,.05,.6,1),opacity .34s ease .12s,filter .4s ease .12s;
}
.dd-trio.is-open .dd-slot--r{
  transform:translate(calc(var(--bw) * .62),calc(var(--lift) * .4)) rotate(calc(var(--fan) * 2.2)) scale(.82);
  opacity:0;filter:blur(3px);
  transition:transform .6s cubic-bezier(.4,.05,.6,1),opacity .34s ease .12s,filter .4s ease .12s;
}

/* 只给能 hover 的设备挂上；触屏一律走 JS 的 .is-open，避免 hover 卡死 */
@media (hover:hover){
  .dd-tile:hover .dd-trio .dd-slot--l{
    transform:translate(calc(var(--bw) * -.62),calc(var(--lift) * .4)) rotate(calc(var(--fan) * -2.2)) scale(.82);
    opacity:0;filter:blur(3px);
    transition:transform .6s cubic-bezier(.4,.05,.6,1),opacity .34s ease .12s,filter .4s ease .12s;
  }
  .dd-tile:hover .dd-trio .dd-slot--r{
    transform:translate(calc(var(--bw) * .62),calc(var(--lift) * .4)) rotate(calc(var(--fan) * 2.2)) scale(.82);
    opacity:0;filter:blur(3px);
    transition:transform .6s cubic-bezier(.4,.05,.6,1),opacity .34s ease .12s,filter .4s ease .12s;
  }
}

@keyframes dd-float{0%,100%{transform:translateY(0)}50%{transform:translateY(-7px)}}
@keyframes dd-breathe{
  0%,100%{transform:translateX(-50%) scale(1);opacity:.9}
  50%{transform:translateX(-50%) scale(.86);opacity:.45}
}

/* 四宫格里其余三条产品线的示意图（占位，等真图换上） */
.dd-figure{display:block;width:calc(var(--bw) * 1.58)}
.dd-figure svg{display:block;width:100%;height:auto;overflow:visible}

@media (hover:hover){
  .dd-tile:hover .dd-figure{transform:translateY(-4px)}
}
.dd-figure{transition:transform .42s cubic-bezier(.32,.74,.26,1)}

/* ============================================================
   舞台 · 一切尺寸都从 --mm 派生（1mm 的屏幕长度）
   76 × 102 mm 的真实倍率与 sleeve-preview 同一物理尺度：
   那边卡套 66mm → 262px，即 3.97px/mm，这边沿用。
   ============================================================ */
.tl-stage{
  --mm:1.45px;          /* 唯一尺寸开关：单独预览 3.4px/mm，四宫格缩到 1.01px/mm ≈ 与卡套同宽 */
  --tilt:0deg;

  position:relative;display:flex;align-items:center;justify-content:center;
  cursor:pointer;outline:none;
}

/* 倾斜 / 漂浮分层，避免 transform 互相覆盖 */
.tl-tilt{position:relative;transform:rotate(var(--tilt))}

/* 桌面：卡夹底下那个「假装很硬」的东西。挂在 tilt 内 → 永远贴着卡夹底 */
.tl-table{
  position:absolute;left:50%;bottom:calc(-20 * var(--mm));
  width:calc(88 * var(--mm));height:1px;
  transform:translateX(-50%);
  background:linear-gradient(90deg,
    rgba(150,120,96,0) 0%,rgba(150,120,96,.34) 16%,
    rgba(150,120,96,.52) 50%,rgba(150,120,96,.34) 84%,rgba(150,120,96,0) 100%);
  opacity:.72;z-index:3;
  pointer-events:none;
}
.tl-ground{
  position:absolute;left:50%;bottom:calc(-23 * var(--mm));
  width:calc(76 * var(--mm) * .96);height:calc(12 * var(--mm));
  transform:translateX(-50%);
  border-radius:50%;
  background:radial-gradient(50% 50% at 50% 50%,rgba(93,60,45,.34),rgba(93,60,45,0) 72%);
  animation:tl-breathe 5.2s ease-in-out infinite;
  z-index:2;
  pointer-events:none;
}
.tl-float{animation:tl-float 5.2s ease-in-out infinite;will-change:transform}
.tl-stage:hover .tl-float,.dd-tile:hover .tl-float,.tl-stage.is-in .tl-float{animation-play-state:paused}

/* ---------- 卡夹本体：硬质 PVC 板 ---------- */
.tl{
  --w:calc(76 * var(--mm));
  --h:calc(102 * var(--mm));
  --ap:calc(3.5 * var(--mm));    /* 左右封边 */
  --bp:calc(3.5 * var(--mm));    /* 底部折边（与左右封边同厚） */
  --r:calc(2.8 * var(--mm));     /* 外圆角 */

  position:relative;
  width:var(--w);height:var(--h);
  border-radius:var(--r);
  /* 板材本身几乎无色，只有一层很淡的紫（防蓝光配方）；形状靠封边和折射线读出来 */
  /* 封边不是画上去的框，是「板在边缘叠了两层」：用双轴渐变铺，
     两轴在四个角相交 → 角落自然加深，跟真实折边转角一致。 */
  background:
    /* 顶部：后片露出 1.2mm */
    linear-gradient(180deg,
      rgba(118,112,188,.22) 0,
      rgba(118,112,188,.22) calc(1.2 * var(--mm)),
      rgba(118,112,188,0) calc(1.2 * var(--mm))),
    /* 底部折边 3.5mm —— 与左右封边同厚 */
    linear-gradient(0deg,
      rgba(114,108,186,.34) 0,
      rgba(114,108,186,.34) calc(3.5 * var(--mm)),
      rgba(114,108,186,.13) calc(3.5 * var(--mm)),
      rgba(114,108,186,.05) calc(4.5 * var(--mm)),
      rgba(114,108,186,0) calc(6 * var(--mm))),
    /* 左右封边 3.5mm */
    linear-gradient(90deg,
      rgba(118,112,188,.27) 0,
      rgba(118,112,188,.27) calc(3.5 * var(--mm)),
      rgba(118,112,188,.085) calc(3.5 * var(--mm)),
      rgba(118,112,188,.085) calc(4.5 * var(--mm)),
      rgba(118,112,188,0) calc(4.5 * var(--mm)),
      rgba(118,112,188,0) calc(100% - 4.5 * var(--mm)),
      rgba(118,112,188,.085) calc(100% - 4.5 * var(--mm)),
      rgba(118,112,188,.085) calc(100% - 3.5 * var(--mm)),
      rgba(118,112,188,.27) calc(100% - 3.5 * var(--mm)),
      rgba(118,112,188,.27) 100%),
    /* 板体本身：中央几乎无色 */
    linear-gradient(180deg,
      rgba(158,154,222,.10) 0%,
      rgba(158,154,222,.028) 30%,
      rgba(158,154,222,.045) 80%,
      rgba(158,154,222,.09) 100%);
  /* 轮廓只有一道 1px 冷线 —— 硬塑料的边是折光，不是描边。
     ⚠️ 不能用 border：border 会让绝对定位的包含块缩进，卡牌的左右空隙就不等了。
     改用 ::after 叠一层，不占布局。 */
  box-shadow:
    0 calc(.5 * var(--mm)) calc(1.8 * var(--mm)) rgba(126,126,178,.14),
    0 calc(24 * var(--mm)) calc(42 * var(--mm)) calc(-26 * var(--mm)) rgba(93,60,45,.38),
    0 calc(6 * var(--mm)) calc(14 * var(--mm)) calc(-8 * var(--mm)) rgba(93,60,45,.13);
  transition:box-shadow .5s ease;
}

.tl::after{
  content:"";position:absolute;inset:0;z-index:9;
  border-radius:inherit;
  border:1px solid rgba(118,114,182,.44);
  pointer-events:none;
}
.tl-stage:hover .tl,.dd-tile:hover .tl,.tl-stage.is-in .tl{
  box-shadow:
    0 calc(.7 * var(--mm)) calc(2.2 * var(--mm)) rgba(126,126,178,.18),
    0 calc(28 * var(--mm)) calc(48 * var(--mm)) calc(-27 * var(--mm)) rgba(93,60,45,.42),
    0 calc(7 * var(--mm)) calc(16 * var(--mm)) calc(-9 * var(--mm)) rgba(93,60,45,.15);
}

/* 后片 = 整块板：封边是实心的，内腔是空的 —— 用一层薄填充区分 */
.tl__back{position:absolute;inset:0;border-radius:inherit;pointer-events:none}

/* 内腔台阶：toploader 与软膜最大的区别就是这圈「凹下去」的沿 */
.tl__inner{
  position:absolute;
  left:calc(3.5 * var(--mm));
  right:calc(3.5 * var(--mm));
  bottom:calc(3.5 * var(--mm));
  top:calc(1.2 * var(--mm));
  border-radius:calc(1.9 * var(--mm));
  /* 内沿一道折光 + 内侧一道极浅的影：读成凹下去一层，而不是画了个框 */
  background:rgba(160,156,222,.026);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.30),
    inset 0 0 0 1px rgba(126,122,186,.14),
    inset 0 1px 3px rgba(126,122,186,.10);
  pointer-events:none;
}

/* ---------- 卡牌：从左上角绕过圆角，竖直往下塞 ---------- */
.tl-card{
  position:absolute;
  /* 卡 63×89（标准卡 63×88 灌到 89，让顶端空 5mm 而不是 9mm）：
     内腔 69 → 左右各 3mm；内腔高 97.3 − 底折边 3.5 − 卡 89 = 顶端空 4.8mm */
  left:calc((76 * var(--mm) - 63 * var(--mm)) / 2);
  bottom:calc(3.5 * var(--mm));    /* 落到底折边上 */
  width:calc(63 * var(--mm));
  height:calc(89 * var(--mm));
  border-radius:calc(2.2 * var(--mm));
  opacity:0;
  transform:translate(-20%,-124%) rotate(-23deg);
  transform-origin:50% 100%;   /* 绕卡底边转 → 左下尖贴着圆角划进去，像被捏着上沿递进门 */
  box-shadow:
    0 calc(10 * var(--mm)) calc(16 * var(--mm)) calc(-7 * var(--mm)) rgba(21,24,40,.46),
    0 calc(2 * var(--mm)) calc(5 * var(--mm)) calc(-2 * var(--mm)) rgba(21,24,40,.24),
    /* 卡牌离后片有 35PT 的空隙，所以它会在内腔里留下一道很淡的影 */
    0 calc(1 * var(--mm)) calc(3 * var(--mm)) rgba(58,48,84,.20),
    /* 一圈贴边的冷影：把「卡 —— 缝 —— 内壁」这条关系读出来（要淡，太浓会把缝糊脏） */
    0 0 calc(1.6 * var(--mm)) rgba(58,48,84,.15),
    inset 0 0 0 1px rgba(255,255,255,.14);
  will-change:transform,opacity;
}
.tl-card svg{display:block;width:100%;height:100%;border-radius:inherit}

.tl-stage:hover .tl-card,.dd-tile:hover .tl-card,
.tl-stage.is-in .tl-card{animation:tl-insert 1.6s linear forwards}

/* ---------- 前片：硬板，所以反光是「整片」而不是「皱褶」 ---------- */
.tl__front{
  position:absolute;inset:0;border-radius:inherit;pointer-events:none;
  background:
    /* 上沿吃天光，冷紫 */
    linear-gradient(180deg,
      rgba(196,196,238,.20) 0%,
      rgba(200,198,240,.07) 6%,
      rgba(255,255,255,.008) 16%,
      rgba(255,255,255,0) 84%,
      rgba(206,186,192,.05) 95%,
      rgba(198,170,168,.10) 100%),
    /* 一整片硬的斜向反光：位置固定、边缘干净 */
    linear-gradient(104deg,
      rgba(255,255,255,0) 8%,
      rgba(255,255,255,.10) 20%,
      rgba(255,255,255,.02) 27%,
      rgba(255,255,255,0) 40%,
      rgba(255,255,255,0) 74%,
      rgba(255,255,255,.05) 86%,
      rgba(255,255,255,0) 96%),
    /* 右上角被灯兜住的一小片 */
    radial-gradient(30% 18% at 84% 3%,
      rgba(255,255,255,.22) 0%,rgba(255,255,255,.05) 44%,rgba(255,255,255,0) 72%);
  -webkit-backdrop-filter:saturate(.94) blur(.3px);
  backdrop-filter:saturate(.94) blur(.3px);
  box-shadow:
    inset 0 1px 0 rgba(255,255,255,.28),
    inset 0 -1px 0 rgba(255,255,255,.20);
}

/* 顶部开口：前片比后片短 1.2mm，露出来的那段是后片（更透、更亮） */
.tl__mouth{
  position:absolute;left:calc(3.5 * var(--mm));right:calc(3.5 * var(--mm));
  top:0;height:calc(1.2 * var(--mm));
  border-radius:calc(1.7 * var(--mm)) calc(1.7 * var(--mm)) 0 0;
  pointer-events:none;
  background:linear-gradient(180deg,
    rgba(124,118,180,.30),rgba(134,128,190,.20));
}
/* 前片顶边那道折光：这才是「开口」的边 */
.tl__mouth::after{
  content:"";position:absolute;left:0;right:0;bottom:0;height:1px;
  background:linear-gradient(90deg,
    rgba(255,255,255,0) 0%,rgba(255,255,255,.38) 10%,
    rgba(255,255,255,.58) 50%,rgba(255,255,255,.38) 90%,rgba(255,255,255,0) 100%);
}


/* 板材厚度：两层 35PT + 中间空隙 ≈ 2.2mm 的截面 */
.tl__thick{
  position:absolute;left:0;right:0;
  bottom:calc(-1.2 * var(--mm));height:calc(1.2 * var(--mm));
  border-radius:0 0 calc(1.6 * var(--mm)) calc(1.6 * var(--mm));
  background:linear-gradient(180deg,
    rgba(110,104,182,.50) 0%,
    rgba(110,104,182,.28) 58%,
    rgba(110,104,182,.07) 100%);
  pointer-events:none;
}

/* 插到位后扫过一道硬的直光（不是软膜那种圆斑） */
.tl__sheen{
  position:absolute;inset:0;border-radius:inherit;pointer-events:none;opacity:0;
  background:linear-gradient(100deg,
    rgba(255,255,255,0) 36%,
    rgba(255,255,255,.55) 47%,
    rgba(255,255,255,.86) 50%,
    rgba(255,255,255,.50) 53%,
    rgba(255,255,255,0) 64%);
  background-size:260% 100%;
  background-position:150% 0;
}
.tl-stage:hover .tl__sheen,.dd-tile:hover .tl__sheen,
.tl-stage.is-in .tl__sheen{animation:tl-sheen .7s cubic-bezier(.3,.6,.3,1) 1.28s both}

/* ---------- 动画 ---------- */
/* 卡牌：整张悬在左上方 → 下沿贴着左上圆角划进来 → 摆正 → 顺着开口竖直下压，
   到半路停住等两下敲击，每砸一下往里掉一截 */
@keyframes tl-insert{
  /* 0–46%：整张卡斜在左上方 23°，左下尖悬在开口左端上空；
     一边转正一边落到开口正中 —— 内腔只比卡宽 4mm，斜着是插不进去的。 */
  0%   {opacity:0;transform:translate(-20%,-124%) rotate(-23deg)}
  3%   {opacity:1;transform:translate(-19.4%,-123%) rotate(-22.2deg)}
  16%  {opacity:1;transform:translate(-14.5%,-117.5%) rotate(-16.2deg)}
  28%  {opacity:1;transform:translate(-8.5%,-112.5%) rotate(-9.5deg)}
  38%  {opacity:1;transform:translate(-2.5%,-110%) rotate(-3.2deg)}
  /* 46%：摆正、卡底正好落在开口 —— 从这里开始是纯垂直的「从上往下」 */
  46%  {opacity:1;transform:translate(0,-108.2%) rotate(0deg)}
  /* 46–58%：越推越快，一路压到半路（不再中途停） */
  50%  {opacity:1;transform:translate(0,-96%) rotate(0deg)}
  54%  {opacity:1;transform:translate(0,-78%) rotate(0deg)}
  58%  {opacity:1;transform:translate(0,-50%) rotate(0deg)}
  /* 58–65%：卡在半路，等两下敲击 */
  65%  {opacity:1;transform:translate(0,-50%) rotate(0deg)}
  /* 每砸一下，卡往里掉一截（回弹之后才开始掉） */
  69%  {opacity:1;transform:translate(0,-34%) rotate(0deg)}
  74%  {opacity:1;transform:translate(0,-34%) rotate(0deg)}
  79%  {opacity:1;transform:translate(0,-17%) rotate(0deg)}
  85%  {opacity:1;transform:translate(0,0) rotate(0deg)}
  100% {opacity:1;transform:translate(0,0) rotate(0deg)}
}

/* 卡夹：底部撞桌面两下 */
@keyframes tl-knock{
  0%,58%   {transform:translateY(0)}
  61%      {transform:translateY(calc(20 * var(--mm)))}
  63%      {transform:translateY(calc(-2.6 * var(--mm)))}
  65%      {transform:translateY(0)}
  72%      {transform:translateY(calc(17 * var(--mm)))}
  74%      {transform:translateY(calc(-2 * var(--mm)))}
  76%,100% {transform:translateY(0)}
}

.tl-stage:hover .tl,.dd-tile:hover .tl,
.tl-stage.is-in .tl{animation:tl-knock 1.6s linear forwards}

/* 桌面阴影：被砸的那两下压扁、变浓（已在 .tl-tilt 内，不再叠加倾斜角） */
@keyframes tl-ground-hit{
  0%,58%   {transform:translateX(-50%) scale(1,1);opacity:.82}
  61%      {transform:translateX(-50%) scale(1.24,.38);opacity:1}
  63%      {transform:translateX(-50%) scale(.90,1.22);opacity:.55}
  65%      {transform:translateX(-50%) scale(1,1);opacity:.82}
  72%      {transform:translateX(-50%) scale(1.20,.42);opacity:1}
  74%      {transform:translateX(-50%) scale(.92,1.16);opacity:.62}
  76%,100% {transform:translateX(-50%) scale(1,1);opacity:.82}
}

/* 桌面那条硬线被砸到时亮一下、轻微被压开 */
@keyframes tl-table-hit{
  0%,58%   {transform:translateX(-50%) scaleX(1);opacity:.72}
  61%      {transform:translateX(-50%) scaleX(1.07);opacity:1}
  65%      {transform:translateX(-50%) scaleX(1);opacity:.72}
  72%      {transform:translateX(-50%) scaleX(1.05);opacity:1}
  76%,100% {transform:translateX(-50%) scaleX(1);opacity:.72}
}
.tl-stage:hover .tl-ground,.dd-tile:hover .tl-ground,
.tl-stage.is-in .tl-ground{animation:tl-ground-hit 1.6s linear forwards}

.tl-stage:hover .tl-table,.dd-tile:hover .tl-table,
.tl-stage.is-in .tl-table{animation:tl-table-hit 1.6s linear forwards}

@keyframes tl-float{
  0%,100%{transform:translateY(-3px)}
  50%    {transform:translateY(3px)}
}
@keyframes tl-breathe{
  0%,100%{opacity:.72}
  50%    {opacity:1}
}

@media (prefers-reduced-motion:reduce){
  .tl-float,.tl-ground{animation:none}
  .tl-stage:hover .tl-card,.dd-tile:hover .tl-card,.tl-stage.is-in .tl-card{animation:none;opacity:1;transform:none}
  .tl-stage:hover .tl,.dd-tile:hover .tl,.tl-stage.is-in .tl{animation:none}
  .tl__sheen{display:none}
}


/* 四宫格：整格尺寸只有 430/2 宽，卡夹按 1px/mm 缩到 76×102，与卡套（80×112）同量级；
   620 断点随 --bw 68 再缩到 .87 */
@media (max-width:620px){
  .tl-stage{--mm:1.28px}
}
/* 触屏没有 hover：直接呈现装好卡的样子 */
@media (hover:none){
  .tl-card{opacity:1;transform:translate(0,0) rotate(0deg)}
  .tl-stage:hover .tl-card,.dd-tile:hover .tl-card,.tl-stage.is-in .tl-card{animation:none}
  .tl-stage:hover .tl,.dd-tile:hover .tl,.tl-stage.is-in .tl{animation:none}
  .tl-stage:hover .tl-ground,.dd-tile:hover .tl-ground,.tl-stage.is-in .tl-ground{animation:none}
  .tl-stage:hover .tl-table,.dd-tile:hover .tl-table,.tl-stage.is-in .tl-table{animation:none;opacity:.72}
  .tl__sheen{display:none}
  .tl-float,.tl-ground{animation:none}
}

@media (max-width:1024px){
  .dd-hero__inner{grid-template-columns:1fr;gap:44px}
  .dd-hero__copy{padding-left:0}
  /* 单列后卡套上方是正文，不能再让整张卡飞过去（预览窄屏同样收到 62%）*/
  .dv-card{transform:translateY(calc(-62% - 6px * var(--k))) scale(.972)}
  .dd-hero__visual{min-height:400px}
  /* 单列时四格独当一面，模型整体再放大 1.13 倍（与桌面比例一致，所以行距同比放大） */
  .dd-showcase{--bw:104px;--bh:138px;--dep:6px;--fan:13.5deg;gap:140px 8px;max-width:560px}
  .dv-stage{--k:.437}
  .tl-stage{--mm:1.54px}
}
@media (max-width:620px){
  .dd-hero{padding:64px 0}
  .dd-hero__visual{min-height:320px}
  .dd-showcase{--bw:86px;--bh:114px;--dep:5px;--fan:13deg;gap:110px 6px}
  /* 名字与正文同尺寸，窄屏不再缩（18px 的「Binders & Albums」约 150px，列宽够） */
  .dd-tile__name{font-size:17px}
  /* 卡套仍按 262 等比缩：卡册单本 114 高，乘 1.135 得 129，--k=.355 */
  .dv-stage{--k:.355}
}
@media (prefers-reduced-motion:reduce){
  .dd-trio__fan{animation:none}
}

/* ============================================================
   ④ 四宫格适配（只作用于首页这四格，独立预览页一字未动）
   ① 入卡轨迹与独立预览页逐帧一致（斜 23° 从左上绕过圆角递进去），
      全靠 .dd-showcase 的行距撑出上方净空。
   ② 砸击从 20mm 收到 12mm：格底到名字只有 15px，砸 20mm 会顶到名字。
      台面线与阴影同步提到 -12 / -14mm，砸到底时底边正好压线。
   ============================================================ */
.tl-card{transform:translate(-20%,-124%) rotate(-23deg)}
.tl-table{bottom:calc(-12 * var(--mm));opacity:0}
.tl-ground{bottom:calc(-14 * var(--mm))}

@keyframes tl-insert-grid{
  /* 0–30%：整张卡斜在左上方 23°，左下尖悬在开口左端上空；
     一边转正一边落到开口正中 —— 内腔只比卡宽 4mm，斜着是插不进去的。 */
  0%   {opacity:0;transform:translate(-20%,-124%) rotate(-23deg)}
  2%   {opacity:1;transform:translate(-18.6%,-121.4%) rotate(-21.2deg)}
  9%   {opacity:1;transform:translate(-13.6%,-116.2%) rotate(-15deg)}
  18%  {opacity:1;transform:translate(-7.2%,-111.4%) rotate(-8.2deg)}
  26%  {opacity:1;transform:translate(-1.8%,-108.9%) rotate(-2.2deg)}
  /* 30%：摆正、卡底正好落在开口 —— 从这里开始是纯垂直的「从上往下」 */
  30%  {opacity:1;transform:translate(0,-108.2%) rotate(0deg)}
  /* 30–35%：加速压到半路（插入全程 0.56s，比原来 0.83s 快三成） */
  32%  {opacity:1;transform:translate(0,-96%) rotate(0deg)}
  33.5%{opacity:1;transform:translate(0,-78%) rotate(0deg)}
  35%  {opacity:1;transform:translate(0,-50%) rotate(0deg)}
  /* 35–45%：卡在半路，等第一下砸到底（砸下 9% = 144ms，是原来 3% 的三倍长） */
  45%  {opacity:1;transform:translate(0,-50%) rotate(0deg)}
  /* 每砸一下，卡往里掉一截（砸到底之后才开始掉） */
  50%  {opacity:1;transform:translate(0,-34%) rotate(0deg)}
  71%  {opacity:1;transform:translate(0,-34%) rotate(0deg)}
  76%  {opacity:1;transform:translate(0,-17%) rotate(0deg)}
  79%,100%{opacity:1;transform:translate(0,0) rotate(0deg)}
}

/* 敲击：先极轻地抬一下（预备），再落下去砸到桌面；落程占 9%，慢到看得清。
   两下之间留 17%（0.27s）空档，击与击才分得开。 */
@keyframes tl-knock-grid{
  0%,35%   {transform:translateY(0)}
  36%      {transform:translateY(calc(-1.6 * var(--mm)))}
  45%      {transform:translateY(calc(12 * var(--mm)))}
  46.5%    {transform:translateY(calc(-2.4 * var(--mm)))}
  48%      {transform:translateY(0)}
  61%      {transform:translateY(0)}
  62%      {transform:translateY(calc(-1.2 * var(--mm)))}
  71%      {transform:translateY(calc(10 * var(--mm)))}
  72.5%    {transform:translateY(calc(-2 * var(--mm)))}
  74%,100% {transform:translateY(0)}
}

@keyframes tl-ground-hit-grid{
  0%,35%   {transform:translateX(-50%) scale(1,1);opacity:.82}
  36%      {transform:translateX(-50%) scale(.94,1.08);opacity:.70}
  45%      {transform:translateX(-50%) scale(1.28,.36);opacity:1}
  46.5%    {transform:translateX(-50%) scale(.88,1.24);opacity:.52}
  48%      {transform:translateX(-50%) scale(1,1);opacity:.82}
  61%      {transform:translateX(-50%) scale(1,1);opacity:.82}
  62%      {transform:translateX(-50%) scale(.95,1.06);opacity:.70}
  71%      {transform:translateX(-50%) scale(1.24,.40);opacity:1}
  72.5%    {transform:translateX(-50%) scale(.90,1.20);opacity:.58}
  74%,100% {transform:translateX(-50%) scale(1,1);opacity:.82}
}

@keyframes tl-table-hit-grid{
  0%,27%   {transform:translateX(-50%) scaleX(1);opacity:0}
  30%      {transform:translateX(-50%) scaleX(1);opacity:.72}
  40%      {transform:translateX(-50%) scaleX(1);opacity:.72}
  45%      {transform:translateX(-50%) scaleX(1.07);opacity:1}
  48%      {transform:translateX(-50%) scaleX(1);opacity:.72}
  71%      {transform:translateX(-50%) scaleX(1.05);opacity:1}
  74%,100% {transform:translateX(-50%) scaleX(1);opacity:.72}
}

.tl-stage:hover .tl-card,
.dd-tile:hover .tl-card,
.tl-stage.is-in .tl-card{animation:tl-insert-grid 1.6s linear forwards}

.tl-stage:hover .tl,
.dd-tile:hover .tl,
.tl-stage.is-in .tl{animation:tl-knock-grid 1.6s linear forwards}

.tl-stage:hover .tl-ground,
.dd-tile:hover .tl-ground,
.tl-stage.is-in .tl-ground{animation:tl-ground-hit-grid 1.6s linear forwards}

.tl-stage:hover .tl-table,
.dd-tile:hover .tl-table,
.tl-stage.is-in .tl-table{animation:tl-table-hit-grid 1.6s linear forwards}

/* 触屏：直接给装好的样子 */
@media (hover:none){
  .tl-card{opacity:1;transform:translate(0,0)}
  .tl-stage:hover .tl-card,.dd-tile:hover .tl-card,.tl-stage.is-in .tl-card{animation:none}
  .tl-stage:hover .tl,.dd-tile:hover .tl,.tl-stage.is-in .tl{animation:none}
  .tl-stage:hover .tl-ground,.dd-tile:hover .tl-ground,.tl-stage.is-in .tl-ground{animation:none}
  .tl-stage:hover .tl-table,.dd-tile:hover .tl-table,.tl-stage.is-in .tl-table{animation:none;opacity:.72}
}
/* ============================================================
   卡盒 · 一切尺寸从 --mm 派生（1mm 的屏幕长度）
   盒体 76 W × 84 H × 76 D｜内腔 72 × 80 × 72
   两截盖：顶板 76 × 76 ＋ 前舌 76.6 × 46（盖住正面 55%）
   ⚠️ 盒口红圈 top 必须落在盒顶「之内」（+0.8mm），落在盒顶之外
      闭合态会在顶板边上露出一条红线
   ============================================================ */
.db-stage{
  --mm:3.1px;
  --d:1.9s;
  --yaw:-26deg;      /* 看到正面 + 右侧 */
  --pitch:-19deg;    /* 略俯视，能看进盒口 */

  /* 高光压平一点：.95 的斜向亮带会在左边缘读成一条铬条，不像卡通风 */
  --shell:
    linear-gradient(135deg, rgba(255,255,255,.62) 0%, rgba(255,255,255,.26) 12%, rgba(255,255,255,0) 38%),
    linear-gradient(168deg,#fffdfa 0%,#fdfaf3 40%,#f7f1e6 78%,#f2ebdd 100%);
  --shell-side:
    linear-gradient(104deg, rgba(255,255,255,.34) 0%, rgba(255,255,255,0) 22%, rgba(120,94,64,.08) 58%, rgba(120,94,64,.18) 100%),
    linear-gradient(180deg,#f2ebde 0%,#e2dac9 62%,#d3c9b4 100%);
  --suede:
    radial-gradient(120% 88% at 24% 4%, rgba(255,255,255,.24), transparent 56%),
    linear-gradient(164deg,#e8525e 0%,#cd313e 54%,#a41f2c 100%);
  --suede-deep:
    linear-gradient(178deg,#cb303d 0%,#a81d2b 58%,#8b1522 100%);

  position:relative;display:flex;align-items:center;justify-content:center;
  width:100%;max-width:560px;
  padding:calc(96*var(--mm)) 0 calc(16*var(--mm));
  cursor:pointer;outline:none;
}

.db-scene{
  position:relative;
  perspective:calc(420 * var(--mm));
  perspective-origin:50% 52%;
  animation:db-body var(--d) linear both;
  animation-play-state:paused;
}

.db{
  position:relative;
  width:calc(76 * var(--mm));
  height:calc(84 * var(--mm));
  transform-style:preserve-3d;
  transform:rotateX(var(--pitch)) rotateY(var(--yaw));
  /* 待机漂浮：另外三格都在呼吸，只有卡盒钉在板上会读成一张静止图。
     ⚠️ 动画直接挂在 .db 上，关键帧必须把静态的 rotX/rotY 一起写全 ——
     只写 translateY 会把盒子朝向抹平，整只盒变成正对镜头的白板 */
  animation:db-idle-float 5.8s ease-in-out infinite;
  will-change:transform;
}
@keyframes db-idle-float{
  0%,100%{transform:translateY(0) rotateX(var(--pitch)) rotateY(var(--yaw))}
  50%    {transform:translateY(calc(-2.6 * var(--mm))) rotateX(var(--pitch)) rotateY(var(--yaw))}
}
/* 悬停开盖时把漂浮冻住，否则整个盒子一边晃一边翻盖 */
.db-stage:hover .db,.dd-tile:hover .db{animation-play-state:paused}
/* 所有面统一 left/top 显式定位（不用百分比 translate，否则在 3D 里会被压平） */
.db__c{position:absolute}

/* ---------- 壳：前面 / 右面 ----------
   ⚠️ 左/右/下各往外放 0.4mm：两块面恰好拼在 1px 缝上时缝里会透出内衬的红，
      整圈轮廓会浮出一条粉红线（肉眼非常明显）。
   ⚠️ 顶边不能放 —— 顶边一旦高出盒顶（top:-.4），盖板平面就压在它下面，
      缝隙里又漏出一条红边；顶边靠盖板 0.4mm 的外沿压住即可 */
.db__front{
  left:calc(-.4 * var(--mm)); top:0;
  width:calc(76.8 * var(--mm)); height:calc(84.4 * var(--mm));
  transform:translateZ(calc(38 * var(--mm)));
  /* 右边缘（TR/BR）留直角：与右面共用同一个盒角，两边都倒圆会掏出一个三角洞，
     洞里透出内衬红 —— 就是闭合态那个扎眼的红点 */
  border-radius:calc(3 * var(--mm)) 0 0 calc(3 * var(--mm));
  background:var(--shell);
  /* 只留一圈细内描边。原来的底部内投影会在正面下缘压出一条横向黑影，
     缩到四宫格尺寸像一块脏污（用户点名要删） */
  box-shadow:inset 0 0 0 calc(.8 * var(--mm)) rgba(158,128,92,.20);
}

.db__right{
  left:calc(-.4 * var(--mm)); top:0;
  width:calc(76.8 * var(--mm)); height:calc(84.4 * var(--mm));
  transform:rotateY(90deg) translateZ(calc(38 * var(--mm)));
  /* 局部 TL / BL 才是靠正面那两个角（rotateY90 后局部 x 轴映射到 z） */
  border-radius:0 calc(3 * var(--mm)) calc(3 * var(--mm)) 0;
  background:var(--shell-side);
  box-shadow:inset 0 0 0 calc(.8 * var(--mm)) rgba(158,128,92,.22);
}

/* ---------- 内腔：后壁 / 左壁 / 底（红绒） ---------- */
.db__in-back{
  left:calc(2 * var(--mm)); top:calc(2 * var(--mm));
  width:calc(72 * var(--mm)); height:calc(80 * var(--mm));
  transform:translateZ(calc(-36 * var(--mm)));
  background:var(--suede-deep);
  box-shadow:inset 0 calc(4 * var(--mm)) calc(7 * var(--mm)) calc(-4 * var(--mm)) rgba(70,0,10,.5);
}
.db__in-left{
  left:calc(2 * var(--mm)); top:calc(2 * var(--mm));
  width:calc(72 * var(--mm)); height:calc(80 * var(--mm));
  transform:rotateY(90deg) translateZ(calc(-36 * var(--mm)));
  background:linear-gradient(180deg,#bc2734,#8f1826 74%,#7d1522);
  box-shadow:inset 0 calc(4 * var(--mm)) calc(6 * var(--mm)) calc(-4 * var(--mm)) rgba(70,0,10,.45);
}
.db__in-floor{
  left:calc(2 * var(--mm)); top:calc(46 * var(--mm));
  width:calc(72 * var(--mm)); height:calc(72 * var(--mm));
  transform:rotateX(90deg);
  background:radial-gradient(72% 72% at 40% 34%, #c22f3c, #8a1522 90%);
}

/* ---------- 盒口：内沿一圈红边 ---------- */
.db__rim{
  left:calc(2 * var(--mm)); top:calc(.8 * var(--mm));
  width:calc(72 * var(--mm)); height:calc(72 * var(--mm));
  transform:rotateX(90deg);
  border-radius:calc(2 * var(--mm));
  border:calc(1 * var(--mm)) solid #8f1a25;
  background:transparent;
  box-shadow:inset 0 0 calc(3 * var(--mm)) rgba(60,0,8,.55);
}

/* ---------- 一叠卡（卡面朝前，卡边朝上） ---------- */
.db-stack{
  position:absolute;
  left:calc(38 * var(--mm)); top:calc(42 * var(--mm));
  width:0; height:0;
  transform-style:preserve-3d;
  animation:db-cards var(--d) linear both;
  animation-play-state:paused;
}
.db-stack__f{position:absolute}
/* 朝前的卡面 = DeckDorm 样卡（内联 SVG，见下方 DOM）。
   viewBox 用 100×120 —— 与 65×78mm 的面同比例(0.833)，所以 fill 到底也不会变形；
   上面不再画装饰圈：金框和菱形纹章由样卡自带 */
.db-stack__front{
  left:calc(-32.5 * var(--mm)); top:calc(-39 * var(--mm));
  width:calc(65 * var(--mm)); height:calc(78 * var(--mm));
  transform:translateZ(calc(34 * var(--mm)));
  border-radius:calc(2.4 * var(--mm));
  overflow:hidden;
  background:#f0e4cb;
}
.db-stack__front>svg{display:block;width:100%;height:100%}
/* 朝上的卡边：一叠纸的粗横纹（卡通，不追微观） */
.db-stack__top{
  left:calc(-32.5 * var(--mm)); top:calc(-33 * var(--mm));
  width:calc(65 * var(--mm)); height:calc(66 * var(--mm));
  transform:rotateX(90deg) translateZ(calc(37.5 * var(--mm)));
  background:
    repeating-linear-gradient(180deg,#fdfaf2 0 calc(4.2 * var(--mm)),#ded5c1 calc(4.2 * var(--mm)) calc(5.6 * var(--mm)));
  box-shadow:inset 0 0 calc(3 * var(--mm)) rgba(150,118,60,.22);
}
/* 朝右的卡边 */
.db-stack__right{
  left:calc(-33 * var(--mm)); top:calc(-39 * var(--mm));
  width:calc(66 * var(--mm)); height:calc(78 * var(--mm));
  transform:rotateY(90deg) translateZ(calc(31.5 * var(--mm)));
  background:
    repeating-linear-gradient(90deg,#fdfaf2 0 calc(4.2 * var(--mm)),#ded5c1 calc(4.2 * var(--mm)) calc(5.6 * var(--mm)));
}

/* ---------- 两截盖：铰链在盒顶后沿 ---------- */
.db__hinge{
  position:absolute;
  left:calc(38 * var(--mm)); top:0;
  width:0; height:0;
  transform-style:preserve-3d;
  transform:translateZ(calc(-38 * var(--mm)));
}
.db__pivot{
  position:absolute; left:0; top:0;
  width:0; height:0;
  transform-style:preserve-3d;
  animation:db-lid var(--d) linear both;
  animation-play-state:paused;
}
/* 第一截 = 顶板：绕盒顶后沿从竖直摊到水平（rotateX -90） */
.db__lid,.db__lid-in{
  position:absolute;
  left:calc(-38.4 * var(--mm)); top:0;
  width:calc(76.8 * var(--mm)); height:calc(76.8 * var(--mm));
  transform-origin:50% 0;
  transform:rotateX(90deg);
  border-radius:calc(3 * var(--mm));
}
.db__lid{
  transform-style:preserve-3d;
  background:var(--shell);
  box-shadow:
    inset 0 0 0 calc(.8 * var(--mm)) rgba(158,128,92,.20),
    inset 0 calc(-5 * var(--mm)) calc(8 * var(--mm)) calc(-5 * var(--mm)) rgba(120,92,62,.20);
  backface-visibility:hidden;
}
/* 盖板内侧：红绒。共面 + 各自 cull，正好只显示一面 */
.db__lid-in{
  transform:rotateX(90deg) rotateY(180deg);
  background:var(--suede);
  box-shadow:
    /* 第一道是壳边（盖板厚度）：没有它，翻开后就是一整片红卡纸。
       颜色不能用接近背景的 #f8f2e5 —— 与奶油底同值时等于没有 */
    inset 0 0 0 calc(1.6 * var(--mm)) #e7d3b2,
    inset 0 0 calc(7 * var(--mm)) rgba(70,0,10,.34);
  backface-visibility:hidden;
}

/* 第二截 = 前舌：与顶板同为 pivot 的直接子元素（刚性 L 形 90°），
   pivot 转 θ，它就从「垂在正面」转到「指向后上」——不用自己算角度。
   动画挂在 .db__flap-rot，两块面各留自己的 transform，才不会被关键帧冲掉 */
.db__flap-rot{
  position:absolute;
  left:calc(-38.3 * var(--mm)); top:0;
  width:calc(76.6 * var(--mm)); height:calc(46 * var(--mm));
  transform-origin:50% 0;
  transform-style:preserve-3d;
  transform:translateZ(calc(77.9 * var(--mm)));
  animation:db-flap var(--d) linear both;
  animation-play-state:paused;
}
.db__flap,.db__flap-in{
  position:absolute; inset:0;
  transform-origin:50% 0;
  border-radius:0;
  backface-visibility:hidden;
}
/* 纯色而不是渐变：这块面又窄又高，任何 linear-gradient 都会在左侧拉出一条亮带 */
.db__flap{
  /* 比正身暗一档的暖砂色：闭合态两截的差别只靠这点色差 + 上下两道缝影，
     同色同光时整盒在四宫格缩略尺寸下会读成一块白板。
     🔴 缝影必须画成 background 渐变，不能用外 box-shadow —— 在 preserve-3d 里
     带外投影的面会被 Chrome 拆成独立 quad，左边缘会掏出一条 2.7mm 的亮缝，
     缝里透出后面的正面；用背景渐变就没有第二块 quad */
  background:
    /* 只有顶边一道极浅的投影（顶板压在它上面） */
    linear-gradient(180deg, rgba(120,92,62,.18) 0, rgba(120,92,62,0) calc(.9 * var(--mm))),
    #efe6d3;
  box-shadow:
    inset 0 0 0 calc(.8 * var(--mm)) rgba(158,128,92,.22),
    /* 底边一条 0.5mm 硬线＝板材厚度。🔴 不能写成 1.8mm 的渐变：
       那会在正面中间压出一道模糊的横黑影（用户截图圈的就是这条） */
    inset 0 calc(-.5 * var(--mm)) 0 0 rgba(150,120,84,.38);
}
.db__flap-in{
  transform:rotateY(180deg);
  background:#b4212e;
  /* 与顶板内衬同一圈壳边：收起来后两截才分得开，不然就是两块红板拼在一起 */
  box-shadow:
    inset 0 0 0 calc(1.2 * var(--mm)) #e7d3b2,
    inset 0 0 calc(6 * var(--mm)) rgba(70,0,10,.34);
}

/* ---------- 悬空投影 ----------
   ⚠️ 盒子的「视觉底」比布局盒低约 12mm —— 前脸 translateZ(38mm) 在 -19° 俯视下
      往下探出去了。所以这块影子不能用 bottom 定位（会整块躲在正面白皮后面，
      看起来像根本没画），要挂在 100% + 9mm 处、只露在实拍底边之外 */
.db-ground{
  position:absolute;left:50%;top:calc(100% + 12 * var(--mm));
  width:calc(96 * var(--mm));height:calc(14 * var(--mm));
  transform:translateX(-50%);
  background:radial-gradient(50% 50% at 50% 50%, rgba(112,82,54,.46), rgba(112,82,54,0) 72%);
  filter:blur(calc(1.5 * var(--mm)));
  pointer-events:none;
  /* 和盒子反相呼吸：盒子升高，影子收紧变淡 */
  animation:db-idle-ground 5.8s ease-in-out infinite;
}
@keyframes db-idle-ground{
  0%,100%{transform:translateX(-50%) scale(1);opacity:1}
  50%    {transform:translateX(-50%) scale(.93);opacity:.78}
}
.db-stage:hover .db-ground,.dd-tile:hover .db-ground{animation-play-state:paused}

/* ============================================================
   动画 --d (1.9s) · 全部 linear 拼接（节奏靠关键帧密度，不靠 easing）
   3–52%   前舌绕自己上沿往顶板背面收，停在 62°（全程可见，不 hidden）
   2–40%   顶板绕盒顶后沿向后翻到 149°（27% 有个 156° 的过冲回弹）
   37–87%  一叠卡先在盒外完整露出 → 停一拍 → 落入盒口、顿一下塞满
   ============================================================ */
@keyframes db-flap{
  /* 相对「刚性 L」的折叠角：0°＝垂在正面（闭合态），角度越大越往顶板背面收。
     🔴 收尾停在 62° 且全程 visibility:visible —— 不要用 hidden 收尾：那样前舌
     会在半空中直接消失（用户否掉「翻到后面就消失」）。62° 时它斜靠在顶板背面，
     整块红内衬还看得见，一眼能认出是盖子的第二截。
     ⚠️ 关键帧必须按百分比递增写，visible 写在 hidden 之后会被覆盖 */
  0%,3%    {visibility:visible;transform:translateZ(calc(77.9 * var(--mm))) rotateX(0deg)}
  7%       {visibility:visible;transform:translateZ(calc(77.9 * var(--mm))) rotateX(10deg)}
  14%      {visibility:visible;transform:translateZ(calc(77.9 * var(--mm))) rotateX(24deg)}
  22%      {visibility:visible;transform:translateZ(calc(77.9 * var(--mm))) rotateX(42deg)}
  31%      {visibility:visible;transform:translateZ(calc(77.9 * var(--mm))) rotateX(58deg)}
  38%      {visibility:visible;transform:translateZ(calc(77.9 * var(--mm))) rotateX(66deg)}
  44%      {visibility:visible;transform:translateZ(calc(77.9 * var(--mm))) rotateX(61deg)}
  52%,100% {visibility:visible;transform:translateZ(calc(77.9 * var(--mm))) rotateX(62deg)}
}

@keyframes db-lid{
  0%,2%  {transform:rotateX(0deg)}
  4%     {transform:rotateX(5deg)}
  7%     {transform:rotateX(22deg)}
  10%    {transform:rotateX(52deg)}
  13%    {transform:rotateX(86deg)}
  17%    {transform:rotateX(118deg)}
  21%    {transform:rotateX(140deg)}
  24%    {transform:rotateX(152deg)}
  27%    {transform:rotateX(156deg)}
  31%    {transform:rotateX(148deg)}
  36%    {transform:rotateX(152deg)}
  40%,100% {transform:rotateX(149deg)}
}

@keyframes db-cards{
  /* 用 visibility 而不是 opacity：opacity 关键帧会把 .db-stack 变成压平容器，
     一压平所有 rotateX/rotateY 的面（卡边顶面/侧面）就全部消失。
     起点 -88mm ＝ 整叠卡完全在盒口之上、完整露出（用户要「先完整看到卡牌堆」），
     37–46% 停一拍让眼睛认出这是一叠卡，再落进盒里 */
  0%,34%   {visibility:hidden;transform:translateY(calc(-88 * var(--mm)))}
  37%      {visibility:visible;transform:translateY(calc(-88 * var(--mm)))}
  46%      {visibility:visible;transform:translateY(calc(-88 * var(--mm)))}
  56%      {visibility:visible;transform:translateY(calc(-62 * var(--mm)))}
  66%      {visibility:visible;transform:translateY(calc(-32 * var(--mm)))}
  75%      {visibility:visible;transform:translateY(calc(-10 * var(--mm)))}
  80%      {visibility:visible;transform:translateY(calc(-1.6 * var(--mm)))}
  83%      {visibility:visible;transform:translateY(calc(-5 * var(--mm)))}
  87%,100% {visibility:visible;transform:translateY(calc(-2.8 * var(--mm)))}
}

/* 落卡时整台跟着一沉（挂在 scene 上，不碰 3D 层） */
@keyframes db-body{
  0%,78%   {transform:translateY(0)}
  80.5%    {transform:translateY(calc(1.6 * var(--mm)))}
  84%      {transform:translateY(calc(-0.3 * var(--mm)))}
  87%,100% {transform:translateY(0)}
}

/* ④ 卡盒：整段照搬 deckbox-preview.html，唯一开关 --mm。
   ⚠️ --mm 上限由「开盖终态」决定，不是由闭合态决定：前舌停在 62° 时整台会往上长
      约 164px（@0.95），放大后翻起的盖会顶进上排卡套的格子。
      实测 1.15 时盖尖落在 y≈303，上排卡套可见底边在 270，还剩 33px 余量。 */
.dd-tile .db-stage{
  --mm:1.44px;width:auto;max-width:400px;cursor:inherit;
  /* 下内边距 10mm：盒子抬高一点，既让实拍底边贴近另外三格的底线，
     又给地面投影留出可见的缝。⚠️ 改 --mm 时这个值要跟着改，它也是 mm 单位 */
  padding:0 0 calc(10 * var(--mm)) 0;
}

/* 单列（620–1024）：四格放大 1.13 倍，卡盒同比跟上 */
@media (max-width:1024px) and (min-width:621px){
  .dd-tile .db-stage{--mm:1.63px}
}

@media (max-width:620px){
  /* 窄屏另外三格缩到 88%（--bh 130→114），卡盒必须跟着缩，否则一枝独秀 */
  .dd-tile .db-stage{--mm:1.27px}
}

@media (prefers-reduced-motion:reduce){
  .db,.db-ground{animation:none;will-change:auto}
}

/* ---------- Night skins ----------
   The product renders are physical objects, so they keep their own colors in
   both themes; only the type and the tile hover wash need to flip. */
html[data-theme="dark"] .dd-tile__name,
html[data-style="stainedglass"] .dd-tile__name {
	color: var(--color-accent-hover, #e6c87a);
	text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55), 0 2px 10px rgba(0, 0, 0, 0.5);
}

html[data-theme="dark"] .dd-showcase .dd-tile:hover,
html[data-style="stainedglass"] .dd-showcase .dd-tile:hover {
	background: rgba(255, 255, 255, 0.07);
}

@media (prefers-color-scheme: dark) {
	:root:not([data-theme]) .dd-tile__name {
		color: var(--color-accent-hover, #e6c87a);
		text-shadow: 0 1px 0 rgba(0, 0, 0, 0.55), 0 2px 10px rgba(0, 0, 0, 0.5);
	}
	:root:not([data-theme]) .dd-showcase .dd-tile:hover {
		background: rgba(255, 255, 255, 0.07);
	}
}

/* ============================================================
   ⑤ 大屏等比放大（2026-11-11）
   ------------------------------------------------------------
   Carl：最大化后内容偏小、四周空白太多；窗口缩小则按比例一起缩。

   左侧文字走 clamp(vw)，连续缩放。右侧四宫格是**像素标定**的 3D 模型，
   五个尺寸开关必须同时改：
     --bw / --bh / --dep   卡册三件套与舞台高度
     --k                   卡套（262×366 原稿的缩放，无单位）
     --mm（tl / db）       卡夹、卡盒的 1mm 像素数
   外加 gap 与 max-width。
   ⚠️ 为什么用阶梯而不是一个 --s 倍率：`--s` 必须是无单位数，
      `calc(100vw / 1430)` 算出来是长度、`calc(98px * var(--s))` 会整条失效
      （产品直接消失，静默失败）。而无单位数无法从 vw 派生，
      所以按倍率把数值写死成阶梯 —— **五档的倍率完全一致**，比例不失真。
   ⚠️ 只在 ≥1280px 生效。1025–1279 沿用基准值，以下断点逐档核过，不能被覆盖。
   ⚠️ 行距仍是 124 × 倍率（卡夹斜卡轨迹的净空，见 DETAIL-hero-anim.md），
      不要单独动它。
   ============================================================ */
@media (min-width:1280px) and (max-width:1479px){   /* s = 1.08 */
  .dd-showcase{--bw:105.8px;--bh:140.4px;--dep:5.94px;
    gap:133.9px 8.6px;max-width:529px}
  .dd-showcase .dd-tile__stage{padding-bottom:13px}
  .dd-showcase .dv-stage{--k:.4428}
  .dd-showcase .tl-stage{--mm:1.566px}
  .dd-showcase .db-stage{--mm:1.555px}
}
@media (min-width:1480px) and (max-width:1659px){   /* s = 1.16 */
  .dd-showcase{--bw:113.7px;--bh:150.8px;--dep:6.38px;
    gap:143.8px 9.3px;max-width:568px}
  .dd-showcase .dd-tile__stage{padding-bottom:14px}
  .dd-showcase .dv-stage{--k:.4756}
  .dd-showcase .tl-stage{--mm:1.682px}
  .dd-showcase .db-stage{--mm:1.67px}
}
@media (min-width:1660px) and (max-width:1859px){   /* s = 1.24 */
  .dd-showcase{--bw:121.5px;--bh:161.2px;--dep:6.82px;
    gap:153.8px 9.9px;max-width:608px}
  .dd-showcase .dd-tile__stage{padding-bottom:15px}
  .dd-showcase .dv-stage{--k:.5084}
  .dd-showcase .tl-stage{--mm:1.798px}
  .dd-showcase .db-stage{--mm:1.786px}
}
@media (min-width:1860px){                          /* s = 1.32 */
  .dd-showcase{--bw:129.4px;--bh:171.6px;--dep:7.26px;
    gap:163.7px 10.6px;max-width:647px}
  .dd-showcase .dd-tile__stage{padding-bottom:16px}
  .dd-showcase .dv-stage{--k:.5412}
  .dd-showcase .tl-stage{--mm:1.914px}
  .dd-showcase .db-stage{--mm:1.901px}
}
/* 产品名与正文同尺寸（Carl：名字要和灰字一样大）。两边用同一条 clamp，
   所以文字连续缩、模型按档跳，也不会读成两种字体大小。 */
@media (min-width:1025px){
  .dd-showcase .dd-tile__name{font-size:clamp(15px,calc(12.5px + .39vw),20px)}
  .dd-hero__visual{min-height:clamp(440px,36vw,620px)}
}
