@charset "UTF-8";
/* ============================================================
   sections.css · 区块布局（移动端优先）
   归属：engineer-ui（独占）。只引用 tokens.css 的变量，不重定义。

   选择器以真实渲染层为准（已逐一核对 hero.js / timeline.js /
   toolbar.js / footer.js / music.js 的 createElement 输出）：
     .page > .hero(>.hero__inner.glass + .theme-toggle) + .stage
     .stage > .toolbar(>.toolbar__row>.__label + .__chips>.chip) /
              .timeline(>.tl-group + .tl-item.reveal) /
              .empty-state / .site-footer
   计时器脉冲由 hero.js 每秒切换 .is-beat 触发（不是常驻动画）。
   ============================================================ */

/* ---------- 1. 页面骨架 ---------- */

.page {
  position: relative;
  z-index: var(--z-content);
  display: flex;
  flex-direction: column;
  align-items: stretch;
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  /* 横向绝不溢出：配合 base.css 的 html/body overflow-x 再做一层保险 */
  overflow-x: clip;
  min-height: 100%;
}

.stage {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--sp-7);
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  padding: var(--sp-6) var(--sp-4) var(--sp-8);
  /* 底部给浮动音乐按钮留出呼吸位置，避免压到页脚文字 */
  padding-bottom: calc(var(--sp-10) + var(--safe-bottom));
}

/* ---------- 2. 首屏 hero ---------- */

.hero {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  /* 首屏视觉焦点；svh 自适应移动端地址栏，vh 作为老浏览器回退 */
  min-height: 88vh;
  min-height: 88svh;
  padding: calc(var(--sp-7) + var(--safe-top)) var(--sp-4) var(--sp-7);
}

.hero__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  padding: var(--sp-6) var(--sp-4);
  text-align: center;
  border-radius: var(--r-xl);
}

.hero__eyebrow {
  margin: 0;
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
  font-weight: 500;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--c-gold-500);
}

.hero__title {
  margin: 0;
  font-family: var(--ff-display);
  /* 375px 下约 34px，PC 上限 52px */
  font-size: clamp(2.125rem, 1.35rem + 3.2vw, 3.25rem);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: -0.01em;
  text-wrap: balance;
  /* 兜底色：不支持 background-clip:text 时用纯色，绝不出现不可见文字 */
  color: var(--c-rose-600);
}

@supports (-webkit-background-clip: text) or (background-clip: text) {
  .hero__title {
    background-image: var(--grad-text);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

.hero__amp {
  display: inline-block;
  padding: 0 0.08em;
  font-family: var(--ff-display);
  font-weight: 500;
  color: var(--c-gold-400);
  animation: amp-breathe 5.6s var(--ease-in-out-soft) infinite;
}

@keyframes amp-breathe {
  0%,
  100% {
    opacity: 1;
    text-shadow: 0 0 0 transparent;
  }
  50% {
    opacity: 0.88;
    /* 玫瑰金柔光：用 currentColor，避免硬编码色值 */
    text-shadow: 0 0 18px currentColor;
  }
}

.hero__subtitle {
  max-width: 34em;
  margin: 0;
  font-family: var(--ff-body);
  font-size: var(--fs-md);
  line-height: var(--lh-loose);
  color: var(--c-ink-500);
  text-wrap: pretty;
}

.hero__avatars {
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: var(--sp-1);
}

/* 头像环 / 内发光 / 外发光由 glass.css 的 .avatar 负责（designer 独占），
   这里只做 hero 内的排布：并排 + 轻微重叠 + 交错层级。
   用 .hero__avatars 提高特异性，避免影响其他位置的头像。 */
.hero__avatars .avatar {
  width: 60px;
  height: 60px;
}

/* 两个头像现在由中间的「粉线 + 爱心」隔开，不再需要负边距重叠 */
.hero__avatars .avatar + .avatar {
  margin-left: 0;
}

.hero__avatars .avatar--her {
  position: relative;
  z-index: 2;
}

.hero__avatars .avatar--him {
  position: relative;
  z-index: 1;
}

/* --------------------------------------------------------------------------
   两个头像之间：一条粉色渐隐线 + 正中一颗「扑通扑通」跳的红爱心
   （用户原话：「两个头像中间有一条粉色的线 然后中间有一个扑通扑通跳的红爱心」）
   DOM 顺序为 头像(her) → .hero__heart → 头像(him)。
   -------------------------------------------------------------------------- */
.hero__heart {
  position: relative;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  /* 左右各 16px：给头像留出真实空档，粉线才有地方画，两个头像不再贴在一起。
     取值需 > 粉线长 + 心跳放大余量（心率动画会 scale 到 1.18，会额外吃掉约 2.7px）。 */
  margin: 0 16px;
  color: var(--c-heart, #e8385f);
  /* 爱心是红心，给它一层暖玫瑰光晕让它在粉底上跳出来 */
  filter:
    drop-shadow(0 0 6px color-mix(in srgb, var(--c-heart, #e8385f) 42%, transparent))
    drop-shadow(0 2px 4px color-mix(in srgb, var(--c-rose-600) 22%, transparent));
}

/* 「扑通扑通」= 两下快搏动 + 一段停顿，而不是匀速呼吸。
   幅度刻意克制（峰值 1.18），要的是心跳感，不是闪烁。
   ★ 动画只加在「图标」上，不加在 .hero__heart 容器上：
   缩放若加在容器上，容器上的伪元素（那条粉线）会跟着被放大伸到头像底下，
   这正是两个头像"贴在一起"的隐藏成因。 */
.hero__heart-svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
  animation: heart-throb 1.35s var(--ease-in-out-soft) infinite;
  transform-origin: 50% 55%;
}

/* 那条粉线：画在爱心容器上，向两侧延伸并渐隐（用渐变而非实线，才有质感）。
   因为动画只在 svg 上，容器不缩放，所以线长只要 ≤ 容器左右外边距（16px）即可，
   取 12px 留一点呼吸感，线绝不会压到头像上。 */
.hero__heart::before,
.hero__heart::after {
  content: "";
  position: absolute;
  top: 50%;
  width: 12px;
  height: 1.5px;
  margin-top: -0.75px;
  border-radius: 999px;
  pointer-events: none;
}

.hero__heart::before {
  right: 100%;
  background-image: linear-gradient(
    to right,
    transparent 0%,
    color-mix(in srgb, var(--c-rose-300) 55%, transparent) 45%,
    var(--c-rose-400) 100%
  );
}

.hero__heart::after {
  left: 100%;
  background-image: linear-gradient(
    to left,
    transparent 0%,
    color-mix(in srgb, var(--c-rose-300) 55%, transparent) 45%,
    var(--c-rose-400) 100%
  );
}

/* 「扑通扑通」= 两下快搏动 + 一段停顿，而不是匀速呼吸。
   幅度刻意克制（峰值 1.18），用户要的是心跳感，不是闪烁。 */
@keyframes heart-throb {
  0% {
    transform: scale(1);
  }
  12% {
    transform: scale(1.18);
  }
  24% {
    transform: scale(1);
  }
  36% {
    transform: scale(1.12);
  }
  50% {
    transform: scale(1);
  }
  100% {
    transform: scale(1);
  }
}

/* 减少动态效果：关掉心跳（动画在 svg 上，容器与降级文本一并兜底） */
@media (prefers-reduced-motion: reduce) {
  .hero__heart-svg,
  .hero__heart {
    animation: none;
  }

  .hero__heart {
    filter: drop-shadow(0 0 5px color-mix(in srgb, var(--c-heart, #e8385f) 36%, transparent));
  }
}

/* 里程碑彩蛋（hero.js 可选渲染）：柔和胶囊 */
.hero__milestone {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  max-width: 100%;
  margin: 0;
  padding: var(--sp-2) var(--sp-4);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-full);
  background: var(--glass-bg);
  color: var(--c-ink-700);
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
}

.hero__milestone-emoji {
  font-size: var(--fs-sm);
  line-height: 1;
}

.hero__milestone-days {
  font-family: var(--ff-num);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--c-rose-600);
}

/* 主题切换：hero 右上角圆形玻璃按钮（44px 热区） */
.theme-toggle {
  position: absolute;
  top: calc(var(--sp-3) + var(--safe-top));
  right: calc(var(--sp-3) + var(--safe-right));
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: var(--tap-min);
  height: var(--tap-min);
  padding: 0;
  border: 1px solid var(--glass-border);
  border-radius: var(--r-full);
  background: var(--glass-bg);
  box-shadow: inset 0 1px 0 var(--glass-inner-glow);
  -webkit-tap-highlight-color: transparent;
}

.theme-toggle__icon {
  display: block;
  width: 18px;
  height: 18px;
  border-radius: var(--r-full);
  background-image: var(--grad-btn);
  /* 用内阴影「咬」出一弯月牙，比画字形更稳、不依赖字体。
     咬口颜色用 --glass-bg-strong，随主题自动变（浅色=奶白玻璃，深色=墨玫瑰），
     所以无需为深色单独重写形状。 */
  box-shadow: inset -6px -3px 0 0 var(--glass-bg-strong);
  transition: box-shadow var(--dur-base) var(--ease-out-soft);
}

/* 夜间模式：保留同一弯月牙，另加一圈柔和金光（即使该规则被覆盖，
   上面那条基于主题变量的月牙依然成立）。 */
html[data-theme="dark"] .theme-toggle__icon {
  box-shadow:
    inset -6px -3px 0 0 var(--glass-bg-strong),
    0 0 10px var(--c-gold-400);
}

/* ---------- 3. 计时器 ---------- */

.timer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  margin-top: var(--sp-2);
}

.timer__label {
  margin: 0;
  font-family: var(--ff-body);
  font-size: var(--fs-xs);
  letter-spacing: 0.16em;
  color: var(--c-ink-500);
}

.timer__grid {
  display: flex;
  flex-wrap: nowrap;
  /* 用 baseline 让四组文字共享同一条基线（正确的排版结果）：
     天数格子字号更大，所以它的盒顶自然更高，这不是错位。 */
  align-items: baseline;
  justify-content: center;
  gap: 0;
  width: 100%;
  max-width: 100%;
}

/* 用户要求：连写形态「30天 · 21时 · 39分 · 10秒」。
   .timer__unit 只是内联文本组，绝不能有任何卡片外观
   （背景/描边/圆角/内发光/padding 一律不要 —— 用户点名批评过「每个数字一个空间」）。 */
.timer__unit {
  display: inline-flex;
  flex-direction: row;
  align-items: baseline;
  justify-content: center;
  gap: 0.12em;
  box-sizing: border-box;
  min-width: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
  box-shadow: none;
}

.timer__num {
  display: inline-block;
  font-family: var(--ff-num);
  font-size: clamp(1.5rem, 1.05rem + 1.9vw, 2.15rem);
  font-weight: 600;
  /* 等宽数字：避免每秒跳字导致宽度抖动 */
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: 0.01em;
  line-height: 1.1;
  color: var(--c-rose-600);
}

@supports (-webkit-background-clip: text) or (background-clip: text) {
  .timer__num {
    background-image: var(--grad-text);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

/* 单位小而轻：数字大、单位小，形成主次节奏 */
.timer__unit-label {
  font-family: var(--ff-body);
  font-size: clamp(0.78rem, 0.62rem + 0.55vw, 0.98rem);
  font-weight: 500;
  line-height: 1;
  color: var(--c-ink-500);
  letter-spacing: 0.02em;
}

/* 组间的小圆点分隔：低调的点缀，不喧宾夺主 */
.timer__sep {
  display: inline-block;
  flex: 0 0 auto;
  margin: 0 0.42em;
  font-family: var(--ff-body);
  font-size: clamp(0.8rem, 0.64rem + 0.6vw, 1rem);
  font-weight: 400;
  line-height: 1;
  color: color-mix(in srgb, var(--c-rose-400) 62%, transparent);
  transform: translateY(-0.06em);
}

/* 天数是主角：数字最大最重（无卡片衬托，只靠字号与字重拉主次） */
.timer__unit--days .timer__num {
  font-size: clamp(1.85rem, 1.25rem + 2.5vw, 2.7rem);
  font-weight: 700;
}

.timer__unit--days .timer__unit-label {
  font-size: clamp(0.86rem, 0.68rem + 0.6vw, 1.05rem);
  font-weight: 600;
  color: var(--c-ink-700);
}

.timer__unit--days .timer__num {
  /* 天数变化极慢（一天一次），用静态柔光即可照亮主角。
     不用 text-shadow：数字是 background-clip:text 的透明填充，
     text-shadow 会从字芯透出来糊成一片，drop-shadow 才是正确的发光做法。 */
  filter: drop-shadow(0 0 10px color-mix(in srgb, var(--c-rose-300) 55%, transparent));
}

/* 秒位脉冲：两种驱动方式都兼容（render 层在两种实现间切换过）。
   · 形态 A：hero.js 输出 .timer__num--pulse → 与秒跳同步的 1s 无限循环
   · 形态 B：hero.js 每秒加/删 .is-beat 重放 → 单次播放
   元素同时带 .timer__num--pulse 与 .is-beat 时，后者在下方以单次播放覆盖，
   所以不会出现「双重动画」。幅度刻意压到 1.055 —— 甜而不闪。 */
.timer__num--pulse {
  animation: timer-pulse 1s var(--ease-out-soft) infinite;
}

.timer__num.is-beat {
  animation: timer-pulse 900ms var(--ease-out-soft) 1;
}

@keyframes timer-pulse {
  0% {
    transform: scale(1);
  }
  35% {
    transform: scale(1.055);
    filter: drop-shadow(0 0 12px color-mix(in srgb, var(--c-rose-400) 70%, transparent));
  }
  100% {
    transform: scale(1);
    filter: drop-shadow(0 0 0 transparent);
  }
}

/* ---------- 4. 筛选工具栏 ---------- */

/* render 层存在两种 DOM 形态，两者都要好看：
   · 扁平形态：chip 直接平铺在 .toolbar 里（.toolbar 自身横向滚动）
   · 分组形态：.toolbar > .toolbar__row > (.toolbar__label + .toolbar__chips > .chip)
   默认按「分组形态」写（列向堆叠），再用 :has() 精确识别扁平形态改为横向滚动。
   不支持 :has() 的老浏览器落到默认列向；因为 .toolbar__chips 自己也带横向滚动，
   即使形态判断错位，chips 也不会溢出或换行。 */
.toolbar {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  box-sizing: border-box;
  width: auto;
  max-width: none;
  /* 负外边距让滚动区贴到屏幕边缘，再用 padding 把内容拉回与正文对齐 */
  margin-inline: calc(-1 * var(--sp-4));
}

.toolbar__row {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  box-sizing: border-box;
  min-width: 0;
}

.toolbar__label {
  padding-inline: calc(var(--sp-4) + var(--safe-left));
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
  letter-spacing: 0.16em;
  color: var(--c-ink-300);
}

/* chips 行：移动端横向滚动、不换行、隐藏滚动条、轻吸附 */
.toolbar__chips,
.toolbar:not(:has(.toolbar__chips)) {
  display: flex;
  flex-wrap: nowrap;
  gap: var(--sp-2);
  align-items: center;
  box-sizing: border-box;
  padding-block: var(--sp-1);
  padding-inline: calc(var(--sp-4) + var(--safe-left)) calc(var(--sp-4) + var(--safe-right));
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scroll-padding-inline: var(--sp-4);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  scrollbar-color: transparent transparent;
}

/* 扁平形态下 .toolbar 自己就是滚动容器，行间距要收掉 */
.toolbar:not(:has(.toolbar__chips)) {
  gap: var(--sp-2);
}

.toolbar::-webkit-scrollbar,
.toolbar__chips::-webkit-scrollbar {
  width: 0;
  height: 0;
}

.toolbar__chips > .chip,
.toolbar:not(:has(.toolbar__chips)) > .chip {
  flex: 0 0 auto;
  scroll-snap-align: start;
}

/* 分组提示：竖向细分隔 + 小字标签，不参与交互。
   只在扁平形态出现，因此两种形态都能用同一条规则。 */
.toolbar__sep {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  align-self: stretch;
  gap: var(--sp-1);
  padding-inline: var(--sp-1);
  border-left: 1px solid var(--glass-border);
  color: var(--c-ink-300);
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
  letter-spacing: 0.12em;
  white-space: nowrap;
  user-select: none;
}

/* chip 的玻璃外观与 is-active 粉色渐变由 glass.css 提供（designer 独占），
   base.css 也已给 button 兜底 44px 热区，这里只补内部排版 */

/* toolbar.js 同时设置 .is-active 与 aria-pressed="true"，
   保证两种选择器都能吃到选中样式 */
.chip.is-active {
  font-weight: 600;
}

.chip__text {
  white-space: nowrap;
}

.chip__count {
  font-family: var(--ff-num);
  font-size: var(--fs-2xs);
  font-variant-numeric: tabular-nums;
  opacity: 0.62;
}

/* 「更多标签」按钮：幽灵样式，用虚线边框与常规 chip 区分 */
.chip--ghost {
  border-style: dashed;
  color: var(--c-ink-500);
}

/* ---------- 5. 时间轴 ---------- */

.timeline {
  /* sticky 分组标题的偏移基准：本文件的所有权钩子（不是设计令牌）。
     在这里显式声明以保持自洽（验收报告 G1 建议），下游想要覆盖只需在
     .timeline 或更外层重新赋值即可，无需改本文件的规则。 */
  --tl-sticky-top: 56px;
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  box-sizing: border-box;
  width: auto;
  max-width: none;
  margin-inline: calc(-1 * var(--sp-4));
  padding-inline: calc(var(--sp-4) + var(--safe-left)) calc(var(--sp-4) + var(--safe-right));
}

/* 分组标题本身就是 <h2 class="tl-group">，与 .tl-item 平级排列（便于 sticky） */
.tl-group {
  position: sticky;
  top: calc(var(--tl-sticky-top, 56px) + var(--safe-top));
  z-index: var(--z-content);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: baseline;
  margin: 0;
  padding-block: var(--sp-2);
  /* 用纯色底 + 渐隐托住标题，滚动时始终可读。
     注意：必须用纯色变量 --bg-app-solid；
     --bg-app 是 radial-gradient()，不能当 linear-gradient 的色标使用，
     否则整条 background 声明会被浏览器判定非法而丢弃（吸顶时会与卡片文字叠字）。 */
  background-image: linear-gradient(
    to bottom,
    var(--bg-app-solid) 0%,
    color-mix(in srgb, var(--bg-app-solid) 88%, transparent) 62%,
    transparent 100%
  );
}

.tl-group__label {
  font-family: var(--ff-display);
  font-size: var(--fs-xl);
  font-weight: 600;
  letter-spacing: -0.005em;
  line-height: 1.2;
  color: var(--c-rose-600);
}

.tl-group__count {
  margin-left: auto;
  font-family: var(--ff-num);
  font-size: var(--fs-2xs);
  font-variant-numeric: tabular-nums;
  color: var(--c-ink-300);
}

/* 标题下方的柔和渐变下划线 */
.tl-group::after {
  content: "";
  flex: 1 1 100%;
  order: 9;
  height: 1px;
  margin-top: var(--sp-1);
  background-image: linear-gradient(
    to right,
    var(--c-rose-300),
    color-mix(in srgb, var(--c-lilac-200) 70%, transparent) 45%,
    transparent
  );
}

.tl-item {
  position: relative;
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  /* 左侧为轨道留出空间 */
  padding-left: 34px;
}

/* 贯穿竖线：上下端渐隐，绝不用纯色实线（质感关键） */
.tl-item__line {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 10px;
  width: 2px;
  border-radius: var(--r-full);
  background-image: linear-gradient(
    to bottom,
    transparent 0%,
    color-mix(in srgb, var(--c-lilac-300) 70%, transparent) 16%,
    color-mix(in srgb, var(--c-rose-300) 70%, transparent) 50%,
    color-mix(in srgb, var(--c-lilac-300) 70%, transparent) 84%,
    transparent 100%
  );
  opacity: 0.65;
  transition: opacity var(--dur-slow) var(--ease-out-soft);
}

.tl-item__dot {
  position: absolute;
  top: 22px;
  left: 5px;
  z-index: 1;
  display: block;
  box-sizing: border-box;
  width: 12px;
  height: 12px;
  border: 2px solid var(--c-cream-50);
  border-radius: var(--r-full);
  background-image: var(--grad-btn);
  box-shadow:
    0 0 0 4px color-mix(in srgb, var(--c-rose-300) 22%, transparent),
    0 4px 12px color-mix(in srgb, var(--c-rose-400) 45%, transparent);
  /* 滚动到视口前：节点是「未点亮」的 */
  opacity: 0.35;
  transform: scale(0.85);
  transition:
    opacity var(--dur-slow) var(--ease-out-soft),
    transform var(--dur-slow) var(--ease-spring),
    box-shadow var(--dur-slow) var(--ease-out-soft);
}

/* timeline.js 的 IntersectionObserver 添加 .is-visible → 节点与竖线「点亮」 */
.tl-item.is-visible .tl-item__dot {
  opacity: 1;
  transform: scale(1);
}

.tl-item.is-visible .tl-item__line {
  opacity: 1;
}

/* 置顶记录：节点换成玫瑰金，与普通节点区分 */
.tl-item--pinned .tl-item__dot {
  background-image: linear-gradient(135deg, var(--c-gold-300), var(--c-rose-400));
  box-shadow:
    0 0 0 4px color-mix(in srgb, var(--c-gold-300) 26%, transparent),
    0 4px 14px color-mix(in srgb, var(--c-gold-500) 45%, transparent);
}

.tl-card {
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  padding: var(--sp-5);
  border-radius: var(--r-lg);
  /* 只补 transform 过渡；背景/描边/阴影的过渡由 glass.css 的 .glass 负责 */
  transition: transform var(--dur-base) var(--ease-out-soft);
}

.tl-card:active {
  transform: scale(0.995);
}

.tl-card__head {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: center;
  margin-bottom: var(--sp-3);
}

.tl-card__date {
  font-family: var(--ff-num);
  font-size: var(--fs-2xs);
  letter-spacing: 0.06em;
  color: var(--c-ink-300);
}

/* 徽章：与 glass.css 的 .badge 同款观感，但类名由 timeline.js 固定 */
.tl-card__badge {
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  padding: calc(var(--sp-1) * 0.5) var(--sp-2);
  border: 1px solid color-mix(in srgb, var(--c-rose-400) 60%, transparent);
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--c-rose-100) 70%, transparent);
  color: var(--c-rose-600);
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: 0.06em;
  white-space: nowrap;
}

.tl-card__title {
  flex: 1 1 100%;
  margin: var(--sp-1) 0 0;
  font-family: var(--ff-display);
  font-size: var(--fs-xl);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: -0.005em;
  color: var(--c-ink-900);
  text-wrap: pretty;
}

.tl-card__text {
  margin: var(--sp-3) 0 0;
  font-family: var(--ff-body);
  font-size: 0.9375rem;
  line-height: var(--lh-loose);
  color: var(--c-ink-700);
  text-wrap: pretty;
}

.tl-card__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin: var(--sp-3) 0 0;
  padding: 0;
  list-style: none;
}

/* 标签胶囊外观由 glass.css 的 .tag 提供（designer 独占）。
   timeline.js 严格输出 li.tag 本身（可点击时加 role="button" + tabindex），
   这里只保证换行排布与可点击时的触摸热区。 */
.tl-card__tags .tag {
  flex: 0 0 auto;
}

/* 可点击的标签：撑到 44px 热区，并给出按下反馈 */
.tag[role="button"] {
  display: inline-flex;
  align-items: center;
  box-sizing: border-box;
  min-height: var(--tap-min);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-fast) var(--ease-out-soft);
}

.tag[role="button"]:active {
  background: color-mix(in srgb, var(--c-rose-200) 70%, transparent);
}

/* ---------- 6. 媒体网格与媒体卡 ---------- */

.tl-card__media {
  display: grid;
  gap: var(--sp-2);
  margin-top: var(--sp-4);
}

.tl-card__media--single {
  grid-template-columns: minmax(0, 1fr);
}

.tl-card__media--duo {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  grid-auto-rows: 1fr;
}

.tl-card__media--grid {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-auto-rows: 1fr;
}

/* 每张图的宽高比由 timeline.js 写成行内 aspect-ratio，这里不再覆盖，
   只负责裁切与圆角，保证「加载前后不跳版」的既有约定不被破坏。
   注意：下图对多图布局显式钉死 width/height，原因见那段注释。 */
.tl-media {
  position: relative;
  display: block;
  box-sizing: border-box;
  overflow: hidden;
  margin: 0;
  /* 尺寸容器：让内部提示胶囊能按「卡片自身宽度」自适应（见 .tl-media__missing-hint） */
  container-type: inline-size;
  border-radius: var(--r-md);
  background: color-mix(in srgb, var(--c-rose-50) 70%, transparent);
  /* 内描边高光 */
  box-shadow:
    inset 0 0 0 1px var(--glass-inner-glow),
    inset 0 0 0 1px var(--glass-border);
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-base) var(--ease-spring);
}

/* ── 多图布局的尺寸归一（T6 修复）──────────────────────────────
   根因：CSS 规范规定「元素带有 aspect-ratio 时，align-self: stretch 不再生效」
   （auto 不再是 stretch）。timeline.js 会给每张图写行内 aspect-ratio，于是同一行里
   4/3 的格子坚持按自身比例算高、3/4 的格子按自己算高，浏览器实测出现同一行
   91.5px 与 162.7px 并存（最大差 94px）—— 也就是「高度参差不齐」。
   这里把两个轴都钉成确定值（宽度取列宽、高度取行高），aspect-ratio 因两轴均已
   确定而不再参与计算，格子遂能随行等高；配合 grid-auto-rows: 1fr 让每行等高。
   不能改用 aspect-ratio: 1/1 之类的统一比例：行内声明的优先级高于样式表，
   只有 width/height 这类不同属性才能真正压过它（不用 !important）。 */
.tl-card__media--duo > .tl-media,
.tl-card__media--grid > .tl-media {
  width: 100%;
  height: 100%;
  min-width: 0;
}

.tl-media:active {
  transform: scale(0.972);
}

.tl-media__img,
.tl-media__video {
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  border-radius: inherit;
  object-fit: cover;
}

.tl-card__media--single > .tl-media {
  border-radius: var(--r-lg);
}

/* 交错圆角：网格里第 2、5、8… 个换一种圆角，避免呆板 */
.tl-card__media--grid > .tl-media:nth-child(3n + 2),
.tl-card__media--duo > .tl-media:nth-child(odd) {
  border-radius: var(--r-lg);
}

/* 视频播放三角：居中玻璃圆 + 极缓呼吸 */
.tl-media__play {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  display: block;
  box-sizing: border-box;
  width: var(--tap-min);
  height: var(--tap-min);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--c-ink-900) 42%, transparent);
  backdrop-filter: blur(var(--blur-sm));
  -webkit-backdrop-filter: blur(var(--blur-sm));
  /* rgba 白是深色遮罩上的 1px 上缘高光，令牌里没有对应深色玻璃高光，故保留字面量 */
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.24),
    0 6px 18px color-mix(in srgb, var(--c-ink-900) 30%, transparent);
  transform: translate(-50%, -50%);
  pointer-events: none;
  animation: play-breathe 3.4s var(--ease-in-out-soft) infinite;
}

/* 三角形用 border 技巧画，不依赖图标字体 */
.tl-media__play::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 54%;
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  border-left: 11px solid var(--c-cream-50);
  transform: translate(-50%, -50%);
}

@keyframes play-breathe {
  0%,
  100% {
    transform: translate(-50%, -50%) scale(1);
  }
  50% {
    transform: translate(-50%, -50%) scale(1.06);
  }
}

/* 说明文字常显（移动端不依赖 hover），做底部渐变蒙层 */
.tl-media__cap {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  z-index: 1;
  box-sizing: border-box;
  display: -webkit-box;
  margin: 0;
  padding: calc(var(--sp-5) + var(--sp-2)) var(--sp-2) var(--sp-2);
  overflow: hidden;
  /* 图注较长时最多 2 行，超出省略。
     -webkit-box 与 -webkit-line-clamp 属于同一套机制，写成 display:-webkit-box
     后 Chrome 会忽略标准 line-clamp，故这里保留 -webkit- 前缀（iOS Safari 同样支持）。 */
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  background: linear-gradient(
    to top,
    color-mix(in srgb, var(--c-ink-900) 62%, transparent) 0%,
    color-mix(in srgb, var(--c-ink-900) 34%, transparent) 46%,
    transparent 100%
  );
  color: var(--c-cream-50);
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
  line-height: var(--lh-normal);
  text-align: left;
  pointer-events: none;
}

/* 素材待补：timeline.js 会同时给 figure 加 .tl-media--missing，
   并在 figure 内插入一个 div.tl-media__missing 承载占位图与提示。
   两层都做样式，保证「不空白、不破图、依然好看」。

   关于实测到的 border-top-width: 0px —— 这是有意的：普通 .tl-media 的「描边」
   统一由 box-shadow 的两层 1px inset 内发光承担（不占盒模型、不会把内容推挤位移）。
   占位卡额外要一条更明确的「这是待填格子」的金色细线，但这里用 outline 而不是
   border：border 会压缩内容盒，导致内部 img 比容器小一圈、出现约 5% 的空余面积
   （实测 347x195 的卡会掉到 5.02%），而 outline + 负 offset 是纯绘制的，
   视觉上是贴着边缘的 1px 线、布局上完全不占位。 */
.tl-media--missing {
  outline: 1px solid color-mix(in srgb, var(--c-gold-500) 78%, transparent);
  outline-offset: -1px;
  background-image: linear-gradient(
    135deg,
    color-mix(in srgb, var(--c-rose-200) 78%, transparent),
    color-mix(in srgb, var(--c-lilac-200) 70%, transparent) 55%,
    color-mix(in srgb, var(--c-cream-100) 80%, transparent)
  );
}

.tl-media__missing {
  position: absolute;
  inset: 0;
  z-index: 1;
  display: block;
  overflow: hidden;
  border-radius: inherit;
}

/* 斜纹质感层，让占位卡不像「加载失败」而像「待补的卡片」 */
.tl-media__missing::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image: repeating-linear-gradient(
    135deg,
    transparent 0 10px,
    color-mix(in srgb, var(--c-cream-50) 34%, transparent) 10px 11px
  );
  pointer-events: none;
}

.tl-media__missing .tl-media__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.9;
}

/* 提示文字：底部居中的玻璃小胶囊，任何尺寸下都读得清。
   注意三栏网格里格子只有 ~79px 宽，整句提示会折成 5 行、纵向顶出卡片
   （实测溢出 19.3px）。所以这里固定最多 2 行并省略号收尾，
   再用容器查询在窄格里进一步收紧字号与内边距 —— 保证「永远不会顶破格子」。 */
.tl-media__missing-hint {
  position: absolute;
  right: var(--sp-1);
  bottom: var(--sp-1);
  left: var(--sp-1);
  z-index: 2;
  display: -webkit-box;
  box-sizing: border-box;
  max-height: calc(100% - var(--sp-2));
  overflow: hidden;
  padding: var(--sp-1) var(--sp-2);
  border: 1px solid var(--glass-border);
  border-radius: var(--r-full);
  background: var(--glass-bg-strong);
  backdrop-filter: blur(var(--blur-sm));
  -webkit-backdrop-filter: blur(var(--blur-sm));
  color: var(--c-ink-700);
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
  line-height: var(--lh-normal);
  text-align: center;
  overflow-wrap: break-word;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
}

/* 窄格子（三栏网格 / 窄屏双栏）：字号与内边距再收一档，并只留 1 行 */
@container (max-width: 150px) {
  .tl-media__missing-hint {
    padding: 2px var(--sp-1);
    border-radius: var(--r-sm);
    -webkit-line-clamp: 1;
    line-clamp: 1;
  }
}

/* ---------- 7. 空状态与页脚 ---------- */

/* 空态由 app.js 直接写 textContent（无子元素），
   所以这里只做容器排版，并补一个装饰性心形 */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-3);
  box-sizing: border-box;
  min-height: 30vh;
  padding: var(--sp-8) var(--sp-5);
  color: var(--c-ink-500);
  font-family: var(--ff-body);
  font-size: var(--fs-sm);
  line-height: var(--lh-loose);
  text-align: center;
  text-wrap: pretty;
}

.empty-state::before {
  content: "♡";
  font-family: var(--ff-display);
  font-size: var(--fs-2xl);
  line-height: 1;
  color: color-mix(in srgb, var(--c-rose-400) 70%, transparent);
}

/* 页脚：render 层存在两种形态，都要覆盖
   · 扁平：.site-footer 直接装 __note / __stats / __meta
   · 嵌套：.site-footer > .footer__inner.glass > (__note / __stats / __since / __links / __sign)
   用 gap 统一间距，两种形态视觉一致。 */
.site-footer {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-5);
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  margin-top: var(--sp-4);
  padding: var(--sp-7) var(--sp-4) var(--sp-6);
  text-align: center;
}

.footer__inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-5);
  box-sizing: border-box;
  width: 100%;
  max-width: 100%;
  padding: var(--sp-6) var(--sp-5);
  border-radius: var(--r-xl);
}

.footer__note {
  max-width: 26em;
  margin: 0;
  font-family: var(--ff-body);
  font-size: var(--fs-sm);
  line-height: var(--lh-loose);
  color: var(--c-ink-500);
}

.footer__stats {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4) var(--sp-6);
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

.footer__stat {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-1);
  min-width: 4.5em;
}

/* 统计数字：两种命名都覆盖（__stat-value 与 __stat-num） */
.footer__stat-value,
.footer__stat-num {
  font-family: var(--ff-num);
  font-size: var(--fs-2xl);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
  color: var(--c-rose-600);
}

@supports (-webkit-background-clip: text) or (background-clip: text) {
  .footer__stat-value,
  .footer__stat-num {
    background-image: var(--grad-text);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
  }
}

.footer__stat-label {
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
  letter-spacing: 0.12em;
  color: var(--c-ink-300);
}

/* 起点信息（单独成行或与其他信息合并成一行，都能正常排布） */
.footer__since,
.footer__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
  align-items: baseline;
  justify-content: center;
  max-width: 100%;
  margin: 0;
  font-family: var(--ff-num);
  font-size: var(--fs-2xs);
  letter-spacing: 0.04em;
  color: var(--c-ink-300);
}

.footer__since-value {
  color: var(--c-ink-500);
}

.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: center;
  justify-content: center;
  margin: 0;
  padding: 0;
  list-style: none;
}

.footer__link {
  color: var(--c-rose-600);
  font-family: var(--ff-body);
  font-size: var(--fs-2xs);
}

.footer__sign {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  align-items: center;
  justify-content: center;
  margin: 0;
  font-family: var(--ff-body);
  font-size: var(--fs-xs);
  color: var(--c-ink-500);
}

.footer__sign-heart {
  line-height: 1;
}

/* ---------- 8. 浮动音乐按钮 ---------- */

.music-fab {
  position: fixed;
  right: calc(var(--sp-4) + var(--safe-right));
  bottom: calc(var(--sp-5) + var(--safe-bottom));
  z-index: var(--z-music);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 1px solid var(--glass-border);
  border-radius: var(--r-full);
  background: var(--glass-bg-strong);
  backdrop-filter: blur(var(--blur-md));
  -webkit-backdrop-filter: blur(var(--blur-md));
  box-shadow:
    inset 0 1px 0 var(--glass-inner-glow),
    0 10px 26px color-mix(in srgb, var(--c-rose-400) 34%, transparent);
  -webkit-tap-highlight-color: transparent;
  transition: transform var(--dur-base) var(--ease-spring);
}

.music-fab:active {
  transform: scale(0.94);
}

/* 音符 / 暂停图标：由 music.js 写入文字（♪ / ❚❚），不依赖图标字体 */
.music-fab__icon {
  font-family: var(--ff-display);
  font-size: var(--fs-lg);
  line-height: 1;
  color: var(--c-rose-500);
}

/* 播放时的三根律动竖条 */
.music-fab__waves {
  position: absolute;
  right: 0;
  bottom: 6px;
  left: 0;
  display: flex;
  gap: 2px;
  align-items: flex-end;
  justify-content: center;
  height: 12px;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out-soft);
}

.music-fab__wave {
  width: 2px;
  height: 100%;
  border-radius: var(--r-full);
  background-image: var(--grad-btn);
  transform: scaleY(0.35);
  transform-origin: bottom center;
}

.music-fab.is-playing .music-fab__waves {
  opacity: 1;
}

/* 每根竖条错开相位，形成「随节奏起伏」的观感 */
.music-fab.is-playing .music-fab__wave {
  animation: fab-wave 900ms var(--ease-in-out-soft) infinite;
}

.music-fab.is-playing .music-fab__wave:nth-child(2) {
  animation-delay: 150ms;
}

.music-fab.is-playing .music-fab__wave:nth-child(3) {
  animation-delay: 300ms;
}

.music-fab.is-playing .music-fab__icon {
  animation: fab-breathe 2.4s var(--ease-in-out-soft) infinite;
}

@keyframes fab-wave {
  0%,
  100% {
    transform: scaleY(0.35);
  }
  50% {
    transform: scaleY(1);
  }
}

@keyframes fab-breathe {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.1);
  }
}

/* 音乐素材待补：按钮保留但降低存在感，依然可聚焦/可读 */
.music-fab.is-unavailable {
  opacity: 0.55;
}

.music-fab.is-unavailable .music-fab__icon {
  color: var(--c-ink-300);
}

/* ---------- 9. PC 端增强（≥900px） ---------- */

@media (min-width: 900px) {
  .stage {
    gap: var(--sp-8);
    padding: var(--sp-7) var(--sp-6) var(--sp-10);
  }

  .hero {
    min-height: 78vh;
    padding: calc(var(--sp-8) + var(--safe-top)) var(--sp-6) var(--sp-8);
  }

  .hero__inner {
    max-width: 720px;
    padding: var(--sp-7) var(--sp-6);
  }

  .hero__avatars .avatar {
    width: 76px;
    height: 76px;
  }

  /* 两个头像由中间的「粉线 + 爱心」隔开，不用负边距重叠。
     原先这里写的 -18px 会把爱心宽度吃掉一大半，导致头像挤在一起，现移除。 */
  .hero__avatars .avatar + .avatar {
    margin-left: 0;
  }

  .timer__grid {
    gap: var(--sp-3);
  }

  .timer__unit {
    padding: var(--sp-4) var(--sp-2);
    border-radius: var(--r-lg);
  }

  /* 居中收窄到约 720px */
  .toolbar,
  .timeline,
  .site-footer {
    width: 100%;
    max-width: 720px;
    margin-inline: auto;
  }

  /* PC 端 chips 不再横向滚动，让它们自己换行居中 */
  .toolbar__row {
    align-items: center;
  }

  .toolbar__label {
    padding-inline: 0;
  }

  .toolbar__chips,
  .toolbar:not(:has(.toolbar__chips)) {
    flex-wrap: wrap;
    justify-content: center;
    padding-inline: 0;
    overflow-x: visible;
  }

  .tl-item {
    padding-left: 44px;
  }

  .tl-item__line {
    left: 15px;
  }

  .tl-item__dot {
    top: 26px;
    left: 10px;
  }

  .tl-card {
    padding: var(--sp-6);
  }

  .music-fab {
    right: var(--sp-6);
    bottom: var(--sp-6);
    width: 60px;
    height: 60px;
  }
}

/* PC 端加分项：中轴双列「副时间轴」。
   用 :nth-child(n of .tl-item) 只数条目本身，这样即使 .timeline 里混入
   .tl-group 标题（真实 DOM 就是平级混排），左右交错依然正确；
   不支持该语法的浏览器自动使用上面的左侧单轨回退。 */
@supports selector(.tl-item:nth-child(1 of .tl-item)) {
  @media (min-width: 900px) {
    .timeline {
      position: relative;
    }

    /* 中轴轨道：与移动端一样上下端渐隐，不做生硬实线 */
    .timeline::before {
      content: "";
      position: absolute;
      top: 0;
      bottom: 0;
      left: 50%;
      width: 2px;
      border-radius: var(--r-full);
      background-image: linear-gradient(
        to bottom,
        transparent 0%,
        color-mix(in srgb, var(--c-lilac-300) 65%, transparent) 8%,
        color-mix(in srgb, var(--c-rose-300) 65%, transparent) 50%,
        color-mix(in srgb, var(--c-lilac-300) 65%, transparent) 92%,
        transparent 100%
      );
      opacity: 0.7;
      transform: translateX(-50%);
      pointer-events: none;
    }

    /* 中轴已由 .timeline::before 统一绘制，单条自带的竖线隐藏 */
    .tl-item__line {
      display: none;
    }

    .tl-item {
      /* 卡片外缘到中轴的留白；--tl-node-gap 是本文件的局部变量，不是设计令牌 */
      --tl-node-gap: var(--sp-5);
      box-sizing: border-box;
      width: calc(50% - var(--tl-node-gap));
      padding-right: 0;
      padding-left: 0;
    }

    .tl-item:nth-child(odd of .tl-item) {
      align-self: flex-start;
      margin-right: auto;
      margin-left: 0;
    }

    /* 6px 是节点半径：让圆点圆心正好落在中轴线上 */
    .tl-item:nth-child(odd of .tl-item) .tl-item__dot {
      right: auto;
      left: calc(100% + var(--tl-node-gap) - 6px);
    }

    .tl-item:nth-child(even of .tl-item) {
      align-self: flex-end;
      margin-right: 0;
      margin-left: auto;
    }

    .tl-item:nth-child(even of .tl-item) .tl-item__dot {
      right: calc(100% + var(--tl-node-gap) - 6px);
      left: auto;
    }
  }
}

/* 超宽屏：整站维持窄栏，避免内容被拉散 */
@media (min-width: 1280px) {
  .hero__inner {
    max-width: 760px;
  }
}

/* 极小屏（<360px）：时间轴轨道再收一点，保证文字区宽度 */
@media (max-width: 359px) {
  .stage {
    padding-inline: var(--sp-3);
  }

  /* .stage 横向内边距变小后，负外边距与内 padding 都要跟着变，否则会横向溢出 */
  .toolbar {
    margin-inline: calc(-1 * var(--sp-3));
  }

  .toolbar__label,
  .toolbar__chips,
  .toolbar:not(:has(.toolbar__chips)),
  .timeline {
    padding-inline: calc(var(--sp-3) + var(--safe-left)) calc(var(--sp-3) + var(--safe-right));
  }

  .timeline {
    margin-inline: calc(-1 * var(--sp-3));
  }

  .tl-item {
    padding-left: 28px;
  }

  .tl-item__line {
    left: 7px;
  }

  .tl-item__dot {
    left: 2px;
  }

  .tl-card {
    padding: var(--sp-4);
  }

  .tl-card__media--grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* 横屏且矮：hero 不再强撑首屏 */
@media (orientation: landscape) and (max-height: 520px) {
  .hero {
    min-height: auto;
  }

  .empty-state {
    min-height: 24vh;
  }
}

/* ---------- 10. 降低动效偏好 ---------- */

@media (prefers-reduced-motion: reduce) {
  /* tokens.css/base.css 已把时长压到约 0ms，这里额外关掉本文件里的循环动画，
     避免「0ms 时长 + infinite」造成高频闪烁或持续重绘 */
  .hero__amp,
  .timer__num--pulse,
  .timer__num.is-beat,
  .tl-media__play,
  .music-fab.is-playing .music-fab__wave,
  .music-fab.is-playing .music-fab__icon {
    animation: none;
  }

  /* 脉冲动画关掉后把光晕一并归零，避免残留一层恒定发光 */
  .timer__num--pulse,
  .timer__num.is-beat {
    filter: none;
  }

  /* 关闭动画后把状态直接落到终态，保证视觉完整、不残留半透明 */
  .tl-item__dot {
    opacity: 1;
    transform: none;
  }

  .tl-item__line {
    opacity: 1;
  }

  .tl-card,
  .tl-media,
  .tl-media__play,
  .music-fab,
  .theme-toggle__icon {
    transition: none;
  }
}
