/* 四十二道流光 抽牌工具頁樣式。色票/動效 token 取自 css/tools-shared.css（--lf-*），
   本頁只放版面與抽牌流程專屬樣式。視覺插畫尚未產出，卡面一律先用 gate palette 漸層佔位。 */

.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* 全站水平溢出防呆：曾實測 375px 下舊版 .gate-glow(130% 寬) 造成 3px 橫向溢出。
   用 overflow-x:hidden 會有副作用：CSS Overflow spec 規定 x/y 其中一軸是 hidden、
   另一軸維持預設 visible 時，visible 那軸的 used value 會被迫轉成 auto——這會讓
   html/body 變成一個新的捲動容區，導致 #gates-journey 裡 position:sticky 完全失效
   （.gate-stage 實測 rect.top 會一路變負值、不會卡在 0）。改用 overflow-x:clip 才能
   「只擋橫向溢出、不動 y 軸 used value」，sticky 才會正常黏住。 */
html, body { overflow-x: clip; }

/* 七道門溫柔吸附：scroll-snap-type 必須掛在「實際捲動的容器」上才有效——本頁沒有
   任何 overflow:auto/scroll 的內層容器，document.scrollingElement 量測證實真正捲動的
   是 <html>，掛在 #gates-journey（overflow:visible、非捲動容器）上完全無效
   （2026-07-24 實測：掛在 #gates-journey 上，wheel/scrollTo 逼近門起點前 20px 皆不吸附）。
   proximity（非 mandatory）＝只在自然停靠點附近才吸附，不強制吃掉捲動意圖，
   與 .gate-stage 的 position:sticky 共存無抖動/無卡死（同一批次 p/wrapProg 讀值不受影響）。 */
html { scroll-snap-type: y proximity; }

/* ---------- 場景背景層：依步驟切換 class（js/oracle.js setScene()，同 chakra.css .chakra-scene-bg pattern） ---------- */
.oracle-scene-bg {
  position: fixed;
  inset: 0;
  z-index: -2;
  pointer-events: none;
  background-size: cover;
  background-position: center;
}
/* 「抵達牌桌」場景：工具區入口(intro)＋緊接其後的安住呼吸(breathe)共用同一 SCENE_BY_STEP 分組，
   兩步都已走過七道門巡禮抵達儀式桌，共用背景圖敘事上是連續的，不需另開專屬 class。 */
.oracle-scene-bg--intro { background-image: linear-gradient(rgba(244, 235, 221, 0.65), rgba(244, 235, 221, 0.65)), url(../images/tools/oracle/ritual-table.webp); }
.oracle-scene-bg--shuffle { background-image: linear-gradient(rgba(244, 235, 221, 0.55), rgba(244, 235, 221, 0.55)), url(../images/tools/oracle/shuffle-background.webp); }
.oracle-scene-bg--reading { background-image: linear-gradient(rgba(244, 235, 221, 0.55), rgba(244, 235, 221, 0.55)), url(../images/tools/oracle/ritual-table.webp); }
/* .oracle-scene-bg--outro（hero-sanctuary 底圖）已於 2026-07-27 finale 併入 reading 捲軸後
   移除——SCENE_BY_STEP 不再有 finale key 會指到這裡（見 js/oracle.js 該常數註解），
   合流後的收尾層維持 reading 場景背景，不在捲動途中切換底圖。此規則本身沒有刪除，
   純粹因為沒有任何呼叫端會再套用這個 class，屬孤兒 CSS，工單回報中列出。 */

/* ---------- 入口雲煙紋理：雙拷貝無縫單向飄移（機制見 tools-shared.css .lf-fog-band 註解）----------
   兩層皆改用同一份底圖各自的 .lf-fog-tile 兩份拷貝、由 JS 之外純 CSS transform 驅動；
   舊版「來回擺盪」的 keyframe 已移除，改成等速單向、永遠接不上縫。
   px/s 換算（1440px 桌機實測基準，軌道寬度＝軌道自身 width%×viewport）：
   band--a 寬 130%×1440≈1872px、29s → ≈64.6px/s；
   band--b 寬 110%×1440≈1584px、37s → ≈42.8px/s。皆落在 40-70px/s 目標區間內。 */
/* ⚠️ absolute 不是 fixed：fixed 會讓霧層釘在「視窗」底部，往下捲時霧不動、七道門的滿版圖
   從它後面滑過去——那正是 user 反饋的「底圖怎麼移來移去」。改 absolute 後霧屬於引言區塊，
   引言捲走霧就一起走，只在該待的地方出現。父層 .gates-journey-intro 需 position:relative。 */
.oracle-entrance-fx { position: absolute; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
/* 三層混搭（2026-07-29 user 提議「新舊霧搭配比較有變化」）：
   a=新水彩淡紫雲（主體、最慢最大）、b=新水彩暖金霧（對向、中速）、
   c=舊的程式合成分形霧（細絲縷、最淡最快，補「近景細節」層次）。
   濃度全部下修（原 0.85 太濃、讀起來像貼一張圖）。
   速度（2026-07-29 user 反問「會不會太慢」→ 實測 26/17/32 px/s，一個特徵要 45-85 秒才橫越
   1440px 螢幕，確實讀不出在動）：三層各加速 ~1.65 倍 → 約 43/28/51 px/s。
   三個時長刻意保持非整數倍關係（44/58/34），否則三層會週期性對齊、看起來像同一片霧。 */
.lf-fog-band--a {
  width: 130%;
  height: 30vh;
  left: -15%;
  bottom: -2%;
  filter: blur(6px);
  opacity: 0.5;
  animation-duration: 44s;
}
.lf-fog-band--a .lf-fog-tile { background-image: var(--lf-fog-a); }
.lf-fog-band--b {
  width: 110%;
  height: 22vh;
  left: -15%;
  bottom: 12%;
  filter: blur(7px);
  opacity: 0.4;
  animation-duration: 58s;
  animation-direction: reverse;
}
.lf-fog-band--b .lf-fog-tile { background-image: var(--lf-fog-b); }
.lf-fog-band--c {
  width: 120%;
  height: 18vh;
  left: -15%;
  bottom: 4%;
  filter: blur(3px);
  opacity: 0.28;
  animation-duration: 34s;
}
.lf-fog-band--c .lf-fog-tile { background-image: var(--lf-fog-fine); }

/* ---------- 沉浸背景層：霧層 + 粒子（FX-001/002，純 CSS） ---------- */
.oracle-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: linear-gradient(180deg, var(--lf-warm-ivory) 0%, #fbf7f0 100%);
}

.oracle-fog {
  position: absolute;
  inset: -10%;
  background:
    radial-gradient(45% 35% at 20% 25%, rgba(198, 183, 218, 0.35), transparent 70%),
    radial-gradient(50% 40% at 80% 70%, rgba(216, 179, 106, 0.22), transparent 70%),
    radial-gradient(40% 30% at 55% 90%, rgba(175, 199, 213, 0.28), transparent 70%);
  filter: blur(30px);
  animation: oracle-fog-drift 40s var(--lf-ease-breath) infinite alternate;
}

@keyframes oracle-fog-drift {
  0% { transform: translate3d(0, 0, 0) scale(1); }
  100% { transform: translate3d(-2%, 2%, 0) scale(1.06); }
}

.oracle-particles { position: absolute; inset: 0; }
.oracle-particles span {
  position: absolute;
  bottom: -5%;
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--lf-champagne-gold);
  opacity: 0.55;
  animation: oracle-particle-rise 18s linear infinite;
}
/* ponytail: 12 顆手排 left/delay/size，行動裝置粒子數量克制（不用 JS 生成、不用 canvas） */
.oracle-particles span:nth-child(1) { left: 6%;  animation-delay: 0s;  }
.oracle-particles span:nth-child(2) { left: 14%; animation-delay: 2.4s; width:3px; height:3px; background: var(--lf-pale-lavender); }
.oracle-particles span:nth-child(3) { left: 23%; animation-delay: 5s;  }
.oracle-particles span:nth-child(4) { left: 32%; animation-delay: 1.2s; width:4px; height:4px; background: var(--lf-blush-rose); }
.oracle-particles span:nth-child(5) { left: 41%; animation-delay: 7.5s; }
.oracle-particles span:nth-child(6) { left: 50%; animation-delay: 3.6s; width:3px; height:3px; background: var(--lf-mist-blue); }
.oracle-particles span:nth-child(7) { left: 59%; animation-delay: 9s;  }
.oracle-particles span:nth-child(8) { left: 68%; animation-delay: 4.8s; width:4px; height:4px; background: var(--lf-pale-lavender); }
.oracle-particles span:nth-child(9) { left: 77%; animation-delay: 6.2s; }
.oracle-particles span:nth-child(10){ left: 85%; animation-delay: 0.8s; width:3px; height:3px; background: var(--lf-blush-rose); }
.oracle-particles span:nth-child(11){ left: 93%; animation-delay: 10.4s; }
.oracle-particles span:nth-child(12){ left: 38%; animation-delay: 13s; width:4px; height:4px; background: var(--lf-mist-blue); }

@keyframes oracle-particle-rise {
  0%   { transform: translateY(0) translateX(0);      opacity: 0; }
  10%  { opacity: 0.55; }
  90%  { opacity: 0.2; }
  100% { transform: translateY(-110vh) translateX(12px); opacity: 0; }
}

/* ---------- 版面 ---------- */
.oracle-main { position: relative; min-height: 60vh; }
.oracle-shell {
  max-width: 640px;
  margin: 0 auto;
  /* ponytail: top padding 蓋過站台 navbar(position:absolute,非文件流,實測
     <992px 高 101px/≥992px 高 121px)——每次 setStep() 都會捲回 shell 頂端,
     故用單一夠大的值涵蓋兩個斷點,不開 media query。 */
  padding: 10rem 1.25rem 4rem;
}

.oracle-step { display: block; }
.oracle-step[hidden] { display: none; }
.oracle-step.is-active { animation: oracle-step-in var(--lf-dur-slow) var(--lf-ease-breath); }
@keyframes oracle-step-in { from { opacity: 0; } to { opacity: 1; } }

/* 收尾層（2026-07-27 併入 reading 這條捲軸，見 oracle.html 巢狀結構＋ js finishReading()）：
   不是 .oracle-step、不受上面的 step-switch 系統管，只由 JS 直接 hidden=false 顯示——
   借同一顆 keyframe 給它一個輕量淡入，銜接感與其餘步驟切換一致。 */
#step-finale:not([hidden]) { animation: oracle-step-in var(--lf-dur-slow) var(--lf-ease-breath); }

#product-name {
  font-family: 'Cormorant Garamond', 'LXGW WenKai TC', serif;
  font-size: clamp(2rem, 6vw, 2.75rem);
  color: var(--lf-deep-lavender);
  text-align: center;
  margin-bottom: 0.5rem;
}
.oracle-positioning {
  text-align: center;
  color: var(--lf-earth-clay);
  font-style: italic;
  margin-bottom: 2.5rem;
}
.oracle-subhead {
  font-size: 1.05rem;
  color: var(--lf-deep-lavender);
  margin: 2rem 0 1rem;
}

/* ---------- 牌陣選擇 ---------- */
.spread-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 0.75rem;
}
@media (min-width: 560px) {
  .spread-grid { grid-template-columns: 1fr 1fr; }
}
.spread-option {
  text-align: left;
  border: 1px solid rgba(74, 58, 99, 0.2);
  background: rgba(255, 255, 255, 0.55);
  border-radius: 14px;
  padding: 0.9rem 1rem;
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.spread-option strong { color: var(--lf-deep-lavender); }
.spread-option .spread-meta { font-size: 0.85rem; color: var(--lf-earth-clay); }
.spread-option:hover { border-color: var(--lf-champagne-gold); }
.spread-option.is-selected {
  border-color: var(--lf-champagne-gold);
  background: rgba(216, 179, 106, 0.14);
}

.question-input {
  width: 100%;
  min-height: 4.5rem;
  border-radius: 12px;
  border: 1px solid rgba(74, 58, 99, 0.25);
  padding: 0.75rem 1rem;
  font-family: inherit;
  background: rgba(255, 255, 255, 0.6);
  resize: vertical;
}

.oracle-notice {
  font-size: 0.9rem;
  color: var(--lf-earth-clay);
  margin: 0.75rem 0;
}
.oracle-error { color: #a3423b; }
/* 選牌可取消的持續提示（工單 item 2，2026-07-28）：比 .oracle-notice 更小更淡，
   避免搶掉「還要選 N 張」的主提示。只在已選 ≥1 張時顯示，見 js updateSelectHint()。 */
.oracle-notice-sub { font-size: 0.78rem; opacity: 0.75; margin: -0.3rem 0 0.75rem; }

.btn.custom-btn { display: inline-block; }
.btn-outline-lf {
  display: inline-block;
  border: 1px solid var(--lf-deep-lavender);
  color: var(--lf-deep-lavender);
  background: transparent;
  border-radius: 999px;
  padding: 0.5rem 1.5rem;
}
.btn-link {
  background: none;
  border: none;
  color: var(--lf-earth-clay);
  text-decoration: underline;
  cursor: pointer;
  padding: 0.5rem;
}

#step-intro, #step-finale { text-align: center; }
.spread-grid, .question-input { text-align: left; }

/* ---------- 安住呼吸 ---------- */
/* 2026-07-28（工單 item 1，與 chakra.html 同步同一套機制；後追加反饋「4s/4s 太快」，
   改吸 5s／吐 7s）：光暈只認 class、時鐘完全交給 JS：吸氣加 .is-inhale、吐氣加
   .is-exhale，transform/opacity 用 transition 過渡，JS 在同一個 tick 換 class 也
   觸發吸吐文字交叉淡出淡入＋輪次指示（見 js/oracle.js startBreath/applyBreathPhase）
   ——光暈視覺（漸層色、尺寸）本身維持原樣不動，只換驅動時鐘的機制。transition-duration
   不寫死在這裡——吸/吐時長不同且兩端各留停留感，實際時長由 JS 依相位動態設定
   （style.transitionDuration）。36s 引導偏長，略過鈕不能被畫面淡入蓋住（見下面
   [data-step="breathe"].is-active 的 animation:none 覆寫）。 */
[data-step="breathe"] { text-align: center; padding: 2rem 0; }
/* 覆寫 .oracle-step.is-active 的整段淡入（1.2s）——只在呼吸步驟關掉，其餘步驟不受影響，
   確保「略過」與呼吸引導本身從一開始就明顯可見，不會被畫面淡入遮蔽（user 明訂）。 */
[data-step="breathe"].is-active { animation: none; }
.breath-glow {
  width: clamp(160px, 40vw, 240px);
  height: clamp(160px, 40vw, 240px);
  margin: 0 auto 1.5rem;
  border-radius: 50%;
  background: radial-gradient(circle, var(--lf-champagne-gold) 0%, var(--lf-blush-rose) 55%, transparent 75%);
  opacity: 0.65;
  transform: scale(0.85);
  transition-property: transform, opacity;
  transition-timing-function: var(--lf-ease-breath);
}
.breath-glow.is-inhale { transform: scale(1.08); opacity: 0.95; }
.breath-glow.is-exhale { transform: scale(0.85); opacity: 0.65; }
/* reduced-motion：.breath-glow 本身即帶 .lf-motion，全域規則（tools-shared.css）
   「只留 opacity，不做 transform」自動生效——光暈不縮放但仍隨相位淡入淡出，
   文字與輪次指示照常按各自時長推進、3 輪後照常前進（與 chakra.html 一致的取捨）。 */
.breath-copy { color: var(--lf-deep-lavender); }
/* 吸/吐文字交叉淡出淡入（user 反饋「太快」有一部分是硬切換字造成的急促感）：
   opacity + 極輕微上移，中文字不做 blur（工單明訂）。transition-duration 0.65s
   （≥0.6s，對應驗收要求）須與 js/oracle.js 的 BREATH_TEXT_FADE_MS 同步。 */
.breath-phase-text {
  font-size: 1.05rem;
  color: var(--lf-deep-lavender);
  opacity: 0.85;
  margin: 0 0 0.4rem;
  transform: translateY(0);
  transition: opacity 0.65s ease, transform 0.65s ease;
}
.breath-phase-text.is-fading { opacity: 0; transform: translateY(6px); }
/* 輪次指示：常駐更新、不加淡出淡入（只在跨輪那兩次 tick 才變，動畫邊際效益低）。 */
.breath-countdown { font-size: 1.05rem; color: var(--lf-earth-clay); margin: 0 0 1.5rem; letter-spacing: 0.03em; }

/* ---------- 洗牌選卡：3a 洗牌動畫 ---------- */
[data-step="shuffle"] { text-align: center; }
.shuffle-copy { color: var(--lf-deep-lavender); margin-bottom: 1.25rem; }

.shuffle-deck {
  position: relative;
  width: clamp(120px, 32vw, 150px);
  aspect-ratio: var(--lf-aspect-card);
  margin: 1.5rem auto;
}
.shuffle-card {
  position: absolute;
  inset: 0;
  background: url(../images/tools/oracle/card-back.webp) center / cover;
  border-radius: 8px;
  box-shadow: 0 4px 10px rgba(0, 0, 0, 0.2);
  animation: oracle-shuffle-riffle 2.4s var(--lf-ease-breath);
  animation-fill-mode: forwards; /* 停在 100% 的極小疊起錯位（--dxf/--dyf/--drotf），牌堆才有厚度感 */
}
/* 真 riffle 兩輪：0%靜止 → 13%分兩半甩開(--dx1/--dy1/--drot1) → 24%交錯歸位途中向上微彈(--bounce1)
   ＋反向回彈 → 32%-48%第一輪落定「停頓平台」（見下）→ 60%第二輪甩開(--dx2/--drot2，幅度較小)
   → 70%第二輪歸位微彈 → 78%-92%第二輪落定「停頓平台」→ 100%最終疊起（極小錯位，forwards 保留，見上）。
   兩段停頓刻意做成「平台」（32-48%＝16%＝384ms、78-92%＝14%＝336ms 的區間值都相同=靜止），
   而不是單一個時間點：12 張牌的 animation-delay 展開總長只有 (12-1)*22ms=242ms（見 js
   SHUFFLE_MAX_DELAY_MS），比兩個平台的寬度都窄，所以無論每張牌自己的 delay 多少，兩個平台之間
   一定有一段【全部 12 張同時靜止】的真實停頓窗（2026-07-27 修正：舊版停頓只在單一時間點 40%/85%，
   被 12 張牌的交錯 delay 一沖散，畫面上完全看不出停頓，只有持續的騷動——實測 playwright 截圖＋
   像素差證實，見工單回報）。
   每張牌的 --dx1/--dy1/--drot1/--dx2/--dy2/--drot2/--bounce1/--bounce2/--dxf/--dyf/--drotf 與
   animation-delay 由 js buildShuffleDeckVisual() 依起手順序(rank)公式化產生（見該函式註解），
   不用 nth-child。 */
@keyframes oracle-shuffle-riffle {
  0%   { transform: translate(0, 0) rotate(0deg); }
  13%  { transform: translate(var(--dx1, 0), var(--dy1, 0)) rotate(var(--drot1, 0deg)); }
  24%  { transform: translate(calc(var(--dx1, 0) * -0.15), var(--bounce1, 0)) rotate(calc(var(--drot1, 0deg) * -0.35)); }
  32%  { transform: translate(0, 0) rotate(0deg); }
  48%  { transform: translate(0, 0) rotate(0deg); }
  60%  { transform: translate(var(--dx2, 0), var(--dy2, 0)) rotate(var(--drot2, 0deg)); }
  70%  { transform: translate(calc(var(--dx2, 0) * -0.15), var(--bounce2, 0)) rotate(calc(var(--drot2, 0deg) * -0.35)); }
  78%  { transform: translate(0, 0) rotate(0deg); }
  92%  { transform: translate(0, 0) rotate(0deg); }
  100% { transform: translate(var(--dxf, 0), var(--dyf, 0)) rotate(var(--drotf, 0deg)); }
}

.shuffle-actions { display: flex; gap: 1rem; justify-content: center; align-items: center; flex-wrap: wrap; }

/* ---------- 洗牌選卡：3b 扇形選牌 ---------- */
.selected-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  justify-content: center;
  min-height: 76px;
  align-items: flex-start;
  margin-bottom: 0.5rem;
}
.card-fan {
  position: relative;
  height: clamp(230px, 38vw, 270px);
  max-width: 680px;
  margin: 1rem auto 0.5rem;
}
/* 攤牌過場進行中：擋掉點擊，避免過場過程中點到還沒抵達最終角度的牌（js buildFan 加/移除這個 class）。
   只擋 .card-fan 內的 fan-slot，不影響 selected-rail 裡已選中、搬出去的牌（那些不受這個 class 管）。 */
.card-fan.is-dealing .fan-slot { pointer-events: none; }
.fan-slot {
  position: absolute;
  left: 50%;
  bottom: 150px;
  width: 56px;
  aspect-ratio: var(--lf-aspect-card);
  border: none;
  padding: 0;
  cursor: pointer;
  background: none;
  /* 經典手持牌扇（2026-07-24 改，取代舊版 170px/170deg 近閉合彈簧管；2026-07-27 再收窄弧展角＋
     加深樞紐距離）：樞紐點放在卡片自身下緣再往下 800px（半徑夠大，旋轉弧線才夠「平緩」，
     42 張攤開才讀作一把牌而非螺旋管）；js buildFan 的 68deg 總張角配這個半徑，每張間距約 1.66deg，
     目標露出邊落在 20-26px 區間（比 2026-07-24 版本窄角＋深樞紐——實測數字見 scratchpad
     anim2-*.png 與工單回報；即便邊寬達標，42 張仍是同一張圖同一個裁切，讀起來仍偏「一條紋理帶」，
     這是素材本身的限制，另外用下面的接縫陰影緩解，不是純角度/半徑能解的）。
     bottom:150px 把起始基準往上抬，讓最外側兩張旋轉後的下緣落在容器底部附近，
     不會探出容器外遮到下面的 select-hint 文字。 */
  transform-origin: 50% calc(100% + 800px);
  /* 手感展開（2026-07-27 新增，取代原本固定的 :hover translateY+scale）：--spread-dx/
     --spread-lift 由 js/oracle.js applyFanSpreadAt() 依指標／focus 鄰近距離即時算出並合成
     進這一個 transform（rotate 之後再 translate，因此 translateX 是「已旋轉座標系」下的位移，
     約略沿弧切線方向外推）。刻意不在這裡放 scale——這個 transform-origin 特意設在卡片下緣
     再往下 800px 處是為了讓旋轉弧夠平緩，若在同一個 transform 鏈裡疊加 scale()，scale 會連帶
     放大「卡片中心到 800px 外原點」的距離，scale(1.15) 一次就會把卡片整個推高快 130px，遠超
     spec 要的上浮 6-14px（2026-07-27 實測踩過，見 buildFan() 呼叫端註解）。近距離放大的視覺
     效果改放進子元素 .fan-slot-face（transform-origin 預設 50% 50%，不受這顆 800px 遠的原點
     牽連），見下方規則。 */
  transform: translateX(-50%) rotate(var(--angle, 0deg))
    translateX(var(--spread-dx, 0px)) translateY(var(--spread-lift, 0px));
  opacity: 1;
  transition: transform 0.2s ease;
}
.fan-slot:focus-visible { outline: 2px solid var(--lf-champagne-gold); outline-offset: 2px; z-index: 999; }

/* 牌背視覺／露出寬度放大層：與 .fan-slot 分離的子元素，自己的 transform-origin 是預設的
   50% 50%（卡片自身中心），scale 只會讓卡片視覺變寬變高，不會像放在 .fan-slot 上那樣
   被 800px 外的樞紐原點放大位移。--spread-scale 由 js applyFanSpreadAt() 設在 .fan-slot
   上（inline style custom property 會往子元素繼續 inherit），這裡直接讀。 */
.fan-slot-face {
  position: absolute;
  inset: 0;
  border-radius: 6px;
  background: url(../images/tools/oracle/card-back.webp) center / cover;
  /* 兩層 box-shadow：後者是整體浮起的環境陰影（沿用既有）；前者是「每張牌自己投在左側」的窄接縫陰影
     ——42 張牌背用的是同一張圖同一個裁切，內容完全相同，光靠角度/間距調整無法讓相鄰兩張讀出差異
     （2026-07-27 實測：純調寬露出邊，畫面仍是一條連續花紋帶，見 scratchpad anim2-fan-t1300.png）。
     這條接縫陰影負責在每張牌的可見左緣咬出一條深色線，把「一條帶子」拆讀成「多張牌互相疊蓋」。 */
  box-shadow: -3px 0 5px -1px rgba(30, 20, 48, 0.45), 0 2px 6px rgba(0, 0, 0, 0.25);
  transform: scale(var(--spread-scale, 1));
  transition: transform 0.2s ease, box-shadow 0.2s ease;
  /* 特意不設 pointer-events:none：這層是 button 的子元素，點在它身上事件一樣會冒泡到外層
     .fan-slot 的 click listener（功能不受影響），但若設成 none，document.elementFromPoint()
     在 scale 放大後超出按鈕原生 56px 盒的溢出範圍會直接「穿透」看到底下的鄰居，導致驗收用的
     露出寬度量測（elementFromPoint 逐 px 掃描）量不到真正放大的視覺範圍——2026-07-27 實測
     真的踩到：拿掉 pointer-events:none 前量到 57px，拿掉後量到 62px，才是這張牌實際的視覺露出。 */
}

/* 選中：移進 #selected-rail 後套用這組（描述子選擇器，不靠額外 class）。
   position:relative 而非 static——一樣正常參與 flex 排版，同時給 ::after 徽章當定位錨點。 */
.selected-rail .fan-slot {
  position: relative;
  /* position:relative 的 top/left/bottom/right 是「相對自己原本流位置」的位移，
     不會被 position 覆寫自動清空——base .fan-slot 的 left:50%/bottom(150px) 沒歸零的話，
     選中的卡會被推離 flex 排版本該在的位置（2026-07-24 改 pivot 深度時炸出來，
     bottom 從 0 加大到 150px 後尤其明顯，卡直接飛到 rail 外）。 */
  left: auto;
  bottom: auto;
  transform: none;
  width: 48px;
  margin: 0.15rem;
  box-shadow: 0 0 0 2px var(--lf-champagne-gold), 0 4px 10px rgba(0, 0, 0, 0.2);
}
.selected-rail .fan-slot:hover { transform: none; }
.selected-rail .fan-slot[data-order]::after {
  content: attr(data-order);
  position: absolute;
  top: -8px;
  right: -6px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--lf-champagne-gold);
  color: #3a2c00;
  font-size: 0.7rem;
  line-height: 18px;
  text-align: center;
}
/* 「可取消」可發現性補強（工單 item 2，2026-07-28）：選牌本身早已支援再點一次取消
   （見 js onFanCardToggle），這裡補一個小徽章當視覺提示——桌機只在 hover/focus-visible
   時顯示，手機（無 hover）在下方 680px 斷點改常駐顯示。鏡射既有順位徽章（右上角、
   18x18 圓形）放在左下角，同一套視覺語言。z-index:1 是刻意值：.fan-slot-face 是後面的
   兄弟元素但 z-index:auto（stacking 分類第 6 級，依 DOM 順序疊放），positive z-index
   一律排在 auto 之上（分類第 7 級），不需要調整 DOM 順序就能蓋在牌背圖上面。
   pointer-events:none 避免這個生成內容干擾按鈕本身的點擊命中。 */
.selected-rail .fan-slot[data-order]::before {
  content: '✕';
  position: absolute;
  left: -6px;
  bottom: -6px;
  width: 18px;
  height: 18px;
  z-index: 1;
  border-radius: 50%;
  background: rgba(30, 20, 48, 0.78);
  color: #fff;
  font-size: 0.65rem;
  line-height: 18px;
  text-align: center;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
}
.selected-rail .fan-slot:hover::before,
.selected-rail .fan-slot:focus-visible::before {
  opacity: 1;
}

@media (max-width: 680px) {
  .card-fan {
    position: static;
    display: flex;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    gap: 0.5rem;
    height: auto;
    padding: 0.5rem 1rem;
    justify-content: flex-start;
  }
  .fan-slot {
    /* relative，不是 static：.fan-slot-face 是 position:absolute;inset:0 的子元素
       （2026-07-27 手感展開重構時把牌背視覺抽成獨立子層，見上方 .fan-slot-face 註解），
       若父層是 static 就不是定位容器，inset:0 會往上找到下一個有定位的祖先，讓牌背圖整個
       撐爆版面（2026-07-27 playwright 375px screenshot 實測踩到：整頁變成一張放大的牌背
       花紋）。relative 不影響 flex item 的排版行為，只補上定位容器，同時修正這個 bug。 */
    position: relative;
    /* base .fan-slot 的 left:50%/bottom:150px 是給桌機弧形定位用的 offset——position:relative
       （不像 static）仍然會吃這兩個 offset，若不歸零，卡片會被硬推到 flex 排版位置以外
       （2026-07-27 實測：加了 position:relative 後卡片跑到 fan 容器外、整排看起來是空的，
       同一顆 bug 的下半段，跟 .selected-rail .fan-slot 已經處理過的歸零手法一致）。 */
    left: auto;
    bottom: auto;
    flex: 0 0 auto;
    width: 56px;
    transform: none !important;
    scroll-snap-align: center;
  }
  /* 手機無 hover——「可取消」提示改常駐顯示（工單 item 2 明訂），維持小而不吵：
     沿用桌機同一顆 18x18 小徽章，不放大、不加閃爍。 */
  .selected-rail .fan-slot[data-order]::before { opacity: 1; }
}

/* ---------- 翻牌 / 狀態確認 / 解讀 ---------- */
.reading-rail {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
  justify-content: center;
  margin-bottom: 1.5rem;
}
.rail-chip {
  font-size: 0.8rem;
  padding: 0.25rem 0.75rem;
  border-radius: 999px;
  background: rgba(169, 184, 162, 0.2);
  color: var(--lf-deep-lavender);
}
.rail-chip.is-current { background: var(--lf-champagne-gold); color: #3a2c00; }
.rail-chip.is-done { background: rgba(169, 184, 162, 0.45); }

.reading-stage { text-align: center; }
.reading-position {
  font-size: 0.9rem;
  color: var(--lf-earth-clay);
  margin-bottom: 0.75rem;
}
.card-face {
  position: relative;
  width: min(220px, 60vw);
  aspect-ratio: var(--lf-aspect-card);
  margin: 0 auto 1.25rem;
  border-radius: 14px;
  box-shadow: 0 10px 24px rgba(74, 58, 99, 0.18);
  perspective: 1000px;
}
.card-face-img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- 翻牌儀式：3D 翻面 + 一次性光塵綻放 ---------- */
.card-flip-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.8s var(--lf-ease-breath);
}
.card-flip.is-flipped .card-flip-inner { transform: rotateY(180deg); }
.card-flip-face {
  position: absolute;
  inset: 0;
  backface-visibility: hidden;
  border-radius: 14px;
  overflow: hidden;
}
.card-flip-face--back { background: url(../images/tools/oracle/card-back.webp) center / cover; }
.card-flip-face--front { transform: rotateY(180deg); }

.dust-burst { position: absolute; left: 50%; top: 50%; z-index: 3; pointer-events: none; }
.dust-mote {
  position: absolute;
  left: 0;
  top: 0;
  width: 5px;
  height: 5px;
  margin: -2.5px;
  border-radius: 50%;
  background: var(--lf-champagne-gold);
  box-shadow: 0 0 6px 1px rgba(216, 179, 106, 0.7);
  opacity: 0;
  animation: oracle-dust-scatter 1.1s var(--lf-ease-breath) forwards;
  animation-delay: calc(var(--i, 0) * 0.04s);
}
@keyframes oracle-dust-scatter {
  0%   { transform: translate(0, 0) scale(0.4); opacity: 0; }
  25%  { opacity: 1; }
  100% { transform: translate(var(--dx, 0), var(--dy, 0)) scale(1); opacity: 0; }
}

/* reduced-motion：無 3D 旋轉，改前後面 opacity 淡入淡出；光塵整段不建（見 js buildDustBurstHtml 呼叫端） */
@media (prefers-reduced-motion: reduce) {
  .card-flip-inner, .card-flip.is-flipped .card-flip-inner { transition: none; transform: none; }
  .card-flip-face { transition: opacity 0.4s ease; }
  .card-flip-face--front { transform: none; backface-visibility: visible; opacity: 0; }
  .card-flip-face--back { opacity: 1; }
  .card-flip.is-flipped .card-flip-face--front { opacity: 1; }
  .card-flip.is-flipped .card-flip-face--back { opacity: 0; }

  /* 扇形選牌：手感展開(js applyFanSpreadAt)本身已對 reduced-motion 全段短路(isReducedMotionNow
     guard，--spread-* 三個變數永遠不會被設定，維持 CSS 預設 0/0/1)，base transform 因此自動
     等同舊版靜態扇形，這裡不需要、也不能用 !important 再蓋一次——那會贏過
     .selected-rail .fan-slot{transform:none} 與 375px 斷點的 transform:none!important
     （同specificity、後定義者贏，兩處都會被打穿，2026-07-27 advisor 抓到），只留 transition
     收斂成 box-shadow 即可。 */
  .fan-slot, .fan-slot-face { transition: box-shadow 0.25s ease; }
}

.card-placeholder {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 1rem;
  color: #fff;
  text-align: center;
}
.card-placeholder[hidden] { display: none; }
.card-placeholder-name { font-family: 'LXGW WenKai TC', serif; font-size: 1.1rem; }
.card-placeholder-en { font-size: 0.75rem; opacity: 0.85; }

/* gate palette 漸層佔位（ponytail：湊 7 門 × 9 色 tokens 的簡化對應，
   非官方 hex，等真實插畫進場即可整組刪除） */
.gate-g1 { background: linear-gradient(160deg, var(--lf-earth-clay), var(--lf-sage)); }
.gate-g2 { background: linear-gradient(160deg, var(--lf-blush-rose), var(--lf-amber)); }
.gate-g3 { background: linear-gradient(160deg, var(--lf-amber), var(--lf-champagne-gold)); }
.gate-g4 { background: linear-gradient(160deg, var(--lf-sage), var(--lf-blush-rose)); }
.gate-g5 { background: linear-gradient(160deg, var(--lf-mist-blue), var(--lf-pale-lavender)); }
.gate-g6 { background: linear-gradient(160deg, var(--lf-pale-lavender), var(--lf-deep-lavender)); }
.gate-g7 { background: linear-gradient(160deg, var(--lf-deep-lavender), var(--lf-champagne-gold)); }

/* 牌名區（2026-07-27 排版收斂，工單 item 5）：中文名（最大）→ 英文名（小、寬字距、
   另起一行，不再擠在中文名右下）→ 編號（更小、最淡），三層依序降階，呼吸感靠 margin
   而非字級硬撐出來。 */
.card-name-group { margin-top: 0.3rem; }
.card-name {
  font-family: 'Cormorant Garamond', 'LXGW WenKai TC', serif;
  font-size: clamp(1.7rem, 5.5vw, 2.3rem);
  color: var(--lf-deep-lavender);
  margin: 0 0 0.35rem;
}
.card-name-en {
  font-size: 0.82rem;
  font-weight: 400;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--lf-deep-lavender);
  opacity: 0.65;
  margin: 0 0 0.5rem;
}
/* 編號（如 No.01）：資料 id 逐字使用（含前導零），不自創格式；刻意壓到全頁最小字級，
   讀作中性 metadata，不與牌名/英文名爭視覺焦點。 */
.card-no {
  font-size: 0.7rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--lf-earth-clay);
  opacity: 0.75;
  margin: 0;
}

/* 解讀單頁化（2026-07-27，取代原本 image/state/reading 三個 subview，見 js
   renderCombinedReading）：牌面大圖＋牌名＋位置維持置中的「hero」區塊，其餘八層沿用
   .oracle-result-layer/.oracle-eyebrow（finale 收尾同一套分層留白節奏，見下方註解），
   一路往下捲，不再需要點擊才能看到下一段。 */
.reading-hero { text-align: center; margin-bottom: 2.5rem; }
/* .reading-stage 本身 text-align:center（見上）是給 hero 卡面用的；下面八層內容一律
   左對齊才好讀（同舊版 .reading-block 的做法），單一 parent 規則涵蓋，不必逐段補 class。 */
.reading-stage .oracle-result-layer { text-align: left; }

/* 層間分隔線（工單 item 5：極淡、非實線）＋中文行距 1.75-1.9 統一套用——排除「速讀」
   （data-layer="quick"）：它是 hero 卡面之後第一個內容層，不需要一條線把它跟牌面隔開。
   finale 收尾層也共用 .oracle-result-layer，合流成一條捲軸後在這裡一併套用，
   讀者從解讀捲到收尾時分隔節奏不中斷。 */
.oracle-result-layer:not([data-layer="quick"]) {
  padding-top: 1.75rem;
  border-top: 1px dotted rgba(74, 58, 99, 0.16);
}
.reading-stage .oracle-result-layer p,
.reading-stage .oracle-result-layer li,
#step-finale .oracle-result-layer p {
  line-height: 1.85;
}

/* 字級層級（工單 item 5：牌名 > 核心訊息 > 速讀/轉化方向/回到身體（正文級）> 反思/練習
   （正文級＋清單縮排）> eyebrow（最小，見上方 .oracle-eyebrow 既有 0.8rem/寬字距不動））。
   速讀與核心訊息過去量級太接近——速讀維持正文級，核心訊息拉高到 1.2rem/略粗，才有
   「這是這張牌的重點句」的份量。 */
.reading-quick { font-size: 1rem; font-style: italic; color: var(--lf-deep-lavender); text-align: left; }
.reading-core { font-size: 1.2rem; font-weight: 500; color: var(--lf-deep-lavender); text-align: left; }
.reading-reflection-list { text-align: left; padding-left: 1.25rem; margin: 0; }
.reading-reflection-list li { margin-bottom: 0.4rem; }

/* 「同行的動物」層（工單 item 1，2026-07-28）：eyebrow 沿用左對齊的層級規則（見上方
   .reading-stage .oracle-result-layer 統一 text-align:left），圖與小字則自行置中——
   跟 hero 牌面圖一樣讀作「一張圖卡」，而非跟文字對齊的插圖。圓角/投影沿用 .card-face
   的既有數值（14px / 0 10px 24px rgba(74,58,99,.18)），與頁面其他圖的處理一致。 */
.animal-companion-media { text-align: center; margin: 0.6rem 0 0.75rem; }
.animal-companion-img {
  width: min(220px, 100%);
  aspect-ratio: var(--lf-aspect-card);
  object-fit: cover;
  display: block;
  margin: 0 auto;
  border-radius: 14px;
  box-shadow: 0 10px 24px rgba(74, 58, 99, 0.18);
}
.animal-companion-name { text-align: center; font-size: 0.85rem; color: var(--lf-earth-clay); margin: 0; }
@media (max-width: 680px) {
  .animal-companion-img { width: min(56vw, 220px); }
}
/* 練習清單縮排（工單「反思/練習正文級但帶清單縮排」）：縮排包一層 wrapper，不直接對
   [data-layer="practices"] 裡的所有 <p> 下手——舊版 [data-layer="practices"] p 的
   margin 規則 specificity 蓋過 .oracle-eyebrow，導致「練習」這個 eyebrow 跟內容的間距
   跟其他層不一致（2026-07-27 advisor 抓到），改成只作用在這個 wrapper 內即可修正。 */
.reading-practices-list { padding-left: 1.25rem; }
.reading-practices-list p { text-align: left; margin: 0 0 0.6rem; padding: 0.3rem 0.6rem; border-radius: 8px; border-left: 3px solid transparent; }
.reading-practices-list p:last-child { margin-bottom: 0; }
/* 2026-07-28 兩階段改版：這裡不再做「高亮強調」（原本 is-emphasized，見 js
   updateFlowMarksUI 舊版），因為對應的那個時長現在整條搬進 .flow-promoted-practice
   完整呈現，下面只剩「其他練習」——不是強調，是不重複，見 js syncPracticesLayer。
   顯式補 [hidden] → display:none（防禦性寫法，同 .practice-note，避免未來若有規則
   對 <p> 設 display 蓋掉原生 hidden 語意）。 */
.reading-practices-list p[hidden] { display: none; }
.practice-note { display: block; font-size: 0.8rem; color: var(--lf-earth-clay); margin-top: 0.25rem; }
.reading-blessing { font-style: italic; text-align: left; margin: 0 0 0.5rem; }
.reading-boundary { font-size: 0.8rem; color: var(--lf-earth-clay); text-align: left; }

/* 三種流動——兩階段閱讀（2026-07-28 改版，取代原本三段永遠並列）：
   階段 1（未標記）＝三段平等並列，每段可點「這個最像現在的我」標記；
   階段 2（已標記）＝被選的那段升格為「你此刻的解讀」（.flow-promoted，見下方），
   另外兩段收進 <details class="flow-others"> 摺疊。兩階段共用 .flow-cards/.flow-card
   （階段 1 的三段、階段 2 摺疊內的兩段——這兩處出現的卡片一律是「未標記」樣式，
   標記中的那段改走 .flow-promoted，不會同時以 .flow-card 形式出現，故 .flow-card
   不再需要 is-marked 修飾類別）。 */
/* 直式堆疊、不做三欄——.oracle-shell 容器上限只有 640px，扣 padding 後三欄每欄僅約
   180px，塞不下一句完整的牌義全文（2026-07-27 advisor 抓到：3 欄在此寬度下不可讀）。 */
.flow-cards {
  display: flex;
  flex-direction: column;
  gap: 0.85rem;
}
.flow-card {
  text-align: left;
  border: 1px solid rgba(74, 58, 99, 0.18);
  border-left: 3px solid transparent;
  background: rgba(255, 255, 255, 0.55);
  border-radius: 14px;
  padding: 0.9rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.flow-card-head { display: flex; flex-direction: column; gap: 0.15rem; }
.flow-card-label { font-weight: 700; color: var(--lf-deep-lavender); font-size: 0.98rem; }
.flow-card-annot { font-size: 0.82rem; color: var(--lf-earth-clay); }
.flow-card-body { font-size: 0.92rem; margin: 0; flex-grow: 1; }
.flow-mark-btn {
  align-self: flex-start;
  font-size: 0.78rem;
  color: var(--lf-earth-clay);
  background: transparent;
  border: 1px solid rgba(74, 58, 99, 0.25);
  border-radius: 999px;
  padding: 0.3rem 0.75rem;
  cursor: pointer;
  transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease;
}
.flow-mark-btn:hover, .flow-mark-btn:focus-visible { border-color: var(--lf-champagne-gold); color: var(--lf-deep-lavender); }
/* 用 aria-pressed 而非父層 class 判斷「填色鈕」樣式——marked 按鈕只會出現在
   .flow-promoted 裡，aria-pressed 屬性本身已精確對應 JS 的標記狀態。 */
.flow-mark-btn[aria-pressed="true"] {
  border-color: var(--lf-champagne-gold);
  background: var(--lf-champagne-gold);
  color: #3a2c00;
}
/* 標記／取消後的確認行（工單 item 4 沿用；2026-07-28 改為常駐在 flows 區塊外的
   單一節點，不隨 flows-body 整段重繪——見 js showFlowMarkConfirm 註解），
   2.6 秒後淡出清空。 */
.flow-mark-confirm {
  font-size: 0.8rem;
  color: var(--lf-earth-clay);
  margin: 0.6rem 0 0;
  transition: opacity 0.3s ease;
}
.flow-mark-confirm.is-fading { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .flow-mark-btn, .flow-mark-confirm { transition: none; }
}

/* 階段 2 升格區塊「你此刻的解讀」（2026-07-28 新增，取代原本的下方一行高亮）：
   warm pastel 底＋左粗色條，讀作這一頁與「核心訊息」同等重量的第二個主體段落。
   overextension 的 protective_intent（見 renderPromotedFlow）與練習全文都在這個
   容器內接續呈現，不再各自獨立於流動區塊之外。 */
.flow-promoted {
  text-align: left;
  border: 1px solid rgba(216, 179, 106, 0.4);
  border-left: 4px solid var(--lf-champagne-gold);
  background: rgba(216, 179, 106, 0.12);
  border-radius: 16px;
  padding: 1.15rem 1.3rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
/* 主文字級放大：與 .reading-core（核心訊息）同一組數值——比其他正文（.flow-card-body
   0.92rem）大一階、行距拉寬到接近兩倍，讀起來是這一頁的主體之一，不是附屬說明。 */
.flow-promoted-body {
  font-size: 1.2rem;
  font-weight: 500;
  line-height: 1.95;
  color: var(--lf-deep-lavender);
  margin: 0;
}
.flow-promoted .flow-mark-btn { align-self: flex-start; }
.flow-promoted-practice {
  border-top: 1px dotted rgba(74, 58, 99, 0.16);
  padding-top: 0.75rem;
}
.flow-promoted-practice p { margin: 0 0 0.4rem; line-height: 1.85; }
.flow-promoted-practice p:last-child { margin-bottom: 0; }
.flow-promoted-practice .practice-note { color: var(--lf-earth-clay); font-size: 0.85rem; }
/* 淡入（工單 reduced-motion 條款：階段切換不做位移動畫，透明度變化可留）——
   bindFlowMarkButtons() 重繪後短暫加上 .flow-fade-start（opacity:0），下一幀移除、
   靠 transition 淡入；reduced-motion 直接跳終態，不留過場。 */
.flow-promoted.flow-fade-start,
.flow-others.flow-fade-start { opacity: 0; }
.flow-promoted, .flow-others { transition: opacity 0.3s ease; }
@media (prefers-reduced-motion: reduce) {
  .flow-promoted, .flow-others { transition: none; }
}

/* 承擔較多（overextension）標記後才展開的專屬內容（指揮官裁決升級，工單 item 4；
   2026-07-28 從流動區塊外的獨立常駐區塊，改為巢狀在 .flow-promoted 內部，只在
   overextension 被標記時才渲染，見 js renderPromotedFlow）：warm pastel 底＋左色條，
   讀作「慈悲框架」的一段展開內容。 */
.oracle-protective-block {
  background: rgba(216, 179, 106, 0.1);
  border-left: 3px solid var(--lf-champagne-gold);
  border-radius: 12px;
  padding: 1rem 1.15rem;
}
.oracle-protective-block .oracle-eyebrow { margin-bottom: 0.4rem; }
.oracle-protective-block p:last-child { margin: 0; }

/* 階段 2：另外兩段收進原生 <details>（工單 item 3——無障礙摺疊、aria 語意由瀏覽器
   原生提供，展開狀態走 details.open，不需另外手動維護 aria-expanded）。 */
.flow-others { margin-top: 0.25rem; }
.flow-others-summary {
  cursor: pointer;
  list-style: none;
  position: relative;
  padding: 0.4rem 0 0.4rem 1.15rem;
  font-size: 0.85rem;
  color: var(--lf-earth-clay);
}
.flow-others-summary::-webkit-details-marker { display: none; }
.flow-others-summary::marker { content: ""; }
.flow-others-summary::before {
  content: "▸";
  position: absolute;
  left: 0;
  top: 0.45rem;
  display: inline-block;
  transition: transform 0.2s ease;
}
.flow-others[open] > .flow-others-summary::before { transform: rotate(90deg); }
.flow-others[open] > .flow-others-summary { color: var(--lf-deep-lavender); }
.flow-others-summary:focus-visible { outline: 2px solid var(--lf-champagne-gold); outline-offset: 2px; }
.flow-others .flow-cards { margin-top: 0.6rem; }
@media (prefers-reduced-motion: reduce) {
  .flow-others-summary::before { transition: none; }
}

/* ---------- 分享 / 導流 ---------- */
.finale-cta { display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: center; margin: 1.5rem 0; }
.oracle-disclaimer { font-size: 0.78rem; color: var(--lf-earth-clay); max-width: 480px; margin: 0 auto; }
.oracle-footer { text-align: center; padding: 1.5rem 1.25rem; }

/* ---------- 收尾（finale）六層：分層留白，沿用 chakra 結果頁節奏（.chakra-result-layer）
   的視覺韻律但獨立命名，不 @import css/chakra.css（兩頁刻意互不依賴，見 memory
   tool-pages-pipeline.md）。 ---------- */
.oracle-eyebrow {
  text-transform: uppercase;
  letter-spacing: 0.15em;
  font-size: 0.8rem;
  color: var(--lf-earth-clay);
  margin-bottom: 0.75rem;
}
.oracle-result-layer { margin: 0 auto 3rem; max-width: 560px; }
.oracle-result-layer:last-child { margin-bottom: 0; }

/* 1. 你的流光圖景回顧：user 反饋「牌太小」（2026-07-28 工單）——單張放大到與解讀頁
   hero 牌面（.card-face）同量級置中；多張（3/4/5 張）桌機同排且每張 ≥150px，7 張
   換行兩排；手機一律直排每張 min(60vw,240px)。3/4/5 張同排所需寬度（例：5 張×150px+
   4 個 1.25rem gap ≈ 914px）超出 .oracle-shell 的 640px 上限，故本層獨立撐出視窗寬
   （full-bleed：width:100vw + calc(50% - 50vw) 負邊界置中，html,body 的
   overflow-x:clip 已確保不會產生橫向捲軸，見本檔第 14-20 行說明），內層
   .oracle-recap-row 再收回置中最大寬。 */
.oracle-result-layer[data-layer="recap"] {
  max-width: none;
  width: 100vw;
  margin-left: calc(50% - 50vw);
  margin-right: calc(50% - 50vw);
  padding-left: 1.25rem;
  padding-right: 1.25rem;
  box-sizing: border-box;
}
.oracle-recap-row {
  display: flex;
  flex-wrap: wrap;
  gap: 1.25rem;
  justify-content: center;
  max-width: 1000px;
  margin: 0 auto;
}
.finale-recap-card { flex: 1 1 150px; max-width: 200px; text-align: center; }
.finale-recap-media {
  position: relative;
  width: 100%;
  aspect-ratio: var(--lf-aspect-card);
  border-radius: 14px;
  overflow: hidden;
  box-shadow: 0 10px 24px rgba(74, 58, 99, 0.18);
  margin-bottom: 0.6rem;
}
.finale-recap-img { width: 100%; height: 100%; object-fit: cover; display: block; }
.finale-recap-name { font-family: 'Cormorant Garamond', 'LXGW WenKai TC', serif; color: var(--lf-deep-lavender); font-size: 1.05rem; margin: 0; }
.finale-recap-position { font-size: 0.85rem; color: var(--lf-earth-clay); margin: 0.2rem 0 0; }

/* 七道門巡禮（7 張）：row 預設 max-width 1000px 剛好容得下 6 張同排（6*150+5*
   1.25rem gap=1000px），flex-wrap 會切成不均衡的「6+1」、落單那張還因 flex-grow
   被撐到 200px（2026-07-28 playwright 實測＋advisor 覆核抓到）。收窄到 700px（4*
   150+3*1.25rem=660px 剛好落在內）逼出均衡的 4+3 兩排；js renderFinaleRecap() 依
   app.entries.length===7 加這個 class，只影響 7 張，不動 3/4/5 張的單排邏輯
   （5 張 5*150+4*1.25rem=830px，仍在其餘牌陣沿用的預設 1000px 內，不受本規則影響）。 */
.oracle-recap-row.is-seven { max-width: 700px; }

/* 單張牌陣（js renderFinaleRecap() 依 app.entries.length===1 加 is-single class）：
   不走多張的 flex 平分邏輯，牌面直接放大到與 .card-face（解讀頁 hero 牌面）同量級。 */
.oracle-recap-row.is-single { max-width: min(340px, 92vw); }
.oracle-recap-row.is-single .finale-recap-card { flex: 0 1 auto; width: min(320px, 72vw); max-width: none; }
.oracle-recap-row.is-single .finale-recap-name { font-size: 1.2rem; }
.oracle-recap-row.is-single .finale-recap-position { font-size: 0.95rem; }

@media (max-width: 480px) {
  /* 手機直排：basis 用 min(60vw,240px) 撐到約 92vw 可用寬的 2 倍以上，flex-wrap
     自然擠成單欄（同一列容不下兩張），不需額外 flex-direction:column。 */
  .finale-recap-card { flex-basis: min(60vw, 240px); max-width: min(60vw, 240px); }
  .oracle-result-layer[data-layer="recap"] { padding-left: 1rem; padding-right: 1rem; }
}

/* 2. 祝福：每張牌一行，牌名＋blessing */
.finale-blessing-item { font-style: italic; color: var(--lf-deep-lavender); margin: 0 0 0.75rem; }
.finale-blessing-item:last-child { margin-bottom: 0; }
.finale-blessing-item strong { font-style: normal; }

/* 3. 今日練習 */
.oracle-practice-text { color: var(--lf-deep-lavender); }

/* 5. 儲存流光圖卡 + 分享：兩顆按鈕並排（主：儲存圖卡／次：分享，ghost），
   下方接圖卡預覽（先看預覽再下載，不是點下去直接跳系統下載視窗，同 chakra 手法）。 */

/* 圖卡格式切換（工單新增）：兩顆小 pill，預設「直式」active。放在按鈕列上方，
   切格式若預覽已在畫面上會立即重繪（見 js 格式 pill 事件監聽器）。 */
.oracle-share-format { display: flex; gap: 0.5rem; justify-content: center; margin-bottom: 1rem; }
.oracle-format-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px; /* 觸控下限，同站上 .custom-btn/.btn-outline-lf 慣例 */
  border: 1px solid var(--lf-pale-lavender);
  background: transparent;
  color: var(--lf-deep-lavender);
  border-radius: 999px;
  padding: 0.4rem 1.1rem;
  font-size: 0.85rem;
  cursor: pointer;
  transition: background-color 0.25s ease, color 0.25s ease, border-color 0.25s ease;
}
.oracle-format-pill.is-active {
  background: var(--lf-deep-lavender);
  border-color: var(--lf-deep-lavender);
  color: #fff;
}
@media (prefers-reduced-motion: reduce) {
  .oracle-format-pill { transition-duration: 0.01ms; }
}

.oracle-share-actions { display: flex; flex-wrap: wrap; gap: 0.75rem; justify-content: center; margin-bottom: 1.25rem; }
/* 兩顆按鈕的自然寬度（站上 .custom-btn 的水平 padding 很寬）在窄螢幕會超過容器，
   flex 預設 shrink 會把它們壓扁——實測 375px 下「儲存流光圖卡」只剩 67px 寬、文字折成三行
   （2026-07-28 user 截圖發現）。禁止壓縮＋文字不折行，窄螢幕改成上下堆疊、各自滿寬。 */
.oracle-share-actions > .btn { flex: 0 0 auto; white-space: nowrap; }
@media (max-width: 560px) {
  .oracle-share-actions { flex-direction: column; align-items: stretch; }
  /* 滿寬堆疊時站上 .custom-btn 的垂直 padding 撐不出 44px（實測 37px），顯式補觸控下限 */
  .oracle-share-actions > .btn { width: 100%; min-height: 44px; display: inline-flex; align-items: center; justify-content: center; }
}
.oracle-share-preview { display: flex; flex-direction: column; align-items: center; gap: 1rem; margin: 1.25rem 0; }
.oracle-share-preview-img { width: min(280px, 70vw); border-radius: 16px; box-shadow: 0 12px 28px rgba(74, 58, 99, 0.22); }
/* 分享狀態 inline 提示：顯示 ~2 秒後淡出（js showShareStatus() 控制 hidden + is-fading） */
.oracle-share-status { transition: opacity 0.3s ease; }
.oracle-share-status.is-fading { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .oracle-share-status { transition-duration: 0.01ms; }
}

/* 6. 導流 */
.oracle-cta-lede { color: var(--lf-deep-lavender); }

/* ---------- 抽牌開始後收起巡禮（2026-07-27 新增，工單 item 1） ----------
   離開 intro 步驟（js setStep()，name !== 'intro'）即在 <body> 加這個 class，把七道門
   巡禮＋抵達段整塊 display:none——不是只降 opacity，讓文件高度真的縮回去，使用者進入
   洗牌/選牌/解讀流程後往上捲不會撞見已經走完的敘事，也不會留著一顆多餘的「開始抽牌」CTA。
   scroll/resize 監聽的計算量停止：見 js initGatesThru() 的 requestUpdate() guard
   （IntersectionObserver 本身也會在 display:none 後自然把這些門移出 activeSet）。 */
.lf-journey-collapsed #gates-intro,
.lf-journey-collapsed #gates-journey,
.lf-journey-collapsed .gates-outro {
  display: none;
}

/* ---------- 巡禮引言（頁面最先看見的內容，先講故事再給工具） ---------- */
/* top padding 沿用 .oracle-shell 同一套「蓋過站台 navbar」的值（見該規則註解：
   navbar 是 position:absolute、非文件流，<992px 高 101px/≥992px 高 121px） */
#gates-intro { position: relative; padding: 10rem 1.25rem 3rem; }
.gates-journey-title { text-align: center; }

/* 敘事開場段：兩行式引言，字級大於一般 subhead 才有「開場白」的份量 */
.gates-journey-intro {
  /* 撐成完整首屏：原本只有內容高（~344px），七道門的滿版圖幾乎立刻開始，交界在畫面中段
     形成一條明顯橫線；改成幾乎滿屏後，第一眼是完整的邀請畫面，門從第二屏才進場。
     position:relative 是霧層（.oracle-entrance-fx, absolute）的定位錨。 */
  position: relative;
  min-height: 88vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  max-width: none;
  padding: 0 1.25rem;
}
.gates-journey-intro > *:not(.oracle-entrance-fx) {
  max-width: 640px;
}
.gates-journey-lead {
  font-family: 'Cormorant Garamond', 'LXGW WenKai TC', serif;
  font-size: clamp(1.6rem, 4vw, 2.3rem);
  margin: 0 0 0.6rem;
  /* mix-blend-mode 文字融色（工單新增，改良自 rocketweblabs 的白字+exclusion——我們淺底
     深字，exclusion 會反相破壞可讀性，改用 multiply：深紫字疊上底下水彩紋理只會更暗、
     不會被拉淺，對比只升不降。已實測合成後像素對比度 ≥4.5:1（見驗收報告），此處為
     疊在 .oracle-bg 漸層+紙紋理之上，非滿版場景圖。 */
  mix-blend-mode: multiply;
}
.gates-journey-intro-sub {
  color: var(--lf-earth-clay);
  font-style: italic;
  line-height: 1.8;
  margin: 0;
}
/* 開場引言兩行依序 blur-in：一次性（IO 觸發補 class，不隨 scroll scrub），
   only opacity+filter——刻意不碰 transform，避免跟其他規則的 transform 打架。 */
.gates-journey-lead,
.gates-journey-intro-sub {
  opacity: 0;
  filter: blur(8px);
  transition: opacity 0.9s ease, filter 0.9s ease;
}
.gates-journey-intro-sub { transition-delay: 0.25s; }
#gates-intro.is-revealed .gates-journey-lead,
#gates-intro.is-revealed .gates-journey-intro-sub {
  opacity: 1;
  filter: none;
}
/* 安靜的跳過連結：不搶巡禮引言的視覺焦點，僅在需要時可見可點 */
.gates-journey-skip {
  display: inline-block;
  margin-top: 1.5rem;
  font-size: 0.85rem;
  color: var(--lf-earth-clay);
  opacity: 0.75;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.gates-journey-skip:hover, .gates-journey-skip:focus-visible { opacity: 1; color: var(--lf-deep-lavender); }

/* ---------- 七道門滿版穿越（JS scroll+rAF 算進度，只動 transform/opacity） ---------- */
/* scroll-snap-type 已改掛在 html（見上方 overflow-x:clip 區塊旁的說明），這裡不重複掛。 */
#gates-journey { position: relative; }

/* 200vh→240vh：跑道加長，配合文字子區間前移壓縮後，讓「全文穩定可讀高原」與淡出前的
   停留期都有更多實際捲動距離可用，不會覺得一晃就過去（2026-07-24 閱讀節奏修正）。 */
.gate-thru { position: relative; height: 240vh; scroll-snap-align: start; }
.gate-stage {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  justify-content: center;
}
.gate-thru-media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 45%;
  transform-origin: 50% 45%;
  will-change: transform;
}
/* 中央徑向亮光：模擬「門內的光迎面而來」，screen 混合模式讓光疊在畫面上而非蓋掉畫面 */
.gate-thru-glow {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 45%, rgba(255, 255, 255, 0.95) 0%, var(--gate-glow, var(--lf-champagne-gold)) 32%, transparent 72%);
  mix-blend-mode: screen;
  opacity: 0;
  pointer-events: none;
}
.gate-thru-copy {
  position: relative;
  z-index: 2;
  width: min(92%, 560px);
  margin: 0 auto clamp(2.5rem, 8vh, 4.5rem);
  background: rgba(244, 235, 221, 0.88);
  border-radius: 20px;
  padding: 1.5rem 1.75rem;
  text-align: left;
  box-shadow: 0 18px 40px rgba(74, 58, 99, 0.2);
  /* 卡片本體常駐可見（不再整卡淡入）——分段展開改成卡片內每個元素各自
     認自己的 scroll 進度子區間淡入，見 js/oracle.js updateGate()。 */
}
.gate-thru-copy .gate-marker,
.gate-thru-copy .gate-name,
.gate-thru-copy .gate-story,
.gate-thru-copy .gate-body { margin-bottom: 0.5rem; }
.gate-thru-copy .gate-body { margin-bottom: 0; }
.gate-thru-index {
  position: absolute;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.8rem;
  color: var(--lf-deep-lavender);
  background: rgba(255, 255, 255, 0.7);
  padding: 0.35rem 0.85rem;
  border-radius: 999px;
}
.gate-thru-dot {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--gate-glow, var(--lf-champagne-gold));
  box-shadow: 0 0 8px var(--gate-glow, var(--lf-champagne-gold));
}

/* 分段敘事展開：卡片內每個元素預設「未登場」狀態（opacity:0 + 各自的位移/字距/blur 起始值），
   實際數值每 rAF 由 js/oracle.js updateGate() 依 scroll 子區間覆寫成 inline style，
   這裡的值只是 JS 尚未接手前（或 reduced-motion 未強制覆寫時）的靜態起點，不是動畫本身。 */
.gate-marker {
  font-family: 'Cormorant Garamond', 'LXGW WenKai TC', serif;
  font-size: 0.85rem;
  letter-spacing: 0.18em; /* 就定位後的收攏值；JS 從 0.5em 動態收攏到此 */
  color: var(--gate-glow, var(--lf-champagne-gold));
  opacity: 0;
}
.gate-name {
  font-family: 'Cormorant Garamond', 'LXGW WenKai TC', serif;
  font-size: clamp(1.4rem, 3vw, 1.9rem);
  color: var(--lf-deep-lavender);
  opacity: 0;
  transform: translateY(18px);
  /* mix-blend-mode 文字融色：門名文字疊在 .gate-thru-copy 的 88% 不透明 ivory 卡片上
     （見上方 .gate-thru-copy 註解，卡片背景才是實際 backdrop，滿版場景圖只透出約 12%
     的量），multiply 讓字色吃到卡片底色與微量場景色，仍是深色字、對比只升不降
     （實測數字見驗收報告）。 */
  mix-blend-mode: multiply;
}
.gate-story {
  color: var(--lf-deep-lavender);
  line-height: 1.85;
  font-size: 1.05rem;
}
/* 場景句／指引句：以「；」分割成兩個 inline span，各自水彩暈染式 blur-in（見 js updateGate） */
.gate-story-scene,
.gate-story-guide {
  display: inline;
  opacity: 0;
  filter: blur(6px);
}
/* 身體對應：字級小於 story，與舊版 chakra/domain 小字同層級；settle 目標 0.85（JS 控制，非滿 1） */
.gate-body {
  font-size: 0.8rem;
  color: var(--lf-earth-clay);
  opacity: 0;
  transform: translateY(10px);
}

.gates-outro { position: relative; text-align: center; padding: 5.5rem 1.25rem 4rem; }

/* 銜接第七道門 → 抵達段：原本是硬切（實測接縫單一像素列亮度落差 55，肉眼就是一條橫線）。
   往上疊一層漸層把門的底邊化開；顏色是從實際畫面取樣的 rgb(243,231,216)——不能用 CSS 變數，
   因為 body 是白的、這片米色其實來自 fixed 的 .oracle-scene-bg 底圖，查 token 會拿到錯的顏色。
   .gates-outro 是 position:relative 且在 DOM 中晚於巡禮 section → 會蓋在 sticky 場景之上。 */
.gates-outro::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: -190px;
  height: 190px;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    rgba(243, 231, 216, 0) 0%,
    rgba(243, 231, 216, 0.45) 42%,
    rgba(243, 231, 216, 0.82) 72%,
    rgb(243, 231, 216) 100%
  );
}

/* 七道門地圖（工單新增）：巡禮走完的視覺回顧，桌機收在 720px、手機 92vw——width+max-width
   組合一次處理兩個尺寸，不需要另外寫 media query。eyebrow 語氣與站上其他 eyebrow
   （.oracle-eyebrow）同樣式，只是置中，放在圖片上方當作「這是什麼」的極小字說明。 */
/* 尺寸取捨（2026-07-29 user 反饋「圖是不是太小」）：原檔只有 1536×1024（AI 產圖、母檔同尺寸），
   要在 2x 螢幕維持像素級銳利，CSS 寬度上限就是 768px——原本的 720px 正是踩著這條線。
   問題不是像素而是「相對感」：前面七道門都是滿版 1440px，接一張 720px 的卡就像縮圖。
   取捨＝放大到 1060px（2x 螢幕約 1.38 倍放大，水彩軟邊吃得住），並把「卡片感」拿掉
   （圓角變小、陰影放大變淡），讓它像融進頁面的一幅畫、不像貼上去的物件。
   若要真正滿版，需要重產更大的原圖——那是另一件事。 */
.gates-outro-map {
  margin: 0 auto 2rem;
  width: 92vw;
  max-width: 1060px;
}
.gates-outro-map-eyebrow { text-align: center; }
.gates-outro-map-img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 12px;
  box-shadow: 0 30px 80px rgba(74, 58, 99, 0.14);
}

.gates-outro-copy {
  color: var(--lf-deep-lavender);
  font-style: italic;
  max-width: 480px;
  margin: 0 auto 1.5rem;
}
/* 地圖／句子／CTA 依序浮現（同款 blur-in、一次性 IO 觸發，見 initStaggeredReveal）。
   CTA 用一層 wrap div 承接淡入效果，不直接動 .custom-btn 本身的 opacity/filter/transition
   ——.custom-btn 自己有 hover 用的 transition（transform/box-shadow/background，見
   lumina-polish.css），若直接在按鈕上覆寫 transition 簡寫會整包蓋掉、連帶弄壞 hover 動畫。
   delay 依序拉開（地圖先、文案次之、CTA 最後），呼應「先看見走過的路→再讀文案→再按鈕」的閱讀順序。 */
.gates-outro-map,
.gates-outro-copy,
.gates-outro-cta-wrap {
  opacity: 0;
  filter: blur(8px);
  transition: opacity 0.9s ease, filter 0.9s ease;
}
.gates-outro-copy { transition-delay: 0.2s; }
.gates-outro-cta-wrap { transition-delay: 0.45s; }
.gates-outro.is-revealed .gates-outro-map,
.gates-outro.is-revealed .gates-outro-copy,
.gates-outro.is-revealed .gates-outro-cta-wrap {
  opacity: 1;
  filter: none;
}

/* 375px：文字卡收斂寬度、字級 clamp 已含在 gate-marker/gate-name/gate-story，cover 裁切左右可接受 */
@media (max-width: 480px) {
  .gate-thru-copy { padding: 1.25rem 1.35rem; margin-bottom: clamp(2rem, 10vh, 3rem); }
  .gate-story { font-size: 1rem; }
}

/* reduced-motion：每段變回 100vh 靜態滿版圖＋文字常駐可讀，無 scale 無視差、JS 不掛 scroll 監聽。
   卡片內每個分段元素（標記/門名/場景句/指引句/身體行）與開場引言/抵達段的 blur-in
   一律強制常駐可見、無 transform/filter/transition，不套任何動態效果。 */
@media (prefers-reduced-motion: reduce) {
  .gate-thru { height: 100vh; }
  .gate-stage { position: relative; top: auto; }
  .gate-thru-media { transform: none !important; }
  .gate-thru-glow { opacity: 0.28; }
  .gate-thru-copy { opacity: 1 !important; transform: none !important; }
  .gate-thru-copy .gate-marker,
  .gate-thru-copy .gate-name,
  .gate-thru-copy .gate-story-scene,
  .gate-thru-copy .gate-story-guide,
  .gate-thru-copy .gate-body {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
  }
  .gate-thru-copy .gate-body { opacity: 0.85 !important; }

  .gates-journey-lead,
  .gates-journey-intro-sub,
  .gates-outro-map,
  .gates-outro-copy,
  .gates-outro-cta-wrap {
    opacity: 1 !important;
    filter: none !important;
    transition: none !important;
  }
}

/* 「再抽一張」：finale 導流層上方的次要出口（巡禮收起後唯一不用重新載入頁面的再抽入口） */
.oracle-again-row {
  margin: 0 0 2rem;
  text-align: center;
}
