/* ui-motion：全站動態效果庫（2026-10-29 立）
 * 純 CSS、零依賴、框架無關。class 前綴一律 m-，避免撞名。
 * 需要 JS 的效果（漣漪、捲動進場、數字累加、長按確認、通知、複製回饋）見 motion.js。
 * 規範與選用原則見 ../SKILL.md。靈感參考 Motion Library（kittenjam），程式碼為自行實作。
 */

/* ───────── 1. 動態 token（全站只用這幾個值） ───────── */
:root {
  --m-fast: 150ms;   /* 微互動：按壓、hover、切換 */
  --m-base: 250ms;   /* 元件：抽屜、彈窗、通知、手風琴 */
  --m-slow: 400ms;   /* 大面積：頁面轉場、捲動進場 */
  --m-stagger: 40ms; /* 列表逐項間隔 */

  --m-ease: cubic-bezier(.4, 0, .2, 1);       /* 一般位移、狀態切換 */
  --m-ease-out: cubic-bezier(0, 0, .2, 1);    /* 進場（減速落定） */
  --m-ease-in: cubic-bezier(.4, 0, 1, 1);     /* 離場（加速離開） */
  --m-ease-spring: cubic-bezier(.34, 1.56, .64, 1); /* 輕微回彈，只用於小元素 */

  --m-rise: 12px;    /* 進場位移距離 */
  --m-accent: var(--gold-600, #F5C518);
  --m-ink: var(--navy-800, #16244A);
  --m-surface: var(--card, #ffffff);
  --m-skeleton: #e9edf4;
  --m-skeleton-hi: #f6f8fb;
  --m-ok: var(--green, #12a150);
  --m-danger: var(--red, #e0453a);
}

/* ───────── 2. 介面回饋 ───────── */

/* 按鈕按壓：所有可點按鈕的基本回饋 */
.m-press { transition: transform var(--m-fast) var(--m-ease); }
.m-press:active { transform: scale(.97); }

/* 漣漪：從點擊位置擴散（需 motion.js 的 initRipple） */
.m-ripple { position: relative; overflow: hidden; isolation: isolate; }
.m-ripple-wave {
  position: absolute; border-radius: 50%; pointer-events: none; z-index: -1;
  background: currentColor; opacity: .18; transform: scale(0);
  animation: m-ripple var(--m-slow) var(--m-ease-out) forwards;
}
@keyframes m-ripple { to { transform: scale(1); opacity: 0; } }

/* 卡片浮起：只在有滑鼠的裝置生效，手機不會黏住 */
.m-lift { transition: transform var(--m-base) var(--m-ease), box-shadow var(--m-base) var(--m-ease); }
@media (hover: hover) {
  .m-lift:hover { transform: translateY(-3px); box-shadow: 0 10px 24px rgba(22, 36, 74, .10); }
}

/* 連結底線：進入時從左長出、離開時往右收 */
.m-underline {
  text-decoration: none;
  background: linear-gradient(currentColor, currentColor) no-repeat right bottom / 0 1.5px;
  transition: background-size var(--m-base) var(--m-ease);
}
@media (hover: hover) {
  .m-underline:hover { background-position: left bottom; background-size: 100% 1.5px; }
}
.m-underline:focus-visible { background-position: left bottom; background-size: 100% 1.5px; }

/* 開關：圓鈕滑動，按住時略拉長 */
.m-switch {
  --w: 44px; --h: 26px; --k: 20px;
  position: relative; display: inline-block; width: var(--w); height: var(--h);
  border-radius: 999px; background: #c9d1de; cursor: pointer; border: 0; padding: 0;
  transition: background-color var(--m-base) var(--m-ease);
}
.m-switch::after {
  content: ""; position: absolute; top: 3px; left: 3px; width: var(--k); height: var(--k);
  border-radius: 999px; background: #fff; box-shadow: 0 1px 3px rgba(0, 0, 0, .25);
  transition: transform var(--m-base) var(--m-ease), width var(--m-fast) var(--m-ease);
}
.m-switch:active::after { width: calc(var(--k) + 5px); }
.m-switch[aria-checked="true"] { background: var(--m-ok); }
.m-switch[aria-checked="true"]::after { transform: translateX(calc(var(--w) - var(--k) - 6px)); }
.m-switch[aria-checked="true"]:active::after { transform: translateX(calc(var(--w) - var(--k) - 11px)); }

/* 按讚／收藏：點擊彈跳＋光圈（加 .is-on 觸發） */
.m-pop { position: relative; transition: color var(--m-fast) var(--m-ease); }
.m-pop.is-on { animation: m-pop var(--m-base) var(--m-ease-spring); }
.m-pop.is-on::before {
  content: ""; position: absolute; inset: -6px; border-radius: 50%;
  border: 2px solid currentColor; opacity: 0; pointer-events: none;
  animation: m-ring var(--m-slow) var(--m-ease-out);
}
@keyframes m-pop { 0% { transform: scale(1); } 40% { transform: scale(1.25); } 100% { transform: scale(1); } }
@keyframes m-ring { 0% { transform: scale(.6); opacity: .6; } 100% { transform: scale(1.4); opacity: 0; } }

/* 按鈕載入→成功：data-state="loading" | "done" */
.m-btn-state { position: relative; transition: background-color var(--m-base) var(--m-ease); }
.m-btn-state > .m-label { transition: opacity var(--m-fast) var(--m-ease); }
.m-btn-state[data-state="loading"] > .m-label,
.m-btn-state[data-state="done"] > .m-label { opacity: 0; }
.m-btn-state[data-state="loading"] { pointer-events: none; }
.m-btn-state[data-state="loading"]::after {
  content: ""; position: absolute; inset: 0; margin: auto; width: 18px; height: 18px;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: m-spin .7s linear infinite;
}
.m-btn-state[data-state="done"] { background: var(--m-ok); color: #fff; }
.m-btn-state[data-state="done"]::after {
  content: ""; position: absolute; inset: 0; margin: auto; width: 6px; height: 12px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: translateY(-2px) rotate(45deg);
  animation: m-check var(--m-base) var(--m-ease-spring);
}
@keyframes m-check { from { opacity: 0; transform: translateY(-2px) rotate(45deg) scale(.4); } }

/* 長按確認：危險操作用（需 motion.js 的 initHoldConfirm） */
.m-hold { position: relative; overflow: hidden; isolation: isolate; user-select: none; -webkit-user-select: none; touch-action: none; }
.m-hold::before {
  content: ""; position: absolute; inset: 0; z-index: -1; background: var(--m-danger); opacity: .22;
  transform: scaleX(0); transform-origin: left; transition: transform var(--m-base) var(--m-ease-in);
}
.m-hold.is-holding::before { transform: scaleX(1); transition: transform var(--hold, 1200ms) linear; }

/* 複製回饋：data-copied 出現「已複製」小提示（需 motion.js 的 initCopy） */
.m-copy { position: relative; }
.m-copy::after {
  content: attr(data-copied-text); position: absolute; left: 50%; bottom: calc(100% + 8px);
  padding: 4px 10px; border-radius: 6px; background: var(--m-ink); color: #fff;
  font-size: 12px; white-space: nowrap; pointer-events: none;
  opacity: 0; transform: translate(-50%, 4px);
  transition: opacity var(--m-fast) var(--m-ease), transform var(--m-fast) var(--m-ease);
}
.m-copy[data-copied]::after { opacity: 1; transform: translate(-50%, 0); }

/* ───────── 3. 進場 ───────── */

/* 淡入上移：區塊、卡片出現
 * 進場位移一律用獨立的 translate 屬性，不佔用 transform，才不會蓋掉 .m-lift／.m-press */
.m-fade-up { animation: m-fade-up var(--m-slow) var(--m-ease-out) both; }
@keyframes m-fade-up { from { opacity: 0; translate: 0 var(--m-rise); } }

/* 列表逐項：子元素設 style="--i: 0..n"，超過 10 項後同時出現 */
.m-stagger > * {
  animation: m-fade-up var(--m-slow) var(--m-ease-out) both;
  animation-delay: calc(min(var(--i, 0), 10) * var(--m-stagger));
}

/* 捲動進場：加 .m-reveal，進入畫面時 motion.js 設 data-in 屬性。
 * 只有 JS 載入後（html.m-js）才先藏起來，JS 壞掉時內容照常顯示。
 * 用 data-in 而非 class：React 重新 render className 時不會被洗掉。 */
.m-reveal { transition: opacity var(--m-slow) var(--m-ease-out), translate var(--m-slow) var(--m-ease-out); }
.m-js .m-reveal:not([data-in]) { opacity: 0; translate: 0 calc(var(--m-rise) * 2); }

/* 骨架載入：資料回來前的佔位 */
.m-skeleton {
  border-radius: 6px; color: transparent !important; user-select: none;
  background: linear-gradient(90deg, var(--m-skeleton) 25%, var(--m-skeleton-hi) 37%, var(--m-skeleton) 63%) 0 0 / 400% 100%;
  animation: m-shimmer 1.4s ease infinite;
}
@keyframes m-shimmer { from { background-position: 100% 0; } to { background-position: 0 0; } }

/* 頂部載入條：換頁、送出表單時（加 .is-active 開始、.is-done 結束） */
.m-topbar {
  position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 100; pointer-events: none;
  background: var(--m-accent); transform-origin: left; transform: scaleX(0); opacity: 0;
}
.m-topbar.is-active { opacity: 1; transform: scaleX(.8); transition: transform 8s cubic-bezier(.1, .6, .2, 1); }
.m-topbar.is-done {
  opacity: 0; transform: scaleX(1);
  transition: transform var(--m-base) var(--m-ease), opacity var(--m-base) var(--m-ease) var(--m-base);
}

/* 讀取轉圈 */
.m-spinner {
  display: inline-block; width: 1em; height: 1em; vertical-align: -.125em;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: m-spin .7s linear infinite;
}
@keyframes m-spin { to { transform: rotate(360deg); } }

/* ───────── 4. 浮層與轉場 ───────── */

/* 遮罩：抽屜、底部選單、彈窗共用（加 .is-open） */
.m-overlay {
  position: fixed; inset: 0; background: rgba(10, 18, 38, .5); z-index: 40;
  opacity: 0; pointer-events: none; transition: opacity var(--m-base) var(--m-ease);
}
.m-overlay.is-open { opacity: 1; pointer-events: auto; }

/* 側邊抽屜：手機導覽（加 .is-open） */
.m-drawer {
  position: fixed; top: 0; bottom: 0; left: 0; width: min(300px, 85vw); z-index: 41;
  background: var(--m-surface); box-shadow: 4px 0 24px rgba(0, 0, 0, .2); overflow-y: auto;
  transform: translateX(-100%); visibility: hidden;
  transition: transform var(--m-base) var(--m-ease-in), visibility 0s var(--m-base);
}
.m-drawer.is-open { transform: none; visibility: visible; transition: transform var(--m-base) var(--m-ease-out); }

/* 底部選單：手機的下拉選項（加 .is-open） */
.m-sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 41; max-height: 80vh; overflow-y: auto;
  background: var(--m-surface); border-radius: 20px 20px 0 0;
  padding-bottom: env(safe-area-inset-bottom); box-shadow: 0 -8px 30px rgba(0, 0, 0, .2);
  transform: translateY(105%); visibility: hidden;
  transition: transform var(--m-base) var(--m-ease-in), visibility 0s var(--m-base);
}
.m-sheet.is-open { transform: none; visibility: visible; transition: transform var(--m-base) var(--m-ease-out); }

/* 彈窗：只用於確認／刪除，CRUD 一律獨立頁 */
.m-dialog { animation: m-dialog var(--m-base) var(--m-ease-out); }
@keyframes m-dialog { from { opacity: 0; transform: scale(.96) translateY(6px); } }

/* 通知：右上（手機為上方置中）滑入，motion.js 的 toast() 產生 */
.m-toast-stack {
  position: fixed; top: 16px; right: 16px; z-index: 90; display: flex; flex-direction: column; gap: 8px;
  pointer-events: none; width: min(360px, calc(100vw - 32px));
}
@media (max-width: 640px) { .m-toast-stack { right: 50%; transform: translateX(50%); } }
.m-toast {
  pointer-events: auto; padding: 12px 16px; border-radius: 10px; background: var(--m-ink); color: #fff;
  font-size: 14px; box-shadow: 0 8px 24px rgba(0, 0, 0, .18); border-left: 4px solid var(--m-accent);
  animation: m-toast-in var(--m-base) var(--m-ease-out);
}
.m-toast[data-type="ok"] { border-left-color: var(--m-ok); }
.m-toast[data-type="error"] { border-left-color: var(--m-danger); }
.m-toast.is-leaving { animation: m-toast-out var(--m-fast) var(--m-ease-in) forwards; }
@keyframes m-toast-in { from { opacity: 0; transform: translateY(-8px) scale(.98); } }
@keyframes m-toast-out { to { opacity: 0; transform: translateY(-8px) scale(.98); } }

/* 手風琴展開：外層 .m-collapse，內層包一個 div（加 .is-open） */
.m-collapse { display: grid; grid-template-rows: 0fr; transition: grid-template-rows var(--m-base) var(--m-ease); }
.m-collapse > * { overflow: hidden; min-height: 0; visibility: hidden; transition: visibility 0s var(--m-base); }
.m-collapse.is-open { grid-template-rows: 1fr; }
.m-collapse.is-open > * { visibility: visible; transition-delay: 0s; } /* 收合時不可 Tab、不被朗讀 */
.m-chevron { display: inline-block; transition: transform var(--m-base) var(--m-ease); }
[aria-expanded="true"] .m-chevron { transform: rotate(180deg); }

/* 分頁底線滑動：容器 .m-tabs，設 --x / --w（motion.js 的 initTabs 會算好） */
.m-tabs { position: relative; }
.m-tabs::after {
  content: ""; position: absolute; left: 0; bottom: 0; height: 2px; background: var(--m-accent);
  width: var(--w, 0); transform: translateX(var(--x, 0));
  transition: transform var(--m-base) var(--m-ease), width var(--m-base) var(--m-ease);
}

/* 頁面轉場：支援 View Transitions 的瀏覽器才有，不支援就直接換頁 */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: m-vt-out var(--m-fast) var(--m-ease-in) both; }
::view-transition-new(root) { animation: m-vt-in var(--m-base) var(--m-ease-out) both; }
@keyframes m-vt-out { to { opacity: 0; } }
@keyframes m-vt-in { from { opacity: 0; transform: translateY(6px); } }

/* ───────── 5. 狀態提示 ───────── */

/* 表單錯誤搖晃：送出失敗時加 .is-error（約 300ms 後可移除） */
.m-shake.is-error { animation: m-shake 300ms var(--m-ease); }
@keyframes m-shake {
  20% { transform: translateX(-6px); } 40% { transform: translateX(5px); }
  60% { transform: translateX(-3px); } 80% { transform: translateX(2px); }
}

/* 新資料高亮：剛新增或更新的列閃一下底色 */
.m-flash { animation: m-flash 1.6s var(--m-ease-out); }
@keyframes m-flash { from { background-color: color-mix(in srgb, var(--m-accent) 35%, transparent); } }

/* 未讀徽章脈衝：最多跑 3 次就停，不做無限循環 */
.m-pulse { position: relative; isolation: isolate; }
.m-pulse::after {
  content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; background: inherit;
  animation: m-pulse 1.6s var(--m-ease-out) 3; pointer-events: none;
}
@keyframes m-pulse { from { transform: scale(1); opacity: .6; } to { transform: scale(2.2); opacity: 0; } }

/* ───────── 6. 前台行銷頁專用（後台不要用） ───────── */

/* 螢光筆劃記：重點詞。預設直接顯示；加 data-reveal 則捲到畫面時才劃過 */
.m-highlight {
  background: linear-gradient(color-mix(in srgb, var(--m-accent) 55%, transparent), color-mix(in srgb, var(--m-accent) 55%, transparent))
    no-repeat left 85% / 100% 40%;
  transition: background-size 700ms var(--m-ease-out);
}
.m-js .m-highlight[data-reveal]:not([data-in]) { background-size: 0 40%; }

/* 標題逐行揭示：每行包 <span class="m-line"><span>文字</span></span>，--i 為行序 */
.m-line { display: block; overflow: hidden; padding-bottom: .08em; }
.m-line > span {
  display: inline-block; animation: m-line-up 700ms var(--m-ease-out) both;
  animation-delay: calc(var(--i, 0) * 90ms);
}
@keyframes m-line-up { from { transform: translateY(110%); } }

/* 換詞輪播：固定前綴，後面的詞輪流替換（.m-rotator 內放 3 個 span） */
.m-rotator { display: inline-grid; vertical-align: bottom; overflow: hidden; height: 1.25em; line-height: 1.25; }
.m-rotator > span { grid-area: 1 / 1; animation: m-rotate 7.5s var(--m-ease) infinite both; opacity: 0; }
.m-rotator > span:nth-child(2) { animation-delay: 2.5s; }
.m-rotator > span:nth-child(3) { animation-delay: 5s; }
@keyframes m-rotate {
  0% { opacity: 0; transform: translateY(100%); }
  6%, 30% { opacity: 1; transform: none; }
  36%, 100% { opacity: 0; transform: translateY(-100%); }
}

/* 主按鈕光澤：一頁最多一顆 CTA 用 */
.m-shine { position: relative; overflow: hidden; isolation: isolate; }
.m-shine::after {
  content: ""; position: absolute; inset: 0 auto 0 -60%; width: 40%; z-index: 1; pointer-events: none;
  background: linear-gradient(105deg, transparent, rgba(255, 255, 255, .45), transparent);
  transform: skewX(-20deg); animation: m-shine 4s var(--m-ease) 1s infinite;
}
@keyframes m-shine { 0%, 70% { left: -60%; } 100% { left: 130%; } }

/* Logo 跑馬燈：合作客戶牆。內容要放兩份，第二份加 aria-hidden="true" */
.m-marquee {
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 10%, #000 90%, transparent);
}
.m-marquee > .m-marquee-track { display: flex; width: max-content; gap: var(--gap, 48px); animation: m-marquee var(--speed, 30s) linear infinite; }
@media (hover: hover) { .m-marquee:hover > .m-marquee-track { animation-play-state: paused; } }
@keyframes m-marquee { to { transform: translateX(calc(-50% - var(--gap, 48px) / 2)); } }

/* ───────── 7. 減少動態效果（必備，不准刪） ─────────
 * 使用者在系統設定開了「減少動態效果」：位移、縮放、循環一律關掉，只保留瞬間狀態切換。
 */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 1ms !important; animation-iteration-count: 1 !important;
    animation-delay: 0ms !important; transition-duration: 1ms !important; transition-delay: 0ms !important;
    scroll-behavior: auto !important;
  }
  .m-js .m-reveal:not([data-in]) { opacity: 1; translate: none; }
  .m-js .m-highlight[data-reveal]:not([data-in]) { background-size: 100% 40%; }
  .m-skeleton { animation: none; }
  .m-spinner, .m-btn-state[data-state="loading"]::after { animation-duration: 1.4s !important; animation-iteration-count: infinite !important; }
  /* 長按進度是功能資訊，不是裝飾：保留線性填滿，否則使用者看不出要按多久 */
  .m-hold.is-holding::before { transition-duration: var(--hold, 1200ms) !important; }
  .m-shine::after, .m-pulse::after { display: none; }
  .m-marquee { overflow-x: auto; mask-image: none; -webkit-mask-image: none; }
  .m-marquee > .m-marquee-track { animation: none; }
  .m-marquee [aria-hidden="true"] { display: none; }
  .m-rotator > span { animation: none; opacity: 0; }
  .m-rotator > span:first-child { opacity: 1; }
  ::view-transition-old(root), ::view-transition-new(root) { animation: none; }
}
