/* ============================================================
   wedding-duo｜牽手日常（一片平靜的水面）
   概念：水獺睡覺的時候會牽著手，才不會漂散。喜帖＝一片水面，
   一對水獺牽著手，陪賓客漂過每一段；賓客＝被他們招手的人。
   - 開場：兩隻各漂在一邊，賓客把他們推到一起、牽到手，水波盪開、喜帖從水波裡浮出來
   - 每一區的開頭是一條會流動的水線；有陪伴角色的區，水獺就浮在那條水線上，
     旁邊的對話泡泡冠賓客的名字——點一下會跳、會濺水、會換一句話
   - Hero 是圓池形的照片；Gallery 原生版式「池畔」（pond）＝兩欄錯落的卵石
   - 終章「繫海帶」：按住不放，幫睡著的兩隻繫上海帶，結上掛賓客的名字
   動態語彙：水（流動的水線、盪開的水波、濺起的水滴）＋角色的小動作（眨眼、花搖、輕輕晃）。
   色彩與間距 token 覆寫自 /shared/base.css。

   ── 這一套的六條體系規則，改這份檔案前先讀 ──
   ① 可愛來自角色，不來自版面：版面仍然是留白、單一強調色、襯線標題。
      插圖只出現在「水線上」與「開場／終章」，內容區裡一隻都沒有（Tracy v2 的教訓：
      把主題做滿＝太可愛）。新增區塊時角色最多一對，而且站在水線上。
   ② 只有水獺說的話用圓體（--font-round）：對話泡泡、開場引導、終章提示。
      其餘中文標題一律 Serif TC、內文 Sans TC——一眼分得出「誰在講話」。
   ③ 強調色分兩階：--accent 給面積小的裝飾與角色（中明度，對紙約 3），
      --accent-tx 給文字與實色按鈕底（同色相降亮度，對紙與對 --sand 都 ≥4.5）。
      新增配色要兩個一起給（同韓系 UX-P0-1）。
   ④ 角色的顏色全走 class：.k 線條＝--ink、.w 毛色＝--paper-raise、.a＝--accent。
      SVG 是 tools/gen-duo-svg.mjs 的生成物，這裡零寫死色碼。
   ⑤ 會一直動的東西（水線、輕晃、眨眼、花搖、水波）預設 paused，
      只有捲進畫面的那一段才 running（.is-live，render.js 的 IntersectionObserver 掛）。
      看不到的地方不動＝省電，也不會一整頁十幾個動畫同時跑。
   ⑥ 互動絕不擋內容：角色是 <button>（≥44px），其餘裝飾 pointer-events:none；
      不點、不拖，喜帖照樣完整可讀（PRODUCT 原則 1：長輩零摩擦）。
   ============================================================ */

/* 洞口半徑（開場收場用）：註冊成 <length> 才能做 transition。
   不支援 @property 的瀏覽器＝洞直接開到最大（沒有擴張過程，內容照樣出現）。 */
@property --du-hole {
  syntax: "<length>";
  inherits: true;
  initial-value: 14px;
}

:root {
  --paper:        #f4f1e8;              /* 淺沙＝水面 */
  --paper-raise:  #fffefb;              /* 浮起表面（卡片、輸入框、水獺的毛色） */
  --ink:          #37302a;              /* 暖褐墨（正文／標題／角色的線條） */
  --ink-soft:     #6a6259;              /* 次要文字（對紙 5.3／對 --sand 5.1） */
  --accent:       #5f9186;              /* 青瓷綠（唯一強調色：水線、角色的花與領結、小裝飾） */
  --accent-tx:    #3f6e64;              /* 青瓷綠「當文字用」與實色按鈕底（對紙 5.1／對 --sand 4.9） */
  --accent-ink:   #f6fbf9;              /* 強調色底上的文字色 */
  --hairline:     rgba(55, 48, 42, .14);
  --ink-rgb:      55, 48, 42;           /* 墨的 rgba 家族單一來源 */
  --sand:         #e8eeea;              /* RSVP：比較深的那一池水 */
  --scratch-a:    #bcd3cc;              /* 刮刮樂塗層＝一層水面 */
  --scratch-b:    #a3c2b9;
  --font-sans-tc: "Noto Sans TC", "PingFang TC", sans-serif;
  --font-round:   "Huninn", "PingFang TC", "Microsoft JhengHei", sans-serif;  /* 水獺說話用的圓體 */
  --font-serif-latin: "Quicksand", "Nunito", system-ui, sans-serif;           /* 拉丁小標：圓頭無襯線 */

  /* 圓角：卡片與輸入框吃 --radius（編輯器的「版面檔位」會改它）；
     照片是「卵石」——四組不規則半徑輪流用。選「方正」檔位時卵石收回 --radius（見下方）。 */
  --radius: 1.4rem;
  --pond-1: 47% 53% 51% 49% / 56% 47% 53% 44%;
  --pond-2: 54% 46% 48% 52% / 46% 55% 45% 54%;
  --pond-3: 50% 50% 44% 56% / 52% 44% 56% 48%;
  --pond-4: 45% 55% 53% 47% / 50% 56% 44% 50%;
  --section-y: clamp(4.5rem, 13vw, 7.5rem);
  --shadow-soft: 0 22px 50px -30px rgba(var(--ink-rgb), .3);

  /* 水波線：一個波長 48×12 的遮罩，顏色由用它的元素自己的 background 決定。
     --wave-fill 是同一條曲線「以下填滿」的版本，給水體的上緣用（水色的邊就是那條水線）。 */
  --wave-fill: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='12'%3E%3Cpath d='M0 6q12-8 24 0t24 0V12H0z'/%3E%3C/svg%3E");
  --wave: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='48' height='12'%3E%3Cpath d='M0 6q12-8 24 0t24 0' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* ---------- 配色變體：珊瑚（config.theme = "coral"）----------
   只覆寫色彩 token、版式一律不動（DESIGN.md §7）。預設青瓷是冷調，這組走暖。 */
html[data-wk-theme="coral"] {
  --accent:     #c2715a;
  --accent-tx:  #96503b;
  --accent-ink: #fdf8f6;
  --sand:       #f3ebe6;
  --scratch-a:  #e6c9bd;
  --scratch-b:  #d9b3a4;
}

/* ---------- 配色變體：靛藍（config.theme = "indigo"）----------
   冷調，比青瓷更深更靜；與珊瑚互為冷暖。 */
html[data-wk-theme="indigo"] {
  --accent:     #6480ad;
  --accent-tx:  #455f8b;
  --accent-ink: #f7f9fd;
  --sand:       #e8ecf2;
  --scratch-a:  #c3cee0;
  --scratch-b:  #aebcd4;
}

/* ---------- 配色變體：麥金（config.theme = "wheat"）----------
   暖調，像傍晚的水面。 */
html[data-wk-theme="wheat"] {
  --accent:     #a98538;
  --accent-tx:  #7c6125;
  --accent-ink: #fdfaf2;
  --sand:       #f2ecdd;
  --scratch-a:  #e0d2ae;
  --scratch-b:  #d2c092;
}

/* 版面檔位「方正」：卵石收回一般圓角（新人選了方正，就不該還有一顆顆不規則的形狀） */
html[data-wk-corner="square"] {
  --pond-1: var(--radius); --pond-2: var(--radius); --pond-3: var(--radius); --pond-4: var(--radius);
}

/* 主按鈕底色：base.css 的 .btn 是 accent 底＋accent-ink 字，中明度的 --accent 只有 3.5——
   本模板改壓 --accent-tx（規則③）。 */
.btn:not(.btn-ghost) { background: var(--accent-tx); }
.on-dark .btn:not(.btn-ghost) { background: var(--accent-ink); color: var(--accent-tx); }
.btn-ghost { box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .28); }
.btn-ghost:hover { box-shadow: inset 0 0 0 1.5px var(--ink); }

/* 底色放 html、body 透明（body 有底色會蓋掉負 z-index 的裝飾層；這一套沒有負 z 的層，
   但規矩照守，之後加裝飾才不會踩雷）。上緣一層很淡的光＝水面的反光。 */
html {
  background:
    radial-gradient(110% 38% at 50% 0%, rgba(255, 255, 255, .75) 0%, transparent 70%) no-repeat,
    var(--paper);
}
body {
  font-family: var(--wk-f-body, var(--font-sans-tc));
  font-weight: 400;
  font-size: var(--text-body);
  line-height: 1.8;
  letter-spacing: 0;
  background: transparent;
  color: var(--ink);
  overflow-x: hidden;
}

/* ---------- 動態語彙：浮上來（位移小、收得軟） ---------- */
.reveal {
  transform: translateY(16px);
  transition:
    opacity 1s var(--ease-out),
    transform 1s var(--ease-out);
}

/* ════════════════════════════════════════════════════════════
   角色（inline SVG，tools/gen-duo-svg.mjs 的生成物）
   ════════════════════════════════════════════════════════════ */
.duo { display: block; width: 100%; height: auto; overflow: visible; pointer-events: none; }
.duo .k  { fill: var(--ink); }
.duo .k2 { fill: var(--ink); opacity: .4; }
.duo .w  { fill: var(--paper-raise); }
.duo .a  { fill: var(--accent); }
/* 補上去的領結（gen-duo-svg 的 BOW）：母版的外框線帶不過來，改由描邊補 */
.duo .bw--s .a { stroke: var(--ink); stroke-width: 6; stroke-linejoin: round; paint-order: stroke; }

/* 零件動畫：預設 paused，.is-live（捲進畫面）才跑；開場層自己的角色一律跑。
   transform-box: fill-box＝以零件自己的外框為基準，才能「原地」眨眼、搖花。 */
.duo .e, .duo .fl, .duo .bw, .duo .ht { transform-box: fill-box; transform-origin: 50% 50%; }
.duo .e  { animation: du-blink 5.2s linear infinite paused; animation-delay: var(--du-blink-d, 0s); }
.duo .fl { animation: du-sway 6.4s ease-in-out infinite paused; }
.duo .rp { animation: du-ripple 3.6s ease-in-out infinite paused; animation-delay: calc(var(--i, 0) * -.41s); }
.duo .ht { animation: du-heart 2.6s ease-in-out infinite paused; animation-delay: calc(var(--i, 0) * -.65s); }
.is-live .duo .e, .is-live .duo .fl, .is-live .duo .rp, .is-live .duo .ht,
.intro .duo .e, .intro .duo .fl, .intro .duo .rp { animation-play-state: running; }
@keyframes du-blink {
  0%, 93%, 100% { transform: none; }
  96%           { transform: scaleY(.12); }
}
@keyframes du-sway {
  0%, 100% { transform: rotate(-7deg); }
  50%      { transform: rotate(8deg); }
}
@keyframes du-ripple {
  0%, 100% { opacity: .35; transform: translateX(-3px); }
  50%      { opacity: 1;   transform: translateX(3px); }
}
@keyframes du-heart {
  0%   { opacity: 0; transform: translateY(8px) scale(.7); }
  30%  { opacity: 1; }
  100% { opacity: 0; transform: translateY(-12px) scale(1.05); }
}

/* 輕輕晃：0%＝靜止姿勢（開場飛過來的那一對落定時，Hero 這一對必須正好在原位——
   所以關鍵影格從「不動」開始，paused 的時候就是不動）。 */
.duo-bob { display: block; animation: du-bob 4.8s ease-in-out infinite paused; }
.duo-bob--late { animation-delay: -2.4s; }
.duo-bob--slow { animation-duration: 6.4s; }
.is-live .duo-bob, .intro .duo-bob { animation-play-state: running; }
/* 開場期間 Hero 那一對不晃：開場的那一對是照牠「靜止姿勢」的位置飛過來的，
   牠要是在底下先晃起來，落定那一刻兩對就差了幾個像素（duo-check 的一鏡到底斷言守這條）。 */
html.has-intro .tide--hero .duo-bob { animation-play-state: paused; }
@keyframes du-bob {
  0%, 100% { transform: none; }
  30%      { transform: translateY(-4px) rotate(1deg); }
  70%      { transform: translateY(2px) rotate(-1deg); }
}

/* ════════════════════════════════════════════════════════════
   水線
   ════════════════════════════════════════════════════════════ */
/* .tide＝每一區開頭的那一段水面：水獺浮在裡面，水線橫過去。 */
.tide {
  position: relative;
  height: clamp(7.4rem, 33vw, 9rem);
  margin-bottom: clamp(1.8rem, 6vw, 2.6rem);
}
.tide--plain { height: 2.6rem; }
/* ⚠ 水在角色「後面」（z-index 0，角色是 1）：水獺的身體是實心的毛色，水線被牠擋住、
     從兩側露出來＝牠浮在水面上。放在前面的話水線會橫切過牠的身體（Jacob 10/1 實測：
     「海浪都擋到海獺了」）。水面與身體交界的那幾道水波是角色插圖自己帶的（.rp）。

   水有深度（Jacob 10/1 第二輪）：一條線讀起來是「分隔線」，不是「水」。現在一段水面有三樣東西——
   ① .shore 的第一條＝水線本身（最濃、最快）
   ② .shore 的第二、三條＝水面底下的波：越深越淡、波長越長、越慢，第二條還反著流
      ——三層速度不同，眼睛才讀得出前後
   ③ .sea＝水線以下一片很淡的水色，上緣就是那條水線的形狀（--wave-fill）、跟水線同速同相位地流，
      往下漸漸淡到看不見
   全部只動 transform（compositor），而且只有捲進畫面才跑（.is-live）。 */
.shore {
  position: absolute;
  left: 0; right: 0;
  bottom: -2px;               /* 高 40px、第一條在最上面 → 水線中心仍在離底 32px 的位置 */
  height: 40px;
  overflow: hidden;
  z-index: 0;
  pointer-events: none;
}
.shore i, .intro-line i {
  position: absolute;
  left: 0;
  display: block;
  background: var(--accent);
  animation: du-flow 7s linear infinite paused;
}
.shore i:nth-child(1), .intro-line i:nth-child(1) {
  top: 0; height: 12px;
  width: calc(100% + 96px);
  opacity: .85;
  -webkit-mask: var(--wave) 0 50% / 48px 12px repeat-x;
  mask: var(--wave) 0 50% / 48px 12px repeat-x;
}
.shore i:nth-child(2), .intro-line i:nth-child(2) {
  top: 13px; height: 10px;
  width: calc(100% + 144px);
  opacity: .4;
  --du-shift: -72px;
  -webkit-mask: var(--wave) 0 50% / 72px 10px repeat-x;
  mask: var(--wave) 0 50% / 72px 10px repeat-x;
  animation-duration: 11s;
  animation-direction: reverse;
}
.shore i:nth-child(3) {
  top: 25px; height: 9px;
  width: calc(100% + 192px);
  opacity: .2;
  --du-shift: -96px;
  -webkit-mask: var(--wave) 0 50% / 96px 9px repeat-x;
  mask: var(--wave) 0 50% / 96px 9px repeat-x;
  animation-duration: 17s;
}
/* 水體：上緣與水線同一條曲線（上 12px 是 --wave-fill，再往下是實心），底色是往下淡出的漸層。
   濃度全靠外層的 opacity——不用 color-mix（舊一點的 Safari 沒有）。 */
.sea {
  position: absolute;
  left: 0; right: 0;
  top: calc(100% - 38px);     /* ＝.shore 的上緣，兩者的波形才疊得起來 */
  height: 8.5rem;
  overflow: hidden;
  z-index: 0;
  opacity: .13;
  pointer-events: none;
}
.sea i {
  display: block;
  width: calc(100% + 96px);
  height: 100%;
  background: linear-gradient(to bottom, var(--accent) 0%, transparent 100%);
  -webkit-mask: var(--wave-fill) 0 0 / 48px 12px repeat-x, linear-gradient(#000, #000) 0 11.5px / 100% 100% no-repeat;
  mask: var(--wave-fill) 0 0 / 48px 12px repeat-x, linear-gradient(#000, #000) 0 11.5px / 100% 100% no-repeat;
  animation: du-flow 7s linear infinite paused;
}
.is-live .shore i, .is-live .sea i { animation-play-state: running; }
@keyframes du-flow { to { transform: translateX(var(--du-shift, -48px)); } }

/* 標題下的小水波：進場時由左往右畫出來。
   ⚠ 「畫出來」的 clip-path 掛在 ::before，不掛元素本身：元素自己被 clip 成零面積的話，
     IntersectionObserver 永遠判定它不在畫面裡，.is-in 就永遠掛不上（實測踩過，
     整頁的小水波全部消失）。外層只負責 .reveal 的淡入。 */
.wave-rule {
  position: relative;
  display: block;
  width: 3.4rem;
  height: 9px;
  margin: clamp(.8rem, 2.6vw, 1.1rem) auto clamp(1.8rem, 5.5vw, 2.6rem);
}
.wave-rule.reveal { transform: none; }
.wave-rule::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
  -webkit-mask: var(--wave) 0 50% / 34px 9px repeat-x;
  mask: var(--wave) 0 50% / 34px 9px repeat-x;
}
.wave-rule.reveal::before {
  clip-path: inset(0 100% 0 0);
  transition: clip-path 1.1s var(--ease-out) .15s;
}
.wave-rule.reveal.is-in::before { clip-path: inset(0 0 0 0); }

/* ════════════════════════════════════════════════════════════
   陪伴角色（每一區水線上的那一對）
   ════════════════════════════════════════════════════════════ */
/* bottom 12px：把角色腳下那幾道水波抬到水線的高度（水線中心在 32px） */
.duo-stop {
  position: absolute;
  bottom: 12px;
  z-index: 1;
  display: flex;
  align-items: flex-end;
  gap: .5rem;
  max-width: calc(100% - var(--gutter) * 2);
}
.duo-stop--l { left: max(var(--gutter), calc(50% - 19rem)); }
.duo-stop--r { right: max(var(--gutter), calc(50% - 19rem)); flex-direction: row-reverse; }
/* 可點的角色：真的 <button>，觸控目標一定 ≥44px。
   overflow:hidden＋上方留 30px：角色「從水裡浮上來」的進場要被水線以下裁掉，
   但點了往上跳的那一下不能被切到頭。 */
.duo-btn {
  position: relative;
  flex: none;
  display: block;
  width: clamp(9rem, 40vw, 10.6rem);
  min-height: 44px;
  padding: 30px 4px 0;
  margin: -30px -4px 0;
  overflow: hidden;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  border-radius: 1rem;
}
/* 鍵盤焦點：不框一個方框（框住兩隻水獺很醜），改成牠們腳下一圈實線的水波 */
.duo-btn:focus { outline: none; }
.duo-btn:focus-visible::after {
  content: "";
  position: absolute;
  left: 8%; right: 8%;
  bottom: 2px;
  height: 14px;
  border: 2px solid var(--accent-tx);
  border-radius: 50%;
  pointer-events: none;
}
.duo-rise { display: block; transform-origin: 50% 100%; }
/* 點一下：壓扁→彈起→落定。只動 transform（compositor）。 */
.duo-btn.is-hop .duo-rise { animation: du-hop .62s cubic-bezier(.3, 1.4, .5, 1); }
.duo-btn.is-hop .duo .bw { animation: du-wiggle .62s ease-in-out; }
@keyframes du-hop {
  0%   { transform: none; }
  16%  { transform: scale(1.07, .9); }
  46%  { transform: translateY(-15px) scale(.96, 1.06); }
  76%  { transform: scale(1.03, .96); }
  100% { transform: none; }
}
@keyframes du-wiggle {
  0%, 100% { transform: none; }
  30%      { transform: rotate(-10deg); }
  60%      { transform: rotate(8deg); }
}

/* 對話泡泡：水獺說的話（規則②：整頁只有這裡用圓體） */
.duo-say {
  position: relative;
  z-index: 3;
  margin-bottom: clamp(3rem, 13vw, 3.8rem);
  padding: .5rem .85rem;
  max-width: 11.5em;
  font-family: var(--wk-f-body, var(--font-round));
  font-size: .95rem;
  line-height: 1.55;
  color: var(--ink);
  background: var(--paper-raise);
  border: 1.5px solid var(--ink);
  border-radius: 1.1rem 1.1rem 1.1rem .3rem;
  white-space: pre-line;
}
.duo-stop--r .duo-say { border-radius: 1.1rem 1.1rem .3rem 1.1rem; }
.duo-say:empty { display: none; }
.duo-say.is-pop { animation: du-pop .36s cubic-bezier(.3, 1.5, .5, 1); }
@keyframes du-pop {
  from { opacity: 0; transform: scale(.82); }
  to   { opacity: 1; transform: none; }
}

/* 相機的閃光（相簿那一站，點一下才閃） */
.duo-flash {
  position: absolute;
  left: 30%; top: 58%;
  width: 14px; height: 14px;
  margin: -7px 0 0 -7px;
  border-radius: 50%;
  background: #fff;
  opacity: 0;
  pointer-events: none;
}
.duo-btn.is-hop .duo-flash { animation: du-flash .5s ease-out; }
@keyframes du-flash {
  0%   { opacity: .95; transform: scale(.4); }
  100% { opacity: 0;   transform: scale(16); }
}

/* fx 模式：GSAP 接手「從水裡浮上來」的進場（前置隱藏態只寫在 html.fx 底下） */
html.fx main .duo-stop .duo-rise,
html.fx .footer .kelp-stage { opacity: 0; }
html.fx main .duo-stop .duo-say { opacity: 0; }

/* ════════════════════════════════════════════════════════════
   開場（html.has-intro 才顯示）
   基態＝兩隻各漂在一邊、靜止（等 boot 讀 config 呼叫 apply()）：
   - .intro-wait（manual）：拖曳進度 --wk-open（0~1）映射兩隻往中間漂，
     放手未過閾值由 transition 彈回（.is-dragging 時關 transition 跟手）。
   - .intro-play（auto）：自己漂過去（整組比 manual 晚 --du-d）。
   收場序列與時間軸寫在 index.html 開場 script 的檔頭。
   ════════════════════════════════════════════════════════════ */
.intro { display: none; }
html.has-intro .intro {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: block;
  cursor: pointer;
  touch-action: none;                 /* 手勢自己接管 */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  --du-w: min(100vw, 30rem);          /* 舞台寬：兩隻的大小與漂的距離都由它算 */
}
html.has-intro .intro-play { --du-d: .4s; }
.intro-stage { position: absolute; inset: 0; overflow: hidden; }
/* 水面：一整片紙色。收場時被洞挖開（mask 只在 .intro-hole 之後才掛，靜止時零遮罩）。 */
.intro-water {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(110% 46% at 50% 0%, rgba(255, 255, 255, .75) 0%, transparent 70%),
    var(--paper);
}
.intro-to, .intro-names {
  position: absolute;
  left: 0; right: 0;
  text-align: center;
  transition: opacity .25s var(--ease-out);
}
.intro-to {
  top: calc(50% - 10.4rem);
  font-family: var(--wk-f-body, var(--font-round));
  font-size: 1rem;
  color: var(--ink-soft);
}
.intro-to .to-pre { margin-right: .5em; }
.intro-names {
  top: calc(50% - 8.4rem);
  display: flex;
  justify-content: center;
  align-items: center;
  gap: .55em;
  margin-inline: var(--gutter);
  white-space: nowrap;
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.5rem, 7vw, 1.9rem) * var(--wk-fs-heading, 1) * var(--names-fit, 1));
  font-weight: 600;
  letter-spacing: .14em;
  color: var(--ink);
}
.intro-tilde {
  flex: none;
  width: 1.5em; height: 9px;
  background: var(--accent);
  -webkit-mask: var(--wave) 0 50% / 30px 9px repeat-x;
  mask: var(--wave) 0 50% / 30px 9px repeat-x;
}
.intro-g:empty + .intro-tilde { display: none; }
/* 水線：橫過兩隻的腰（兩層水波，樣式與 .shore 的前兩條共用）；底下一片往下淡出的水色 */
.intro-line {
  position: absolute;
  left: 0; right: 0;
  top: calc(50% + 3.1rem);
  height: 26px;
  overflow: hidden;
  opacity: .8;
}
.intro-line i { animation-play-state: running; }
.intro-water::after {
  content: "";
  position: absolute;
  left: 0; right: 0;
  top: calc(50% + 3.4rem);
  height: 40vh;
  background: linear-gradient(to bottom, var(--accent) 0%, transparent 100%);
  opacity: .1;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 22px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 22px);
  pointer-events: none;
}

/* 舞台：兩隻各 33% 寬、離邊 3%，中間空 28%——各漂 16%（＝自己寬度的 48.5%）就牽得到手 */
.meet {
  position: absolute;
  left: 50%;
  top: calc(50% - 3.4rem);
  width: var(--du-w);
  height: 8rem;
  margin-left: calc(var(--du-w) / -2);
  pointer-events: none;
}
.meet-l, .meet-r {
  position: absolute;
  bottom: 0;
  width: 33%;
  will-change: transform;
}
.meet-l { left: 3%; }
.meet-r { right: 3%; }
/* 身後的水波：漂得越快越明顯（--du-v 由開場 script 依速度寫） */
.meet-l::before, .meet-r::before {
  content: "";
  position: absolute;
  bottom: 14%;
  width: 34%; height: 22%;
  border: 1.5px solid var(--accent);
  border-color: var(--accent) transparent transparent transparent;
  border-radius: 50%;
  opacity: calc(var(--du-v, 0) * .9);
}
.meet-l::before { left: -22%; }
.meet-r::before { right: -22%; }
html.has-intro .intro-wait .meet-l,
html.has-intro .intro-play .meet-l {
  transform: translateX(calc(var(--wk-open, 0) * 48.5%));
  rotate: calc(var(--wk-open, 0) * (1 - var(--wk-open, 0)) * -16deg);
}
html.has-intro .intro-wait .meet-r,
html.has-intro .intro-play .meet-r {
  transform: translateX(calc(var(--wk-open, 0) * -48.5%));
  rotate: calc(var(--wk-open, 0) * (1 - var(--wk-open, 0)) * 16deg);
}
/* 放手沒過閾值：漂回去（linear() 彈簧；上一行 cubic-bezier 是不支援時的後備） */
html.has-intro .intro-wait .meet-l,
html.has-intro .intro-wait .meet-r {
  transition: transform .5s cubic-bezier(.2, 1.1, .3, 1), rotate .6s cubic-bezier(.2, 1.1, .3, 1);
  transition:
    transform .62s linear(0, .06 2%, .23 5%, .53 10%, .8 16%, .95 22%, 1.03 29%, 1.05 35%, 1.04 42%, 1.01 55%, .995 70%, 1),
    rotate .78s linear(0, .06 2%, .23 5%, .53 10%, .8 16%, .95 22%, 1.03 29%, 1.05 35%, 1.04 42%, 1.01 55%, .995 70%, 1);
}
html.has-intro .intro-wait.is-dragging .meet-l,
html.has-intro .intro-wait.is-dragging .meet-r { transition: none; }
/* 收場①：兩隻漂到正中間，快牽到手的時候淡掉，換成牽好手的那一對 */
html.has-intro .intro-commit .meet-l,
html.has-intro .intro-commit .meet-r {
  opacity: 0;
  transition:
    transform calc(.34s + var(--du-d, 0s)) cubic-bezier(.3, .7, .3, 1),
    rotate calc(.34s + var(--du-d, 0s)) cubic-bezier(.3, .7, .3, 1),
    opacity .16s linear calc(.26s + var(--du-d, 0s));
}
/* 牽好手的那一對：位置用 transform（之後飛去 Hero 也用它），彈一下用獨立的 scale——
   兩件事分開才不會互相蓋掉。 */
.meet-both {
  position: absolute;
  left: 50%;
  bottom: -6%;
  width: 62%;
  margin-left: -31%;
  opacity: 0;
  transform-origin: 50% 50%;
  will-change: transform;
}
html.has-intro .intro-commit .meet-both {
  animation: du-join .46s cubic-bezier(.3, 1.5, .5, 1) calc(.28s + var(--du-d, 0s)) both;
}
@keyframes du-join {
  from { opacity: 0; scale: .9; }
  35%  { opacity: 1; }
  to   { opacity: 1; scale: 1; }
}
/* 收場②：一鏡到底——飛到 Hero 那一對的位置（--fx／--fy／--fs 由開場 script 量好寫入） */
html.has-intro .intro-land .meet-both {
  transform: translate(var(--fx, 0px), var(--fy, 0px)) scale(var(--fs, 1));
  transition: transform .86s cubic-bezier(.6, 0, .3, 1);
}
/* 牽到手的那一刻：三圈水波從牽手的地方盪開 */
.meet-ring {
  position: absolute;
  left: 50%;
  bottom: 30%;
  width: 26%;
  aspect-ratio: 2.6 / 1;
  margin-left: -13%;
  border: 1.5px solid var(--accent);
  border-radius: 50%;
  opacity: 0;
}
html.has-intro .intro-commit .meet-ring { animation: du-ring 1s ease-out calc(.36s + var(--du-d, 0s)) both; }
html.has-intro .intro-commit .meet-ring.r2 { animation-delay: calc(.5s + var(--du-d, 0s)); }
html.has-intro .intro-commit .meet-ring.r3 { animation-delay: calc(.64s + var(--du-d, 0s)); }
@keyframes du-ring {
  0%   { opacity: .9; transform: scale(.5); }
  100% { opacity: 0;  transform: scale(3.4); }
}
/* 收場③：水面被一個往外擴的洞挖開（mask），底下的喜帖露出來。
   .intro-hole＝掛上遮罩（洞＝起始半徑，被牽好手的那一對蓋著，看不到）；
   .intro-land＝洞開始擴。兩個 class 由開場 script 先後掛（中間強制 reflow，transition 才有起點）。 */
html.has-intro .intro-hole .intro-water {
  -webkit-mask-image: radial-gradient(circle at var(--du-hx, 50%) var(--du-hy, 56%), transparent calc(var(--du-hole) - 2px), #000 var(--du-hole));
  mask-image: radial-gradient(circle at var(--du-hx, 50%) var(--du-hy, 56%), transparent calc(var(--du-hole) - 2px), #000 var(--du-hole));
}
html.has-intro .intro-land {
  --du-hole: 150vmax;
  transition: --du-hole .88s cubic-bezier(.6, 0, .3, 1);
}
/* 洞口的邊：一圈水波跟著洞一起擴、擴到一半淡掉 */
.intro-rim {
  position: absolute;
  left: var(--du-hx, 50%);
  top: var(--du-hy, 56%);
  width: calc(var(--du-hole) * 2);
  height: calc(var(--du-hole) * 2);
  translate: -50% -50%;
  border: 2px solid var(--accent);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}
html.has-intro .intro-hole .intro-rim { opacity: .75; }
html.has-intro .intro-land .intro-rim { opacity: 0; transition: opacity .6s ease-in .12s; }
/* 名字、冠名、引導、跳過：一開始收場就退 */
html.has-intro .intro-commit .intro-to,
html.has-intro .intro-commit .intro-names,
html.has-intro .intro-commit .intro-guide,
html.has-intro .intro-commit .intro-skip {
  opacity: 0;
  animation: none;
  pointer-events: none;
  transition: opacity .22s var(--ease-out);
}
/* 手動模式引導（apply("manual") 後才顯示）：脈動吸引輕觸／拖曳 */
.intro-guide {
  display: none;
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  top: calc(50% + 6.4rem);
  white-space: nowrap;
  font-family: var(--wk-f-body, var(--font-round));
  font-size: .95rem;
  color: var(--accent-tx);
  padding: .55rem 1.1rem;
  border-radius: var(--radius-pill);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .16);
}
html.has-intro .intro-wait .intro-guide {
  display: block;
  animation: du-guide 2.6s ease-in-out infinite;
}
@keyframes du-guide {
  0%, 100% { opacity: .5; }
  50%      { opacity: 1; }
}
/* 一開始拖，引導就讓位（跟著進度淡掉，放手彈回再淡回來） */
html.has-intro .intro-wait.is-touched .intro-guide {
  animation: none;
  opacity: max(0, calc(.9 - var(--wk-open, 0) * 3));
}
.intro-skip {
  position: absolute;
  bottom: max(2rem, env(safe-area-inset-bottom, 0px) + 1.5rem);
  left: 50%;
  transform: translateX(-50%);
  font-size: .82rem;
  color: var(--ink-soft);
  padding: .7rem 1.4rem;
  min-height: 44px;
  border-radius: var(--radius-pill);
}

/* ---------- 通用：標頭系統（全站置中——水面是對稱的） ---------- */
.section-title {
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.5rem, 5.6vw, 2rem) * var(--wk-fs-heading, 1));
  font-weight: 600;
  letter-spacing: .1em;
  text-indent: .1em;
  text-align: center;
  line-height: 1.5;
}
/* 拉丁小標：Quicksand 小寫、寬字距（寬字距是拉丁字的語彙，中文不加） */
.label-en {
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-size: calc(.8rem * var(--wk-fs-latin, 1));
  font-weight: 600;
  text-transform: lowercase;
  color: var(--accent-tx);
  text-align: center;
  letter-spacing: .26em;
  text-indent: .26em;
  margin-bottom: clamp(.4rem, 1.6vw, .6rem);
  line-height: 1.4;
}
.dot-sep::before { content: "\00B7"; margin-inline: .6em; opacity: .5; }

/* 每一區：上面是自己的水線（.tide），所以上內距歸零，只留下內距 */
main > .section, .footer { position: relative; padding-top: 0; }
main > .section > .container,
main > .section > .container-wide { text-align: center; }

/* ============ ① Hero ============ */
.hero {
  position: relative;
  padding: clamp(3rem, 10vw, 4.4rem) var(--gutter) clamp(3rem, 9vw, 4.5rem);
  text-align: center;
}
/* ⚠ 欄寬一定要寫死 minmax(0, 1fr)：不寫的話這一欄是 auto，會被「不折行的姓名」撐寬——
     姓名的 width:100% 跟著變寬，WK.fitOneLine 量到的可用寬就等於自然寬＝永遠不縮，
     長名字直接戳出畫面（網路字型換上來的那一刻才發生，所以是時有時無；duo-check 抓到的）。 */
.hero-content {
  max-width: 40rem;
  margin-inline: auto;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  justify-items: center;
}
.hero-label {
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-size: calc(.8rem * var(--wk-fs-latin, 1));
  font-weight: 600;
  letter-spacing: .26em;
  text-indent: .26em;
  text-transform: lowercase;
  color: var(--accent-tx);
}
/* 圓池形的照片：不規則的半徑＋外面一圈水波。
   一圈用 box-shadow 疊（先一圈紙色、再一圈強調色）——會跟著不規則半徑走，
   而且不佔版面、不需要多一個元素。 */
.hero-frame {
  width: min(76%, 19.5rem);
  margin: clamp(1.8rem, 6vw, 2.4rem) 0 0;
}
.hero-photo {
  width: 100%;
  aspect-ratio: 1 / 1.12;
  object-fit: cover;
  object-position: center 30%;
  border-radius: var(--pond-1);
  box-shadow: 0 0 0 9px var(--paper), 0 0 0 10.5px var(--accent);
}
/* 雙方姓名永遠同一排：放不下就整行等比縮（--names-fit 由 WK.fitOneLine 算，放得下＝1）。
   ⚠ 一定要 width:100%＋nowrap：fitOneLine 量的是「這個盒子的可用寬」對「整行的自然寬」。 */
.hero-names {
  width: 100%;
  min-width: 0;
  margin-top: clamp(2.4rem, 8vw, 3rem);
  display: flex;
  flex-wrap: nowrap;
  white-space: nowrap;
  justify-content: center;
  align-items: center;
  gap: .5em;
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.9rem, 9.4vw, 2.6rem) * var(--wk-fs-heading, 1) * var(--names-fit, 1));
  font-weight: 600;
  letter-spacing: .12em;
  line-height: 1.4;
}
/* 名字中間不是 &，是一小段水波 */
.hero-names .amp {
  flex: none;
  width: 1.3em; height: 10px;
  background: var(--accent);
  -webkit-mask: var(--wave) 0 50% / 34px 10px repeat-x;
  mask: var(--wave) 0 50% / 34px 10px repeat-x;
}
.hero-names-en {
  margin-top: .5rem;
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-size: calc(.82rem * var(--wk-fs-latin, 1));
  font-weight: 500;
  letter-spacing: .28em;
  text-indent: .28em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.hero-names-en:empty { display: none; }
.hero-date {
  margin-top: clamp(.8rem, 3vw, 1.1rem);
  font-size: 1rem;
  letter-spacing: .08em;
  color: var(--ink-soft);
}
/* 補充說明：刻意當 .hero-date 的子元素（跟著日期行一起進場）。留空由 :empty 收掉
   （這裡不能用 hidden：.hero-note 有 display 宣告會蓋掉它）。 */
.hero-note {
  display: block;
  margin-top: .3rem;
  font-size: .9rem;
  letter-spacing: .02em;
}
.hero-note:empty { display: none; }

/* Hero 那一站：水線橫跨整個畫面寬，水獺在正中間，泡泡在右邊 */
/* ⚠ 滿版要用 margin-inline 往兩邊撐（calc(50% - 50vw)）：只寫 width:100vw 的話，
     超出格線的項目會靠左起算、整頁多出一條橫向捲軸（實測踩過）。 */
.tide--hero {
  width: 100vw;
  max-width: none;
  height: clamp(9.6rem, 44vw, 11.4rem);
  margin: clamp(1.4rem, 5vw, 2rem) calc(50% - 50vw) 0;
  justify-self: stretch;
}
.duo-stop--hero {
  left: 50%;
  transform: translateX(-50%);
  width: min(100% - var(--gutter) * 2, 26rem);
  justify-content: center;
}
.duo-stop--hero .duo-btn {
  width: clamp(11rem, 50vw, 13rem);
  touch-action: pan-y;               /* 可以左右拖；垂直滑照樣讓給捲頁 */
}
.duo-stop--hero .duo-say { margin-bottom: clamp(5rem, 22vw, 6rem); max-width: 9.5em; }

/* 倒數：四顆氣泡 */
.countdown {
  display: inline-flex;
  gap: clamp(.5rem, 2.6vw, .9rem);
  margin-top: clamp(1.6rem, 5vw, 2.2rem);
}
.cd-cell {
  display: grid;
  place-content: center;
  justify-items: center;
  width: clamp(3.9rem, 19vw, 4.6rem);
  aspect-ratio: 1;
  border-radius: 50%;
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .16);
  background: rgba(255, 255, 255, .45);
}
.cd-cell strong {
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-size: calc(clamp(1.25rem, 5.6vw, 1.5rem) * var(--wk-fs-latin, 1));
  font-weight: 600;
  line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
.cd-cell span { font-size: .72rem; color: var(--ink-soft); line-height: 1.2; }

/* fx 模式：GSAP 接手 hero 進場（等開場收場後開演）。
   ⚠ 選擇器要帶 .hero 才壓得過上一條的 (0,3,1)，而且要排在它後面（中式 9/29 踩過）。 */
html.fx .hero .reveal { opacity: 1; transform: none; transition: none; }
html.fx .hero .hero-label,
html.fx .hero .hero-frame,
html.fx .hero .hero-names,
html.fx .hero .hero-names-en,
html.fx .hero .hero-date,
html.fx .hero .countdown,
html.fx .hero .tide--hero .shore,
html.fx .hero .duo-btn,
html.fx .hero .duo-say { opacity: 0; }
/* 一鏡到底（開場 script 的 land() 掛 html.wk-duo-landed）：這條路徑的「進場」就是那個
   往外擴的水波洞——Hero 的照片、名字、日期在洞擴開之前就先備好，洞一開就看得到，
   不再各自淡入（否則洞擴開露出來的是一張空白的紙）。開場飛過來的那一對正好落在
   Hero 這一對的位置，所以它也直接現身。泡泡與倒數留給 fx.js 在落定之後演。 */
html.fx.wk-duo-landed .hero .hero-label,
html.fx.wk-duo-landed .hero .hero-frame,
html.fx.wk-duo-landed .hero .hero-names,
html.fx.wk-duo-landed .hero .hero-names-en,
html.fx.wk-duo-landed .hero .hero-date,
html.fx.wk-duo-landed .hero .tide--hero .shore,
html.fx.wk-duo-landed .hero .duo-btn { opacity: 1; }
html.fx.wk-duo-landed .hero .tide--hero .shore { opacity: 1; }
/* 飛行中畫面上只准有一對：Hero 這一對在開場層撤掉之前先藏著（洞一擴開就會露出它，
   跟還在半路的那一對同時出現＝四隻水獺）。用 visibility——版面還在，land() 照樣量得到矩形。
   開場 script 的 done(false) 同一刻拿掉 has-intro 並移除開場層：飛過來的那一對消失、
   這一對現身，同一幀交棒。 */
html.has-intro.wk-duo-landed #duo-hero svg.duo { visibility: hidden; }

/* ============ ② Story ============ */
.timeline { display: grid; gap: clamp(2.4rem, 8vw, 3.4rem); }
.timeline-item { display: grid; justify-items: center; gap: .55rem; }
/* 兩則之間：一小段水波 */
.timeline-item + .timeline-item::before {
  content: "";
  width: 2.2rem; height: 9px;
  margin-bottom: clamp(1.6rem, 6vw, 2.4rem);
  background: var(--accent);
  opacity: .65;
  -webkit-mask: var(--wave) 0 50% / 34px 9px repeat-x;
  mask: var(--wave) 0 50% / 34px 9px repeat-x;
}
.tl-date {
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-size: calc(.82rem * var(--wk-fs-latin, 1));
  font-weight: 600;
  letter-spacing: .14em;
  color: var(--accent-tx);
  padding: .18rem .9rem;
  border-radius: var(--radius-pill);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .16);
}
.tl-title {
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.15rem, 4.4vw, 1.35rem) * var(--wk-fs-heading, 1));
  font-weight: 600;
  letter-spacing: .06em;
  margin-top: .2rem;
}
.tl-text { max-width: 26em; color: var(--ink-soft); white-space: pre-line; }
.tl-photo { width: min(80%, 18rem); margin: .6rem 0 .4rem; }
.tl-photo img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--pond-2);
}
.timeline-item:nth-child(even) .tl-photo img { border-radius: var(--pond-3); }

/* ============ ③ Gallery：原生版式「池畔」（pond） ============
   照片是一顆一顆圓潤的卵石：兩欄錯落（第二欄往下錯半格），四種不規則半徑與
   三種比例輪流出現，每一顆外面都有一圈細細的水波。
   ⚠ 全部規則包在 #gallery:not([data-wk-gallery]) 底下：新人選了 shared 版式
     （grid／polaroid／…）時 #gallery 會掛 data-wk-gallery，這一段一條都選不到。 */
#gallery:not([data-wk-gallery]) .photo-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.4rem, 6vw, 2rem) clamp(1.1rem, 5vw, 1.8rem);
  align-items: start;
  padding: 10px 8px 0;
}
#gallery:not([data-wk-gallery]) .photo-item {
  position: relative;
  margin: 0;
  cursor: zoom-in;
}
#gallery:not([data-wk-gallery]) .photo-item:nth-child(even) { margin-top: clamp(2.2rem, 10vw, 3.4rem); }
#gallery:not([data-wk-gallery]) .photo-item img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--pond-1);
  box-shadow: 0 0 0 6px var(--paper), 0 0 0 7.5px rgba(var(--ink-rgb), .16);
  transition: box-shadow .5s var(--ease-out);
}
#gallery:not([data-wk-gallery]) .photo-item:nth-child(4n+2) img { aspect-ratio: 1 / 1; border-radius: var(--pond-2); }
#gallery:not([data-wk-gallery]) .photo-item:nth-child(4n+3) img { aspect-ratio: 1 / 1.08; border-radius: var(--pond-3); }
#gallery:not([data-wk-gallery]) .photo-item:nth-child(4n) img   { border-radius: var(--pond-4); }
@media (hover: hover) {
  #gallery:not([data-wk-gallery]) .photo-item:hover img { box-shadow: 0 0 0 6px var(--paper), 0 0 0 7.5px var(--accent); }
}
#gallery:not([data-wk-gallery]) .pond-caption {
  margin-top: .9rem;
  font-size: .86rem;
  line-height: 1.5;
  color: var(--ink-soft);
  text-align: center;
}
#gallery:not([data-wk-gallery]) .pond-caption:empty { display: none; }
@media (min-width: 720px) {
  #gallery:not([data-wk-gallery]) .photo-grid { grid-template-columns: repeat(3, 1fr); max-width: 52rem; margin-inline: auto; }
  #gallery:not([data-wk-gallery]) .photo-item:nth-child(even) { margin-top: 0; }
  #gallery:not([data-wk-gallery]) .photo-item:nth-child(3n+2) { margin-top: 3.4rem; }
}
/* fx 模式：GSAP 接手照片進場（transition 讓位，避免雙重 easing） */
html.fx #gallery:not([data-wk-gallery]) .photo-item.reveal {
  transition: none;
  transform: none;
  opacity: 0;
}

/* Lightbox */
.wk-lightbox {
  position: fixed; inset: 0; z-index: 100;
  display: none;
  align-items: center; justify-content: center;
  background: rgba(28, 24, 20, .94);
  padding: max(4vh, env(safe-area-inset-top, 0px)) max(4vw, env(safe-area-inset-right, 0px))
           max(4vh, env(safe-area-inset-bottom, 0px)) max(4vw, env(safe-area-inset-left, 0px));
  overscroll-behavior: contain;
}
.wk-lightbox.is-open { display: flex; }
.wk-lightbox img {
  max-width: 100%; max-height: 88dvh;
  margin-inline: auto;
  border-radius: var(--radius);
}
.wk-lightbox button {
  position: absolute;
  color: #f5f3ee;
  font-size: 2rem;
  line-height: 1;
  padding: .75rem 1rem;
  min-width: 44px; min-height: 44px;
  display: grid; place-items: center;
  opacity: .8;
  transition: opacity .3s var(--ease-out);
}
.wk-lightbox button:hover { opacity: 1; }
.wk-lightbox button:active { opacity: 1; scale: .92; }
.wk-lb-close { top: max(.5rem, env(safe-area-inset-top, 0px)); right: max(.75rem, env(safe-area-inset-right, 0px)); font-size: 2.4rem; }
.wk-lb-prev { left: max(.25rem, env(safe-area-inset-left, 0px)); top: 50%; transform: translateY(-50%); }
.wk-lb-next { right: max(.25rem, env(safe-area-inset-right, 0px)); top: 50%; transform: translateY(-50%); }

/* ============ ③b 婚禮影片（P2-20）：facade ============ */
.video-frame {
  position: relative;
  margin-top: clamp(.5rem, 2vw, 1rem);
  aspect-ratio: 16 / 9;
  background: #000;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 0 0 6px var(--paper), 0 0 0 7.5px rgba(var(--ink-rgb), .16);
}
/* 直式（Shorts／手機拍的成長 MV）：寬度上限鎖 rem、置中（2026-08-25 各套統一） */
.video-frame[data-ratio="portrait"] {
  aspect-ratio: 9 / 16;
  width: min(100%, 20rem);
  margin-inline: auto;
}
/* 沒有官方縮圖的平台：藏掉 <img>，露出框自己的深底當色塊（播放鈕是白的，底一定要深） */
.video-frame.is-noposter { background: #000; }
.video-frame.is-noposter .video-poster { display: none; }
/* Instagram embed 自帶頂欄底欄：框要比 9:16 更高（放在 portrait 之後，同分靠順序贏） */
.video-frame[data-provider="instagram"] {
  aspect-ratio: 10 / 19;
  width: min(100%, 20rem);
  margin-inline: auto;
}
.video-facade {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  padding: 0;
  border: 0;
  background: none;
  display: block;
  cursor: pointer;
}
.video-poster { width: 100%; height: 100%; object-fit: cover; display: block; }
.video-facade::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, .22);
}
.video-play-ico {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: 3.6rem; height: 3.6rem;
  z-index: 1;
  color: #f5f3ee;
  border: 1.5px solid rgba(245, 243, 238, .8);
  border-radius: 50%;
}
.video-play-ico::before {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-38%, -50%);
  border-style: solid;
  border-width: .48rem 0 .48rem .84rem;
  border-color: transparent transparent transparent currentColor;
}
.video-frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }
@media (hover: hover) {
  .video-facade:hover::after { background: rgba(0, 0, 0, .12); }
}
.video-facade:focus-visible { outline: 2px solid var(--accent-tx); outline-offset: 3px; }

/* ============ ④ Event Details ============ */
.details-date {
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.25rem, 5vw, 1.6rem) * var(--wk-fs-heading, 1));
  font-weight: 600;
  letter-spacing: .06em;
  line-height: 1.5;
}
.details-note { color: var(--ink-soft); font-size: var(--text-small); margin-top: .5rem; white-space: pre-line; }
.details-note:empty { display: none; }

/* ---------- 視覺化婚期月曆（2026-08-17）----------
   ⚠ 絕不對 .wk-cal 宣告 display——顯隱靠 [hidden] 的 display:none !important
     （子元素才可以用 grid）。顏色只用既有 token。 */
.wk-cal {
  width: 100%;
  max-width: 19rem;
  margin: clamp(2rem, 6vw, 2.8rem) auto 0;
  padding: clamp(1.2rem, 4vw, 1.6rem) clamp(1rem, 3.5vw, 1.4rem);
  background: var(--paper-raise);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .1);
  text-align: center;
}
.wk-cal-title {
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-size: calc(clamp(1.4rem, 5.6vw, 1.7rem) * var(--wk-fs-latin, 1));
  font-weight: 600;
  line-height: 1;
  letter-spacing: .16em;
  text-indent: .16em;
  color: var(--accent-tx);
  font-variant-numeric: lining-nums tabular-nums;
}
.wk-cal-grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: .15rem;
  margin-top: clamp(.9rem, 3.5vw, 1.2rem);
  font-variant-numeric: lining-nums tabular-nums;
}
.wk-cal-wd, .wk-cal-d, .wk-cal-pad { position: relative; }
.wk-cal-wd { padding-bottom: .5rem; color: var(--ink-soft); font-size: .7rem; letter-spacing: .04em; }
.wk-cal-d, .wk-cal-pad { aspect-ratio: 1; }
.wk-cal-d { display: grid; place-items: center; font-size: clamp(.84rem, 3.3vw, .95rem); }
/* 婚期：實心愛心＋反白數字。圖形在 shared/utils.js 的 calHeart()，這裡只上色
   （用 --accent-tx：反白數字壓在上面要 ≥4.5）。 */
.wk-cal-d.is-day { color: var(--accent-ink); font-weight: 500; }
.wk-cal-heart {
  position: absolute;
  top: 0;
  left: 50%;
  width: 120%;
  max-width: none;      /* base.css 的 svg{max-width:100%} 會把愛心夾回格寬，必須解開 */
  transform: translateX(-50%);
  z-index: 0;
  pointer-events: none;
}
.wk-cal-heart path { fill: var(--accent-tx); }
.wk-cal-n { position: relative; z-index: 1; }
.wk-cal-count {
  display: inline-block;
  margin-top: clamp(.9rem, 3.5vw, 1.2rem);
  padding: .2rem .9rem;
  border-radius: var(--radius-pill);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .14);
  color: var(--ink-soft);
  font-size: var(--text-small);
}

/* 時程表：左時間、右項目，一行一道細線 */
.event-list {
  margin: clamp(2.2rem, 7vw, 3rem) auto 0;
  max-width: 24rem;
  display: grid;
  text-align: left;
}
.event-row {
  display: grid;
  grid-template-columns: 4.4rem 1fr;
  column-gap: 1.1rem;
  align-items: baseline;
  padding-block: clamp(.95rem, 3.4vw, 1.25rem);
  border-top: 1px solid var(--hairline);
}
.event-row:last-child { border-bottom: 1px solid var(--hairline); }
.event-row dt {
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-size: calc(1.1rem * var(--wk-fs-latin, 1));
  font-weight: 600;
  color: var(--accent-tx);
  font-variant-numeric: tabular-nums;
  letter-spacing: .06em;
}
.event-row dd strong {
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.08rem, 3vw, 1.2rem) * var(--wk-fs-heading, 1));
  font-weight: 600;
  letter-spacing: .04em;
  display: block;
}
.event-row dd span { color: var(--ink-soft); font-size: var(--text-small); }

.calendar-actions {
  display: flex; flex-wrap: wrap; gap: .75rem; justify-content: center;
  margin-top: clamp(2.2rem, 7vw, 3rem);
}
.parents {
  margin-top: clamp(2.2rem, 7vw, 3rem);
  color: var(--ink-soft);
  font-size: var(--text-small);
  line-height: 2;
}
.parents:empty { display: none; }

/* ============ ④b Dress Code：一顆一顆的卵石 ============ */
.dc-note { max-width: 26em; margin-inline: auto; color: var(--ink-soft); white-space: pre-line; }
.dc-note:empty { display: none; }
.dc-swatches {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: clamp(.9rem, 4vw, 1.3rem);
  margin-top: clamp(1.8rem, 6vw, 2.4rem);
}
.dc-swatch {
  width: clamp(3.2rem, 15vw, 3.8rem);
  aspect-ratio: 1;
  border-radius: var(--pond-1);
  box-shadow: 0 0 0 4px var(--paper), 0 0 0 5.5px rgba(var(--ink-rgb), .16);
}
.dc-swatch:nth-child(4n+2) { border-radius: var(--pond-2); }
.dc-swatch:nth-child(4n+3) { border-radius: var(--pond-3); }
.dc-swatch:nth-child(4n)   { border-radius: var(--pond-4); }

/* ============ ⑤b 自訂區塊 ============ */
.custom-text { max-width: 28em; margin-inline: auto; white-space: pre-line; }
.custom-text:empty { display: none; }
.custom-block + .custom-block { margin-top: clamp(2.6rem, 8vw, 3.6rem); }
.custom-block-card {
  padding: clamp(1.6rem, 6vw, 2.2rem) clamp(1.3rem, 5vw, 2rem);
  background: var(--paper-raise);
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .1);
}
.custom-block-quote .custom-text {
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.1rem, 4vw, 1.25rem) * var(--wk-fs-heading, 1));
  line-height: 2;
  letter-spacing: .04em;
}
.custom-block-quote::before {
  content: "";
  display: block;
  width: 2.2rem; height: 9px;
  margin: 0 auto 1.2rem;
  background: var(--accent);
  -webkit-mask: var(--wave) 0 50% / 34px 9px repeat-x;
  mask: var(--wave) 0 50% / 34px 9px repeat-x;
}
/* 錨點宿主：插在別的區塊之後的自訂筆（不是 section、沒有自己的水線） */
.custom-anchored { padding-bottom: var(--section-y); text-align: center; }
.custom-img {
  display: block;
  width: min(100%, 22rem);
  margin: 0 auto clamp(1.2rem, 4vw, 1.6rem);
  border-radius: var(--radius);
}
.custom-block-side::after { content: ""; display: block; clear: both; }
@media (min-width: 560px) {
  .custom-block-side .custom-img { float: left; width: 42%; margin: .3rem 1.6rem .8rem 0; }
  .custom-block-side .custom-text { text-align: left; }
}

/* ============ ⑤c 悄悄話：刮開水面 ============ */
.secret .scratch-card {
  background: var(--paper-raise);
  border-radius: calc(var(--radius) * 1.15);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .12);
}
.secret .scratch-text {
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(1.08rem * var(--wk-fs-heading, 1));
  color: var(--ink);
  white-space: pre-line;      /* 長悄悄話的換行靠這條（功能，不是裝飾，別刪） */
}
.secret .scratch-hint { color: var(--ink); font-family: var(--wk-f-body, var(--font-round)); letter-spacing: .12em; text-indent: .12em; }
.secret .scratch-open { color: var(--ink-soft); border-radius: var(--radius-pill); min-height: 44px; }

/* ============ ⑤ Venue ============ */
.venue-hall { color: var(--ink-soft); }
.venue-hall:empty { display: none; }
.venue-address { color: var(--ink-soft); font-size: var(--text-small); margin-top: .2rem; }
/* 雙地點的標籤（「證婚」「宴客」）：一顆小膠囊。⚠ 絕不宣告 display（會蓋掉 [hidden]） */
.venue-tag {
  width: fit-content;
  margin: 0 auto .8rem;
  padding: .15rem .9rem;
  font-size: var(--text-small);
  color: var(--accent-tx);
  border-radius: var(--radius-pill);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .16);
}
/* 第二地點卡：上面隔一小段水波。⚠ 絕不宣告 display（同上） */
.venue--dual .venue-card2 {
  margin-top: clamp(3rem, 9vw, 4rem);
  padding-top: clamp(3rem, 9vw, 4rem);
  border-top: 1px solid var(--hairline);
}
.venue2-name {
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.3rem, 4.8vw, 1.7rem) * var(--wk-fs-heading, 1));
  font-weight: 600;
  letter-spacing: .08em;
  line-height: 1.5;
}
.map-frame {
  margin-top: clamp(1.75rem, 5vw, 2.5rem);
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
  overflow: hidden;           /* iframe 是替換元素，不加就會方方正正地戳出圓角 */
  box-shadow: 0 0 0 6px var(--paper), 0 0 0 7.5px rgba(var(--ink-rgb), .16);
}
@media (min-width: 700px) { .map-frame { aspect-ratio: 16 / 8; } }
.map-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.venue-bottom {
  margin: clamp(1.9rem, 5.5vw, 2.6rem) auto 0;
  max-width: 28rem;
  display: grid;
  gap: 1.6rem;
  justify-items: center;
}
.transport { display: grid; gap: 0; width: 100%; text-align: left; }
.transport:empty { display: none; }
.transport li {
  display: grid; grid-template-columns: 3.6rem 1fr; gap: 1rem; align-items: baseline;
  padding-block: .95rem;
  border-top: 1px solid var(--hairline);
}
.transport li:last-child { border-bottom: 1px solid var(--hairline); }
.transport strong { font-weight: 500; color: var(--accent-tx); font-size: var(--text-small); }
.transport span { color: var(--ink-soft); white-space: pre-line; }

/* ============ ⑥ RSVP：比較深的那一池水 ============
   水線以上（水獺露出來的那一段）是紙色，水線以下換成 --sand。
   ⚠ background-color 單獨寫：computed 的 backgroundColor 仍是 --sand，
     表單文字的對比基準就是它（ux-check 量的那一層）。 */
.rsvp { background-color: var(--sand); }
.rsvp > .tide { background: linear-gradient(to bottom, var(--paper) calc(100% - 26px), transparent calc(100% - 26px)); }
/* 紙色與 --sand 的交界不是一條直線：這一區的 .sea 改成實心的 --sand，上緣就是水線的波形，
   蓋住上面那道漸層的直邊（直邊在離底 26px，.sea 從離底 38px 蓋到 12px）。 */
.rsvp > .tide .sea { height: 26px; opacity: 1; }
.rsvp > .tide .sea i { background: var(--sand); }
.rsvp-intro { max-width: 28em; margin-inline: auto; white-space: pre-line; }
.rsvp-deadline { font-size: var(--text-small); color: var(--ink-soft); margin-top: .5rem; }

/* 送出回覆之後：舉卡片的那一對換成歡呼的那一對（#rsvp.is-sent 由 render.js 掛） */
.duo-pose--yay { display: none; }
.rsvp.is-sent .duo-pose--ask { display: none; }
.rsvp.is-sent .duo-pose--yay { display: block; }

.rsvp-form {
  margin-top: clamp(2.2rem, 7vw, 3rem);
  padding: clamp(1.6rem, 6vw, 2.4rem) clamp(1.2rem, 5vw, 2rem);
  display: grid;
  gap: 1.5rem;
  text-align: left;
  background: var(--paper-raise);
  border-radius: calc(var(--radius) * 1.15);
  box-shadow: var(--shadow-soft);
}
.rsvp .field label, .rsvp .field legend {
  font-size: .95rem;
  letter-spacing: .02em;
  color: var(--ink);
  font-weight: 500;
}
/* ⚠ input 字級一律 ≥16px（base.css 給的 var(--text-body)，最小 1rem）：低於它 iOS
     會自動縮放整頁。這裡不宣告 font-size＝繼承 body，天生安全。 */
.rsvp .field input, .rsvp .field textarea, .rsvp .field select {
  background: var(--paper);
  border: 1.5px solid rgba(var(--ink-rgb), .2);
}
.field input:focus-visible, .field textarea:focus-visible, .field select:focus-visible {
  border-color: var(--accent-tx);
  box-shadow: 0 0 0 3px rgba(var(--ink-rgb), .1);
}
.rsvp .field select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8'%3E%3Cpath d='M1 1.5 L6 6.5 L11 1.5' fill='none' stroke='%23555' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1rem center;
  padding-right: 2.6rem;
}
.attend-field { border: 0; }
.companion-list { display: grid; gap: .5rem; }
[data-rsvp-custom] { display: grid; gap: 1.5rem; }
.cq-checks { display: grid; gap: .5rem; }
.rsvp .cq-checks label { display: flex; align-items: center; gap: .5rem; font-size: 1rem; cursor: pointer; }
.rsvp .cq-checks input[type="checkbox"] { width: 1.15rem; height: 1.15rem; flex: none; accent-color: var(--accent-tx); }

.attend-options { display: grid; grid-template-columns: 1fr 1fr; gap: .75rem; margin-top: .6rem; }
.attend-pill input { position: absolute; opacity: 0; pointer-events: none; }
.attend-pill span {
  display: flex; align-items: center; justify-content: center;
  min-height: 48px;
  padding: .7rem 1rem;
  background: var(--paper);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .2);
  border-radius: var(--radius-pill);
  letter-spacing: .06em;
  transition: background .35s var(--ease-out), color .35s var(--ease-out), box-shadow .35s var(--ease-out), scale .2s var(--ease-out);
  user-select: none;
}
.attend-pill:active span { scale: .97; }
.attend-pill input:checked + span {
  background: var(--accent-tx);
  color: var(--accent-ink);
  box-shadow: none;
}
.attend-pill input:focus-visible + span { box-shadow: 0 0 0 3px rgba(var(--ink-rgb), .18); }

.rsvp-details { display: grid; gap: 1.5rem; }
.field-row { display: grid; gap: 1.5rem; }
@media (min-width: 520px) { .field-row { grid-template-columns: repeat(3, 1fr); } }

/* stepper：−/＋ 直覺加減（手機免打字） */
.stepper {
  display: grid;
  grid-template-columns: 3rem 1fr 3rem;
  align-items: stretch;
  min-height: 48px;
  background: var(--paper);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .2);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.rsvp .field .stepper input {
  border: 0;
  background: transparent;
  text-align: center;
  padding-inline: .25rem;
}
.stepper input::-webkit-outer-spin-button,
.stepper input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper input[type="number"] { -moz-appearance: textfield; }
.rsvp .field .stepper input:focus-visible { box-shadow: none; border-color: transparent; }
.stepper:focus-within { box-shadow: inset 0 0 0 1.5px var(--accent-tx), 0 0 0 3px rgba(var(--ink-rgb), .1); }
.stepper button {
  border: 0;
  background: transparent;
  cursor: pointer;
  color: var(--ink);
  font-size: 1.2rem;
  line-height: 1;
  transition: background .3s var(--ease-out), opacity .3s var(--ease-out);
}
.stepper button:active { background: rgba(var(--ink-rgb), .08); }
.stepper button[disabled] { opacity: .3; cursor: default; }
.cap-note { font-size: var(--text-small); color: var(--ink-soft); }

.rsvp-error { color: var(--warn); font-size: var(--text-small); }
.rsvp-submit { width: 100%; min-height: 52px; letter-spacing: .1em; }

.rsvp-success, .rsvp-closed {
  position: relative;
  margin-top: clamp(2.2rem, 7vw, 3rem);
  text-align: center;
  display: grid;
  gap: .75rem;
  justify-items: center;
  padding: clamp(2rem, 6vw, 3rem) var(--gutter);
  background: var(--paper-raise);
  border-radius: calc(var(--radius) * 1.15);
  box-shadow: var(--shadow-soft);
}
.success-mark {
  width: 2.8rem; height: 2.8rem;
  display: grid; place-items: center;
  background: var(--accent-tx);
  color: var(--accent-ink);
  font-size: 1.3rem;
  border-radius: 50%;
}
.rsvp-success h3, .rsvp-closed h3 {
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(clamp(1.2rem, 3.2vw, 1.4rem) * var(--wk-fs-heading, 1));
  font-weight: 600;
  letter-spacing: .06em;
}
/* 紀念章：一顆圓章，裡面一小段水波＋see you there（落章動畫在 base.css wk-stamp-drop） */
.rsvp-stamp {
  --stamp-rot: -9deg;
  position: absolute;
  top: .9rem; right: .9rem;
  width: 4.4rem; height: 4.4rem;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: .3rem;
  border: 1.5px solid var(--accent-tx);
  border-radius: 50%;
  color: var(--accent-tx);
}
.rsvp-stamp i {
  width: 1.7rem; height: 7px;
  background: currentColor;
  -webkit-mask: var(--wave) 0 50% / 27px 7px repeat-x;
  mask: var(--wave) 0 50% / 27px 7px repeat-x;
}
.rsvp-stamp em {
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-style: normal;
  font-weight: 600;
  font-size: .56rem;
  line-height: 1.2;
  max-width: 3.2rem;
  text-transform: lowercase;
  letter-spacing: .1em;
}
.success-note { font-size: var(--text-small); color: var(--ink-soft); }

/* ============ ⑦ Footer：靠岸 ============ */
.footer {
  padding-bottom: max(3.2rem, env(safe-area-inset-bottom, 0px) + 2.4rem);
  text-align: center;
}
.footer-yours {
  font-family: var(--wk-f-latin, var(--font-serif-latin));
  font-size: calc(.8rem * var(--wk-fs-latin, 1));
  font-weight: 600;
  letter-spacing: .26em;
  text-indent: .26em;
  text-transform: lowercase;
  color: var(--accent-tx);
}
.footer-message { max-width: 24em; margin: 1.2rem auto 0; white-space: pre-line; }
.footer-names {
  margin-top: 1rem;
  font-family: var(--wk-f-heading, var(--font-serif-tc));
  font-size: calc(1.1rem * var(--wk-fs-heading, 1));
  font-weight: 600;
  letter-spacing: .1em;
}
.footer-brand {
  display: inline-block;
  margin-top: 2.4rem;
  padding: .6rem 1rem;
  min-height: 44px;
  line-height: calc(44px - 1.2rem);
  font-size: var(--text-tiny);
  color: var(--ink-soft);
  text-decoration: none;
  letter-spacing: .06em;
}
.footer-brand:hover { color: var(--ink); }

/* ---------- 終章祝福「繫海帶」 ----------
   按住不放：海帶（.kelp-line）沿著路徑畫出來（stroke-dashoffset 吃 WK.press 的 --wk-press）；
   蓄滿（.is-committed）：打結（.kelp-knot 彈出來）、結上掛賓客名字的小牌子（.kelp-tag）。
   沒蓄滿放手＝--wk-press 歸零，海帶收回去。RM＝單點即完成（WK.press 內建）。 */
.blessing { margin-top: clamp(2.6rem, 8vw, 3.4rem); }
.blessing-lead {
  max-width: 19em;
  margin-inline: auto;
  font-family: var(--wk-f-body, var(--font-round));
  font-size: .98rem;
  line-height: 1.7;
  color: var(--ink-soft);
}
.blessing-seal {
  display: block;
  width: min(100%, 20rem);
  margin: 1rem auto 0;
  padding: .6rem 0 0;
  cursor: pointer;
  touch-action: none;                 /* 按住蓄力期間不捲頁（小面積按鈕才准用 none） */
  -webkit-user-select: none;
  user-select: none;
  -webkit-touch-callout: none;
  -webkit-tap-highlight-color: transparent;
  border-radius: var(--radius);
}
.blessing-seal:focus-visible { outline: 2px solid var(--accent-tx); outline-offset: 4px; }
.kelp-stage { position: relative; display: block; }
.kelp {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  max-width: none;
  overflow: visible;
  pointer-events: none;
  fill: none;
  stroke: var(--accent-tx);
  stroke-width: 5;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.kelp-line {
  stroke-dasharray: 1;
  stroke-dashoffset: calc(1 - var(--wk-press, 0));
  transition: stroke-dashoffset .35s var(--ease-out);   /* 放手沒蓄滿：收回去 */
}
.blessing-seal.is-pressing .kelp-line { transition: none; }
.blessing-seal.is-committed .kelp-line { stroke-dashoffset: 0; }
.kelp-knot {
  fill: var(--paper-raise);
  opacity: 0;
  transform-box: fill-box;
  transform-origin: 50% 40%;
  transform: scale(.3);
}
.blessing-seal.is-committed .kelp-knot { animation: du-knot .5s cubic-bezier(.3, 1.6, .5, 1) both; }
@keyframes du-knot {
  from { opacity: 0; transform: scale(.3); }
  to   { opacity: 1; transform: none; }
}
/* 結上的小牌子：賓客的名字（render.js 填；沒有名字就不出現） */
.kelp-tag {
  position: absolute;
  left: 50%;
  top: 76%;
  translate: -50% 0;
  padding: .12rem .7rem;
  white-space: nowrap;
  font-family: var(--wk-f-body, var(--font-round));
  font-size: .82rem;
  color: var(--accent-tx);
  background: var(--paper-raise);
  border: 1.5px solid var(--accent-tx);
  border-radius: var(--radius-pill);
  opacity: 0;
  pointer-events: none;
}
.kelp-tag:empty { display: none; }
.blessing-seal.is-committed .kelp-tag { animation: du-tag .5s var(--ease-out) .25s both; }
@keyframes du-tag {
  from { opacity: 0; transform: translateY(-8px) rotate(-6deg); }
  to   { opacity: 1; transform: rotate(-3deg); }
}
.blessing-hint {
  margin-top: 1.4rem;
  font-family: var(--wk-f-body, var(--font-round));
  font-size: .92rem;
  color: var(--accent-tx);
}
.blessing-seal.wants-hold ~ .blessing-hint { animation: du-nudge .5s var(--ease-out); }
@keyframes du-nudge {
  30% { transform: translateY(-4px); }
  60% { transform: translateY(2px); }
}
.blessing-done {
  margin-top: 1.4rem;
  font-family: var(--wk-f-body, var(--font-round));
  color: var(--ink);
}
.blessing:not(.is-sealed) .blessing-done { display: none; }
.blessing.is-sealed .blessing-hint { display: none; }
.blessing.is-sealed .blessing-done { animation: du-pop .4s cubic-bezier(.3, 1.4, .5, 1); }

/* 彩蛋：漂流瓶 */
.ps-egg {
  display: block;
  margin: 2rem auto 0;
  min-height: 44px;
  padding: .5rem 1.1rem;
  font-family: var(--wk-f-body, var(--font-round));
  font-size: .86rem;
  color: var(--ink-soft);
  border-radius: var(--radius-pill);
  box-shadow: inset 0 0 0 1.5px rgba(var(--ink-rgb), .14);
}
.ps-egg:hover { color: var(--ink); }
.ps-egg-text {
  max-width: 22em;
  margin: 2rem auto 0;
  font-family: var(--wk-f-body, var(--font-round));
  font-size: .95rem;
  color: var(--ink);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity .6s var(--ease-out), transform .6s var(--ease-out);
}
.ps-egg-text.is-open { opacity: 1; transform: none; }

/* 粒子的皮（WKFX.tapBurst／burstAt 的物件池；位置與動畫由 shared/fx.js 管）：
   水滴三種大小＋愛心。顏色走 token。 */
.wk-drop-p1, .wk-drop-p2, .wk-drop-p3 {
  background: var(--accent);
  border-radius: 50% 50% 50% 50% / 62% 62% 38% 38%;
}
.wk-drop-p1 { width: 7px; height: 9px; opacity: .85; }
.wk-drop-p2 { width: 5px; height: 6px; opacity: .6; }
.wk-drop-p3 { width: 9px; height: 11px; opacity: .45; }
.wk-heart-p {
  width: 13px; height: 12px;
  background: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 22'%3E%3Cpath d='M12 21C5 15 1 11.5 1 7a5.5 5.5 0 0 1 11-1.2A5.5 5.5 0 0 1 23 7c0 4.5-4 8-11 14z'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 22'%3E%3Cpath d='M12 21C5 15 1 11.5 1 7a5.5 5.5 0 0 1 11-1.2A5.5 5.5 0 0 1 23 7c0 4.5-4 8-11 14z'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* 點水面的漣漪（fx.js 動態掛在自己的圖層上；z-index 夾在內容與開場層之間） */
.du-ring-layer {
  position: fixed;
  inset: 0;
  z-index: 69;
  pointer-events: none;
  overflow: hidden;
}
.du-ring {
  position: absolute;
  left: 0; top: 0;
  width: 44px; height: 18px;
  margin: -9px 0 0 -22px;
  border: 1.5px solid var(--accent);
  border-radius: 50%;
  opacity: 0;
  will-change: transform, opacity;
}

/* 新人聯絡方式 icon 連結（頁尾；填了才由 render.js 生成，空則 [hidden]） */
.footer-contacts { margin-top: 2rem; display: flex; flex-direction: column; align-items: center; gap: .55rem; }
.footer-contacts[hidden] { display: none; }
.contact-row { display: inline-flex; align-items: center; gap: .7rem; }
.contact-who { font-size: var(--text-tiny); letter-spacing: .02em; color: var(--ink-soft); }
.contact-ico { display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; margin-block: -12px; color: var(--ink-soft); transition: color .3s var(--ease-out), transform .3s var(--ease-out); }
.contact-ico + .contact-ico { margin-left: -.7rem; }
.contact-ico svg { width: 20px; height: 20px; display: block; }
.contact-ico:hover { color: var(--accent-tx); transform: translateY(-1px); }

/* ============ shared 婚紗照版式的覆寫（/shared/gallery-layouts.css） ============
   只有 config.gallery.layout 選了非原生版式時 #gallery 才有 data-wk-gallery。
   ⚠ 特異度 (1,1,0) 高於 gallery-layouts.css :root 的預設 (0,1,0)——那份檔案比本檔
     晚載入，靠順序是蓋不過去的，一定要用這個選擇器。 */
#gallery[data-wk-gallery] {
  --gl-frame-shadow: var(--shadow-soft);
}

/* ---------- reduced-motion：這一款不降級 ----------
   Jacob 2026-10-01 裁定（他手機常年開著「減少動態效果」）：水不流、角色不眨眼、開場不漂——
   這一款就只剩一張靜態的圖。所以這裡沒有 @media (prefers-reduced-motion) 區塊：
   index.html 的旗標 script 在 <html> 掛 wk-keep-motion，base.css 的 RM 通殺整段讓位、
   WKFX（GSAP 層）與 WK.press 也照常；開場、水線、零件動畫、點角色全部跟一般模式同一套。
   動態本來就克制：沒有視差、沒有 scroll-scrub，會一直動的只有水線與角色的小動作，
   大位移只有開場那一秒（而且是賓客自己推的）。?preview=1 不掛旗標＝編輯器預覽照樣素顏。
   舊的「零位移精緻版」（.intro-glow 光層＋du-rm-* keyframes）留在 commit 816757f8，
   要恢復尊重 RM 就從那裡撈回來，並拿掉旗標 script 的 wk-keep-motion。 */

/* ============================================================
   字型庫 bridge（P2-26，2026-08-24）——由 shared/fonts.js（WKFONTS）驅動
   font-family 已經在各條規則用 var(--wk-f-<role>, 原值) 包好；
   字級倍率（--wk-fs-*）也已經乘在各自的 font-size 上。
   這一段補的是字距／行距／字重／斜體：新人沒調的時候 <html> 上沒有對應的
   gate 屬性，整條規則選不到＝本檔上面的原值一個都不動。
   ⚠ gate 屬性名與變數名都由引擎決定（tools/fonts-check.mjs 會逐條核對），別改拼法。
   ============================================================ */
html[data-wkf-ls-heading] :is(.hero-names, .section-title, .tl-title, .event-row dd strong, .venue2-name, .footer-names, .details-date, .intro-names, .rsvp-success h3, .rsvp-closed h3) { letter-spacing: var(--wk-fls-heading); }
html[data-wkf-lh-heading] :is(.hero-names, .section-title, .tl-title, .event-row dd strong, .venue2-name, .footer-names, .details-date, .intro-names, .rsvp-success h3, .rsvp-closed h3) { line-height: var(--wk-flh-heading); }
html[data-wkf-w-heading] :is(.hero-names, .section-title, .tl-title, .event-row dd strong, .venue2-name, .footer-names, .details-date, .intro-names, .rsvp-success h3, .rsvp-closed h3) { font-weight: var(--wk-fw-heading); }

html[data-wkf-ls-body] :is(body, .tl-text, .hero-note, .venue-tag, .duo-say) { letter-spacing: var(--wk-fls-body); }
html[data-wkf-lh-body] :is(body, .tl-text, .hero-note, .venue-tag, .duo-say) { line-height: var(--wk-flh-body); }
html[data-wkf-w-body] :is(body, .tl-text, .hero-note, .venue-tag, .duo-say) { font-weight: var(--wk-fw-body); }

html[data-wkf-ls-latin] :is(.label-en, .hero-label, .hero-names-en, .cd-cell strong, .tl-date, .wk-cal-title, .event-row dt, .footer-yours) { letter-spacing: var(--wk-fls-latin); }
html[data-wkf-lh-latin] :is(.label-en, .hero-label, .hero-names-en, .cd-cell strong, .tl-date, .wk-cal-title, .event-row dt, .footer-yours) { line-height: var(--wk-flh-latin); }
html[data-wkf-w-latin] :is(.label-en, .hero-label, .hero-names-en, .cd-cell strong, .tl-date, .wk-cal-title, .event-row dt, .footer-yours) { font-weight: var(--wk-fw-latin); }
html[data-wkf-i-latin] :is(.label-en, .hero-label, .hero-names-en, .cd-cell strong, .tl-date, .wk-cal-title, .event-row dt, .footer-yours) { font-style: italic; }
