/* 首頁投影片式滾動。只有首頁載入這支。
   跨頁共用的元件（.card、.tag、.btn）仍在 pages.css / base.css。 */

/* ---- 捲動容器 ----
   預設就是可用的原生捲動 + snap：手機是這個模式，JS 載入失敗時也是這個模式。
   桌機接管由 deck.js 加上 .is-controlled 才啟用，絕不能只靠 media query，
   否則 deck.js 掛掉會讓整頁凍結。 */
.deck {
  height: 100svh;
  overflow-y: auto;
  overflow-x: hidden;
  scroll-snap-type: y mandatory;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.deck::-webkit-scrollbar { display: none; }

html.js .deck.is-controlled {
  overflow: hidden;
  scroll-snap-type: none;
}

/* ---- 單張投影片 ----
   預設（原生 snap 模式：觸控裝置、窄視窗、沒有 JS）是 min-height + 不自己捲。
   投影片**絕對不能**在這個模式下變成自己的捲動容器：內容一超出一屏，手指
   滑動就會先捲投影片內部，捲到底才輪得到 .deck 換頁，往回滑時內部沒歸零
   還上不去——使用者感覺就是「滑不上去也滑不下來」，而且畫面越小越嚴重
   （實測 375x667 有六張超出、390x844 仍有兩張）。

   內容超出時整張自己長高，多出來的部分靠下面 .deck__snap-end 那個底端
   吸附點抵達，所以 scroll-snap-type 可以維持 mandatory，換頁一樣是一張一張
   吸附的手感。這兩件事是一組的，不要只改其中一個。 */
.slide {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-content: center;
  scroll-snap-align: start;
  scroll-snap-stop: always;   /* 一次手勢只跳一張，不會連跳 */
}

/* JS 接管模式（.is-controlled）才回到「剛好一屏、內容自己捲」。
   那邊 deck.js 直接寫 scrollTop 且 .deck 是 overflow:hidden，原生捲動鏈根本
   沒被用到，所以巢狀容器不會咬到手勢；反而是投影片一旦比視窗高就會有一段
   永遠捲不到，因此這個模式必須維持固定高度。

   這裡絕對不能加 overscroll-behavior。overflow-y: auto 讓每張都成為捲動容器，
   但內容通常剛好一個螢幕高、捲動範圍是 0；此時若再寫 overscroll-behavior:
   contain，Chrome 會把手勢鎖在這個「捲不動的容器」裡，永遠不往上傳給真正在
   捲的 .deck。 */
html.js .deck.is-controlled .slide {
  height: 100svh;
  overflow-y: auto;
  overflow-x: hidden;
}

/* 每張投影片後面跟著一個零高度的哨兵（見 templates/home.html）。
   它把「這張的底部」也變成一個吸附點：
     - 剛好一屏的投影片，底端吸附點跟頂端重合，行為與從前完全相同；
     - 比一屏高的投影片，多出一個「先看完這張」的停靠點，再一次手勢才換頁。
   沒有它的話，mandatory 只認得每張的頂端，最後一張超出的部分（b2b 那 398px，
   裡面就是頁尾）永遠捲不到——這是實測出來的，不是理論顧慮。 */
.deck__snap-end {
  height: 0;
  scroll-snap-align: end;
  scroll-snap-stop: always;
}

.slide__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---- Hero 多鳥輪播 ----
   預設「第一張不透明、其餘透明」是刻意的：沒有 JS、JS 拋錯、或
   prefers-reduced-motion 時都會定格在第一張。這裡不能沿用 deck 的 .is-ready
   模式（那個的預設是「沒 JS 就全部顯示」），疊層輪播套上去會變成所有圖疊在一起。 */
.hero-carousel { position: absolute; inset: 0; }
.hero-carousel .slide__media {
  opacity: 0;
  transition: opacity 1.2s var(--ease);
}
.hero-carousel .slide__media.is-current { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .hero-carousel .slide__media { transition: none; }
}

/* Hero 的四張底圖都是白底棚拍，原本的深色漸層遮罩＋白字蓋上去會變成一片濁灰，
   所以 hero 走淺色版。遮罩留一層極淡的白，只為了讓底部文字區與影像分離。
   .slide--offline 仍是深色遮罩＋白字，兩者不再共用宣告。
   導覽列不必跟著改：.nav 是 mix-blend-mode: difference 配 color: #fff，
   白字對白底 difference 之後自己變黑字，深色投影片上仍是白字。 */
.slide--hero::after,
.slide--offline::after {
  content: "";
  position: absolute;
  inset: 0;
}
.slide--hero::after {
  background: linear-gradient(180deg, rgba(245,245,243,0) 40%, rgba(245,245,243,0.92) 100%);
}
.slide--offline::after { background: rgba(10,10,10,0.6); }

.slide__inner {
  position: relative;
  z-index: 1;
  width: 100%;
  display: grid;
  gap: 1.25rem;
  justify-items: start;
  /* 上方讓出固定 nav 的高度，避免第一行被壓到 */
  padding: calc(var(--nav-h) + 2rem) var(--gutter) 3rem;
}
.slide__inner--end { align-self: end; padding-bottom: 12vh; }
.slide__inner--wide { padding-inline: 0; }

.slide--hero .slide__inner { color: var(--ink); }
.slide--offline .slide__inner { color: #fff; }

/* 手機直幅時白底棚拍的鳥會被 object-fit: cover 裁掉頭或腳，
   取景往上挪一點。沒有配對的直幅素材，所以用 object-position 而不是 <picture>。 */
@media (max-width: 899px) {
  .slide--hero .slide__media { object-position: 50% 35%; }
}

/* ---- 第 7 張：B2B ＋ 頁尾 ----
   頁尾收在這張底部而不是自成一張，維持「首頁頁尾在 deck 內」的既有測試。 */
.slide--b2b { background: var(--black); color: var(--paper); align-content: space-between; }
/* 0826：這張一直沒有自己的 padding-inline 覆寫（slide__inner--wide 是 0），
   標題貼著視窗左緣、右欄選項疊到固定的 deck-nav 頁碼上。比照 voice／guide
   補回 gutter；上下留白收一點，讓「標題＋舞台＋CTA＋頁尾」進一屏。 */
.slide--b2b .slide__inner {
  padding-inline: var(--gutter);
  padding-block: calc(var(--nav-h) + 1rem) 1rem;
  gap: 1rem;
}
/* base.css 的 .footer 是長捲頁的版本（--section-y 上內距，一整個段落高）。
   塞進 100svh 的投影片會把整張撐成 1.6 屏，所以 deck 內用緊湊版覆寫：
   字級降一級、留白全收，資訊一項不少。 */
.slide--b2b .footer {
  width: 100%;
  padding: 0.75rem var(--gutter) 0.75rem;
  border-top: 1px solid var(--grey-700);
}
.slide--b2b .footer__brand { font-size: var(--step-md); margin: 0; }
.slide--b2b .footer__links { margin-block: 0.4rem; gap: 0.5rem 1.5rem; }
.slide--b2b .footer__auth { margin: 0 0 0.25rem; }
.slide--b2b .footer__meta { margin: 0; }
.slide--b2b .footer .eyebrow { margin: 0; }
.b2b__head { display: grid; gap: 0.5rem; }
.b2b__title { font-family: var(--font-en); font-size: var(--step-lg); margin: 0; }
.b2b__lead { color: var(--grey-500); max-width: 60ch; margin: 0; }

.b2b__stage {
  /* .slide__inner 是 justify-items: start 的格線：不寫 width 的話 stage 會
     縮成內容寬，而圖片箱沒有內在尺寸（絕對定位的子圖撐不開它），1fr 那欄
     直接被算成 0px —— 整個 OEM 視覺就這樣消失了（0826 的修正對象）。 */
  width: 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 14rem);
  gap: 2rem;
  align-items: center;
}
.b2b__images {
  position: relative;
  aspect-ratio: 4 / 3;
  /* 高度上限用 vh 換算回寬度：整張投影片還要放標題、CTA 與頁尾，
     圖再高就會把頁尾擠出一屏。 */
  width: min(100%, calc(37vh * 4 / 3));
  margin-inline: auto;
}
.b2b__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 4px;
  opacity: 0;
  transition: opacity 0.5s var(--ease);
}
.b2b__image.is-current { opacity: 1; }

.b2b__looks { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.b2b__look {
  font-size: var(--step-sm);
  color: var(--grey-500);
  border-bottom: 1px solid var(--grey-700);
}
/* 可操作的是 <li> 裡面那顆 <button>（見 slides/b2b.html 的註解）：外觀要維持
   原本那排純文字選項，所以把瀏覽器給按鈕的預設外觀（背景、外框、置中、
   自己的字體）全部歸零，內距則從 <li> 搬到這裡來。寬度撐滿是為了讓整條選項
   都可點，而不是只有文字那幾個字寬。 */
.b2b__look-btn {
  display: block;
  width: 100%;
  font: inherit;
  color: inherit;
  text-align: left;
  background: none;
  border: 0;
  padding: 0.3rem 0;
  cursor: pointer;
}
.b2b__look-btn[aria-current="true"] { color: var(--neon); }
.b2b__look-btn:focus-visible { outline: 2px solid var(--neon); outline-offset: 3px; }

.b2b__ctas { display: flex; flex-wrap: wrap; gap: 0.75rem; margin: 0; }

@media (prefers-reduced-motion: reduce) {
  .b2b__image { transition: none; }
}
@media (max-width: 899px) {
  .b2b__stage { grid-template-columns: minmax(0, 1fr); }
  /* 手機這張本來就靠投影片內捲動收尾（.slide 是 overflow-y: auto），
     圖再收一點少捲一點 */
  .b2b__images { width: min(100%, calc(30vh * 4 / 3)); margin-inline: 0; }
  /* 手機沒有 hover，選項橫排讓使用者用點的（click 會觸發切換） */
  .b2b__looks { display: flex; flex-wrap: wrap; gap: 0.5rem; }
  .b2b__look { border: 1px solid var(--grey-700); border-radius: 999px; }
  .b2b__look-btn { padding: 0.3rem 0.75rem; text-align: center; }
}

/* ---- Hero ---- */
/* 主標從英文品牌名換成中文長句之後，英文的緊排設定會讓中文擠在一起：
   font-en 沒有中文字、-0.04em 字距與 0.88 行高都是為了大寫拉丁字母調的。
   max-width 也從 ch 換成 em —— ch 是「0」的寬度，中文字約是它的兩倍，
   14ch 實際只放得下 7 個中文字。--step-hero 不能改，/404 的錯誤碼也在用。 */
.hero__title {
  font-family: var(--font-zh);
  font-size: clamp(2rem, 6vw, 5rem);
  font-weight: 700;
  line-height: 1.15;
  letter-spacing: 0;
  max-width: 14em;
  text-wrap: balance;
}
.hero__eyebrow { color: var(--grey-700); }
.hero__sub { font-size: var(--step-lg); margin: 0; }
.hero__badges {
  font-family: var(--font-en);
  font-size: var(--step-sm);
  letter-spacing: 0.16em;
  color: var(--neon);
  margin: 0;
}

/* ---- Hero 底部信任三欄 ---- */
.hero-trust {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem 2.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.hero-trust__item { display: grid; gap: 0.25rem; }
.hero-trust__num {
  font-family: var(--font-en);
  font-size: var(--step-xs);
  letter-spacing: 0.2em;
  color: var(--grey-700);
}
.hero-trust__en {
  font-family: var(--font-en);
  font-size: var(--step-sm);
  letter-spacing: 0.14em;
  font-weight: 700;
}
.hero-trust__zh { font-size: var(--step-xs); color: var(--grey-700); }

/* ---- 信任單張 ----
   共用的基本樣式在 pages.css；這裡只放滿版投影片的放大版覆寫。 */

/* ---- 第 2 張：叫聲試聽室 ---- */
.slide--voice { background: var(--black); color: var(--paper); }
.slide--voice .slide__inner { gap: 2rem; padding-inline: var(--gutter); }
.voice__head { display: grid; gap: 0.75rem; }
.voice__title { font-family: var(--font-zh); font-size: var(--step-lg); margin: 0; }
.voice__lead { color: var(--neon); font-size: var(--step-md); margin: 0; }

/* 固定排列，不是可捲動容器。理由同商品卡那段註解：
   Chrome 會把垂直滾輪量拿去捲游標下方的橫向捲軸，上層擋不乾淨。

   三格「等高、各自原生比例」：voice-1 是 720x720 方形，另外兩支是 1080x1920
   直式。舊版把三格都壓成 9/16 再 object-fit: cover，方形那支被裁掉近半個畫面
   （0826 的修正對象）。高度同時被 vh 與 vw 箝住：46vh 保證「標題＋整排影片
   ＋亮點列」塞得進一屏，38vw 保證 900px 上下的窄桌機不會把整排撐出橫向溢出。 */
.voice__clips {
  --clip-h: min(46vh, 30rem, 38vw);
  display: flex;
  gap: 1.25rem;
  justify-self: center;
  align-items: start;
}
.voice__clip { position: relative; margin: 0; }
.voice__video {
  height: var(--clip-h);
  width: auto;
  display: block;
  object-fit: cover;
  border-radius: 4px;
  background: var(--ink);
}
.voice__clip--square .voice__video { aspect-ratio: 1 / 1; }
.voice__clip--portrait .voice__video { aspect-ratio: 9 / 16; }
.voice__caption {
  font-size: var(--step-xs);
  color: var(--grey-500);
  padding-top: 0.5rem;
}
.voice__sound {
  position: absolute;
  left: 0.5rem;
  bottom: 2.25rem;
  font-family: var(--font-zh);
  font-size: var(--step-xs);
  background: rgba(10, 10, 10, 0.7);
  color: var(--paper);
  border: 1px solid var(--grey-700);
  border-radius: 999px;
  padding: 0.35rem 0.75rem;
  cursor: pointer;
}
/* 按鈕文字跟著 aria-pressed 走，狀態只有一個來源 */
.voice__sound .voice__sound-on { display: none; }
.voice__sound[aria-pressed="true"] .voice__sound-on { display: inline; }
.voice__sound[aria-pressed="true"] .voice__sound-off { display: none; }

.voice__highlights {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 2rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: var(--step-sm);
  color: var(--grey-500);
}

@media (max-width: 899px) {
  /* 手機一次看一支就好，三支並排會小到看不出細節 */
  .slide--voice .slide__inner { gap: 1.25rem; }
  /* 0903：改版後只剩兩格、且都是方形短片，手機並排兩欄剛好塞得下，
     不必再像三格時代那樣把第二格之後全藏起來。 */
  .voice__clips { justify-self: stretch; gap: 0.75rem; }
  .voice__clip { flex: 1 1 0; min-width: 0; }
  .voice__video { width: 100%; height: auto; max-height: 34vh; }
}

/* ---- 焦點商品 ---- */
.slide--roster .slide__inner { gap: 3rem; }
.featured__head {
  display: flex;
  align-items: baseline;
  gap: 1.5rem;
  flex-wrap: wrap;
  width: 100%;
  padding-inline: var(--gutter);
}
.featured__title { font-family: var(--font-zh); font-size: var(--step-lg); }
.featured__all { margin-left: auto; font-family: var(--font-en); font-size: var(--step-sm); letter-spacing: 0.14em; }
.featured__all:hover { color: var(--neon); }
.featured__meta {
  display: flex;
  align-items: center;
  gap: 1rem 2rem;
  flex-wrap: wrap;
  width: 100%;
  padding-inline: var(--gutter);
  margin-top: -1.5rem;   /* 貼近標題列，不要另起一個區塊的距離 */
}
.featured__lead { font-size: var(--step-sm); color: var(--grey-500); margin: 0; }
.featured__tags { display: flex; gap: 0.5rem; flex-wrap: wrap; list-style: none; margin: 0; padding: 0; }
.featured__tag {
  font-family: var(--font-en);
  font-size: var(--step-xs);
  letter-spacing: 0.12em;
  border: 1px solid var(--grey-700);
  color: var(--paper);
  padding: 0.15rem 0.6rem;
}

/* 商品卡完全不橫移：這裡是固定格線，不是可捲動的容器，也沒有任何橫向溢出。
   「游標停在商品上滾滾輪會往右推商品」的根源就是這排卡片以前是個橫向捲動
   容器 —— Chrome 只要看到游標下方有個只能橫捲的東西，就會把垂直的滾輪量
   直接拿去捲它的橫向捲軸，任何上層的 preventDefault 都擋不乾淨。現在容器
   沒了，滾輪一律往下換頁，不需要 JS 幫忙擋。
   minmax(0, 1fr) 不能寫成 1fr：grid 軌道的自動最小尺寸是內容的 max-content，
   商品名稱是不斷行的中文長句時會把軌道撐出視窗外，橫向捲軸就又回來了。 */
.featured__grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 1.5rem;
  width: 100%;
  padding-inline: var(--gutter);
}

/* 窄螢幕一列擠四張會小到看不清楚，改成 2×2。
   900px 這個斷點跟 deck.js 的 .is-controlled 條件同一個值，兩邊要一起改。
   （deck.js 的滾輪／鍵盤接管不看寬度，只看高度，別跟這個斷點混在一起。） */
@media (max-width: 899px) {
  .featured__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1.25rem 1rem;
  }
}

/* ---- 第 4 張：場景導購 ✕ 好評牆 ----
   桌機左右兩欄：左欄標題＋評分徽章＋三張情境卡＋CTA，右欄是兩條反向
   自動滾動的好評牆（.gwall）。手機收回單欄並隱藏牆（voices 那張已有
   文字跑馬燈，小螢幕塞兩面牆一定爆出一屏）。 */
.slide--guide { background: var(--paper); color: var(--ink); }
.slide--guide .slide__inner {
  gap: 2rem;
  padding-inline: var(--gutter);
  /* 左欄（標題＋三卡＋CTA）逼近一屏上限，上下留白比通用值再收一點，
     否則 eyebrow 會頂進固定 nav 底下。 */
  padding-block: calc(var(--nav-h) + 1.25rem) 1.5rem;
}
.guide__head { display: grid; gap: 0.5rem; }
.guide__title { font-family: var(--font-zh); font-size: var(--step-lg); margin: 0; }
.guide__lead { color: var(--grey-700); margin: 0; }
.guide__rating {
  margin: 0.25rem 0 0;
  font-size: var(--step-sm);
  font-weight: 700;
  background: var(--ink);
  color: var(--neon);
  border-radius: 999px;
  padding: 0.35rem 0.9rem;
  justify-self: start;
}

.guide__main { display: grid; gap: 1rem; justify-items: start; }
.guide__cases {
  display: grid;
  gap: 0.6rem;
  width: 100%;
}
.guide__case {
  display: grid;
  gap: 0.35rem;
  align-content: start;
  padding: 0.75rem 1.1rem;
  background: #fff;
  border-top: 3px solid var(--neon);
}
.guide__case-title { font-family: var(--font-zh); font-size: var(--step-md); margin: 0; }
.guide__scenario { font-size: var(--step-sm); color: var(--grey-700); margin: 0; }
.guide__quote {
  font-size: var(--step-sm);
  font-style: italic;
  line-height: 1.6;
  margin: 0;
  padding-left: 0.75rem;
  border-left: 2px solid var(--grey-500);
}
.guide__author { font-size: var(--step-xs); color: var(--grey-500); margin: 0; }

@media (min-width: 900px) {
  .slide--guide .slide__inner {
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
    column-gap: clamp(2rem, 4vw, 4rem);
    align-items: center;
  }
  .guide__main, .gwall { justify-self: stretch; }
}

/* ---- 好評牆（.gwall）----
   跟 voices 跑馬燈同一套規矩：不是可捲動容器（viewport overflow: hidden，
   柱子靠 transform 動畫位移），每條柱子渲染兩份、位移 -50% 無縫接回開頭。
   卡片間距用 margin-bottom 而不是 grid gap —— gap 會讓兩份複本之間的縫
   跟 -50% 對不上，循環接點就會跳一下。 */
.gwall { position: relative; width: 100%; }
.gwall__viewport {
  height: min(74vh, calc(100vh - var(--nav-h) - 6rem));
  overflow: hidden;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 1rem;
  mask-image: linear-gradient(180deg, transparent, #000 8%, #000 92%, transparent);
}
.gwall__col { height: max-content; }
.gwall__col--up { animation: gwall-scroll 52s linear infinite; }
/* 第二條反向：reverse 讓它從 -50% 往 0 走，視覺上就是往下滾 */
.gwall__col--down { animation: gwall-scroll 64s linear infinite reverse; }
@keyframes gwall-scroll {
  to { transform: translateY(-50%); }
}
.gwall__viewport:hover .gwall__col { animation-play-state: paused; }

.gwall__item {
  margin: 0 0 1rem;
  background: #fff;
  border-radius: 8px;
  overflow: hidden;
  box-shadow: 0 1px 2px rgba(10, 10, 10, 0.06), 0 10px 28px rgba(10, 10, 10, 0.07);
}
.gwall__item--quote { padding: 1rem 1.1rem; display: grid; gap: 0.45rem; }
.gwall__stars {
  margin: 0;
  font-size: var(--step-xs);
  letter-spacing: 0.2em;
  color: #f0b400; /* 星等的通用琥珀黃；--neon 在白卡上會糊掉 */
}
.gwall__body { margin: 0; font-size: var(--step-sm); line-height: 1.7; }
.gwall__author { font-size: var(--step-xs); color: var(--grey-500); }
.gwall__img, .gwall__video { display: block; width: 100%; height: auto; }

/* 自動動畫超過 5 秒，WCAG 2.2.2 要求可暫停；:hover 只服務滑鼠，
   所以比照 voices 做一顆鍵盤／觸控都能操作的核取方塊。視覺隱藏手法
   相同：縮到 1px 再裁掉溢出，保留 focus 能力。 */
.gwall__pause-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.gwall__pause-label {
  position: absolute;
  right: 0.5rem;
  bottom: 0.25rem;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.35rem 0.85rem;
  font-size: var(--step-xs);
  color: var(--ink);
  background: var(--paper);
  border: 1px solid rgba(26, 26, 26, 0.25);
  border-radius: 999px;
  cursor: pointer;
  user-select: none;
}
.gwall__pause-input:focus-visible + .gwall__pause-label {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}
.gwall__pause-label .gwall__pause-resume { display: none; }
.gwall__pause-input:checked ~ .gwall__pause-label .gwall__pause-resume { display: inline; }
.gwall__pause-input:checked ~ .gwall__pause-label .gwall__pause-halt { display: none; }
.gwall__pause-input:checked ~ .gwall__viewport .gwall__col { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .gwall__col { animation: none; }
}

@media (max-width: 899px) {
  .guide__case { padding: 1rem; }
  /* 三張卡在手機一個螢幕高塞不下，場景描述和標題內容重複，改隱藏它保留證言。
     證言是這張投影片的核心內容，刪掉它會失去整張投影片的意義。 */
  .guide__case .guide__scenario { display: none; }
  /* 牆整面收掉：第 5 張 voices 已有跑馬燈，這張在手機維持緊湊單欄 */
  .gwall { display: none; }
}

/* ---- 第 6 張：線下據點 ---- */
.offline__title { font-family: var(--font-en); font-size: var(--step-xl); max-width: 16ch; margin: 0; }
.offline__head { display: grid; gap: 0.5rem; }
.offline__events { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.75rem; }
.offline__event {
  display: grid;
  /* 第四欄是狀態標記（已結束／COMING SOON）。0903 之前只有三欄，多塞一個
     元素會被擠到下一列去，所以欄數要跟著樣板一起改。沒有標記的場次留空格，
     grid 自己會處理。 */
  grid-template-columns: minmax(0, 10rem) minmax(0, 1fr) minmax(0, 8rem) minmax(0, 8rem);
  gap: 1rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid rgba(245, 245, 243, 0.2);
  font-size: var(--step-sm);
}
.offline__event[data-status="past"] { opacity: 0.5; }
.offline__tag {
  font-family: var(--font-en);
  font-size: var(--step-xs);
  letter-spacing: 0.14em;
  color: var(--grey-500);
  justify-self: start;
}
/* 跟 /stores 的 .timeline__tag--soon 同一個訊號：這場還沒開始，記日期。 */
.offline__tag--soon { color: var(--neon); }
.offline__date { font-family: var(--font-en); letter-spacing: 0.1em; }
.offline__soon {
  font-family: var(--font-en);
  font-size: var(--step-sm);
  letter-spacing: 0.2em;
  color: var(--neon);
  margin: 0;
}

@media (max-width: 899px) {
  .offline__event { grid-template-columns: minmax(0, 1fr); gap: 0.2rem; }
  .offline__tag { margin-top: 0.15rem; }
}

/* ---- 進場錯位 ----
   只在「JS 可用」且「deck.js 真的成功初始化過一次」(.is-ready，見 deck.js) 才隱藏，
   否則只用 html.js 判斷的話，deck.js 404 或拋錯時 .is-ready 永遠不會加上去，
   內容卻已經被隱藏，首頁會直接變空白。.is-ready 由 deck.js 在 setActive
   第一次跑完之後才加，所以隱藏規則一定伴隨著「已經有一張被標記 is-active」。 */
@media (prefers-reduced-motion: no-preference) {
  html.js .deck.is-ready .slide [data-stagger] {
    opacity: 0;
    transform: translateY(28px);
    transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
  }
  html.js .deck.is-ready .slide.is-active [data-stagger] {
    opacity: 1;
    transform: none;
  }

  /* Hero 底圖緩推 */
  html.js .deck.is-ready .slide--hero .slide__media {
    transform: scale(1.06);
    transition: transform 1.6s var(--ease);
  }
  html.js .deck.is-ready .slide--hero.is-active .slide__media { transform: scale(1); }
}

/* 商品卡在 deck 裡由上面的 is-active 機制負責進場，
   關掉 reveal.js 那套避免兩邊打架（reveal.js 仍供其他分頁使用）。 */
html.js .deck .reveal { opacity: 1; transform: none; }

/* ---- 頁碼指示器 ---- */
.deck-nav {
  position: fixed;
  right: clamp(0.75rem, 2vw, 2rem);
  top: 50%;
  transform: translateY(-50%);
  z-index: 90;
  display: grid;
  gap: 0.9rem;
  mix-blend-mode: difference;
  color: #fff;
}

.deck-nav__item {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.6rem;
  background: none;
  border: 0;
  padding: 0.25rem 0;
  color: inherit;
  cursor: pointer;
}

.deck-nav__num {
  font-family: var(--font-en);
  font-size: var(--step-xs);
  letter-spacing: 0.16em;
  opacity: 0.45;
  transition: opacity 0.4s var(--ease), color 0.4s var(--ease);
}

.deck-nav__line {
  display: block;
  width: 14px;
  height: 1px;
  background: currentColor;
  opacity: 0.45;
  transition: width 0.4s var(--ease), opacity 0.4s var(--ease), background 0.4s var(--ease);
}

.deck-nav__item.is-active .deck-nav__num { opacity: 1; color: var(--neon); }
.deck-nav__item.is-active .deck-nav__line { width: 32px; opacity: 1; background: var(--neon); }
.deck-nav__item:hover .deck-nav__num,
.deck-nav__item:hover .deck-nav__line { opacity: 1; }

/* 手機縮成小圓點 */
@media (max-width: 899px) {
  .deck-nav { gap: 0.65rem; }
  .deck-nav__num { display: none; }
  .deck-nav__line {
    width: 6px;
    height: 6px;
    border-radius: 50%;
  }
  .deck-nav__item.is-active .deck-nav__line { width: 6px; }
}

/* ---- 矮視窗退場 ----
   橫向手機與小筆電塞不下 100svh 的中文長句，直接退回自然高度捲動。
   deck.js 的 deckMode 用同一個 640px 界線，這裡降級成長捲頁時，
   那邊也會把滾輪／鍵盤還給瀏覽器，兩邊必須一起改。 */
@media (max-height: 640px) {
  .deck {
    height: auto;
    overflow: visible;
    scroll-snap-type: none;
  }
  .slide {
    min-height: 0;
    padding-block: var(--section-y);
  }
  .slide--hero { min-height: 100svh; padding-block: 0; }
  .slide--b2b { padding-block: 0; }
  .deck-nav { display: none; }

  /* C2 保底：矮視窗下 .deck 變成 overflow:visible（見上方），IntersectionObserver
     若把 root 設成 .deck，會因為沒有裁切矩形而拿整個文件當 root，七張全部永遠
     intersecting，最後一次 callback 會把 is-active 定在 b2b，其餘六張的
     [data-stagger] 就永久 opacity:0。deck.js 已經把 root 改成 null 修掉成因，
     這裡再加一層保險，強制矮視窗一律可見，選擇器特異度刻意跟上面的隱藏規則
     一致（都帶 .deck.is-ready），這樣不管 source order 或未來調整順序，
     這條「強制顯示」規則都保證贏過隱藏規則。 */
  html.js .deck.is-ready .slide [data-stagger] {
    opacity: 1;
    transform: none;
  }
}

/* ---- 第 5 張：好評牆跑馬燈 ----
   絕對不能用 overflow-x: auto 做橫向捲動。README 記載過這個坑：Chrome 只要
   看到游標下方有能橫捲的容器，就把垂直滾輪量拿去捲它，上層 preventDefault
   擋不乾淨。這裡 viewport 是 overflow: hidden，位移全靠 transform 動畫，
   使用者的滾輪永遠是換投影片。 */
.slide--voices { background: var(--ink); color: var(--paper); }
.slide--voices .slide__inner { gap: 2rem; padding-inline: 0; }
.voices__head { display: grid; gap: 0.5rem; padding-inline: var(--gutter); }
.voices__title { font-family: var(--font-zh); font-size: var(--step-lg); margin: 0; }
.voices__lead { color: var(--grey-500); margin: 0; }
.slide--voices .slide__inner > p:last-child { padding-inline: var(--gutter); }

/* 跑馬燈自動播放超過 5 秒，WCAG 2.2.2 要求可暫停；:hover 只服務滑鼠，
   所以另外做一顆鍵盤／觸控都能操作的核取方塊開關。視覺上隱藏但不能用
   display: none，否則會從 tab 順序消失，改用「縮到 1px 又裁掉溢出」的
   慣用手法，保留 focus 能力。 */
.voices__pause-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.voices__pause-label {
  justify-self: start;
  margin-inline-start: var(--gutter);
  padding: 0.5rem 1rem;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: var(--step-xs);
  color: var(--paper);
  background: var(--black);
  border: 1px solid var(--grey-700);
  border-radius: 999px;
  cursor: pointer;
  user-select: none;
}
/* label 本身不能 focus，能 focus 的是它 for 到的 input；outline 要畫在
   看得見的 label 上，所以用相鄰選擇器借 input 的 :focus-visible 狀態
   去畫緊接在後面的 label 外框。 */
.voices__pause-input:focus-visible + .voices__pause-label {
  outline: 2px solid var(--neon);
  outline-offset: 2px;
}
/* 預設（未勾選＝播放中）只顯示「暫停輪播」；勾選（＝已暫停）換成「繼續播放」。 */
.voices__pause-label .voices__pause-resume { display: none; }
.voices__pause-input:checked ~ .voices__pause-label .voices__pause-resume { display: inline; }
.voices__pause-input:checked ~ .voices__pause-label .voices__pause-halt { display: none; }

.voices__viewport {
  overflow: hidden;
  width: 100%;
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.voices__track {
  display: flex;
  width: max-content;
  animation: voices-marquee 48s linear infinite;
}
.voices__viewport:hover .voices__track { animation-play-state: paused; }
/* 純 CSS 暫停開關：勾選後同樣把 animation-play-state 設成 paused，
   鍵盤與觸控使用者不必依賴滑鼠 hover 或作業系統的 reduced-motion 設定。 */
.voices__pause-input:checked ~ .voices__viewport .voices__track { animation-play-state: paused; }

.voices__item {
  width: 22rem;
  max-width: 70vw;
  display: grid;
  gap: 0.75rem;
  align-content: start;
  margin: 0;
  /* 間距刻意放在項目自己身上（而不是 track 的 gap），這樣「兩份複本」
     的每一份寬度都完全相等，-50% 的位移才會剛好落在第二份複本的起點，
     不會因為 flex gap 在最後一項後面少算一份間距而多跑半格。 */
  margin-right: 1rem;
  padding: 1.5rem;
  background: var(--black);
  border: 1px solid var(--grey-700);
  border-radius: 4px;
}
.voices__body { font-size: var(--step-sm); line-height: 1.9; margin: 0; }
.voices__author { font-size: var(--step-xs); color: var(--grey-500); }

@keyframes voices-marquee {
  from { transform: translateX(0); }
  /* 每個項目寬度一致（間距算在項目自己身上，不是 track 的 gap），所以
     半個 track 剛好是一份完整複本，-50% 準確接回開頭，不會有跳格。 */
  to { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .voices__track { animation: none; }
}
