@font-face{font-family:"Dancing Script";src:url("assets/DancingScript.ttf") format("truetype");font-style:normal;font-weight:400 700;font-display:swap}
@font-face{font-family:"Be Vietnam Pro";src:url("assets/BeVietnamPro-Regular.ttf") format("truetype");font-style:normal;font-weight:400;font-display:swap}
@font-face{font-family:"Be Vietnam Pro";src:url("assets/BeVietnamPro-SemiBold.ttf") format("truetype");font-style:normal;font-weight:600;font-display:swap}
:root{--heart:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 22'%3E%3Cpath d='M12 21C10.6 19.8 1 13.6 1 7.2 1 3.7 3.7 1 7 1c2.1 0 3.9 1.1 5 2.8C13.1 2.1 14.9 1 17 1c3.3 0 6 2.7 6 6.2 0 6.4-9.6 12.6-11 13.8z'/%3E%3C/svg%3E");color-scheme:dark;--night:#100a1f;--plum:#21142f;--pink:#f58ab8;--gold:#f3c971;--cream:#fff3de;--ease:cubic-bezier(.22,.8,.22,1)}
body{--balloon:#ff8db4;--balloon-light:#ffd3e2}
body[data-palette="do-hong"]{--balloon:#f0506e;--balloon-light:#ffb7c2}
body[data-palette="tim-mong-mo"]{--balloon:#b98cec;--balloon-light:#eadcff}
body[data-palette="vang-nang"]{--balloon:#ffb24d;--balloon-light:#fff0c4}

*{box-sizing:border-box}
[hidden]{display:none!important}
html,body{width:100%;height:100%;margin:0}
body{min-height:100vh;min-height:100dvh;overflow:hidden;background:var(--night);color:var(--cream);font-family:"Be Vietnam Pro",system-ui,sans-serif;-webkit-font-smoothing:antialiased;overscroll-behavior:none}
button{font:inherit;cursor:pointer}
button:focus-visible{outline:2px solid #ffe3b4;outline-offset:4px}
::selection{background:var(--pink);color:var(--night)}

.sky{position:fixed;inset:0;overflow:hidden;pointer-events:none;background:linear-gradient(180deg,#86689f 0,#d67f9c 30%,#ffb486 52%,#ffd59c 60%,#c06f7c 70%,#3a1d3c 88%,#1c0f1f 100%)}
.sky-parallax{position:absolute;inset:-18px;transform:translate3d(var(--sky-x,0px),var(--sky-y,0px),0);transition:transform .7s var(--ease)}
.sky-art-frame{display:block;width:100%;height:100%}
.sky-art{display:block;width:100%;height:100%;object-fit:cover;object-position:50% 50%;animation:sky-drift 48s ease-in-out infinite alternate}
.sky:after{content:"";position:absolute;inset:0;background:radial-gradient(ellipse 85% 80% at 36% 48%,#100a1f00 58%,#0a061633 100%),linear-gradient(90deg,#100a1f0a 0,transparent 48%,#100a1f14 70%,#100a1f33 100%),linear-gradient(180deg,#100a1f20 0,transparent 17%,transparent 64%,#100a1f14 88%,#100a1f33 100%)}
@keyframes sky-drift{from{transform:scale(1.025) translateX(-.25%)}to{transform:scale(1.045) translateX(.25%)}}
.sky-stage{position:absolute;inset:0}
/* ---- Sunset: the sky and the sun sit behind the cloud sea (assets/sunset-clouds.webp is clear above
   the horizon), so the sun can set behind the clouds page by page. Their frame is the painting's
   rectangle like the stage, but without the drift, so it never repaints: only the sun moves (a small
   layer of its own while it sets) and the three skies cross-fade. */
.sun-frame{position:absolute;inset:0;--frame-h:100vh;background:linear-gradient(180deg,#86689f 0,#b9739c 17%,#e5899a 33%,#fba78a 46%,#ffc58f 54%,#ffdca4 59.5%,#f3b392 61%,#8a4a6c 78%,#2a1530 100%)}
.sun-frame:before,.sun-frame:after{content:"";position:absolute;inset:0;opacity:0;transition:opacity 3.4s var(--ease)}
/* Page 3: rose. Page 5: dusk, the sun gone, violet overhead and a last band of coral at the horizon. */
.sun-frame:before{background:linear-gradient(180deg,#5f5296 0,#93619b 17%,#d0729a 33%,#f28f8e 46%,#ffab8a 54%,#ffc596 59.5%,#e79b8c 61%,#7a3f68 78%,#24122c 100%);opacity:var(--sky-rose,0)}
.sun-frame:after{background:linear-gradient(180deg,#1b1740 0,#2f2559 17%,#58397a 33%,#9a4d82 45%,#d86b82 53%,#f4977f 59%,#cf7c7f 61%,#5c3058 78%,#1a0d22 100%);opacity:var(--sky-dusk,0)}
.sun{position:absolute;z-index:1;left:var(--sun-x,50%);top:0;width:6.6%;aspect-ratio:1;border-radius:50%;translate:-50% calc(var(--frame-h) * var(--sun-y,.5) - 50%);background:radial-gradient(circle closest-side,#fffdf4 0,#fff6dc 50%,#ffe5b2 86%,#ffd79c 96%,#ffd79c00 100%);transition:translate 3.6s cubic-bezier(.45,.05,.3,1)}
/* Halo: wide, low and warm; flattened a little like the light over a cloud sea. */
.sun:before{content:"";position:absolute;left:50%;top:50%;width:900%;height:640%;translate:-50% -50%;border-radius:50%;background:radial-gradient(closest-side,#ffe7b07a 0,#ffcf9a4a 18%,#ffb08c24 42%,#ff9f8a0c 70%,#ff9f8a00 100%)}
/* The stage keeps the painting's 1659:948 ratio, so the clouds, the sun and the sun path line up at any viewport. */
@supports (width:1cqw){
  .sky-parallax{container-type:size}
  .sky-stage,.sun-frame{--stage-w:max(100cqw,100cqh * 1.75);--stage-h:max(100cqh,100cqw * .5714);--frame-h:var(--stage-h);inset:auto;left:calc((100cqw - var(--stage-w)) * var(--sky-focus,.5));top:calc((100cqh - var(--stage-h)) * .5);width:var(--stage-w);height:var(--stage-h)}
  .sky-stage{animation:sky-drift 48s ease-in-out infinite alternate}
  .sky-stage .sky-art{object-fit:fill;animation:none}
}
/* The sun sets as the letter goes on: golden on the first page, rose by the third, below the clouds
   at the end, when the first stars come out and the fairy lights are at their brightest. */
body[data-chapter="1"]{--sun-y:.528;--dusk:.1}
body[data-chapter="2"]{--sun-y:.556;--sky-rose:1;--dusk:.2}
body[data-chapter="3"]{--sun-y:.588;--sky-rose:1;--dusk:.32;--stars-level:.35}
body[data-chapter="4"]{--sun-y:.66;--sky-dusk:1;--dusk:.46;--stars-level:.85;--lantern-dawn:1}
/* Dusk over the clouds too: a violet shade multiplied over the whole sky, heavier overhead. */
.sky:before{content:"";position:absolute;z-index:1;inset:0;pointer-events:none;background:linear-gradient(180deg,#4b3d86 0,#6b4b8c 40%,#8a5a8e 62%,#5a3a6a 100%);mix-blend-mode:multiply;opacity:var(--dusk,0);transition:opacity 3s var(--ease)}
.stars:before{content:"";position:absolute;inset:0;pointer-events:none;background:radial-gradient(ellipse 62% 16% at 38% 61%,#ffd1b45c 0,#ffb9b02b 48%,#ffb9b000 80%),radial-gradient(ellipse 34% 12% at 84% 60%,#ffd9b040 0,#ffd9b000 75%);mix-blend-mode:screen;opacity:var(--lantern-dawn,0);transition:opacity 3.2s var(--ease)}
.stars{position:absolute;inset:0;z-index:1;opacity:var(--stars-level,0);transition:opacity 3s var(--ease)}
.stars i{position:absolute;left:var(--x);top:var(--y);width:var(--size);height:var(--size);border-radius:50%;background:#fff0d6;opacity:.7;animation:star-twinkle var(--duration) ease-in-out var(--delay) infinite alternate}
.stars i:nth-child(8n):after{content:"";position:absolute;left:50%;top:50%;width:7px;height:1px;transform:translate(-50%,-50%);background:linear-gradient(90deg,transparent,#fff0d6,transparent)}
@keyframes star-twinkle{to{opacity:.16;transform:scale(.68)}}
.distant-lanterns{position:absolute;inset:0;z-index:1}
.sky-lantern{position:absolute;left:var(--x);top:var(--y);width:var(--size);height:calc(var(--size)*1.4);opacity:var(--alpha);filter:drop-shadow(0 0 calc(var(--size)*.65) #ff9dbb55);animation:lantern-drift var(--duration) ease-in-out var(--delay) infinite alternate}
.sky-lantern i{position:absolute;left:0;right:0;top:0;height:72%;background:radial-gradient(circle at 34% 30%,#fff 0,var(--balloon-light) 24%,var(--balloon) 72%);-webkit-mask:var(--heart) center/contain no-repeat;mask:var(--heart) center/contain no-repeat;animation:lantern-paper-breathe 4.8s ease-in-out var(--delay) infinite}
.sky-lantern:after,.wish-lantern:after{content:"";position:absolute;left:50%;top:70%;width:1px;height:32%;background:linear-gradient(#fff9,#fff0)}
@keyframes lantern-drift{from{transform:translate3d(-7px,12px,0) rotate(-6deg)}to{transform:translate3d(10px,-24px,0) rotate(5deg)}}
@keyframes lantern-paper-breathe{50%{filter:brightness(1.06)}}
.sky-lantern:before,.wish-lantern:before{content:"";position:absolute;z-index:2;left:calc(100% - 2px);top:-7px;width:clamp(7px,var(--size),12px);height:clamp(7px,var(--size),12px);background:#fff7dc;clip-path:polygon(50% 0,57% 42%,100% 50%,57% 58%,50% 100%,43% 58%,0 50%,43% 42%);filter:drop-shadow(0 0 5px #ffc9d9);opacity:0;animation:lantern-wink 3.6s ease-in-out var(--delay,0s) infinite}
@keyframes lantern-wink{0%,35%,55%,100%{opacity:0;transform:scale(.45)}44%{opacity:.92;transform:scale(1.15)}}
.wish-lantern{position:absolute;left:var(--x);top:var(--y);width:var(--size);height:calc(var(--size)*1.45);opacity:0;will-change:transform,opacity;filter:drop-shadow(0 0 5px #ffd2e0cc) drop-shadow(0 0 16px #ff8fb080)}
.wish-lantern i{position:absolute;left:0;right:0;top:0;height:72%;background:radial-gradient(circle at 34% 30%,#fff 0,var(--balloon-light) 22%,var(--balloon) 72%);-webkit-mask:var(--heart) center/contain no-repeat;mask:var(--heart) center/contain no-repeat;animation:lantern-paper-breathe 4.3s ease-in-out var(--delay) infinite}
.wish-lantern i:before{content:none}

body.is-finale-lanterns .wish-lantern{animation:wish-lantern-rise 2.2s ease-out both;animation-delay:var(--finale-delay,0s)}
@keyframes wish-lantern-rise{0%{opacity:0;transform:translate3d(0,22px,0) scale(.8) rotate(-4deg)}12%{opacity:.78}68%{opacity:.65}100%{opacity:0;transform:translate3d(var(--drift),-58vh,0) scale(.48) rotate(5deg)}}
body.is-page-hidden .sky *{animation-play-state:paused!important}
/* Preview from the sales studio: a mark that always stays on screen, so a preview cannot pass for the real gift. */
.wish-title .emoji,.wish-sign .emoji{-webkit-text-fill-color:currentColor;color:#f797bd;background:none}
.shop-link{font-family:"Playfair Display",Georgia,serif;position:fixed;z-index:30;left:50%;top:max(14px,env(safe-area-inset-top));display:flex;align-items:center;gap:8px;min-height:44px;padding:0 6px 0 18px;border-radius:99px;background:#fff3de;color:#2a1633;font-size:.84rem;font-weight:600;text-decoration:none;white-space:nowrap;box-shadow:0 8px 24px #100a1f80;opacity:0;transform:translate(-50%,-14px);pointer-events:none;transition:opacity .5s ease,transform .6s cubic-bezier(.34,1.56,.64,1)}
.shop-link.is-shown{opacity:1;transform:translate(-50%,0);pointer-events:auto}
.shop-link b{padding:4px 9px;border-radius:99px;background:#f58ab833;color:#a3346a;font-weight:700}
.shop-link b:empty{display:none}
.shop-link.is-shown:active{transform:translate(-50%,0) scale(.97)}
@media (prefers-reduced-motion:reduce){.shop-link{transition:none}}
.motion-gentle *,.motion-gentle *::before,.motion-gentle *::after{animation:none!important;transition:none!important}
.preview-mark.sample-mark{transition:opacity .4s ease;font-family:"Playfair Display",Georgia,serif;font-style:italic;font-weight:500;letter-spacing:.02em;text-transform:none;font-size:.88rem}
.sample-mark.is-gone{opacity:0}
/* The last page of an ordered card: a quiet button to thank the sender. */
/* The last page: the thank-you pill, then the keepsake line on a row of its own (no :has, for older iPhones). */
.card-actions{flex-wrap:wrap;row-gap:2px}
.keep-line{flex-basis:100%}
.keep-line{display:flex;align-items:center;justify-content:center;margin:0;color:#fff1e4b3;font-size:.86rem}
.keep-line[hidden],.keep-line [hidden]{display:none}
.keep-line button{min-height:44px;padding:0 12px;border:0;background:none;color:#fff1e4;font:inherit;font-weight:600;letter-spacing:.01em;text-decoration:underline;text-decoration-color:#f6cf8e99;text-decoration-thickness:1px;text-underline-offset:6px;text-shadow:0 1px 8px #100a1fcc;cursor:pointer;-webkit-tap-highlight-color:transparent}
.keep-line button:disabled{cursor:progress;opacity:.75}
.keep-line button:focus-visible{outline:2px solid #f6cf8e;outline-offset:2px;border-radius:6px}
@media(hover:hover){.keep-line button:not(:disabled):hover{text-decoration-color:#f6cf8e}}
.keep-sheet{position:fixed;inset:0;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:18px;padding:max(20px,env(safe-area-inset-top)) 16px max(20px,env(safe-area-inset-bottom));background:#120a18eb;animation:keep-in .25s ease-out}
.keep-media{display:block;height:min(72vh,860px);max-width:100%;aspect-ratio:9/16;object-fit:contain;border-radius:16px;background:#1c0f1f;box-shadow:0 0 0 1px #f6cf8e33,0 18px 50px #000a}
.keep-actions{display:flex;align-items:center;gap:6px}
.keep-actions .thanks-button{margin-top:0}
.keep-close{min-height:44px;padding:0 14px;border:0;background:none;color:#fff1e4cc;font:inherit;font-size:.86rem;font-weight:600;text-decoration:underline;text-underline-offset:6px;text-decoration-color:#fff1e455;cursor:pointer}
@keyframes keep-in{from{opacity:0}}
@media(prefers-reduced-motion:reduce){.keep-sheet{animation:none}}
.thanks-button{margin-top:4px;background:#2a163399;color:#fff1e4}
.thanks-button.is-done{border-color:#f6cf8e;color:#f6cf8e}
/* The cover of a card that opens later: when, then how long is left. */
.lock-line{margin:6px 0 0;text-align:center;color:#fff1e4;font-size:.9rem;line-height:1.5;text-shadow:0 1px 8px #100a1fb3}
.lock-line b{display:block;font-size:1.02rem;font-weight:700;letter-spacing:.01em}
.lock-line span{color:#f6cf8e;font-variant-numeric:tabular-nums}
.preview-mark{position:fixed;z-index:30;left:50%;top:max(12px,env(safe-area-inset-top));translate:-50% 0;padding:6px 14px;border:1px solid #ffffff3d;border-radius:99px;background:#21142fd9;color:#fff3de;font-size:.7rem;font-weight:600;letter-spacing:.16em;text-transform:uppercase;white-space:nowrap;pointer-events:none;box-shadow:0 4px 16px #100a1f66}
body.is-page-hidden .sky-lantern:before,body.is-page-hidden .wish-lantern:before{animation-play-state:paused!important}

.scene-viewport{position:fixed;z-index:2;left:2%;top:6%;width:66%;height:88%;overflow:visible}
.world-canvas{display:block;width:100%;height:100%;touch-action:none;cursor:grab;outline:none;opacity:0;transition:opacity .48s var(--ease)}
.world-canvas:active{cursor:grabbing}
.world-canvas.is-hovering:not(:active){cursor:pointer}
.scene-viewport.is-ready .world-canvas{opacity:1}
.scene-fallback{position:absolute;inset:0;display:grid;place-items:center;text-align:center;color:#f9dfd9;pointer-events:none;opacity:1;transition:opacity .48s var(--ease),visibility 0s linear}
.scene-viewport.is-ready .scene-fallback{opacity:0;visibility:hidden;transition-delay:0s,.48s}
.scene-poster{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}
.scene-loading{position:absolute;left:50%;bottom:7%;display:flex;align-items:center;gap:9px;transform:translateX(-50%);margin:0;padding:9px 14px;border-radius:100px;background:#20122dd9;color:#fff0df;font-size:.78rem;letter-spacing:.01em;white-space:nowrap;box-shadow:0 8px 24px #100a1f80}
.scene-loading svg{width:15px;height:15px;fill:var(--gold);flex:none;animation:heart-loader 2.8s ease-in-out infinite}
@keyframes heart-loader{0%,100%{transform:rotate(-8deg) scale(.92);opacity:.72}50%{transform:rotate(8deg) scale(1.08);opacity:1}}
/* Fireworks burst in the sky behind the island (the island's silhouette hides what is behind it). */
.fireworks{position:absolute;z-index:1;inset:0;width:100%;height:100%;pointer-events:none}

.story-card{position:fixed;z-index:5;right:clamp(30px,4.7vw,82px);top:50%;transform:translateY(-50%);width:min(390px,31vw);max-height:70dvh;overflow-y:auto;scrollbar-width:none;padding:44px 24px 24px;background:radial-gradient(ellipse 90% 60% at 50% 0,#f58ab81f,#f58ab800 70%),url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 .92 0 0 0 0 .95 0 0 0 .075 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E") 0 0/160px 160px,rgba(16,10,31,.12);backdrop-filter:blur(2px);-webkit-backdrop-filter:blur(2px);border:1px solid rgba(245,138,184,.08);border-radius:24px;text-align:center;transition:border-color .4s ease}
.story-card::-webkit-scrollbar{display:none}
/* Letter dressing: a gilt hairline border, a heart ornament and a soft paper grain. */
.story-card:before{content:"";position:absolute;left:50%;top:16px;width:120px;height:24px;translate:-50% 0;background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 24' fill='none' stroke-linecap='round'%3E%3Cpath d='M4 12h34M82 12h34' stroke='%23f3c971' stroke-opacity='.5' stroke-width='.8'/%3E%3Cpath d='M38 12c4-3 8-3 12 0' stroke='%23f3c971' stroke-opacity='.55'/%3E%3Cpath d='M70 12c4-3 8-3 12 0' stroke='%23f3c971' stroke-opacity='.55'/%3E%3Cpath d='M60 18.2c-.9-.8-6-4.2-6-7.8 0-1.9 1.4-3.3 3.2-3.3 1.2 0 2.2.6 2.8 1.6.6-1 1.6-1.6 2.8-1.6 1.8 0 3.2 1.4 3.2 3.3 0 3.6-5.1 7-6 7.8Z' fill='%23ffd9a8' fill-opacity='.92'/%3E%3Ccircle cx='22' cy='12' r='1.3' fill='%23ffc6dc'/%3E%3Ccircle cx='98' cy='12' r='1.3' fill='%23ffc6dc'/%3E%3C/svg%3E") center/contain no-repeat;pointer-events:none;opacity:.9}
.story-card:after{content:"";position:absolute;inset:0;border-radius:inherit;padding:1px;background:linear-gradient(160deg,#ffc6dc70,#f3c97138 34%,#ffffff08 62%,#f58ab83d);-webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);-webkit-mask-composite:xor;mask:linear-gradient(#000 0 0) content-box exclude,linear-gradient(#000 0 0);pointer-events:none}
.wish-title{--title-glow:drop-shadow(0 2px 3px #10091fcc) drop-shadow(0 0 16px #f58ab840);max-width:15ch;margin:0 auto;color:#f797bd;font-family:"Dancing Script","Brush Script MT",cursive;font-size:clamp(2.5rem,3.6vw,3.75rem);font-weight:700;line-height:1.13;letter-spacing:-.015em;text-wrap:balance;text-shadow:0 2px 3px #10091f;transition:opacity .18s ease,transform .18s var(--ease)}
@supports ((-webkit-background-clip:text) or (background-clip:text)){.wish-title{background:linear-gradient(100deg,#ffc4da 0,#f797bd 42%,#f59ac0 60%,#ffd0a4 100%);-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent;text-shadow:none;filter:var(--title-glow)}}
body[data-chapter="2"] .wish-title,body[data-chapter="3"] .wish-title{font-size:clamp(2.25rem,3.25vw,3.45rem)}
.wish-note{max-width:38ch;margin:14px auto 0;color:#f8e4e9;font-size:1rem;line-height:1.62;text-wrap:balance;text-shadow:0 2px 6px #10091f;transition:opacity .18s ease,transform .18s var(--ease)}
.story-card .wish-title,.story-card .wish-note{transform:translate3d(var(--drag-x,0),0,0);opacity:var(--drag-opacity,1);transition:transform .18s var(--ease),opacity .18s ease}
.story-card.is-dragging .wish-title,.story-card.is-dragging .wish-note{transition:none}
.story-card .wish-title.is-swapping,.story-card .wish-note.is-swapping{opacity:0;transform:translate3d(var(--drag-x,0),7px,0)}
.wish-note:empty{display:none}
.card-actions{display:flex;justify-content:center;margin-top:22px}
body:not([data-chapter="0"]) .card-actions{margin-top:0}
.action-button{position:relative;isolation:isolate;overflow:hidden;display:inline-flex;align-items:center;justify-content:center;gap:10px;min-height:48px;max-width:100%;padding:12px 22px;border:1px solid #f1d6ad8c;border-radius:18px;font-size:.84rem;font-weight:600;line-height:1.4;letter-spacing:.01em;color:#fff0e3;background:linear-gradient(145deg,#64354dd9,#462d50eb);box-shadow:inset 0 1px 0 #ffe9d629,0 5px 14px #100a1f33;transition:transform .18s var(--ease),background .2s,border-color .2s,box-shadow .2s}
.action-button svg{width:20px;height:20px;fill:none;stroke:#f5d5a7;stroke-width:1.65;stroke-linecap:round;stroke-linejoin:round;flex:none}
.action-button:after{content:"";position:absolute;inset:0;z-index:-1;border-radius:inherit;background:linear-gradient(105deg,transparent 25%,#ffe7d612 50%,transparent 75%);transform:translateX(-110%);pointer-events:none}
@media(hover:hover){.action-button:not(:disabled):hover{transform:translateY(-1px);border-color:#f5d9b0bf;background:linear-gradient(145deg,#713c57e0,#503459f0);box-shadow:inset 0 1px 0 #fff0d938,0 7px 18px #100a1f40}}
.action-button:not(:disabled):active{transform:translateY(1px) scale(.98);box-shadow:inset 0 2px 5px #2a142e33}
.action-button.is-tapped:after{animation:gift-shine .4s ease-out}
.action-button:disabled{opacity:.5;cursor:default;transform:none;animation:none;box-shadow:none}
@keyframes gift-shine{from{transform:translateX(-110%)}to{transform:translateX(110%)}}
.rabbit-message{position:fixed;z-index:7;left:0;top:0;transform:translate3d(var(--anchor-x,50vw),var(--anchor-y,80vh),0) translate(-50%,calc(-100% - 14px));transform-origin:50% 100%;width:min(120px,calc(100vw - 28px));box-sizing:border-box;padding:5px 8px;border:1px solid #f58ab8ad;border-radius:14px 12px 13px 11px;background:linear-gradient(145deg,#fff7f3f2,#ffe5f2f2);color:#49263f;font-size:clamp(10px,2.8vw,11px);font-weight:600;line-height:1.25;text-align:center;text-wrap:balance;box-shadow:0 3px 8px #100a1f55;opacity:0;scale:.88;pointer-events:none;transition:opacity .2s ease,scale .2s ease;will-change:transform,opacity}
.rabbit-message:before,.rabbit-message:after{content:"";position:absolute;left:var(--tail-x,50%);border:1px solid #f58ab8ad;border-radius:50%;background:#ffeaf3;transform:translateX(-50%)}
.rabbit-message:before{bottom:-5px;width:6px;height:6px}
.rabbit-message:after{bottom:-10px;width:3px;height:3px}
.rabbit-message.is-thinking{color:#c85989;letter-spacing:.16em;animation:thought-pulse .4s ease-in-out infinite alternate}
.rabbit-message.is-visible{opacity:1;scale:1}
.rabbit-message.is-occluded{opacity:0;scale:.96}
.rabbit-message.is-flipped{transform:translate3d(var(--anchor-x,50vw),var(--anchor-y,80vh),0) translate(-50%,14px);transform-origin:50% 0}
.rabbit-message.is-flipped:before{top:-5px;bottom:auto}
.rabbit-message.is-flipped:after{top:-10px;bottom:auto}
@keyframes thought-pulse{to{opacity:.55}}
.no-script{position:fixed;z-index:9;inset:auto 20px 20px;margin:0;padding:15px;background:#2b193c;color:var(--cream);font-size:.8rem}
.no-script a{color:var(--gold)}
.chapter-dots{display:flex;justify-content:center;gap:0;margin-top:8px}
.chapter-dot{position:relative;flex:0 0 44px;width:44px;height:44px;border:1px solid transparent;border-radius:50%;padding:0;background:transparent;cursor:pointer;transition:background .2s,border-color .2s,transform .18s var(--ease)}
.chapter-dot:before{content:"";position:absolute;left:50%;top:50%;width:13px;height:12px;translate:-50% -46%;background:linear-gradient(180deg,#a0607b,#6d3a53);-webkit-mask:var(--heart) center/contain no-repeat;mask:var(--heart) center/contain no-repeat;transition:background .25s,scale .25s}
.chapter-dot:after{content:none}
.chapter-dot:has(~ .chapter-dot.is-active):before{background:linear-gradient(180deg,#eab0c3,#b8718f)}
.chapter-dot.is-active{background:#62364b40;border-color:#e7c79b70}
.chapter-dot.is-active:before{background:linear-gradient(180deg,#fff1d6,#f5a9c2);scale:1.3}

@media(hover:hover){.chapter-dot:hover{background:#dba5bd1f;border-color:#e7c79b55}}
.chapter-dot:active{transform:scale(.93)}
body[data-chapter="0"] .chapter-dots{visibility:hidden;pointer-events:none}
.wish-title.is-entering{animation:title-slide-in .5s var(--ease) both}
.wish-note.is-entering{animation:text-slide-in .38s var(--ease) .12s both}
@keyframes text-slide-in{from{opacity:0;transform:translate3d(0,16px,0);filter:blur(4px)}to{opacity:1;transform:none;filter:blur(0)}}
@keyframes title-slide-in{from{opacity:0;transform:translate3d(0,16px,0);filter:blur(6px) var(--title-glow)}to{opacity:1;transform:none;filter:blur(0) var(--title-glow)}}
.story-card.is-dragging{transform:translateY(-50%) perspective(800px) rotateY(calc(var(--drag-x,0) * .12));transition:none;user-select:none}

@media(max-width:900px){
  .scene-viewport{left:0;top:max(7%,env(safe-area-inset-top));width:100%;height:61dvh}
  .story-card{left:0;right:0;top:auto;bottom:0;transform:none;width:100%;max-height:49dvh;min-height:36dvh;padding:clamp(38px,5.5dvh,52px) max(23px,env(safe-area-inset-left) + 20px) max(20px,env(safe-area-inset-bottom) + 15px) max(23px,env(safe-area-inset-right) + 20px);background:linear-gradient(180deg,#100a1f00 0,#100a1f1f 24%,#100a1f40 58%,#100a1f2e 100%);backdrop-filter:none;-webkit-backdrop-filter:none;border:none;border-radius:28px 28px 0 0;touch-action:pan-y}
  .story-card:after{display:none}
  .story-card:before{top:clamp(12px,2.2dvh,20px)}
  .story-card.is-dragging{transform:perspective(600px) rotateY(calc(var(--drag-x,0) * .15))}
  .wish-title{font-size:clamp(2.18rem,8.1vw,3.15rem);line-height:1.13}
  body[data-chapter="2"] .wish-title,body[data-chapter="3"] .wish-title{font-size:clamp(2.08rem,7.6vw,3rem)}
  .wish-note{margin-top:11px;font-size:.95rem}
  .card-actions{margin-top:18px}
  .sky-art{object-position:58.5% 50%}
  .sky-stage,.sun-frame{--sky-focus:.585}
  .sky:after{background:linear-gradient(180deg,#100a1f20 0,transparent 13%,transparent 48%,#100a1f08 64%,#100a1f1a 82%,#100a1f33 100%)}
  .sky-lantern:nth-child(n+5){display:none}
}
/* Touch devices render without the post pass, so the canvas edge is feathered in CSS instead. */
@media(max-width:900px) and (pointer:coarse){
  .world-canvas{-webkit-mask-image:linear-gradient(180deg,#000 84%,#0000 100%);mask-image:linear-gradient(180deg,#000 84%,#0000 100%)}
}
@media(max-width:390px){
  .story-card{padding-inline:21px;max-height:54dvh}
  .wish-title{font-size:clamp(2.13rem,9vw,2.6rem)}
  body[data-chapter="2"] .wish-title,body[data-chapter="3"] .wish-title{font-size:clamp(1.95rem,8vw,2.38rem)}
  .action-button{min-height:48px;font-size:.82rem;padding:11px 20px}
}
@media(max-height:650px){
  .scene-viewport{top:1%;height:56dvh}
  .story-card{max-height:52dvh;min-height:39dvh;padding-top:34px}
  .story-card:before{top:8px;height:20px}
  .wish-title{font-size:clamp(1.95rem,7vw,2.85rem)}
  body[data-chapter="2"] .wish-title,body[data-chapter="3"] .wish-title{font-size:clamp(1.8rem,6.5vw,2.5rem)}
  .wish-note{font-size:.9rem}
}
@media(max-width:900px) and (orientation:landscape){
  .scene-viewport{left:0;top:0;width:57%;height:100dvh}
  .story-card{left:auto;right:0;top:0;bottom:0;width:48%;max-height:100dvh;min-height:100dvh;padding:clamp(32px,8dvh,60px) max(25px,env(safe-area-inset-right) + 18px) max(18px,env(safe-area-inset-bottom) + 12px) 42px;background:linear-gradient(90deg,#100a1f00 0,#100a1f1f 24%,#100a1f40 64%,#100a1f2e 100%)}
  .wish-title{font-size:clamp(1.8rem,5vw,2.65rem)}
  .wish-note{font-size:.88rem;line-height:1.5}
}
body.is-finale .wish-title{--title-glow:drop-shadow(0 2px 3px #10091fcc) drop-shadow(0 0 22px #ffb97a66)}
@media(prefers-reduced-motion:reduce){
  *,*:before,*:after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
  .fireworks{display:none}
  .sky-parallax{transform:none}
  .sky-art{animation:none;transform:scale(1.025)}
  .sky-stage{animation:none;transform:scale(1.025)}
  .sky-stage .sky-art{transform:none}
  .wish-lantern{display:none}
  .sky-lantern:before,.wish-lantern:before{animation:none!important;opacity:.42;transform:scale(.7)}
}

/* Primary "open the gift" button: a muted plum-rose pill with a soft gilt rim and a lit lantern badge. */
.action-button.action-button--primary{gap:12px;min-height:52px;padding:9px 30px;border:1.5px solid transparent;border-radius:999px;color:#fff1e4;font-size:.9rem;font-weight:700;letter-spacing:.02em;text-shadow:0 1px 2px #3a14286b;background:linear-gradient(180deg,#a8698a 0,#8b4e70 52%,#6d3a5b 100%) padding-box,linear-gradient(120deg,#f3dcae,#c79b62 30%,#e8c7b0 55%,#b8864f 80%,#efd4a4) border-box;box-shadow:inset 0 1px 0 #ffe3ef59,inset 0 -3px 8px #3c16304d,0 6px 18px #100a1f59,0 0 18px #f58ab81f}
/* The breathing glow lives on a layer hugging the button and only its opacity animates, so the
   button is never repainted per frame (an animated box-shadow repaints on every frame). */
.card-actions{position:relative;width:fit-content;margin-left:auto;margin-right:auto}
.card-actions:has(#openGift:not([hidden])):before{content:"";position:absolute;z-index:0;inset:0;border-radius:999px;box-shadow:0 0 26px #f58ab838;opacity:0;pointer-events:none;animation:gift-glow 3.6s ease-in-out infinite}
.card-actions .action-button{z-index:1}
.action-button.action-button--primary:before{content:"";position:absolute;z-index:-1;left:8px;right:8px;top:2px;height:45%;border-radius:999px;background:linear-gradient(180deg,#ffffff2e,#ffffff00);pointer-events:none}
.action-button.action-button--primary:after{inset:0;z-index:-1;border-radius:inherit;background:linear-gradient(105deg,transparent 34%,#ffe6f029 50%,transparent 66%);transform:translateX(-130%);animation:gift-sheen 5.5s ease-in-out 1.6s infinite}
.action-button.action-button--primary svg{width:36px;height:36px;padding:4px;border-radius:50%;stroke:#f6cf8e;stroke-width:1.5;background:radial-gradient(circle at 50% 58%,#7a3a4c 0,#4b2440 70%,#3a1c34 100%);box-shadow:inset 0 0 0 1px #f3c97159,0 0 12px #ffb46e40;filter:drop-shadow(0 0 3px #ffb46e66)}
@media(hover:hover){.action-button.action-button--primary:not(:disabled):hover{transform:translateY(-2px);border-color:transparent;background:linear-gradient(180deg,#b57596 0,#98597c 52%,#784265 100%) padding-box,linear-gradient(120deg,#f7e3b8,#d4a86c 30%,#efd2bd 55%,#c79358 80%,#f4dcae) border-box;box-shadow:inset 0 1px 0 #ffe3ef66,inset 0 -3px 8px #3c16304d,0 10px 24px #100a1f66,0 0 24px #f58ab840}}
.action-button.action-button--primary:not(:disabled):active{transform:translateY(1px) scale(.97);box-shadow:inset 0 2px 6px #2e10245e,0 3px 10px #100a1f4d}
.action-button.action-button--primary.is-tapped:after{animation:gift-shine .45s ease-out}
@keyframes gift-glow{0%,100%{opacity:0}50%{opacity:1}}
@keyframes gift-sheen{0%,74%{transform:translateX(-130%)}100%{transform:translateX(130%)}}
@media(prefers-reduced-motion:reduce){.action-button.action-button--primary,.action-button.action-button--primary:after,.card-actions:before{animation:none}}

/* ---- Sky life (skyfx.js). Every continuous motion here is transform/opacity on its own layer, and no
   new layer uses a blend mode: pale light at low alpha composites almost exactly like "screen" does. */
.sky{--tilt-x:0;--tilt-y:0}
/* Mouse / device-tilt parallax, pivoting on the island: the far sky shifts most, nearer layers less. */
.sky-stage,.sun-frame,.stars{translate:calc(var(--tilt-x) * 9px) calc(var(--tilt-y) * 6px)}
.distant-lanterns{translate:calc(var(--tilt-x) * 5px) calc(var(--tilt-y) * 3.5px)}
/* 7. First stars: painted once, out only at dusk. */
.sky-starfield{position:absolute;inset:0;pointer-events:none;contain:content;opacity:var(--stars-level,0);transition:opacity 3s var(--ease)}
.sky-stars{position:absolute;pointer-events:none}
/* Still stars are drawn into the painting layer once decoded; only the band shimmer is a layer. */
.sky-stars--still{inset:0;width:100%;height:100%;opacity:0;transition:opacity .8s var(--ease)}
.sky-stars--still.is-ready{opacity:1}
/* 1. Cloud wisps: each strip holds two identical tiles and slides by exactly one tile, so it loops seamlessly. */
.sky-wisps{position:absolute;inset:0;overflow:hidden;pointer-events:none;contain:content}
.sky-wisp{position:absolute;left:0;width:200%;animation:wisp-drift 200s linear infinite}
.sky-wisp--far{opacity:.9;translate:calc(var(--tilt-x) * -2px) 0}
.sky-wisp--mid{opacity:.85;translate:calc(var(--tilt-x) * -4px) calc(var(--tilt-y) * -1px)}
.sky-wisp--near{opacity:.72;translate:calc(var(--tilt-x) * -6px) calc(var(--tilt-y) * -2px)}
.sky-wisp--high{opacity:.78}
@keyframes wisp-drift{from{transform:translate3d(0,0,0)}to{transform:translate3d(-50%,0,0)}}
/* Layers pinned to the sun need the stage to keep the painting's 1659:948 ratio. */
.sun-path{display:none}
@supports (width:1cqw){
  /* 2. Sun path: a warm glow and two glitter layers on the cloud sea, straight below the sun. */
  /* The level is applied to each child: an opacity on the box itself would make it a layer too. */
  .sun-path{display:block;position:absolute;left:var(--sun-x,50%);top:59.4%;width:26%;height:40.6%;translate:-50% 0;pointer-events:none;--path-level:.85}
  .sun-path canvas,.sun-path img{position:absolute;inset:0;width:100%;height:100%}
  .sun-path-still{opacity:0;transition:opacity 2.4s var(--ease)}
  .sun-path-still.is-ready{opacity:var(--path-level)}
  .sun-path-glitter{opacity:calc(.5 * var(--path-level));animation:glitter-b 4.6s ease-in-out -1.7s infinite alternate}
}
@keyframes glitter-b{from{opacity:calc(.15 * var(--path-level))}to{opacity:calc(.9 * var(--path-level))}}
/* The path fades as the sun goes down: a last glow on the clouds at dusk. */
body[data-chapter="3"] .sun-path{--path-level:.6}
body[data-chapter="4"] .sun-path{--path-level:.32}
/* Meteors and bird flights (sky-life) and the firework flash sit above the stars, below the tint. */
.sky-life{position:absolute;inset:0;z-index:1;overflow:hidden;pointer-events:none;contain:content}
.meteor{position:absolute;left:0;top:0;width:var(--len);height:2px;margin-top:-1px;border-radius:2px;transform-origin:0 50%;background:linear-gradient(270deg,#fffaf2 0,#ffe9f2d9 6%,#ffd4e666 38%,#ffd4e600 100%);opacity:0}
.meteor:after{content:"";position:absolute;right:-3px;top:50%;width:7px;height:7px;margin-top:-3.5px;border-radius:50%;background:radial-gradient(circle,#fff 0,#fff3f8b3 38%,#ffd9ea00 72%)}
.bird-flock{position:absolute;left:0;top:0;opacity:.88}
.bird{position:absolute;width:var(--size);height:calc(var(--size) * .5);animation:bird-bob 2.4s ease-in-out var(--bob-delay,0s) infinite alternate}
.bird svg{display:block;width:100%;height:100%;overflow:visible}
.bird-flock.is-leftward .bird svg{scale:-1 1}
/* Birds flying home: soft plum silhouettes against the sunset, the far wing a little lighter. */
.bird-body,.bird-wing{fill:#4a2440}
.bird-wing--far{fill:#6e3a5c}
.bird-wing{transform-box:view-box;transform-origin:61px 27px;animation:bird-flap var(--flap,1s) ease-in-out var(--flap-delay,0s) infinite}
.bird-wing--far{animation-delay:calc(var(--flap-delay,0s) - .08s)}
@keyframes bird-flap{0%,100%{transform:scaleY(1)}50%{transform:scaleY(-.5)}}
@keyframes bird-bob{from{translate:0 calc(var(--size) * -.05)}to{translate:0 calc(var(--size) * .05)}}
.sky-flash{position:absolute;inset:0;z-index:1;pointer-events:none;opacity:0;background:radial-gradient(circle 55vmax at var(--fx,50%) var(--fy,30%),var(--fc,#ffd9e8) 0,#ffd9e800 70%)}
/* 9. Foreground lens: out-of-focus petals and bokeh drifting in front of the island, behind the card. */
.lens-fx{position:fixed;z-index:4;inset:0;overflow:hidden;pointer-events:none;contain:content}
.lens-item{position:absolute;left:0;top:0;background:center/100% 100% no-repeat;opacity:0}

/* ---- 10. Opening: the loading heart fills, then a veil of sunset clouds parts while the camera descends. */
.intro-veil{display:none}
.js .intro-veil{display:block;position:absolute;inset:-10% -8%;pointer-events:none}
.intro-veil:before{content:"";position:absolute;inset:12% 10%;border-radius:50%;background:radial-gradient(closest-side,#9a5a7af0 0,#b46d84d9 52%,#c97e8a80 78%,#c97e8a00 100%);transition:opacity 1.5s ease .15s}
.veil-cloud{position:absolute;width:80%;height:76%;background:radial-gradient(closest-side at 36% 40%,#ffe0c4 0,#f9c2b0e6 36%,#f9c2b000 100%),radial-gradient(closest-side at 66% 50%,#e99aa4f0 0,#d9849bd9 40%,#d9849b00 100%),radial-gradient(closest-side at 48% 70%,#9a5a80f0 0,#7e4870d9 44%,#7e487000 100%);will-change:transform,opacity;transition:transform 2.6s cubic-bezier(.6,.02,.24,1),opacity 1.9s ease .5s;animation:veil-breathe 7s ease-in-out infinite alternate}
.veil-cloud--a{left:-16%;top:-12%;--bx:1.5%;--by:1%}
.veil-cloud--b{right:-18%;top:-8%;--bx:-1.5%;--by:1.2%;animation-delay:-2s}
.veil-cloud--c{left:-12%;bottom:-14%;--bx:1.2%;--by:-1%;animation-delay:-4s}
.veil-cloud--d{right:-14%;bottom:-10%;--bx:-1.2%;--by:-1.4%;animation-delay:-5.5s}
@keyframes veil-breathe{to{translate:var(--bx) var(--by)}}
.intro-heart{position:absolute;left:50%;top:47%;width:clamp(46px,7.5vmin,70px);aspect-ratio:1;translate:-50% -50%;overflow:hidden;background:radial-gradient(circle at 40% 34%,#fff6f2 0,#ffc8d6 46%,#ef89a8 100%);-webkit-mask:var(--heart) center/contain no-repeat;mask:var(--heart) center/contain no-repeat;transition:opacity .8s var(--ease),scale .9s var(--ease);animation:intro-heart-float 3.2s ease-in-out infinite alternate}
/* The shade slides off the heart as loading progresses, until it is full. */
.intro-heart i{position:absolute;inset:-3%;border-radius:50%;background:#2c1f3be8;translate:calc(var(--phase,.12) * -108%) 0;transition:translate 1s var(--ease)}
@keyframes intro-heart-float{from{transform:translateY(-3px)}to{transform:translateY(3px)}}
.scene-viewport.is-revealing .intro-veil:before,.scene-viewport.is-revealing .veil-cloud{opacity:0}
.scene-viewport.is-revealing .veil-cloud--a{transform:translate(-58%,-42%) scale(1.18)}
.scene-viewport.is-revealing .veil-cloud--b{transform:translate(60%,-40%) scale(1.18)}
.scene-viewport.is-revealing .veil-cloud--c{transform:translate(-56%,44%) scale(1.14)}
.scene-viewport.is-revealing .veil-cloud--d{transform:translate(58%,46%) scale(1.14)}
.scene-viewport.is-revealing .intro-heart{opacity:0;scale:1.35}
.scene-viewport.is-revealed .intro-veil{display:none}
/* With scripts the veil and its heart replace the poster and the small loader icon. */
.js .scene-poster,.js .scene-loading svg{display:none}

/* ---- Handwritten wish: the title is written word by word behind a glowing pen tip; the note fades in. */
/* Hidden until the writer takes over, but shown after 3 s whatever happens, so the text is never lost. */
.js:not(.ink-ready) .wish-title,.js:not(.ink-ready) .wish-note{opacity:0;animation:ink-failsafe 0s 3s forwards}
@keyframes ink-failsafe{to{opacity:1}}
/* Padding keeps the script swashes inside each word mask; the negative margin keeps the layout. */
.ink-word{display:inline-block;padding:.1em .14em .14em;margin:-.1em -.14em -.14em;-webkit-mask-image:linear-gradient(90deg,#000 0 40%,#0000 60% 100%);mask-image:linear-gradient(90deg,#000 0 40%,#0000 60% 100%);-webkit-mask-size:250% 100%;mask-size:250% 100%;-webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;-webkit-mask-position:100% 0;mask-position:100% 0;animation:ink-write .42s cubic-bezier(.42,.08,.36,1) both}
@keyframes ink-write{to{-webkit-mask-position:0 0;mask-position:0 0}}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  /* Each word carries the title gradient (aligned by ink.js), since the parent cannot paint through the masks. */
  .wish-title.is-inked{background:none}
  .wish-title.is-inked .ink-word{background-image:linear-gradient(100deg,#ffc4da 0,#f797bd 42%,#f59ac0 60%,#ffd0a4 100%);background-repeat:no-repeat;-webkit-background-clip:text;background-clip:text}
}
.ink-fade{display:inline-block;animation:ink-fade .56s var(--ease) both}
@keyframes ink-fade{from{opacity:0;transform:translate3d(0,.4em,0)}to{opacity:1;transform:none}}
.ink-tip{position:absolute;left:0;top:0;z-index:1;width:8px;height:8px;margin:-4px 0 0 -4px;border-radius:50%;background:radial-gradient(circle,#fffbef 0,#ffe3b8 38%,#ffc38a00 72%);box-shadow:0 0 9px 2px #ffd29a8c,0 0 20px 5px #f58ab840;opacity:0;pointer-events:none}

@media(prefers-reduced-motion:reduce){
  .sky-stage,.stars,.distant-lanterns,.sky-wisp{translate:none}
  .sky-wisp,.veil-cloud,.intro-heart{animation:none}
  .sun{transition:none}
  .sky-life,.sky-flash,.lens-fx,.ink-tip{display:none}
}

/* ---- Letter (letter.js): a sealed envelope on the first page, page turns, a handwritten signature. */
.letter-envelope{position:absolute;left:0;right:0;top:0;height:0;z-index:2;pointer-events:none;display:none}
body[data-chapter="0"] .letter-envelope,body.is-opening .letter-envelope{display:block}
/* The seal replaces the heart ornament on the envelope; the ornament returns once it is open. */
.story-card:before{transition:opacity .7s var(--ease) .5s}
body[data-chapter="0"]:not(.is-opening) .story-card:before{opacity:0;transition-delay:0s}
.letter-flap{position:absolute;left:12px;right:12px;top:6px;height:30px;transform-origin:50% 0}
.letter-flap svg{display:block;width:100%;height:100%;overflow:visible}
.letter-flap-fill{fill:#f58ab80b}
.letter-flap-line{fill:none;stroke:#f3c97170;stroke-width:1;vector-effect:non-scaling-stroke}
.letter-seal{position:absolute;left:50%;top:11px;width:38px;height:38px;translate:-50% 0;filter:drop-shadow(0 2px 3px #24081c99)}
.letter-seal-half{position:absolute;inset:0}
.letter-seal-half svg{display:block;width:100%;height:100%}
.letter-seal-half--a{clip-path:polygon(0 0,55% 0,45% 36%,57% 63%,47% 100%,0 100%)}
.letter-seal-half--b{clip-path:polygon(55% 0,100% 0,100% 100%,47% 100%,57% 63%,45% 36%)}
.seal-wax{fill:#a83a62}
.seal-rim{fill:none;stroke:#f19ab6;stroke-opacity:.5;stroke-width:1.3}
.seal-heart,.seal-star{fill:#f7d9ad}
body.is-opening .letter-seal-half--a{animation:seal-crack-a .95s cubic-bezier(.3,.6,.3,1) forwards}
body.is-opening .letter-seal-half--b{animation:seal-crack-b .95s cubic-bezier(.3,.6,.3,1) forwards}
body.is-opening .letter-flap{animation:flap-open 1s cubic-bezier(.5,.1,.3,1) .18s forwards}
@keyframes seal-crack-a{0%{transform:none;opacity:1}22%{transform:translate(-1.5px,0) rotate(-5deg);opacity:1}100%{transform:translate(-30px,18px) rotate(-42deg);opacity:0}}
@keyframes seal-crack-b{0%{transform:none;opacity:1}22%{transform:translate(1.5px,0) rotate(5deg);opacity:1}100%{transform:translate(30px,20px) rotate(38deg);opacity:0}}
@keyframes flap-open{0%{transform:perspective(520px) rotateX(0);opacity:1}55%{opacity:.85}100%{transform:perspective(520px) rotateX(168deg);opacity:0}}
/* A translucent sheet sweeping over the card when the page changes (animated by letter.js). */
.letter-page{position:absolute;inset:0;z-index:1;border-radius:inherit;pointer-events:none;opacity:0;background:linear-gradient(90deg,#fff4f800 0,#fff4f81c 36%,#ffffff38 50%,#fff4f817 64%,#fff4f800 100%);box-shadow:inset 0 0 0 1px #f3c9712b}
/* Signature under the last page, written by the ink pen like the title. */
.wish-sign{width:fit-content;margin:6px 10% 0 auto;color:#f7a6c6;font-family:"Dancing Script","Brush Script MT",cursive;font-size:clamp(1.45rem,2.2vw,1.85rem);font-weight:700;line-height:1.25;text-shadow:0 2px 3px #10091f;filter:drop-shadow(0 0 10px #f58ab833)}
.wish-sign:empty{display:none}
@supports ((-webkit-background-clip:text) or (background-clip:text)){
  .wish-sign{background:linear-gradient(100deg,#ffd0e2 0,#f797bd 55%,#ffd0a4 100%);-webkit-background-clip:text;background-clip:text;color:transparent;-webkit-text-fill-color:transparent;text-shadow:none}
  .wish-sign.is-inked{background:none}
  .wish-sign.is-inked .ink-word{background-image:linear-gradient(100deg,#ffd0e2 0,#f797bd 55%,#ffd0a4 100%);background-repeat:no-repeat;-webkit-background-clip:text;background-clip:text}
}
@media(max-width:900px){
  .wish-sign{font-size:clamp(1.3rem,5.4vw,1.7rem);margin-top:4px}
  .letter-seal{top:calc(clamp(12px,2.2dvh,20px) - 6px)}
}
@media(prefers-reduced-motion:reduce){
  .letter-page{display:none}
}
