/* ══ @font-face：自托管字体（fonts.py 生成，别手改） ═════════════════════════
   全部 OFL 许可，切过字（见 fonts.py）。swap：字还没到先用系统衬线顶着，
   总比一片空白强 —— 弱网下这一两百毫秒客人是有感的。 */
@font-face{
  font-family:"Pinyon Script";
  src:url("../fonts/pinyon-script-400.4f012af4.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"Cormorant Garamond";
  src:url("../fonts/cormorant-garamond-300.299f9f33.woff2") format("woff2");
  font-weight:300;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"Cormorant Garamond";
  src:url("../fonts/cormorant-garamond-400.94efe8d1.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"Cormorant Garamond";
  src:url("../fonts/cormorant-garamond-300-italic.67a8bbb7.woff2") format("woff2");
  font-weight:300;font-style:italic;font-display:swap;
}
@font-face{
  font-family:"Cormorant Garamond";
  src:url("../fonts/cormorant-garamond-400-italic.0766c28f.woff2") format("woff2");
  font-weight:400;font-style:italic;font-display:swap;
}
@font-face{
  font-family:"Noto Serif SC";
  src:url("../fonts/noto-serif-sc-300.59a0c8ba.woff2") format("woff2");
  font-weight:300;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"Noto Serif SC";
  src:url("../fonts/noto-serif-sc-400.a825225e.woff2") format("woff2");
  font-weight:400;font-style:normal;font-display:swap;
}
/* ══ @font-face 结束 ═════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════════════
   婚礼请柬 · 暗调蕾丝
   照 studiogwyn 那套 Canva 模板的样子重建。要换照片只改下面 PHOTOS 一段。
   ════════════════════════════════════════════════════════════════ */

:root{
  /* ══ 照片位 ══════════════════════════════════════════════════
     路径一律相对本 CSS 文件写（图放在 assets/img/ 就是 ../img/xxx.jpg）。
     留 none 就显示虚线占位框。 */
  --photo-hero:     url("../img/hero.jpg");     /* 封面 + 首屏的底 */
  /* 四张合影。第二个值是裁切位置，按人脸在原图里的位置量的：cover 会把竖图
     裁进横框/方框，不给位置就居中裁，脸容易被切掉。换图记得重新量一遍。
     页面上的顺序就是下面的顺序：银椭圆 → 拍立得上 → 拍立得下 → 落款蕾丝框。 */
  --photo-portrait:     url("../img/photo-1.jpg");  /* 信笺 · 银椭圆（横框，竖图只露中间一条） */
  --photo-portrait-pos: 50% 62%;
  --photo-pola-a:       url("../img/photo-2.jpg");  /* 当日安排 · 上面那张拍立得（近方框） */
  --photo-pola-a-pos:   50% 50%;
  --photo-pola-b:       url("../img/photo-3.jpg");  /* 当日安排 · 下面那张拍立得 */
  --photo-pola-b-pos:   50% 26%;
  --photo-story:        url("../img/photo-4.jpg");  /* 落款那段蕾丝框里的合影（正方） */
  --photo-story-pos:    50% 24%;
  /* 当日路线那块银框里的地图。**文件还没有** —— 图不存在时这一层就是透明的，
     底下的虚线占位照样露出来，所以路径可以先指着。把截图存成
     assets/img/map.jpg（1600×1000 左右，16:10）就自动生效，这行不用改。 */
  --photo-map:      url("../img/map.jpg");
  /* 晚宴那一版换一张：只剩酒店一个点，所以取景推到金沙湖东南角，
     酒店在画面中间偏下。比例和标记坐标跟着改，见文件下面 .mapframe 那一节
     末尾的 html.v-dinner 那一小块。 */
  --photo-map-dinner: url("../img/map-dinner.jpg");
  --photo-details:  url("../img/details.jpg");   /* 当日安排 那段的背景 */
  /* 下面两个是"敬请回复"那段用的。那一段现在不在页面里了（见文件末尾），
     想加回来就把 invitation.html 里那个 .env-open 段落贴回去。 */
  --photo-rsvp:     url("../img/rsvp.jpg");
  --photo-inner:    url("../img/hallway.jpg");   /* 信封里露出的走廊照 */

  /* ══ 美术件 ══════════════════════════════════════════════════
     路径同样相对本 CSS 文件。想回退到程序生成的矢量版，
     把对应这行换成 ../art/xxx.svg 即可。 */
  --art-lace:       url("../img/lace-frame.png");       /* 手帕框，中间半透明纱面 */
  --art-lace-open:  url("../img/lace-frame-open.png");  /* 手帕框，中间全镂空（放照片） */
  --art-oval:       url("../img/silver-oval.png");      /* 银质椭圆框 */
  --art-scallop:    url("../img/scallop-card.png");     /* 花瓣形卡片（The Details） */
  --art-arch:       url("../img/arch-card.png");        /* 拱形卡（RSVP，原页素材） */
  --art-polaroid:   url("../img/polaroid.png");         /* 拍立得空框 */
  --art-env-lace:   url("../img/env-lace.png");         /* 封盖的 V 形蕾丝边 */
  /* 封面信封封盖的蕾丝：原页是三层错位叠加，所以比单层宽、层次也厚 */
  --art-cover-lace-1: url("../img/cover-lace-1.png");
  --art-cover-lace-2: url("../img/cover-lace-2.png");
  --art-cover-lace-3: url("../img/cover-lace-3.png");
  --art-env-shadow: url("../img/env-shadow.png");       /* 前襟投在内容上的阴影 */
  --art-env-body:   url("../art/envelope-body.svg?v=3");      /* 信封纸身（矢量，没有导出图） */
  /* art.py 还会生成 envelope-flap-plain.svg，但矢量封盖的 V 尖在 60%，
     跟导出图 cover-lace-3 的 73% 对不上，封面已经不用它了。 */
  --art-env-open-back:  url("../img/envelope-open.png");   /* 整个敞口信封，原页素材 */
  --art-env-open-front: url("../img/envelope-front.png");  /* 真实前襟素材 *//* 封盖纸面，蕾丝单独叠 */

  /* 婚礼流程那四枚线稿小图。文件本身不带颜色（上色走下面 .tline 的 CSS 遮罩），
     四张都归一到 320×280 的公共画布，来路见 assets/img/SOURCES.txt。 */
  --art-tl-church:  url("../img/church.svg");   /* 宾客到场 · 教堂 */
  --art-tl-rings:   url("../img/rings.svg");    /* 户外婚礼 · 戒指盒 */
  --art-tl-cherub:  url("../img/cherub.svg");   /* 拍照合影 · 天使与相机 */
  --art-tl-dinner:  url("../img/dinner.svg");   /* 晚宴开席 · 双人餐桌 */

  /* ── 色板：直接取的参考页计算值 ─────────────────────────────── */
  --ink:    #0F1015;
  --cream:  #FFFCF4;
  --cocoa:  #251712;
  --ivory:  #F7F2E6;
  --muted:  #988E82;
  --muted-on-dark: #B9B0A2;

  --script: "Pinyon Script", "Snell Roundhand", "Apple Chancery", cursive;
  /* 拉丁字形取 Cormorant，中日韩自动落到 Noto Serif SC —— 混排的正确写法 */
  --serif:  "Cormorant Garamond", "Noto Serif SC", Didot, serif;

  /* 花边占整块蕾丝的 15%，纱面/照片就在中间 70% */
  --lace-band: 15%;
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--cream);color:var(--cocoa);
  font-family:var(--serif);font-weight:400;
  -webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}

/* ── 两个版本 ──────────────────────────────────────────────────
   请柬发两版：**完整版**（默认）和**只有晚宴的那版**，后者是链接后面加
   `?v=dinner`。哪一版由两个 HTML 的 <head> 里那句内联脚本决定 —— 它在首帧
   之前就把 v-dinner 挂到 <html> 上，所以不会先闪一眼完整版再收起来
   （交给 defer 的 site.js 去挂就会闪）。

   标了 data-v="full" 的只在完整版出现，data-v="dinner" 反过来。
   现在挂着 data-v="full" 的：
     时间和地点里「户 外 婚 礼」那一栏
     当日路线的标题（当日路线 / Getting There / 分隔线）
     地图上「仪 式」那枚标记
     两张地点卡（.venues）
     婚礼流程、着装两整段
   晚宴版剩下的就是：日期 + 晚宴 + 一张只标了酒店的地图。

   两版都用同一份 HTML，靠 display:none 收起来 —— 不是"少发了点东西"，
   是"这一版不给看"。真要藏秘密别用这个法子，请柬没这个需求。

   没有 JS 的时候 <html> 上是 no-js，两个类都不挂，落回完整版：
   少给信息不如多给。链接那边（site.js 第 7 段）也是同一个兜底方向。 */
html:not(.v-dinner) [data-v="dinner"]{display:none}
html.v-dinner       [data-v="full"]  {display:none}

/* 喂微信爬虫的那张缩略图（见两个 HTML 里 <body> 开头那段注释）。
   必须"在渲染树里但不在画面里"：display:none 和 visibility:hidden 都会
   让爬虫跳过它，所以走绝对定位挪出左边。不占位、不可点、读屏也念不到。 */
.share-seed{position:absolute;left:-9999px;top:0;width:500px;height:500px;
  pointer-events:none}

/* ── 排版基元 ──────────────────────────────────────────────────── */
.caps{
  font-family:var(--serif);font-weight:400;
  letter-spacing:.28em;text-transform:uppercase;
  font-size:clamp(11px,1.05vw,14px);line-height:1.55;
}
/* 中文小字：字距拉开才有请柬的仪式感，但比拉丁大写要收一点。
   字号比 .caps 抬一档（约 +18%）—— 同样 11px，拉丁大写还认得出，
   中文叠上 .34em 字距就散成一片灰。手机上生效的是 clamp 的**下限**：
   1.25vw 在 430 宽只算出 5.4px，中间那档永远够不着。 */
.cn{letter-spacing:.34em;font-weight:300;
  font-size:clamp(14px,1.3vw,17px)}
.cn-lg{letter-spacing:.28em;font-weight:300;
  font-size:clamp(17px,1.9vw,25px);line-height:1.9}
.caps--tight{letter-spacing:.2em}
/* 中文没有词边界，折行时浏览器爱在哪断在哪断 —— "期待见面"会被劈成
   "期 / 待见面"。要护住的词组包一层 .nb，折行只能落在两个 .nb 之间。
   （text-wrap:balance 只保证两行长度匀，管不了断点合不合理。） */
.nb{white-space:nowrap}
.script{
  font-family:var(--script);font-weight:400;line-height:1;
  /* 花体的斜体已经在字形里，别再叠 skew */
}
.rule{
  display:flex;align-items:center;justify-content:center;gap:12px;
  color:var(--muted);
}
.rule::before,.rule::after{
  content:"";height:1px;width:clamp(40px,7vw,90px);background:currentColor;opacity:.45;
}
.rule i{display:block;width:5px;height:5px;background:currentColor;
        transform:rotate(45deg);opacity:.7}

/* ── 通用段落 ──────────────────────────────────────────────────── */
.sec{position:relative;padding:clamp(72px,11vh,140px) 24px;overflow:hidden}
.sec--dark{background:var(--ink);color:var(--ivory)}
.sec--cocoa{background:var(--cocoa);color:var(--ivory)}
.sec--cocoa{--lace-size:clamp(230px,25vw,400px)}
.sec--cream{background:var(--cream);color:var(--cocoa)}
/* 参考页每一段都是按宽度定高的固定比例横幅（Canva 的页面是固定画布，
   按视口宽度整体缩放），不是整屏高。窗口一高，100svh 会让 background:cover
   把底图整张摊开、主体相对变小，参考页那种裁进画面的感觉就没了。
   实测参考页：横幅高 ≈ 视口宽 / 2.25。用 min-height 而不是 height，
   内容万一更高也不会被切掉。 */
.sec--full{min-height:clamp(520px,73.2vw,1040px);
  display:grid;place-items:center;padding-block:clamp(40px,4vw,80px)}
.wrap{width:min(100%,620px);margin:0 auto;text-align:center;position:relative;z-index:2}
.wrap--wide{width:min(100%,840px)}   /* 婚礼流程四列 / 落款蕾丝框要更宽 */

/* 段落背景照片层（带一点缓慢推近，避免死板） */
/* 注意这里不要再写 filter：同一串 filter 原来 .bg 和 .bg>i 各写了一份，
   父子叠两遍 = brightness .846 / contrast 1.0404 / saturate .846，
   比注释里写的暗一截。滤镜只留 .bg>i 上那一份。 */
.bg{position:absolute;inset:0;z-index:0;overflow:hidden}
/* 底图放在内层，比容器宽 6%，给横向视差留余量（余量 6%，视差 ±1.5%）。
   缩放和取景位置都是实测参考页换算的（换算式见下面"底图取景怎么算"）：
     首屏  视口 1080   图 1408×2182  left=-131.7 top=-592.9  → 123.0%  37.7% 42.6%
     首屏  视口 1335.5 图 1741×2698           top=-616.768   → 123.0%   ——   35.9%
     封面  视口 1080   图 3275×5074  left=-1105  top=-1160   → 286.1%  50.4% 35.4%
   封面的放大倍数是首屏的 2.3 倍（原页就是这么裁的，一大片花瓣特写）。

   ── 底图取景怎么算 ──────────────────────────────────────────────
   容器 = 分区宽 × 1.06，高 = 分区高（参考页的横幅高 ≈ 视口宽 × 0.732）
     --zoom = 图宽 / 容器宽
     --bgy  = |top| / (图高 − 容器高)
   两次首屏实测的 --zoom 都落在 123.0%（1741.19 / (1335.5×1.06) = 123.04），
   缩放这一项可以认为是定死了。--bgy 两次不一样（42.6% / 35.9%）：top 偏移
   没有按视口等比缩放，说明参考页那个横幅的高不是简单的 0.732×视口宽，
   或者两次测的滚动位置不同。现在取的是较新的那次 35.9%。要定死这一项，
   缺的就一个数：那个 1741×2698 的 div 外面那层裁切框的高。 */
.bg>i{position:absolute;top:0;bottom:0;left:-3%;width:106%;
  background-repeat:no-repeat;
  background-size:var(--zoom,123%) auto;
  background-position:var(--bgx,37.7%) var(--bgy,42.6%);
  will-change:transform;backface-visibility:hidden;
  transform:translateX(var(--pan,0%));
  transition:transform .35s ease-out}      /* 参考页是 transform .3s */
.bg--hero>i{background-image:var(--photo-hero);--bgy:35.9%}
/* 封面用同一张图但裁得紧得多 */
.cover .bg--hero>i{--zoom:286.1%;--bgx:50.4%;--bgy:35.4%}
.bg--rsvp>i{background-image:var(--photo-rsvp)}
/* 当日安排那段：原页实测（视口 1096 / 606 各量一遍）
     桌面  分区 1081×800.9  底图渲染 1410.8 宽  左 -164.9  上 -829.9
     窄屏  分区 591×726.3   底图渲染 1279.4 宽  左 -344.2  上 -752.7
   按 .bg>i 的容器（分区宽 ×1.06）换算：
     --zoom = 底图渲染宽 / 容器宽      → 桌面 123.1%、窄屏 204.2%
     --bgx  = 左溢出 / (底图宽−容器宽) → 两个断点都是 50%（横向正好居中）
     --bgy  = 上溢出 / (底图高−分区高) → 两个断点都是 48.5%
   窄屏那一档是桌面的 1.66 倍：原页在窄屏裁得紧得多，一只手怼满画面。
   之前这里吃的是 .bg>i 的默认值（123% / 37.7% / 42.6%），所以窄屏上
   底图明显偏小、取景也偏上。 */
.bg--details>i{background-image:var(--photo-details);
  --zoom:123.1%;--bgx:50%;--bgy:48.5%}
/* 宽屏那一档：分区高在 822px 封顶之后，原页的画布也停止放大，
   底图渲染宽变成 max(分区宽 × 1.06, 1447px) —— 1447 就是画布满比例时
   底图的设计宽。两档在分区宽 1109px 处正好接上（123.1% × 1.06 × 1109 = 1447），
   所以不会跳。100% 指的是 .bg > i 的宽，也就是分区宽的 1.06 倍。
     视口 1600（分区 1585）→ max(1680, 1447) = 1680   实测原页 1680.1 ✓
     视口 1200（分区 1185）→ max(1256, 1447) = 1447 */
@media (min-width:1125px){
  .bg--details>i{background-size:max(100%,1447px) auto}
}
/* 压暗层。每段照片的明暗差很大，而且要不要压取决于"文字压在哪":
   · 文字直接压在照片上（首屏、当日安排）→ 必须压暗
   · 文字在奶油色卡片上（敬请回复）→ 参考页那张是大白天的亮调照片，几乎不压
   --dim 就是这个开关：0 = 完全不压，1 = 压到接近全黑。 */
.bg>i{filter:saturate(.92) brightness(.92) contrast(1.02)}
.bg{--dim:.55}
.bg--rsvp{--dim:.10}
.bg--rsvp>i{filter:saturate(.96) brightness(1.01)}   /* 亮调实景，还原原样 */
/* 原页这一段是**零处理**：把 details.jpg 那个 <img> 的整条祖先链翻了一遍，
   filter 全是 none、opacity 全是 1、mix-blend-mode 全是 normal，分区里也没有
   任何铺满的半透明遮罩层。所以这里既不压暗也不调色，底图原样铺。
   （先后给过 .45 和 .16，都还是偏暗 —— 字全在花瓣卡上，压它没有意义。） */
.bg--details{--dim:0}
.bg--details>i{filter:none}
.bg::after{content:"";position:absolute;inset:0;z-index:1;
  background:
    radial-gradient(ellipse 66% 58% at 50% 46%,
      rgb(6 7 10 / calc(var(--dim) * .12)) 0%,
      rgb(6 7 10 / calc(var(--dim) * .60)) 62%,
      rgb(4 5 7  / calc(var(--dim) * .96)) 100%),
    linear-gradient(rgb(6 7 10 / calc(var(--dim) * .10)),
                    rgb(6 7 10 / calc(var(--dim) * .34)));
}
/* 深棕那段（我们的故事）底色不同，压暗也换成棕调，别串色 */
.sec--cocoa .bg::after{
  background:
    radial-gradient(ellipse 66% 58% at 50% 46%,
      rgb(20 12 9 / calc(var(--dim) * .12)) 0%,
      rgb(20 12 9 / calc(var(--dim) * .58)) 62%,
      rgb(14 8 6  / calc(var(--dim) * .94)) 100%),
    linear-gradient(rgb(20 12 9 / calc(var(--dim) * .10)),
                    rgb(20 12 9 / calc(var(--dim) * .32)));
}
/* 油画颗粒：小图平铺，不跟着背景缩放，成本可忽略 */
.grain{position:absolute;inset:0;z-index:1;pointer-events:none;
  background:url("../art/grain.svg") repeat;background-size:200px 200px;
  opacity:.07;mix-blend-mode:overlay}

/* ── 蕾丝手帕框 ────────────────────────────────────────────────── */
.lace{
  position:relative;width:min(92vw,var(--lace-size,420px));aspect-ratio:1;margin:0 auto;
  background:var(--art-lace) center/contain no-repeat;
  mix-blend-mode:var(--lace-blend);
  filter:drop-shadow(0 10px 26px rgba(0,0,0,.45));
}
.lace__inner{
  position:absolute;inset:var(--lace-band);
  border-radius:7%;overflow:hidden;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(10px,1.4vw,20px);
}
/* 纱面：半透明 + 细网眼，底下的暗调背景会透出来 */
/* 纱面已经画在 lace-frame.png 里了（中心 alpha≈130），这里不用再铺网眼 */
.lace--open{background-image:var(--art-lace-open)}
.lace__inner img,.lace__photo{width:100%;height:100%;object-fit:cover;
  background-position:center;background-size:cover}
/* 照片和它底下的虚线占位都撑满蕾丝内框（不进 flex 排队），照片在上层：
   --photo-story 留 none 时这一层是透明的，占位就露出来了。 */
.lace__ph,.lace__photo{position:absolute;inset:0}
.lace__photo{background-image:var(--photo-story);
  background-position:var(--photo-story-pos,center);z-index:1}

/* ── 银质椭圆相框 ──────────────────────────────────────────────── */
/* 导出的 silver-oval.png 是竖版（1230×1599，贝壳纹样在上下），
   参考页把它转了 90° 用成横版（纹样在左右）。所以容器是横的，
   框那一层保持竖版尺寸再 rotate(90deg)：
   转后包围盒 = (竖版高 × 竖版宽)，正好填满横容器。 */
/* 阴影**不能**用 filter:drop-shadow。iOS 上滤镜的输出会被裁到元素自己的盒子里：
   本该顺着银框椭圆铺开的软阴影，左右两边被削成两条直线（在 iPhone 上量过，
   暗区边界正好卡在 .oval 的 102/1187 两条盒边上），看着就是奶油底上多了个
   比背景略深的方框。改成一枚跟银框同形的椭圆 ::before 投 box-shadow ——
   box-shadow 不进滤镜管线，各家浏览器画法一致。
   3.4% 是 silver-oval.png 四周透明留白的实测值（旋转 90° 后横竖都是这个数），
   所以这枚椭圆和银框的外沿是重合的。 */
.oval{position:relative;width:min(84vw,var(--oval-size,470px));aspect-ratio:1599/1230;
      margin:0 auto}
.oval::before{content:"";position:absolute;inset:3.4%;border-radius:50%;
      box-shadow:0 14px 30px rgba(0,0,0,.28)}
.oval__photo{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:73%;height:69%;border-radius:50%;overflow:hidden;z-index:2;  /* 转 90° 后宽高互换 */
  background:#EBE4D8 center/cover no-repeat;
  background-image:var(--photo-portrait);
  background-position:var(--photo-portrait-pos,center);
}
.oval__frame{position:absolute;left:50%;top:50%;
  width:76.9%;height:130.05%;                 /* = 竖版的 宽/高，按横容器换算 */
  transform:translate(-50%,-50%) rotate(90deg);
  background:var(--art-oval) center/contain no-repeat}
.oval__ph{position:absolute;inset:0;display:grid;place-items:center;text-align:center;
  color:#8C8172;font-size:12px;letter-spacing:.2em;text-transform:uppercase;padding:0 20px}
/* 占位是 .oval__photo 的同级而不是子级：子级会画在照片之上，照片放进来就盖不住它。 */

/* ── 照片占位（还没放照片时的样子） ───────────────────────────── */
.ph{
  width:100%;height:100%;display:grid;place-items:center;text-align:center;
  background:rgba(120,105,90,.14);
  border:1px dashed rgba(200,185,165,.45);
  color:rgba(220,208,190,.75);
  font-size:11px;letter-spacing:.22em;text-transform:uppercase;padding:16px;
}
.sec--cream .ph{background:rgba(60,45,35,.05);border-color:rgba(120,100,80,.3);
                color:rgba(90,75,60,.7)}

/* ── 封面（信封页） ────────────────────────────────────────────── */
.cover{min-height:100svh;display:grid;place-items:center;position:relative;
       background:var(--ink);overflow:hidden;padding:40px 20px}
.cover__stack{position:relative;z-index:2;text-align:center;display:flex;
  flex-direction:column;align-items:center;gap:clamp(10px,1.7vh,20px)}
.cover__eyebrow{color:var(--ivory);opacity:.9}
.cover__names span{white-space:nowrap}   /* 换行只允许断在两个名字之间 */
.cover__names{color:var(--cream);font-size:clamp(38px,7.2vw,104px);max-width:92vw;
  padding:0 .1em .14em;margin:-.16em 0 -.1em;text-wrap:balance}          /* 给花体的 swash 留出上下空间 */
.cover__hint{color:var(--muted-on-dark);opacity:.85;
  animation:breathe 2.8s ease-in-out infinite}
/* 关键：动画在跑的时候，元素上的 opacity（含内联 style）压不住它，
   必须先把 animation 停掉。之前直接设 style.opacity=0 没生效就是这个原因。 */
.cover__hint.is-gone{animation:none;opacity:0;transition:opacity .3s ease}
@keyframes breathe{0%,100%{opacity:.5}50%{opacity:.95}}

/* 封面信封。实测参考页（视口 1080）：信封框 471×315 = 43.6vw，比例 1.495。
   三张导出图不是"三层蕾丝"，是三个不同的零件（用 canvas 数过每张的 alpha）：
     cover-lace-1  整只闭合信封的纸面，93.6% 不透明的矩形
                   L=1.66%  T=0.98%  96.7% × 99.2%
     cover-lace-2  封盖边上的蕾丝褶边，V 形，尖端在自身 82.4%，上沿两头探出纸外
                   L=-1.93% T=-6.06% 105.6% × 108.3%
     cover-lace-3  封盖纸面，V 形，上沿满宽（圆角）、尖端在自身 72.74%
                   L=0.81%  T=0.03%  98.3% × 100.8%
   所以只有 2 和 3 属于封盖、该跟着转；1 是信封本体，必须钉死不动。
   之前三张全塞进 .envelope__flap，一点开整只信封跟着翻上去、还被透视压小 ——
   就是这个原因，不是透视参数的问题，别去调 perspective。

   反推出来的几个位置（都相对 471×315 那个外框，已和原版截图核过）：
     纸身        L=3.89%  T=1.79%  R=96.13%  B=99.19%   → --paper-*
     封盖尖端    T=73.35%          （不是 art.py 矢量版那个 60%）
     前襟尖端    T≈54.2%           （lace-1 上那道淡折线）
   封盖的转轴取纸身上沿；lace-3 自己的上沿在 1.50%，差 0.3% 不到 1px，不用管。 */
.envelope{
  position:relative;width:min(43.6vw,580px);aspect-ratio:471/315;
  cursor:pointer;border:0;padding:0;background:none;
  perspective:1500px;filter:drop-shadow(0 18px 40px rgba(0,0,0,.55));
}
.envelope{--paper-l:3.80%;--paper-t:1.79%;--paper-w:92.38%;--paper-h:97.49%}
.envelope>i,.envelope__flap>i{position:absolute;background-repeat:no-repeat;
  background-size:100% 100%;background-position:center}

/* 兜底的矢量纸身：lace-1 有 2MB，慢网络下先让人看见一只信封 */
.envelope__body{left:var(--paper-l);top:var(--paper-t);
  width:var(--paper-w);height:var(--paper-h);
  background-image:var(--art-env-body)}
/* 信封本体。静止 —— 这一层无论如何都不要加 transform。 */
.envelope__paper{left:1.66%;top:.98%;width:96.7%;height:99.2%;
  background-image:var(--art-cover-lace-1)}
/* 封口内壁。内壁和封盖背面都拿封盖那张图当蒙版，形状跟封盖严丝合缝，
   闭合时正好被封盖盖住，不用另算多边形。内壁的颜色是上深下透的竖向渐变，
   到前襟尖那一带（54%）褪干净，下面继续露本体纸面 ——
   掀开后看到的就是"喉口有阴影、前襟还在"的样子。
   mask 一律写长属性：-webkit-mask 简写里的 position/size 在老 WebKit
   上不一定认，简写没生效就等于整块没蒙版、糊成一个矩形。 */
.envelope__throat,.envelope__flap-shade{
  left:.81%;top:.03%;width:98.3%;height:100.8%;
  -webkit-mask-image:var(--art-cover-lace-3);   mask-image:var(--art-cover-lace-3);
  -webkit-mask-size:100% 100%;                  mask-size:100% 100%;
  -webkit-mask-position:center;                 mask-position:center;
  -webkit-mask-repeat:no-repeat;                mask-repeat:no-repeat;
}
.envelope__throat{background-image:linear-gradient(180deg,
    rgba(52,40,27,.55) 1.5%, rgba(52,40,27,.40) 14%,
    rgba(52,40,27,.22) 32%,  rgba(52,40,27,.08) 46%,
    rgba(52,40,27,0)   58%)}

/* 封盖。这一层（连里面的纸面、蕾丝、花体）是唯一会转的东西。
   转轴在纸身上沿，转过 90° 之后整片都跑到转轴上方去了，
   跟本体没有重叠，所以不需要中途换 z-index。 */
.envelope__flap{
  position:absolute;inset:0;transform-style:preserve-3d;
  transform-origin:50% var(--paper-t);transform:rotateX(0deg);
  transition:transform .9s cubic-bezier(.5,-.08,.25,1);
  will-change:transform;
}
.envelope__flap-paper{left:.81%;top:.03%;width:98.3%;height:100.8%;
  background-image:var(--art-cover-lace-3)}
.envelope__flap-lace{left:-1.93%;top:-6.06%;width:105.6%;height:108.3%;
  background-image:var(--art-cover-lace-2)}
/* 封盖的背面：同一个形状压暗一点，只在封盖翻过 90°、我们看见底面时出现。
   蕾丝探出封盖轮廓的那一截不在蒙版里，翻过去也还看得见 —— 本来就该看得见。
   两道闸门叠在一起，是有意的：
     · rotateY(180deg) + backface-visibility —— 精确到角度，翻过去才露；
     · opacity 0→1 延时 .34s —— 那条 .9s 的曲线正好在 378ms 过 90°。
   backface 生效时后者只是跟着一起亮，没有副作用；
   而 Safari 上祖先有 filter 时 backface 偶尔失效，这时全靠延时那道兜住，
   不然封盖正面从头到尾都会蒙一层灰。 */
.envelope__flap-shade{background-color:rgba(38,28,18,.3);
  opacity:0;transform:rotateY(180deg);backface-visibility:hidden}
/* 花体缩写印在封盖正面，所以翻过 90° 就该消失。backface-visibility 到点自动
   切掉（那一瞬封盖正好侧对着看不见，不会突兀）；下面那条 opacity 是兜底，
   万一哪个浏览器不认 backface 也不会留一个镜像的 CM 在天上。 */
.envelope__mono{
  position:absolute;left:50%;top:43%;transform:translate(-50%,-50%);
  font-family:var(--script);font-size:clamp(30px,4vw,54px);color:#1A130F;
  pointer-events:none;line-height:1;opacity:.9;
  display:inline-flex;align-items:baseline;z-index:2;
  backface-visibility:hidden;
}
.envelope__mono i:last-child{font-style:normal;margin-left:-.26em;
  transform:translateY(.12em)}
.envelope:focus-visible{outline:1px solid var(--ivory);outline-offset:12px}
.envelope:hover .envelope__flap{transform:rotateX(-10deg)}

/* 点开：封盖向上翻过去 → 整页淡出 → 跳转。
   封盖翻起来会盖到上面那行花体名字，所以名字同时淡出，给它腾地方。 */
.envelope.is-open .envelope__flap{transform:rotateX(-176deg)}
.envelope.is-open .envelope__mono{opacity:0;transition:opacity .3s ease .3s}
.envelope.is-open .envelope__flap-shade{opacity:1;transition:opacity .22s ease .34s}
.cover.is-opening .cover__eyebrow,
.cover.is-opening .cover__names{opacity:0;transition:opacity .5s ease}
.cover.is-leaving{animation:leave .8s cubic-bezier(.4,0,1,1) forwards}
@keyframes leave{to{opacity:0;transform:scale(1.05)}}

/* ── 首屏（请柬页） ────────────────────────────────────────────── */
/* 实测参考页（视口 1080 宽）：首屏高 791px = 73.2vw，蕾丝 530px = 49.1vw。
   Canva 的页面是按视口宽整体缩放的，所以这两个值用 vw 才对。 */
.hero{min-height:clamp(520px,73.2vw,1040px);
      --lace-size:clamp(240px,49.1vw,700px);
      display:grid;place-items:center;position:relative;
      background:var(--ink);overflow:hidden;
      /* 内容要能装进 73.2vw 的横幅里（中文版比参考页多一行名字），所以间距收紧 */
      padding:clamp(20px,2.6vw,48px) 20px}
.hero__stack{position:relative;z-index:2;display:flex;flex-direction:column;
  align-items:center;gap:clamp(8px,1.3vw,18px);text-align:center}
/* 字号是照着**纱面内宽**定的，不是拍脑袋：纱面内宽 = 蕾丝的 70%，
   最长那行（& Mingxuan）的墨迹宽 ≈ 4.85 × 字号，两边一除，
   5.1vw 让它占到纱面内宽的 72%（原来 4.4vw 只占 62%，空得慌）。
   上限 72px 是蕾丝顶到 700px 那一档算出来的，再大就贴边了。 */
.hero__names{color:var(--cream);font-size:clamp(39px,5.1vw,72px);line-height:1.06;
  text-shadow:0 2px 18px rgba(0,0,0,.5)}
/* 两行各自居中。原来第二行挂了 padding-left:.6em 往右错开（照着参考图做的），
   中文名那行在底下是居中的，三行摞起来只有中间一行歪着，看着像没对齐。 */
.hero__names span{display:block}
/* 中文名字跟花体名字放在一起（在纱面里），副文案就只剩日期和地点两行，
   和参考页的结构一致；否则多一行会把整叠内容顶高。 */
.hero__names-cn{color:var(--cream);opacity:.9;margin:0;
  font-size:clamp(14.5px,1.5vw,19px);text-shadow:0 2px 14px rgba(0,0,0,.5)}
.hero__sub{color:var(--cream);font-size:clamp(26px,3.1vw,40px);
  text-shadow:0 2px 14px rgba(0,0,0,.5)}
.hero__meta{color:var(--cream);display:flex;flex-direction:column;gap:.35em}

/* ── 段落标题 ──────────────────────────────────────────────────── */
.title{font-family:var(--script);font-size:clamp(46px,7vw,86px);line-height:1.05;
  padding-bottom:.12em}
/* 长句标题（如 The countdown is on!）用小一档，别在 620px 的栏宽里折行 */
.title--long{font-size:clamp(34px,4.6vw,60px)}
.eyebrow{color:var(--muted);margin-bottom:.95em;
  font-size:clamp(16px,1.7vw,21px)}
.sec--dark .eyebrow,.sec--cocoa .eyebrow{color:var(--muted-on-dark)}

/* ── 正文（斜体衬线，参考页那种信笺口吻） ────────────────────────── */
.letter{font-size:clamp(16px,1.45vw,20px);line-height:1.95;letter-spacing:.05em;
  font-weight:300;color:inherit;opacity:.92;max-width:26em;margin-inline:auto}
.letter--en{font-style:italic;letter-spacing:0;max-width:34em}
/* 段间距按"一句一行"的新文案收窄了（原来 1.15em 是给成段的信笺文字留的）。
   text-wrap:balance：句子折行时两行分得匀，不会像原来那样把最后一个"情"字
   单独甩到第二行。老浏览器不认这个属性，退回普通折行，不影响别的。 */
.letter p{margin:0 0 .65em;text-wrap:balance}
.sign{font-family:var(--script);font-size:clamp(36px,5vw,60px);padding-bottom:.1em}
/* 花体签名底下那行中文名：比别处的 .cn 再大一点，它跟签名是一组的 */
.sign-cn{font-size:clamp(15px,1.55vw,20px)}

/* ── CLICK HERE ────────────────────────────────────────────────── */
.cta{display:inline-flex;flex-direction:column;align-items:center;gap:.7em;
     padding:6px 4px;position:relative}
.cta__label{color:inherit;opacity:.72}
.cta::after{content:"";height:1px;width:0;background:currentColor;opacity:.6;
  transition:width .45s cubic-bezier(.4,0,.2,1)}
.cta:hover::after,.cta:focus-visible::after{width:min(120px,60%)}
.cta:focus-visible{outline:1px solid currentColor;outline-offset:8px}

/* ── 倒计时 ────────────────────────────────────────────────────── */
.count{display:flex;justify-content:center;gap:clamp(16px,4vw,46px);
  font-variant-numeric:tabular-nums;margin-top:.4em}
.count div{display:flex;flex-direction:column;align-items:center;gap:.7em;min-width:3.4em}
.count b{font-weight:400;font-size:clamp(34px,5vw,58px);line-height:1;letter-spacing:.02em}
.count span{color:var(--muted);font-size:clamp(10px,.9vw,12px);letter-spacing:.3em;
  font-weight:300}

/* ── 流程表 ────────────────────────────────────────────────────── */
.sched{display:grid;gap:1.1em;margin:0 auto;max-width:24em;text-align:left}
.sched li{display:grid;grid-template-columns:4.6em 1fr;gap:1.4em;align-items:baseline;
  list-style:none}
.sched time{font-variant-numeric:tabular-nums;letter-spacing:.08em;opacity:.85;
  text-align:right}
.sched em{font-style:normal;letter-spacing:.2em;font-weight:300}
.sched{padding:0}

/* ── 拍立得 ──────────────────────────────────────────────────────
   位置是相对 .detstage（银盘）量的，数值见下面"当日安排 · 引导"。 */
.pola{position:absolute;width:49.13%;aspect-ratio:682/799;
  background:var(--art-polaroid) center/contain no-repeat;
  /* 相纸是矩形、又正好铺满这个盒子，所以直接 box-shadow 就够，
     不必走 filter —— 顺带绕开 iOS 把滤镜阴影裁方的毛病（见 .oval 那段）。
     旋转是加在同一个元素上的，阴影会跟着一起转，方向不会错。 */
  box-shadow:0 12px 26px rgba(0,0,0,.42)}
/* 取景窗的四个数 = polaroid.png 里那块**实心黑**的实测位置（左 7.33% 上 7.88%
   宽 84.60% 高 69.96%，682×799 上量的），四边各再往外让 0.35% 压住抗锯齿的毛边。
   PNG 的窗是黑的不是透明的：这四个数只要比它小一点，露出来的就是纯黑，
   看着像照片上多了一道黑边 —— 之前底下那道黑带就是这么来的（差 2.3%）。 */
.pola>*{position:absolute;left:7%;top:7.5%;width:85.3%;height:70.7%}
.pola--a{left:58.30%;top:9.43%;transform:rotate(-6.6deg)}
.pola--b{left:53.54%;top:46.76%;transform:rotate(23.2deg)}
/* 相纸里的照片。跟虚线占位是同一组 .pola>* 定位，写在占位后面所以压在它上面：
   路径留 none 时背景是透明的，底下的占位框照样露出来。 */
.pola__photo{background-repeat:no-repeat;background-size:cover;background-position:center}
.pola--a .pola__photo{background-image:var(--photo-pola-a);
  background-position:var(--photo-pola-a-pos,center)}
.pola--b .pola__photo{background-image:var(--photo-pola-b);
  background-position:var(--photo-pola-b-pos,center)}

/* ── 落款 monogram ─────────────────────────────────────────────── */
.mono{font-family:var(--script);font-size:clamp(92px,14vw,180px);line-height:.9;
  display:inline-flex;align-items:baseline;justify-content:center}
.mono i{font-style:normal;display:inline-block}
.mono i:last-child{margin-left:-.34em;transform:translateY(.14em)}

/* ── 出场：滚到了才浮现 ─────────────────────────────────────────── */
/* 注意：这里不能写 transform:none。带 .rise 的元素自己可能有 transform
   （比如敬请回复那个信封要 rotate(-7.1631deg)），写 none 会把它清掉。
   所以浮现位移和元素自身的 transform 用 --rise-tf 复合。 */
.js .rise{opacity:0;transform:translateY(26px) var(--rise-tf,);
  transition:opacity 1.1s cubic-bezier(.22,.61,.36,1),transform 1.1s cubic-bezier(.22,.61,.36,1)}
.js .rise.in{opacity:1;transform:translateY(0) var(--rise-tf,)}
.rise:nth-child(2){transition-delay:.12s}
.rise:nth-child(3){transition-delay:.24s}
.rise:nth-child(4){transition-delay:.36s}

/* ── 页脚 ──────────────────────────────────────────────────────── */
/* 上下都给一点，别再是 0 / 8vh 那种头轻脚重 —— 那样"回到信封"贴着上面
   那片可可色，底下却空掉七八十像素，看着就是一条多出来的尾巴。 */
.foot{padding:clamp(14px,1.8vh,22px) 24px clamp(22px,2.8vh,34px);
  text-align:center;background:var(--cream)}
.foot a{color:var(--muted);border-bottom:1px solid transparent;transition:border-color .3s}
.foot a:hover{border-color:currentColor}

@media (prefers-reduced-motion:reduce){
  *{animation-duration:.01ms!important;animation-iteration-count:1!important;
    transition-duration:.01ms!important}
  html{scroll-behavior:auto}
  /* 同上：不能写 transform:none —— .detstage 靠 translateX(-50%) 居中，
     清掉它整组会右移半个身位。走 --rise-tf，没设的元素照样拿到 none。 */
  .rise{opacity:1;transform:var(--rise-tf,none)}
}

/* ── 敞开的信封（RSVP 那段） ────────────────────────────────────
   参考页那里是一整个敞口信封，卡片和拍立得从里面探出来。
   层序（文档顺序即 z 序）：掀起的封盖蕾丝 → 后片+内衬 → 内容 → 前襟 → 落款。*/
/* 尺寸和角度全部照原页 DevTools 抄：
     信封组  512.273 × 626.803  rotate(-7.1631deg)
     蕾丝    499.944 × 332.269  rotate(172.837deg)
   172.837 −(−7.1631) = 180°，所以蕾丝自身就是翻转 180°。
   两个元素是同坐标系的兄弟节点，translate 相减得到蕾丝相对信封的位置：
     (22.0288−37.0803, 29.8735−55.8572) = (−15.05, −25.99)
     → left −2.94%，top −4.15%（按信封框换算） */
/* ══ 敞开的信封 ══════════════════════════════════════════════════
   全部数值来自原页实测：把每层渲染后的中心点投影到信封的局部坐标系，
   再换算成信封框的百分比。不要用 translate 相减——那些 translate 分属
   不同的嵌套坐标空间，相减出来是错的（我先后错过两次）。

     层            w       h      left     top     自身额外旋转
     蕾丝         120.1%  67.3%  -10.5%  -15.1%   +180°
     信封整体     100%    100%    0        0       0（组的 -7.1631°）
     走廊照       86.9%   70.9%   6.6%     5.9%    0（内部再放大 4.2 倍裁切）
     拱形卡       66.4%   41.7%  36.2%     5.3%   +21.908°
     └ 文字块     68.5%   49.6%  17.3%    20.1%（相对卡片框）
     拍立得       38.1%   36.5%   5.9%    21.8%   -11.28°
     └ 框内照片   85.8%   72.1%   5.5%     6.6%（相对拍立得）
     前襟         99.0%   99.1%   0.7%     0.0%    0

   拱形卡的 PNG 是一个横向宽穹顶（800×615），+21.908° 正好把它转成
   参考页里那个斜拱。所以这个额外旋转不能省。 */
.env-open{position:relative;width:min(46vw,var(--env-size,340px));
  aspect-ratio:513.222/627.963;margin:0 auto;
  /* 直接写字面量，不要套 var 中转：嵌套 var 有一层解析不出来，
     整条 transform 会在计算值阶段失效变成 none。 */
  --rise-tf:rotate(-7.1631deg);
  transform:rotate(-7.1631deg);
  filter:drop-shadow(0 30px 56px rgba(0,0,0,.45))}
.env-open>*{position:absolute}
.env-open__back{inset:0;background:var(--art-env-open-back) center/contain no-repeat}
/* 蕾丝的容器不裁切，实际显示完整图片，所以按内层 img 的实测框放。
   按外层容器框（L=1.3% w=97.6%）放会偏小，花边左尾探不出信封，
   就被拍立得整个盖住——我先后错过两次都是这个原因。 */
.env-open__lace{left:-10.5%;top:-15.1%;width:120.1%;height:67.3%;
  background:var(--art-env-lace) center/100% 100% no-repeat;
  transform:rotate(180deg)}
/* 走廊照的裁切。原页的链子里藏着一个 scale(0.239215)：
     img       1864 × 2429.39   偏移 (0, -283.852)
     裁切窗    1864 × 1861.68   overflow:hidden，再 scale(0.2392)
     实际窗口  445.898 × 445.342
   所以图片那些 px 是"缩放前"空间里的值，要拿 1861.68 而不是 445.342 去除：
     top = -283.852/1861.68 = -15.24%   height = 2429.39/1861.68 = 130.5%
   这样露出的是图片纵向 11.7%~88.3%（含棋盘地板）。
   我一开始按 445.342 去除，算出 -63.7%，露出来的是走廊天花板。 */
.env-open__inner{left:6.6%;top:5.9%;width:86.9%;height:70.9%;overflow:hidden;
  background:#E4DCC8}
.env-open__inner>i{position:absolute;left:0;top:-15.24%;width:100%;height:130.5%;
  background:var(--photo-inner) left top/100% 100% no-repeat}
.env-open__card{
  left:36.2%;top:5.3%;width:66.4%;height:41.7%;
  transform:rotate(21.908deg);
  background:var(--art-arch) center/100% 100% no-repeat;
  filter:drop-shadow(0 10px 22px rgba(0,0,0,.26))}
.env-open__cardtext{position:absolute;left:17.3%;top:20.1%;width:68.5%;height:49.6%;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.55em;
  color:var(--cocoa);text-align:center}
.env-open__card .script{font-family:var(--script);line-height:.92;
  font-size:clamp(19px,2.5vw,33px)}
.env-open__card .caps{font-size:clamp(8px,.72vw,10px)}
.env-open__card:hover .caps,.env-open__card:focus-visible .caps{color:#6B4E3D}
.env-open__card:focus-visible{outline:1px solid var(--cocoa);outline-offset:6px}
.env-open__pola{left:5.9%;top:21.8%;width:38.1%;height:36.5%;
  background:var(--art-polaroid) center/contain no-repeat;
  transform:rotate(-11.28deg);box-shadow:0 12px 24px rgba(0,0,0,.32)}
/* 同上，和 .pola>* 是同一张 PNG、同一组数 */
.env-open__pola>*{position:absolute;left:7%;top:7.5%;width:85.3%;height:70.7%}
.env-open__front{left:0.7%;top:0;width:99%;height:99.1%;
  background:var(--art-env-open-front) center/100% 100% no-repeat;
  pointer-events:none}
.env-open__mono{left:50%;bottom:5%;transform:translateX(-50%);
  font-family:var(--script);font-size:clamp(22px,2.9vw,38px);color:#241A15;
  line-height:1;display:inline-flex;align-items:baseline;pointer-events:none}
.env-open__mono i:last-child{font-style:normal;margin-left:-.26em;transform:translateY(.12em)}
@media (max-width:760px){
  .env-open{width:min(74vw,290px)}
}
@media (max-width:760px){
  /* 封面在窄屏也是另一套比例：实测参考页信封 ≈81vw、名字每行 ≈57vw */
  .envelope{width:81vw}   /* 窄屏比例来自你的 440×956 截图，约略值 */
  /* 15.5vw 是按 Chunyuan / Mingxuan 这两个名字调的（比参考页的
     Arabella / James 长不少）。换名字后如果溢出或偏小，调这个值。 */
  .cover__names{font-size:15.5vw}
}

/* ── RSVP 表单 ─────────────────────────────────────────────────── */
.rsvp{display:flex;flex-direction:column;gap:1.6em;text-align:left;
  max-width:26em;margin:clamp(28px,4vh,48px) auto 0}
.rsvp label{display:flex;flex-direction:column;gap:.7em}
.rsvp label>span,.rsvp legend{color:var(--muted)}
.rsvp input[type=text],.rsvp input[type=number],.rsvp textarea{
  font:inherit;font-size:17px;color:var(--cocoa);background:transparent;
  border:0;border-bottom:1px solid rgba(120,100,80,.4);padding:.5em 0;
  border-radius:0;transition:border-color .3s;
}
.rsvp input:focus,.rsvp textarea:focus{outline:0;border-bottom-color:var(--cocoa)}
.rsvp textarea{resize:vertical}
.rsvp fieldset{border:0;padding:0;margin:0;display:flex;flex-direction:column;gap:.7em}
.rsvp .row{flex-direction:row;align-items:center;gap:.8em;font-size:17px}
.rsvp .row input{accent-color:var(--cocoa);width:15px;height:15px}
.btn{
  align-self:flex-start;font-family:var(--serif);cursor:pointer;
  background:transparent;color:var(--cocoa);
  border:1px solid rgba(120,100,80,.5);padding:1.05em 3em;
  transition:background .35s,color .35s,border-color .35s;
}
.btn:hover,.btn:focus-visible{background:var(--cocoa);color:var(--cream);
  border-color:var(--cocoa)}

/* ── 事实栏（时间和地点：日期 / 仪式 / 晚宴） ────────────────────── */
.facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:clamp(24px,4vw,48px);margin-top:clamp(30px,4.6vh,56px);text-align:center}
.facts p{margin:0}
.facts .caps{color:var(--muted);margin-bottom:.62em}
.facts div>p:last-child{font-size:19px;line-height:1.68;letter-spacing:.08em;
  font-weight:300}
/* 单列版：原页那三块（日期 / 户外婚礼 / 晚宴）是竖着排的，不是三栏 */
.facts--col{grid-template-columns:1fr;gap:clamp(22px,3.2vh,38px)}

/* ── 婚礼流程（四列 × 两行，窄屏两列） ──────────────────────────
   每格上面是一枚线稿小图，照原页的样子来（教堂 / 戒指盒 / 天使 / 餐桌）。
   站主 2026-09-02 自己从 Canva 页导出的四张，记账在 assets/img/SOURCES.txt。
   菱形点还留着当兜底 —— 见下面 @supports。 */
.tline{list-style:none;padding:0;margin:clamp(38px,5.4vh,68px) auto 0;
  display:grid;grid-template-columns:repeat(4,1fr);
  gap:clamp(34px,4.6vw,56px) clamp(14px,2vw,28px);text-align:center}
.tline i{display:block;width:5px;height:5px;background:var(--muted);
  transform:rotate(45deg);opacity:.8;margin:0 auto clamp(14px,1.8vw,22px)}
/* 那枚小图 ──────────────────────────────────────────────────────
   一条规则管四枚：四张图共用 320×280 画布，各自的美术件按原页的渲染尺寸
   居中放在画布里，所以相对大小还是原页那个比例（戒指盒瘦高、餐桌矮宽），
   不会被"每张各自撑满"抹平。
   上色用遮罩而不是直接贴图：图里不写死颜色，颜色跟着 --cocoa 走，
   以后改色板不用回去动那四个文件。
   宽度按格子走（min(104px,62%)）而不是按 vw —— 窄屏两列时格子会宽一倍，
   按 vw 算的话图反而变小。 */
@supports (mask-image:none) or (-webkit-mask-image:none){
  .tline i[class*="ic--"]{
    width:min(104px,62%);height:auto;aspect-ratio:320/280;
    transform:none;opacity:1;background:var(--cocoa);
    /* --ic 忘了给就是一层透明遮罩（整枚不显示），不会露出一个纯色方块 */
    -webkit-mask:var(--ic,linear-gradient(transparent,transparent)) center/contain no-repeat;
            mask:var(--ic,linear-gradient(transparent,transparent)) center/contain no-repeat}
  .tline .ic--church{--ic:var(--art-tl-church)}
  .tline .ic--rings {--ic:var(--art-tl-rings)}
  .tline .ic--cherub{--ic:var(--art-tl-cherub)}
  .tline .ic--dinner{--ic:var(--art-tl-dinner)}
}
/* 时刻要压得住底下的中文标签（标签跟着 .cn 抬到 14–17 了），差一档才有主次 */
.tline time{display:block;font-variant-numeric:tabular-nums;
  letter-spacing:.12em;font-size:clamp(17.5px,1.65vw,22px)}
.tline .caps{display:block;margin-top:.7em;color:var(--muted)}

/* ── 着装色板：原页那个拱形色片（44×58，上圆下平），现在是七片 ───────
   色号不是原页那套（米白→酒红→深棕），是这场婚礼要的浅色系：
   白 / 米 / 香槟 / 浅粉 / 浅蓝 / 浅绿 / 卡其。
   七片挤得住：320 宽的时候 7×34 + 6×5 = 268，栏宽 272，刚好；再窄就靠
   flex-shrink 自己缩（li 没写 flex-shrink:0），比例由 aspect-ratio 保着，
   不会溢出去。
   **不描边**：原页就是四五片纯色，靠色片自己和奶油底色的一点色差立住。
   所以"白色"那片不能真给 #FFFFFF —— 跟底色 #FFFCF4 差不到 2 个 L，不描边
   就整片消失。原页第一片是 #F1F0ED，同一个路子：比底色冷一点、暗一点，
   看着是白的，又还看得出是个色片。 */
.swatch{list-style:none;padding:0;display:flex;justify-content:center;
  gap:clamp(5px,.9vw,9px);margin:clamp(32px,4.6vh,54px) auto 0}
.swatch li{width:clamp(34px,4.4vw,50px);aspect-ratio:44/58;background:var(--c);
  border-radius:50% 50% 0 0 / 37.9% 37.9% 0 0}

/* ── 落款那段（原页 With love） ──────────────────────────────────
   蕾丝框的花边比首屏那张宽：实测照片占框的 62.4%，也就是花边 18.7%
   （首屏那张纱面框用的是 15%）。框宽占分区宽 79.2%。
   --lace-size 必须写在 .lace 元素自己身上：写在 .sec--signoff 上是"继承"
   来的，会被窄屏那条 .lace{--lace-size:91.8vw} 直接盖掉。 */
.lace--signoff{--lace-band:18.7%;--lace-size:clamp(300px,38vw,560px)}

/* ── 当日路线：银框里的地图 ──────────────────────────────────────
   银盘那张 PNG 是椭圆的，装地图会把四角切掉，所以这里的框是矩形、用渐变
   现画的 —— 材质对齐银盘（同一种冷调铬光，明暗带的走向也照着来），
   但保持 16:10，地图不被裁。

   地图本身是照片：--photo-map 指到你自己导出的截图（1600×1000 左右）。
   没放之前 .mapframe__img 是透明的，底下的虚线占位就露出来。
   彩色地图直接放进来会和整站打架，所以过一层 --map-tone：去掉大部分
   饱和再补一层暖褐，压成和奶油底一个调子。六档预设见 web/_map.html，
   挑好把值抄到下面这一行。 */
.mapframe{
  display:block;width:min(100%,var(--map-size,720px));
  margin:clamp(34px,5vh,60px) auto 0;
  /* 框的比例 = 截图的比例。两个一致才不会 cover 裁掉边上的东西 ——
     现在这张截图两个地点正好一个在左上角、一个在右下角，按 16:10 裁
     左右各切 9.8%，左边那个直接被切掉。换图就改这一个数。 */
  aspect-ratio:var(--map-ratio,2000/1005);padding:clamp(9px,1.15vw,15px);
  background:linear-gradient(145deg,
    #EDEEF0 0%, #B4B7BD 16%, #F6F7F8 32%, #9A9DA4 50%,
    #E6E7EA 68%, #A9ACB2 84%, #DFE0E3 100%);
  border-radius:2px;
  box-shadow:0 16px 36px rgb(0 0 0 / .22), inset 0 0 0 1px rgb(255 255 255 / .5);
  transition:box-shadow .4s ease;
}
.mapframe:hover{box-shadow:0 20px 44px rgb(0 0 0 / .28), inset 0 0 0 1px rgb(255 255 255 / .6)}
/* 外框现在是个 <div>：图上要放三个链接（整图一个 + 两枚标记各一个），
   再用 <a> 包在外面就成了 <a> 套 <a>。整图那个铺满内芯、压在标记底下，
   点空白处照旧打开整片金沙湖。 */
.mapframe__all{position:absolute;inset:0;z-index:1;display:block}
.mapframe__all:focus-visible{outline:1px solid var(--cocoa);outline-offset:-5px}
/* 内芯：画框的"槽"。压一圈内阴影，看着像照片嵌在框里而不是贴上去的 */
.mapframe__inner{
  display:block;position:relative;width:100%;height:100%;overflow:hidden;
  background:#EBE6DC;
  box-shadow:inset 0 0 0 1px rgb(40 30 24 / .38), inset 0 2px 12px rgb(40 30 24 / .2);
}
.mapframe__img{
  position:absolute;inset:0;
  background:var(--photo-map) center/cover no-repeat;
  /* 底图只当底纹：地点靠下面 .mappin 那两枚自己的标记来认，所以这里可以压得狠。
     但别用 grayscale —— 高德的绿地和湖水饱和度接近，grayscale 一重就把两者
     压成同一个值，金沙湖直接没了。改成留一点饱和（saturate）+ 一层暖褐（sepia），
     湖还看得出是水，整体又贴得住奶油底。六档并排见 web/_map.html。 */
  filter:var(--map-tone, saturate(.34) sepia(.22) brightness(1.06) contrast(.94));
  transition:filter .5s ease;
}
.mapframe:hover .mapframe__img{filter:var(--map-tone-hover, saturate(.6) sepia(.12) brightness(1.04) contrast(.97))}

/* 图上那两枚标记 ─────────────────────────────────────────────────
   高德那张图上满是同样大小的灰标签（营港二组、仓家七组、蒋墩…），
   真正要人看的两个反而淹在里面。所以自己叠两枚：菱形用 .rule / .tline
   同一套，标签压一层半透明奶油底，压在什么颜色上都读得清。
   位置是按截图量的，占图宽/图高的百分比（框和图同比例，所以百分比通用）：
     仪式（七彩阜宁农业公园）  左上  9.3% / 28.6%
     晚宴（金陵湖滨酒店）      右下 86.4% / 84.1%
   两个点都贴着边，所以标签朝画面里侧排（--b 那枚反向），免得探出框外。
   换截图就改这两组数。注意标记要和 .mapframe__img 平级，套进去会被滤镜一起压。 */
/* .mappin 本身是个零尺寸的锚点：菱形钉在坐标上，标签浮在正上方。
   一开始写成 flex 一行 + translate(-50%,-50%)，那是把"菱形+标签"整行
   居中，菱形反而被推离了锚点好几个百分点；标签也正好压住高德自己的地名。
   高德的地名一律向右延伸，所以标签往上排最不打架。 */
/* 标记本身是链接了（点仪式跳仪式、点晚宴跳晚宴），所以不能再
   pointer-events:none 把点击放过去。零尺寸的锚点自己接不住手指，
   接得住的是它的两块生成盒：菱形 ::before 和标签 <b>；另外补一块
   44×44 的透明热区 ::after，手机上才够得着（44 是触摸目标的下限）。 */
.mappin{position:absolute;width:0;height:0;z-index:2}
.mappin::after{content:"";position:absolute;left:0;top:0;
  width:44px;height:44px;margin:-22px 0 0 -22px}
.mappin::before{content:"";position:absolute;left:0;top:0;
  width:9px;height:9px;margin:-4.5px 0 0 -4.5px;
  background:var(--cocoa);transform:rotate(45deg);
  box-shadow:0 0 0 3px rgb(255 252 244 / .9), 0 2px 6px rgb(0 0 0 / .32)}
.mappin b{position:absolute;left:0;bottom:11px;transform:translateX(-50%);
  font-family:var(--serif);font-weight:300;white-space:nowrap;
  letter-spacing:.24em;text-indent:.24em;font-size:clamp(10px,1.02vw,13px);
  color:var(--cocoa);background:rgb(255 252 244 / .92);
  padding:.4em .65em .4em .8em;border-radius:1px;
  box-shadow:0 1px 5px rgb(0 0 0 / .16);
  transition:background .3s,box-shadow .3s}
/* 能点就得看得出能点：手指/光标压上去标签实一点、浮起来一点。
   焦点圈画在标签上 —— 锚点自己零尺寸，描边根本看不见。 */
.mappin:hover b{background:rgb(255 252 244);box-shadow:0 3px 10px rgb(0 0 0 / .26)}
.mappin:focus-visible{outline:none}
.mappin:focus-visible b{outline:1px solid var(--cocoa);outline-offset:3px}
.mappin--a{left:9.25%;top:28.56%}
.mappin--b{left:86.35%;top:84.08%}
/* 晚宴那一版用的是另一张截图（只有酒店一个点，仪式那枚 data-v="full" 收起来了），
   所以换地图要动的三样东西在这儿一起改掉：图、比例、标记坐标。
   :root 那边是 (0,1,0)，html.v-dinner 是 (0,1,1)，直接盖得住。
     酒店（阜宁金沙湖金陵湖滨酒店）  62.5% / 71.9%（量的是那枚紫色圆标的圆心）
   这一版酒店在画面里侧，标签朝上排不会探出框外。 */
html.v-dinner{--photo-map:var(--photo-map-dinner);--map-ratio:1964/1150}
html.v-dinner .mappin--b{left:62.50%;top:71.94%}
.map__hint{margin:clamp(16px,2.2vh,26px) 0 0;color:var(--muted)}

/* 两个地点，**始终并排**（原来是 auto-fit，手机上会叠成一列）。
   一行两栏在 390 宽的手机上每栏只有 160 左右，所以每张卡砍到三行：
   名头 / 地点名 / 地址，字号和字距在下面 600px 那档再收一道。 */
/* minmax(0,1fr) 不能写成 1fr：1fr 的下限是 min-content，两栏内容不一样长
   （「金陵湖滨酒店」比「七彩农业园」多一个字），320 宽的屏上会分成
   112 / 128 两栏，跟上面居中的地图对不齐。 */
.venues{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  gap:clamp(16px,4.6vw,60px);margin-top:clamp(38px,5.4vh,66px);text-align:center}
.venue p{margin:0}
.venue__tag{color:var(--muted);margin-bottom:1em}
.venue__name{font-size:clamp(18px,1.9vw,24px);letter-spacing:.2em;text-indent:.2em;
  font-weight:300;line-height:1.6}
/* 地点名就是导航入口（底下原来那排「高德 / 百度」去掉了），得看着能点：
   颜色不动（这行是卡片的主角，压暗反而糊），加一条细下划线当记号。 */
.venue__name a{color:inherit;text-decoration:none;
  border-bottom:1px solid rgb(120 100 80 / .45);padding-bottom:.32em;
  transition:color .3s,border-color .3s}
.venue__name a:hover,.venue__name a:focus-visible{
  color:#6B5A4B;border-bottom-color:currentColor}
.venue__addr{margin-top:1.1em;font-size:16px;line-height:1.8;letter-spacing:.06em;
  font-weight:300;opacity:.9}

/* ── 故事时间线 ────────────────────────────────────────────────── */
.story{list-style:none;padding:0;margin:clamp(36px,5vh,64px) auto 0;
  max-width:30em;text-align:left;display:flex;flex-direction:column;gap:2.2em}
.story li{display:grid;grid-template-columns:9em 1fr;gap:1.6em;align-items:baseline}
.story p{margin:0}
.story .caps{color:var(--muted)}
.story li>p:last-child{font-size:17px;line-height:1.95;letter-spacing:.05em;
  font-weight:300;opacity:.9}

/* ── 当日安排 · 引导：银盘 + 花瓣卡 + 两张拍立得 ──────────────────
   原页这一组是一个 Canva 组，两个断点只是整体缩放，相对关系一模一样
   （视口 1096 和 606 各量一遍，换算成百分比后小数点后两位都对得上）。
   所以做法是：拿银盘当"舞台"，舞台里的位置只需要一套数。

   舞台 = 银盘（silver-oval.png 1230×1599，正立，不像信笺那段转 90°）：
     高 = 分区高 × 81.96%（上下各留 9.02%），水平中心在分区的 48.5%
       桌面 分区 1081×800.9 → 舞台 504.8×656.4   实测 504.8×656.4 ✓
       窄屏 分区  591×726.3 → 舞台 457.8×595.2   实测 457.8×595.2 ✓
   舞台宽由"高 × 1230/1599"推出来，不写 vw：vw 是含滚动条的视口宽，
   桌面比分区宽多 15px、手机上又不多，写成百分比 + aspect-ratio 就没这个
   误差。同理分区的高用 aspect-ratio 而不是 vw —— 而且百分比的 top 要生效，
   容器必须有个确定的高。

   ── 分区高怎么来的 ──────────────────────────────────────────────
   桌面这一档量了两个视口，凑出来是这么个规律：
     视口 1096（分区 1081）→ 分区 1081×800.9   银盘 504.8   舞台/分区高 81.96%
     视口 1600（分区 1585）→ 分区 1585×822     银盘 518.2   舞台/分区高 81.96%
   宽长了 46.6%，高只长了 2.6% —— 说明原页的画布有个设计尺寸（≈1109×822），
   窄于它就整体等比缩小，宽于它就高度封顶、只让底图横向铺满：
     分区高   = min(分区宽 × 0.7413, 822px)
     画布倍率 = 分区高 / 822
   银盘 518.2 × (800.9/822) = 504.9，和 1096 那次量的 504.8 对得上，
   说明"整组只跟分区高走"这条规律在两档都成立 —— 所以 CSS 里只写一个
   81.96%，宽屏窄屏都不用改。

   舞台内各层（left 占舞台宽 / top 占舞台高 / 宽占舞台宽）：
     层        left      top      宽        额外旋转
     银盘       0         0       100%       0
     花瓣卡    -6.77%    21.08%    80.30%    0
     拍立得 A  58.30%     9.43%    49.13%   -6.6°
     拍立得 B  53.54%    46.76%    49.13%   23.2°
   top 量出来本来是按舞台"宽"算的（27.41 / 12.26 / 60.80%），
   CSS 的 top:% 是按容器高算的，所以都除了 1.3003（= 1599/1230）。

   两张拍立得压在卡片上层（文档顺序即 z 序），和原页一致：它们只压到
   卡片右半边，字在左边 13%~53% 那一段，不会被盖住。原先卡片是居中的、
   拍立得按分区百分比放，正好压在 "The Details" 上，就是这个原因。

   卡片里的字号用 cqw（1cqw = 卡片宽的 1%），一套数管两个断点。
   注意 cqw 只能写在容器的"后代"上，写在 .detcard 自己身上会去找
   上一级容器（没有），所以间距走 .detcard__cta 的 margin-top。 */
/* width:100% 不能省。只写 aspect-ratio + max-height 的话，高一被 max-height
   截住，Chrome 会拿这个确定的高反过来算宽（"transferred size"），分区就缩成
   1404 宽，右边露出一条奶油色。宽写死成 100% 之后比例只单向传（宽 → 高）。
   822px 这个上限是量出来的，不是拍的 —— 见下面"分区高怎么来的"。 */
.sec--details{width:100%;aspect-ratio:1081/801;max-height:822px;padding:0}
.detstage{
  position:absolute;z-index:2;top:9.02%;left:48.9%;
  height:81.96%;aspect-ratio:1230/1599;
  /* .rise 会重写 transform，居中的位移得从 --rise-tf 带进去 */
  --rise-tf:translateX(-50%);
  transform:translateX(-50%);
}
.detstage>*{position:absolute}
.detstage__plate{inset:0;
  background:var(--art-oval) center/contain no-repeat;
  filter:drop-shadow(0 18px 40px rgba(0,0,0,.42))}
.detcard{
  left:-6.77%;top:21.08%;width:80.30%;aspect-ratio:1089/1046;
  background:var(--art-scallop) center/contain no-repeat;
  container-type:inline-size;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  color:var(--cocoa);text-align:center;
  filter:drop-shadow(0 12px 26px rgba(0,0,0,.34));
}
/* 原页这里是花体 "The / Details" + CLICK HERE，换成中文两行 + 点这里。
   标题里不写全角空格（页面别处那些 "点 这 里" 是写了的）：字号一大，
   空格叠上 letter-spacing 就散得不成一块了，字距只留 letter-spacing。
   text-indent 把留在行尾那一格补回来，不然居中会偏左半格。 */
.detcard__title{font-size:14cqw;line-height:1.4;letter-spacing:.3em;
  text-indent:.3em;font-weight:300}
.detcard__cta{font-size:5cqw;letter-spacing:.42em;text-indent:.42em;
  margin-top:5.6cqw;color:#6B5A4B;position:relative;padding-bottom:1em}
.detcard__cta::after{content:"";position:absolute;left:50%;bottom:0;
  width:0;height:1px;background:currentColor;opacity:.65;
  transform:translateX(-50%);transition:width .45s cubic-bezier(.4,0,.2,1)}
.detcard:hover .detcard__cta::after,
.detcard:focus-visible .detcard__cta::after{width:min(120px,58%)}
.detcard:focus-visible{outline:1px solid var(--cocoa);outline-offset:6px}

/* ── 窄屏 ──────────────────────────────────────────────────────── */
@media (max-width:760px){
  /* 参考页在窄屏是另一套断点，不是等比缩放。数是拿原页窄屏截图
     （assets/img/_ref-hero.png）扫出来的，单位都是 vw：
       首屏高          158.9   （原先写 150.7，那次量少了）
       蕾丝可见宽       88.31   ← 注意是"可见"，不是元素宽
       蕾丝可见上边距   22.36
     蕾丝那张 PNG 四周有透明边，元素宽要按同一套检测的比例放大：
       88.4 × 88.31/85.02 = 91.8vw
     上边距不能靠居中凑（我们文案块比原页高 8.4vw，居中只能到 18vw），
     所以窄屏改成顶对齐 + 显式 padding-top：22.36 − 1.76(透明边) = 20.6vw。 */
  .hero{min-height:158.9vw;align-content:start;padding-top:20.6vw}
  .sec--full{min-height:150.7vw}
  /* 原页窄屏每一段的高各不相同（不是一个 150.7vw 通吃），按分区宽算：
     首屏 136.8% / 信笺 123.1% / 敬请回复 139.3% / 当日安排 122.9% / 故事 128.9%。
     当日安排那段量的是 726.3 ÷ 591。舞台跟着分区高走，不用另外写。 */
  /* max-height 是桌面那一档的画布上限（822px），窄屏这一档不受它管：
     760px 宽的时候按比例该有 934 高，被 822 截住就压扁了。 */
  .sec--details{aspect-ratio:591/726;max-height:none}
  .bg--details>i{--zoom:204.2%}
  .tline{grid-template-columns:repeat(2,1fr)}
  .hero{--lace-size:91.8vw}
  .lace{--lace-size:91.8vw}
  .sec--cocoa{--lace-size:84vw}
  .lace--signoff{--lace-size:79.3vw}   /* 落款那张：实测 468.3 ÷ 591 */
  .hero__stack{gap:clamp(10px,2.4vw,20px)}
  /* 窄屏字号回到绝对值，vw 算出来会太小 */
  .hero__sub{font-size:27px}
  .hero__meta .cn{font-size:15px}
  .hero__names-cn{font-size:16.5px}
  /* 名字也得回到 vw：蕾丝在这一档是 91.8vw，字号却被 clamp 的下限钉死在 39px，
     390 宽的手机上蕾丝有 358，名字只有 39，小得不成比例（.cover__names 那边
     早就是 15.5vw，跟着走的）。10.6vw = 最长那行占纱面内宽的 80%，
     和 320 宽时的观感一致 —— 320 上算出来 33.9px，跟改之前一样，不会顶框。 */
  .hero__names{font-size:10.6vw}
  /* 窄屏底图取景。把 hero.jpg 和原页窄屏截图做归一化互相关搜出来的
     （NCC 0.72）：底图渲染宽 = 分区宽 ×250.7%，也就是内层容器的 236.5%。
     窄屏比桌面端裁得紧得多（桌面 123%），一整朵马蹄莲怼满画面。
     --bgy 搜出来 35.9%，和你量那个 div 反推的 35.9% 撞上了，可以放心。 */
  .bg--hero>i{--zoom:236.5%;--bgx:51.7%;--bgy:35.9%}
  /* 亮度。取景对齐之后逐点比对（9625 个背景采样点最小二乘）得到：
     原页那张几乎就是底图原样，压暗层等于没有。x=12 那一列最直白——
       y=100 原版 79 / 底图 81    y=180 原版 1 / 底图 1
       y=460 原版 28 / 底图 28
     拟合出 brightness 1.07、--dim 0。原先是 brightness .92（还叠了两遍
     = .846）+ dim .55，背景均值 19，原版 50.3，难怪发闷。
     只给首屏；封面那一屏没量过，先不动。 */
  .hero .bg--hero{--dim:0}
  .hero .bg--hero>i{filter:saturate(.92) brightness(1.07) contrast(1.02)}
}
@media (max-width:600px){
  .sched li{grid-template-columns:4.2em 1fr;gap:1em}
  .story li{grid-template-columns:1fr;gap:.5em}
  .count{gap:14px}
  .count div{min-width:0}
  /* 两张地点卡并排：390 宽的手机上每栏只剩 160 左右，字距先收。
     word-spacing 收的是 HTML 里那些真空格（「七 彩 农 业 园」是写出来的，
     不全靠 letter-spacing），负一点点就省下十几像素，看着还是疏朗的。
     nowrap 是底线：名字断成「七彩农 / 业园」比小两号难看得多。 */
  .venues{word-spacing:-.14em}
  .venue__tag{font-size:12px;letter-spacing:.18em;text-indent:.18em;white-space:nowrap}
  .venue__name{font-size:16px;letter-spacing:.13em;text-indent:.13em;white-space:nowrap}
  .venue__addr{font-size:13.5px;letter-spacing:.02em;word-spacing:0}
}
/* 再窄一档（320 那种老 iPhone / 微信里开了大字体的机器）：一栏只剩 121，
   「金陵湖滨酒店」六个字在上面那档要 126，得再收一号。 */
@media (max-width:360px){
  .venue__tag{font-size:11px;letter-spacing:.14em;text-indent:.14em}
  .venue__name{font-size:14.5px;letter-spacing:.09em;text-indent:.09em}
  .venue__addr{font-size:12px}
}

/* ── 背景音乐开关 ────────────────────────────────────────────────
   浮在右下角，两个页面都有。请柬从暖白滚到近黑，所以按钮自己不吃
   底色：半透明可可 + 毛玻璃 + 一圈浅描边，压在哪一段上都看得见。
   放着的时候音符轻轻摆，外面一圈涟漪往外散；关掉就叠一道斜杠。 */
.bgm{
  position:fixed;z-index:20;
  right:max(16px,env(safe-area-inset-right));
  bottom:max(16px,env(safe-area-inset-bottom));
  width:44px;height:44px;padding:0;border:0;border-radius:50%;
  display:grid;place-items:center;cursor:pointer;
  color:var(--ivory);background:rgba(23,15,12,.5);
  box-shadow:inset 0 0 0 1px rgba(247,242,230,.34),
             0 5px 20px rgba(15,16,21,.26);
  -webkit-backdrop-filter:blur(9px) saturate(1.15);
  backdrop-filter:blur(9px) saturate(1.15);
  -webkit-tap-highlight-color:transparent;
  transition:color .4s,background .4s,box-shadow .4s,transform .4s;
}
.bgm:hover{transform:translateY(-2px)}
.bgm:active{transform:translateY(0) scale(.94)}
.bgm:focus-visible{outline:1px solid currentColor;outline-offset:5px}
/* 涟漪。只有在放的时候才转，关掉是静止的一圈都不留 */
.bgm::after{
  content:"";position:absolute;inset:0;border-radius:50%;
  border:1px solid currentColor;opacity:0;pointer-events:none;
  animation:bgm-ripple 2.9s ease-out infinite;
}
.bgm.is-off{color:var(--muted-on-dark);background:rgba(23,15,12,.34)}
.bgm.is-off::after{animation:none}
.bgm__note{
  width:20px;height:20px;fill:none;stroke:currentColor;
  stroke-width:1.3;stroke-linecap:round;stroke-linejoin:round;
  transform-origin:8px 17px;
  animation:bgm-sway 2.8s ease-in-out infinite;
}
.bgm.is-off .bgm__note{animation:none}
/* 斜杠：从中间往两头长出来，别一下蹦出来 */
.bgm__slash{
  position:absolute;width:24px;height:1px;background:currentColor;
  transform:rotate(-45deg) scaleX(0);transform-origin:50% 50%;
  transition:transform .34s cubic-bezier(.4,0,.2,1);
}
.bgm.is-off .bgm__slash{transform:rotate(-45deg) scaleX(1)}
@keyframes bgm-ripple{
  0%  {transform:scale(1);   opacity:.42}
  70% {transform:scale(1.42);opacity:0}
  100%{transform:scale(1.42);opacity:0}
}
@keyframes bgm-sway{
  0%,100%{transform:rotate(-7deg)}
  50%    {transform:rotate(7deg)}
}

/* ════════════════════════════════════════════════════════════════
   下面这几块现在页面里没有用到，留着是为了随时能加回来：
     .env-open__*        敬请回复那个敞口信封
     .rsvp / .btn        回复表单
     .count              倒计时
     .sched              竖排流程表（现在用的是 .tline 四列版）
     .story              我们的故事时间线
     .sec--full          按宽定高的通用横幅
   要恢复哪一段，把对应的 HTML 段落贴回 invitation.html 就行，样式都在
   （备份在 git 里，或者问我要）。确定不要了再删，顺手把 assets/img 里
   只被它们引用的图（rsvp.jpg / hallway.jpg / envelope-*.png / arch-card.png）
   一起清掉。
   ════════════════════════════════════════════════════════════════ */
