/* Dear Dilraba — design A, "the unsigned letter".
   A letter found at night: a cream envelope on deep night ink, opened into ruled letter paper.
   The cover's iridescent colours appear only as small glints: the wax seal, the line being sung,
   the waveform, and the pen tip that signs the letter after a hello.

   Plain CSS for old in-app browsers (WeChat / Weibo): no nesting, no :has(), no container queries.
   States live on <html>: .js (script running) · .sealed (envelope closed) · .opening · .opened. */

/* ---------- Fonts (subset by tools/subset-fonts.py; licences in fonts/LICENSES.txt) ---------- */
@font-face {
  font-family: "DD WenKai";
  src: url("fonts/wenkai.woff2") format("woff2");
  font-display: swap;
}
/* The handwriting: one family per language, one file each (--hand switches with <html lang>).
   Chinese writes with Long Cang alone, so even the … in 寄送中… is Long Cang's, and a Chinese-mode
   visitor never downloads Caveat. (When both files shared one family split by unicode-range, that
   … fell in Caveat's range and pulled in the 41 KB Latin font the moment a letter was sent.)
   English writes with Caveat; U+0020 stays out of its range, as before, so spaces keep coming
   from the next font in the stack. */
@font-face {
  font-family: "DD Hand ZH";
  src: url("fonts/longcang.woff2") format("woff2");
  font-display: swap;
}
@font-face {
  font-family: "DD Hand EN";
  src: url("fonts/caveat.woff2") format("woff2");
  font-display: swap;
  unicode-range: U+0021-024F, U+2000-206F, U+20A0-20CF;
}

/* ---------- Tokens ---------- */
:root {
  --night: #191744;        /* deep night ink, echoes the cover's navy type */
  --night-deep: #100E2E;
  --night-lift: #2A2566;
  --moon: #F3EDE0;         /* cream text on night */
  --moon-soft: rgba(243, 237, 224, 0.68);   /* 6.9:1 on --night */
  --paper: #FBF7EE;
  --envelope: #F3E9D5;
  --envelope-deep: #E8DABD;
  --envelope-inside: #D5C29E;
  --ink: #1F1C57;          /* blue-black ink: 14.4:1 on paper */
  --ink-2: #5B5890;        /* secondary ink: 6.1:1 on paper, 5.5:1 on the card */
  --ink-faint: #6A6699;    /* lines not yet sung: 4.7:1 or more on every paper tone, still readable */
  --rule: #E3DCF0;         /* letter-paper rules, from the cover's lavender */
  --card: #ECE7FA;         /* the reply postcard, the cover's lavender */
  --rose: #9B2453;         /* errors: 7.0:1 on the card */
  --g1: #FF9A3C;           /* the cover's glass: orange, pink, magenta, deep blue, cyan */
  --g2: #FF5AA5;
  --g3: #C83CE0;
  --g4: #4C3BEA;
  --g5: #38C9F2;
  --iris: linear-gradient(90deg, #FF9A3C, #FF5AA5 26%, #C83CE0 50%, #4C3BEA 74%, #38C9F2);
  --text: "DD WenKai", "PingFang SC", "Hiragino Sans GB", "Noto Sans CJK SC", "Microsoft YaHei", sans-serif;
  --hand: "DD Hand ZH", "Kaiti SC", "STKaiti", "KaiTi", "DD WenKai", cursive;
  --gutter: 14px;
  --lh: 36px;              /* one ruled line on the letter paper */
  --lyric: 18px;
}
html[lang="en"] { --hand: "DD Hand EN", "Kaiti SC", "STKaiti", "KaiTi", "DD WenKai", cursive; }
@media (min-width: 700px) {
  :root { --gutter: 32px; --lh: 42px; --lyric: 21px; }
}

/* ---------- Base ---------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  background: var(--night);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}
body {
  margin: 0;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--night);
  color: var(--moon);
  font-family: var(--text);
  font-size: 16px;
  line-height: 1.7;
  overflow-x: hidden;
}
h1, h2, p { margin: 0; font-weight: 400; }
button, input, textarea { font: inherit; color: inherit; }
button { cursor: pointer; }
a { color: inherit; }
img { display: block; max-width: 100%; }
[hidden] { display: none !important; }

.vh {
  position: absolute !important;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  -webkit-clip-path: inset(50%);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Focus: always visible for keyboards; hidden only where the browser knows it was a tap.
   (Old browsers without :focus-visible drop the second rule and keep the outline.) */
:focus { outline: 2px solid var(--moon); outline-offset: 3px; }
:focus:not(:focus-visible) { outline: none; }
.paper :focus, .card :focus, .mini :focus { outline-color: var(--ink); }
.mini :focus { outline-color: var(--moon); }

/* ---------- The night sky: a lamp-warm glow and a few glints of the cover's glass ---------- */
.sky {
  position: fixed;
  top: 0; right: 0; bottom: 0; left: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 70% 48% at 50% 40%, rgba(70, 60, 160, 0.55), rgba(70, 60, 160, 0) 70%),
    radial-gradient(ellipse 120% 80% at 50% 110%, rgba(16, 14, 46, 0.9), rgba(16, 14, 46, 0) 60%),
    var(--night);
}
.glint {
  position: absolute;
  width: 10px; height: 10px;
  filter: drop-shadow(0 0 4px currentColor) drop-shadow(0 0 10px currentColor);
  opacity: 0.85;
}
.glint::before {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background: currentColor;
  -webkit-clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
.g1 { top: 15%; left: 11%; width: 9px; height: 9px; color: var(--g1); }
.g2 { top: 23%; right: 13%; width: 14px; height: 14px; color: var(--g2); }
.g3 { top: 66%; left: 7%; width: 8px; height: 8px; color: var(--g5); }
.g4 { top: 77%; right: 9%; width: 11px; height: 11px; color: #9E8BFF; }
.g5 { top: 8%; left: 60%; width: 6px; height: 6px; color: #FFD3E8; opacity: 0.6; }
.g6 { top: 88%; left: 28%; width: 7px; height: 7px; color: var(--g3); opacity: 0.7; }
@media (prefers-reduced-motion: no-preference) {
  .g2 { animation: twinkle 7s ease-in-out infinite; }
  .g3 { animation: twinkle 9s ease-in-out 2s infinite; }
}
@keyframes twinkle {
  0%, 100% { opacity: 0.85; transform: scale(1); }
  50% { opacity: 0.3; transform: scale(0.7); }
}

/* Everything sits above the sky. */
.bar, .main, .foot { position: relative; z-index: 1; }
.main {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
}

/* ---------- Top bar: the language toggle, top right ---------- */
.bar {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  min-height: 60px;
  padding: 8px var(--gutter) 4px;
}
.lang {
  min-width: 56px;
  min-height: 44px;
  padding: 0 16px;
  border: 1px solid rgba(243, 237, 224, 0.34);
  border-radius: 22px;
  background: rgba(16, 14, 46, 0.35);
  color: var(--moon);
  font-family: var(--text);
  font-size: 15px;
  letter-spacing: 0.06em;
  line-height: 1;
}
.lang:hover { border-color: rgba(243, 237, 224, 0.7); }

/* ---------- 1. The envelope ---------- */
.hero {
  flex: 1 0 auto;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 4px 20px 20px;
  text-align: center;
}
.envelope {
  --ew: 300px;                     /* envelope width (fluid below, where min() is supported) */
  --eh: calc(var(--ew) * 0.64);    /* envelope height */
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0 0 6px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--moon);
}
.envelope:focus { outline: none; }
.envelope:focus-visible .env { outline: 2px solid var(--moon); outline-offset: 8px; }

.env {
  position: relative;
  display: block;
  width: var(--ew);
  height: var(--eh);
  filter: drop-shadow(0 22px 30px rgba(6, 4, 30, 0.55)) drop-shadow(0 2px 3px rgba(6, 4, 30, 0.35));
}
.env-back, .env-letter, .env-pocket, .env-flap { position: absolute; display: block; }
.env-back {
  top: 0; right: 0; bottom: 0; left: 0;
  border-radius: 3px;
  background: linear-gradient(180deg, var(--envelope-inside), #E2D2B3);
}
.env-letter {
  top: 5%; left: 5%; right: 5%; bottom: 3%;
  z-index: 2;
  border-radius: 2px;
  background:
    repeating-linear-gradient(180deg, rgba(227, 220, 240, 0) 0 11px, var(--rule) 11px 12px) 0 18px / 100% 100% no-repeat,
    var(--paper);
  box-shadow: 0 -1px 0 rgba(255, 255, 255, 0.6) inset;
}
.env-pocket {
  top: 0; right: 0; bottom: 0; left: 0;
  z-index: 3;
  border-radius: 3px;
  background: 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='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .45 0 0 0 0 .35 0 0 0 0 .2 0 0 0 .06 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"), linear-gradient(180deg, #F6EEDD 0%, var(--envelope) 45%, var(--envelope-deep) 100%);
  -webkit-clip-path: polygon(0 0, 50% 50%, 100% 0, 100% 100%, 0 100%);
  clip-path: polygon(0 0, 50% 50%, 100% 0, 100% 100%, 0 100%);
}
.env-seams { position: absolute; top: 0; left: 0; width: 100%; height: 100%; }
.env-seams path { fill: none; stroke: rgba(140, 108, 64, 0.22); stroke-width: 0.5; vector-effect: non-scaling-stroke; }
.env-flap {
  top: 0; left: 0; right: 0;
  height: 55%;
  z-index: 5;
  transform-origin: 50% 0;
  filter: drop-shadow(0 3px 3px rgba(90, 66, 30, 0.2));
}
.env-flap-face {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  border-radius: 3px 3px 0 0;
  background: 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='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .45 0 0 0 0 .35 0 0 0 0 .2 0 0 0 .06 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"), linear-gradient(180deg, #F8F1E2, #EEE2C9);
  -webkit-clip-path: polygon(0 0, 100% 0, 52% 97%, 50% 100%, 48% 97%);
  clip-path: polygon(0 0, 100% 0, 52% 97%, 50% 100%, 48% 97%);
}

/* The wax seal: liquid rainbow glass, like the cover. It breaks in two when the letter opens. */
.seal {
  --sd: calc(var(--ew) * 0.2);
  position: absolute;
  left: 50%; top: 54%;
  z-index: 7;
  display: block;
  width: var(--sd); height: var(--sd);
  margin: calc(var(--sd) / -2) 0 0 calc(var(--sd) / -2);
  filter: drop-shadow(0 2px 2px rgba(30, 16, 70, 0.4)) drop-shadow(0 6px 10px rgba(30, 16, 70, 0.22));
}
.seal-half {
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  display: block;
}
.seal-half svg { display: block; width: 100%; height: 100%; overflow: visible; }
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.seal-l { -webkit-clip-path: polygon(0 0, 55% 0, 47% 23%, 57% 45%, 44% 66%, 54% 84%, 47% 100%, 0 100%);
  clip-path: polygon(0 0, 55% 0, 47% 23%, 57% 45%, 44% 66%, 54% 84%, 47% 100%, 0 100%); }
.seal-r { -webkit-clip-path: polygon(55% 0, 100% 0, 100% 100%, 47% 100%, 54% 84%, 44% 66%, 57% 45%, 47% 23%);
  clip-path: polygon(55% 0, 100% 0, 100% 100%, 47% 100%, 54% 84%, 44% 66%, 57% 45%, 47% 23%); }

.env-to {
  position: absolute;
  left: 0; right: 0;
  top: 72%;
  z-index: 6;
  display: block;
  color: var(--ink);
  font-family: var(--hand);
  font-size: calc(var(--ew) * 0.1);
  line-height: 1;
  letter-spacing: 0.02em;
  transform: rotate(-1.5deg);
  pointer-events: none;
}
.env-sub {
  display: block;
  margin-top: 30px;
  color: var(--moon);
  font-size: 18px;
  letter-spacing: 0.12em;
}
.env-open {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  color: var(--moon-soft);
  font-size: 15px;
  letter-spacing: 0.2em;
}
.env-open::before, .env-open::after {
  content: "";
  width: 18px; height: 1px;
  background: currentColor;
  opacity: 0.6;
}
@media (prefers-reduced-motion: no-preference) {
  .sealed .env-open { animation: breathe 3.6s ease-in-out infinite; }
}
@keyframes breathe {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.55; }
}
.env-skip {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: 6px;
  padding: 0 12px;
  color: var(--moon-soft);
  font-size: 14px;
  letter-spacing: 0.08em;
  text-decoration: underline;
  text-decoration-color: rgba(243, 237, 224, 0.35);
  text-underline-offset: 5px;
}
.env-skip:hover { color: var(--moon); }

html[lang="en"] .env-sub { letter-spacing: 0.03em; font-size: 19px; }
html[lang="en"] .env-open { letter-spacing: 0.06em; }
html[lang="en"] .env-skip, html[lang="en"] .listen-h, html[lang="en"] .song-sub { letter-spacing: 0.02em; }
/* On a short screen (a phone held sideways) the envelope shrinks so that it, the subtitle and the
   prompt still fit on the first screen; 280px is the bar plus the lines under the envelope.
   Portrait phones, tablets and desktops are tall enough that only the width rule applies. */
@supports (width: min(1px, 2px)) {
  .envelope { --ew: min(80vw, 340px, max(220px, (100vh - 280px) / 0.64)); }
}
@media (min-width: 700px) {
  .envelope { --ew: 500px; }
  .env-sub { font-size: 20px; margin-top: 36px; }
}
@supports (width: min(1px, 2px)) {
  @media (min-width: 700px) {
    .envelope { --ew: min(500px, max(220px, (100vh - 280px) / 0.64)); }
  }
}

/* ---------- States ---------- */
/* Closed letter: only the envelope (and the footer) until it is opened or skipped. */
.sealed .desk, .opening .desk { display: none; }
.opened .hero { display: none; }
.static .hero-h { display: none; }   /* reduced motion shows the letter at once, with its own h1 */

/* The opening, in three beats: the seal breaks, the flap lifts, the letter slides out.
   app.js then hands over to a paper sheet (.lift) that grows into the page. */
.opening .env-sub, .opening .env-open, .opening .env-skip, .opening .foot {
  opacity: 0;
  transition: opacity 0.3s ease;
}
.opening .seal-l { animation: seal-l 0.55s cubic-bezier(0.5, 0, 0.75, 0) forwards; }
.opening .seal-r { animation: seal-r 0.55s cubic-bezier(0.5, 0, 0.75, 0) forwards; }
.opening .env-flap { animation: flap-open 0.62s cubic-bezier(0.45, 0, 0.2, 1) 0.28s forwards; }
.opening .env-flap-face { animation: flap-inside 0.62s steps(1, end) 0.28s forwards; }
.opening .env-letter { animation: letter-out 0.72s cubic-bezier(0.22, 0.7, 0.2, 1) 0.82s forwards; }
.falling .env {
  transform: translateY(70vh) rotate(-5deg);
  opacity: 0;
  transition: transform 0.85s cubic-bezier(0.55, 0, 0.8, 0.2), opacity 0.85s ease-in;
}
.falling .env-letter { visibility: hidden; }
@keyframes seal-l {
  0% { transform: none; opacity: 1; }
  25% { transform: translate(-2px, 1px) rotate(-6deg); opacity: 1; }
  100% { transform: translate(-16px, 48px) rotate(-34deg); opacity: 0; }
}
@keyframes seal-r {
  0% { transform: none; opacity: 1; }
  25% { transform: translate(2px, 1px) rotate(5deg); opacity: 1; }
  100% { transform: translate(18px, 54px) rotate(28deg); opacity: 0; }
}
@keyframes flap-open {
  0% { transform: perspective(900px) rotateX(0deg); z-index: 5; }
  49% { z-index: 5; }
  50% { z-index: 1; }
  100% { transform: perspective(900px) rotateX(180deg); z-index: 1; }
}
@keyframes flap-inside {
  0% { background: linear-gradient(180deg, #F8F1E2, #EEE2C9); }
  50%, 100% { background: linear-gradient(180deg, #E4D4B5, #D9C7A4); }
}
@keyframes letter-out {
  0% { transform: translateY(0); }
  100% { transform: translateY(-58%); }
}

/* The sheet that grows from the envelope into the page (created by app.js).
   It arrives blank; the real page then fades in on top, so the writing seems to ink itself in. */
.lift {
  position: fixed;
  z-index: 20;
  border-radius: 3px;
  background: linear-gradient(180deg, #FCF9F2, var(--paper) 30%, #F8F2E5);
  box-shadow: 0 30px 60px -20px rgba(6, 4, 30, 0.7);
  pointer-events: none;
  transition:
    top 0.75s cubic-bezier(0.3, 0.7, 0.2, 1),
    left 0.75s cubic-bezier(0.3, 0.7, 0.2, 1),
    width 0.75s cubic-bezier(0.3, 0.7, 0.2, 1),
    height 0.75s cubic-bezier(0.3, 0.7, 0.2, 1),
    opacity 0.45s ease;
}
.lift::before {
  content: "";
  position: absolute;
  top: 18px; left: 0; right: 0; bottom: 0;
  background: repeating-linear-gradient(180deg, rgba(227, 220, 240, 0) 0 11px, var(--rule) 11px 12px);
  transition: opacity 0.5s ease;
}
.lift.grown::before { opacity: 0; }
.lift.gone { opacity: 0; }
.opened.arriving .desk { animation: arrive 0.5s ease both; }
@keyframes arrive { from { opacity: 0; } to { opacity: 1; } }

/* ---------- 2. The letter paper ---------- */
.desk {
  width: 100%;
  max-width: 700px;
  margin: 0 auto;
  padding: 0 var(--gutter);
}
/* Phones with a notch in landscape: keep content clear of it. Guarded, because a max() that an old
   browser cannot read inside a var() silently becomes 0 instead of falling back. */
@supports (padding: max(1px, 2px)) {
  .bar { padding-left: max(var(--gutter), env(safe-area-inset-left)); padding-right: max(var(--gutter), env(safe-area-inset-right)); }
  .desk { padding-left: max(var(--gutter), env(safe-area-inset-left)); padding-right: max(var(--gutter), env(safe-area-inset-right)); }
}
.paper {
  position: relative;
  z-index: 2;
  padding: 26px 22px 34px;
  border-radius: 3px;
  background:
    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='.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 .35 0 0 0 0 .3 0 0 0 0 .45 0 0 0 .05 0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E"),
    linear-gradient(180deg, #FCF9F2, var(--paper) 30%, #F8F2E5);
  color: var(--ink);
  box-shadow:
    0 1px 0 rgba(255, 255, 255, 0.7) inset,
    0 26px 44px -26px rgba(4, 3, 24, 0.6),
    0 2px 6px rgba(4, 3, 24, 0.3);
  outline: none;
}
@media (min-width: 700px) {
  .paper { padding: 56px 68px 60px; }
}

.paper-head { min-height: 92px; }
.intro {
  padding-top: 6px;
  color: var(--ink);
  font-family: var(--hand);
  font-size: 32px;
  line-height: 1.3;
  letter-spacing: 0.02em;
}
.intro .br { display: block; }
html[lang="en"] .intro { font-size: 34px; }
/* Long Cang centres ，：？！ in their box; pull them back next to the word (app.js wraps them
   in .pn and adds .hand-ready only once Long Cang has loaded, so a fallback font never overlaps). */
.hand-ready .pn { margin-left: -0.3em; }
.hand-ready .pn-q { margin-left: -0.2em; }
@media (min-width: 700px) {
  .intro { font-size: 42px; }
  html[lang="en"] .intro { font-size: 44px; }
}

/* The cover art as the letter's postage stamp, cancelled with the release date. */
.stamp {
  float: right;
  position: relative;
  margin: -4px -4px 8px 14px;
  transform: rotate(3deg);
}
.stamp-frame {
  position: relative;
  z-index: 1;
  display: block;
  width: 84px;              /* a multiple of the 7px perforation step, so every corner is a hole */
  height: 84px;
  padding: 5px;
  background: #FFFDF8;
  /* perforations: holes on a 7px grid, kept only in the white border (the picture stays whole) */
  -webkit-mask: linear-gradient(#000, #000) content-box, radial-gradient(circle, transparent 2.3px, #000 2.7px) -3.5px -3.5px / 7px 7px;
  mask: linear-gradient(#000, #000) content-box, radial-gradient(circle, transparent 2.3px, #000 2.7px) -3.5px -3.5px / 7px 7px;
}
.stamp::before {          /* the stamp's shadow on the paper (a mask would cut a box-shadow off) */
  content: "";
  position: absolute;
  top: 3px; left: 3px; right: 3px; bottom: 3px;
  box-shadow: 0 3px 10px -2px rgba(31, 28, 87, 0.35);
}
.stamp img {
  width: 74px;
  height: 74px;
  object-fit: cover;
  background: #E9E3F7;
}
.postmark {
  position: absolute;
  z-index: 2;
  left: -82px;
  top: -4px;
  width: 140px;
  height: auto;
  overflow: visible;
  transform: rotate(-9deg);
  pointer-events: none;
}
.postmark circle, .postmark path { fill: none; stroke: rgba(31, 28, 87, 0.5); stroke-width: 1.2; }
.postmark text { fill: rgba(31, 28, 87, 0.62); font-family: var(--text); font-size: 8.4px; letter-spacing: 0; }
@media (min-width: 700px) {
  .stamp { margin: -18px -14px 16px 24px; }
  .stamp-frame { width: 112px; height: 112px; }
  .stamp img { width: 102px; height: 102px; }
  .postmark { left: -96px; top: 0; width: 168px; }
}

.song-title {
  clear: both;
  margin-top: 22px;
  color: var(--ink);
  font-family: var(--text);
  font-size: 23px;
  line-height: 1.4;
  letter-spacing: 0.03em;
}
html[lang="zh-CN"] .title-text::before { content: "《"; margin-left: -0.45em; }
html[lang="zh-CN"] .title-text::after { content: "》"; }
.song-sub {
  margin-top: 2px;
  color: var(--ink-2);
  font-size: 15px;
  letter-spacing: 0.08em;
}
@media (min-width: 700px) {
  .song-title { font-size: 28px; margin-top: 30px; }
  .song-sub { font-size: 16px; }
}

/* ---------- The player ---------- */
.player {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  margin-top: 22px;
}
.play {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px; height: 56px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--ink);
  color: var(--paper);
  box-shadow: 0 6px 14px -6px rgba(31, 28, 87, 0.7);
  transition: transform 0.15s ease;
}
.play:active { transform: scale(0.96); }
.play svg, .mini-play svg { width: 24px; height: 24px; fill: currentColor; }
.play .i-play { margin-left: 2px; }
.i-pause { display: none; }
.is-playing .i-play { display: none; }
.is-playing .i-pause { display: block; }

.track {
  position: relative;
  flex: 1 1 auto;
  min-width: 0;
  padding-top: 4px;
}
.wave {
  height: 44px;
  overflow: hidden;
}
.wave svg { display: block; width: 100%; height: 44px; }
.wave .bars-base { fill: rgba(76, 59, 160, 0.22); }
.seek {
  position: absolute;
  top: 0; left: -6px; right: -6px;
  width: calc(100% + 12px);
  height: 52px;
  margin: 0;
  background: transparent;
  -webkit-appearance: none;
  appearance: none;
  cursor: pointer;
}
.seek:focus { outline: none; }
.seek:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; border-radius: 6px; }
.seek::-webkit-slider-runnable-track { height: 52px; background: transparent; border: 0; }
.seek::-moz-range-track { height: 52px; background: transparent; border: 0; }
.seek::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px; height: 14px;
  margin-top: 19px;
  border: 2px solid var(--paper);
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 0 0 1px rgba(31, 28, 87, 0.35), 0 2px 6px rgba(31, 28, 87, 0.4);
}
.seek::-moz-range-thumb {
  width: 10px; height: 10px;
  border: 2px solid var(--paper);
  border-radius: 50%;
  background: var(--ink);
  box-shadow: 0 0 0 1px rgba(31, 28, 87, 0.35), 0 2px 6px rgba(31, 28, 87, 0.4);
}
.time {
  display: flex;
  justify-content: space-between;
  margin-top: 4px;
  color: var(--ink-2);
  font-size: 13px;
  line-height: 1.4;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}
.time-sep { display: none; }

/* ---------- The lyrics, on ruled letter paper ---------- */
.lyrics-wrap { margin-top: 26px; }
/* No fixed height: in English (and when content.json fails) the toggle is hidden, and a reserved
   44px row left an empty band between the player and the lyrics. The toggle sets its own height. */
.lyrics-head {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 6px;
}
.tr-toggle {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  min-height: 44px;
  padding: 0 4px;
  border: 0;
  background: none;
  color: var(--ink-2);
  font-size: 14px;
  letter-spacing: 0.04em;
}
.tr-box {
  position: relative;
  width: 18px; height: 18px;
  border: 1.5px solid currentColor;
  border-radius: 4px;
}
.tr-toggle[aria-pressed="true"] { color: var(--ink); }
.tr-toggle[aria-pressed="true"] .tr-box { background: var(--ink); border-color: var(--ink); }
.tr-toggle[aria-pressed="true"] .tr-box::after {
  content: "";
  position: absolute;
  left: 5px; top: 1px;
  width: 5px; height: 10px;
  border: solid var(--paper);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.lyrics {
  font-size: var(--lyric);
  line-height: var(--lh);
  letter-spacing: 0.04em;
  background-image: linear-gradient(180deg, rgba(227, 220, 240, 0) calc(var(--lh) - 1px), var(--rule) calc(var(--lh) - 1px));
  background-size: 100% var(--lh);
  background-position: 0 -5px;
}
.stanza + .stanza { margin-top: var(--lh); }   /* a blank ruled line between song sections */
.line { margin: 0; color: var(--ink); }
.line .zh {
  position: relative;
  display: inline-block;
  max-width: 100%;
}
.line .en {
  display: none;
  color: var(--ink-2);
  font-size: 0.8em;
  letter-spacing: 0.01em;
  line-height: var(--lh);
  text-wrap: balance;   /* a long translation splits into two even lines, not one long + "takes”" */
}
html[lang="en"] .line .en, .with-tr .line .en { display: block; }
html[lang="en"] .lyrics { letter-spacing: 0.03em; }

/* Timed lyrics: lines not sung yet stay faint but readable; sung lines are in full ink. */
.timed .line { color: var(--ink-faint); transition: color 0.6s ease; }
.timed .line .en { color: var(--ink-faint); }
.timed .line.sung, .timed .line.now { color: var(--ink); }
.timed .line.sung .en, .timed .line.now .en { color: var(--ink-2); }
/* The line being sung writes itself: app.js moves the wet-ink edge with the singing. */
@supports ((-webkit-background-clip: text) or (background-clip: text)) {
  .timed .line.now.inking .zh {
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
  }
}
.timed .line.now .zh::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  bottom: 3px;
  height: 2px;
  border-radius: 2px;
  background: var(--iris);
  opacity: 0.8;
  transform-origin: 0 50%;
  transform: scaleX(var(--p, 1));
}
/* Reduced motion: every line in full ink, the current line only underlined. */
.calm .timed .line { color: var(--ink); transition: none; }
.calm .timed .line .en { color: var(--ink-2); }

/* ---------- 3. The unsigned signature ---------- */
.signoff {
  margin-top: calc(var(--lh) * 1.4);
  text-align: right;
}
.sig {
  display: inline-flex;
  align-items: flex-end;
  justify-content: flex-end;
  gap: 0;
  color: var(--ink);
}
.sig-label {
  font-family: var(--hand);
  font-size: 25px;
  line-height: 1.4;
  letter-spacing: -0.08em;
}
/* The tight spacing above is for the Chinese full-width colon; "Signed:" needs its own air. */
html[lang="en"] .sig-label { font-size: 27px; letter-spacing: 0.01em; padding-right: 8px; }
.sig-line {
  position: relative;
  display: inline-block;
  width: 132px;
  height: 58px;
}
.sig-rule {
  position: absolute;
  left: 0; right: 0; bottom: 6px;
  width: 100%; height: 14px;
  overflow: visible;
}
.sig-rule path { fill: none; stroke: var(--ink); stroke-width: 1.4; stroke-linecap: round; opacity: 0.75; }
.sig-name {
  position: absolute;
  left: 50%;
  bottom: 10px;
  display: inline-block;
  color: var(--ink);
  font-family: var(--hand);
  font-size: 52px;
  line-height: 1;
  white-space: nowrap;
  /* A mask clips everything outside the box, and Caveat's last letter draws past its box
     (the English name lost half of its last letter), so the box gets a little air on both sides.
     (The name itself never appears in this file or the HTML: it lives only in content.json.) */
  padding: 0 0.15em;
  transform: translateX(-50%) rotate(-3deg);   /* hugs the glyph, so the write-on mask spans just the name */
}
html[lang="en"] .sig-name { font-size: 50px; }
/* The write-on: a soft-edged mask slides from left to right, so the name appears the way a pen
   writes it (the water radical first, then the rest). The mask is three times the name's width;
   between 85% and 15% its soft edge crosses the name exactly once, so the whole run is writing. */
.sig-name.writing {
  -webkit-mask-image: linear-gradient(90deg, #000 42%, rgba(0, 0, 0, 0) 58%);
  mask-image: linear-gradient(90deg, #000 42%, rgba(0, 0, 0, 0) 58%);
  -webkit-mask-size: 300% 100%;
  mask-size: 300% 100%;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: 85% 0;
  mask-position: 85% 0;
  animation: write-on 1.5s cubic-bezier(0.45, 0.05, 0.55, 0.95) forwards;
}
@keyframes write-on {
  from { -webkit-mask-position: 85% 0; mask-position: 85% 0; }
  to { -webkit-mask-position: 15% 0; mask-position: 15% 0; }
}
/* Wet ink catches the light for a moment, in the cover's magenta and blue, then dries. */
.sig-name.wet { animation: write-on 1.5s cubic-bezier(0.45, 0.05, 0.55, 0.95) forwards, ink-dry 2.4s ease-out forwards; }
@keyframes ink-dry {
  0%, 40% { text-shadow: 0 0 7px rgba(200, 60, 224, 0.6), 0 0 1px rgba(76, 59, 234, 0.7); }
  100% { text-shadow: 0 0 0 rgba(200, 60, 224, 0); }
}
/* As the pen lifts, three small glints of the cover's glass catch the light once around the name —
   the same four-point stars as the night sky on the envelope. First reveal only (app.js adds
   .sparkle); never for returning visitors or with reduced motion. */
.sig-glint {
  position: absolute;
  z-index: 1;
  width: 10px; height: 10px;
  opacity: 0;
  pointer-events: none;
  filter: drop-shadow(0 0 3px currentColor) drop-shadow(0 0 8px currentColor);
}
.sig-glint::before {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  background: currentColor;
  -webkit-clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
  clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%);
}
.sig-glint.s1 { left: calc(50% - 42px); top: -4px; color: var(--g1); }
.sig-glint.s2 { left: calc(50% + 22px); top: -16px; width: 16px; height: 16px; color: var(--g2); }
.sig-glint.s3 { left: calc(50% + 34px); top: 22px; width: 9px; height: 9px; color: var(--g5); }
.sparkle .sig-glint { animation: glint-once 1.1s cubic-bezier(0.2, 0.7, 0.3, 1) both; }
.sparkle .sig-glint.s2 { animation-delay: 0.14s; }
.sparkle .sig-glint.s3 { animation-delay: 0.28s; }
@keyframes glint-once {
  0% { opacity: 0; transform: scale(0.2) rotate(-20deg); }
  30% { opacity: 1; transform: scale(1.15) rotate(10deg); }
  100% { opacity: 0; transform: scale(0.5) rotate(45deg); }
}
.sig-reveal {
  margin: 8px 0 0 auto;
  color: var(--ink);
  font-size: 16px;
  line-height: 1.8;
  text-align: right;
}
.sig-reveal .br { display: block; text-wrap: balance; }
@media (min-width: 700px) {
  .sig-label { font-size: 30px; }
  .sig-line { width: 168px; height: 70px; }
  .sig-name { font-size: 62px; }
  .sig-reveal { font-size: 17px; }
  .sig-glint.s1 { left: calc(50% - 50px); top: -6px; }
  .sig-glint.s2 { left: calc(50% + 27px); top: -18px; }
  .sig-glint.s3 { left: calc(50% + 41px); top: 28px; }
}
.sig-reveal .br + .br { color: var(--ink-2); }
.sig-reveal:empty { display: none; }
.sig-reveal.fresh { animation: arrive 0.8s ease 0.2s both; }

/* ---------- 4. The reply: a lavender postcard tucked under the letter ---------- */
.reply {
  position: relative;
  z-index: 1;
  margin: -22px 6px 0;
}
.card {
  position: relative;
  z-index: 0;
  padding: 50px 20px 22px;
  color: var(--ink);
}
.card::before {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0;
  z-index: -1;
  border-radius: 4px;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0) 40%),
    var(--card);
  box-shadow: 0 30px 50px -28px rgba(4, 3, 24, 0.9), 0 2px 6px rgba(4, 3, 24, 0.35);
  transform: rotate(-0.7deg);
}
@media (min-width: 700px) {
  .reply { margin: -26px 36px 0; }
  .card { padding: 62px 52px 32px; }
}
.reply-h {
  font-family: var(--hand);
  font-size: 34px;
  line-height: 1.25;
}
html[lang="en"] .reply-h { font-size: 36px; }
.reply-intro { margin-top: 8px; font-size: 17px; line-height: 1.7; }
.reply-note { margin-top: 2px; color: var(--ink-2); font-size: 15px; }

.form { margin-top: 18px; }
.field { margin-top: 14px; }
.field label {
  display: block;
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.5;
}
.field input, .field textarea {
  display: block;
  width: 100%;
  margin-top: 2px;
  padding: 9px 6px;
  border: 0;
  border-bottom: 1.5px solid rgba(31, 28, 87, 0.42);
  border-radius: 6px 6px 0 0;
  background: rgba(255, 255, 255, 0.38);
  color: var(--ink);
  font-family: var(--text);
  font-size: 17px;           /* 16px or more: stops iOS from zooming in */
  line-height: 1.6;
  -webkit-appearance: none;
  appearance: none;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}
.field input { min-height: 46px; }
.field textarea {
  min-height: 168px;
  padding: 0 8px;
  line-height: 32px;
  resize: vertical;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0) 31px, rgba(91, 88, 144, 0.22) 31px) 0 0 / 100% 32px local,
    rgba(255, 255, 255, 0.38);
}
.field input:focus, .field textarea:focus {
  outline: none;
  border-bottom: 2px solid var(--ink);
  background-color: rgba(255, 255, 255, 0.78);
}
.field textarea::placeholder { color: var(--ink-2); opacity: 1; }
.field textarea[aria-invalid="true"] { border-bottom-color: var(--rose); }
.field-foot {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  margin-top: 4px;
  font-size: 13px;
  line-height: 1.5;
}
.field-error { color: var(--rose); font-size: 14px; }
.count { margin-left: auto; color: var(--ink-2); font-variant-numeric: tabular-nums; }

.hp {
  position: absolute;
  left: -10000px;
  top: auto;
  width: 1px; height: 1px;
  overflow: hidden;
}

.send-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 18px;
  margin-top: 20px;
}
/* The send button is a postage stamp: navy, with a perforated edge. */
.send {
  position: relative;
  min-width: 128px;
  min-height: 56px;
  padding: 6px 28px 8px;
  border: 0;
  border-radius: 2px;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--hand);
  font-size: 27px;
  line-height: 1.2;
  letter-spacing: 0.08em;
  box-shadow: 0 8px 16px -8px rgba(31, 28, 87, 0.8);
  transition: transform 0.15s ease;
}
/* The perforations: rows of card-coloured dots along each edge, half on the button. Drawn with
   gradients because Safari (and so WeChat on iPhone) paints a "dotted" border as squares.
   "round" spaces the dots evenly whatever the button's width. The clear stop is the card colour
   at zero alpha, so old WebKit does not blend towards black at the dot's edge. */
.send::after {
  content: "";
  position: absolute;
  top: -3px; right: -3px; bottom: -3px; left: -3px;
  background:
    radial-gradient(circle, var(--card) 2.1px, rgba(236, 231, 250, 0) 2.6px) 0 0 / 10px 5px round no-repeat,
    radial-gradient(circle, var(--card) 2.1px, rgba(236, 231, 250, 0) 2.6px) 0 100% / 10px 5px round no-repeat,
    radial-gradient(circle, var(--card) 2.1px, rgba(236, 231, 250, 0) 2.6px) 0 0 / 5px 10px no-repeat round,
    radial-gradient(circle, var(--card) 2.1px, rgba(236, 231, 250, 0) 2.6px) 100% 0 / 5px 10px no-repeat round;
  pointer-events: none;
}
.send:active { transform: translateY(1px) rotate(-1deg); }
.send[disabled] { cursor: progress; opacity: 0.75; }
.send:focus-visible { outline: 2px solid var(--ink); outline-offset: 5px; }
.form-status {
  flex: 1 1 12em;
  font-size: 15px;
  line-height: 1.6;
}
.form-status.is-error { color: var(--rose); }
.form-status.is-quiet {
  position: absolute;
  width: 1px; height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
}

.email-line {
  margin-top: 18px;
  padding-top: 14px;
  border-top: 1px dashed rgba(31, 28, 87, 0.25);
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.5;
}
.mail {
  display: inline-block;
  padding: 11px 0;
  color: var(--ink);
  text-decoration-thickness: 1px;
  text-underline-offset: 4px;
  word-break: break-all;
}

/* ---------- 5. Listen on ---------- */
.listen {
  margin-top: 54px;
  text-align: center;
}
.listen-h {
  color: var(--moon-soft);
  font-size: 15px;
  letter-spacing: 0.1em;
}
.stores {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin: 12px 0 0;
  padding: 0;
  list-style: none;
}
.store {
  display: inline-flex;
  align-items: center;
  min-height: 48px;
  padding: 0 26px;
  border: 1px solid rgba(243, 237, 224, 0.42);
  border-radius: 999px;
  color: var(--moon);
  font-size: 16px;
  letter-spacing: 0.06em;
  text-decoration: none;
  transition: background-color 0.2s ease, border-color 0.2s ease;
}
.store:hover { background: rgba(243, 237, 224, 0.08); border-color: rgba(243, 237, 224, 0.75); }

/* ---------- 6. Footer ---------- */
.foot {
  max-width: 34em;
  margin: 0 auto;
  padding: 36px 24px calc(28px + env(safe-area-inset-bottom));
  color: var(--moon-soft);
  font-size: 13px;
  line-height: 1.7;
  text-align: center;
  text-wrap: balance;
}
.sealed .foot { padding-top: 8px; }
.opened .foot { padding-bottom: calc(96px + env(safe-area-inset-bottom)); } /* room for the mini player */

/* ---------- The mini player (fixed; steps aside when the reply is on screen) ---------- */
.mini {
  position: fixed;
  left: 50%;
  bottom: calc(10px + env(safe-area-inset-bottom));
  z-index: 30;
  display: flex;
  align-items: center;
  gap: 12px;
  width: calc(100% - 20px);
  max-width: 560px;
  padding: 6px 16px 6px 6px;
  border-radius: 30px;
  background: rgba(22, 20, 62, 0.94);
  color: var(--moon);
  box-shadow: 0 12px 30px -10px rgba(0, 0, 0, 0.7), 0 0 0 1px rgba(243, 237, 224, 0.1) inset;
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, 130%);
  transition: transform 0.35s ease, opacity 0.35s ease, visibility 0s linear 0.35s;
}
.mini.show {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition: transform 0.35s ease, opacity 0.35s ease, visibility 0s;
}
.mini-play {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 46px; height: 46px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--moon);
  color: var(--night);
}
.mini-play .i-play { margin-left: 2px; }
.mini-title {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  font-size: 15px;
  white-space: nowrap;
  text-overflow: ellipsis;
}
html[lang="zh-CN"] .mini-title::before { content: "《"; }
html[lang="zh-CN"] .mini-title::after { content: "》"; }
.mini-track {
  position: relative;
  flex: 1 1 60px;
  height: 3px;
  overflow: hidden;
  border-radius: 3px;
  background: rgba(243, 237, 224, 0.2);
}
.mini-fill {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  width: 100%;
  background: var(--iris);
  transform-origin: 0 50%;
  transform: scaleX(0);
}
.mini-time {
  flex: 0 0 auto;
  color: var(--moon-soft);
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

/* ---------- Reduced motion: no flying envelope, nothing that moves by itself ---------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
}
