/**
 * 生日支线样式。沿用 style.css 的 CSS 变量（深空 + 金色），不新造一套配色。
 *
 * 性能约束（docs/任务系统.md 第九节实测）：
 *   - 不用全屏 backdrop-filter
 *   - 动效只用 opacity / transform
 *   - 不给大量元素各挂动画层
 */

/* ---- 封面入口 ---- */
/* 视觉要压过原按钮：冷启动浏览器禁止自动出声，birthday.m4a 要等她第一次触屏才响，
   所以「今天不一样」只能靠视觉先说出来，不能指望歌先响。 */
.btn-birthday {
    margin-top: 1rem;
    padding: 0.9rem 2.2rem;
    font-size: 1.05rem;
    font-weight: 600;
    color: #1a1206;
    background: linear-gradient(135deg, var(--gold-light), var(--gold));
    border: none;
    border-radius: 999px;
    cursor: pointer;
    box-shadow: 0 0 24px rgba(232, 168, 56, 0.45);
    animation: bdayBreathe 2.6s ease-in-out infinite;
}

@keyframes bdayBreathe {
    0%, 100% { transform: scale(1);    box-shadow: 0 0 22px rgba(232, 168, 56, 0.40); }
    50%      { transform: scale(1.04); box-shadow: 0 0 34px rgba(232, 168, 56, 0.65); }
}

.bday-countdown {
    margin-top: 1.2rem;
    font-size: 0.85rem;
    color: var(--gold-dark);
    letter-spacing: 0.08em;
}

/* 常驻入口：低调，不抢主线的镜 */
.btn-bday-quiet {
    position: absolute;
    right: 0;
    bottom: -2.6rem;
    width: 2rem;
    height: 2rem;
    font-size: 0.95rem;
    line-height: 1;
    color: var(--gold-dark);
    background: transparent;
    border: 1px solid rgba(232, 168, 56, 0.25);
    border-radius: 50%;
    opacity: 0.5;
    cursor: pointer;
}
.btn-bday-quiet:active { opacity: 1; }

/* 图标换成阿 mo 本人（用户第 6 条）。原来是 🗄 emoji——它是这条支线的主角，
   一眼认得出点进去是什么，而 emoji 在不同系统上还长得不一样。
   ⚠️ 按钮是 2rem 的圆，抠图件按 contain 缩进去、留一点内边距，
   别用 cover：那会把它的头或脚裁掉。 */
.btn-bday-quiet img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    padding: 2px;
    box-sizing: border-box;
    /* 深空底上的小图标给一点暖光，跟金色描边的圆呼应 */
    filter: drop-shadow(0 0 3px rgba(232, 168, 56, 0.35));
}

/* 车库派对 → 回首页（用户第 4 条）。
   ⚠️ 左上角，不能放右上：那儿被 .audio-toggle（fixed, z-index 100）占着，
   叠上去 elementFromPoint 拿到的是音乐按钮、而它的 click 里有 stopPropagation
   ——贺卡那个 ✕ 已经因此永远关不掉一次了。
   z-index 3 跟进度小字同层、低于 .bday-layer（10）：弹层开着时它被盖住是**对的**，
   那会儿该点的是 ✕ 先回房间。 */
.bday-room-home {
    position: absolute;
    left: 1rem;
    top: 1rem;
    z-index: 3;
    /* 2.6rem = 41.6px，跟弹层那个 ✕ 一样大。2.2rem（35px）手指点着偏小，
       而这一屏她是站在三屏宽的场景里滑来滑去的，按钮小了容易点空 */
    width: 2.6rem;
    height: 2.6rem;
    font-size: 1rem;
    line-height: 1;
    color: rgba(232, 168, 56, 0.7);
    background: rgba(10, 14, 26, 0.55);
    border: 1px solid rgba(232, 168, 56, 0.28);
    border-radius: 50%;
    cursor: pointer;
    /* 别抢镜：这一屏的主角是她要逛的六个热点 */
    opacity: 0.62;
}
.bday-room-home:active { opacity: 1; }

.bday-badge {
    display: inline-block;
    margin-top: 0.8rem;
    padding: 0.25rem 0.8rem;
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    color: var(--gold);
    border: 1px solid rgba(232, 168, 56, 0.4);
    border-radius: 999px;
}

/* 扫雷页那一枚：挤在 header 一排按钮里，去掉 margin、缩小一档，
   别把「返回」和右边那几个功能键顶出去 */
.bday-badge-ms {
    margin-top: 0;
    margin-left: 0.4rem;
    padding: 0.15rem 0.5rem;
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    white-space: nowrap;
    align-self: center;
}

/* ---- 集满反馈：架子上留给明年的空位 (T7.6) ---- */
.bday-card-slot {
    width: 4.5rem;
    height: 6rem;
    border: 1px dashed rgba(232, 168, 56, 0.35);
    border-radius: 6px;
    background: rgba(232, 168, 56, 0.04);
}

.bday-card-slot-note {
    align-self: center;
    margin-left: 0.7rem;
    font-size: 0.75rem;
    color: rgba(232, 168, 56, 0.75);
}

/* 集满那一句。只用 opacity + transform，不加 filter/阴影动画——
   房间关有 canvas 在跑，别再给合成器添活儿（性能红线见任务系统文档第九节） */
.bday-all-seen {
    position: absolute;
    left: 50%;
    bottom: 12%;
    transform: translate(-50%, 8px);
    padding: 0.5rem 1.1rem;
    font-size: 0.85rem;
    color: var(--gold);
    background: rgba(10, 14, 26, 0.82);
    border: 1px solid rgba(232, 168, 56, 0.3);
    border-radius: 999px;
    opacity: 0;
    transition: opacity 0.6s ease, transform 0.6s ease;
    pointer-events: none;
    z-index: 6;
}

.bday-all-seen.show {
    opacity: 1;
    transform: translate(-50%, 0);
}

/* ---- 番外解密关 ---- */
/* 单独一层 canvas 画「9.16」的聚散，不动现有 100 个 .star DOM 元素 */
.bday-gate-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 0;
}
/* 星点聚成「9.16」的那一下必须盖在密码框**上面**。
   「9.16」是照 textPoints() 画在**画面正中**的，而正中恰好是那张玻璃卡片——
   z-index 0 时整个字被卡片挡住，她只看到密码框、看不到聚字（用户报的就是这个）。
   聚字期间才抬层：平时那层是背景里的散星，压在内容下才对。
   pointer-events 一直是 none，抬上来也不吃点击。 */
.bday-gate-canvas.converging { z-index: 3; }
/* 同时把卡片让开。只抬 canvas 也能看见字，但金色星点压在磨砂玻璃上
   （背景更亮、还带 backdrop-filter 模糊）对比会掉一大截；淡出之后
   星点落在深空底上，才是「星空聚成了你的生日」。
   卡片留 0.12 而不是 0：整块凭空消失像 bug，留一点余影看得出它还在原地。 */
#slide-bday-gate .slide-content {
    transition: opacity 0.7s ease;
}
#slide-bday-gate.gate-passed .slide-content {
    opacity: 0.12;
    pointer-events: none;
}
#slide-bday-gate .slide-content { position: relative; z-index: 1; }

/* ---- 阿 mo 关 ---- */
#slide-bday-mo, #slide-bday-room {
    /* 两关都是整屏 canvas，不用 slide 默认的居中盒子 */
    padding: 0;
    display: block;
}

.bday-mo-canvas, .bday-room-canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    /* 相机靠 Pointer Events 拖，必须掐掉浏览器自己的滚动/缩放手势 */
    touch-action: none;
}

.bday-mo-ui {
    position: absolute;
    inset: auto 0 0 0;
    padding: 0 1.2rem 2.2rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1rem;
    pointer-events: none;       /* 默认穿透，让画布收到拖拽 */
    z-index: 2;
}
.bday-mo-ui button { pointer-events: auto; }

.bday-hint {
    max-width: 18rem;
    font-size: 0.9rem;
    line-height: 1.7;
    color: var(--eve-blue);
    text-align: center;
    white-space: pre-line;      /* 文案里用 \n 换行 */
    text-shadow: 0 0 12px rgba(10, 14, 26, 0.9);
    opacity: 0;
    transform: translateY(6px);
    transition: opacity 0.4s ease, transform 0.4s ease;
}
.bday-hint.show { opacity: 1; transform: translateY(0); }

/* 阿 mo 的心里旁白。⚠️ **故意一条视觉都不覆盖**：用户要的是「和之前提示信息的
   展示形式相同」，所以它就是一个 .bday-hint，这个 class 只当选择器钩子用
   （测试要分得清底部这两个 <p> 谁是谁）。想加斜体/变暗之类的先回去看那句要求。 */
.bday-narr { }

/* ---- 许愿 ---- */
.bday-wish {
    width: 100%;
    max-width: 20rem;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.9rem;
    padding: 1.2rem 1rem;
    background: rgba(21, 27, 46, 0.82);
    border: 1px solid rgba(232, 168, 56, 0.22);
    border-radius: 1rem;
    pointer-events: auto;
    opacity: 0;
    transform: translateY(10px);
    transition: opacity 0.32s ease, transform 0.32s ease;
}
.bday-wish.show { opacity: 1; transform: translateY(0); }

.bday-wish-text {
    font-size: 0.9rem;
    line-height: 1.7;
    color: var(--gold-light);
    text-align: center;
    white-space: pre-line;
}

.bday-wish-controls {
    display: flex;
    align-items: center;
    gap: 0.8rem;
}

.btn-wish {
    padding: 0.7rem 1.6rem;
    font-size: 0.95rem;
    color: #1a1206;
    background: linear-gradient(135deg, var(--gold-light), var(--gold));
    border: none;
    border-radius: 999px;
    cursor: pointer;
}

.bday-wish-timer {
    font-size: 0.85rem;
    font-variant-numeric: tabular-nums;
    color: var(--eve-blue);
}
/* 快到 60 秒上限时变色，别让她说到一半被切断 */
.bday-wish-timer.warn { color: #e07a5f; }

.bday-wish-after {
    display: flex;
    gap: 0.6rem;
}

.btn-wish-sm {
    padding: 0.45rem 0.9rem;
    font-size: 0.8rem;
    color: var(--gold-light);
    background: transparent;
    border: 1px solid rgba(232, 168, 56, 0.35);
    border-radius: 999px;
    cursor: pointer;
}
.btn-wish-sm.primary {
    color: #1a1206;
    background: var(--gold);
    border-color: var(--gold);
}

/* 「先不说」要一直在，跳过不能有代价 */
.btn-wish-skip {
    font-size: 0.75rem;
    color: rgba(168, 216, 234, 0.6);
    background: none;
    border: none;
    text-decoration: underline;
    cursor: pointer;
}

/* ⚠️ 这儿原来是 .bday-burst：全屏金光 clip-path 炸开，蛋糕 → 车库的过场。
   已删（用户评「很割裂不自然」）。现在走画面接力：车库关在蛋糕的**同一批像素**上
   接上自己那张 cake-side.png，再从蛋糕处扩开整间车库。
   实现全在 canvas 里（birthday-room.js 的 drawEnter），不需要 DOM 层。 */

/* ---- 房间 ---- */
.bday-room-progress {
    position: absolute;
    right: 1rem;
    top: 1rem;
    font-size: 0.72rem;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.06em;
    color: rgba(232, 168, 56, 0.55);   /* 角落常驻小字，克制不抢镜 */
    z-index: 3;
    pointer-events: none;
}

/* 内容弹层：覆盖在房间之上，不切 slide，保持「还在房间里」的感觉 */
.bday-layer {
    position: absolute;
    inset: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1.2rem;
    background: rgba(6, 10, 20, 0.86);   /* 用半透明底色，不用 backdrop-filter */
    opacity: 0;
    transition: opacity 0.3s ease;
}
.bday-layer.show { opacity: 1; }

.bday-layer-inner {
    width: 100%;
    max-width: 22rem;
    max-height: 82vh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
}

/**
 * 关闭按钮放**左上角**，不能放右上角。
 * 右上角被 .audio-toggle 占着（position:fixed，z-index:100，36×36，
 * top/right 都是 16px）——这个 ✕ 原来是 absolute + z-index:auto，
 * 两者像素级重叠，`elementFromPoint` 拿到的是 audio-toggle，
 * 而它的 click 里有 stopPropagation，所以点 ✕ 只会切音乐、永远关不掉这一层。
 * 表现就是用户报的「贺卡的热点点开后无法返回」。
 * 移到左上角之外还要给 z-index：这一层本身 z-index:10，
 * 不显式抬高的话，将来任何 fixed 元素都可能再压上来。
 */
.bday-layer-close {
    position: absolute;
    left: 1rem;
    top: 1rem;
    z-index: 120;
    width: 2.6rem;
    height: 2.6rem;
    font-size: 1.05rem;
    color: var(--gold-light);
    background: rgba(21, 27, 46, 0.92);
    border: 1px solid rgba(232, 168, 56, 0.34);
    border-radius: 50%;
    cursor: pointer;
}

/* 童乐独占一屏：不与其它内容抢镜 */
.bday-layer-full .bday-layer-inner { max-width: 26rem; }

/* ---- 详情（道具 / 礼物）---- */
.bday-detail {
    text-align: center;
    padding: 1.4rem 1.2rem;
    background: var(--bg-card);
    border: 1px solid rgba(232, 168, 56, 0.2);
    border-radius: 1rem;
}
.bday-detail h3 {
    margin: 0.9rem 0 0.5rem;
    font-size: 1.05rem;
    color: var(--gold);
}
.bday-detail p {
    font-size: 0.88rem;
    line-height: 1.8;
    color: var(--text-secondary, #b8c0d0);
    white-space: pre-line;
}
.bday-detail-img {
    width: 100%;
    max-height: 40vh;
    object-fit: contain;         /* 横竖图自适应，别裁掉内容 */
}
/* 难抠的礼物用「相框里的照片」摆着，反而更省事也不违和 */
.bday-detail-img.framed {
    padding: 0.5rem;
    background: rgba(60, 48, 32, 0.5);
    border: 2px solid rgba(232, 168, 56, 0.35);
    border-radius: 0.3rem;
}

/* ---- 切蛋糕 ---- */
/* 3 块，clip-path 扇形。块数是个位数，不存在合成层爆炸风险。 */
.bday-cake { text-align: center; }
.bday-cake h3 { font-size: 1.05rem; color: var(--gold); margin-bottom: 1rem; }

/* 盘面 = **空盘子**，不铺蛋糕图。蛋糕只画在扇形上（.bday-slice）。
   ⚠️ 别再把俯视图铺回盘面。原来盘面和扇形铺的是**同一张图、同样的 size/position**，
   于是切走一块之后露出来的还是那张图（只是被 ::after 压暗一档）——
   切开前后几乎一个样，用户反馈「切掉一块剩余的蛋糕应该是 3/4 而不是完整的」就是这个。
   现在盘面是深色陶瓷（径向渐变 + 金色内圈边），切走一块就露出**盘底**，
   剩下的 n-1 块拼起来正好是 (n-1)/n 个圆，「剩 3/4」是几何上真实的。
   俯视图仍由 JS 挂在扇形上（素材清单是唯一事实来源，CSS 不写死路径；
   写成 var() 会按声明它的样式表解析成 /css/assets/... → 404）。 */
.bday-cake-plate {
    position: relative;
    width: 13rem;
    height: 13rem;
    margin: 0 auto 1.2rem;
    border-radius: 50%;
    /* 陶瓷盘：中心略亮、边缘压暗，读起来是个凹面 */
    background: radial-gradient(circle at 50% 42%, #23282f 0%, #171b21 58%, #0d1015 100%);
    box-shadow: inset 0 0 0 1px rgba(232, 168, 56, 0.28),
                inset 0 6px 14px rgba(0, 0, 0, 0.55);
}

/* 每块扇形显示的是**同一张俯视图的对应扇区**：背景图和盘面同尺寸同定位，
   clip-path 一切，几块拼起来正好是完整的一个蛋糕。
   用 background 而不是 <img>：clip-path 要切的是这一格自己的盒子，
   插图片还得再套一层定位，没必要。 */
.bday-slice {
    position: absolute;
    inset: 0;
    border: none;
    cursor: pointer;
    /* 同盘面：background-image 走 JS，别改回 var(--cake-top)（会 404） */
    background-color: #e8d9c0;
    background-position: center;
    background-size: cover;
    background-repeat: no-repeat;
    border-radius: 50%;
    transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.5s ease;
}

/* 扇形的 clip-path 与滑出方向由 JS 按份数算好、写成行内样式（见 birthday-room.js
   sliceGeometry）。原先是按 3 块把坐标写死在这儿，改成 4 块就得重算一遍几何——
   份数跟着照片数量走（每年加一张），不该每次都来动 CSS。

   仍然不用 CSS 的 cos()/sin()：那是 Chrome 111+ 才有的，她那台华为不一定够新，
   算不出来的话 clip-path 整条失效、几块会叠成一个整圆。JS 里算完给死值最稳。
   顶点取到半径远超圆的 50%，多出来的部分由 border-radius 收掉。 */
/* 点了：滑出去 + **彻底消失**（opacity 0，原来是 0.35）。
   留着半透明的那一片会盖在盘子外沿上，缺口读不出来——用户要的是「剩 3/4」，
   那这一块就必须真的不在盘子上了。它的内容已经在下面的轮播里出现，不会丢。
   位移也从 34px 加到 46px（见 sliceGeometry），滑出的动作看得更清楚。 */
.bday-slice.cut {
    transform: translate(var(--dx), var(--dy));
    opacity: 0;
    pointer-events: none;
}

/* 照片改**横向切换**（用户要求）。原来是 flex-column，每切一刀往下追加一张，
   第二张就掉到屏幕外了——不滑手机根本看不见新照片，而这一层还叠在房间之上、
   她未必意识到能滚。现在是个轮播：切一刀自动翻到最新那张，左右滑看以前的。
   跟相册用同一套做法（track 整条 translateX），别再发明第二种。 */
.bday-cake-reveal {
    position: relative;
    overflow: hidden;               /* 关键：把没在看的那几张关在外面 */
    border-radius: 0.8rem;
    /* 横向手势归我，竖向留给页面滚动。
       ⚠️ 不写这条的话浏览器可能把横滑判成「侧滑返回 / 横向滚动」并直接
       pointercancel，手势中途丢掉 —— 用户反馈「左右滑动不顺畅，会失灵」的一半在这。 */
    touch-action: pan-y;
}
.bday-cake-reveal:empty { display: none; }

.bday-cake-track {
    display: flex;
    transition: transform 0.42s cubic-bezier(0.22, 1, 0.36, 1);
    will-change: transform;
}
/* 拖动中关掉过渡：不关的话每一帧的 transform 都要走一遍 0.42s 缓动，
   手指走了、画面还在追，就是「不顺畅」的手感。松手时去掉这个类再吸附。 */
.bday-cake-track.dragging { transition: none; }

.bday-cake-item {
    flex: 0 0 100%;                 /* 一屏一张，宽度锁死才好算 translateX */
    box-sizing: border-box;
    padding: 0.9rem;
    background: var(--bg-card);
    border: 1px solid rgba(232, 168, 56, 0.2);
    border-radius: 0.8rem;
    opacity: 0;
    transition: opacity 0.4s ease;
}
.bday-cake-item.show { opacity: 1; }
/* 照片横竖都有：限高 + contain，竖图不会把这张卡顶得老长。
   56vh → 44vh：底下多了一行圆点，且这一层整体不该再需要滚动 */
.bday-cake-item img {
    display: block;
    width: 100%;
    max-height: 44vh;
    object-fit: contain;
    border-radius: 0.5rem;
    margin-bottom: 0.6rem;
    /* ⚠️ <img> 默认可拖拽。手指/鼠标从图上开始横滑会触发浏览器原生的
       「拖图片」，紧接着一个 pointercancel，滑动手势当场丢——这是
       「左右滑动会失灵」的另一半。JS 里还有 draggable=false 兜一道。 */
    -webkit-user-drag: none;
    user-select: none;
    pointer-events: none;           /* 手势统一由 .bday-cake-reveal 收 */
}

/* 圆点：既是「一共几张、在第几张」的指示，也能点着跳 */
.bday-cake-dots {
    display: flex;
    justify-content: center;
    gap: 0.4rem;
    margin-top: 0.7rem;
}
.bday-cake-dots:empty { display: none; }
.bday-cake-dot {
    width: 0.42rem;
    height: 0.42rem;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: rgba(232, 168, 56, 0.28);
    cursor: pointer;
    transition: background 0.25s ease, transform 0.25s ease;
}
.bday-cake-dot.on { background: var(--gold); transform: scale(1.35); }
/* 年份现在是卡片里最后一个元素（文案已取消），不用再留下边距 */
.bday-cake-year {
    display: block;
    font-size: 0.72rem;
    letter-spacing: 0.14em;
    color: var(--gold-dark);
    text-align: center;
}

/* ---- 贺卡陈列 ---- */
.bday-cards h3 { font-size: 1.05rem; color: var(--gold); margin-bottom: 1rem; text-align: center; }
.bday-card-group { margin-bottom: 1.4rem; }
.bday-card-group h4 {
    font-size: 0.78rem;
    font-weight: 500;
    color: var(--gold-dark);
    letter-spacing: 0.08em;
    margin-bottom: 0.6rem;
}
.bday-card-row {
    display: flex;
    gap: 0.7rem;
    overflow-x: auto;
    padding-bottom: 0.4rem;
    -webkit-overflow-scrolling: touch;
}

/* 礼物陈列：横排铺开，点不开。抠图件用 contain 完整显示，别裁掉边 */
.bday-gift-row {
    display: flex;
    gap: 0.6rem;
    overflow-x: auto;
    padding-bottom: 0.4rem;
    -webkit-overflow-scrolling: touch;
}

.bday-gift-item {
    flex: 0 0 auto;
    width: 5.2rem;
    height: 5.2rem;
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    background: rgba(255, 255, 255, 0.04);
    border: 1px solid rgba(232, 168, 56, 0.18);
    /* 现在是 <button>（点开看大图），得把按钮的默认样式压掉 */
    padding: 0;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
}

.bday-gift-item img {
    max-width: 84%;
    max-height: 84%;
    object-fit: contain;
    pointer-events: none;      /* 点击一律落在按钮上，别被原生图片拖拽劫走 */
}

.bday-card-thumb {
    flex: 0 0 auto;
    width: 6.4rem;
    padding: 0;
    background: none;
    border: 1px solid rgba(232, 168, 56, 0.25);
    border-radius: 0.5rem;
    overflow: hidden;
    cursor: pointer;
}
.bday-card-thumb img {
    width: 100%;
    height: 8.4rem;
    object-fit: cover;
    display: block;
}
.bday-card-thumb span {
    display: block;
    padding: 0.35rem;
    font-size: 0.7rem;
    color: var(--gold-light);
}
/* 今年那张要看得出不一样 */
.bday-card-thumb.fresh {
    border-color: var(--gold);
    box-shadow: 0 0 14px rgba(232, 168, 56, 0.45);
}

/* 翻开：封面 → 内页 */
.bday-card-open { text-align: center; }
/* 回信匣：跟左上角的 ✕ 分工不同——✕ 退回房间，这个退回那一架 */
.bday-card-back {
    display: inline-block;
    margin-bottom: 0.6rem;
    padding: 0.4rem 0.9rem;
    font-size: 0.78rem;
    color: var(--gold-light);
    background: rgba(21, 27, 46, 0.9);
    border: 1px solid rgba(232, 168, 56, 0.28);
    border-radius: 999px;
    cursor: pointer;
}
.bday-card-page {
    animation: bdayFlip 0.42s cubic-bezier(0.22, 1, 0.36, 1);
    transform-origin: left center;
}
@keyframes bdayFlip {
    from { transform: perspective(800px) rotateY(-38deg); opacity: 0.4; }
    to   { transform: perspective(800px) rotateY(0);      opacity: 1; }
}
.bday-card-page img {
    width: 100%;
    border-radius: 0.5rem;
    cursor: zoom-in;
}
.bday-card-nav {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    margin-top: 0.8rem;
    font-size: 0.8rem;
    color: var(--gold-light);
}
.bday-card-nav button {
    padding: 0.4rem 0.9rem;
    font-size: 0.78rem;
    color: var(--gold-light);
    background: transparent;
    border: 1px solid rgba(232, 168, 56, 0.3);
    border-radius: 999px;
    cursor: pointer;
}
.bday-card-nav button:disabled { opacity: 0.3; }
.bday-card-tip {
    margin-top: 0.6rem;
    font-size: 0.7rem;
    color: rgba(168, 216, 234, 0.55);
}

/* 全屏看图：手写字在手机上直接缩放显示大概率糊，必须能放大 */
.bday-zoom {
    position: fixed;
    inset: 0;
    z-index: 120;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #05070d;
    opacity: 0;
    transition: opacity 0.25s ease;
    overflow: auto;
    /* 双指缩放交给浏览器原生手势，最稳 */
    touch-action: pinch-zoom;
}
.bday-zoom.show { opacity: 1; }
.bday-zoom img { max-width: 100%; height: auto; }
/* 同上：右上角是音乐按钮的地盘，放这儿点不到（z-index 也得压过它的 100） */
.bday-zoom-close {
    position: fixed;
    left: 1rem;
    top: 1rem;
    z-index: 120;
    padding: 0.45rem 1rem;
    font-size: 0.8rem;
    color: var(--gold-light);
    background: rgba(21, 27, 46, 0.9);
    border: 1px solid rgba(232, 168, 56, 0.3);
    border-radius: 999px;
    cursor: pointer;
}

/* ---- 童乐 ---- */
.bday-tongle {
    text-align: center;
    padding: 1.6rem 1.2rem;
    background: linear-gradient(160deg, #1e2542, #151b2e);
    border: 1px solid rgba(232, 168, 56, 0.25);
    border-radius: 1rem;
}
/* 童乐是**抠图件**（透明底全身像），不是方照片：
   `width:100%` 会把元素撑成一个宽盒子、图在里头居中，两侧留出大片透明——
   而 border-radius / 背景色是画在盒子上的，于是圆角落在空气里。
   改成 `width:auto` 让盒子贴着人形；`max-height` 仍然管住高度（他是 1:2.5 的竖图，
   按宽算会顶到屏外）。圆角也去掉：抠图没有边，圆角切的是透明像素，看不见还会啃到脚。 */
.bday-tongle img {
    width: auto;
    max-width: 100%;
    max-height: 52vh;
    object-fit: contain;
    margin-bottom: 1rem;
    /* 深空底上一个人形剪影容易发飘，给一点落地感 */
    filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.45));
}
/* 蜡笔卡片质感：手写体 + 暖色 */
.bday-tongle-text {
    font-size: 1rem;
    line-height: 1.9;
    color: var(--gold-light);
    white-space: pre-line;
}
.bday-tongle-sign {
    margin-top: 0.9rem;
    font-size: 0.8rem;
    color: var(--gold-dark);
}

/* ---- 成长相册：胶片横滚 ---- */
/* 用户要求「用影片胶带的方式循环自左向右横向滚动播放」。
   胶带的两条齿孔用 repeating-linear-gradient 画在容器的上下边（伪元素），
   不用图片素材：那是一排等距小方块，代码画一行的事，多一张图就多一次预取。 */
.bday-growth { text-align: center; }
.bday-growth h3 {
    margin-bottom: 0.9rem;
    font-size: 1rem;
    color: var(--gold-light);
}
.bday-film {
    position: relative;
    overflow: hidden;                 /* 轨道比容器宽得多，多出来的必须裁掉 */
    padding: 0.55rem 0;               /* 给上下两条齿孔留位置 */
    /* 片基是**中性黑**，不带蓝（原来是 #0b0e17，深空蓝）：这一层要的是黑白胶片，
       颜色一旦有色相就又回到「黑+某个颜色」了。 */
    background: #0e0e0e;
    border-radius: 0.4rem;
    /* 两侧淡出，胶带像是从画面外走进来的，而不是撞在两条硬边上 */
    -webkit-mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
            mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}
/* 齿孔：上下各一条 */
.bday-film::before,
.bday-film::after {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    height: 0.55rem;
    /* 齿孔是**白的**（用户：「当前是黑黄色，改为黑白」）。真胶片的齿孔是打穿的洞，
       背后透光 → 比片基亮，所以白比金更像那个东西。 */
    background: repeating-linear-gradient(to right,
        transparent 0 0.34rem, rgba(255, 255, 255, 0.72) 0.34rem 0.62rem);
    pointer-events: none;             /* 点齿孔应该算「点胶带」（暂停），不该被它吃掉 */
    z-index: 1;
}
.bday-film::before { top: 0; }
.bday-film::after { bottom: 0; }

.bday-film-track {
    display: flex;
    width: max-content;               /* 由内容撑开，别被容器宽度压回去 */
    gap: 0.4rem;
    /* 用户要「自左向右」滚动，所以轨道从 -50% 走到 0（往右走）——照片于是从左边缘
       进场、向右移出。位移正好半个轨道：轨道是两份拷贝，走完时画面跟起点像素级一致，
       动画归零那一帧看不出跳。**方向和 openGrowth 里的倒排是一套**，改一处必错位
       （往右走时先进场的是 DOM 里靠后那一格，所以那边倒着排才是 1,2,3…）。
       ⚠️ 那边**第一张不参与倒排**，起点那一格是 1.jpg，再由 JS 设 margin-left 把它
       推到屏幕正中（用户：「首先页面正中间显示的是 1.jpg」）。偏移走 margin 而不是
       改这里的 -50%：位移是按轨道自身宽度算的，外面加常量不影响周期。
       时长由 JS 按张数设（每张 GROWTH_PER_S 秒），这里不写死。 */
    animation: bday-film-roll 40s linear infinite;
}
@keyframes bday-film-roll {
    from { transform: translateX(-50%); }
    to   { transform: translateX(0); }
}
/* 一张一格。**高度固定、宽度自适应**：这一叠里有一张竖图（33.jpg，0.75），
   按宽度固定的话竖图会被压扁或裁掉半个人。object-fit: contain 保证整张都在。 */
.bday-film-frame {
    flex: 0 0 auto;
    height: 34vh;
    padding: 0;
    background: #050505;
    border: 1px solid rgba(255, 255, 255, 0.28);
    border-radius: 0.2rem;
    cursor: pointer;
}
.bday-film-frame img {
    display: block;
    height: 100%;
    width: auto;
    max-width: 78vw;                  /* 横图别宽到一张就占满整屏 */
    object-fit: contain;
    -webkit-user-drag: none;
}
.bday-film-tip {
    margin-top: 0.8rem;
    font-size: 0.75rem;
    color: rgba(255, 255, 255, 0.62);   /* 跟着胶带走黑白，不用 --gold-dark */
}
/* 她系统里开了「减少动态效果」就别硬滚：横向自动滚动是前庭不适的典型触发源。
   停下来照样能看——胶带静止时是一排照片，点开大图的路还在。 */
@media (prefers-reduced-motion: reduce) {
    .bday-film-track { animation: none; }
    .bday-film { overflow-x: auto; -webkit-overflow-scrolling: touch; }
}

/* ---- 3D 蛋糕转盘（左右滑转视角）---- */
/* 序列帧，不是 WebGL：转帧是换 <img> 的 src，这一层没有任何 transform。
   所以「转到第几度」在样式里读不出来，测试要看 _cake3d().idx（见那个钩子的 ⚠️）。 */
.bday-cake3d { text-align: center; }
.bday-cake3d h3 {
    margin-bottom: 0.9rem;
    font-size: 1rem;
    color: var(--gold-light);
}
.bday-turn {
    position: relative;
    /* 底色跟烤进帧里的背景同色（#0a0e1a）：帧的四边有留白，两边不同色就会看见
       一圈方框。烤的脚本用的就是这个色，改一处必须改另一处 */
    background: #0a0e1a;
    border-radius: 0.5rem;
    overflow: hidden;
    /* 横滑是我们自己处理的手势，别让浏览器抢去做横向滚动/后退手势；
       竖滑仍留给页面（pan-y），跟切蛋糕那套锁轴一致 */
    touch-action: pan-y;
    cursor: grab;
    outline: none;
}
.bday-turn:focus-visible { box-shadow: 0 0 0 2px var(--gold); }
.bday-turn-img {
    display: block;
    width: 100%;
    height: auto;
    /* 拖拽会被浏览器的原生图片拖拽劫走（切蛋糕横滑那笔的老账），
       三条一起关：draggable=false 在 JS 里设，这两条在这儿 */
    pointer-events: none;
    -webkit-user-drag: none;
    user-select: none;
}
/* 转到哪儿了。36 个圆点太密读不出来，用一条细进度条 + 一枚游标 */
.bday-turn-bar {
    position: absolute;
    left: 12%;
    right: 12%;
    bottom: 0.55rem;
    height: 2px;
    background: rgba(232, 168, 56, 0.20);
    border-radius: 1px;
}
.bday-turn-bar i {
    position: absolute;
    top: -3px;
    left: 0;
    width: 8px;
    height: 8px;
    margin-left: -4px;
    background: var(--gold);
    border-radius: 50%;
    /* 只动 transform/left，别加 width/height 过渡（那会掉帧）。
       擦帧时每帧都在改 left，过渡要短，否则游标追不上手指 */
    transition: left 0.08s linear;
}
.bday-turn-tip {
    margin-top: 0.8rem;
    font-size: 0.75rem;
    color: var(--gold-dark);
}
@media (prefers-reduced-motion: reduce) {
    .bday-turn-bar i { transition: none; }
}

/* ---- 隐藏彩蛋：录音机（按 WALL·E 的肚子）---- */
/* 用户提供的 recoder.png 当播放器外壳。四角是透明的（所以走 PNG 不走 JPEG），
   底下不铺任何卡片色，让它直接落在弹层的深空底上。 */
.bday-rec { text-align: center; }
.bday-rec-shell img {
    display: block;
    /* 抠图件按宽撑满会把盒子撑到容器宽、人在中间两侧全是透明（童乐那条老账）。
       这张是横的（363×331），按宽给上限、高度自适应就行。 */
    width: auto;
    max-width: 100%;
    height: auto;
    margin: 0 auto;
    pointer-events: none;
    -webkit-user-drag: none;
    user-select: none;
    filter: drop-shadow(0 6px 14px rgba(0, 0, 0, 0.45));
}
.bday-rec-now {
    margin-top: 0.9rem;
    font-size: 0.95rem;
    color: var(--gold-light);
}
.bday-rec-time {
    margin-top: 0.25rem;
    font-size: 0.72rem;
    color: var(--gold-dark);
    font-variant-numeric: tabular-nums;   /* 秒数跳动时别让整行左右抖 */
}
.bday-rec-bar {
    position: relative;
    margin: 0.5rem auto 0;
    width: 78%;
    height: 3px;
    background: rgba(232, 168, 56, 0.18);
    border-radius: 2px;
    overflow: hidden;
}
.bday-rec-bar i {
    position: absolute;
    inset: 0 auto 0 0;
    width: 0;
    background: var(--gold);
    border-radius: 2px;
}
.bday-rec-btns {
    display: flex;
    justify-content: center;
    align-items: center;
    gap: 1rem;
    margin-top: 1rem;
}
/* 44px 是手指的下限（同车库回首页那颗按钮的教训：小了在滑动的场景里容易点空） */
.bday-rec-play {
    width: 3rem;
    height: 3rem;
    border: 1px solid rgba(232, 168, 56, 0.5);
    border-radius: 50%;
    background: rgba(232, 168, 56, 0.12);
    color: var(--gold-light);
    font-size: 1.05rem;
    line-height: 1;
    cursor: pointer;
}
.bday-rec-play:disabled {
    opacity: 0.4;
    cursor: default;
}
.bday-rec-next {
    min-height: 2.75rem;
    padding: 0 1rem;
    border: 1px solid rgba(232, 168, 56, 0.35);
    border-radius: 1.4rem;
    background: transparent;
    color: var(--gold);
    font-size: 0.85rem;
    cursor: pointer;
}
.bday-rec-play:focus-visible,
.bday-rec-next:focus-visible { box-shadow: 0 0 0 2px var(--gold); }

/* ---- 调试面板 ---- */
.bday-debug {
    position: fixed;
    left: 0.5rem;
    bottom: 0.5rem;
    z-index: 200;
    padding: 0.4rem 0.6rem;
    font: 11px/1.5 monospace;
    color: #8f8;
    background: rgba(0, 0, 0, 0.8);
    border-radius: 0.3rem;
    pointer-events: none;
    white-space: pre;
}

/* ---- 隐藏调试入口（长按封面标题 10 秒）---- */

/* 长按文字在手机上会弹系统的「复制/全选」菜单，还会顺带打断 pointer 事件，
   长按计时器就永远走不到 10 秒。必须两条都关掉：iOS 认 touch-callout，
   安卓认 user-select。这只影响标题，其余文案照样可选。
   position:relative 是给进度条做定位锚点，不改标题任何视觉表现。

   ⚠️⚠️ `touch-action: none` 这一行**就是「安卓上长按标题进不去调试面板」的全部修复**，
   别当成可有可无的一条删掉：`body` 上有 `touch-action: pan-y`（style.css:46），
   会**继承到标题上**。于是安卓 Chrome 把长按期间手指的竖向微动判成「要滚页面」，
   直接发 `pointercancel` 收走这个指针——10 秒计时器当场死，她**永远按不进**。
   实测（Pixel 5、真 touch 事件、按满 10 秒）：
     合位移 12.7px → 能进（还没超过浏览器的 slop）
     合位移 19.8px → ta=pan-y 时收到 pointercancel、不开；ta=none 时正常开
     合位移 28.3px → 同上，ta=none 才开
   而真机上按住 10 秒纹丝不动是不可能的，所以修之前线上基本等于进不去。

   ⚠️ JS 那边**不需要**配合改（我试过，是白改）：触摸指针有**隐式捕获**
   （Chrome 自动把 touch 指针捕获到 pointerdown 的目标上），所以 `pointerleave`
   对手指压根不会触发，`birthday-debug.js` 里那个 pointerleave 监听只对鼠标有意义。
   我一度还加了 setPointerCapture + 24px 位移容差，实测两者都是多余的，而位移容差
   反而**比原来更严**（把 28.3px 那档从「能进」改成了「不进」），已回退。 */
.cover-title {
    position: relative;
    touch-action: none;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
}

/* 长按进度条：贴在标题下沿 */
.bday-debug-progress {
    position: absolute;
    left: 0;
    bottom: -2px;
    width: 0;
    height: 2px;
    background: var(--gold, #e8a838);
    opacity: 0.55;
    transition: width linear;      /* 时长由 JS 给，随长按剩余时间变 */
    pointer-events: none;
}

/*
 * ⚠️ `align-items: center` + `overflow-y: auto` 是**不能一起用**的。
 * 面板比视口高时，居中的 flex 子项会朝**两个方向**溢出，而滚动只到得了「正方向」
 * 那一侧 —— 上面溢出的那截既被裁掉、又滚不到，连 scrollHeight 都不算它。
 * 实测（390×844、卡片高 1241）：scrollTop 已经是 0，卡片顶边却在 -198px，
 * 标题「🎂 生日支线（调试）」整条看不见，就是用户报的「顶部看不到（滚不到）」。
 * 而 scrollHeight 只有 1059 < 1241，差的正是被吞掉的那 198px。
 *
 * 正解是 `align-items: flex-start` + 卡片 `margin: auto`：auto 外边距在有余量时
 * 照样居中（短面板的观感不变），空间不够时**不会**产生负向溢出，从顶部开始正常滚。
 * 别改回 center —— 面板条目还会继续加，只会更高。
 */
.bday-debug-panel {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: flex-start;
    justify-content: center;
    padding: 1rem;
    background: rgba(4, 7, 15, 0.9);
    overflow-y: auto;
    /* iOS 上惯性滚动 + 安全区（PWA 全屏没有地址栏，顶部就是刘海） */
    -webkit-overflow-scrolling: touch;
    padding-top: max(1rem, env(safe-area-inset-top));
    padding-bottom: max(1rem, env(safe-area-inset-bottom));
}

.bday-debug-card {
    width: 100%;
    max-width: 22rem;
    /* 有余量时居中，不够时从顶部开始 —— 见 .bday-debug-panel 的 ⚠️ */
    margin: auto;
    padding: 1.1rem;
    background: var(--bg-card, #151b2e);
    border: 1px solid rgba(232, 168, 56, 0.28);
    border-radius: 0.9rem;
}

.bday-debug-card h3 {
    font-size: 1rem;
    color: var(--gold, #e8a838);
    margin-bottom: 0.6rem;
    text-align: center;
}

.bday-debug-group { margin-bottom: 0.9rem; }

.bday-debug-group h4 {
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0.08em;
    color: var(--gold-dark, #b07f22);
    margin-bottom: 0.4rem;
}

.bday-debug-hint {
    font-size: 0.68rem;
    line-height: 1.7;
    color: var(--text-secondary, #b8c0d0);
    margin-bottom: 0.7rem;
}

.bday-debug-hint code {
    font-size: 0.66rem;
    padding: 0 0.2rem;
    background: rgba(255, 255, 255, 0.06);
    border-radius: 0.2rem;
    word-break: break-all;
}

.bday-debug-btn {
    display: block;
    width: 100%;
    /* 44px 是手指最小可靠命中高度，调试面板也别做成得瞄准的按钮 */
    min-height: 44px;
    margin-bottom: 0.4rem;
    padding: 0.5rem 0.7rem;
    font-size: 0.82rem;
    text-align: left;
    color: var(--eve-blue, #a8d8ea);
    background: rgba(168, 216, 234, 0.08);
    border: 1px solid rgba(168, 216, 234, 0.22);
    border-radius: 0.5rem;
    cursor: pointer;
}

.bday-debug-btn.danger {
    color: #e8a0a0;
    background: rgba(232, 160, 160, 0.08);
    border-color: rgba(232, 160, 160, 0.28);
}

.bday-debug-close {
    display: block;
    width: 100%;
    min-height: 44px;
    margin-top: 0.3rem;
    font-size: 0.85rem;
    color: var(--gold, #e8a838);
    background: none;
    border: 1px solid rgba(232, 168, 56, 0.35);
    border-radius: 0.5rem;
    cursor: pointer;
}

/* ---- 拖星光调位置的小工具条（编辑模式，她永远看不到）---- */
/* 贴底：星光都在画面上半部（挂在货架框上方），工具条压在下面才不挡被调的东西 */
.bday-edit-bar {
    position: fixed;
    left: 50%;
    bottom: 14px;
    transform: translateX(-50%);
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 8px 10px;
    border-radius: 10px;
    background: rgba(10, 14, 26, 0.92);
    border: 1px solid rgba(168, 216, 234, 0.35);
    /* 高过热点层，低过看大图那一层（120） */
    z-index: 118;
    font-size: 12px;
    color: var(--eve-blue);
}
.bday-edit-bar button {
    padding: 6px 9px;
    border-radius: 8px;
    border: 1px solid rgba(232, 168, 56, 0.5);
    background: rgba(232, 168, 56, 0.14);
    color: var(--gold-light);
    font-size: 12px;
    cursor: pointer;
}
