/* 画布固定比例：每层使用生成配置的百分比布局，保证不拉伸人物。 */
.scene { width:calc(var(--u) * 750); height:calc(var(--u) * 1333.333); position:relative; overflow:hidden; background:#f5f1e7; }
.artboard { position:absolute; inset:0; }
.layer { position:absolute; opacity:0; will-change:transform,opacity; }
.art { display:block; width:100%; height:100%; }
.entered .layer { animation:rise-in 0.909s cubic-bezier(.2,.75,.3,1) both; }
.entered .enter-left { animation-name:left-in; }
.entered .enter-unfold { animation-name:unfold; transform-origin:50% 100%; }
.entered .enter-settle { animation-name:settle; }
.entered .enter-peek { animation-name:peek; }
.entered .motion-breathe { animation:breathe 7s ease-in-out infinite alternate; }
.entered .motion-sway { animation:sway 8s ease-in-out infinite alternate; }
.entered .motion-depth0 { animation:depth0 10s ease-in-out infinite alternate; }
.entered .motion-depth1 { animation:depth1 11s ease-in-out infinite alternate; }
.entered .motion-depth2 { animation:depth2 9s ease-in-out infinite alternate; }
/* 离屏时同时暂停入场、倒计时与微动，回到这一幕再继续。 */
.scene:not(.in-view) .layer, .scene:not(.in-view) .art, .scene:not(.in-view) .countdown { animation-play-state:paused!important; }
.scene:not(.in-view) .loading { visibility:hidden; }
@keyframes rise-in { from {opacity:0;transform:translateY(calc(var(--u) * 28));} to {opacity:1;transform:translateY(0);} }
@keyframes left-in { from {opacity:0;transform:translateX(calc(var(--u) * -58)) rotate(-3deg);} to {opacity:1;transform:none;} }
@keyframes unfold { from {opacity:0;transform:translateY(calc(var(--u) * 35)) scale(.97);} to {opacity:1;transform:none;} }
@keyframes settle { 0% {opacity:0;transform:translateY(calc(var(--u) * 65)) scale(.985);} 75% {opacity:1;transform:translateY(calc(var(--u) * -3));} 100% {opacity:1;transform:none;} }
@keyframes peek { from {opacity:0;transform:translateY(calc(var(--u) * 100));} to {opacity:1;transform:none;} }
/* 贴纸呼吸只缩放装饰；人物使用平移，避免身体形变。 */
@keyframes breathe { from {transform:scale(.985);} to {transform:scale(1.025);} }
@keyframes sway { from {transform:rotate(-1.1deg);} to {transform:rotate(1.1deg);} }
@keyframes depth0 { from {transform:translateX(calc(var(--u) * -1));} to {transform:translateX(calc(var(--u) * 1));} }
@keyframes depth1 { from {transform:translateX(.calc(var(--u) * 8));} to {transform:translateX(-.calc(var(--u) * 8));} }
@keyframes depth2 { from {transform:translateY(0);} to {transform:translateY(calc(var(--u) * -1));} }
.countdown {position:absolute;left:10%;top:73.4%;width:80%;height:14%;background:#f5f1e7;text-align:center;opacity:0;}
.countdown.show {animation:rise-in 0.840s 0.629s both;}
.count-label {display:block;font-size:calc(var(--u) * 26);margin:calc(var(--u) * 6) 0 calc(var(--u) * 28);}
.digits {display:flex;justify-content:center;align-items:center;gap:calc(var(--u) * 15);}
.digit {background:#626647;color:#f5f1e7;border-radius:calc(var(--u) * 11);width:calc(var(--u) * 95);height:calc(var(--u) * 101);line-height:calc(var(--u) * 101);font-size:calc(var(--u) * 66);font-family:Arial,sans-serif;}
.days-unit {font-size:calc(var(--u) * 31);margin-left:calc(var(--u) * 15);}
.count-celebrate {font-size:calc(var(--u) * 65);color:#c79690;}
.hit {position:absolute;background:transparent!important;margin:0;padding:0;border:0;border-radius:calc(var(--u) * 32);min-height:calc(var(--u) * 58);}
.map-hit {left:25%;top:84%;width:24%;height:4.4%;}
.copy-hit {left:52.5%;top:83.4%;width:24%;height:4.4%;}
.hit-pressed {box-shadow:0 0 0 calc(var(--u) * 5) rgba(199,150,144,.25);}
.loading {position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;background:#f5f1e7;font-size:calc(var(--u) * 26);letter-spacing:calc(var(--u) * 3);z-index:5;}
.loading button {margin-top:calc(var(--u) * 25);font-size:calc(var(--u) * 25);background:#e7e3d7;}
.music-control {position:fixed;right:calc(var(--u) * 22);top:calc(var(--u) * 22);z-index:20;border:calc(var(--u) * 1) solid #999c80;border-radius:50%;width:calc(var(--u) * 66);height:calc(var(--u) * 66);padding:0;line-height:calc(var(--u) * 62);background:rgba(245,241,231,.92);font-size:calc(var(--u) * 40);}
.music-control.playing {border-color:#626647;box-shadow:0 0 0 calc(var(--u) * 6) rgba(98,102,71,.07);}
.music-caption {position:fixed;top:calc(var(--u) * 100);right:calc(var(--u) * 20);font-size:calc(var(--u) * 23);background:#f5f1e7;z-index:25;padding:calc(var(--u) * 12);}
.page-mark {position:fixed;right:calc(var(--u) * 24);bottom:calc(calc(var(--u) * 23) + env(safe-area-inset-bottom));font-size:calc(var(--u) * 22);letter-spacing:calc(var(--u) * 2);z-index:20;background:rgba(245,241,231,.85);padding:calc(var(--u) * 4) calc(var(--u) * 10);border-radius:calc(var(--u) * 15);}
.closing {padding:calc(var(--u) * 20) calc(var(--u) * 100) calc(calc(var(--u) * 90) + env(safe-area-inset-bottom));text-align:center;}
.share-button {border:calc(var(--u) * 1) solid #a6a88e;background:transparent;border-radius:calc(var(--u) * 40);font-size:calc(var(--u) * 28);}
.back-top {font-size:calc(var(--u) * 24);background:transparent;margin-top:calc(var(--u) * 20);}

/* 返回上一幕从上方轻落；方向固定到本轮入场结束。 */
.entered.from-above .layer { animation-name:return-in; animation-duration:0.840s; }
.from-above .countdown.show { animation-name:return-in; }
@keyframes return-in { from {opacity:0;transform:translateY(calc(var(--u) * -24));} to {opacity:1;transform:translateY(0);} }
.entered .motion-float {animation:photo-float 6s ease-in-out infinite alternate;}
.entered .motion-sparkle {animation:sparkle 3.6s ease-in-out infinite;}
.entered .motion-wiggle {animation:wiggle 5.5s ease-in-out infinite;transform-origin:35% 65%;}
.entered .motion-cat-breathe {animation:cat-breathe 4.8s ease-in-out infinite alternate;transform-origin:50% 100%;}
.entered .motion-cat-float {animation:cat-float 5.6s ease-in-out infinite alternate;transform-origin:50% 100%;}
@keyframes photo-float {from {transform:translateY(0);} to {transform:translateY(calc(var(--u) * -3));}}
@keyframes sparkle {0%,100% {transform:scale(.9) rotate(-5deg);opacity:.65;} 50% {transform:scale(1.12) rotate(5deg);opacity:1;}}
@keyframes wiggle {0%,65%,100% {transform:rotate(0);} 72% {transform:rotate(-3deg);} 80% {transform:rotate(2deg);} 88% {transform:rotate(-1deg);}}
@keyframes cat-breathe {from {transform:translateY(0) rotate(-.4deg);} to {transform:translateY(calc(var(--u) * -5)) rotate(.4deg);}}
@keyframes cat-float {from {transform:translateY(calc(var(--u) * -1)) rotate(.3deg);} to {transform:translateY(calc(var(--u) * -6)) rotate(-.5deg);}}
/* 确认样片的首幕动效：背景分层上升，人物大幅浮入回弹，停留继续前后错速移动。 */
.entered .enter-opening-title{animation:title-pop 0.769s cubic-bezier(.2,.8,.2,1) both;}
.entered .enter-opening-far{animation:far-pop 1.189s cubic-bezier(.16,.85,.22,1) both;}
.entered .enter-opening-mid{animation:mid-pop 1.189s cubic-bezier(.16,.85,.22,1) both;}
.entered .enter-opening-near{animation:near-pop 1.189s cubic-bezier(.16,.85,.22,1) both;}
.entered .enter-opening-people{animation:people-pop 1.260s both;transform-origin:50% 88%;}
.entered .enter-opening-names{animation:names-pop 0.629s ease-out both;}
.entered .motion-opening-landscape{animation:landscape-hover 5s ease-in-out infinite;}
.entered .motion-opening-people{animation:people-hover 3.8s ease-in-out infinite;transform-origin:50% 90%;}
@keyframes title-pop{from{opacity:0;transform:translateX(-12%) rotate(-3deg)}to{opacity:1;transform:none}}
@keyframes far-pop{from{opacity:0;transform:translateY(12%)}to{opacity:1;transform:none}}
@keyframes mid-pop{from{opacity:0;transform:translateY(18%)}to{opacity:1;transform:none}}
@keyframes near-pop{from{opacity:0;transform:translateY(24%)}to{opacity:1;transform:none}}
@keyframes people-pop{0%{opacity:0;transform:translateY(32%) scale(.94) rotate(-2deg)}55%{opacity:1}76%{opacity:1;transform:translateY(-2.4%) scale(1.012) rotate(.5deg)}100%{opacity:1;transform:none}}
@keyframes names-pop{from{opacity:0;transform:translateY(70%)}to{opacity:1;transform:none}}
@keyframes landscape-hover{0%,100%{transform:translateY(0)}50%{transform:translateY(-.65%)}}
@keyframes people-hover{0%,100%{transform:translateY(0) rotate(0)}50%{transform:translateY(-2.4%) rotate(.5deg)}}
/* 卡片和胶卷带角度展开，邮戳压下，流程纸条交错进入；文字落位后不抖动。 */
.entered .enter-card{animation:card-pop 1.189s both;transform-origin:50% 85%;}
.entered .enter-card-right{animation:card-right-pop 1.189s both;transform-origin:50% 85%;}
.entered .enter-film{animation:film-pop 1.189s both;transform-origin:50% 0;}
.entered .enter-stamp{animation:stamp-pop 0.980s both;}
.entered .enter-tape{animation:tape-pop 0.980s both;transform-origin:10% 50%;}
.entered .enter-cat{animation:cat-pop 1.260s both;}
@keyframes card-pop{0%{opacity:0;transform:translateX(calc(var(--u) * -90)) translateY(calc(var(--u) * 45)) rotate(-9deg) scale(.92)}72%{opacity:1;transform:translateX(calc(var(--u) * 7)) rotate(1.5deg) scale(1.015)}100%{opacity:1;transform:none}}
@keyframes card-right-pop{0%{opacity:0;transform:translateX(calc(var(--u) * 90)) translateY(calc(var(--u) * 45)) rotate(9deg) scale(.92)}72%{opacity:1;transform:translateX(calc(var(--u) * -7)) rotate(-1.5deg) scale(1.015)}100%{opacity:1;transform:none}}
@keyframes film-pop{0%{opacity:0;transform:translateY(calc(var(--u) * -55)) rotate(-7deg) scaleY(.3)}75%{opacity:1;transform:rotate(1deg) scaleY(1.035)}100%{opacity:1;transform:none}}
@keyframes stamp-pop{0%{opacity:0;transform:translateY(calc(var(--u) * -45)) rotate(-18deg) scale(1.7)}65%{opacity:1;transform:rotate(2deg) scale(.91)}100%{opacity:1;transform:none}}
@keyframes tape-pop{0%{opacity:0;transform:translateX(calc(var(--u) * -70)) rotate(-25deg)}75%{opacity:1;transform:rotate(3deg)}100%{opacity:1;transform:none}}
@keyframes cat-pop{0%{opacity:0;transform:translateY(75%) rotate(-5deg)}70%{opacity:1;transform:translateY(-5%) rotate(2deg)}100%{opacity:1;transform:none}}
/* 持续微动增强；照片只做整体变换，不改变人像或猫咪表情。 */
@keyframes breathe{from{transform:scale(.96)}to{transform:scale(1.065)}}
@keyframes sway{from{transform:rotate(-2.6deg)}to{transform:rotate(2.6deg)}}
@keyframes photo-float{from{transform:translateY(0)}to{transform:translateY(calc(var(--u) * -14))}}
@keyframes sparkle{0%,100%{transform:scale(.72) rotate(-12deg);opacity:.5}50%{transform:scale(1.32) rotate(12deg);opacity:1}}
@keyframes wiggle{0%,55%,100%{transform:rotate(0)}65%{transform:rotate(-9deg)}75%{transform:rotate(7deg)}85%{transform:rotate(-4deg)}}
@keyframes cat-breathe{from{transform:translateY(0) rotate(-1.5deg)}to{transform:translateY(calc(var(--u) * -18)) rotate(1.5deg)}}
@keyframes cat-float{from{transform:translateY(calc(var(--u) * -2)) rotate(1deg)}to{transform:translateY(calc(var(--u) * -22)) rotate(-1.8deg)}}
/* 重新进入时按上滑方向播放，完整离屏才由逻辑层复位。 */
.entered.from-above .layer{animation-name:return-in;animation-duration:0.980s;}
@keyframes return-in{from{opacity:0;transform:translateY(calc(var(--u) * -65))}to{opacity:1;transform:none}}
.entered.from-above .enter-opening-title,.entered.from-above .enter-opening-names{animation-name:return-opening-text;}
.entered.from-above .enter-opening-far,.entered.from-above .enter-opening-mid,.entered.from-above .enter-opening-near{animation-name:return-opening-scene;animation-duration:1.189s;}
.entered.from-above .enter-opening-people{animation-name:return-opening-people;animation-duration:1.260s;}
@keyframes return-opening-text{from{opacity:0;transform:translateY(-35%)}to{opacity:1;transform:none}}
@keyframes return-opening-scene{from{opacity:0;transform:translateY(-12%)}to{opacity:1;transform:none}}
@keyframes return-opening-people{0%{opacity:0;transform:translateY(-19%) scale(.97)}60%{opacity:1}78%{opacity:1;transform:translateY(2%) scale(1.01)}100%{opacity:1;transform:none}}

/* 首幕停留状态持续运动，名字与日期保持稳定。 */
.entered .motion-opening-people{animation:opening-stay-person 3.6s ease-in-out infinite;transform-origin:50% 90%;}
.entered .motion-opening-landscape{animation:opening-stay-landscape 4.2s ease-in-out infinite;}
@keyframes opening-stay-person{0%,100%{transform:translateY(0) rotate(-.4deg);}50%{transform:translateY(calc(var(--u) * -32)) rotate(.8deg);}}
@keyframes opening-stay-landscape{0%,100%{transform:translateY(0);}50%{transform:translateY(calc(var(--u) * -10));}}
/* 爱心双拍、星星错峰闪烁、细线路渐显、小邮签摇摆。 */
.entered .motion-decor-heart{animation:decor-heart 2.5s ease-in-out infinite;}
.entered .motion-decor-star{animation:decor-star 2.4s ease-in-out infinite;}
.entered .motion-decor-route{animation:decor-route 3.6s ease-in-out infinite;}
.entered .motion-decor-sway{animation:decor-sway 3.4s ease-in-out infinite;transform-origin:30% 50%;}
@keyframes decor-heart{0%,45%,100%{transform:scale(1) rotate(-7deg);}16%{transform:scale(1.18) rotate(-3deg);}30%{transform:scale(1.04) rotate(-6deg);}}
@keyframes decor-star{0%,100%{opacity:.45;transform:scale(.8) rotate(-10deg);}50%{opacity:1;transform:scale(1.18) rotate(12deg);}}
@keyframes decor-route{0%,100%{opacity:.4;}50%{opacity:1;}}
@keyframes decor-sway{0%,100%{transform:rotate(-4deg) translateY(0);}50%{transform:rotate(3deg) translateY(calc(var(--u) * -4));}}


/* 特效只绘制在透明覆盖层，不拦截触摸，页面使用系统原生滚动。 */
.touch-effects{position:fixed;left:0;top:0;width:100%;height:100%;overflow:hidden;pointer-events:none;z-index:100;}
.touch-particle{position:absolute;display:block;pointer-events:none;opacity:0;will-change:transform,opacity;animation:touch-scatter .82s ease-out both;}
@keyframes touch-scatter{0%{opacity:0;transform:translate(0,0) scale(.45) rotate(0);}15%{opacity:.9;}100%{opacity:0;transform:translate(var(--fx-x),var(--fx-y)) scale(.85) rotate(var(--fx-rotate));}}
/* 普通按钮按下缩小，松手自然恢复；透明导航热区不改变大小以保持位置准确。 */
.music-control,.share-button,.back-top,.loading button{transition:transform .16s ease-out,box-shadow .16s ease-out;}
.button-pressed{transform:scale(.94);box-shadow:0 0 0 calc(var(--u) * 4) rgba(199,150,144,.12);}

/* 宴会厅位于地点纸条空白处，沿用纸条轻微倾斜，不覆盖按钮。 */
.venue-hall{position:absolute;left:0;top:57%;width:100%;text-align:center;font-size:calc(var(--u) * 23);line-height:1.1;color:#626647;letter-spacing:calc(var(--u) * 3);transform:rotate(-2deg);pointer-events:none;}

/* 浏览器外壳与小程序共享相同画布比例。 */
:root { --u:min(.133333333vw,.64px); color-scheme:light; }
* { box-sizing:border-box; }
html { background:#e8e5dc; }
body { margin:0;color:#626647;font-family:KaiTi,STKaiti,serif; }
button,a { -webkit-tap-highlight-color:transparent; }
button { color:inherit;cursor:pointer;font:inherit;border:0; }
.invitation { max-width:480px;margin:auto;background:#f5f1e7;overflow:hidden;min-height:100vh; }
.scene { width:100%;height:auto;aspect-ratio:9/16; }
.loading[hidden] { display:none; }
.music-control { right:max(12px,calc((100vw - 480px)/2 + 12px));top:max(12px,env(safe-area-inset-top)); }
.page-mark { right:max(12px,calc((100vw - 480px)/2 + 12px)); }
.hit { display:block;cursor:pointer; }
.hit:focus-visible,button:focus-visible { outline:2px solid #c79690;outline-offset:4px; }
.music-control:active,.share-button:active,.back-top:active,.loading button:active {transform:scale(.94);}
.hit:active { box-shadow:0 0 0 3px rgba(199,150,144,.25); }
.dynamic.show { animation:none;opacity:0; }
.entered .dynamic.show { animation:rise-in .84s .45s both; }
.entered.from-above .dynamic.show { animation-name:return-in; }
.closing button {display:block;width:100%;padding:12px 10px;}
.toast {position:fixed;bottom:85px;left:50%;transform:translateX(-50%);background:#626647;color:#fffdf7;padding:12px 20px;border-radius:22px;z-index:50;opacity:0;pointer-events:none;transition:opacity .2s;max-width:90vw;}
.toast.visible {opacity:1;}
dialog {border:0;border-radius:12px;padding:18px;background:#f5f1e7;max-width:min(420px,90vw);color:#626647;}
dialog::backdrop {background:#3338;}
dialog img {width:100%;display:block;}
dialog p {font-size:16px;line-height:1.7;text-align:center;}
#close-share {float:right;background:transparent;font-size:28px;margin-bottom:8px;}

/* 拖动星星先保持明亮再淡出，独立于点击散开的动画。 */
@keyframes touch-trail{0%{opacity:0;transform:translate(0,0) scale(.45) rotate(0);}10%,55%{opacity:1;}100%{opacity:0;transform:translate(var(--fx-x),var(--fx-y)) scale(.85) rotate(var(--fx-rotate));}}

/* 第一幕确认分镜：三段景物只淡入并固定；返回时也不移动背景，避免重复山影与露缝。 */
.entered .enter-sequential-background,.entered.from-above .enter-sequential-background,
.entered .enter-sequential-caption,.entered.from-above .enter-sequential-caption{animation:sequential-reveal .7s ease-out both;}
.entered .enter-sequential-person,.entered.from-above .enter-sequential-person{animation:sequential-person 1.15s cubic-bezier(.2,.75,.3,1) both;}
@keyframes sequential-reveal{from{opacity:0;}to{opacity:1;}}
@keyframes sequential-person{from{opacity:0;transform:translateY(10%);}to{opacity:1;transform:none;}}

/* 仅加快三层山景淡入，人物和文字动作速度保持原样。 */
.entered .enter-sequential-background,.entered.from-above .enter-sequential-background{animation-duration:.4s;}

/* 确认稿装饰：图形独立循环，人物及文字区域保持清楚。 */
.entered .motion-approved-spark{animation:approved-spark 3.2s ease-in-out infinite;}
.entered .motion-approved-heart{animation:approved-heart 3.4s ease-in-out infinite;}
.entered .motion-approved-rings{animation:approved-rings 4.5s ease-in-out infinite;}
.entered .motion-approved-plane{animation:approved-plane 8s ease-in-out infinite both;}
.entered .motion-approved-love{animation:approved-love 6s ease-out infinite both;}
.entered .motion-approved-love-small{animation:approved-love 6.8s ease-out infinite both;}
@keyframes approved-spark{0%,100%{opacity:.4;transform:scale(.8);}50%{opacity:1;transform:scale(1.2);}}
@keyframes approved-heart{0%,50%,100%{transform:scale(1);}20%{transform:scale(1.13);}32%{transform:scale(1.03);}}
@keyframes approved-rings{0%,100%{transform:rotate(-4deg);}50%{transform:rotate(4deg);}}
/* 位移以calc(var(--u) * 750)画布换算，沿现有粉色虚线飞行。 */
@keyframes approved-plane{0%,12%{opacity:0;transform:translate(0,0) rotate(-16deg);}18%{opacity:1;}38%{opacity:1;transform:translate(calc(var(--u) * 70.14),calc(var(--u) * -23.61)) rotate(1deg);}58%{opacity:1;transform:translate(calc(var(--u) * 138.19),calc(var(--u) * -2.08)) rotate(22deg);}70%,100%{opacity:0;transform:translate(calc(var(--u) * 149.31),calc(var(--u) * 5.56)) rotate(26deg);}}
@keyframes approved-love{0%,12%{opacity:0;transform:translate(0,0) scale(.75);}23%{opacity:.8;}65%{opacity:.65;transform:translate(calc(var(--u) * 6.94),calc(var(--u) * -43.06)) scale(1);}83%,100%{opacity:0;transform:translate(calc(var(--u) * 3.47),calc(var(--u) * -64.58)) scale(1.07);}}
