/* ============================================================
   KRONOS ZHOU · Vibe Coding 项目列表 —— 扇形封面流
   路径：vibecoding/vibecoding-list.css
   ------------------------------------------------------------
   版面分三层，从后往前：
     0  .vibe-fx       ThreeUI ribbon-field（WebGL 丝带场，组件原样挂进来）
     1  .vibe-veil     压暗 + 暗角，保证卡片和文字在任何一帧上都读得清
     2  .vibe-main     页头 / 工具栏 / 舞台（扇形卡片在这里做 3D 变换）

   卡片的 3D 位置由 JS 每帧写 transform（角度、半径、缩放都在 JS 里算），
   CSS 只负责长相 —— 不然加一张卡就得回来改一次魔法数字。

   配色跟背景走：青 / 靛 / 紫；标题用衬线体，和"游戏闪卡"的实体感对得上。
   ============================================================ */

.vibe-body {
  --vq-ink: #05070d;
  --vq-line: rgba(150, 220, 255, 0.16);
  --vq-line-strong: rgba(150, 220, 255, 0.34);
  --vq-cyan: #57e0ff;
  --vq-indigo: #7b8cff;
  --vq-violet: #b98cff;
  --vq-text: #eaf2ff;
  --vq-muted: rgba(214, 230, 255, 0.58);
  --vq-serif: 'Songti SC', 'Noto Serif SC', Georgia, 'Times New Roman', serif;

  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  background: var(--vq-ink);
  color: var(--vq-text);
  overflow: hidden;
  font-family: Inter, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', system-ui, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* ============================================================
   0. 背景层
   ============================================================ */

/* 组件宿主：.threeui-background.ribbon-field 两个类由 JS 补上，
   下面的规则照着注册包里的 threeui.css 抄（只挑了这两个选择器）。
   ⚠️ .threeui-background 自带 position:relative + height:100%，会把固定层压成 0 高，
   所以这里用 .vibe-fx.threeui-background 提高优先级，把它钉回整屏。 */
.vibe-fx {
  position: fixed;
  inset: 0;
  z-index: 0;
}

.vibe-fx.threeui-background {
  position: fixed;
  inset: 0;
  width: auto;
  height: auto;
}

.threeui-background {
  position: relative;
  width: 100%;
  height: 100%;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  isolation: isolate;
  background: #030304;
}

.threeui-background > canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

/* 压暗：只压四周和底部，中间尽量让丝带场露出来（背景是这个页面的主角之一） */
.vibe-veil {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    radial-gradient(120% 78% at 50% 42%, rgba(3, 5, 10, 0) 34%, rgba(3, 5, 10, 0.34) 72%, rgba(2, 3, 6, 0.82) 100%),
    linear-gradient(180deg, rgba(2, 4, 8, 0.72) 0%, rgba(2, 4, 8, 0) 20%, rgba(2, 4, 8, 0) 78%, rgba(2, 4, 8, 0.62) 100%);
}

body.vibe-body .app-root {
  position: relative;
  z-index: 2;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex !important;
  flex-direction: column !important;
  height: auto !important;
  min-height: 100dvh;
  grid-template-rows: none !important;
}

/* 顶栏浮在背景上：去掉整页滚动时那层实底 */
body.vibe-body .site-header {
  position: relative;
  z-index: 6;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: 1px solid rgba(150, 220, 255, 0.08);
}

/* ============================================================
   1. 页头
   ============================================================ */
.vibe-main {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  width: min(1320px, calc(100vw - 3rem));
  margin: 0 auto;
  padding: 1.1rem 0 0;
}

.vibe-head {
  position: relative;
  z-index: 3;
  flex: 0 0 auto;
  text-align: center;
  padding: 0.35rem 0 0.15rem;
}

.vibe-eyebrow {
  margin: 0;
  font-size: 0.64rem;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: rgba(120, 210, 255, 0.78);
}

.vibe-title {
  margin: 0.34rem 0 0;
  font-family: var(--vq-serif);
  font-size: clamp(1.8rem, 3.4vw, 2.9rem);
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.1;
  text-shadow: 0 8px 40px rgba(0, 0, 0, 0.7);
}

.vibe-sub {
  margin: 0.42rem 0 0;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  color: var(--vq-muted);
}

/* ============================================================
   2. 工具栏（搜索 / 标签 / 计数）
   ============================================================ */
.vibe-toolbar {
  position: relative;
  z-index: 3;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex-wrap: wrap;
  justify-content: center;
  padding: 0.9rem 0 0.55rem;
}

.vibe-search {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 240px;
  flex: 0 1 300px;
}

.vibe-search-icon {
  position: absolute;
  left: 0.75rem;
  font-size: 0.95rem;
  color: rgba(150, 220, 255, 0.55);
  pointer-events: none;
}

.vibe-search-input {
  width: 100%;
  padding: 0.48rem 2rem;
  border-radius: 999px;
  border: 1px solid var(--vq-line);
  background: rgba(8, 14, 24, 0.66);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--vq-text);
  font: inherit;
  font-size: 0.78rem;
  outline: none;
  transition: border-color 180ms ease, box-shadow 180ms ease;
}

.vibe-search-input::placeholder {
  color: rgba(200, 220, 245, 0.4);
}

.vibe-search-input:focus {
  border-color: var(--vq-cyan);
  box-shadow: 0 0 0 3px rgba(87, 224, 255, 0.12);
}

.vibe-search-input::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
}

.vibe-search-clear {
  position: absolute;
  right: 0.5rem;
  width: 1.3rem;
  height: 1.3rem;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.08);
  color: var(--vq-muted);
  font-size: 0.62rem;
  cursor: pointer;
}

.vibe-search-clear:hover {
  color: var(--vq-text);
  background: rgba(255, 255, 255, 0.16);
}

.vibe-tag-filter {
  display: flex;
  gap: 0.32rem;
  flex-wrap: wrap;
  justify-content: center;
}

.vibe-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.34rem;
  padding: 0.36rem 0.72rem;
  border-radius: 999px;
  border: 1px solid var(--vq-line);
  background: rgba(8, 14, 24, 0.6);
  color: var(--vq-muted);
  font: inherit;
  font-size: 0.72rem;
  cursor: pointer;
  transition: color 160ms ease, border-color 160ms ease, background 160ms ease, transform 160ms ease;
}

.vibe-chip:hover {
  color: var(--vq-text);
  border-color: var(--vq-line-strong);
  transform: translateY(-1px);
}

.vibe-chip.active {
  color: #041017;
  font-weight: 600;
  background: linear-gradient(150deg, var(--vq-cyan), var(--vq-indigo));
  border-color: transparent;
  box-shadow: 0 6px 20px -10px rgba(87, 224, 255, 0.8);
}

.vibe-chip-count {
  font-variant-numeric: tabular-nums;
  font-size: 0.66rem;
  opacity: 0.7;
}

.vibe-count {
  font-size: 0.7rem;
  letter-spacing: 0.06em;
  color: rgba(214, 232, 255, 0.62);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.85);
  font-variant-numeric: tabular-nums;
}

/* ============================================================
   3. 舞台与扇形
   ============================================================ */
.vibe-stage {
  position: relative;
  flex: 1 1 auto;
  min-height: 380px;
  margin-top: 0.3rem;
  perspective: 1500px;
  perspective-origin: 50% 46%;
  overflow: hidden;
  touch-action: pan-y;
  /* 整个舞台统一用手指光标：卡片、按钮、索引点都能点，空白处也能拖。
     以前卡片是 pointer、舞台是 grab，指针在卡片边缘来回切会闪；
     拖动时的"抓住"由 .is-dragging 单独给。 */
  cursor: pointer;
}

.vibe-stage.is-dragging {
  cursor: grabbing;
}

.vibe-fan {
  position: absolute;
  inset: 0;
  transform-style: preserve-3d;
  /* 整层跟指针轻微倾斜，卡片自己再各转各的：视差就是这么来的 */
  transform: rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  transition: transform 420ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.vibe-stage.is-dragging .vibe-fan {
  transition: none;
}

/* ---------------- 卡片本体 ---------------- */
.vibe-item {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 300px;
  height: 424px;
  margin-left: -150px;
  margin-top: -212px;
  transform-style: preserve-3d;
  will-change: transform, opacity;
  transition: opacity 260ms ease;
  cursor: pointer;
}

.vibe-card {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 18px;
  overflow: hidden;
  background:
    linear-gradient(158deg, rgba(32, 44, 66, 0.96) 0%, rgba(13, 19, 31, 0.98) 46%, rgba(8, 12, 20, 0.99) 100%);
  border: 1px solid rgba(160, 210, 255, 0.16);
  box-shadow:
    0 30px 60px -30px rgba(0, 0, 0, 0.95),
    inset 0 1px 0 rgba(255, 255, 255, 0.09);
  transition: border-color 220ms ease, box-shadow 220ms ease;
}

/* 四角裁切标记：让它看起来像一张实体卡，而不是一个 div */
.vibe-card-frame {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.vibe-card-frame::before,
.vibe-card-frame::after,
.vibe-card-frame > i::before,
.vibe-card-frame > i::after {
  content: '';
  position: absolute;
  width: 16px;
  height: 16px;
  border: 1px solid rgba(150, 220, 255, 0.42);
}

.vibe-card-frame::before { top: 10px; left: 10px; border-right: 0; border-bottom: 0; border-radius: 4px 0 0 0; }
.vibe-card-frame::after  { top: 10px; right: 10px; border-left: 0; border-bottom: 0; border-radius: 0 4px 0 0; }
.vibe-card-frame > i::before { bottom: 10px; left: 10px; border-right: 0; border-top: 0; border-radius: 0 0 0 4px; }
.vibe-card-frame > i::after  { bottom: 10px; right: 10px; border-left: 0; border-top: 0; border-radius: 0 0 4px 0; }

/* 跟随指针的高光（--mx / --my 由 JS 写） */
.vibe-card-sheen {
  position: absolute;
  inset: -30%;
  pointer-events: none;
  background: radial-gradient(
    46% 42% at var(--mx, 50%) var(--my, 40%),
    rgba(255, 255, 255, 0.1),
    rgba(255, 255, 255, 0.03) 42%,
    transparent 68%
  );
  opacity: 0;
  transition: opacity 260ms ease;
}

/* 斜向全息扫光 */
.vibe-card-holo {
  position: absolute;
  inset: -40%;
  pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 34%,
    rgba(120, 220, 255, 0.16) 45%,
    rgba(185, 140, 255, 0.2) 52%,
    rgba(255, 255, 255, 0.08) 56%,
    transparent 68%
  );
  background-size: 300% 300%;
  background-position: 0% 50%;
  opacity: 0;
  transition: opacity 260ms ease;
}

.vibe-item:hover .vibe-card-sheen,
.vibe-item.is-active .vibe-card-sheen {
  opacity: 1;
}

.vibe-item:hover .vibe-card-holo,
.vibe-item.is-active .vibe-card-holo {
  opacity: 1;
  animation: vibe-holo 3.6s linear infinite;
}

@keyframes vibe-holo {
  0%   { background-position: 0% 50%; }
  100% { background-position: 200% 50%; }
}

.vibe-item.is-active .vibe-card {
  border-color: rgba(120, 225, 255, 0.55);
  box-shadow:
    0 40px 80px -34px rgba(0, 0, 0, 0.98),
    0 0 0 1px rgba(120, 225, 255, 0.26),
    0 0 46px -12px rgba(87, 224, 255, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.14);
}

/* 卡片内容 */
.vibe-card-inner {
  position: relative;
  z-index: 2;
  height: 100%;
  display: flex;
  flex-direction: column;
  padding: 1.15rem 1.15rem 1rem;
}

.vibe-card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.5rem;
}

.vibe-item-icon {
  font-size: 2rem;
  line-height: 1;
  filter: drop-shadow(0 6px 16px rgba(0, 0, 0, 0.7));
}

.vibe-item-icon img {
  width: 2rem;
  height: 2rem;
  object-fit: contain;
}

.vibe-item-tags {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.24rem;
}

.vibe-card-body {
  margin-top: 1.15rem;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

.vibe-item-title-cn {
  margin: 0;
  font-family: var(--vq-serif);
  font-size: 1.42rem;
  font-weight: 600;
  line-height: 1.22;
  letter-spacing: 0.01em;
  color: #f4f9ff;
  text-shadow: 0 4px 20px rgba(0, 0, 0, 0.6);
}

.vibe-item-title-en {
  margin: 0.3rem 0 0;
  font-size: 0.6rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(150, 220, 255, 0.62);
}

.vibe-item-brief {
  margin: 0.85rem 0 0;
  font-size: 0.78rem;
  line-height: 1.72;
  color: rgba(220, 234, 252, 0.72);
  display: -webkit-box;
  -webkit-line-clamp: 5;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.vibe-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding-top: 0.75rem;
  border-top: 1px solid rgba(150, 220, 255, 0.1);
  font-size: 0.68rem;
  color: rgba(200, 220, 245, 0.5);
}

.vibe-card-index {
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.18em;
  color: rgba(120, 210, 255, 0.55);
}

.vibe-item.is-active .vibe-card-open {
  color: rgba(140, 235, 255, 0.92);
}

/* ---------------- 左右切换 ---------------- */
.vibe-nav {
  position: absolute;
  top: 50%;
  z-index: 4;
  width: 2.9rem;
  height: 2.9rem;
  margin-top: -1.45rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(150, 220, 255, 0.22);
  background: rgba(8, 14, 24, 0.62);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: rgba(220, 240, 255, 0.8);
  font-size: 1.2rem;
  cursor: pointer;
  transition: background 180ms ease, border-color 180ms ease, transform 180ms ease, color 180ms ease;
}

.vibe-nav:hover {
  color: #fff;
  border-color: rgba(150, 240, 255, 0.6);
  background: rgba(20, 40, 62, 0.82);
  transform: scale(1.06);
}

.vibe-nav:active {
  transform: scale(0.96);
}

/* 左右切换按钮往内让 1.1rem：iframe 的药丸裁切比按钮本身大一圈，
   贴着边放会被 .vibe-stage 的 overflow:hidden 切出一道直边。 */
.vibe-nav-prev { left: 1.1rem; }
.vibe-nav-next { right: 1.1rem; }

/* ---------------- 索引点 ---------------- */
.vibe-dots {
  position: absolute;
  left: 50%;
  bottom: 3.1rem;
  transform: translateX(-50%);
  z-index: 4;
  display: flex;
  align-items: center;
  gap: 0.34rem;
  padding: 0.32rem 0.5rem;
  border-radius: 999px;
  background: rgba(6, 10, 18, 0.5);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.vibe-dot {
  width: 7px;
  height: 7px;
  padding: 0;
  border: 0;
  border-radius: 999px;
  background: rgba(160, 200, 240, 0.28);
  cursor: pointer;
  transition: background 160ms ease, transform 160ms ease, width 160ms ease;
}

.vibe-dot:hover {
  background: rgba(180, 230, 255, 0.6);
}

.vibe-dot.is-active {
  width: 18px;
  background: linear-gradient(90deg, var(--vq-cyan), var(--vq-indigo));
}

/* ---------------- 底部提示 ---------------- */
.vibe-hint {
  position: absolute;
  left: 50%;
  bottom: 0.85rem;
  transform: translateX(-50%);
  z-index: 4;
  margin: 0;
  font-size: 0.66rem;
  letter-spacing: 0.05em;
  color: rgba(200, 220, 245, 0.38);
  pointer-events: none;
  display: flex;
  align-items: center;
  gap: 0.34rem;
  white-space: nowrap;
}

.vibe-hint-key {
  color: rgba(150, 225, 255, 0.72);
}

.vibe-hint-sep {
  opacity: 0.35;
}

/* ---------------- 空状态 ---------------- */
.vibe-list-empty {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 3;
  padding: 1.1rem 1.4rem;
  border-radius: 14px;
  border: 1px dashed rgba(150, 220, 255, 0.24);
  background: rgba(6, 10, 18, 0.7);
  font-size: 0.82rem;
  color: var(--vq-muted);
  text-align: center;
}

/* ============================================================
   4. 标签（色相与全站一致，形状改成窄牌照）
   ============================================================ */
.vibe-tag {
  display: inline-flex;
  align-items: center;
  padding: 0.16rem 0.5rem;
  border-radius: 999px;
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  white-space: nowrap;
  border: 1px solid transparent;
}

.tag-web-app {
  color: #a8e4ff;
  background: rgba(48, 140, 200, 0.22);
  border-color: rgba(120, 200, 255, 0.34);
}

.tag-dev-tool {
  color: #b6f3cf;
  background: rgba(40, 150, 110, 0.22);
  border-color: rgba(110, 230, 180, 0.32);
}

.tag-data {
  color: #c8c4ff;
  background: rgba(90, 80, 200, 0.24);
  border-color: rgba(150, 140, 255, 0.34);
}

.tag-visual {
  color: #ffc9a6;
  background: rgba(200, 110, 50, 0.24);
  border-color: rgba(255, 170, 120, 0.34);
}

.tag-interactive {
  color: #ffb8df;
  background: rgba(190, 70, 150, 0.24);
  border-color: rgba(255, 150, 210, 0.32);
}

.tag-unknown {
  color: rgba(230, 236, 248, 0.72);
  background: rgba(140, 150, 170, 0.2);
  border-color: rgba(180, 190, 210, 0.3);
}

.tag-version {
  color: rgba(198, 220, 245, 0.6);
  background: rgba(255, 255, 255, 0.06);
  border-color: rgba(255, 255, 255, 0.1);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}

/* ============================================================
   4b. 液态金属按钮（ThreeUI LiquidMetalButton · play）
   ------------------------------------------------------------
   每个按钮里塞一个 sandbox iframe，跑注册包里的那套多 pass 金属着色器；
   按钮自己的文字放在 iframe 上面的 .lm-label 里（pointer-events:none，
   所以鼠标照样能打到金属面上触发悬停 / 按下 / 涟漪）。
   按钮原来的底与描边让位给它，只在材质没加载出来时保留一点暗底。
   ============================================================ */
/* 零优先级的定位：.vibe-nav 自己是 absolute，不能被这里盖掉。
   刻意不写 isolation：.lm-metal 要用 screen 混合穿透到卡片表面上。 */
:where(.lm-host) {
  position: relative;
  overflow: visible !important;
}

.lm-host.lm-metal-on {
  background: none !important;
  border-color: transparent !important;
  box-shadow: none !important;
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}

.lm-metal {
  position: absolute;
  z-index: 0;
  display: block;
  pointer-events: auto;
  /* iframe 里的文档画布是不透明的（源里最终 pass 写的是 alpha=0 的白色），
     所以外面套一层药丸形裁切，并由 JS 把底盘刷成按钮所在表面的同色。
     外面再叠一道边缘渐隐的遮罩：那圈"同色"永远不可能和渐变背景完全对上，
     让它从 88% 处淡出，方框的硬边就彻底看不出来了。 */
  border-radius: 999px;
  overflow: hidden;
  -webkit-mask-image:
    linear-gradient(to right, transparent 0%, #000 9%, #000 91%, transparent 100%),
    linear-gradient(to bottom, transparent 0%, #000 11%, #000 89%, transparent 100%);
  mask-image:
    linear-gradient(to right, transparent 0%, #000 9%, #000 91%, transparent 100%),
    linear-gradient(to bottom, transparent 0%, #000 11%, #000 89%, transparent 100%);
  -webkit-mask-composite: source-in;
  mask-composite: intersect;
}

.lm-metal-frame {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
  background: transparent;
  overflow: hidden;
  border-radius: inherit;
}

.lm-label {
  position: relative;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.2em;
  pointer-events: none;
  white-space: nowrap;
}

/* 圆形按钮（上一个 / 下一个 / 关闭）：字压在金属面正中 */
.vibe-nav.lm-host .lm-label {
  font-size: 1.05rem;
  line-height: 1;
  color: #f2f8ff;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.75);
}

.vibe-detail-close.lm-host .lm-label {
  font-size: 0.72rem;
  line-height: 1;
  color: #eaf4ff;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.8);
}

/* 药丸按钮（进入项目）：文字保持原来的字号与字重 */
.vibe-detail-enter.lm-host .lm-label {
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: #eaf6ff;
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.7);
}

/* ============================================================
   5. 详情：卡片 + 面板作为一组居中
   ------------------------------------------------------------
   PC 上的过场是「点中的卡转一整圈推到左边，右边同时滑出详情」：
     · .vibe-detail-visual 里放的是那张卡的副本（.vibe-flipcard）；
       它的位移由 JS 用 FLIP 算：先量出原卡位置，再把副本从原位置
       拽到最终格子，中间穿过 360° 旋转。
     · .vibe-detail-stage 是 flex 一行：卡片 | 面板，整组用 place-items 居中。
   ============================================================ */
.vibe-detail {
  position: fixed;
  inset: 0;
  z-index: 12;
  display: grid;
  place-items: center;
  padding: 1.6rem;
  background: rgba(2, 4, 8, 0.62);
  backdrop-filter: blur(10px) saturate(120%);
  -webkit-backdrop-filter: blur(10px) saturate(120%);
  /* 入场 / 出场都走这一条：JS 只负责加 .is-open，不再用一次性 keyframes
     （只播一次的话，第二次打开就再也不会动了）。 */
  opacity: 0;
  transition: opacity 300ms ease;
}

.vibe-detail.is-open {
  opacity: 1;
}

.vibe-detail[hidden] {
  display: none;
}

.vibe-detail-stage {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(20px, 3.4vw, 56px);
  width: min(1060px, 94vw);
  max-height: calc(100dvh - 3.2rem);
}

/* 卡片副本的舞台：给 3D 旋转一个透视 */
.vibe-detail-visual {
  flex: 0 0 auto;
  width: 300px;
  height: 424px;
  perspective: 1400px;
  perspective-origin: 50% 50%;
}

.vibe-flipcard {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 18px;
  overflow: hidden;
  transform-style: preserve-3d;
  /* 注意：这里不能开 backface-visibility:hidden —— 卡片要转整圈，
     转一半的时候会被自己隐藏掉，看起来就是"卡消失了一下"。 */
  background:
    linear-gradient(158deg, rgba(32, 44, 66, 0.96) 0%, rgba(13, 19, 31, 0.98) 46%, rgba(8, 12, 20, 0.99) 100%);
  border: 1px solid rgba(120, 225, 255, 0.55);
  box-shadow:
    0 44px 90px -36px rgba(0, 0, 0, 1),
    0 0 0 1px rgba(120, 225, 255, 0.26),
    0 0 60px -14px rgba(87, 224, 255, 0.55),
    inset 0 1px 0 rgba(255, 255, 255, 0.14);
}

.vibe-flipcard .vibe-card-inner {
  height: 100%;
}

.vibe-flipcard .vibe-card-holo {
  opacity: 1;
  animation: vibe-holo 4.2s linear infinite;
}

/* 打开详情时，后面的扇形流退成背景（收回时 180ms 就解开，不留"虚着不能点"的空档） */
.vibe-body.is-detail-open .vibe-fan {
  filter: blur(3px) brightness(0.55) saturate(0.8);
  transform: rotateX(0deg) rotateY(0deg) scale(0.96);
}

.vibe-fan {
  transition:
    transform 420ms cubic-bezier(0.2, 0.8, 0.2, 1),
    filter 180ms ease;
}

.vibe-stage.is-dragging .vibe-fan {
  transition: filter 180ms ease;
}

.vibe-body.is-detail-open .vibe-nav,
.vibe-body.is-detail-open .vibe-dots,
.vibe-body.is-detail-open .vibe-hint,
.vibe-body.is-detail-open .vibe-toolbar {
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms ease;
}

.vibe-detail-card {
  position: relative;
  flex: 1 1 460px;
  max-width: 520px;
  max-height: min(720px, calc(100dvh - 3.2rem));
  overflow-y: auto;
  padding: 1.5rem 1.5rem 1.2rem;
  border-radius: 20px;
  border: 1px solid rgba(150, 220, 255, 0.24);
  background: linear-gradient(166deg, rgba(18, 26, 40, 0.98), rgba(8, 12, 20, 0.99));
  box-shadow: 0 50px 100px -50px #000, inset 0 1px 0 rgba(255, 255, 255, 0.08);

  /* 面板本体：从右边偏 34px 起、轻微缩小 + 一层薄模糊，然后落到位置上 */
  opacity: 0;
  transform: translate3d(34px, 0, 0) scale(0.986);
  filter: blur(7px);
  transition:
    opacity 300ms ease,
    transform 440ms cubic-bezier(0.22, 0.9, 0.24, 1),
    filter 380ms ease;
}

.vibe-detail.is-open .vibe-detail-card {
  opacity: 1;
  transform: translate3d(0, 0, 0) scale(1);
  filter: blur(0);
}

/* 面板边缘那道入场扫光：一条从上方滑下来的亮线，只在打开时出现一次 */
.vibe-detail-card::after {
  content: '';
  position: absolute;
  top: 0;
  left: 12%;
  right: 12%;
  height: 1px;
  border-radius: 1px;
  background: linear-gradient(90deg, transparent, rgba(140, 232, 255, 0.9), transparent);
  opacity: 0;
  transform: scaleX(0.2);
  transition: opacity 260ms ease 180ms, transform 620ms cubic-bezier(0.22, 0.9, 0.24, 1) 120ms;
  pointer-events: none;
}

.vibe-detail.is-open .vibe-detail-card::after {
  opacity: 1;
  transform: scaleX(1);
}

/* 内容逐块浮上来：先标题、再标签、再正文，最后按钮 —— 比整块一起淡入耐看 */
.vibe-detail-head,
.vibe-detail-tags,
.vibe-detail-desc,
.vibe-detail-meta,
.vibe-detail-actions {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 300ms ease, transform 380ms cubic-bezier(0.22, 0.9, 0.24, 1);
}

.vibe-detail-close {
  opacity: 0;
  transition: opacity 260ms ease, color 160ms ease, border-color 160ms ease;
}

.vibe-detail.is-open .vibe-detail-head { opacity: 1; transform: none; transition-delay: 110ms; }
.vibe-detail.is-open .vibe-detail-tags { opacity: 1; transform: none; transition-delay: 170ms; }
.vibe-detail.is-open .vibe-detail-desc { opacity: 1; transform: none; transition-delay: 220ms; }
.vibe-detail.is-open .vibe-detail-meta { opacity: 1; transform: none; transition-delay: 270ms; }
.vibe-detail.is-open .vibe-detail-actions { opacity: 1; transform: none; transition-delay: 320ms; }
.vibe-detail.is-open .vibe-detail-close { opacity: 1; transition-delay: 160ms; }

.vibe-detail-close {
  position: absolute;
  top: 0.9rem;
  right: 0.9rem;
  width: 1.9rem;
  height: 1.9rem;
  display: grid;
  place-items: center;
  border-radius: 50%;
  border: 1px solid rgba(150, 220, 255, 0.2);
  background: rgba(255, 255, 255, 0.05);
  color: var(--vq-muted);
  font-size: 0.7rem;
  cursor: pointer;
}

.vibe-detail-close:hover {
  color: #fff;
  border-color: rgba(150, 240, 255, 0.5);
}

.vibe-detail-head {
  display: flex;
  align-items: center;
  gap: 0.8rem;
  padding-right: 2rem;
}

.vibe-detail-icon {
  font-size: 2.2rem;
  line-height: 1;
}

.vibe-detail-icon img {
  width: 2.2rem;
  height: 2.2rem;
}

.vibe-detail-heading {
  min-width: 0;
}

.vibe-detail-title {
  margin: 0;
  font-family: var(--vq-serif);
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.2;
}

.vibe-detail-en {
  margin: 0.22rem 0 0;
  font-size: 0.62rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(150, 220, 255, 0.6);
}

.vibe-detail-tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.32rem;
  margin-top: 0.9rem;
}

.vibe-detail-desc {
  margin: 0.95rem 0 0;
  font-size: 0.82rem;
  line-height: 1.85;
  color: rgba(220, 234, 252, 0.78);
}

.vibe-detail-meta {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.6rem;
  margin: 1.1rem 0 0;
  padding-top: 0.9rem;
  border-top: 1px solid rgba(150, 220, 255, 0.1);
}

.vibe-detail-meta div {
  min-width: 0;
}

.vibe-detail-meta dt {
  font-size: 0.58rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(160, 200, 240, 0.45);
}

.vibe-detail-meta dd {
  margin: 0.2rem 0 0;
  font-size: 0.76rem;
  color: rgba(228, 240, 255, 0.86);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.vibe-detail-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-top: 1.2rem;
}

.vibe-detail-enter {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.62rem 1.1rem;
  border-radius: 999px;
  background: linear-gradient(150deg, var(--vq-cyan), var(--vq-indigo));
  color: #04121a;
  font-size: 0.82rem;
  font-weight: 600;
  text-decoration: none;
  box-shadow: 0 14px 32px -16px rgba(87, 224, 255, 0.9);
  transition: transform 180ms ease, box-shadow 180ms ease;
}

.vibe-detail-enter:hover {
  transform: translateY(-2px);
  box-shadow: 0 20px 40px -18px rgba(87, 224, 255, 1);
}

.vibe-detail-enter {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.62rem 1.1rem;
  border-radius: 999px;
  background: linear-gradient(150deg, var(--vq-cyan), var(--vq-indigo));
  color: #04121a;
  font-size: 0.82rem;
  font-weight: 600;
  text-decoration: none;
  box-shadow: 0 14px 32px -16px rgba(87, 224, 255, 0.9);
  transition: transform 180ms ease, box-shadow 180ms ease;
}

.vibe-detail-enter:hover {
  transform: translateY(-2px);
  box-shadow: 0 20px 40px -18px rgba(87, 224, 255, 1);
}

/* ============================================================
   6. 页脚
   ============================================================ */
body.vibe-body .site-footer {
  flex: 0 0 auto;
  position: relative;
  z-index: 3;
  margin-top: 0;
  display: flex;
  justify-content: center;
  gap: 1.2rem;
  padding: 0.55rem 1rem 0.7rem;
  font-size: 0.62rem;
  color: rgba(200, 220, 245, 0.34);
  border-top: 1px solid rgba(150, 220, 255, 0.06);
  background: transparent;
}

/* ============================================================
   6. 进入项目：卡片放大到全屏 → 隐去，信息留在上方中部
   ------------------------------------------------------------
   背景（丝带场）完全不动，只在上面压一层很淡的暗；
   .vibe-launch-face 是那张卡的"面"，从卡片位置 FLIP 放大到整屏，
   到 80% 左右开始淡出，露出后面的背景 —— 视觉上就是"卡片炸开消失"。
   ============================================================ */
.vibe-launch {
  position: fixed;
  inset: 0;
  z-index: 20;
  overflow: hidden;
  /* 进入项目期间页面内容整体隐去（见下面的 .is-launching 规则），
     所以这里只压一层很薄的暗，主要让背景的粒子动画露出来 */
  background: rgba(3, 6, 12, 0.34);
  animation: vibe-fade 200ms ease;
}

.vibe-launch[hidden] {
  display: none;
}

/* 放大过场期间：顶栏 / 页头 / 工具栏 / 卡片 / 页脚全部淡出，只留背景动画 */
body.is-launching .site-header,
body.is-launching .vibe-main,
body.is-launching .site-footer {
  opacity: 0 !important;
  pointer-events: none !important;
  transition: opacity 240ms ease;
}

body.is-launching .vibe-fan {
  filter: none;
}

.vibe-launch-face {
  position: absolute;
  left: 0;
  top: 0;
  width: 100vw;
  height: 100vh;
  border-radius: 18px;
  background:
    linear-gradient(158deg, rgba(40, 56, 84, 0.98) 0%, rgba(16, 24, 38, 0.99) 46%, rgba(8, 12, 20, 1) 100%);
  box-shadow:
    0 0 0 1px rgba(150, 225, 255, 0.35),
    0 0 80px -10px rgba(87, 224, 255, 0.6);
  transform-origin: 50% 50%;
  will-change: transform, opacity, border-radius;
  pointer-events: none;
}

.vibe-launch-info {
  position: absolute;
  left: 50%;
  top: 12vh;
  width: min(640px, 86vw);
  transform: translateX(-50%);
  text-align: center;
  color: var(--vq-text);
  opacity: 0;
  transition: opacity 320ms ease, transform 420ms cubic-bezier(0.22, 0.9, 0.24, 1);
}

.vibe-launch.is-shown .vibe-launch-info {
  opacity: 1;
  transform: translateX(-50%) translateY(0);
}

.vibe-launch-icon {
  font-size: 2.9rem;
  line-height: 1;
  margin-bottom: 0.6rem;
  filter: drop-shadow(0 10px 30px rgba(0, 0, 0, 0.7));
}

.vibe-launch-icon img {
  width: 2.9rem;
  height: 2.9rem;
}

.vibe-launch-title {
  margin: 0;
  font-family: var(--vq-serif);
  font-size: clamp(1.5rem, 3.2vw, 2.4rem);
  font-weight: 600;
  letter-spacing: 0.01em;
  text-shadow: 0 6px 30px rgba(0, 0, 0, 0.8);
}

.vibe-launch-desc {
  margin: 0.6rem auto 0;
  max-width: 46ch;
  font-size: 0.82rem;
  line-height: 1.8;
  color: rgba(214, 232, 255, 0.66);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.vibe-launch-meter {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  justify-content: center;
  margin: 1.5rem auto 0;
  width: min(420px, 70vw);
}

.vibe-launch-bar {
  position: relative;
  flex: 1 1 auto;
  height: 3px;
  border-radius: 99px;
  background: rgba(160, 210, 255, 0.18);
  overflow: hidden;
}

.vibe-launch-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  border-radius: 99px;
  background: linear-gradient(90deg, var(--vq-cyan), var(--vq-indigo));
  box-shadow: 0 0 14px -2px var(--vq-cyan);
}

.vibe-launch-pct {
  flex: 0 0 auto;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  font-variant-numeric: tabular-nums;
  color: rgba(190, 226, 255, 0.7);
}

.vibe-launch-pct b {
  color: #eaf7ff;
  font-weight: 600;
}

.vibe-launch-sub {
  margin: 0.75rem 0 0;
  font-size: 0.66rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: rgba(170, 205, 240, 0.42);
}

/* ============================================================
   7. 响应式
   ============================================================ */
@media (max-width: 1080px) {
  .vibe-item {
    width: 268px;
    height: 386px;
    margin-left: -134px;
    margin-top: -193px;
  }

  .vibe-item-title-cn {
    font-size: 1.26rem;
  }
}

@media (max-width: 820px) {
  body.vibe-body {
    overflow: auto;
  }

  .vibe-main {
    width: calc(100vw - 1.6rem);
  }

  .vibe-stage {
    min-height: 440px;
  }

  .vibe-nav {
    width: 2.4rem;
    height: 2.4rem;
    margin-top: -1.2rem;
  }

  .vibe-nav-prev { left: 0.9rem; }
  .vibe-nav-next { right: 0.9rem; }

  .vibe-dots {
    bottom: 2.2rem;
  }

  .vibe-hint {
    display: none;
  }

  /* 窄屏：不做"转一圈推到左边"，卡片副本直接不出现，详情变成底部抽屉 */
  .vibe-detail {
    display: flex;
    align-items: flex-end;
    justify-content: center;
    padding: 0.6rem;
  }

  .vibe-detail-stage {
    display: block;
    width: 100%;
    max-height: none;
  }

  .vibe-detail-visual {
    display: none;
  }

  .vibe-detail-card {
    width: 100%;
    max-width: none;
    max-height: 84dvh;
    border-radius: 18px;
    /* 底部多留一点：金属按钮的辉光要地方呼吸，不然会被抽屉边切一刀 */
    padding-bottom: 1.9rem;
  }

  .vibe-body.is-detail-open .vibe-fan {
    filter: none;
    transform: rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg));
  }

  .vibe-detail-meta {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* 窄屏：进入项目按钮占满一行，手指更好点 */
  .vibe-detail-actions {
    flex-direction: column;
    align-items: stretch;
    gap: 1rem;
  }

  .vibe-detail-enter {
    justify-content: center;
  }
}

@media (max-width: 560px) {
  .vibe-title {
    font-size: 1.55rem;
  }

  .vibe-sub {
    font-size: 0.72rem;
  }

  .vibe-item {
    width: 232px;
    height: 336px;
    margin-left: -116px;
    margin-top: -168px;
  }

  .vibe-item-title-cn {
    font-size: 1.1rem;
  }

  .vibe-item-brief {
    -webkit-line-clamp: 4;
    font-size: 0.72rem;
  }

  .vibe-card-inner {
    padding: 0.9rem 0.9rem 0.8rem;
  }

  .vibe-stage {
    min-height: 400px;
  }

  .vibe-toolbar {
    gap: 0.5rem;
  }

  .vibe-search {
    flex-basis: 100%;
  }
}

/* 尊重系统的"减少动态效果" */
@media (prefers-reduced-motion: reduce) {
  .vibe-fan {
    transform: none !important;
    transition: none;
  }

  .vibe-card-holo {
    animation: none !important;
  }

  /* 面板只留一个短淡入，位移 / 模糊 / 逐块延迟全关掉 */
  .vibe-detail-card,
  .vibe-detail-head,
  .vibe-detail-tags,
  .vibe-detail-desc,
  .vibe-detail-meta,
  .vibe-detail-actions,
  .vibe-detail-close {
    transform: none !important;
    filter: none !important;
    transition: opacity 160ms ease !important;
    transition-delay: 0ms !important;
  }

  .vibe-detail-card::after {
    display: none;
  }

  .vibe-launch-card {
    transition: none !important;
  }
}
