/* ╔══════════════════════════════════════════════════════════════════╗
   ║  GENERATED FILE — DO NOT EDIT.                                     ║
   ║  Built by tools/build-css.mjs from css/main.css.                   ║
   ║  Edit css/_base.css or css/components/*.css, then re-run:          ║
   ║      node tools/build-css.mjs                                      ║
   ╚══════════════════════════════════════════════════════════════════╝ */

/* main.css — generated manifest. Build: node tools/build-css.mjs
   @import order == cascade order. Numeric prefix == cascade position.
   Edit a component file or add a new @import here, then rebuild. */

/* ── sections / components (cascade-ordered) ── */
/* Afacad — score-orb numerals only (see .ztor-score__value). Imported
   here so all 16 pages get it through the one shared stylesheet. */
@import url('https://fonts.googleapis.com/css2?family=Afacad:wght@600;700&display=swap');

/* Focus ring — brand orange site-wide. Replaces the browser default
   (blue) for any element that doesn't define its own :focus-visible. */
:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 2px; }
/* 觸控裝置 tap 也會標 :focus-visible → 按過的鈕殘留橘框（mtd-bar／短劇／播放器 ⚙
   三度個案修）→ 治本：focus ring 是給實體鍵盤的，觸控環境整站關（Dolly 0727） */
@media (hover: none) and (pointer: coarse) {
  :focus-visible { outline: none; }
}
/* 桌機滑鼠同理（Dolly 0803「一勞永逸」）：滑鼠點擊後 JS 搬焦點（開彈窗聚焦
   關閉鈕等）也會亮環——ds.js 開頭把最後輸入形態寫到 <html data-input>，
   pointer 模式整站關環；按 Tab／方向鍵切回 keyboard，環立刻恢復。
   !important 是刻意的：這條是站規，要壓過所有元件自訂的 :focus-visible
   橘環（77 條），否則又回到逐顆豁免的打地鼠。 */
html[data-input="pointer"] :focus-visible { outline: none !important; }

/* 未來功能閘（0803 Dolly）：確定會回來、但現階段不上的區塊掛這個屬性——
   markup 與資料填充都留在原位，恢復＝把屬性拿掉即可，不用挖 git 歷史。
   !important 是必要的：[hidden] 會被元件自己的 display（inline-flex 等）
   蓋掉（feedback_hidden_attr_display_override），這條必須無條件贏。
   目前使用者：創作者頁的 活動/排行榜 tab、頭號粉絲、關於。 */
[data-future-feature] { display: none !important; }

/* ============================================================
   Ztor 2.0 — Components
   Mirrors Figma component sets:
   - Button (Type × Size × State)
   - IconButton (State + Icon swap)
   - Header (State=Before Login | After Login)
   ============================================================ */

/* ─────────────────────────────────────────
   BUTTON
   variants: primary | ghost | yellow-ghost | destructive
   sizes:    lg | md | sm
   states:   default | :hover | :active | :disabled
   ───────────────────────────────────────── */

/* Raised-pill effect for solid brand buttons — SINGLE SOURCE.
   Used by .btn--primary.btn--lg AND the header login button
   (.header__login-btn). Change here → both stay in sync. */
:root {
  --shadow-btn-primary-raised:
    rgba(255, 255, 255, 0.85) 0 1.4px 0.5px -1px inset,                            /* top light edge */
    color-mix(in srgb, var(--brand-900) 18%, transparent) 0 -3px 4px -2px inset,   /* warm underside */
    color-mix(in srgb, var(--brand-ink) 30%, transparent) 0 2px 5px -2px,          /* contact shadow */
    color-mix(in srgb, var(--brand-ink) 15%, transparent) 0 6px 14px -6px;         /* soft lift */
}

.btn {
  position: relative;
  isolation: isolate;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  cursor: pointer;
  white-space: nowrap;
  transition: box-shadow 150ms var(--ease-out),
              background 150ms var(--ease-out),
              border-color 150ms var(--ease-out),
              color 150ms var(--ease-out),
              transform 120ms var(--ease-out);
}

/* Physical press — the button meets the finger */
.btn:active:not(:disabled) { transform: scale(0.97); }

.btn:disabled { cursor: not-allowed; }
.btn:focus-visible {
  outline: 2px solid var(--yellow-500);
  outline-offset: 2px;
}

/* ── Sizes ── */
.btn--lg {
  padding: 16px 36px;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h5);
  line-height: var(--lh-body);
  font-weight: var(--fw-bold);
}

.btn--md {
  padding: 12px 28px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  font-weight: var(--fw-medium);
}

.btn--sm {
  padding: 8px 16px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-weight: var(--fw-medium);
}

/* ── PRIMARY ──
   Solid brand fill. LG reads as a gently RAISED pill: a crisp top light
   edge + a warm underside shadow + a soft warm lift off the dark canvas.
   The shadow is brand-derived (color-mix on --brand-ink / --brand-900) so it
   stays a warm tone — not a muddy black smudge on the dark bg — and recolours
   with the brand automatically. Hover lightens the fill; no outer halo.
   Pressed sinks in.
*/
.btn--primary {
  background: var(--brand-500);
  color: var(--brand-ink);
  border-color: transparent;
}

.btn--primary.btn--lg {
  box-shadow: var(--shadow-btn-primary-raised);
}

.btn--primary:hover:not(:disabled) {
  background: var(--brand-400);
}

.btn--primary:active:not(:disabled) {
  background: var(--brand-600);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.18);
}

.btn--primary:disabled { opacity: 0.4; }

/* ── MONO（單色主要行動）──
   白底近黑字的主 CTA。用在「畫面本身已經有很多品牌橘」的陳列情境——商城商品頁
   就是這樣：限量徽章、價格、尺寸指南、願望清單都是橘的，主鈕再用橘就沒有主次。
   ⚠️ 這不是「另一種 primary」，是**取代** primary：兩個別一起掛。
   0802 Dolly 拍板收編。原本這個外觀寫在 63-shop-detail.css 的 `.pdp .pdp-buy__add`
   頁面前綴覆寫裡（自稱 LEVER 2），有兩個問題：①掛 .btn--primary 卻不是 primary
   的樣子，同一個 class 在 .pdp 內外長不一樣；②只蓋了底色沒關掉 .btn--primary.btn--lg
   的 --shadow-btn-primary-raised——那道內光暈是為橘底調的，疊在白底上是殘留。 */
.btn--mono {
  background: var(--text-primary);
  color: var(--bg-primary);
  border-color: transparent;
  box-shadow: none;        /* 橘底專用的 raised 光暈不跟過來 */
  transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out);
}
.btn--mono:hover:not(:disabled) { opacity: 0.85; transform: translateY(-1px); }
.btn--mono:active:not(:disabled) { opacity: 0.95; transform: translateY(0); }
/* disabled 只改底色／文字；透明度與 cursor 走全站統一的 .btn[disabled]（0.45）。
   舊的 .pdp 覆寫在這裡寫死 opacity:0.5，其實被 .btn[disabled] 同權重後蓋掉，是死宣告。 */
.btn--mono:disabled {
  background: var(--text-disabled);
  color: var(--bg-primary);
  transform: none;
}
@media (prefers-reduced-motion: reduce) {
  .btn--mono { transition: opacity 150ms ease; }
  .btn--mono:hover:not(:disabled) { transform: none; }
}

/* ── GHOST (Artlist-style glass) ──
   background = bottom-edge bright stripe gradient
   box-shadow = top + bottom edge highlights + inner soft shadow
   text-shadow = subtle text glow
*/
.btn--ghost {
  background: linear-gradient(0deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.18) 2.45%,
    rgba(255, 255, 255, 0) 126.14%);
  border-color: transparent;
  color: var(--text-primary);
  text-shadow: none;
  box-shadow:
    rgba(255, 255, 255, 0.9) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(255, 255, 255, 0.9) -1px -2.2px 0.5px -1.8px inset,
    rgba(0, 0, 0, 0.10) 2px 3px 2px 0 inset;
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}

.btn--ghost:hover:not(:disabled) {
  background: linear-gradient(0deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.30) 2.45%,
    rgba(255, 255, 255, 0) 126.14%);
  text-shadow: rgba(255, 255, 255, 0.45) 0 0 10px;
}

.btn--ghost:active:not(:disabled) {
  background: linear-gradient(0deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.42) 2.45%,
    rgba(255, 255, 255, 0) 126.14%);
  box-shadow:
    rgba(255, 255, 255, 0.6) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(255, 255, 255, 0.6) -1px -2.2px 0.5px -1.8px inset,
    rgba(0, 0, 0, 0.20) 2px 3px 4px 0 inset;
}

.btn--ghost:disabled {
  background: rgba(255, 255, 255, 0.04);
  color: var(--text-disabled);
  text-shadow: none;
  box-shadow:
    rgba(255, 255, 255, 0.25) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(255, 255, 255, 0.25) -1px -2.2px 0.5px -1.8px inset;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* ── YELLOW-GHOST ── */
.btn--yellow-ghost {
  background: linear-gradient(0deg,
    rgba(var(--brand-rgb), 0) 0%,
    rgba(var(--brand-rgb), 0.18) 2.45%,
    rgba(var(--brand-rgb), 0) 126.14%);
  border-color: transparent;
  color: var(--yellow-500);
  text-shadow: none;
  box-shadow:
    rgba(var(--brand-rgb), 0.9) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(var(--brand-rgb), 0.9) -1px -2.2px 0.5px -1.8px inset,
    rgba(0, 0, 0, 0.10) 2px 3px 2px 0 inset;
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}

.btn--yellow-ghost:hover:not(:disabled) {
  background: linear-gradient(0deg,
    rgba(var(--brand-rgb), 0) 0%,
    rgba(var(--brand-rgb), 0.30) 2.45%,
    rgba(var(--brand-rgb), 0) 126.14%);
  text-shadow: rgba(var(--brand-rgb), 0.4) 0 0 10px;
}

.btn--yellow-ghost:active:not(:disabled) {
  background: linear-gradient(0deg,
    rgba(var(--brand-rgb), 0) 0%,
    rgba(var(--brand-rgb), 0.42) 2.45%,
    rgba(var(--brand-rgb), 0) 126.14%);
  box-shadow:
    rgba(var(--brand-rgb), 0.6) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(var(--brand-rgb), 0.6) -1px -2.2px 0.5px -1.8px inset,
    rgba(0, 0, 0, 0.20) 2px 3px 4px 0 inset;
}

.btn--yellow-ghost:disabled {
  background: rgba(var(--brand-rgb), 0.04);
  color: rgba(var(--brand-rgb), 0.4);
  text-shadow: none;
  box-shadow:
    rgba(var(--brand-rgb), 0.3) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(var(--brand-rgb), 0.3) -1px -2.2px 0.5px -1.8px inset;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* ── DESTRUCTIVE ──
   default/hover: red glass（半透紅玻璃）
   pressed: solid red + ink text (state escalation)
   2026-07-14 Dolly 定版：原版只有底部漸層太淡（modal 深底上讀不到紅）→
   疊恆定 12% 紅底＋漸層峰值 20%→30%；中途試過實色紅底白字，否決、
   回到這版「略帶透明感」。disabled 較原版顯色（12% 底＋55% 紅字）。 */
.btn--destructive {
  background:
    linear-gradient(0deg,
      rgba(239, 68, 68, 0) 0%,
      rgba(239, 68, 68, 0.30) 2.45%,
      rgba(239, 68, 68, 0) 126.14%),
    rgba(239, 68, 68, 0.12);
  border-color: transparent;
  color: var(--error-500);
  text-shadow: none;
  box-shadow:
    rgba(239, 68, 68, 0.9) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(239, 68, 68, 0.9) -1px -2.2px 0.5px -1.8px inset,
    rgba(0, 0, 0, 0.10) 2px 3px 2px 0 inset;
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}

.btn--destructive:hover:not(:disabled) {
  background:
    linear-gradient(0deg,
      rgba(239, 68, 68, 0) 0%,
      rgba(239, 68, 68, 0.44) 2.45%,
      rgba(239, 68, 68, 0) 126.14%),
    rgba(239, 68, 68, 0.18);
  text-shadow: rgba(239, 68, 68, 0.4) 0 0 10px;
}

.btn--destructive:active:not(:disabled) {
  background: var(--error-500);
  color: var(--error-ink);
  text-shadow: none;
  box-shadow:
    rgba(255, 255, 255, 0.5) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(0, 0, 0, 0.3) 0 4px 12px;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.btn--destructive:disabled {
  /* 2026-07-15 Dolly：原 .12/.55/.35 整體太淡，各層透明度調高一級；
     仍以「無漸層、無 glow」跟 enabled 態區隔 */
  background: rgba(239, 68, 68, 0.18);
  color: rgba(239, 68, 68, 0.75);
  text-shadow: none;
  box-shadow:
    rgba(239, 68, 68, 0.5) 1.1px 2.2px 0.5px -1.8px inset,
    rgba(239, 68, 68, 0.5) -1px -2.2px 0.5px -1.8px inset;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* ─── Quiet — glassmorphic low-key button.
       Use for cancel / dismiss / tertiary actions inside modals,
       dialogs, banners, or any surface where the action shouldn't
       compete with the primary CTA.

       Composition (3 properties together = the "glass" feel):
       - Translucent black fill (10/16/22/5%) — depth without opacity
       - Translucent white edge (24/32/32/10%) — defines the shape
       - 4px backdrop-blur — frosts the layer behind it

       Text shifts secondary→primary on hover/active so users feel
       the interaction even though the surface change is subtle. */
.btn--quiet {
  background: rgba(0, 0, 0, 0.10);
  border-color: rgba(255, 255, 255, 0.24);
  color: var(--text-secondary);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}

.btn--quiet:hover:not(:disabled) {
  background: rgba(0, 0, 0, 0.16);
  border-color: rgba(255, 255, 255, 0.32);
  color: var(--text-primary);
}

.btn--quiet:active:not(:disabled) {
  background: rgba(0, 0, 0, 0.22);
  border-color: rgba(255, 255, 255, 0.32);
  color: var(--text-primary);
}

.btn--quiet:disabled {
  background: rgba(0, 0, 0, 0.05);
  border-color: rgba(255, 255, 255, 0.10);
  color: var(--text-disabled);
  cursor: not-allowed;
}

/* ─────────────────────────────────────────
   ICON BUTTON
   40×40 glass pill, swap-in icon child
   ───────────────────────────────────────── */

.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  padding: 8px;
  border-radius: var(--radius-pill);
  background: var(--glass-bg-default);
  border: 1px solid var(--glass-stroke-default);
  color: var(--text-primary);
  cursor: pointer;
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  transition: background 150ms var(--ease-out),
              border-color 150ms var(--ease-out),
              transform 120ms var(--ease-out);
}

.icon-btn:hover:not(:disabled) {
  background: var(--glass-bg-hover);
  border-color: var(--glass-stroke-hover);
}

.icon-btn:active:not(:disabled) {
  background: var(--glass-bg-pressed);
  border-color: var(--glass-stroke-pressed);
  transform: scale(0.95);
}

.icon-btn:disabled {
  background: var(--glass-bg-disabled);
  border-color: var(--glass-stroke-disabled);
  color: var(--text-disabled);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  cursor: not-allowed;
}

.icon-btn svg {
  width: 24px;
  height: 24px;
  stroke: currentColor;
  fill: none;
}

/* ─────────────────────────────────────────
   FAB — floating action button
   Circular ghost-glass action anchored to the
   viewport corner (e.g. 發表貼文 on 社群).
   Reuses the .btn--ghost glass treatment but is
   a perfect circle with a centred icon, lifted
   off the page with a soft drop-shadow.
   Markup: <button class="fab btn btn--ghost">
   ───────────────────────────────────────── */
.fab {
  position: fixed;
  right: 32px;
  bottom: 32px;
  z-index: 400;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 60px;
  height: 60px;
  padding: 0;
  border-radius: var(--radius-circle, 50%);
  /* drop-shadow (not box-shadow) so it composes with the ghost
     edge-highlight box-shadow instead of overriding it. */
  filter: drop-shadow(0 6px 20px rgba(0, 0, 0, 0.45));
  transition: filter 150ms ease, transform 150ms ease,
              background 150ms ease, box-shadow 150ms ease;
}

.fab:hover:not(:disabled) {
  transform: translateY(-2px);
  filter: drop-shadow(0 10px 26px rgba(0, 0, 0, 0.5));
}

.fab:active:not(:disabled) {
  transform: translateY(0);
  filter: drop-shadow(0 4px 14px rgba(0, 0, 0, 0.45));
}

.fab__icon {
  width: 26px;
  height: 26px;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}

/* ─────────────────────────────────────────
   AI MOSAIC
   AI 原力創作計畫 section: 2-col layout — copy + CTA on left, mosaic on right.
   Mosaic = 1 big tile (top, full row) + 4 small tiles (2×2 below).
   ───────────────────────────────────────── */

.ai-mosaic {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 80px;
  align-items: center;
}

.ai-mosaic__copy {
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.ai-mosaic__ctas {
  display: flex;
  gap: 12px;
  margin-top: 12px;
  flex-wrap: wrap;
}

.ai-mosaic__grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
}

/* Each tile is 16:9 (matches 1.0 image spec 1056×594).
   Big tile spans both columns; small tiles fill 1 col each. */
.ai-mosaic__tile {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
  overflow: hidden;
  background: var(--bg-tertiary);
  text-decoration: none;
  color: inherit;
  transition: transform 220ms var(--ease-out), border-color 220ms var(--ease-out);
}

.ai-mosaic__tile:hover {
  transform: scale(1.02);
  border-color: var(--border-default);
}

.ai-mosaic__tile-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 0;
}

/* Same frosted "text on photo" treatment as .media-glass, fitted to the tile:
   a progressive backdrop-blur + ink tint, masked to fade up off the image so
   the status badge + title stay legible over ANY photo. (This ::before already
   paints above the img, so backdrop-filter blurs the photo behind it.) */
.ai-mosaic__tile::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 1;
  -webkit-backdrop-filter: blur(22px) saturate(1.35);
          backdrop-filter: blur(22px) saturate(1.35);
  background: linear-gradient(to bottom, transparent 0%, rgba(10, 10, 10, 0.30) 55%, rgba(10, 10, 10, 0.62) 100%);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.5) 52%, #000 76%);
          mask-image: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.5) 52%, #000 76%);
  pointer-events: none;
}
/* No backdrop-filter (old engines): a stronger tint carries legibility. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .ai-mosaic__tile::before {
    background: linear-gradient(to bottom, transparent 0%, rgba(10, 10, 10, 0.62) 40%, rgba(10, 10, 10, 0.88) 100%);
  }
}

.ai-mosaic__tile--big {
  grid-column: 1 / -1;
  border-radius: var(--radius-lg);
}

.ai-mosaic__tile-meta {
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: 14px;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
}

.ai-mosaic__tile-status {
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}

.ai-mosaic__tile-title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 14px;
  line-height: 1.3;
  color: var(--text-primary);
  letter-spacing: -0.005em;
  text-wrap: balance;
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.4);
}

.ai-mosaic__tile--big .ai-mosaic__tile-title {
  font-size: 22px;
  letter-spacing: -0.01em;
}

/* AI campaign row — standard section layout (header on top + one row of cards),
   matching the other homepage sections. Reuses .ai-mosaic__tile visuals but
   lays all campaigns out as equal 16:9 cards in a single row. */
.ai-campaign-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}
/* Larger cards in a 3-up row → bump the overlaid title for legibility. */
.ai-campaign-row .ai-mosaic__tile-title { font-size: 17px; }
@media (max-width: 900px) { .ai-campaign-row { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 600px) { .ai-campaign-row { grid-template-columns: 1fr; } }

/* ─────────────────────────────────────────
   CLASSICS — 片單推薦「忘不了的經典」
   4-up quote cards (single composed image ported from V1) + title below.
   Card image ratio ≈ 0.91:1 (1199×1312 source).
   ───────────────────────────────────────── */
.classics-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
}
.classic-card {
  display: flex;
  flex-direction: column;
  gap: 14px;
  text-decoration: none;
  color: inherit;
}
.classic-card__img {
  width: 100%;
  aspect-ratio: 1199 / 1312;
  object-fit: cover;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle);
  display: block;
  transition: transform 250ms ease;
}
.classic-card:hover .classic-card__img { transform: translateY(-4px); }
.classic-card__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 18px;
  line-height: 1.3;
  color: var(--text-primary);
  margin: 0;
}
@media (max-width: 900px) { .classics-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .classics-grid { grid-template-columns: 1fr; } }

/* ─────────────────────────────────────────
   CO-CREATION CARD / GRID
   3-up cards for 影視共創計畫 section.
   Card structure: 16:9 hero image → badge → title → desc → metrics block
   ───────────────────────────────────────── */

/* ─────────────────────────────────────────
   HEADER
   Mirrors Figma node 485:1139 (State=Before Login | After Login)
   Structure:
     header
       header/nav   (gap 40)  → header/logo + header/nav-links (gap 36)
       header/actions (gap 24) → search-icon + lang-icon + buttons-cluster
   ───────────────────────────────────────── */

.header {
  position: sticky;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  height: var(--header-height);
  background: rgba(10, 10, 10, 0.72);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-subtle);
}

/* ── Artlist-style soft veil (ported from Dolly's sidebar build, 2026-06) ──
   has-rail pages drop the flat fill + hairline; a tint+blur layer extends
   24px below the bar, fades out via mask (no seam), and only fades in once
   scrolled — page-top stays fully crisp. Pinned-tabbar pages use a flat
   material matching the tabbar so bar + tabbar read as one shelf. */
body.has-rail .header {
  background: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  border-bottom: none;
}
body.has-rail .header::before {
  content: '';
  position: absolute;
  inset: 0 0 -24px 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg, rgba(23, 23, 23, 0.7) 0%, rgba(23, 23, 23, 0) 100%);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  -webkit-mask-image: linear-gradient(180deg,
    rgba(0,0,0,1) 0%, rgba(0,0,0,1) 66%,
    rgba(0,0,0,0.96) 74%, rgba(0,0,0,0.82) 81%,
    rgba(0,0,0,0.58) 88%, rgba(0,0,0,0.28) 94%, rgba(0,0,0,0) 100%);
          mask-image: linear-gradient(180deg,
    rgba(0,0,0,1) 0%, rgba(0,0,0,1) 66%,
    rgba(0,0,0,0.96) 74%, rgba(0,0,0,0.82) 81%,
    rgba(0,0,0,0.58) 88%, rgba(0,0,0,0.28) 94%, rgba(0,0,0,0) 100%);
}
/* Veil only appears once scrolled — at page top everything is crisp.
   Scroll-driven, no JS; browsers without scroll-timeline keep it on. */
@supports (animation-timeline: scroll()) {
  body.has-rail .header::before {
    animation: header-veil-in linear both;
    animation-timeline: scroll(root);
    animation-range: 0 120px;
  }
}
@keyframes header-veil-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* 2026-07-15 Dolly：我的片庫主 tab 列捲到 header 下被 veil 的 24px 下滲帶
   壓暗（「淡淡的遮罩」）——tabbar 頁一律用平面材質、不往下滲，加入 mylib */
body.has-rail:has(.community-tabbar) .header::before,
body.has-rail:has(.mylib-tabs-row) .header::before {
  inset: 0;
  background: rgba(10, 10, 10, 0.82);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  -webkit-mask-image: none;
          mask-image: none;
}

.header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;   /* hard floor between nav cluster and actions when space runs out */
  height: 100%;
  padding: 0 var(--container-padding-x);
}

.header__nav {
  display: flex;
  align-items: center;
  gap: clamp(20px, 2.8vw, 40px);   /* fluid: 40px at ≥1430, shrinks with viewport */
  min-width: 0;   /* allow the left cluster to shrink — see .header__nav-links note */
}

.header__logo {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  height: 30px;
}

.header__logo-img {
  height: 30px;
  width: auto;
  display: block;
}

.header__nav-links {
  display: flex;
  align-items: center;
  /* Fluid gap + fluid link font (below) keep all 7 links genuinely fitting
     down to ~800px wide, so the overflow rail is a last-resort safety net,
     not the normal state — a rail that clips mid-glyph reads as a bug. */
  gap: clamp(12px, 2.5vw, 36px);
  /* Responsive guard: without min-width:0 a flex child refuses to shrink
     below its content width, so at narrow viewports the nav plows into
     .header__actions (the two clusters overlap instead of compressing).
     With it, the nav shrinks and overflows into a swipeable rail. */
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: none;          /* Firefox */
}
.header__nav-links::-webkit-scrollbar { display: none; }

.header__nav-link {
  position: relative;
  padding: 8px 4px;
  font-family: var(--font-cjk-display);
  font-size: clamp(13px, 1.15vw, 16px);   /* 16px at ≥1390 → 13px floor on small screens */
  line-height: 24px;
  font-weight: var(--fw-regular);
  color: var(--neutral-400);
  white-space: nowrap;
  transition: color 200ms ease;
}

.header__nav-link:hover { color: var(--neutral-white); }

.header__nav-link.is-active {
  color: var(--neutral-white);
  /* Weight stays regular on purpose: bolding the active item changes its
     width, which shifts the neighbouring links and makes the nav "jump"
     when the active page changes. Active is signalled by colour + the
     glowing underline below instead. */
}

.header__nav-link.is-active::after {
  content: '';
  position: absolute;
  left: 4px;
  right: 4px;
  bottom: 0;
  height: 2px;
  border-radius: 1px;
  background: var(--yellow-500);
  box-shadow: 0 0 8px rgba(var(--brand-rgb), 0.55);
}

.header__actions {
  display: flex;
  align-items: center;
  gap: clamp(12px, 1.6vw, 24px);   /* fluid, matches nav cluster scaling */
  flex-shrink: 0;
}

/* Bare 24×24 icon button (search / language) — no glass pill
   Uses mask-image so the SVG (white-fill) can be recolored on hover.
   Set the icon via inline style: style="--icon: url('assets/icons/search.svg');" */
.header__icon-btn {
  display: inline-block;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  cursor: pointer;
  background-color: var(--neutral-300);
  -webkit-mask: var(--icon) center / 24px 24px no-repeat;
          mask: var(--icon) center / 24px 24px no-repeat;
  transition: background-color 200ms ease;
}

.header__icon-btn:hover { background-color: var(--neutral-white); }

/* Buttons cluster (right of icons) */
.header__buttons {
  display: flex;
  align-items: center;
  gap: 8px;
}

/* ── Header responsive condensation ──
   Gaps and nav type scale fluidly via clamp() (see rules above). The
   nav must FIT at every width ≥768 — the overflow rail clips mid-glyph
   and reads as a bug, so it stays a last-resort safety net only.
   ≤1200px: 計畫與價格 pill collapses to its popcorn icon (aria-label
            keeps it accessible), tighter login padding. The pill label
            is the single widest flexible item — collapsing it early is
            what buys the nav its room.
   ≤1024px: gap floors.
   ≤860px:  smallest nav type (12px).
   ≤768px:  nav links hidden entirely (MOBILE TWEAKS block at end of
            file) — desktop pages have mobile-*.html counterparts. */
@media (max-width: 1200px) {
  /* Doubled class selectors: the chevron's base rule is defined LATER in
     this file, so equal specificity would lose by source order. */
  .popcorn-section .popcorn-section__label,
  .popcorn-section .popcorn-section__chevron { display: none; }
  .header__login-btn { padding: 0 14px; }
}

@media (max-width: 1024px) {
  .header__nav { gap: 16px; }
  .header__nav-links { gap: 12px; }
  .header__actions { gap: 12px; }
  .header__inner { padding: 0 20px; }
}

@media (max-width: 900px) {
  .header__nav-link { font-size: 12px; }
  .header__nav-links {
    gap: 10px;
    /* Below ~830px the 7 CJK labels can't physically fit — the rail
       scrolls. The mask fades the cut edge so it reads as "more to
       scroll", never as a glyph sliced in half. padding-right keeps
       the last link clear of the fade when scrolled to the end. */
    padding-right: 28px;
    -webkit-mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
            mask-image: linear-gradient(90deg, #000 calc(100% - 28px), transparent);
  }
}

@media (max-width: 768px) {
  .header__inner { padding: 0 16px; }
}

.header__buttons--logged-in { gap: 12px; }

/* PopcornSection — Before Login (計畫與價格 pill, Artlist-style glass).
   Renders as <a> on real pages so the whole pill navigates to /popcorn. */
.popcorn-section {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 40px;
  padding: 0 16px;
  border: none;
  border-radius: var(--radius-pill);
  cursor: pointer;
  text-decoration: none;
  color: var(--neutral-white);
  /* L-kit neutral glass (was a copy of the old ghost recipe) */
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  backdrop-filter: var(--lg-frost);
  -webkit-backdrop-filter: var(--lg-frost);
  transition: background 150ms ease;
}

.popcorn-section:hover {
  background: linear-gradient(0deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.30) 2.45%,
    rgba(255, 255, 255, 0) 126.14%);
}

.popcorn-section:hover .popcorn-section__label {
  text-shadow: rgba(255, 255, 255, 1) 0 0 10px;
}

.popcorn-section__balance {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}

.popcorn-section__label {
  font-family: var(--font-cjk-display);
  font-size: 14px;
  line-height: 20px;
  font-weight: var(--fw-regular);
  color: var(--neutral-white);
  white-space: nowrap;
}

.popcorn-section__icon {
  width: 20px;     /* match L endgame (was 24) */
  height: 20px;
  display: block;
  flex-shrink: 0;
}

.popcorn-section__chevron {
  width: 18px;
  height: 18px;
  display: block;
  flex-shrink: 0;
}

/* PopcornSection — After Login (popcorn + balance + deposit) */
.popcorn-section--logged-in {
  gap: 6px;
  padding: 0 4px 0 16px;
}

.popcorn-section__amount {
  font-family: var(--font-latin);
  font-size: 14px;     /* match L endgame (was 16) */
  line-height: 1;      /* match L endgame (was 27px) */
  font-weight: var(--fw-medium);
  color: #eae9f7;
  text-align: right;
  min-width: 40px;
}
/* EN latin font (Satoshi) renders digits ~5% taller + ~10% wider than the ZH
   font (Proxima Nova) at the same size, so the balance number looked bigger in
   English. Nudge down 1px so it reads at the same size as the Chinese version.
   Scoped to body.has-rail (desktop) so it never overrides the mobile pages'
   own popcorn sizing (var(--m-fs-body-sm) / the .profile-menu__popcorn 16px). */
html[lang="en"] body.has-rail .popcorn-section__amount { font-size: 13px; }

.popcorn-section__deposit {
  width: 28px;     /* match L endgame (was 32) */
  height: 28px;
  border-radius: var(--radius-pill);
  background: var(--yellow-500);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  padding: 0;
  cursor: pointer;
  flex-shrink: 0;
  transition: filter 150ms ease;
}

.popcorn-section__deposit:hover { filter: brightness(1.04); }

/* plus.svg is white-fill → recolor to yellow-ink via mask on ::before */
.popcorn-section__deposit::before {
  content: '';
  display: block;
  width: 16px;
  height: 16px;
  background-color: var(--yellow-ink);
  -webkit-mask: url('assets/icons/plus.svg') center / contain no-repeat;
          mask: url('assets/icons/plus.svg') center / contain no-repeat;
}

/* btn-login-signup — yellow pill */
.header__login-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 40px;
  padding: 0 20px;
  background: var(--brand-500);
  color: var(--brand-ink);
  border: none;
  border-radius: var(--radius-pill);
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 20px;
  font-weight: var(--fw-medium);
  cursor: pointer;
  /* same raised-pill effect as .btn--primary.btn--lg (single source) */
  box-shadow: var(--shadow-btn-primary-raised);
  transition: background 120ms var(--ease-out),
              box-shadow 120ms var(--ease-out);
}

.header__login-btn:hover {
  background: var(--brand-400);
}

.header__login-btn:active {
  background: var(--brand-600);
  box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.18);
}

/* btn-notification (After Login) — plain 24px icon matching search / language:
   the bell is a recolorable mask (Neutral/300 → White on hover), the unread
   red-dot is a fixed ::after circle so it never recolors with the bell. The
   baked-in <img> is hidden — bell shape comes from notification_regular.svg. */
.header__notification-btn {
  position: relative;
  display: inline-block;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

.header__notification-btn img { display: none; }

/* Match the 語言↔通知 spacing (header__actions 24px); buttons group gap is 12px
   so add 12px to reach 24px before the popcorn pill — avatar gap stays 12px. */
.header__notification-btn { margin-right: 12px; }

.header__notification-btn::before {
  content: "";
  position: absolute;
  inset: 0;
  background-color: var(--neutral-300);
  /* 兩層 mask 相交:鈴鐺形狀 ∩「紅點周圍挖空圈」— 縫隙是真透明,
     不用深色 ring 模擬(透明 header 下 ring 會在亮背景現形)。*/
  -webkit-mask-image: url('assets/icons/notification_regular.svg'),
    radial-gradient(circle at calc(100% - 4px) 4px, transparent 6px, #000 6.5px);
  -webkit-mask-size: 24px 24px, 100% 100%;
  -webkit-mask-position: center, center;
  -webkit-mask-repeat: no-repeat;
  -webkit-mask-composite: source-in;
          mask-image: url('assets/icons/notification_regular.svg'),
    radial-gradient(circle at calc(100% - 4px) 4px, transparent 6px, #000 6.5px);
          mask-size: 24px 24px, 100% 100%;
          mask-position: center, center;
          mask-repeat: no-repeat;
          mask-composite: intersect;
  transition: background-color 200ms ease;
}
.header__notification-btn:hover::before { background-color: var(--neutral-white); }

/* Unread red dot — matches the SVG's circle (cx20 cy4 r4 in a 24 grid). The
   dark ring (header bg colour) recreates the gap the original baked-in icon had
   between the dot and the bell outline. */
.header__notification-btn::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: #EF4444;
}
/* 全部已讀後隱藏 bell 紅點 */
.header__notification-btn.is-read::after { display: none; }

/* Avatar (After Login) — 40×40 圓形容器，內含用戶頭照
   未上傳時 fallback 到 assets/icons/avatar.svg */
.avatar {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  border: none;
  padding: 0;
  background: none;
  cursor: pointer;
  flex-shrink: 0;
  overflow: hidden;
}

.avatar__img {
  width: 100%;
  height: 100%;
  display: block;
  object-fit: cover;
  border-radius: inherit;
}

/* ─────────────────────────────────────────
   HEADER DROPDOWNS — language picker + profile menu
   Shared glass shell, anchored under the trigger button.
   .header__popover-anchor wraps trigger + dropdown so the
   dropdown can be position:absolute relative to the trigger.
   ───────────────────────────────────────── */

.header__popover-anchor {
  position: relative;
  display: inline-flex;
  align-items: center;
}

.header-dropdown {
  /* position:fixed（非 absolute）：JS 一載入就把這顆選單 portal 到 <body> 尾端，
     absolute 會相對視窗、被 top:100% 推到頁面底下而撐出多餘捲動高度（短頁 footer
     下方憑空多出一段空白）。fixed 不計入頁面捲動高，也和 JS 打開時設的 fixed 一致。 */
  position: fixed;
  top: calc(100% + 16px);
  right: 0;
  background: rgba(19, 19, 19, 0.85);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-md);
  z-index: 200;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity 160ms ease, transform 160ms ease, visibility 160ms;
  /* Clip horizontally; scroll vertically when the menu is taller than the
     viewport. max-height is set inline by JS so it always fits below the
     trigger with a small bottom gap. */
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.07) transparent;
}

.header-dropdown::-webkit-scrollbar {
  width: 6px;
}
.header-dropdown::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.07);
  border-radius: 3px;
}
.header-dropdown::-webkit-scrollbar-thumb:hover {
  background: rgba(255, 255, 255, 0.14);
}
.header-dropdown::-webkit-scrollbar-track {
  background: transparent;
}

.header-dropdown.is-open {
  opacity: 1;
  visibility: visible;
  transform: translateY(0);
}

/* ─── Language picker (260 wide) ─── */
.header-dropdown--lang {
  width: 260px;
}

.lang-dropdown__item {
  display: flex;
  align-items: center;
  gap: 4px;
  width: 100%;
  padding: 16px 8px 16px 16px;
  background: transparent;
  border: none;
  font-family: var(--font-cjk-display);
  font-size: 16px;
  line-height: 24px;
  color: var(--text-secondary);
  text-align: left;
  cursor: pointer;
  transition: background-color 120ms ease, color 120ms ease;
}

.lang-dropdown__item:hover {
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-primary);
}

.lang-dropdown__item--active,
.lang-dropdown__item--active:hover {
  background: rgba(var(--brand-rgb), 0.24);
  color: var(--yellow-500);
}

.lang-dropdown__item-label {
  flex: 1;
  min-width: 0;
}

/* Active-language checkmark — pure CSS, driven by the --active state that i18n
   toggles on load AND on switch. Previously the tick was a hardcoded span living
   only in the zh-Hant button, moved by per-page click JS; on an English page
   load (no click) it never moved, so it stayed on 繁體中文. Rendering it here as
   an ::after on the active item makes it track the real language everywhere.
   The legacy .lang-dropdown__check span (still in markup + created by the page
   JS) is neutralised so it can't double up. */
.lang-dropdown__check { display: none; }

.lang-dropdown__item--active::after {
  content: '';
  width: 14px;
  height: 11px;
  flex-shrink: 0;
  background: currentColor;   /* inherits --yellow-500 from --active */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='11' viewBox='0 0 14 11'%3E%3Cpath d='M1 5.5L5 9.5L13 1.5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='11' viewBox='0 0 14 11'%3E%3Cpath d='M1 5.5L5 9.5L13 1.5' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* ─── Profile menu (375 wide) ─── */
.header-dropdown--profile {
  width: 375px;
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 24px;
  /* Stronger glass — the wider panel needs more blur to feel rich
     against busy backgrounds. */
  background: rgba(19, 19, 19, 0.72);
  backdrop-filter: blur(28px) saturate(1.4);
  -webkit-backdrop-filter: blur(28px) saturate(1.4);
}

/* Clickable area that links to the user's own profile page.
   Negative margin lets the hover bg extend slightly past the head's
   normal content edges so the affordance reads as a contained tile. */
.profile-menu__head-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px;
  margin: -8px;
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: background-color 150ms ease;
}

.profile-menu__head-row:hover {
  background: rgba(255, 255, 255, 0.05);
}

.profile-menu__head-row:hover .profile-menu__head-chevron {
  color: var(--text-primary);
  transform: translateX(2px);
}

.profile-menu__avatar {
  width: 64px;
  height: 64px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  flex-shrink: 0;
}

.profile-menu__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.profile-menu__info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.profile-menu__name {
  font-family: var(--font-cjk-text);
  font-weight: var(--fw-bold);
  font-size: 16px;
  line-height: 24px;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.profile-menu__role {
  font-family: var(--font-latin);
  font-size: 12px;
  line-height: 18px;
  color: var(--text-tertiary);
}

.profile-menu__head-chevron {
  width: 24px;
  height: 24px;
  color: var(--text-tertiary);
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 150ms ease, transform 150ms ease;
}

.profile-menu__actions {
  display: flex;
  gap: 16px;
  margin-top: 20px;        /* extra room so head-row hover bg doesn't crowd the pills */
}

/* Action pills here use .btn .btn--quiet .btn--sm; stretch to fill row. */
.profile-menu__actions .btn { flex: 1; }

.profile-menu__section {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.profile-menu__section-title {
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 25px;
  color: var(--text-tertiary);
}

.profile-menu__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}

.profile-menu__list-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 0;
  background: transparent;
  border: none;
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: 16px;
  line-height: 27px;
  text-align: left;
  width: 100%;
  cursor: pointer;
  transition: color 120ms ease;
}

.profile-menu__list-item:hover { color: var(--yellow-500); }
.profile-menu__list-item:hover .profile-menu__list-icon { color: var(--yellow-500); }

.profile-menu__list-item-inner {
  display: inline-flex;
  align-items: center;
  gap: 12px;
}

/* Icon glyph rendered via CSS mask so it inherits the parent's currentColor
   (so hover yellow flows from list-item color → icon bg). The SVG file path
   comes from the inline --icon CSS variable on each instance. */
.profile-menu__list-icon {
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  display: inline-block;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
  color: var(--text-primary);
  transition: background-color 120ms ease, color 120ms ease;
}

/* Smaller variant used inside .btn--quiet pills (e.g. 設定). */
.profile-menu__pill-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  display: inline-block;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}

.profile-menu__list-value {
  font-family: var(--font-latin);
  font-size: 14px;
  color: var(--text-tertiary);
  flex-shrink: 0;
}

.profile-menu__divider {
  height: 1px;
  background: rgba(255, 255, 255, 0.08);
  border: none;
  margin: 0;
}

.profile-menu__version {
  font-family: var(--font-latin);
  font-size: 14px;
  line-height: 16px;
  color: #707177;
}

/* ─────────────────────────────────────────
   觀看歷史 DRAWER (logged-in)
   Full-height right slide-over — geometry/timing unified with the cart
   drawer (.ztor-cart, 64-cart.css) 2026-07-28: fixed inset:0 shell, panel
   top:0/height:100% (was top:header-height + rounded-bottom-only), width
   min(440px,100%) to match .ztor-cart__panel exactly, head padding/close
   button sized off tokens, 360ms var(--ease-out) slide (was a bespoke
   320ms/cubic-bezier(.4,0,.2,1)). V1-style continue-watching list
   (poster + title + progress bar) is unchanged content.
   Trigger: header [data-watch-history-trigger]; footer 更多 → 我的片庫.
   ───────────────────────────────────────── */

/* Header trigger icon — white on hover like the notification bell. The
   logged-in buttons group gaps 12px; add 12px so watch↔通知 reaches the
   24px header__actions icon rhythm (mirrors .header__notification-btn). */
.header__icon-btn--watch { margin-right: 12px; }
.header__icon-btn--watch:hover { background-color: var(--neutral-white); }

.watch-drawer {
  position: fixed;
  inset: 0;
  z-index: 300;
  pointer-events: none;
  /* Clip the closed panel (translateX(100%)) — without this the parked
     drawer extends scrollWidth and the page gains phantom horizontal
     scroll on narrow windows. */
  overflow: hidden;
}
.watch-drawer.is-open { pointer-events: auto; }

/* Scrim covers the FULL viewport incl. header (matches .ztor-cart__overlay —
   the header used to stay clear when the panel only hugged content below it;
   now the panel is full-height so the scrim is too). */
.watch-drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 280ms var(--ease-out);
}
.watch-drawer.is-open .watch-drawer__scrim { opacity: 1; }

.watch-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  /* Width matches .ztor-cart__panel (64-cart.css) exactly — the three
     header drawers (cart/watch/notif) must be the same width. */
  width: min(440px, 100%);
  display: flex;
  flex-direction: column;
  background: rgba(19, 19, 19, 0.96);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  /* 0729 Dolly：左緣線統一跟 .ztor-cart__panel（64-cart.css）同 token */
  border-left: 1px solid var(--border-default);
  /* Shadow ONLY when open — the closed panel parks off-canvas (translateX(100%))
     but a -24px/60px shadow still bleeds leftward into the viewport's right edge,
     darkening whatever sits there (invisible on dark sections, a grey vignette on
     the yellow zebra stripes). Gating it on .is-open keeps the closed drawer
     shadow-free on every page. */
  box-shadow: none;
  transform: translateX(100%);
  /* Timing unified with .ztor-cart__panel (360ms / var(--ease-out)). */
  transition: transform 360ms var(--ease-out),
              box-shadow 360ms var(--ease-out);
}
.watch-drawer.is-open .watch-drawer__panel {
  transform: translateX(0);
  box-shadow: -24px 0 60px rgba(0, 0, 0, 0.5);
}

.watch-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  /* Padding + bottom hairline unified with .ztor-cart__head. */
  padding: var(--space-6) var(--space-6) var(--space-4);
  flex-shrink: 0;
  border-bottom: 1px solid var(--border-subtle);
}
.watch-drawer__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 22px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  margin: 0;
}
/* Circular close button — sized/coloured to match .ztor-cart__close. */
.watch-drawer__close {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  cursor: pointer;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  transition: background 200ms ease;
}
.watch-drawer__close:hover { background: rgba(255, 255, 255, 0.14); }
.watch-drawer__close-icon {
  width: 16px;
  height: 16px;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
          mask: url('assets/icons/close.svg') center / contain no-repeat;
}

.watch-drawer__list {
  flex: 0 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 4px 24px 8px;
  display: flex;
  flex-direction: column;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.07) transparent;
}
.watch-drawer__list::-webkit-scrollbar { width: 6px; }
.watch-drawer__list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.07); border-radius: var(--radius-pill); }

.watch-item {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 12px 0;
  text-decoration: none;
  color: inherit;
}
.watch-item__cover {
  flex: 0 0 auto;
  width: 52px;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-secondary);
}
.watch-item__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.watch-item__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.watch-item__title {
  font-family: var(--font-cjk-text);
  font-weight: var(--fw-semibold);
  font-size: 15px;
  line-height: 1.4;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: color 200ms ease;
}
.watch-item:hover .watch-item__title { color: var(--yellow-500); }
.watch-item__progress {
  display: flex;
  align-items: center;
  gap: 12px;
}
.watch-item__bar {
  flex: 1;
  height: 4px;
  background: rgba(255, 255, 255, 0.10);
  border-radius: var(--radius-pill);
  overflow: hidden;
}
.watch-item__bar-fill {
  display: block;
  height: 100%;
  /* 品牌黃 + 光暈，與募資卡進度條 (.cocreation-card__bar-fill) 一致 */
  background: var(--yellow-500);
  box-shadow: 0 0 8px rgba(var(--brand-rgb), 0.35);
  border-radius: var(--radius-pill);
}
.watch-item__pct {
  flex: 0 0 auto;
  min-width: 34px;
  text-align: right;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  color: var(--text-tertiary);
}

.watch-drawer__foot {
  flex-shrink: 0;
  padding: 16px 24px 24px;
}
/* Styling comes from .btn.btn--quiet.btn--md; only stretch to full width. */
.watch-drawer__more { width: 100%; }
.watch-drawer__more-arrow {
  width: 16px;
  height: 16px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/chevron_right.svg') center / 16px 16px no-repeat;
          mask: url('assets/icons/chevron_right.svg') center / 16px 16px no-repeat;
}

/* ─────────────────────────────────────────
   通知 DRAWER (logged-in)
   Full-height right slide-over — same shell as 觀看歷史, both unified with
   the cart drawer (.ztor-cart, 64-cart.css) 2026-07-28: fixed inset:0
   shell, panel top:0/height:100% (was top:header-height), width
   min(440px,100%) to match .ztor-cart__panel exactly, head padding/close
   button sized off tokens, 360ms var(--ease-out) slide. Feed still scrolls
   internally. Trigger: 通知 bell.
   ───────────────────────────────────────── */
.notif-drawer {
  position: fixed;
  inset: 0;
  z-index: 300;
  pointer-events: none;
}
.notif-drawer.is-open { pointer-events: auto; }

.notif-drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 280ms var(--ease-out);
}
.notif-drawer.is-open .notif-drawer__scrim { opacity: 1; }

.notif-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;                 /* full height, feed scrolls internally */
  /* Width matches .ztor-cart__panel (64-cart.css) exactly. */
  width: min(440px, 100%);
  display: flex;
  flex-direction: column;
  background: rgba(19, 19, 19, 0.96);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  /* 0729 Dolly：左緣線統一跟 .ztor-cart__panel（64-cart.css）同 token */
  border-left: 1px solid var(--border-default);
  /* Shadow ONLY when open — a closed off-canvas drawer must not bleed its
     leftward shadow into the viewport's right edge (greys the yellow stripes).
     See the matching note in 10-drawer.css (watch-drawer). */
  box-shadow: none;
  transform: translateX(100%);
  /* Timing unified with .ztor-cart__panel (360ms / var(--ease-out)). */
  transition: transform 360ms var(--ease-out),
              box-shadow 360ms var(--ease-out);
}
.notif-drawer.is-open .notif-drawer__panel {
  transform: translateX(0);
  box-shadow: -24px 0 60px rgba(0, 0, 0, 0.5);
}

.notif-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  /* Padding + bottom hairline unified with .ztor-cart__head. */
  padding: var(--space-6) var(--space-6) var(--space-4);
  flex-shrink: 0;
  border-bottom: 1px solid var(--border-subtle);
}
.notif-drawer__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 22px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text-primary);
  margin: 0;
}
.notif-drawer__close {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  cursor: pointer;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  transition: background 200ms ease;
}
.notif-drawer__close:hover { background: rgba(255, 255, 255, 0.14); }
.notif-drawer__close-icon {
  width: 16px;
  height: 16px;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
          mask: url('assets/icons/close.svg') center / contain no-repeat;
}

/* Head right-side actions: 全部已讀 + close. */
.notif-drawer__head-actions {
  display: flex;
  align-items: center;
  gap: 12px;
}
.notif-drawer__mark-read {
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  transition: color 200ms ease;
}
.notif-drawer__mark-read:hover { color: var(--yellow-500); }

.notif-drawer__list {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 4px 24px 16px;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.07) transparent;
}
.notif-drawer__list::-webkit-scrollbar { width: 6px; }
.notif-drawer__list::-webkit-scrollbar-thumb { background: rgba(255, 255, 255, 0.07); border-radius: var(--radius-pill); }

/* ── Notification item — [unread dot] [thumb] [title + time] ── */
.notif-item {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 0;
  text-decoration: none;
  color: inherit;
}
.notif-item + .notif-item { border-top: 1px solid var(--border-subtle); }

/* Unread dot — always reserves the column; coloured only when unread. */
.notif-item__dot {
  flex: 0 0 auto;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: transparent;
}
.notif-item--unread .notif-item__dot { background: var(--error-500); }

.notif-item__thumb {
  flex: 0 0 auto;
  width: 48px;
  aspect-ratio: 4 / 5;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-secondary);
}
.notif-item__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Brand (ztor) / user thumbs — icon centred on a dark tile. */
.notif-item__thumb--brand,
.notif-item__thumb--user {
  display: grid;
  place-items: center;
  background: var(--bg-tertiary);
}
/* ztor wordmark via mask so it renders neutral (品牌黃在此太搶眼). */
.notif-item__brand-logo {
  width: 72%;
  height: 100%;
  background-color: var(--neutral-400);
  -webkit-mask: url('assets/icons/ztor_logo.svg') center / contain no-repeat;
          mask: url('assets/icons/ztor_logo.svg') center / contain no-repeat;
}
.notif-item__thumb--user img { width: 52%; height: 52%; opacity: 0.55; }

.notif-item__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.notif-item__title {
  font-family: var(--font-cjk-text);
  font-weight: var(--fw-regular);
  font-size: 14px;
  line-height: 1.45;
  color: var(--text-primary);
  transition: color 200ms ease;
}
.notif-item:hover .notif-item__title { color: var(--yellow-500); }
.notif-item__time {
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}

/* ─────────────────────────────────────────
   個人頁 (profile.html) — header(info+bio) + tabs + 貼文流
   貼文流為「文字左 + 媒體右」版型;action 重用 .post-card__stat/__bookmark/__more.
   ───────────────────────────────────────── */
/* 0804 Dolly：個人頁的內容欄改成跟創作者商店頁一樣寬。
   原本是刻意收窄的 1056（＝內容欄 960 ＋ 左右各 48），那是兩頁識別區唯一不一致的地方；
   現在整條收斂到 .container--wide 的 1440（內容欄 1344），兩頁切換不再跳寬。
   ⚠️ 這條規則整條移除、不改成 1440——.container--wide 本來就給 1440，
      再寫一次同值只是多一個之後會忘記同步的來源。 */
.profile-head {
  display: flex;
  gap: 32px;
  align-items: flex-start;
  padding: 8px 0 36px;
}
.profile-head__avatar {
  flex: 0 0 auto;
  width: 132px;
  height: 132px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--bg-tertiary);
  display: grid;
  place-items: center;
}
.profile-head__avatar img { width: 54%; height: 54%; opacity: 0.5; }
.profile-head__info { flex: 1; min-width: 0; padding-top: 6px; }
.profile-head__title-row { display: flex; align-items: baseline; gap: 12px; }
.profile-head__name {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 28px;
  line-height: 1.25;
  color: var(--text-primary);
  margin: 0;
}
.profile-head__role {
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-medium);
  color: var(--text-tertiary);
}
/* 職業（創作者商店頁 hero：與追蹤數同一排「歌手／演員｜388 萬 追蹤」，0806 Dolly） */
.profile-head__occupation {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
}
/* 數據列分隔線（0806 從 assets/rwd/profile-mobile.css 搬回元件層——當時是
   css/components/ 凍結才暫放頁面層，凍結已解除。
   flex: 0 0 auto ＝ 1px 寬的項在擠壓時不會被壓成 0。 */
.profile-stat__sep {
  flex: 0 0 auto;
  align-self: center;
  width: 1px;
  height: 12px;
  background: var(--border-default);
}
.profile-head__stats {
  display: flex;
  flex-wrap: wrap;
  gap: 24px;
  margin-top: 14px;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-tertiary);
}
.profile-stat strong {
  color: var(--text-primary);
  font-weight: var(--fw-bold);
  margin-right: 4px;
}
.profile-head__online {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 12px 0 0;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  color: var(--text-tertiary);
}
.profile-head__online-icon {
  width: 14px;
  height: 14px;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/history.svg') center / contain no-repeat;
          mask: url('assets/icons/history.svg') center / contain no-repeat;
}
.profile-head__bio {
  max-width: 780px;
  margin: 16px 0 0;
  font-family: var(--font-cjk-text);
  font-weight: var(--fw-regular);
  font-size: 15px;
  line-height: 1.7;
  color: var(--text-secondary);
}

/* tabs */
/* 頭部的社群連結列（0803 Dolly）。規格照 footer 的 .site-footer__social-icon：
   22px、background-color:currentColor ＋ mask，所以一個 --icon 就換平台、顏色跟著
   文字走。放在自介下方＝內容區的最後一列，跟 IG／YouTube 頻道頁同一個位置。
   平時 --text-secondary、hover 才提到全白：這是次要資訊，不跟名字與數據搶。 */
.profile-head__social {
  display: flex;
  align-items: center;
  /* 圓底本身已經把每顆分開了，間距從裸圖示時的 16 收到 8，不然整排會散開 */
  gap: var(--space-2);
  margin-top: var(--space-3);
}
/* 圓形底（0803 Dolly）＝用站上的 .icon-btn，圓角／玻璃底／hover 與按壓的過場都直接
   繼承，不另外調一套圓。兩處覆寫（0803 Dolly：「不需要外框線，整體也可以小一點」）：
   只覆寫描邊（0803 Dolly：「不需要有外框線」）——這排是次要資訊，有框線會變成四顆
   「按鈕」跟名字搶注意力，只留玻璃底就夠把可點擊講清楚。尺寸維持 .icon-btn 原生的
   40（0803 試過 32，Dolly 要求改回）。
   ⚠️ border:0 跟 .icon-btn 同權重(0,1,0)，靠 profile-mobile.css 在 components.css
      之後載入取勝；日後這段搬回元件層時要重新確認源序（同 memory「.ds-icon 尺寸權重」）。 */
.profile-social {
  flex: 0 0 auto;
  border: 0;
  text-decoration: none;
}
.profile-social::before {
  content: '';
  width: 20px;
  height: 20px;
  background-color: currentColor;
  -webkit-mask: var(--sicon) center / contain no-repeat;
          mask: var(--sicon) center / contain no-repeat;
}
/* ⚠️ 平台圖檔路徑一定要跟 mask 寫在同一支 stylesheet：相對路徑是相對「寫 mask 的
   那支 css」解析，而這支住在 assets/rwd/ → 所以是 ../icons/…。第一版把 --icon 寫在
   HTML 內聯（assets/icons/…），被解成 assets/rwd/assets/icons/… 全部 404，而且
   不報錯、只是圖不見（memory「var() url() 相對 stylesheet」，0803 實際踩到）。
   ✅ 0805 補齊：面板那八個平台現在全部有圖。新增的四個（Youtube／Spotify／Twitch／
      小紅書）取自 Simple Icons（CC0，各平台官方 brand asset 的單色路徑），與既有四個
      同規格（viewBox 24×24、單一 path），存在 assets/icons/social-media/。
      Youtube 那條路徑與 L 在創作者頁內嵌的完全一致 → 同一個來源譜系，不是另一套畫風。
      舊的 assets/icons/xiaohongshu.webp 是裁壞的碎片，**刻意不刪**——12 位創作者頁的
      靜態 markup 還引用著它（.creator-hero__social-word），那是另一條線的事。 */
/* Ztor 自家那顆（0804 Dolly，創作者商店頁固定顯示，連到個人檔案）。字標裡的「z」
   本身沒有留白，跟其他平台那些自帶邊距的方形圖示擺在一起會顯得偏大，所以縮到 16。 */
.profile-social--ztor::before    { --sicon: url('assets/icons/ztor_mark.svg'); width: 16px; height: 16px; }
.profile-social--ig::before      { --sicon: url('assets/icons/ig.svg'); }
.profile-social--threads::before { --sicon: url('assets/icons/thread.svg'); }
.profile-social--x::before       { --sicon: url('assets/icons/social-media/x.svg'); }
.profile-social--tiktok::before  { --sicon: url('assets/icons/social-media/tiktok.svg'); }
.profile-social--youtube::before { --sicon: url('assets/icons/social-media/youtube.svg'); }
.profile-social--spotify::before { --sicon: url('assets/icons/social-media/spotify.svg'); }
.profile-social--twitch::before  { --sicon: url('assets/icons/social-media/twitch.svg'); }
/* 小紅書是**字標**（三個字、約 3:1）不是方形圖標——官方沒有出方形的單色 glyph。
   照其他平台給 20×20 的話 contain 會把它縮成 20 寬 × 7 高，字全糊在一起。
   所以這一顆單獨放寬盒子（26×20），高度仍與其他平台對齊、視覺重量才不會失衡。
   （同 --ztor 那顆為了字標比例單獨調尺寸，站上已有這個先例。） */
.profile-social--xhs::before     { --sicon: url('assets/icons/social-media/xiaohongshu.svg'); width: 26px; height: 20px; }

/* ── 分享 icon 鈕 ────────────────────────────────────────────────────
   0805：創作者商店頁 hero 成為第二個消費者（Dolly：「個人檔案、專屬商店、作品詳情
   都有分享按鈕」），所以「這顆鈕長什麼樣」從只有個人頁會載的 assets/rwd/profile-mobile.css
   搬到元件層——留在頁面層＝創作者頁裸奔（memory「page-key CSS 害新頁裸奔」，已踩三次）。
   ⚠️ 只搬外觀，**版面留在原處**：絕對定位（個人頁桌機的右上角）與 self/guest 的顯隱
      是個人頁的情境邏輯，創作者頁兩者都沒有（它是 flex 排在 actions 裡）。
   設計理由沿用 0803：沿用 .icon-btn 拿 40×40 觸控面積與按壓縮放，只把「玻璃圓底」
   整組關掉（背景／描邊／backdrop-filter，**hover 與 active 三態都要覆寫**，不然滑過
   又長出圓底）；不給 hover 變色，照站上另一顆裸 icon 鈕 .m-pagebar__back。
   ⚠️ 這支住在 css/components/ → url() 基準是 repo root（assets/icons/…），
      不是原本頁面層的 ../icons/（memory「var() url() 相對 stylesheet」）。 */
.profile-head__share,
.profile-head__share:hover:not(:disabled),
.profile-head__share:active:not(:disabled) {
  background: none;
  border: 0;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.profile-head__share { flex: 0 0 auto; }
.profile-head__share::before {
  content: '';
  width: 20px;
  height: 20px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/share.svg') center / contain no-repeat;
          mask: url('assets/icons/share.svg') center / contain no-repeat;
}
/* hover／active／focus 全交給 .icon-btn，這裡不重寫（重寫就跟全站的圓鈕分岔了）。 */

/* 真人頭像變體。base 的 .profile-head__avatar img 是為「沒有頭像」的剪影佔位調的
   （54% ＋ opacity .5）；真照片要滿版填滿圓框，所以另給 --photo 覆蓋那三個值。
   雙 class 抬權重，不靠載入順序取勝。
   消費者：profile.html 的頭部、creator-*.html 的 hero 識別區（0804）。 */
.profile-head__avatar.profile-head__avatar--photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 1;
}

/* 認證標章。assets/icons/verify.svg 走 .ds-icon 的 mask，background-color 就是圖形
   的顏色 → 品牌橘（勾是路徑挖空的，會透出頁面底色）。
   ⚠️ .ds-icon 的 width/height:1em 寫在 assets/ds.css，同權重且載入在後——必須用雙
   class 抬權重才蓋得掉尺寸（memory「.ds-icon 尺寸權重」）。
   title-row 是 baseline 對齊的 flex，圖形沒有文字基線 → align-self:center 才不會沉。
   ⚠️ 這裡刻意不寫 display：顯不顯示是頁面情境（個人頁只有 creator 身分才有、創作者
      商店頁永遠有），由各頁自己決定。 */
.profile-head__verified.ds-icon {
  align-self: center;
  width: 20px;
  height: 20px;
  background-color: var(--brand-500);
}

/* 自介的「更多」只在手機收行時才有意義，桌機自介本來就整段攤開。
   0804 從 profile-mobile.css 搬上來——創作者商店頁的 hero 也用這個元件，
   規則留在頁面層的話那頁桌機會冒出一顆沒樣式的原生按鈕。 */
@media (min-width: 768px) {
  .profile-head__bio-more { display: none; }
}

.profile-tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border-subtle);
}
.profile-tab {
  position: relative;
  padding: 14px 20px;
  border: none;
  background: none;
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: 15px;
  font-weight: var(--fw-medium);
  color: var(--text-tertiary);
  transition: color 200ms ease;
}
.profile-tab:hover { color: var(--text-secondary); }
.profile-tab--active { color: var(--yellow-500); }
.profile-tab--active::after {
  content: "";
  position: absolute;
  left: 16px;
  right: 16px;
  bottom: -1px;
  height: 2px;
  background: var(--yellow-500);
}

/* ══ 貼文流 — 0804 Dolly：改用**首頁那張貼文卡**，桌機一排最多三張 ══════════
   卡片外觀整組吃 17-community.css 的 .community-card（圖在最上→作者列→標題→
   摘要→數據列），這裡只給「放在個人頁分頁裡」才需要的差異。
   為什麼是首頁那張而不是社群頁那張：首頁卡本來就是 grid 元件（標題自帶兩行截斷、
   數據列自帶 margin-top:auto 釘在卡底）；社群頁那張是為單欄 720 的長文設計的。
   ⚠️ 舊的 .profile-post*（文字左＋208 縮圖右）整組移除——markup 已換掉。 */
.profile-feed,
.profile-posts {
  display: flex;
  flex-wrap: wrap;
  /* stretch（預設）＝同一排的卡片等高 */
  gap: var(--pp-gap, 20px);
}
/* 一排三張，且**不 fill container**（0804 Dolly：只有一篇時不該撐滿整條）。
   ⚠️ 用固定 flex-basis 而不是 grid 的 1fr：1fr 會把單一一張拉成整欄寬。 */
.profile-feed > .community-card,
.profile-posts > .community-card {
  flex: 0 0 calc((100% - var(--pp-gap, 20px) * 2) / 3);
  min-width: 0;
}
/* 分頁內的空狀態不是卡片，讓它自己佔滿一整列 */
.profile-feed > .rf-empty,
.profile-posts > .rf-empty { flex: 1 1 100%; }

/* ── 封面鎖比例＝「一排三張不會歪」的關鍵（0804 Dolly：看起來沒對齊）───────
   首頁卡的封面刻意讓圖片保留原始比例（完整顯示、不裁切），所以橫式的矮、
   直式的一路撐到高度上限——三張並排就參差。這裡改成固定 16:10 裁切框。
   卡片內其餘三條線首頁卡自己已經處理好：標題兩行截斷、摘要一行截斷、
   數據列 margin-top:auto 釘在卡底。這裡只再補一條「標題不足兩行也佔兩行」，
   讓摘要那行也對齊。 */
.profile-feed .community-card__cover,
.profile-posts .community-card__cover {
  aspect-ratio: 16 / 10;
  background: var(--bg-tertiary);
}
/* ── 直式圖不裁切：完整呈現、左右用同一張圖模糊墊底（0805 Dolly）─────────
   框仍然固定 16:10（卡片等高的來源，不能動），改的是圖在框裡怎麼放。
   ⚠️ **不需要用 JS 判斷直式或橫式**——讓圖片元素照自己的比例決定寬度就自動分流：
   ・`height:100%` ＋ `width:auto` → 元素寬度 ＝ 框高 × 圖片自身比例。
   ・**直式**（例：608×900 → 252 高、170 寬）沒碰到 max-width，元素比例＝圖片比例，
     object-fit 無事可做 → 整張完整呈現，兩側各留 116px 由 __cover-bg 的模糊複本填滿。
   ・**橫式**（例：900×480 → 252 高、472 寬）超過框寬被 max-width 夾回 403，
     元素變 16:10、圖片 1.875:1 → cover 裁掉左右。**維持 0804 以來的樣子。**
   所以同一組宣告就分出兩種行為，沒有狀態、沒有 class、沒有 load 事件競態。
   ⚠️ `width:auto` 是為了蓋掉基底元件的 `width:100%`（那條會讓元素永遠等於框寬，
      直式圖就又被 object-fit 接手裁掉了）。 */
.profile-feed .community-card__cover-img,
.profile-posts .community-card__cover-img {
  width: auto;
  max-width: 100%;
  height: 100%;
  max-height: none;
  object-fit: cover;
  margin-inline: auto;      /* 直式時把窄元素置中，兩側模糊帶才等寬 */
}
@media (min-width: 768px) {
  .profile-feed .community-card__title,
  .profile-posts .community-card__title {
    min-height: calc(2 * 1.4em);   /* 1.4 = .community-card__title 的 line-height */
  }
}

/* 作者列：首頁卡整張是連結、作者只是文字；這裡作者是真的連結，補回可點的樣式。 */
.community-card__author {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
/* 數據列右側：首頁卡只有收藏一顆，這裡多一顆 ⋯（沿用 ds.js 的 post-more 行為）。 */
.community-card__acts {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
}
/* 「翻譯內容」＝個人頁原本就有、首頁卡沒有的動作，收在左群組末尾。 */
.community-card__translate {
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
  transition: color 150ms ease;
}
.community-card__translate:hover { color: var(--text-secondary); }

/* ─────────────────────────────────────────
   DEV-ONLY: header state toggle
   切換 body[data-auth] 控制顯示 Before/After Login header
   出貨前移除（這支 class 與底部 button）
   ───────────────────────────────────────── */

body[data-auth="logged-out"] .header[data-state="after-login"],
body[data-auth="logged-in"]  .header[data-state="before-login"] {
  display: none;
}

/* ─── DEV-ONLY · 出貨前整段移除 ─────────────────────────
   Header auth-state toggle styles. Pairs with the
   [data-dev-only] button in index.html and the matching JS
   IIFE. Remove all three together. */
.dev-toggle {
  position: fixed;
  right: 24px;
  bottom: 24px;
  z-index: 9999;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 16px;
  border: 1px solid rgba(255, 255, 255, 0.2);
  border-radius: var(--radius-pill);
  background: rgba(20, 20, 20, 0.85);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-medium);
  cursor: pointer;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
  transition: right 240ms ease;
}

/* 0729 Dolly：情境膠囊升級成 wp-roomdev 情境面板（ds.js dev-state 注入，樣式
   =watch-party.css 既有元件、同 staging title.html）。此檔只補商城情境的讓位：
   右側 440px 面板（購物車/結帳、觀看歷史、通知）開啟時滑到面板左側、維持置底。 */
.wp-roomdev[data-shop-devbar] {
  transition: right 240ms ease;
  z-index: 9999;
  /* 商城頁 body.theme-sharp 把 radius token 歸零（直角畫廊皮膚）；情境面板是
     浮動 dev 元件不屬於那個面，還原成 tokens.css 預設刻度＝與 title.html 同觀感 */
  --radius-lg: 12px;
  --radius-xl: 16px;
}
body.cart-open .dev-toggle,
body:has(.watch-drawer.is-open) .dev-toggle,
body:has(.notif-drawer.is-open) .dev-toggle,
body.cart-open .wp-roomdev[data-shop-devbar],
body:has(.watch-drawer.is-open) .wp-roomdev[data-shop-devbar],
body:has(.notif-drawer.is-open) .wp-roomdev[data-shop-devbar] {
  right: calc(min(440px, 100vw) + 16px);
}
/* （0803 移除）選規格面板曾經是桌機右側滑出、需要讓位；0802 晚間改成置中
   彈窗後右緣沒有東西，讓位只剩無意義的跳動——這張面板不再列讓位名單。 */
/* 0806 Dolly：0804 選規格面板改回「貼底 sheet」後又會撞了——sheet 置中貼底，
   膠囊讓到哪一側都可能壓到底部 CTA（願望清單在購物車抽屜裡加規格品時，
   膠囊已因 cart-open 左移、正好疊在 CTA 上）。這個情境要的不是讓位是隱藏；
   關窗（is-open 拿掉）即恢復。 */
body:has(.pdp-sheet.is-open) .dev-toggle,
body:has(.pdp-sheet.is-open) .wp-roomdev[data-shop-devbar] { display: none; }
/* 0807 Dolly：ds-drawer 側板（如何競標／尺寸指南／出價／編輯個人檔案…）開著時
   情境面板先隱藏。這裡用隱藏而不是上面那組「讓位」——側板貼右緣滿高，膠囊讓到
   左邊仍然壓在面板底部 CTA 上（同 0806 選規格 sheet 的理由）。
   body.ds-drawer-open ＝ assets/ds-drawer.js 開窗時掛的類別（關窗即移除），
   所以這條對全站所有 ds-drawer 一體適用，不必逐個面板列名。 */
body.ds-drawer-open .dev-toggle,
body.ds-drawer-open .wp-roomdev[data-shop-devbar] { display: none; }

.dev-toggle::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: var(--radius-pill);
  background: var(--neutral-400);
}

body[data-auth="logged-in"] .dev-toggle::before { background: var(--yellow-500); }

.dev-toggle:hover { background: rgba(30, 30, 30, 0.9); }

/* Any mobile header overlay (hamburger drawer / profile menu / create sheet)
   sets body.menu-open — the floating dev chips must yield to the overlay
   instead of hovering above it (2026-07-09 Dolly). Scoped to the session/auth
   chips so watch-party's in-page .wp-roomdev bars are unaffected. */
body.menu-open .dev-toggle,
body.menu-open .wp-roomdev--session { display: none; }

/* Session-alert demo trigger now reuses the watch-party dev-bar look
   (.wp-roomdev). Its position variant lives in css/components/session-alert.css
   (.wp-roomdev--session), which is bundled after watch-party.css. */
/* ─── /DEV-ONLY ─────────────────────────────────────────── */

/* ─────────────────────────────────────────
   FILMS RAIL — 第三章·電影庫預覽
   橫向 rail with 3 tabs (最新影片 / 現正熱播 / Ztor. 精選).
   Each panel = a horizontally scrollable track of .film-card.
   Nav arrows reuse hero-carousel glass style: hover-to-show.
   ───────────────────────────────────────── */

.films-rail {
  position: relative;
}

/* Tabs row — sits between section header and the rail */
.films-rail__tabs {
  display: flex;
  gap: var(--space-1);
  margin-top: calc(var(--space-10) * -1 + var(--space-5));
  margin-bottom: var(--space-8);
  border-bottom: 1px solid var(--border-subtle);
}

.films-rail__tab {
  position: relative;
  padding: 12px 18px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--text-tertiary);
  font-family: var(--font-cjk-display);   /* 0728 Dolly 試版：16px＋LINE Seed（≥16 合字體規範） */
  font-size: 16px;
  font-weight: var(--fw-regular);
  cursor: pointer;
  transition: color 200ms ease, border-color 200ms ease;
}

.films-rail__tab:hover { color: var(--text-secondary); }

.films-rail__tab[aria-selected="true"] {
  color: var(--neutral-white);   /* 0728 Dolly：橘字→白字（線維持橘） */
  border-bottom-color: var(--yellow-500);
}

/* Panel — only the selected one renders the track. Fade-in on switch
   so tab activations feel responsive. */
.films-rail__panel { display: none; }
.films-rail__panel.is-active {
  display: block;
  animation: filmsRailFade 260ms ease both;
}

@keyframes filmsRailFade {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* Track — horizontal scroll container, snap to start.
   --rail-cols controls how many cards perfectly fill the row at the
   default desktop width. Card width uses calc() so the row always
   spans flush from the section's left edge to the right.
   Modifier .films-rail__track--ranked drops cols to 5 (Top 5 shelf). */
.films-rail__track {
  --rail-cols: 6;
  --rail-gap: 20px;
  display: flex;
  gap: var(--rail-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scrollbar-width: none; /* Firefox */
  padding: 4px 2px 16px;
  margin: -4px -2px 0;   /* offset padding so cards align with section edges */
}
.films-rail__track::-webkit-scrollbar { display: none; }

/* Ranked rails reserve extra left room so the first card's huge "1" (which
   sits at left:-8px) clears the track's overflow clip instead of being cut.
   The matching negative margin keeps the cards aligned to the section edge. */
.films-rail__track--ranked {
  --rail-cols: 5;
  /* The #1 digit overhangs the first card's left edge (rank is at left:-8px),
     and the track's overflow-x clip would cut it. Plain padding-left doesn't
     help — scroll-snap (mandatory + align:start) snaps the first card to the
     scrollport edge and eats the padding. So:
       margin-left:-24  → move the clip edge 24px left of the container content
       scroll-padding-left:24 → snap the first card back to the content edge
                                (cards stay aligned with the section header)
     Net: the clip sits left of the digit's overhang, so it's no longer cut,
     and the row is still aligned. -24 stays within the container padding, so
     no horizontal overflow on ≤1440 screens. */
  margin-left: -24px;
  padding-left: 24px;
  scroll-padding-left: 24px;
}

/* Nav arrows — same glass + hover-to-show as hero carousel.
   Vertical position is set dynamically via JS to align with the poster's
   vertical center (the active panel's first card). The .is-enabled class
   is toggled by JS based on whether scrolling further in that direction
   is still possible — only enabled buttons reveal on hover. */
.films-rail__nav {
  position: absolute;
  /* `top` is set inline by JS once the rail mounts / on resize / on tab change. */
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  border: none;
  padding: 0;
  cursor: pointer;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  /* L-kit neutral glass (was a copy of the old ghost recipe) */
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  backdrop-filter: var(--lg-frost);
  -webkit-backdrop-filter: var(--lg-frost);
  transition: opacity 250ms ease, background 200ms ease;
}

.films-rail:hover .films-rail__nav.is-enabled,
.films-rail__nav.is-enabled:focus-visible {
  opacity: 1;
  pointer-events: auto;
}

.films-rail__nav.is-enabled:hover {
  background: linear-gradient(0deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.30) 2.45%,
    rgba(255, 255, 255, 0) 126.14%);
}

.films-rail__nav--prev { left: -22px; }
.films-rail__nav--next { right: -22px; }

.films-rail__nav img {
  width: 22px;
  height: 22px;
  display: block;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
}

/* ─────────────────────────────────────────
   FILM CARD
   2:3 poster + popcorn-price badge (br) + body (title / meta / score)
   Hover: poster lifts + bottom darken overlay reveals.
   Variant .film-card--ranked: huge outlined number behind the poster (Top 5).
   ───────────────────────────────────────── */

/* CONSISTENCY FIX (2026-06-16): the poster is a FIXED intrinsic width, so the
   card is the SAME size on every page. It used to be computed from --rail-cols
   ÷ the track's container width, which DRIFTED — 172px inside the home page's
   1440 container vs 191px on full-bleed library pages (same component, same
   viewport). The rail now simply fits however many 184px cards fit; the card
   itself never changes size. (Mobile gets a smaller token below — responsive,
   not drift.) */
.film-card {
  --card-w: 184px;
  position: relative;
  flex: 0 0 var(--card-w);
  width: var(--card-w);
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
}

/* 無外框（2026-07-16 Dolly：海報細灰線移除；hover 提示只靠位移＋暗化） */
.film-card__poster {
  position: relative;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-tertiary);
  transition: transform 220ms var(--ease-out);
  z-index: 1; /* above ranked number */
}

.film-card:hover .film-card__poster {
  transform: translateY(-4px);
}

.film-card__poster img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Hover darken — reveals price badge and stays subtle on idle */
.film-card__poster::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg,
    rgba(0, 0, 0, 0) 55%,
    rgba(0, 0, 0, 0.55) 100%);
  opacity: 0.6;
  transition: opacity 250ms ease;
  pointer-events: none;
}

.film-card:hover .film-card__poster::after { opacity: 1; }

/* Popcorn price — bottom-right glass pill (mirrors 1.0) */
/* 地區限制鎖 badge — 與價格膠囊同皮膚同位（右下；無價可租、僅觀影資訊） */
.film-card__lock {
  position: absolute;
  right: 8px;
  bottom: 8px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  white-space: nowrap;
  z-index: 2;
}
.film-card__lock svg { width: 13px; height: 13px; flex: none; }

.film-card__price {
  position: absolute;
  right: 8px;
  bottom: 8px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 4px 8px 4px 6px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  z-index: 2;
}

.film-card__price-icon {
  width: 14px;
  height: 14px;
  display: inline-block;
  background-color: var(--neutral-white);
  -webkit-mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
}

/* ── 繼續觀看(resume)變體 — 租借中影片:海報底部黃進度條 + 左下剩餘時間.
   沿用桌機 2/3 海報比例(.film-card__poster);ref: mobile-screening.html.
   剩餘時間 badge 鏡像 .film-card__price(改放左下). ── */
.film-card__resume-time {
  position: absolute;
  left: 8px;
  bottom: 8px;
  z-index: 2;
  padding: 4px 10px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.film-card__progress {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background: rgba(255, 255, 255, 0.25);
  z-index: 2;
}
.film-card__progress-fill {
  display: block;
  height: 100%;
  background: var(--yellow-500);
}

/* Body — title + meta row (year/tag on left, score on right) */
.film-card__body {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 12px 4px 0;
}

.film-card__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 15px;
  line-height: 1.35;
  color: var(--text-primary);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.film-card__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  min-height: 28px; /* match score ring height to keep rail rhythm */
}

.film-card__meta-text {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-cjk-text);
  font-size: 11px;
  color: var(--text-tertiary);
  min-width: 0;
}

.film-card__meta-tag {
  display: inline-block;
  padding: 1px 6px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-secondary);
  white-space: nowrap;
  flex-shrink: 0;
}

/* Ranked variant — outlined number sits behind the poster on the left.
   The padding-left reserves a slot for the digit so part of it stays
   visible to the left of the poster. Sized to also fit double-digit
   "10" — the leading "1" reads, the "0" tucks behind the poster.

   container-type makes each ranked card a size container, so the digit
   (font-size/offsets in cqi below) scales WITH the card. Fixed px here
   breaks the moment the card shrinks — the digits detach from their
   posters and float over the section. */
.film-card--ranked {
  /* Wider than a normal card by exactly the rank gutter, so the POSTER inside
     is still 184px — matches every other rail (the gutter holds the big #1). */
  --card-w: calc(184px + 56px);
  padding-left: 56px;   /* px on purpose: an element's own cqi can't reference itself */
  container-type: inline-size;
}

.film-card__rank {
  position: absolute;
  left: -9.5cqi;
  bottom: -8.5cqi;    /* lower-left: base sits just below the poster bottom so it
                         reads in the strip under the image. 現正熱播 offsets via
                         a scoped rule. */
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-black);
  /* cqi resolves against the card's CONTENT box (card minus the 56px
     digit slot). 127cqi reproduces the original 240px at the 245px
     reference card, then scales with it. */
  font-size: 127cqi;
  line-height: 0.85;
  color: transparent;
  /* SOLID grey stroke (was white-22% translucent). Pre-composited to the same
     tone the translucent stroke rendered on the dark section (~#404040 over
     neutral-900), but OPAQUE — so the overlapping 1+0 strokes of "10" no longer
     double up into a darker patch at their crossing. color-mix keeps it
     token-based; the plain --neutral-600 line is the no-color-mix fallback. */
  -webkit-text-stroke: 2px var(--neutral-600);
  -webkit-text-stroke: 2px color-mix(in srgb, var(--neutral-600) 74%, var(--neutral-900));
          text-stroke: 2px var(--neutral-600);
          text-stroke: 2px color-mix(in srgb, var(--neutral-600) 74%, var(--neutral-900));
  pointer-events: none;
  user-select: none;
  z-index: 0;
  letter-spacing: -0.05em;
}
.film-card__rank--double {
  left: -44.5cqi;
  /* +2px over the base -0.08em nudges the "0" right so it clears the "1"
     (calc keeps the em base scaling with the card, adds a fixed gap). */
  letter-spacing: calc(-0.08em + 2px);
}

/* ─────────────────────────────────────────
   ZTOR SCORE — 評分 ring (Metacritic-style conic arc)
   Restored from the web build's ring (replaces the liquid glass orb).
   Arc length = score%; the tier sets the colour:
     --gold   → 85–100  iridescent rainbow arc + glow (premium "光")
     --green  → 70–84   success
     --orange → 50–69   amber-yellow (NOT brand orange — see --score-ring-mid)
     --red    → 0–49    error
     --empty  → 評分不足 → faint full track + "--"
   Markup (unchanged): <span class="ztor-score ztor-score--green" style="--score:77">
                         <span class="ztor-score__value">77</span></span>
   Animation: index.html §3.5 counts BOTH --score (arc) AND the numeral
   from 0 → target on viewport entry (replayable). Without JS the inline
   --score renders the ring filled — the count-up is enhancement only.
   ───────────────────────────────────────── */

.ztor-score {
  --score: 0;
  --ring-color: var(--neutral-500);
  --track-color: rgba(255, 255, 255, 0.10);
  --inner-bg: var(--bg-primary);   /* matches the section bg behind the ring */
  --ring-band: 2px;                /* arc thickness — also drives the round caps */
  position: relative;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background:
    conic-gradient(var(--ring-color) calc(var(--score) * 1%), var(--track-color) 0);
  /* Edge frame is THEME-driven (see section-themes.css):
       dark stripe  → none (the arc already pops on black)
       light stripe → a warm brand-family ring (set per surface) */
  box-shadow: var(--score-edge, none);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

/* Inner cut-out — leaves a `--ring-band` ring of the conic showing. */
.ztor-score::before {
  content: '';
  position: absolute;
  inset: var(--ring-band);
  background: var(--inner-bg);
  border-radius: 50%;
}

/* Round (pill) caps on the arc ends — conic-gradient cuts its arc with a hard
   radial edge (square corners). This ::after paints the SAME ring paint
   (`--cap-paint`: rainbow for --gold, solid `--ring-color` otherwise) and
   masks it to two dots on the band centreline at the arc's start (12 o'clock)
   and end (score angle), so each square end gets a rounded cap. The dots
   inherit the conic colour at their position — including during the shimmer.
   Gated on sin() support: if the trig is unsupported the mask would be invalid
   (→ ::after becomes a solid disc), so we simply skip the caps there. */
@supports (left: calc(sin(45deg) * 1px)) {
  .ztor-score::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: var(--cap-paint, var(--ring-color));
    --cap-ang: calc(var(--score) * 3.6deg);
    --cap-r: calc(50% - var(--ring-band) / 2);   /* band centreline radius */
    --cap-d: calc(var(--ring-band) / 2);          /* cap radius = half band */
    -webkit-mask:
      radial-gradient(circle var(--cap-d) at 50% var(--cap-d), #000 98%, transparent 100%),
      radial-gradient(circle var(--cap-d) at calc(50% + var(--cap-r) * sin(var(--cap-ang))) calc(50% - var(--cap-r) * cos(var(--cap-ang))), #000 98%, transparent 100%);
    -webkit-mask-composite: source-over;
    mask:
      radial-gradient(circle var(--cap-d) at 50% var(--cap-d), #000 98%, transparent 100%),
      radial-gradient(circle var(--cap-d) at calc(50% + var(--cap-r) * sin(var(--cap-ang))) calc(50% - var(--cap-r) * cos(var(--cap-ang))), #000 98%, transparent 100%);
    mask-composite: add;
  }
  .ztor-score--empty::after { display: none; }   /* full track, no arc → no caps */
}

.ztor-score__value {
  position: relative;
  z-index: 1;
  /* Afacad (main's score numeral face, decision 2026-06-11) — taller,
     slightly condensed digits that read as a "score". */
  font-family: 'Afacad', var(--font-latin);
  font-size: 12px;
  font-weight: var(--fw-bold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
  color: var(--text-primary);
}

.ztor-score--green  { --ring-color: var(--success-500); }
.ztor-score--orange { --ring-color: var(--score-ring-mid); }
.ztor-score--red    { --ring-color: var(--error-500); }

/* ── Top tier (85–100) · iridescent rainbow arc + glow ──────────────────
   Two stacked conics: the full rainbow sits underneath; a track conic ON
   TOP turns transparent up to the score% (revealing the rainbow along the
   filled arc) then paints --track-color over the remainder — so it works
   at any score. A slow hue rotation (--ztor-spin, registered so it can
   interpolate) gives the living "光" shimmer; a soft two-colour glow adds
   the premium feel. Falls back to a static rainbow where @property is
   unsupported; reduced-motion freezes the shimmer. */
@property --ztor-spin { syntax: '<angle>'; inherits: false; initial-value: 0deg; }

.ztor-score--gold {
  /* The rainbow's first & last stop are the SAME pink (#ff4d6d) by design, so
     the arc's head and tail both sit at pink. Set --ring-color to that pink so
     the round end-caps (which fall back to --ring-color) continue the rainbow
     instead of showing the default grey neutral. */
  --ring-color: #ff4d6d;
  background:
    /* faint white track over the unfilled arc (matches the other tiers) */
    conic-gradient(transparent calc(var(--score) * 1%), var(--track-color) 0),
    /* opaque mask so the rainbow shows ONLY along the filled arc — without it
       the 10% track barely dims the rainbow and the ring reads as a full circle */
    conic-gradient(transparent calc(var(--score) * 1%), var(--inner-bg) 0),
    conic-gradient(from var(--ztor-spin, 0deg),
      #ff4d6d, #ff9a3d, #ffe14d, #4dffa1, #36d1ff, #9b6bff, #ff4d8d, #ff4d6d);
  box-shadow:
    0 0 6px rgba(255, 140, 90, 0.45),
    0 0 13px rgba(140, 100, 255, 0.28);
  /* round caps wear the same rotating rainbow so their colour matches the arc */
  --cap-paint: conic-gradient(from var(--ztor-spin, 0deg),
    #ff4d6d, #ff9a3d, #ffe14d, #4dffa1, #36d1ff, #9b6bff, #ff4d8d, #ff4d6d);
}
/* The shimmer only runs while the orb is in view (index.html adds .is-live
   on viewport entry, removes it on exit) — static rainbow everywhere else,
   so a long page never spins a dozen off-screen conics. */
.ztor-score--gold.is-live { animation: ztor-spin 5s linear infinite; }
@keyframes ztor-spin { to { --ztor-spin: 360deg; } }

@media (prefers-reduced-motion: reduce) {
  .ztor-score--gold.is-live { animation: none; }
}

.ztor-score--empty {
  --score: 100;
  --ring-color: rgba(255, 255, 255, 0.10);
}
.ztor-score--empty .ztor-score__value {
  color: var(--text-disabled);
  font-size: 12px;
  letter-spacing: -0.05em;
}

/* On a light surface stripe: a dark inner disc keeps the white numeral
   legible. No edge frame on either theme — the dark inner disc already
   anchors the ring, and a frame looked harsh on orange. */
.section-bg-surface .ztor-score {
  --inner-bg: var(--bg-secondary);
}

/* 評分環可點：帶 data-ds-modal-open 的 ztor-score 給指標＋鍵盤可及 */
.ztor-score[data-ds-modal-open] { cursor: pointer; }
.ztor-score[data-ds-modal-open]:focus-visible {
  outline: 2px solid var(--brand-500);
  outline-offset: 2px;
}

/* ── Ztor. 綜合指數 — 點評分環開的分數拆解彈窗（.ds-modal primitive）── */
.ztor-index__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 28px;
}
.ztor-index .ds-modal__title { margin: 0; }
.ztor-index .ds-modal__close { position: static; flex: 0 0 auto; }
/* 開窗 autofocus 落在 ✕ → 不畫橘框（同分享彈窗，資訊型彈窗慣例） */
.ztor-index :is(button, a):focus,
.ztor-index :is(button, a):focus-visible { outline: none; }

.ztor-index__hero {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-bottom: 28px;
}
.ztor-index__ring {
  width: 64px;
  height: 64px;
  --ring-band: 4px;
  --inner-bg: var(--bg-secondary);   /* 融入 modal panel 底 */
  flex: none;
}
.ztor-index__ring .ztor-score__value { font-size: 24px; }
.ztor-index__lede {
  margin: 0;
  font-size: var(--fs-body-sm);
  line-height: 1.6;
  color: var(--text-secondary);
}

.ztor-index__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.ztor-index__row {
  padding: 14px 16px;
  background: var(--white-alpha-4);
  border-radius: var(--radius-md);
}
.ztor-index__rowhead {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  margin-bottom: 6px;
}
.ztor-index__icon { width: 28px; height: 28px; flex: 0 0 auto; }
.ztor-index__icon--mask {
  background: var(--brand-500);
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
.ztor-index__icon--img { display: block; }
.ztor-index__score {
  font-family: 'Afacad', var(--font-latin);
  font-size: 20px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.ztor-index__unit { font-size: 13px; font-weight: 500; color: var(--text-tertiary); }
.ztor-index__desc {
  margin: 0;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  text-align: center;
}

/* ─────────────────────────────────────────
   COMMUNITY — 第四章·社群
   3-col grid of article preview cards. Each card pairs a 1920×1024
   landscape cover with a short headline/desc and an author meta row.
   Image spec is fixed (no portrait variants on the homepage for now);
   later, post-detail / community list pages can introduce portrait
   covers + blur-fill, or a true masonry layout.
   ───────────────────────────────────────── */

.community-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
  align-items: start;   /* tiles keep their natural height — landscape stays short, portrait stands taller; tops align */
}

.community-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: transform 250ms ease, border-color 250ms ease;
}

.community-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-default);
}

/* Cover — SAME treatment as the community-page feed: a fixed-max-height stage,
   contain (never cropped). LANDSCAPE is untouched — it fills the width at its
   own (short) height. Only PORTRAIT is capped at --cover-max and shrunk to fit,
   with the blurred copy behind filling its side gaps (ambient bleed). The sharp
   image drives the tile height, so landscape tiles aren't letterboxed. */
.community-card__cover {
  position: relative;
  width: 100%;
  overflow: hidden;
  background: #000;
  line-height: 0;
}
.community-card__cover-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.15);
  filter: blur(24px) brightness(0.6);
  z-index: 0;
}
.community-card__cover-img {
  position: relative;             /* in flow → drives the tile height */
  z-index: 1;
  display: block;
  width: 100%;
  height: auto;
  max-height: var(--cover-max, clamp(240px, 30vh, 340px));   /* portrait cap */
  object-fit: contain;            /* whole image, never cropped */
  margin-inline: auto;
  transition: transform 500ms ease;
}
.community-card:hover .community-card__cover-img {
  transform: scale(1.03);
}

/* Header — author chip left, date right. Sits between the cover and body,
   inherits the social-post pattern from 1.0 (avatar + name + date). */
/* 影片貼文：封面中央的播放鈕。配方＝社群頁 .post-card__cover--video 那顆
   （2026-08-04 個人頁貼文改用本卡時補上，之前只有社群頁那張卡有）。 */
.community-card__cover--video::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  width: 56px;
  height: 56px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgba(10, 10, 10, 0.55)
    url('assets/icons/media-play.svg') center / 22px no-repeat;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  pointer-events: none;
}
/* 0805 Dolly：播放鈕改成 hover 之後才出現。
   ⚠️ **不能用 opacity 淡入**——這顆有 backdrop-filter，opacity 0→1 的中間幀會把
      backdrop 重新取樣、閃一下白（memory「backdrop-filter 淡入閃白」，站上踩過）。
      所以 opacity 恆為 1，改用 visibility 切換＋輕微縮放。
      visibility 只能離散切換，收起時延後到縮放跑完（`visibility 0s linear 220ms`），
      出現時不延遲（`visibility 0s`），才不會一開始就沒東西可縮。
   ⚠️ 整段 gate 在 `hover: hover` ——觸控裝置沒有 hover，藏了就永遠看不到
      「這是影片」的訊號，手機維持常駐顯示。
   ⚠️ 縮放必須跟置中的 translate 合成同一個 transform，分開寫會把它推到右下
      （memory「:active scale 蓋置中 translate」）。 */
@media (hover: hover) and (pointer: fine) {
  .community-card__cover--video::after {
    visibility: hidden;
    transform: translate(-50%, -50%) scale(0.86);
    transition: transform 220ms var(--ease-out), visibility 0s linear 220ms;
  }
  /* :focus-within＝鍵盤 Tab 到卡片裡的連結時也要看得到 */
  .community-card:hover .community-card__cover--video::after,
  .community-card:focus-within .community-card__cover--video::after {
    visibility: visible;
    transform: translate(-50%, -50%) scale(1);
    transition: transform 220ms var(--ease-out), visibility 0s;
  }
}

.community-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 22px 0;
}

.community-card__date {
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
  flex-shrink: 0;
}

/* Body — title → desc → optional chips → meta row (stats + bookmark). */
.community-card__body {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 12px 22px 18px;
  flex: 1;
}

/* Topic chips — subdued, low-contrast pills that surface campaign /
   programme affiliations (e.g. AI 創作 posts pointing to specific
   contests). Designed to be readable but not compete with title/author. */
.community-card__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 2px;
}

.community-card__chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-subtle);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-tertiary);
  font-family: var(--font-cjk-text);
  font-size: 11px;
  font-weight: var(--fw-regular);
}

.community-card__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 19px;
  line-height: 1.4;
  color: var(--text-primary);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color 200ms ease;
}

.community-card:hover .community-card__title {
  color: var(--yellow-500);
}

.community-card__desc {
  font-family: var(--font-cjk-text);
  /* Figma spec: Body/Small/zh · Regular · 14/23 · Text/Tertiary */
  font-weight: var(--fw-regular);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--text-tertiary);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* Meta row — stats cluster left, bookmark action right. Pinned to the
   bottom of the body via margin-top: auto so action rows align across
   cards in the same grid row regardless of title/desc/chip length. */
.community-card__meta {
  margin-top: auto;
  padding-top: 6px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}

.community-card__author {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}

/* Avatar — small circular user image. Falls back to a neutral tile bg
   while the image loads. */
.community-card__avatar {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-pill);
  background: var(--bg-tertiary);
  object-fit: cover;
  display: block;
  flex-shrink: 0;
}

.community-card__author-text {
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.community-card__author-name {
  color: var(--text-secondary);
  font-weight: var(--fw-medium);
}

/* Stats cluster — heart/comment counts + bookmark + share icons. */
.community-card__stats {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  color: var(--text-tertiary);
}

.community-card__stat {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}
/* 首頁的數據是純顯示的 <span>（整張卡是連結）；個人頁／創作者頁的貼文要真的能按
   （讚／留言／分享），所以那邊是 <button>。瀏覽器預設會給 button 一層灰底＋邊框＋
   內距，不重置就會在卡片底部長出三顆白色小方塊（2026-08-04 實際踩到）。
   只重置盒模型，字體與顏色仍由上面那條共用規則決定。 */
button.community-card__stat {
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}

.community-card__stat-icon,
.community-card__action-icon {
  width: 20px;
  height: 20px;
  display: inline-block;
  background-color: currentColor;
  flex-shrink: 0;
}

.community-card__stat-icon--heart   { -webkit-mask: url('assets/icons/heart.svg')   center / contain no-repeat;
                                               mask: url('assets/icons/heart.svg')   center / contain no-repeat; }
.community-card__stat-icon--comment { -webkit-mask: url('assets/icons/comment.svg') center / contain no-repeat;
                                               mask: url('assets/icons/comment.svg') center / contain no-repeat; }
.community-card__stat-icon--share   { -webkit-mask: url('assets/icons/share.svg')   center / contain no-repeat;
                                               mask: url('assets/icons/share.svg')   center / contain no-repeat; }
.community-card__action-icon--bookmark { -webkit-mask: url('assets/icons/bookmark_regular.svg') center / contain no-repeat;
                                                  mask: url('assets/icons/bookmark_regular.svg') center / contain no-repeat; }

/* Active (bookmarked) state — toggle the .is-bookmarked class on the
   button to swap to the filled variant and highlight in brand yellow. */
.community-card__action.is-bookmarked .community-card__action-icon--bookmark {
  -webkit-mask: url('assets/icons/bookmark_active.svg') center / contain no-repeat;
          mask: url('assets/icons/bookmark_active.svg') center / contain no-repeat;
  background-color: var(--yellow-500);
}

/* Action buttons are clickable bare icons — hover lifts contrast. */
.community-card__action {
  background: none;
  border: none;
  padding: 4px;
  margin: -4px;
  cursor: pointer;
  color: var(--text-tertiary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 150ms ease;
}

.community-card__action:hover { color: var(--text-secondary); }


/* ── Community card · OVERLAY variant (UGC-safe) ───────────────────────────
   These photos are USER-uploaded — any aspect ratio. So we NEVER crop to a
   fixed box: the card follows the photo's natural ratio (news-hero model) and
   the .media-glass panel overlays the bottom. The grid is MASONRY so the mixed
   heights read as an intentional feed (Instagram/Pinterest), not broken rows.
   📐 PHOTO SPEC (uploads): any ratio; recommend ≥1080px on the long edge, JPG
      ≤ ~700 KB. Keep key subject/faces out of the very bottom (the text sits
      there). One class on the grid (.community-grid--overlay) converts every card. */
.community-grid--overlay {
  display: block;
  column-count: 3;
  column-gap: 24px;
}
@media (max-width: 900px) { .community-grid--overlay { column-count: 2; } }
@media (max-width: 600px) { .community-grid--overlay { column-count: 1; } }
.community-grid--overlay .community-card {
  position: relative;
  display: block;
  width: 100%;
  margin-bottom: 24px;
  break-inside: avoid;              /* keep a card whole within its masonry column */
  background: var(--bg-card);       /* surface the text flows onto below a short photo */
}
.community-grid--overlay .community-card__cover { position: relative; aspect-ratio: auto; line-height: 0; }
/* Image drives its own height at NATURAL ratio — no object-fit crop, ever. */
.community-grid--overlay .community-card__cover img { position: relative; width: 100%; height: auto; }
/* The text panel FLOWS (not absolute → never clips). A negative margin lifts it
   onto the photo's lower edge for the glass dissolve; whatever doesn't fit on a
   short (landscape) photo simply continues onto the card surface below. */
.community-grid--overlay .community-card__overlay {
  position: relative;
  z-index: 1;
  margin-top: -132px;                 /* ride up onto the photo's lower band */
  padding: 80px 18px 18px;            /* top ~80px = the blur dissolve zone over the photo */
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.community-grid--overlay .community-card__header { padding: 0; }
.community-grid--overlay .community-card__body { flex: 0 0 auto; gap: 6px; padding: 0; }
.community-grid--overlay .community-card__desc {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Text rides the scrim → force light; the scrim guarantees contrast. */
.community-grid--overlay .community-card__title { color: #fff; }
.community-grid--overlay .community-card:hover .community-card__title { color: var(--yellow-500); }
.community-grid--overlay .community-card__desc,
.community-grid--overlay .community-card__date,
.community-grid--overlay .community-card__author,
.community-grid--overlay .community-card__stats,
.community-grid--overlay .community-card__stat,
.community-grid--overlay .community-card__action { color: rgba(255, 255, 255, 0.85); }
.community-grid--overlay .community-card__author-name { color: #fff; }
/* Chips → glass pills on the photo. */
.community-grid--overlay .community-card__chip {
  background: rgba(255, 255, 255, 0.16);
  border-color: rgba(255, 255, 255, 0.30);
  color: #fff;
}
/* Avatar ring so it reads on any photo. */
.community-grid--overlay .community-card__avatar {
  box-shadow: 0 0 0 1.5px rgba(255, 255, 255, 0.5);
}

/* ─────────────────────────────────────────
   COMMUNITY PAGE — 社群貼文流 (community.html)
   V1 /posts structure in V2 dress: a single centred post feed (~720) under a
   visual tab bar. Each post = author row → title → cover → body (更多 expand)
   → action row (♥ 留言 分享 + 收藏). Reuses the community-card stat icons.
   ───────────────────────────────────────── */

/* Centred content column — narrow feed per L (long-form posts ~720). */
.post-feed {
  max-width: 720px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

/* Sticky tab bar — full-bleed dark/blur background that pins below the header
   on scroll; the tabs themselves are centred to the feed width (720 + the
   48px container gutter each side, so they line up with the post cards). */
.community-tabbar {
  position: sticky;
  top: var(--header-height);
  z-index: 20;
  margin-bottom: 20px;
  background: rgba(10, 10, 10, 0.82);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--border-subtle);
}
/* Tabs + feed share one centred rail. The rail owns the 720 width + gutters.
   Logged-out it becomes a 2-col grid (feed + register aside) so the whole
   pair shifts left (Threads-style) while the tabs stay aligned to the feed
   column above the post cards. */
.community-rail {
  max-width: calc(720px + var(--container-padding-x) * 2);
  margin: 0 auto;
  padding: 0 var(--container-padding-x);
}
body[data-auth="logged-out"] .community-rail {
  max-width: calc(720px + 32px + 320px + var(--container-padding-x) * 2);
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 32px;
  align-items: start;
}



/* Shop sub-nav panels (商品/套組/拍賣): the [hidden] attribute must hide the
   grid, but .shop-grid's `display:grid` (author CSS) out-ranks the UA
   `[hidden]{display:none}`. This wins it back so inactive panels stay hidden. */
.shop-grid[hidden] { display: none !important; }


/* SKELETON LOADER -> extracted to css/components/ds-skeleton.css (build inlines it). */

/* ── Register aside — logged-out only. A low-key panel beside the feed that
   nudges sign-up (Threads-style). Quiet surface + subtle border + ghost CTA
   so it guides without competing with the feed. Sticky as you scroll. ── */
.community-aside {
  position: sticky;
  top: calc(var(--header-height) + 84px);
  align-self: start;
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: 28px 24px;
}
.register-card__title {
  font-family: var(--font-cjk-display);
  font-size: 19px;
  line-height: 1.4;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  text-align: center;
}
.register-card__body {
  margin-top: 10px;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 1.65;
  color: var(--text-secondary);
}
.register-card__cta {
  margin-top: 20px;
  width: 100%;
}
.register-card__note {
  margin-top: 14px;
  font-family: var(--font-cjk-text);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--text-tertiary);
  text-align: center;
}
.register-card__note a {
  color: var(--text-secondary);
  text-decoration: none;
}
.register-card__note a:hover { color: var(--text-primary); }

/* Narrow viewports: drop back to single-column feed, hide the aside (it's a
   nice-to-have guide, the feed is primary). */
@media (max-width: 1100px) {
  body[data-auth="logged-out"] .community-rail {
    display: block;
    max-width: calc(720px + var(--container-padding-x) * 2);
  }
  .community-aside { display: none !important; }
}

/* Post card — boxed feed item (V1-style): surface bg + subtle border + radius.
   padding 24→20（2026-07-16 Dolly 拍板）：整卡 padding，圖/標題/內文/作者列
   一起 20——順帶與搜尋 overlay 貼文卡的 --space-5(20px) 對齊。 */
.post-card {
  display: flex;
  flex-direction: column;
  padding: 20px;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
}

/* Author row */
.post-card__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 14px;
}
.post-card__author {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: inherit;
  min-width: 0;
}
.post-card__avatar {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  object-fit: cover;
  flex-shrink: 0;
}
.post-card__author-meta {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.post-card__author-name {
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.post-card__date {
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}
.post-card__more {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: none;
  background: none;
  border-radius: var(--radius-pill);
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 150ms ease, background-color 150ms ease;
}
.post-card__more:hover { color: var(--text-primary); background: var(--white-alpha-8); }
.post-card__more .ds-icon { width: 20px; height: 20px; }

/* Title */
.post-card__title {
  margin: 0 0 14px;
  font-family: var(--font-cjk-display);
  font-size: 24px;
  line-height: 1.4;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}

/* Cover — fixed 16:9 frame (Figma Library v4: 618:348). Any aspect fits via
   blur-fill: a blurred, zoomed copy of the image fills the frame behind a fully
   contained foreground copy. So portrait posters / vertical video thumbs show
   complete and centred, with their own colour bleeding to the edges. */
/* Fixed-HEIGHT stage, never cropped: the sharp image drives the stage height
   (capped at --cover-max), so a LANDSCAPE still fills the width at its own
   height, and a PORTRAIT stands tall up to the cap — shrunk to fit, never over-
   shrunk. The blurred copy behind fills whatever the sharp image leaves (the
   side gaps on portraits, top/bottom on ultra-wides) with the photo's own light
   — ambient bleed, no dead bars, no crop. */
.post-card__cover {
  position: relative;
  width: 100%;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #000;
  line-height: 0;
}
.post-card__cover-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: scale(1.15);          /* hide blur edge bleed */
  filter: blur(28px) brightness(0.6);
}
.post-card__cover-img {
  position: relative;              /* in flow → drives the stage height */
  z-index: 1;
  display: block;
  width: 100%;
  height: auto;
  max-height: var(--cover-max, clamp(420px, 62vh, 600px));  /* portrait cap */
  object-fit: contain;             /* whole image, never cropped */
  margin-inline: auto;
  transition: transform 300ms ease;
}
.post-card:hover .post-card__cover-img { transform: scale(1.02); }

/* ── 貼文內嵌影片（2026-07-16 Dolly 規則：滑到＝靜音自動播、滑走暫停）──
   .post-card__cover[data-post-video] 容器＋<video>；右下 🔇/🔊 圓鈕切聲音。
   聲音狀態＝容器 .is-sound（ds.js 只翻 class，icon 換圖在這裡的 CSS——
   url 相對 components.css=根層，照 side-rail 慣例寫 assets/icons/）。 */
.post-card__video {
  position: relative;
  z-index: 1;
  display: block;
  width: 100%;
  /* 與圖片貼文同一套（2026-07-16 Dolly 拍板「照原本的」）：不鎖比例、
     完整顯示，高度跟來源比例走、吃同一個上限（曾試過鎖 1920×1024 已撤） */
  height: auto;
  max-height: var(--cover-max, clamp(420px, 62vh, 600px));
}
.post-card__mute {
  position: absolute;
  right: 12px;
  bottom: 12px;
  z-index: 2;
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: 50%;
  background: rgba(10, 10, 10, 0.55);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--text-primary);
  cursor: pointer;
  transition: background-color 150ms ease;
}
.post-card__mute:hover { background: rgba(10, 10, 10, 0.75); }
.post-card__mute::before {
  content: '';
  width: 20px;
  height: 20px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/media/volume-off.svg') center / contain no-repeat;
          mask: url('assets/icons/media/volume-off.svg') center / contain no-repeat;
}
.post-card__cover.is-sound .post-card__mute::before {
  -webkit-mask-image: url('assets/icons/media/volume-on.svg');
          mask-image: url('assets/icons/media/volume-on.svg');
}

/* Video state (capability for Type=Video / Short Video): centred play button
   over the cover. Add .post-card__cover--video on the cover to enable. */
.post-card__cover--video::after {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  width: 56px;
  height: 56px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: rgba(10, 10, 10, 0.55)
    url('assets/icons/media-play.svg') center / 22px no-repeat;
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  pointer-events: none;
}

/* Body — 3 lines by default, then 更多 expands it with a calm animated height
   (the JS sets the exact px target; this is the Animation Director's spec —
   240–300ms ease-out, a single quiet reveal on a feed element). */
.post-card__body {
  margin: 16px 0 0;
  font-family: var(--font-cjk-text);
  font-size: 15px;
  line-height: 1.8;                /* CJK long-form reading — denser glyphs need more leading than Latin */
  letter-spacing: 0.01em;          /* a hair of tracking so the characters breathe */
  color: var(--text-secondary);
  max-height: calc(1.8em * 3);     /* 3-line clamp — kept in sync with the line-height */
  overflow: hidden;
  transition: max-height 160ms cubic-bezier(0.32, 0.72, 0, 1);   /* collapse — exit faster than entry */
}
.post-card.is-expanded .post-card__body {
  max-height: 240em;               /* no-JS fallback ceiling; JS sets the exact height */
  transition: max-height 240ms cubic-bezier(0.23, 1, 0.32, 1);   /* expand — calm ease-out entry */
}
/* Calm feed element — respect reduced motion: resize instantly, no grow. */
@media (prefers-reduced-motion: reduce) {
  .post-card__body { transition: none; }
}

/* 更多 / 收合 toggle — quiet text button, hidden when there's nothing to clip. */
.post-card__expand {
  align-self: flex-start;
  margin-top: 6px;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  border: none;
  background: none;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 150ms ease;
}
.post-card__expand:hover { color: var(--text-primary); }
.post-card__expand-chev {
  width: 14px;
  height: 14px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
  transform: rotate(90deg);
  transition: transform 200ms ease;
}
.post-card.is-expanded .post-card__expand-chev { transform: rotate(-90deg); }

/* ── 1.0-style card hover（ported from ztor.com post cards, 2026-07-10 Dolly.
   The 1.0 outer border is deliberately NOT ported — keep the card's own subtle
   border untouched.）Desktop-only: <768 keeps the approved mobile-community
   look (its 更多 is a decorative inline fade), and touch has no hover. ── */
@media (min-width: 768px) {
  /* Cover hover: dim + centred 更多＋arrow_right. Two pseudos because one box
     can't hold text AND a separate masked icon: ::before = scrim + 更多
     (padding-right reserves the icon's width so the text+icon GROUP reads
     centred), ::after = the arrow. Excludes the --video variant, whose
     ::after is the play button (same pseudo — would collide). */
  .post-card__cover:not(.post-card__cover--video)::before {
    content: '更多';
    position: absolute;
    inset: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    padding-right: 28px;                   /* icon 20px + gap 8px → group centred */
    background: rgba(10, 10, 10, 0.55);   /* same scrim as the video play chip */
    color: var(--text-primary);
    font-family: var(--font-cjk-text);
    font-size: 18px;
    font-weight: var(--fw-medium);
    letter-spacing: 0.04em;
    line-height: 1;                        /* cover sets line-height:0 — restore */
    opacity: 0;
    transition: opacity 200ms ease;
    pointer-events: none;
  }
  .post-card__cover:not(.post-card__cover--video)::after {
    content: '';
    position: absolute;
    top: calc(50% - 2px);                  /* optical: PIL ink-centre of the arrow
                                              sits 2.25px below the 更多 ink-centre
                                              at geometric 50% — compensate up */
    left: 50%;
    z-index: 2;
    width: 20px;
    height: 20px;
    margin-left: 12px;                     /* text right edge (50%+4px) + 8px gap */
    transform: translateY(-50%);
    background-color: var(--text-primary);
    -webkit-mask: url('assets/icons/arrow_right.svg') center / contain no-repeat;
            mask: url('assets/icons/arrow_right.svg') center / contain no-repeat;
    opacity: 0;
    transition: opacity 200ms ease;
    pointer-events: none;
  }
  .post-card:hover .post-card__cover:not(.post-card__cover--video)::before,
  .post-card:hover .post-card__cover:not(.post-card__cover--video)::after { opacity: 1; }

  /* No in-text 更多／展開收合 at desktop (2026-07-10 Dolly): the body stays at
     its 3-line clamp; the full post opens in the post popup (planned). The
     button markup stays for the mobile layer, whose approved design keeps a
     decorative inline-fade 更多 (inert there too — see community-mobile.css). */
  .post-card__expand { display: none; }
}

/* Action row */
.post-card__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 18px;
}
.post-card__stats {
  display: inline-flex;
  align-items: center;
  gap: 20px;
}
.post-card__stat,
.post-card__bookmark {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px;
  margin: -4px;
  border: none;
  background: none;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 150ms ease;
}
.post-card__stat:hover,
.post-card__bookmark:hover { color: var(--text-secondary); }
.post-card__stat-icon,
.post-card__bookmark-icon {
  width: 20px;
  height: 20px;
  display: inline-block;
  background-color: currentColor;
  flex-shrink: 0;
}
.post-card__stat-icon--heart   { -webkit-mask: url('assets/icons/heart.svg')   center / contain no-repeat;
                                          mask: url('assets/icons/heart.svg')   center / contain no-repeat; }
.post-card__stat-icon--comment { -webkit-mask: url('assets/icons/comment.svg') center / contain no-repeat;
                                          mask: url('assets/icons/comment.svg') center / contain no-repeat; }
.post-card__stat-icon--share   { -webkit-mask: url('assets/icons/share.svg')   center / contain no-repeat;
                                          mask: url('assets/icons/share.svg')   center / contain no-repeat; }
.post-card__bookmark-icon      { -webkit-mask: url('assets/icons/bookmark_regular.svg') center / contain no-repeat;
                                          mask: url('assets/icons/bookmark_regular.svg') center / contain no-repeat; }

@media (max-width: 768px) {
  .post-card__title { font-size: 18px; }
  .feed-tab { padding: 10px 12px; }
}

/* ─────────────────────────────────────────
   NEWS — 第五章·新聞與公告
   Magazine layout: 1 hero card (image-led, larger) on the left + 3 list
   cards (horizontal compact, image-left/content-right) stacked on the
   right. No social stats — news is editorial chrome, not user posts.
   Each card carries:  tag (yellow) → title → optional excerpt → meta
   (source · date).
   ───────────────────────────────────────── */

.news-grid {
  display: grid;
  /* 左頭條略窄、右側清單較寬 → 頭條維持比例縮小、右欄 fill，兩欄等高
     minmax(0, …) lets the fr columns shrink below their content's
     intrinsic width — without it the list cards force the page to
     overflow horizontally on narrow windows. */
  grid-template-columns: minmax(0, 4fr) minmax(0, 5fr);
  gap: 24px;
}

/* Below ~1080 the two columns can't both breathe — stack them. */
@media (max-width: 1080px) {
  .news-grid { grid-template-columns: 1fr; }
}

.news-card {
  display: flex;
  text-decoration: none;
  color: inherit;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform 250ms ease, border-color 250ms ease;
}

.news-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-default);
}

/* Hero variant — the photo owns the whole card; the body is a glass
   panel that dissolves upward into the image (progressive blur:
   backdrop-filter faded by a gradient mask — masking runs after the
   filter, so the blur itself melts away rather than ending on a seam.
   Refs: joshwcomeau.com/css/backdrop-filter, kennethnym.com progressive
   blur, devslovecoffee Apple progressive blur). */
.news-card--hero {
  flex-direction: column;
  position: relative;
  /* full row height: the right column's three list cards set the row,
     the hero matches them edge to edge (aspect-ratio here would defeat
     the grid stretch and leave a hole under the card) */
  height: 100%;
}
@media (max-width: 1080px) {
  /* stacked: no sibling column to match — the photo's ratio drives */
  .news-card--hero { height: auto; aspect-ratio: 1920 / 1340; }
}

.news-card--hero .news-card__cover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
}
.news-card--hero .news-card__cover img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.news-card--hero .news-card__body {
  position: absolute;
  inset: auto 0 0 0;
  isolation: isolate;   /* keeps the glass pseudos above the cover, below the text */
  padding: 64px 26px 26px;  /* tall top padding IS the dissolve zone */
  gap: 12px;
}
/* The glass: blur strongest at the foot, gone by the panel's top edge. */
.news-card--hero .news-card__body::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -2;
  backdrop-filter: blur(22px) saturate(1.35);
  -webkit-backdrop-filter: blur(22px) saturate(1.35);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.5) 30%, black 62%);
          mask-image: linear-gradient(to bottom, transparent 0%, rgba(0,0,0,0.5) 30%, black 62%);
  pointer-events: none;
}
/* The tint: quiet ink ramp so the title reads on any photograph. */
.news-card--hero .news-card__body::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom,
    transparent 0%,
    rgba(10, 10, 10, 0.30) 34%,
    rgba(10, 10, 10, 0.62) 100%);
  pointer-events: none;
}
/* No backdrop-filter (old engines): the tint alone carries legibility. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .news-card--hero .news-card__body::after {
    background: linear-gradient(to bottom,
      transparent 0%,
      rgba(10, 10, 10, 0.62) 30%,
      rgba(10, 10, 10, 0.88) 100%);
  }
}

.news-card--hero .news-card__title {
  font-size: 24px;
  line-height: 1.35;
}

.news-card--hero .news-card__excerpt {
  -webkit-line-clamp: 3;
}

/* List variant — divider-only style, no card chrome. Image left
   (≈ 40% width), content right. Cards stack inside .news-list with
   subtle horizontal dividers between them. */
.news-card--list {
  flex-direction: row;
  align-items: stretch;
  background: transparent;
  border: none;
  border-radius: 0;
  overflow: visible;
  gap: 18px;
  padding: 20px 0;
}

.news-card--list:hover {
  transform: none;
  border-color: transparent;
}

.news-card--list .news-card__cover {
  flex: 0 0 40%;
  aspect-ratio: 1920 / 1024;
  border-radius: var(--radius-md);
  overflow: hidden;
}

.news-card--list .news-card__body {
  flex: 1;
  padding: 4px 0;
  gap: 10px;
  justify-content: center;
}

.news-card--list .news-card__title {
  font-size: 17px;
  line-height: 1.4;
}

/* One-line excerpt under the title for editorial breathing room. */
.news-card--list .news-card__excerpt {
  display: -webkit-box;
  -webkit-line-clamp: 1;
  margin: 0;
}

/* Cover (shared) */
.news-card__cover {
  position: relative;
  overflow: hidden;
  background: var(--bg-tertiary);
}

.news-card__cover img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 500ms ease;
}

.news-card:hover .news-card__cover img { transform: scale(1.03); }

/* Body (shared) */
.news-card__body {
  display: flex;
  flex-direction: column;
  flex: 1;
}

.news-card__tag {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  gap: 8px;
  padding: 6px 12px;
  border: 0.8px solid rgba(var(--brand-rgb), 0.3);
  border-radius: var(--radius-pill);
  background:
    linear-gradient(90deg, rgba(var(--brand-rgb), 0.12), rgba(var(--brand-rgb), 0.12)),
    rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
  color: var(--yellow-500);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  line-height: 18px;
  font-weight: var(--fw-medium);
}

.news-card__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  transition: color 200ms ease;
}

/* Hover affordance — title turns brand yellow on either hero or list card. */
.news-card:hover .news-card__title {
  color: var(--yellow-500);
}

.news-card__excerpt {
  font-family: var(--font-cjk-text);
  /* Figma spec: Body/Small/zh · Regular · 14/23 · Text/Tertiary */
  font-weight: var(--fw-regular);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--text-tertiary);
  margin: 0;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.news-card__meta {
  margin-top: auto;
  padding-top: 12px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}

.news-card__meta-source { color: var(--text-secondary); font-weight: var(--fw-medium); }

/* Right column wrapper — stacks the 3 list cards with thin dividers in
   between (no card chrome on individual cards). */
.news-list {
  display: flex;
  flex-direction: column;
}

.news-list > .news-card--list:not(:last-child) {
  border-bottom: 1px solid var(--border-subtle);
}

.news-list > .news-card--list:first-child  { padding-top: 0; }
.news-list > .news-card--list:last-child   { padding-bottom: 0; }

/* Card variant — the rail beside the hero shows each item as a standalone
   surface card (bg + hairline border + radius) instead of hairline-divided
   rows. Same component, different container treatment. */
.news-list--cards { gap: 14px; }
.news-list--cards > .news-card--list {
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;        /* clip cover to the card's rounded corners */
  padding: 0;              /* cover hugs the edges; padding lives on the body */
  gap: 16px;
}
.news-list--cards > .news-card--list:not(:last-child) { border-bottom: none; }
/* Cover hugs the card top/bottom/left edge — no gap above/below the image. */
.news-list--cards > .news-card--list .news-card__cover {
  align-self: stretch;
  aspect-ratio: auto;
  border-radius: 0;
}
/* Breathing room only on the text side. */
.news-list--cards > .news-card--list .news-card__body {
  padding: 14px 16px 14px 0;
}

/* ─────────────────────────────────────────
   ACTIVITY — 第六章·同場加映
   3-col grid of lightweight feature cards: icon → title → desc → CTA.
   Each card has a subtle yellow ambient glow in the top-right to hint
   at the playful / supplementary tone of this section.
   Variant .activity-card--disabled drops opacity and removes hover lift
   (used for 敬請期待 / coming-soon CTAs).
   ───────────────────────────────────────── */

.activity-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

.activity-card {
  position: relative;
  display: flex;
  flex-direction: column;
  padding: 32px;
  min-height: 280px;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  transition: transform 250ms ease, border-color 250ms ease;
}

.activity-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-default);
}

/* Yellow ambient glow at top-right — playful tone marker.
   Circle center is anchored exactly on the card's top-right corner so
   only the bottom-left quadrant of the circle bleeds into the card. */
.activity-card::before {
  content: '';
  position: absolute;
  top: -130px;
  right: -130px;
  width: 260px;
  height: 260px;
  background: radial-gradient(circle,
    rgba(var(--brand-rgb), 0.20) 0%,
    rgba(var(--brand-rgb), 0.07) 30%,
    transparent 70%);
  pointer-events: none;
  z-index: 0;
}

.activity-card > * { position: relative; z-index: 1; }

/* Icon chip — outline icon inside a yellow-tinted rounded square. */
.activity-card__icon {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-md);
  background: rgba(var(--brand-rgb), 0.08);
  border: 1px solid rgba(var(--brand-rgb), 0.22);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--yellow-500);
  margin-bottom: 24px;
}

/* Glass variant — chip keeps the base yellow tint as the "halo" behind
   the icon; the class is kept on the markup as a hook for future
   chip-level glass effects (e.g. backdrop-filter blur, refraction). */

.activity-card__icon-img {
  width: 32px;
  height: 32px;
  display: inline-block;
}

.activity-card__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 22px;
  line-height: 1.4;
  color: var(--text-primary);
  margin: 0 0 12px;
}

.activity-card__desc {
  font-family: var(--font-cjk-text);
  /* Figma spec: Body/Small/zh · Regular · 14/23 · Text/Tertiary */
  font-weight: var(--fw-regular);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--text-tertiary);
  margin: 0;
  flex: 1;
}

.activity-card__cta {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 24px;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-medium);
  color: var(--yellow-500);
  transition: gap 200ms ease;
}

.activity-card:hover .activity-card__cta { gap: 10px; }

.activity-card__cta-arrow {
  width: 14px;
  height: 14px;
  display: inline-block;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/arrow_right.svg') center / contain no-repeat;
          mask: url('assets/icons/arrow_right.svg') center / contain no-repeat;
}

/* Disabled / coming-soon CTA — muted text, no arrow, no hover gap shift. */
.activity-card__cta--disabled {
  color: var(--text-disabled);
}

.activity-card--disabled {
  cursor: default;
  opacity: 0.55;
}

.activity-card--disabled:hover {
  transform: none;
  border-color: var(--border-subtle);
}

.activity-card--disabled:hover .activity-card__cta { gap: 6px; }

/* Disabled glow — swap the yellow ambient for a neutral one so the
   dimmed card doesn't compete tonally with the active siblings. */
.activity-card--disabled::before {
  background: radial-gradient(circle,
    rgba(255, 255, 255, 0.10) 0%,
    rgba(255, 255, 255, 0.03) 30%,
    transparent 70%);
}

/* ─── Photo variant (--photo) — full-bleed cover + scrim, text pinned bottom.
   Drops the icon chip; the cover image carries the visual. Scrim gradient
   matches .news-card--overlay for a consistent light/dark rhythm.
   Ported from the dolly 2.0 build (2026-06-16). ─── */
.activity-card--photo {
  justify-content: flex-end;
  aspect-ratio: 3 / 2;          /* events art is 1536×1024 */
  min-height: 0;
  padding: 24px;
}
.activity-card__cover {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 300ms ease;
}
.activity-card--photo:hover .activity-card__cover { transform: scale(1.03); }
/* Repurpose ::before (the yellow glow) as the image→text scrim. */
.activity-card--photo::before {
  inset: 0;
  top: 0;
  right: 0;
  width: auto;
  height: auto;
  background: linear-gradient(180deg,
    rgba(10, 10, 10, 0)    34%,
    rgba(10, 10, 10, 0.46) 58%,
    rgba(10, 10, 10, 0.82) 80%,
    rgba(10, 10, 10, 0.94) 100%);
  z-index: 1;
}
/* Stacking: cover(0) → scrim(1) → text(2). */
.activity-card--photo > * { z-index: 2; }
.activity-card--photo .activity-card__cover { z-index: 0; }
.activity-card--photo .activity-card__desc {
  flex: 0 1 auto;
  color: rgba(255, 255, 255, 0.78);   /* matches news overlay excerpt */
}
/* Second row: desc left, text CTA right, on one line. */
.activity-card__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.activity-card__row .activity-card__cta {
  margin-top: 0;
  flex-shrink: 0;
  white-space: nowrap;
}
/* Disabled photo card: keep the card solid, mute the image instead —
   full-card opacity would read as a loading glitch over a photo. */
.activity-card--photo.activity-card--disabled { opacity: 1; }
.activity-card--photo.activity-card--disabled .activity-card__cover {
  filter: grayscale(1) brightness(0.72);
}
.activity-card--photo.activity-card--disabled:hover .activity-card__cover {
  transform: none;
}

/* ─────────────────────────────────────────
   SITE FOOTER
   5-col layout: brand block (logo + tagline + email + social) on the
   left, 4 nav columns on the right. Below the grid: a divider, then
   a chain of legal links, then the © row, then a small legal notice.
   ───────────────────────────────────────── */

.site-footer {
  background: var(--bg-primary);
  padding: clamp(64px, 8vw, 96px) 0 32px;
  border-top: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  margin-top: auto; /* sticky footer：把 footer 推到 main 底部，短頁不浮上來 */
}

.site-footer__top {
  display: grid;
  /* 品牌欄 + 5 個 nav 欄(平台/社群/合作/條款/支援)。
     新增合作欄後從 4 → 5 nav 欄,故 1fr 軌由 4 → 5。 */
  grid-template-columns: 2fr 1fr 1fr 1fr 1fr 1fr;
  gap: 48px;
}

/* Brand block (col 1) — logo + tagline + body + email + socials. */
.site-footer__brand {
  display: flex;
  flex-direction: column;
  gap: 18px;
  max-width: 360px;
}

.site-footer__logo {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  line-height: 1;
  text-decoration: none;
  height: 40px;
}

.site-footer__logo-img {
  height: 40px;
  width: auto;
  display: block;
}

.site-footer__tagline {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 18px;
  line-height: 1.5;
  color: var(--text-primary);
  margin: 0;
}

.site-footer__desc {
  font-family: var(--font-cjk-text);
  font-weight: var(--fw-regular);
  font-size: 13px;
  line-height: 1.75;
  color: var(--text-tertiary);
  margin: 0;
}

.site-footer__email {
  display: inline-flex;
  align-items: center;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 150ms ease;
}

.site-footer__email:hover { color: var(--yellow-500); }

/* Contact row — socials + email side by side on a single row. */
.site-footer__contact {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-top: 4px;
}

.site-footer__social {
  display: inline-flex;
  align-items: center;
  gap: 10px;
}

.site-footer__social-link {
  width: 44px;
  height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
}

.site-footer__social-link:hover {
  color: var(--yellow-500);
  border-color: rgba(var(--brand-rgb), 0.35);
  background: rgba(var(--brand-rgb), 0.06);
}

.site-footer__social-icon {
  width: 22px;
  height: 22px;
  display: inline-block;
  background-color: currentColor;
}

.site-footer__social-icon--instagram { -webkit-mask: url('assets/icons/ig.svg')     center / contain no-repeat;
                                                mask: url('assets/icons/ig.svg')     center / contain no-repeat; }
.site-footer__social-icon--threads   { -webkit-mask: url('assets/icons/thread.svg') center / contain no-repeat;
                                                mask: url('assets/icons/thread.svg') center / contain no-repeat; }

/* Nav columns (cols 2–5). */
.site-footer__nav {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.site-footer__nav-title {
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.04em;
  color: var(--yellow-500);
  margin: 0 0 6px;
}

.site-footer__nav-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.site-footer__nav-link {
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 150ms ease;
}

.site-footer__nav-link:hover { color: var(--text-primary); }

.site-footer__nav-link.is-disabled {
  color: var(--text-disabled);
  cursor: not-allowed;
  pointer-events: none;
}

/* Bottom blocks — divider then 條款 chain → © row → legal notice. */
.site-footer__divider {
  height: 1px;
  background: var(--border-subtle);
  margin: 56px 0 24px;
}

.site-footer__legal-chain {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 18px;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}

.site-footer__legal-chain a {
  color: inherit;
  transition: color 150ms ease;
}

.site-footer__legal-chain a:hover { color: var(--text-secondary); }

.site-footer__bottom-row {
  margin-top: 20px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}

.site-footer__copy {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

.site-footer__locale {
  display: inline-flex;
  align-items: center;
  gap: 14px;
}

.site-footer__locale a {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: inherit;
  text-decoration: none;
  transition: color 150ms ease;
}

.site-footer__locale a:hover { color: var(--text-secondary); }

.site-footer__locale-icon {
  width: 14px;
  height: 14px;
  display: inline-block;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/language.svg') center / contain no-repeat;
          mask: url('assets/icons/language.svg') center / contain no-repeat;
}

/* Legal notice — sits on the right of the © row. Matches the copy's
   12px to keep both sides of the row visually balanced. max-width
   keeps it a clean block instead of stretching across the row. */
.site-footer__notice {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  line-height: 1.65;
  color: var(--text-tertiary);
  max-width: 640px;
}

/* ─────────────────────────────────────────
   AUTH-GATING — show / hide blocks per auth state
   Pairs with body[data-auth] toggle. For now driven by dev-toggle;
   real auth would render server-side and remove these rules.
   ───────────────────────────────────────── */

body[data-auth="logged-out"] [data-auth-required="logged-in"]  { display: none !important; }
body[data-auth="logged-in"]  [data-auth-required="logged-out"] { display: none !important; }

/* ─────────────────────────────────────────
   POPCORN PRICING PAGE
   /popcorn.html — points-of-entry: header popcorn-entry pill (guest)
   and after-login popcorn balance/+ buttons. Two sections stacked:
   1) "我的爆米花" — balance + history + redeem (logged-in only)
   2) "購買爆米花" — 3 package cards (always)
   3) "使用指南" bullet block (always)
   ───────────────────────────────────────── */

.popcorn-page {
  padding-block: 72px 96px;
  display: flex;
  flex-direction: column;
  gap: 80px;
}

.popcorn-page__heading {
  text-align: center;
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: var(--fs-h3);
  line-height: 1.2;
  color: var(--text-primary);
  margin: 0 0 32px;
}

/* ── 1. My popcorn balance card ── */
.popcorn-balance-card {
  max-width: 720px;
  margin: 0 auto;
  width: 100%;
  background: var(--bg-secondary);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.popcorn-balance-card__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 24px 28px;
  text-decoration: none;
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  border-bottom: 1px solid var(--white-alpha-8);
}

.popcorn-balance-card__row:last-child { border-bottom: none; }

/* Balance row uses a faint yellow tint and brand-coloured value */
.popcorn-balance-card__row--balance {
  background: rgba(var(--brand-rgb), 0.04);
  color: var(--yellow-500);
}

.popcorn-balance-card__label-icon {
  display: inline-block;
  width: 18px;
  height: 18px;
  vertical-align: -3px;
  background-color: var(--yellow-500);
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}

/* History / redeem rows are clickable, hover-lit */
.popcorn-balance-card__row--link {
  cursor: pointer;
  transition: background-color 150ms ease, color 150ms ease;
}

.popcorn-balance-card__row--link:hover {
  background: var(--white-alpha-4);
  color: var(--yellow-500);
}

.popcorn-balance-card__row--link:hover .popcorn-balance-card__chevron {
  background-color: var(--yellow-500);
  transform: translateX(2px);
}

.popcorn-balance-card__label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.popcorn-balance-card__value {
  font-family: var(--font-latin);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
}

/* Chevron rendered via mask so it can recolor on hover */
.popcorn-balance-card__chevron {
  width: 18px;
  height: 18px;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
  transition: background-color 150ms ease, transform 150ms ease;
}

/* ── 2. Purchase package cards ── */
.popcorn-packages {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 20px;
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
}

.popcorn-package {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 36px 24px 28px;
  background: var(--bg-secondary);
  border-radius: var(--radius-lg);
  text-align: center;
  transition: background-color 200ms ease, transform 200ms ease;
}

.popcorn-package:hover {
  background: var(--bg-tertiary);
  transform: translateY(-2px);
}

.popcorn-package__icon {
  width: 56px;
  height: 56px;
  margin-bottom: 4px;
}

.popcorn-package__name {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: var(--fs-h5);
  line-height: var(--lh-h5);
  color: var(--text-primary);
  margin: 0;
}

.popcorn-package__count {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--yellow-500);
  margin: 0;
}

.popcorn-package__buy {
  margin-top: 12px;
  min-width: 152px;
}

/* ── 3. Usage guide ── */
.popcorn-guide {
  max-width: 720px;
  width: 100%;
  margin: 0 auto;
}

.popcorn-guide__title {
  font-family: var(--font-cjk-text);
  font-weight: var(--fw-medium);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-primary);
  margin: 0 0 12px;
}

.popcorn-guide__list {
  margin: 0;
  padding-left: 1.4em;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.75;
  color: var(--text-secondary);
}

.popcorn-guide__list li { list-style: disc; }

/* Responsive — 3 → 1 col on narrow (768 = site-wide mobile breakpoint) */
@media (max-width: 768px) {
  .popcorn-page { padding-block: 48px 64px; gap: 56px; }
  .popcorn-page__heading { font-size: var(--fs-h3); margin-bottom: 20px; }
  .popcorn-packages { grid-template-columns: 1fr; gap: 14px; }
  .popcorn-balance-card__row { padding: 18px 20px; }
}

/* Flush the top padding for index/main pages — their title sits close
   under the site header (~32px per Figma), unlike homepage chapters which
   use the generous .section breathing room. Bottom padding unchanged. */
.section--flush-top { padding-top: 32px; }

/* ─────────────────────────────────────────
   MAIN-PAGE HEADER ROW  (.page-head)
   Compact title row used by content index pages (cocreate / news /
   activity): H3 title on the left + optional info link on the right.
   Mirrors Figma "Title Combo" (97:517). Reusable — see [[cross-page
   consistency]] memory.
   ───────────────────────────────────────── */
.page-head {
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 16px 0;
}

/* Left block — title + optional description, stacked. */
.page-head__copy {
  flex: 1 1 auto;
  min-width: 0;
}

.page-head__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: var(--fs-h3);
  line-height: var(--lh-h3);
  letter-spacing: -0.5px;
  color: var(--text-primary);
}

.page-head__desc {
  margin: 6px 0 0;
  /* 16px 中文＝LINE Seed（0626 站規「≥16px 一律 display stack」；原 text stack
     是規範改版前的漏網，0806 Dolly 抓到）。<16px 才用 --font-cjk-text。 */
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}

.page-head__info {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  flex-shrink: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  text-decoration: none;
  transition: color 160ms ease;
}

.page-head__info:hover { color: var(--text-primary); }

.page-head__info-icon {
  display: inline-block;
  width: 24px;
  height: 24px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/info.svg') center / contain no-repeat;
  mask: url('assets/icons/info.svg') center / contain no-repeat;
}

/* ─────────────────────────────────────────
   GLASS TAB NAV  (.glass-tabs)
   Pill-shaped segmented filter. Active item = yellow tint + yellow bold
   text. Mirrors Figma "Nav_Glass" (315:9438).
   ───────────────────────────────────────── */
.glass-tabs {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  /* Figma spacing: 12px below the title row, 24px above the card grid. */
  margin: 12px 0 24px;
  padding: 4px;
  background: var(--white-alpha-4);
  /* 2026-07-15 Dolly：不透明深灰(--neutral-700 #1e1e1e)不隨背景融合、顯突兀，
     改白色基底 8%（同 header 內容切換器外框）——純黑底亮度近等值，亮底自然變亮 */
  border: 1px solid var(--white-alpha-8);
  border-radius: var(--radius-pill);
  overflow-x: auto;
  scrollbar-width: none;
}
.glass-tabs::-webkit-scrollbar { display: none; }

.glass-tabs__item {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* WebKit resolves a <button> flex item's automatic minimum size to 0
     (Chrome keeps min-content), so in a too-narrow row iOS Safari crushes
     every tab (OTT logos squashed to 9px) instead of overflowing into the
     built-in horizontal scroll. Pin the tabs at their content size on both
     engines (2026-07-10, user-caught on the home ranking OTT row). */
  flex-shrink: 0;
  height: 40px;
  padding: 0 26px;
  border: none;
  background: transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-cjk-display);
  font-size: 16px;
  font-weight: var(--fw-regular);
  line-height: 24px;
  color: var(--text-secondary);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 160ms ease, color 160ms ease;
}

.glass-tabs__item:hover { color: var(--text-primary); }

/* Leading icon — mask uses currentColor so it tracks the tab text colour
   (灰 inactive / 白 hover / 黃 active). Only rendered where the span exists
   (片庫 放映廳/資料庫 toggle). */
.glass-tabs__icon {
  width: 18px;
  height: 18px;
  margin-right: 8px;
  flex: 0 0 auto;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}

/* Active tab wears the SAME orange L-kit glass as .btn--yellow-ghost (2026-07-22:
   was a copy of the OLD yellow-ghost recipe; now references the --lg-* material so
   the active toggle stays in sync with the page's orange buttons). */
.glass-tabs__item--active,
.glass-tabs__item--active:hover {
  color: var(--yellow-500);
  font-weight: var(--fw-bold);
  /* 0730：換 L 版 active 配方（52 檔 --lg-*-active，透明底＋細橘高光）——
     舊的實色橘底＋10 層橘 shine 在暗底上偏深、高光偏粗，與 endgame 對不上 */
  background: var(--lg-fill-active);
  box-shadow: var(--lg-shine-active);
}

/* Disabled tab (e.g. 我的片庫 while the page is unfinished): stays visible
   in the pill but is inert — markup drops the href, this kills hover/cursor.
   Remove .is-disabled + restore the href when the page ships. */
.glass-tabs__item.is-disabled,
.glass-tabs__item.is-disabled:hover {
  color: var(--text-secondary);
  opacity: 0.45;
  cursor: default;
  pointer-events: none;
}

/* Coming-soon cocreation card: no progress bar, just an anticipatory
   start-date line (yellow), pinned to the card bottom. */
.cocreation-card__upcoming {
  margin-top: auto;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  line-height: 1.6;
  font-weight: var(--fw-medium);
  color: var(--yellow-500);
}

/* ── PLAIN（次級）variant：拿掉外框膠囊、active 改「中性白玻璃」而非品牌橘。
   用在彈窗內等不該搶主色的次級篩選列（如送禮流程的好友分頁）。
   ※ .library-genres（29-movie-library.css）是同一配方的頁面版，未來可併入本
   variant；該檔另有 assets/rwd/*-mobile.css 覆寫，暫不動它。 */
.glass-tabs--plain {
  padding: 0;
  gap: 4px;
  background: none;
  border: none;
  border-radius: 0;
}
.glass-tabs--plain .glass-tabs__item {
  height: 32px;
  padding: 0 14px;
  font-size: 14px;
  line-height: 20px;
}
.glass-tabs--plain .glass-tabs__item--active,
.glass-tabs--plain .glass-tabs__item--active:hover {
  color: var(--text-primary);
  font-weight: var(--fw-bold);
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  backdrop-filter: var(--lg-frost);
  -webkit-backdrop-filter: var(--lg-frost);
}

@media (max-width: 768px) {
  .page-head { flex-wrap: wrap; gap: 8px; padding: 8px 0; }
  .glass-tabs { width: 100%; }
}

/* ─────────────────────────────────────────
   SCROLL-REVEAL FOUC GUARD  (content index pages: cocreate / news / activity)
   Pre-hide reveal targets so they don't flash before the reveal script runs.
   Active ONLY when <html> has .js-reveal-ready — added by the page's reveal
   IIFE and ONLY when GSAP is live + motion is allowed. If GSAP fails to load
   or the user prefers reduced motion, the class is never set and everything
   shows immediately. Distinct from index.html's .js-motion-ready so the two
   reveal systems never interfere.
   ───────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  .js-reveal-ready [data-reveal-section] .page-head__title,
  .js-reveal-ready [data-reveal-section] .page-head__desc,
  .js-reveal-ready [data-reveal-section] .page-head__info,
  .js-reveal-ready [data-reveal-section] .glass-tabs,
  .js-reveal-ready [data-reveal-section] .section-eyebrow,
  .js-reveal-ready [data-reveal-section] .section-header__title,
  .js-reveal-ready [data-reveal-section] .section-header__sub,
  .js-reveal-ready [data-reveal-section] .section-link,
  .js-reveal-ready [data-reveal-section] .popcorn-page__heading,
  .js-reveal-ready [data-reveal-section] .popcorn-balance-card,
  .js-reveal-ready [data-reveal-section] .popcorn-guide,
  .js-reveal-ready [data-reveal-stack] > * {
    opacity: 0;
  }
}

/* ─────────────────────────────────────────
   CO-CREATION CARD — lifecycle status states
   Mirrors Figma "Crowdfund-Card" states (node 165:33664). The status badge
   is a glass "ActivityTag": colour tint over a dark underlay so it reads on
   any surface. Footer swaps between metrics / date-line / CTA per state.
   ───────────────────────────────────────── */
.status-tag {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: flex-start;
  padding: 6px 12px;
  border: 0.8px solid transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  line-height: 18px;
  font-weight: var(--fw-medium);
  white-space: nowrap;
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}

.status-tag__icon {
  display: inline-flex;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

/* Movie icon (assets/icons/movie.svg) — mask so it follows the tag colour. */
.status-tag__icon--movie {
  background-color: currentColor;
  -webkit-mask: url('assets/icons/movie.svg') center / contain no-repeat;
  mask: url('assets/icons/movie.svg') center / contain no-repeat;
}

/* 計畫進行中 / 即將投入製作 / 製作中 */
.status-tag--yellow {
  background: linear-gradient(rgba(var(--brand-rgb), 0.12), rgba(var(--brand-rgb), 0.12)), rgba(0, 0, 0, 0.6);
  border-color: rgba(var(--brand-rgb), 0.3);
  color: var(--yellow-500);
}
/* 待退款 — 真黃（--warning-500 #fed200）。「--yellow」是品牌橘的歷史別名，
   給 計畫進行中 那族用；訂單顏色語意的「黃＝等待對方/時間性」要跟橘區隔
   （Dolly 2026-08-05，shop-states.html §2）。 */
.status-tag--warn {
  background: linear-gradient(rgba(254, 210, 0, 0.12), rgba(254, 210, 0, 0.12)), rgba(0, 0, 0, 0.6);
  border-color: rgba(254, 210, 0, 0.3);
  color: var(--warning-500);
}
/* 目標達成 / 已上架 */
.status-tag--green {
  background: linear-gradient(rgba(34, 197, 94, 0.12), rgba(34, 197, 94, 0.12)), rgba(0, 0, 0, 0.6);
  border-color: rgba(34, 197, 94, 0.3);
  color: var(--success-500);
}
/* 即將開放（亮灰）*/
.status-tag--soon {
  background: linear-gradient(rgba(163, 163, 163, 0.12), rgba(163, 163, 163, 0.12)), rgba(0, 0, 0, 0.6);
  border-color: rgba(163, 163, 163, 0.3);
  color: var(--text-secondary);
}
/* 已結束 / 已取消（暗灰）*/
.status-tag--ended {
  background: linear-gradient(rgba(115, 115, 115, 0.12), rgba(115, 115, 115, 0.12)), rgba(0, 0, 0, 0.6);
  border-color: rgba(115, 115, 115, 0.3);
  color: var(--text-tertiary);
}

/* Grey progress fill for ended / cancelled projects (no yellow glow). */
.cocreation-card__bar-fill--muted {
  background: var(--neutral-500);
  box-shadow: none;
}

/* Footer status line (yellow) — start date / release date. Optional leading icon. */
.cocreation-card__note {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-cjk-text);
  font-weight: var(--fw-medium);
  font-size: 16px;
  line-height: 27px;
  color: var(--yellow-500);
}
.cocreation-card__note-icon {
  display: inline-block;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/media-play.svg') center / contain no-repeat;
  mask: url('assets/icons/media-play.svg') center / contain no-repeat;
}

/* CTA pinned bottom-right (e.g. 已上架 → 立即收看). */
.cocreation-card__cta-row {
  margin-top: auto;
  display: flex;
  justify-content: flex-end;
}

/* News index — render the list as a 2-column grid so a long article list
   doesn't become one very tall single column, and rows aren't stretched to
   the full wide container. Collapses to 1 column on mobile. */
.news-list--grid {
  display: grid;
  /* minmax(0,…): plain 1fr can't shrink below the card's intrinsic
     width (image flex-basis 40% of a min-content row), which forced
     horizontal page overflow on narrow windows. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  column-gap: 40px;
}
@media (max-width: 1080px) {
  .news-list--grid { column-gap: 24px; }
}
/* Uniform row hairline + even top padding across both columns. */
.news-list--grid > .news-card--list {
  border-bottom: 1px solid var(--white-alpha-8);
}
.news-list--grid > .news-card--list:first-child { padding-top: 20px; }

@media (max-width: 768px) {
  .news-list--grid { grid-template-columns: 1fr; column-gap: 0; }
  .news-list--grid > .news-card--list:first-child { padding-top: 0; }
}

/* ─────────────────────────────────────────
   Z-ORIGIN (AI 原力創作計畫) — AIC Lobby (Figma 101:5666)
   Two card families on the shared page-head + glass-tabs layout:
   · .aic-card  — landscape 16:9 competition tile (image + bottom-gradient
                  overlay + status tag + title). Figma AIC-Unit (111:3157).
   · .work-card — portrait 2:3 featured work (poster + status badge + title +
                  creator row + optional vote button). Figma Voting-Desktop.
   ───────────────────────────────────────── */

/* Block spacing between the 徵集 section and the 精選作品 section. */
.zorigin-section + .zorigin-section { margin-top: 64px; }

/* Tabs row: glass-tabs on the left, an action (我的投票) pinned right. */
.tabs-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}

/* ── 徵集卡 grid (3-up landscape) ── */
.aic-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

.aic-card {
  position: relative;
  display: block;
  aspect-ratio: 343 / 193;
  border: 0.5px solid var(--white-alpha-8);
  border-radius: var(--radius-md);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  transition: transform 250ms ease;
}
/* 4th 徵集卡: shown only at ≥1680 (see wide-screen block) to fill the 4-col
   row; hidden by default so the 3-col layout stays a clean single row. */
.aic-card--xl-only { display: none; }
.aic-card:hover { transform: translateY(-4px); }
.aic-card:hover .aic-card__img { transform: scale(1.04); }

.aic-card__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 400ms ease;
}

/* Bottom gradient holding the tag + title (transparent → black 60%). */
.aic-card__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 12px 16px;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.6) 50%);
}

.aic-card__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: 16px;
  line-height: 24px;
  font-weight: var(--fw-bold);
  color: #fff;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Load-more — centred quiet text button + chevron. */
.load-more {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  width: 100%;
  margin-top: 24px;
  padding: 12px;
  border: none;
  background: transparent;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 160ms ease;
}
.load-more:hover { color: var(--text-primary); }
.load-more__chev { width: 16px; height: 16px; flex-shrink: 0; }

/* ── 精選作品 grid (5-up portrait poster) ── */
.works-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));   /* minmax(0,…) keeps columns
    truly equal — plain 1fr (= minmax(auto,1fr)) lets a column grow to its content's
    min-width, which was inflating column 1 (submit tile + the landscape work-06). */
  gap: 24px;
}

.work-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  text-decoration: none;
  color: inherit;
}

.work-card__poster {
  position: relative;
  display: block;
  aspect-ratio: 256 / 384;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.work-card__poster img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 300ms ease;
}
.work-card:hover .work-card__poster img { transform: scale(1.03); }

/* Status badge bottom-right of the poster (reuses .status-tag glass styles). */
.work-card__badge {
  position: absolute;
  right: 11px;
  bottom: 11px;
}

/* 優勝 — solid yellow winner badge with ink text. */
.status-tag--win {
  background: var(--yellow-500);
  border-color: transparent;
  color: #0a0a0a;
}

/* Info block below the poster. */
.work-card__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: 16px;
  line-height: 24px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.work-card__meta {
  display: flex;
  align-items: center;
  gap: 8px;
}
.work-card__creator {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  flex: 1;
}
.work-card__avatar {
  width: 24px;
  height: 24px;
  border-radius: 50%;
  flex-shrink: 0;
  object-fit: cover;
}
.work-card__creator-name {
  font-family: var(--font-latin);
  font-size: 14px;
  line-height: 23px;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Vote button is a real .btn (btn--yellow-ghost for 投票, btn--ghost for
   改投・收回); this only stops it shrinking in the meta row. */
.work-card__vote { flex-shrink: 0; }

/* 即將截止 — Error glass tag (DS ActivityTag/Error 640:1776): rose tint over
   black + error-red text. Distinct from the highlight-rose ramp. */
.status-tag--error {
  background: linear-gradient(rgba(243, 53, 85, 0.12), rgba(243, 53, 85, 0.12)), rgba(0, 0, 0, 0.6);
  border-color: rgba(243, 53, 85, 0.3);
  color: var(--error-500);
}

/* Small mask icon inside a button — follows the button text colour. */
.btn__icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}

/* ── Z-ORIGIN responsive ── */
@media (max-width: 1100px) {
  .works-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 900px) {
  .aic-grid { grid-template-columns: repeat(2, 1fr); }
  .works-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (max-width: 600px) {
  .aic-grid { grid-template-columns: 1fr; }
  .works-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tabs-row { flex-wrap: wrap; }
}

/* ─────────────────────────────────────────
   MOVIE LIBRARY (片庫) — 放映廳 / 資料庫
   Reuses .films-rail + .film-card from the homepage 片庫 section. Cards are
   poster-only here (Figma Movie Library 316:13155 / 316:10158): 放映廳 shows a
   popcorn rental price + optional 最新 ribbon; 資料庫 is plain poster (info only).
   Top bar = view toggle + search; a genre chip row sits above the rails.
   ───────────────────────────────────────── */

/* Library pages lead with a small toggle (no page-head title), so the default
   32px flush-top gap reads as wasted space — pull the whole block up closer
   to the header. */
.section--library { padding-top: 24px; }

/* Top bar: 放映廳/資料庫 toggle, sitting left. */
.library-topbar {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 24px;
}
/* The toggle borrows base .glass-tabs (12/24 vertical margin); zero it here so
   it doesn't stack on top of the section + topbar spacing. */
.library-topbar .glass-tabs { margin: 0; }

/* Search pill (visual only for now). Mirrors Figma "Search Bar" (343 wide). */
.library-search {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: 343px;
  max-width: 100%;
  height: 44px;
  padding: 0 16px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-4);
  border: 1px solid var(--neutral-700);
}
.library-search__icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/search.svg') center / contain no-repeat;
  mask: url('assets/icons/search.svg') center / contain no-repeat;
}
.library-search__input {
  flex: 1;
  min-width: 0;
  border: none;
  background: none;
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: 14px;
}
.library-search__input::placeholder { color: var(--text-tertiary); }
.library-search__input:focus { outline: none; }

/* Content-category row — a tier BELOW the 放映廳/資料庫 toggle, so it reads as
   secondary filtering: smaller text, no glass-pill container, and an active
   state that's a flat neutral chip (no yellow, no glow, no blur). Scoped to
   .library-genres so the toggle's glass look is untouched. */
.library-genres {
  margin: 0 0 16px;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
  gap: 4px;
}
.library-genres .glass-tabs__item {
  height: 32px;
  padding: 0 14px;
  font-size: 14px;
  line-height: 20px;
}
/* active = 白色 ghost（.btn--ghost 同配方：白漸層＋白光暈 inset＋玻璃模糊，
   白字非黃字）——2026-07-13 Dolly 拍板：放映廳分類列與我的片庫狀態列一起改，
   取代原本的中性淺灰 chip；片庫 toggle 維持黃 ghost 不動；
   手機端 assets/rwd/*-mobile.css 的覆寫不在此列，維持原樣。 */
.library-genres .glass-tabs__item--active,
.library-genres .glass-tabs__item--active:hover {
  color: var(--text-primary);
  font-weight: var(--fw-bold);
  /* neutral L-kit glass (= .btn--ghost). Was a copy of the old ghost recipe. */
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  backdrop-filter: var(--lg-frost);
  -webkit-backdrop-filter: var(--lg-frost);
}
/* Categories not yet open (綜藝 / 動畫) read dimmer, matching V1. */
.library-genres .glass-tabs__item--muted {
  color: var(--text-tertiary);
  cursor: default;
}
.library-genres .glass-tabs__item--muted:hover { color: var(--text-tertiary); }

/* Platform-filter variant (資料庫): each item carries a brand logo + label.
   Logos are pre-rounded app icons, shown small so the row stays a quiet
   secondary filter. */
.library-platforms .glass-tabs__item {
  gap: 8px;
  padding-left: 8px;
  padding-right: 14px;
}
.glass-tabs__logo {
  width: 22px;
  height: 22px;
  flex-shrink: 0;
  border-radius: 5px;
  object-fit: cover;
}
/* 臺灣排行 section overrides (scoped — leaves cocreate filter tabs / database
   untouched):
   1. tighten the title → platform-tabs gap
   2. active platform tab uses the neutral GHOST glass instead of yellow-ghost,
      so it never clashes with an OTT's brand color when selected. The Ztor
      tab keeps its yellow Z. chip (that's its logo, not the active treatment). */
[data-screen-label="ranking"] .section-header { margin-bottom: var(--space-5); }
/* Ranked cards that have a title row below the poster (現正熱播 on index,
   本週前 10 名 on database, …) raise the digit by the body height (~66px) to
   land its base on the poster bottom — matching the poster-only ranked rails
   which ground at the card bottom. Keyed on the presence of __body (not a page
   id) so every ranked-with-title instance aligns identically, no seams. */
.film-card--ranked:has(.film-card__body) .film-card__rank { bottom: 50px; }
/* Platform pill spans the full content width; the options stay left-packed
   (natural order, widened gap) so the right side is just empty pill bg. */
.ranking-platforms {
  margin-top: 0;
  display: flex;
  width: 100%;
  justify-content: flex-start;
  gap: 16px;
}
.ranking-platforms .glass-tabs__item--active,
.ranking-platforms .glass-tabs__item--active:hover {
  color: var(--text-primary);
  /* neutral L-kit glass (= .btn--ghost), matching the library genre tabs. */
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  backdrop-filter: var(--lg-frost);
  -webkit-backdrop-filter: var(--lg-frost);
}

.library-row + .library-row { margin-top: 40px; }
/* Category title sits tight above its rail. */
.library-row .page-head { margin-bottom: 8px; }

/* 繼續觀看 rail — 只在登入後顯示;標題列右側放「觀看紀錄 →」link. */
body[data-auth="logged-out"] .library-row--continue { display: none; }
/* 登出時 continue 列雖不渲染仍算 + 選擇器的兄弟——下一列歸零，間距才與資料庫頁一致 */
body[data-auth="logged-out"] .library-row--continue + .library-row { margin-top: 0; }
.library-row--continue .page-head { justify-content: space-between; }

/* Corner ribbon on a poster (最新 / 推薦) — dark glass pill + brand-yellow label.
   Glass (not a yellow fill) so it reads on any poster art and keeps yellow as the
   "act here" CTA signal. White-on-blur fallback where backdrop-filter is unsupported. */
.film-card__ribbon {
  position: absolute;
  /* 外擴 1px 塞進海報的 overflow:hidden 圓角裁切裡，左上角由父層裁——
     不再自畫圓角去對弧線（backdrop-filter 會逃逸裁切、自畫弧永遠差
     次像素出縫，2026-07-09 三輪教訓）。代價＝放棄玻璃模糊，改用本就
     存在的實色 fallback 墨色（下方 @supports 區塊同值）。 */
  top: -1px;
  left: -1px;
  z-index: 2;
  padding: 4px 8px;
  border-bottom-right-radius: var(--radius-md);
  background: rgba(10, 10, 10, 0.86);
  /* hairline 只描露在海報上的右、下兩邊 */
  border-right: 1px solid rgba(255, 255, 255, 0.1);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  color: var(--yellow-500);                              /* brand yellow #ffb601 — the label is the brand tie */
  font-family: var(--font-cjk-text);
  font-size: 12px;
  line-height: 18px;
  font-weight: var(--fw-medium);
}
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .film-card__ribbon { background: rgba(10, 10, 10, 0.86); }  /* solid ink so yellow stays legible */
}

@media (max-width: 768px) {
  .library-topbar { flex-wrap: wrap; }
  .library-search { width: 100%; }
}

/* ─────────────────────────────────────────
   WIDE-SCREEN TWEAKS  ─  single breakpoint @ 1680px
   Above 1680px the default 1440-capped layout leaves big side gutters, so
   per-page we either grow the container (共創 / zorigin — more columns) or go
   full-bleed (片庫 — Netflix/YouTube style). News / events keep the 1440 cap
   (container--wide untouched there) per request. Placed at file end so these
   overrides win the cascade over the base grid definitions above.
   ───────────────────────────────────────── */
@media (min-width: 1680px) {

  /* 共創 + zorigin: grow the container well past 1440 so the extra column keeps
     the cards near their 1440 size (≈432px at 3-col → ≈418px at 4-col here),
     not shrunken. Contained, not full-bleed — caps at 1840 then centres. */
  .container--wide-grow { max-width: 1840px; }

  /* Scoped to .container--wide-grow so ONLY the cocreate / zorigin pages widen.
     The 焦點 home previews reuse .cocreation-grid in a normal container and must
     stay 3-up (otherwise 3 preview cards leave an empty 4th column). */
  /* 影視共創頁: 共創卡 3 → 4 */
  .container--wide-grow .cocreation-grid { grid-template-columns: repeat(4, 1fr); }

  /* AI 原力創作計畫: 活動卡 3 → 4，精選作品 5 → 6 */
  .container--wide-grow .aic-grid   { grid-template-columns: repeat(4, 1fr); }
  .container--wide-grow .works-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
  /* The 4th 徵集卡 only exists to fill the 4-col row here — hidden below 1680
     so the 3-col layout doesn't leave an orphan on a second row. */
  .container--wide-grow .aic-card--xl-only { display: block; }

  /* 片庫: full-bleed Netflix/YouTube feel — drop the 1440 cap, keep the 48px
     side padding (from .container). At this width 6 posters are too big, so the
     rails go to 8 across (ranked Top-N shelf to 6). Scoped to .section--library
     so the 焦點 home rail (also .films-rail, but 1440-capped) stays untouched. */
  .section--library .container--wide { max-width: none; }
  .section--library .films-rail__track { --rail-cols: 8; }
  .section--library .films-rail__track--ranked { --rail-cols: 6; }
}

/* ─────────────────────────────────────────
   MATERIAL FALLBACKS — liquid glass degrades honestly
   ───────────────────────────────────────── */

/* No frost without contrast: solid fill replaces transparency */
@media (prefers-reduced-transparency: reduce) {
  .btn--ghost,
  .btn--yellow-ghost,
  .btn--destructive,
  .btn--quiet,
  .icon-btn {
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: rgba(20, 20, 20, 0.95);
  }
}

/* Motion collapses to instant state changes */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* ─────────────────────────────────────────
   TOP-10 RANKING — two fixed rows of five
   Scoped to the homepage ranking section only; other ranked
   rails (library 現正熱播) keep their horizontal scroll.
   ───────────────────────────────────────── */
section[data-screen-label="ranking"] .films-rail__track--ranked {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  row-gap: 40px;                 /* room for the oversized rank digits */
  overflow: visible;             /* rank "1" overhang no longer clipped */
  scroll-snap-type: none;
  margin-left: 0;
  padding-left: 24px;            /* keep the #1 digit clear of the edge */
}

section[data-screen-label="ranking"] .film-card {
  width: auto;                   /* grid cell sizes the card, not --card-w */
  flex: none;
}

/* Rail arrows are pointless on a non-scrolling grid */
section[data-screen-label="ranking"] .films-rail__nav { display: none !important; }

@media (max-width: 768px) {
  section[data-screen-label="ranking"] .films-rail__track--ranked {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* ─────────────────────────────────────────
   MOBILE TWEAKS — single breakpoint @ 768px
   IMPORTANT: this block MUST stay at the END of the file. It overrides
   component rules of equal specificity, so it only wins by source order.
   (It previously sat mid-file and every rule below its components was
   silently dead — do not move it back up.)
   Quick adaptation to make the desktop layout legible on phones for
   demo. Not a full mobile design — future sprint will redesign per
   section (proper nav, hero crop, card hierarchy, etc.).
   ───────────────────────────────────────── */

@media (max-width: 768px) {

  /* Tighter horizontal padding for all .container / sections */
  :root { --container-padding-x: 20px; }

  /* Header — hide nav links, keep logo + actions; tighter padding */
  .header__nav-links { display: none; }
  .header { padding-inline: 16px; }
  .header__actions { gap: 12px; }

  /* Section rhythm — smaller vertical padding */
  .section { padding-block: clamp(48px, 12vw, 72px); }

  /* Section header — stack title row, scale down heading */
  .section-header { margin-bottom: 36px; }
  .section-header__title { font-size: clamp(28px, 7vw, 36px); line-height: 1.3; }
  .section-header__row {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }

  /* Hero carousel — shorter, scaled text */
  .hero-carousel { height: clamp(420px, 70vw, 560px); }
  .hero-carousel__title { font-size: clamp(28px, 7.5vw, 40px); }
  .hero-carousel__sub { font-size: 14px; }
  .hero-carousel__ctas { gap: 10px; }
  .hero-carousel__nav--prev { left: 12px; }
  .hero-carousel__nav--next { right: 12px; }

  /* 影視共創 — 3 col → 1 col */
  .cocreation-grid { grid-template-columns: 1fr; gap: 20px; }

  /* AI 原力 — 2 col → 1 col (mosaic 內保持 2 col) */
  .ai-mosaic { grid-template-columns: 1fr; gap: 28px; }

  /* 電影庫 — rail shows ~2.4 cards (peek next); ranked ~1.5.
     Rank digit size/offsets need no mobile override anymore — they're
     in cqi units and scale with the card automatically. */
  .films-rail__track { --rail-cols: 2.4; --rail-gap: 12px; }
  .films-rail__track--ranked { --rail-cols: 1.5; }
  .film-card { --card-w: 144px; }   /* smaller on phones (responsive, not drift) — ~2.4 cards peek */
  .film-card--ranked { --card-w: calc(144px + 48px); }   /* poster stays 144px; 48px rank gutter */
  .films-rail__tabs { overflow-x: auto; scrollbar-width: none; }
  .films-rail__tabs::-webkit-scrollbar { display: none; }
  .film-card--ranked { padding-left: 48px; }

  /* 社群 — 3 col → 1 col */
  .community-grid { grid-template-columns: 1fr; gap: 20px; }

  /* 新聞 — 2 col → stack; list card 圖左 38% */
  .news-grid { grid-template-columns: 1fr; gap: 28px; }
  .news-card--hero .news-card__title { font-size: 20px; }
  .news-card--hero .news-card__body { padding: 20px; }
  .news-card--list { gap: 14px; padding: 16px 0; }
  .news-card--list .news-card__cover { flex: 0 0 38%; }
  .news-card--list .news-card__title { font-size: 14px; line-height: 1.45; }

  /* 同場加映 — 3 col → 1 col, card 自然 hug */
  .activity-grid { grid-template-columns: 1fr; gap: 16px; }
  .activity-card { min-height: auto; padding: 24px; }

  /* Footer — 5 col → 1 col stack */
  .site-footer { padding: 56px 0 24px; }
  .site-footer__top { grid-template-columns: 1fr; gap: 28px; }
  .site-footer__brand { max-width: 100%; }
  .site-footer__divider { margin: 32px 0 20px; }
  .site-footer__bottom-row {
    flex-direction: column;
    align-items: flex-start;
    gap: 16px;
  }
  .site-footer__notice { max-width: 100%; }
}

/* ─────────────────────────────────────────
   SIDE RAIL — 主導覽 (board variant, 2026-06-11)
   Replaces the horizontal header nav. A fixed near-black rail on the
   left: wordmark up top, seven destinations as icon + 2-character
   label, nothing else. The header stays as a slim utility bar
   (search / language / login) with its nav links hidden.
   Motion per Animation Director spec: zero entrance animation (a rail
   seen 100+ times/day renders in place), opacity-only hover, static
   active state, instant focus. Icons are the existing house set via
   mask — no foreign icon family.
   ───────────────────────────────────────── */

:root { --rail-w: 88px; }   /* L endgame rail width (ported from framework-migration; was 100) */

body.has-rail { padding-left: var(--rail-w); }

/* Header is the full-width top deck: identity (left), persistent
   search (center), utilities (right). Nav links stay hidden — the rail
   below owns navigation. The negative margin cancels the body's rail
   offset so the bar spans edge to edge. */
body.has-rail .header__nav-links { display: none; }
body.has-rail .header { margin-left: calc(-1 * var(--rail-w)); }
body.has-rail .header__logo,
body.has-rail .header__logo-img { height: 32px; }  /* identity reads at deck width */
/* The wordmark's z sits flush over the rail icons' left edge.
   Icon edge = (rail 84 − item 64)/2 + (item 64 − icon 22)/2 = 31px from
   the viewport; subtract the container padding the header applies. */
body.has-rail .header__logo {
  margin-left: calc((var(--rail-w) - 72px) / 2 + 23px - var(--container-padding-x));
}
/* The icon-button search is absorbed by the persistent field.
   Keyed on the --search modifier, NOT [aria-label="搜尋"]: i18n rewrites the
   aria-label to "Search" in English, which would break an attribute selector
   and leak the icon back onto desktop. */
body.has-rail .header__icon-btn--search { display: none; }

/* Persistent search — an important feature deserves a field, not an
   icon. Long on purpose: it owns the header's center. */
.header-search {
  /* The field scales with the window: grows to 820px on wide decks,
     shrinks before anything else hides. min-width is the floor where
     a search field stops being usable. */
  flex: 1 1 160px;
  min-width: 130px;
  max-width: 720px;   /* 配合社群頁下滑時搜尋欄的置中感(Dolly sidebar 版) */
  height: 40px;
  margin: 0 clamp(12px, 3vw, 56px);
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 18px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid var(--white-alpha-16);
  transition: border-color 150ms cubic-bezier(0.23, 1, 0.32, 1),
              background-color 150ms cubic-bezier(0.23, 1, 0.32, 1);
}
.header-search:focus-within {
  border-color: var(--yellow-500);
  background: rgba(255, 255, 255, 0.12);
}
.header-search__icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  background: var(--neutral-400);
  -webkit-mask: url('assets/icons/search.svg') center / contain no-repeat;
          mask: url('assets/icons/search.svg') center / contain no-repeat;
}
.header-search:focus-within .header-search__icon { background: var(--yellow-500); }
.header-search__input {
  flex: 1;
  min-width: 0;
  background: transparent;
  border: none;
  outline: none;
  color: var(--neutral-white);
  /* Display tier — search bar is header chrome (Nav/Item family), so CJK
     renders in LINE Seed like the web/sidebar build (was --font-cjk-text=Noto).
     Token, not a hardcoded 'LINE Seed TW' string: Latin/numbers stay Proxima. */
  font-family: var(--font-cjk-display);
  font-size: 16px;
}
.header-search__input::placeholder { color: var(--neutral-400); }
.header-search__input::-webkit-search-cancel-button { display: none; }

/* Centre the search field's AXIS on the CONTENT column, not the viewport
   (design call 2026-06-18: centre-alignment chosen over keeping the field
   a full 720 wide). The header spans edge to edge while the body content
   is pushed right by the rail, so the centred content — e.g. the logged-in
   community feed, max-width 720 — sits at 50vw + rail/2 (= 770px @1440).
   Plain flex centring would land the field on 50vw, visibly left of that.

   We pin the field to the content axis with absolute positioning, then cap
   its width so it never collides with the right-hand utility cluster.
   Width = viewport − 2·(header pad + utilities + clearance + rail/2), capped
   at the original 720 on wide decks. The side clusters keep their natural
   flex — logo hugs left, utilities hug right.

   ⚠️ 2026-08-01：原本這裡寫死 `100vw - 910px`，兩個常數都過期了——右側工具列
   322 → 實測 366（0731 全站加上購物車鈕），rail 100 → 88。結果 1280〜1440 只
   剩 1〜6px 間距、1512〜1600 直接負值（搜尋欄壓到工具列上）。改成用變數逐項
   表示，之後工具列增減只要改 --header-utils-w 一個值，並且自動跟著
   --container-padding-x／--rail-w 走。量測法＝取「可見的那一份」header markup
   （兩套 markup：未登入／已登入），比對 .header-search 右緣與 .header__actions 左緣。 */
body.has-rail .header__inner { position: relative; }
body.has-rail .header-search {
  /* 右側工具列實測最寬值（未登入 366 / 已登入 364，取大者）＋兩者之間的呼吸 */
  --header-utils-w: 366px;
  --header-search-clearance: 32px;
  position: absolute;
  left: calc(50% + var(--rail-w) / 2);
  top: 50%;
  transform: translate(-50%, -50%);
  margin: 0;
  width: min(720px, 100vw - 2 * (var(--container-padding-x) + var(--header-utils-w)
                                 + var(--header-search-clearance) + var(--rail-w) / 2));
}

.side-rail {
  position: fixed;
  inset: var(--header-height) auto 0 0;
  width: var(--rail-w);
  z-index: 90;  /* beneath the full-width header (100) */
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 20px 0;   /* rail items start level with the 放映廳 toggle */
  background: var(--neutral-900);
  border-right: 1px solid var(--border-subtle);  /* same token as the header hairline — one line, not two */
}

/* Logo lives in the header now — the rail is navigation only. */
.side-rail__logo { display: none; }

.side-rail__nav {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 100%;
  /* ⚡ 0731：這裡原本是 overflow-y:auto。CSS 規則是「一軸非 visible，另一軸
     就從 visible 變成 auto」——所以 nav 變成雙軸捲動容器，而 hover 說明泡泡
     ([data-tip]::after 掛在 left:calc(100% + 14px)) 伸出 rail 右緣約 117px，
     等於幫 nav 造了一條看不見的橫向捲軸（scrollbar 又被 display:none 藏掉）。
     後果兩個：泡泡被裁成半截；trackpad 兩指一滑就把整排 icon 推出左緣、
     滑不回來。改回 visible ⇒ 泡泡完整飛出、也不會有幽靈橫捲。 */
  overflow: visible;
  overscroll-behavior: contain;
  scrollbar-width: none;
}
.side-rail__nav::-webkit-scrollbar { display: none; }
/* 視窗矮到裝不下六個項目＋底部「＋」時才需要縱向捲動（實測 578px 以下 foot
   會被切）。此時 nav 必須是捲動容器，泡泡就會被裁——刻意取捨：被切掉的
   ＋ 按鈕比被裁的 hover 泡泡嚴重。overflow-x:clip 用值為 hidden，
   擋掉使用者的橫向滑動，避免上面那個推出左緣的雷再現。 */
@media (max-height: 620px) {
  .side-rail__nav { overflow-y: auto; overflow-x: clip; }
}

.side-rail__item {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  width: 68px;
  padding: 10px 0;
  border-radius: var(--radius-md);
  color: var(--neutral-white);
  opacity: 0.64;
  text-decoration: none;
  transition: opacity 100ms cubic-bezier(0.23, 1, 0.32, 1),
              transform 120ms cubic-bezier(0.23, 1, 0.32, 1);
}

@media (hover: hover) and (pointer: fine) {
  .side-rail__item:hover {
    opacity: 1;
    transition-duration: 150ms, 120ms; /* enter 150ms, exit (above) 100ms */
  }
}

.side-rail__item:active { transform: scale(0.96); }

.side-rail__item:focus-visible {
  outline: 2px solid var(--yellow-500);
  outline-offset: -2px;
  border-radius: 8px;
}

/* Active destination: brand ink on a soft pill behind the glyph (per
   L's mock, 2026-06-11 — replaces the earlier edge bar). Static on
   purpose — each page load is fresh; animating it would be an entrance
   animation by the back door. */
.side-rail__item[aria-current="page"] {
  color: var(--yellow-500);
  opacity: 1;
}
.side-rail__item[aria-current="page"]::before {
  content: '';
  position: absolute;
  left: -10px;             /* item is inset (88−68)/2 = 10px; bar hugs the rail edge */
  top: 50%;
  transform: translateY(-50%);
  width: 3px;
  height: 38px;
  border-radius: 0 2px 2px 0;
  background: var(--yellow-500);
}

.side-rail__icon {
  /* --icon-size: optical correction hook. The filled play glyph (焦點)
     carries more ink than its stroke siblings; it renders at 19px so
     the seven icons share one visual weight. */
  width: var(--icon-size, 24px);
  height: var(--icon-size, 24px);
  background: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}

.side-rail__label {
  font-family: var(--font-cjk-display);
  font-size: 13px;
  line-height: 1;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* Full program names return on hover — the two flagship programs
   (影視共創計畫 / AI 原力創作計畫) compress to 共創 / 創作 in the rail,
   so the flyout restores the differentiator. Only items with a
   data-tip carry one. */
.side-rail__item[data-tip]::after {
  content: attr(data-tip);
  position: absolute;
  left: calc(100% + 14px);
  top: 50%;
  transform: translate(-4px, -50%);
  padding: 7px 12px;
  border-radius: var(--radius-pill);
  background: rgba(10, 10, 10, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--neutral-white);
  font-family: var(--font-cjk-display);
  font-size: 12px;
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms cubic-bezier(0.23, 1, 0.32, 1),
              transform 150ms cubic-bezier(0.23, 1, 0.32, 1);
}
@media (hover: hover) and (pointer: fine) {
  .side-rail__item[data-tip]:hover::after {
    opacity: 1;
    transform: translate(0, -50%);
  }
}

/* Rail foot — divider + 發表 button after the nav items (ported from
   Dolly's sidebar build). The + reuses the glass FAB language at rail scale. */
.side-rail__foot {
  margin-top: 14px;
  width: calc(100% - 24px);
  display: flex;
  justify-content: center;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.10);
}
.side-rail__post {
  width: 40px;
  height: 40px;
  padding: 0;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.side-rail__post .fab__icon { width: 18px; height: 18px; }

@media (prefers-reduced-motion: reduce) {
  .side-rail__item { transition-duration: 100ms; }
  .side-rail__item:active { transform: none; }
  .side-rail__item[data-tip]::after { transition: opacity 100ms ease; transform: translate(0, -50%); }
}

/* ─────────────────────────────────────────
   HEADER CONTENT SWITCHER — ALL / Music / Films
   Reference: Spotify Home's content-type chips (All/Music/Podcasts),
   re-spoken in the house language: one pill container, a single yellow
   thumb that slides between three equal segments (Animation Director
   spec — transform-only, 200ms house ease, retargets cleanly).
   Presentational in this static preview; selection is local state.
   ───────────────────────────────────────── */
.header-switch {
  --seg: 0;
  position: relative;
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
  margin-left: 20px;
  padding: 3px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.07);
  border: 1px solid var(--white-alpha-8);
}
.header-switch__thumb {
  position: absolute;
  left: 3px;
  top: 3px;
  bottom: 3px;
  width: 64px;
  border-radius: var(--radius-pill);
  background: var(--yellow-500);
  transform: translateX(calc(var(--seg) * 100%));
  transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1);
}
.header-switch__btn {
  position: relative;
  z-index: 1;
  width: 64px;
  height: 30px;
  border: none;
  background: transparent;
  border-radius: var(--radius-pill);
  font-family: var(--font-latin);
  font-size: 13px;
  font-weight: var(--fw-medium);
  color: rgba(255, 255, 255, 0.55);
  cursor: pointer;
  transition: color 200ms cubic-bezier(0.23, 1, 0.32, 1),
              transform 120ms cubic-bezier(0.23, 1, 0.32, 1);
}
@media (hover: hover) and (pointer: fine) {
  .header-switch__btn:not(.is-active):hover { color: rgba(255, 255, 255, 0.92); }
}
.header-switch__btn:active { transform: scale(0.97); }
.header-switch__btn.is-active { color: var(--yellow-ink); }
.header-switch__btn:focus-visible {
  outline: 2px solid var(--yellow-500);
  outline-offset: 2px;
  transition: none;
}
/* Active-segment colour follows the content vertical (same --cat tokens as the
   category pills): 全部 = brand yellow (default) · 音樂 = red · 影視 = orange.
   Driven PURELY by which tab carries .is-active (the switch behaviour already
   toggles it on every page), so the colour lives entirely in this shared
   stylesheet — no per-page JS, no data-* hook. Buttons order: 全部 / 音樂 / 影視. */
.header-switch:has(.header-switch__btn:nth-of-type(2).is-active) .header-switch__thumb { background: var(--cat-music); }  /* 音樂 */
.header-switch:has(.header-switch__btn:nth-of-type(3).is-active) .header-switch__thumb { background: var(--cat-film); }   /* 影視 */
/* On a coloured thumb the active label flips to white (dark ink only reads on yellow). */
.header-switch:has(.header-switch__btn:nth-of-type(2).is-active) .header-switch__btn.is-active,
.header-switch:has(.header-switch__btn:nth-of-type(3).is-active) .header-switch__btn.is-active { color: #fff; }

@media (prefers-reduced-motion: reduce) {
  .header-switch__thumb { transition-duration: 0.01ms; }
  .header-switch__btn { transition: color 100ms ease; }
  .header-switch__btn:active { transform: none; }
}
/* Narrowing ladder: the search compresses first (flex above), then the
   switcher tightens its segments, then the plans pill steps aside.
   The switcher itself never disappears — it's navigation-level state. */
@media (max-width: 1240px) {
  .header-switch { margin-left: 14px; }
  .header-switch__btn { width: 54px; font-size: 12px; }
  .header-switch__thumb { width: 54px; }
}
@media (max-width: 1080px) {
  body.has-rail .header__buttons .popcorn-section { display: none; }
}
@media (max-width: 880px) {
  body.has-rail .header__inner { gap: 12px; }
  body.has-rail .header__logo,
  body.has-rail .header__logo-img { height: 30px; }
}

/* ─────────────────────────────────────────
   TITLE DETAIL  ·  作品詳情頁 (title.html)
   Netflix-style work/film detail page. Built from the Figma
   "Funding+Movie" frame, re-skinned to the 2.0 design system
   (brand orange, dark canvas, CJK+Proxima type, glass).

   All values are tokens. Components here are page-agnostic
   (no body.rf-* keying) so they can move to other pages.
   New components: detail-hero, detail-head, crowdfund-banner,
   avatar-group, cast-card, media-card, comment.
   Reuses verbatim: .btn, .icon-btn, .ztor-score, .post-card,
   .film-card__poster pattern, .section-eyebrow.
   ───────────────────────────────────────── */

/* ===== HERO ============================================== */
.detail-hero {
  position: relative;
  isolation: isolate;
  /* the backdrop is scaled 1.08 (blur-feather cover) so it overspills the
     viewport sides — clip horizontally so the page can't scroll left/right.
     overflow-x:clip (not hidden) keeps overflow-y visible (poster shadow) and
     doesn't turn the page into a scroll container (sticky header stays put). */
  overflow-x: clip;
}

/* full-bleed background image behind the contained content;
   fades down into the page canvas (Netflix-style) */
.detail-hero__bg {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 480px;
  z-index: -1;
  background-position: center top;
  background-size: cover;
  background-repeat: no-repeat;
  /* soft-focus backdrop (劇照) — blur reads as "若隱若現"; scale past the
     edges so the blur feather never reveals the page canvas behind it. */
  filter: blur(10px);
  transform: scale(1.08);
  transform-origin: center top;
}
.detail-hero__bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    linear-gradient(90deg, rgba(10,10,10,0.92) 0%, rgba(10,10,10,0.55) 42%, rgba(10,10,10,0.30) 100%),
    linear-gradient(180deg, rgba(10,10,10,0.30) 0%, rgba(10,10,10,0.55) 56%, var(--bg-primary) 100%);
}

.detail-hero__inner {
  padding-top: 20px;
  padding-bottom: 8px;
}

/* ── back bar ── */
.detail-back {
  display: flex;
  align-items: center;
  gap: 16px;
  height: 56px;
}
/* chevron + label are one clickable nav (history back) */
.detail-back__nav {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  text-decoration: none;
  transition: opacity 150ms var(--ease-out);
}
.detail-back__nav:hover { opacity: 0.82; }
/* back ‹ = .icon-btn.icon-btn--sm.icon-btn--plain (close style) ·
   more ⋯ = .icon-btn.icon-btn--ghost — set on the markup, see ds.css variants. */
/* ⋯ 尺寸對齊返回圓鈕 32px（Dolly 0727 晚場：hover 圈不能比 ← 大）。
   ghost:hover 只蓋 background 沒蓋 border → base .icon-btn:hover 的玻璃描邊
   會漏出來畫圈；圈可以留（桌機平時仍裸 icon），但尺寸必須同 --sm */
.detail-back .icon-btn--ghost { width: 32px; height: 32px; padding: 7px; }
.detail-back .icon-btn--ghost .ds-icon { width: 18px; height: 18px; }
.detail-back__title {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.detail-back__spacer { flex: 1; }

/* ── head: poster + details ── */
.detail-head {
  display: flex;
  gap: 40px;
  align-items: flex-start;
  padding-top: 8px;
}
.detail-head__poster {
  flex: 0 0 240px;
  width: 240px;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-lg);
  object-fit: cover;
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
  box-shadow: 0 24px 60px rgba(0,0,0,0.5);
}
.detail-head__main {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 24px;
}

/* title row */
.detail-titlebar {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
.detail-titlebar__lead { flex: 1 1 auto; min-width: 0; }
.detail-title {
  margin: 0;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  font-family: var(--font-cjk-display);
  font-size: 40px;
  line-height: var(--lh-h1);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  letter-spacing: -0.01em;
}
.detail-subtitle {
  margin: 6px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-secondary);
}
/* 區塊標題可點（創作團隊 → team.html 內頁，Dolly 0729）：外觀同純標題、不變色不底線，
   游標與鍵盤焦點行為由 <a> 自帶 */
.detail-section__titlelink { color: inherit; text-decoration: none; }

/* 簡介「更多」（Dolly 0729）：桌機 >3 行才截斷、鈕釘在第 3 行行尾，點擊展開/「收合」
   （ds.js detail-desc 量測，真超行才加 .has-more）。被切的字尾用 mask 淡成透明——
   底是 hero 糊化劇照沒有實色可蓋，同 shortpanel 配方。
   手機不截斷（簡介在資訊分頁顯示全文），clamp 全 gate 在 ≥769。 */
.detail-desc { position: relative; }
.detail-more-link {
  display: none;                 /* 量測到真超過 3 行（.has-more）才現身 */
  border: none;
  background: none;
  padding: 0;
  font: inherit;
  color: var(--brand-500);
  cursor: pointer;
  white-space: nowrap;
}
.detail-more-link:hover { text-decoration: underline; }
@media (min-width: 769px) {
  .detail-desc__text {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
  }
  /* 展開＝全文；span 轉 inline 讓「收合」貼在最後一個字後面 */
  .detail-desc.is-expanded .detail-desc__text {
    display: inline;
    -webkit-line-clamp: unset;
    overflow: visible;
  }
  .detail-desc.has-more .detail-more-link { display: inline; }
  .detail-desc.has-more:not(.is-expanded) .detail-more-link {
    position: absolute;
    right: 0;
    bottom: 0;
    padding-left: 36px;
  }
  /* 末行（--lh-body＝一行高）右端 84px 前全顯 → 36px 處全透，讓位給右貼齊的「更多」 */
  .detail-desc.has-more:not(.is-expanded) .detail-desc__text {
    -webkit-mask: linear-gradient(#000 0 0) top / 100% calc(100% - var(--lh-body)) no-repeat,
      linear-gradient(90deg, #000 calc(100% - 84px), transparent calc(100% - 36px)) bottom / 100% var(--lh-body) no-repeat;
    mask: linear-gradient(#000 0 0) top / 100% calc(100% - var(--lh-body)) no-repeat,
      linear-gradient(90deg, #000 calc(100% - 84px), transparent calc(100% - 36px)) bottom / 100% var(--lh-body) no-repeat;
  }
}
/* "寫下你的影評" quiet link on the right of the title row */
.detail-titlebar__aside {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  flex: 0 0 auto;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  text-decoration: none;
  white-space: nowrap;
  transition: color 150ms var(--ease-out);
}
.detail-titlebar__aside:hover { color: var(--text-primary); }
.detail-titlebar__aside svg { width: 18px; height: 18px; }

/* score + write-review link */
.detail-scorerow {
  display: flex;
  align-items: center;
  gap: 16px;
}
/* the Ztor score ring is larger on the detail hero than on cards (Figma: 50px
   ring, 20px numeral). Component-scoped — the base ring stays 28px elsewhere. */
.detail-scorerow .ztor-score { width: 50px; height: 50px; --ring-band: 4px; }
.detail-scorerow .ztor-score__value { font-size: 20px; }
/* "寫下你的影評" — star + label sitting to the right of the score */
.detail-review-link {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;   /* 空間不夠時整顆掉下一行，不許拆成直排字 */
  padding: 8px 16px 8px 12px;
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  text-decoration: none;
  transition: color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.detail-review-link:hover { background: var(--white-alpha-8); }
/* 實心星（assets/icons/star.svg，ds-icon mask）壓透明度；hover 隨連結亮起。
   雙 class 抬權重：ds.css 的 .ds-icon{width:1em} 源序在後會蓋 24px */
.detail-review-link .detail-review-link__star { width: 24px; height: 24px; flex-shrink: 0; color: var(--text-secondary); opacity: 0.45; }
.detail-review-link:hover .detail-review-link__star { opacity: 0.8; }

/* 已評分（body[data-my-rating="N"]，dev 面板「我的評分」）：影評鈕換
   「我的評分」卡＝標籤＋5 星亮到 N（mock 0727；星色照 2.0 highlight rose，
   不照抄 1.0 紅）。★ 尺寸要雙 class——ds.css 的 .ds-icon{1em} 載在後面同權重反蓋 */
.detail-myrating { display: none; }
body[data-my-rating] .detail-scorerow .detail-review-link { display: none; }
body[data-my-rating] .detail-myrating {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 10px 16px;
  border: 1px solid var(--border-default);   /* 細白線框、無底（Dolly 0727 三輪） */
  border-radius: var(--radius-lg);
}
.detail-myrating__label { font-size: var(--fs-caption); color: var(--text-secondary); }
.detail-myrating__stars { display: flex; gap: 8px; }
.detail-myrating__star.ds-icon {
  --icon: url('icons/star.svg');   /* mask 在 ds.css（assets/）套用→路徑相對它，同 create-menu 慣例；定義在此非 inline，變體才換得掉 */
  width: 20px;
  height: 20px;
  color: var(--white-alpha-16);
}
/* 電影人（body[data-usertype=filmmaker]，同評分流程 taxonomy）：星換開拍板 */
body[data-usertype="filmmaker"] .detail-myrating__star.ds-icon { --icon: url('icons/filmmaker.svg'); }
body[data-my-rating="1"] .detail-myrating__star:nth-child(-n+1),
body[data-my-rating="2"] .detail-myrating__star:nth-child(-n+2),
body[data-my-rating="3"] .detail-myrating__star:nth-child(-n+3),
body[data-my-rating="4"] .detail-myrating__star:nth-child(-n+4),
body[data-my-rating="5"] .detail-myrating__star:nth-child(-n+5) { color: var(--brand-500); }   /* active＝品牌橘（Dolly 0728 拍板，rose 退場） */

/* 地區版權限制提示（body[data-region="restricted"]）— 與分數同列，僅供觀影資訊。
   restricted 時隱藏購買/領票鈕（單次收費 + 共看派對入場券），保留觀看預告片。
   同列塞不下（中等寬度/手機）時公告整塊換行成自己一排——沒有 wrap 的話
   flex 會把影評鈕與公告壓成一字一行的直排字。 */
.detail-region-note { display: none; }
body[data-region="restricted"] .detail-scorerow { flex-wrap: wrap; }
body[data-region="restricted"] .detail-scorerow .detail-region-note {
  display: flex; align-items: center; gap: 8px; margin-left: auto; min-width: 0;
}
/* 公告膠囊皮膚（1.0 參考：深色膠囊底＋品牌橘字；地區限制與 DB 附註同款） */
.detail-region-note,
.detail-db-note {
  padding: 12px 22px;
  background: var(--white-alpha-8);
  border-radius: var(--radius-pill);
}
.detail-region-note__ico { flex: none; display: inline-flex; color: var(--brand-500); }
.detail-region-note__ico .ds-icon { width: 16px; height: 16px; }
.detail-region-note__txt { font-size: var(--fs-body-sm); color: var(--brand-500); text-align: right; }
body[data-region="restricted"] .detail-price { display: none; }

/* 共看派對入場券鈕：免費 ↔ 付費（80 爆米花）由 body[data-claim="paid"] 純 CSS 切換 */
.detail-price--wpticket .detail-price__main--paid { display: none; }
body[data-claim="paid"] .detail-price--wpticket .detail-price__main--free { display: none; }
body[data-claim="paid"] .detail-price--wpticket .detail-price__main--paid { display: inline-flex; }

/* 一人一票：已擁有（body[data-owned]）→ 入場券鈕改純標示、不可點。
   免費＝已領取／付費（疊 data-claim=paid）＝已購買 */
.detail-price--wpticket .detail-price__main--owned { display: none; }
body[data-wp-owned] .detail-price--wpticket .detail-price__main--free,
body[data-wp-owned] .detail-price--wpticket .detail-price__main--paid { display: none; }
body[data-wp-owned] .detail-price--wpticket .detail-price__main--owned { display: inline-flex; }
.detail-price__ownlabel--paid { display: none; }
body[data-claim="paid"] .detail-price__ownlabel--free { display: none; }
body[data-claim="paid"] .detail-price__ownlabel--paid { display: inline; }
/* 純標示不可點：拿掉互動、淡化到「已完成」的態，游標不再是可點手勢 */
body[data-wp-owned] .detail-price--wpticket { pointer-events: none; opacity: 0.65; cursor: default; }

/* 產品規則：活動一旦開演，就停止領票（免費）與售票（付費）——
   body[data-live] 疊加層，獨立於 data-claim/data-owned/data-region。
   優先序：data-owned 蓋過 data-live（持票者照常顯示 ✓已領取/✓已購買，不受影響）；
   data-region="restricted" 本來就整顆藏鈕（見上方 195 行），不受此處影響。 */
.detail-price--wpticket .detail-price__main--live,
.detail-price--wpticket .detail-price__label--live { display: none; }
body[data-live]:not([data-wp-owned]) .detail-price--wpticket .detail-price__main--free,
body[data-live]:not([data-wp-owned]) .detail-price--wpticket .detail-price__main--paid,
body[data-live]:not([data-wp-owned]) .detail-price--wpticket .detail-price__label--std { display: none; }
body[data-live]:not([data-wp-owned]) .detail-price--wpticket .detail-price__main--live { display: inline-flex; }
body[data-live]:not([data-wp-owned]) .detail-price--wpticket .detail-price__label--live { display: inline-flex; }
/* 免費場＝暫停領票／付費場（疊 data-claim=paid）＝暫停售票 */
.detail-price__livelabel--paid { display: none; }
body[data-claim="paid"] .detail-price__livelabel--free { display: none; }
body[data-claim="paid"] .detail-price__livelabel--paid { display: inline; }
/* disabled 外觀：比 owned 的 0.65（已完成）更暗一階（0.45）—— live 是「不可用」，非「已完成」 */
body[data-live]:not([data-wp-owned]) .detail-price--wpticket { pointer-events: none; cursor: default; opacity: 0.45; }

/* meta row: year · rating · duration · lang */
.detail-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 12px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.detail-meta__sep {
  width: 1px;
  height: 12px;
  background: var(--border-default);
}
.detail-meta__label { color: var(--text-tertiary); }

/* keyword chips */
.detail-keywords {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}
/* Figma: filled white-16% pill, no border, 14px CJK, 2/12/4 padding */
.detail-chip {
  display: inline-flex;
  align-items: center;
  padding: 2px 12px 4px;
  border-radius: var(--radius-pill);
  border: none;
  background: var(--white-alpha-16);
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  white-space: nowrap;
}

.detail-desc {
  margin: 0;
  max-width: 920px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}

/* actions: primary action + price pill + bare icon group */
.detail-actions {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 4px;
}
.detail-actions__primary {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
}

/* all three primary actions share one footprint (58px tall pills) */
.detail-actions .btn--lg { min-width: 168px; height: 58px; }

/* popcorn price pill — wears the .btn.btn--yellow-ghost (orange ghost) skin;
   this class only restyles the inner layout into two stacked lines. */
.detail-price {
  flex-direction: column;
  gap: 6px;
  line-height: 1;
}
.detail-price__main {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.detail-price__amount {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  line-height: 1;
  font-weight: var(--fw-bold);
}
/* a word amount (免費／已領取／已購買) reads heavier than a number — size it
   down a notch below the numeric amount（18→16，與「共看派對入場券」小字更分層） */
.detail-price__amount--text { font-size: var(--fs-body); }
.detail-price__coin {
  width: 18px;
  height: 18px;
  background-color: var(--brand-500);
  -webkit-mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
}
.detail-price__label {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-regular);   /* override the .btn bold */
  line-height: 1;
  color: var(--brand-600);
}

/* bare icon actions (gift / save / heart / share) — icons, not glass pills */
.detail-actions__icons {
  display: flex;
  align-items: flex-start;
  gap: 24px;
  margin-left: auto;
}
.detail-iconbtn {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-secondary);
  transition: color 150ms var(--ease-out);
}
/* hover 白只給真滑鼠：觸控 tap 會黏 :hover，取消收藏後 icon 卡在白色回不去灰 */
@media (hover: hover) and (pointer: fine) {
  .detail-iconbtn:hover { color: var(--text-primary); }
}
.detail-iconbtn__ico {
  width: 24px;
  height: 24px;
  background-color: currentColor;
  -webkit-mask: var(--ico) center / contain no-repeat;
          mask: var(--ico) center / contain no-repeat;
}
.detail-iconbtn__count {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  line-height: 1;
}

/* ===== CROWDFUND LINKAGE BANNER ========================== */
.crowdfund-banner {
  position: relative;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 28px 32px;
  border-radius: var(--radius-xl);
  border: 1px solid var(--border-subtle);
  background:
    radial-gradient(120% 160% at 100% 0%, rgba(255,163,63,0.10) 0%, rgba(255,163,63,0) 55%),
    var(--bg-secondary);
}
.crowdfund-banner__head {
  display: flex;
  align-items: center;
  gap: 16px;
}
/* eyebrow chip — brand-tinted (12% fill, brand text), scoped to this banner
   so the shared category-pill component is untouched. */
.crowdfund-banner .section-eyebrow--tag {
  background: color-mix(in srgb, var(--brand-500) 12%, transparent);
  color: var(--brand-500);
}
.crowdfund-banner__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  line-height: var(--lh-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.crowdfund-banner__desc {
  margin: 0;
  max-width: 880px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
.crowdfund-banner__desc strong {
  color: var(--brand-500);
  font-weight: var(--fw-bold);
}
.crowdfund-banner__foot {
  display: flex;
  align-items: center;
  gap: 16px;
}
.crowdfund-banner__supporters {
  display: flex;
  align-items: center;
  gap: 12px;
}
.crowdfund-banner__supporters-label {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.crowdfund-banner__cta { margin-left: auto; }

/* ── stacked avatar group (reusable) ── */
.avatar-group {
  display: flex;
  align-items: center;
}
.avatar-group__item {
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  object-fit: cover;
  background: var(--bg-tertiary);
  border: 2px solid var(--bg-secondary);
  margin-left: -10px;
}
.avatar-group__item:first-child { margin-left: 0; }
.avatar-group__more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-pill);
  background: var(--bg-tertiary);
  border: 2px solid var(--bg-secondary);
  margin-left: -10px;
  font-family: var(--font-cjk-text);
  font-size: 11px;
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
}

/* ===== CONTENT SECTIONS ================================== */
.detail-body {
  display: flex;
  flex-direction: column;
  gap: 56px;
  padding-top: 8px;
}
.detail-section {
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.detail-section__head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.detail-section__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4, 20px);
  line-height: var(--lh-h3);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.detail-section__count {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-h4);
  color: var(--text-tertiary);
}
/* chevron sits right after the title/count cluster (Figma), not pushed right */
.detail-section__more {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  text-decoration: none;
  transition: color 150ms var(--ease-out);
}
.detail-section__more:hover { color: var(--text-primary); }
.detail-section__more svg { width: 20px; height: 20px; }

/* ── cast row ── */
.cast-row {
  display: flex;
  gap: 16px;
  flex-wrap: wrap;
}
.cast-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  width: 120px;
  text-align: center;
}
.cast-card__avatar {
  width: 96px;
  height: 96px;
  border-radius: var(--radius-pill);
  object-fit: cover;
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
}
.cast-card__name {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);        /* 14px（2026-07-16 Dolly 定版，影片頁版型通用） */
  font-weight: var(--fw-medium);
  color: var(--text-primary);
  line-height: 1.3;
}
.cast-card__role {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);        /* 14px（同上定版） */
  color: var(--text-tertiary);
}

/* ── photo / video media grid ── */
.media-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 24px;
}
.media-card {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
  cursor: pointer;
  /* the 相關影片 variant is a <button>; kill the UA padding/appearance so the
     image fills edge-to-edge like the <a> 劇照 cards (no left/right dark bars). */
  padding: 0;
  appearance: none;
  -webkit-appearance: none;
}
.media-card__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 500ms var(--ease-out);
}
.media-card:hover .media-card__img { transform: scale(1.05); }
.media-card__play {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: rgba(0,0,0,0.30);
  cursor: pointer;
  transition: background 150ms var(--ease-out);
}
.media-card:hover .media-card__play { background: rgba(0,0,0,0.45); }
.media-card__play::before {
  content: '';
  width: 56px;
  height: 56px;
  border-radius: var(--radius-pill);
  background: rgba(0,0,0,0.55) url("assets/icons/media/play.svg") center / 22px no-repeat;
  border: 1px solid rgba(255,255,255,0.7);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* ===== DISCUSSION: comments + related posts ============= */
.detail-discussion {
  display: grid;
  /* 2:1 — matches the Figma comment(772):posts(388) ratio. Was 1fr/388px,
     which over-stretched the comment column on our 1344px-wide container. */
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 40px;
  align-items: start;
}
/* both column heads share one height so the comment card top lines up with
   the first related post (置頂對齊). */
.detail-col__head {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 48px;
  margin-bottom: 24px;
}
.detail-col__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4, 20px);
  line-height: var(--lh-h3);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.detail-col__count {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
/* 依熱門度 sort — glass chip, right of the 留言 header */
.comment-sort {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 40px;
  padding: 4px 8px 4px 16px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--white-alpha-8);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.comment-sort svg { width: 20px; height: 20px; }
/* 排序 dropdown（0725 稿；ds.js title-comments 注入、fixed 錨在按鈕下）：
   依熱門度／依發佈時間，選中橘字。全站留言區共用。 */
.comment-sortmenu {
  position: fixed;
  z-index: 2200;  /* 要壓過留言 sheet（.mtd-bar z180）＋貼文彈窗（.postpop z2100）；低了會被蓋住點不到 */
  min-width: 150px;
  padding: 6px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
}
.comment-sortmenu[hidden] { display: none; }
.comment-sortmenu__opt {
  display: block;
  width: 100%;
  padding: 10px 12px;
  border: none;
  background: none;
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  text-align: left;
  cursor: pointer;
}
.comment-sortmenu__opt:hover { background: var(--white-alpha-8); }
.comment-sortmenu__opt.is-on { color: var(--brand-500); }

/* ── comment card (bordered, Figma comment area) ── */
.comment-card {
  --like-color: #b63a3a;   /* Figma "Secondary 500" — liked heart; no global token yet */
  display: flex;
  flex-direction: column;
  gap: 20px;
  padding: 16px 16px 24px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xl);
}

/* composer */
.comment-composer { display: flex; flex-direction: column; gap: 8px; }
.comment-compose { display: flex; align-items: center; gap: 16px; }
.comment-compose__field {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 8px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);   /* 0727 Dolly：原 alpha-4 太不明顯，亮一階（桌機手機共用） */
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}
.comment-compose__avatar {
  flex: 0 0 24px;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--bg-tertiary);
}
.comment-compose__input {
  flex: 1 1 auto;
  min-width: 0;
  border: none;
  background: none;
  outline: none;
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
}
.comment-compose__input::placeholder { color: var(--white-alpha-24); }
.comment-compose__tools { display: inline-flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.comment-compose__tool,
.comment-compose__send {
  width: 24px;
  height: 24px;
  padding: 0;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-tertiary);
  transition: color 150ms var(--ease-out);
}
.comment-compose__send { color: var(--text-secondary); }
.comment-compose__tool:hover,
.comment-compose__send:hover { color: var(--text-primary); }
.comment-compose__tool svg,
.comment-compose__send svg,
.comment-compose__tool .ds-icon,
.comment-compose__send .ds-icon { width: 20px; height: 20px; }
/* 留言使用說明 hint */
.comment-guide {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding-left: 8px;
  color: var(--text-tertiary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  text-decoration: none;
}
.comment-guide svg,
.comment-guide .ds-icon { width: 16px; height: 16px; }

/* ── comment item ── */
.comment-list { display: flex; flex-direction: column; gap: 28px; }
.comment { display: flex; gap: 16px; align-items: flex-start; }
.comment__avatar {
  flex: 0 0 32px;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  object-fit: cover;
  background: var(--bg-tertiary);
}
.comment__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.comment__head { display: flex; align-items: baseline; gap: 8px; }
.comment__name {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.comment__date {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.comment__text {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
.comment__actions { display: flex; align-items: center; gap: 16px; margin-top: 2px; }
.comment__action {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font-cjk-text);   /* 0730：拉丁改 Satoshi，stack 首位已是它，不再寫死 Proxima */
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  transition: color 150ms var(--ease-out);
}
.comment__action:hover { color: var(--text-secondary); }
.comment__action--translate { font-weight: var(--fw-bold); }
.comment__action--like.is-active { color: var(--like-color); }
.comment__action-ico { width: 20px; height: 20px; flex: 0 0 auto; display: inline-flex; }
.comment__action-ico svg { width: 20px; height: 20px; }
.comment__action-ico--mask {
  background-color: currentColor;
  -webkit-mask: var(--ico) center / contain no-repeat;
          mask: var(--ico) center / contain no-repeat;
}
/* 查看回覆 — leading 30px rule then label */
.comment__replies {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-top: 2px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--text-tertiary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
}
.comment__replies::before { content: ''; width: 30px; height: 1px; background: currentColor; flex: 0 0 auto; }

.related-col { display: block; }
.related-posts { display: flex; flex-direction: column; gap: 24px; }
/* 創建貼文 sits at the right end of the 相關貼文 header (Figma justify-between) */
.related-create { margin-left: auto; }
.related-create__plus { width: 16px; height: 16px; flex: 0 0 auto; }

/* the detail body sits directly under the hero — trim the generic .section top
   padding so 共 N 集 hugs the hero CTA row instead of the full section gap. */
.section:has(> .detail-body) { padding-top: 48px; }

/* ── 集數列表（共 N 集）— 橫向可滑的每集卡 ─────────────────────── */
.episode-rail { position: relative; }   /* positioning context for the nav arrows */
.episode-rail__track {
  display: flex;
  gap: 16px;
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  /* overflow-x:auto forces overflow-y to clip → the card's hover translateY(-2px)
     would be cut at the top. Pad the scroll box and pull it back so the lift has
     room without changing the outer spacing. */
  padding-block: 8px;
  margin-block: -8px;
}
.episode-rail__track::-webkit-scrollbar { display: none; }

/* hover-reveal glass nav (same look as .films-rail__nav; auto-hidden at the
   ends via [disabled], toggled by ds.js). */
.episode-rail__nav,
.goods-rail__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  border: none;
  padding: 0;
  cursor: pointer;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  /* L-kit neutral glass (was a copy of the old ghost recipe) */
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  backdrop-filter: var(--lg-frost);
  -webkit-backdrop-filter: var(--lg-frost);
  transition: opacity 250ms ease, background 200ms ease;
}
.episode-rail:hover .episode-rail__nav:not([disabled]),
.episode-rail__nav:not([disabled]):focus-visible,
.goods-rail:hover .goods-rail__nav:not([disabled]),
.goods-rail__nav:not([disabled]):focus-visible {
  opacity: 1;
  pointer-events: auto;
}
.episode-rail__nav:not([disabled]):hover,
.goods-rail__nav:not([disabled]):hover {
  background: linear-gradient(0deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.30) 2.45%,
    rgba(255, 255, 255, 0) 126.14%);
}
.episode-rail__nav--prev,
.goods-rail__nav--prev { left: -22px; }
.episode-rail__nav--next,
.goods-rail__nav--next { right: -22px; }
.episode-rail__nav img,
.goods-rail__nav img {
  width: 22px;
  height: 22px;
  display: block;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
}
.episode-card {
  flex: 0 0 256px;
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 20px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
  transition: border-color 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.episode-card:hover { border-color: var(--border-default); transform: translateY(-2px); }
.episode-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
}
.episode-card__no {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
}
.episode-card__dur {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.episode-card__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  line-height: var(--lh-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.episode-card__desc {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.55;
  color: var(--text-tertiary);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.episode-card__price {
  align-self: flex-start;
  margin-top: auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px 5px 8px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.episode-card__price-icon {
  width: 14px;
  height: 14px;
  display: inline-block;
  background-color: var(--neutral-white);
  -webkit-mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
}

/* ===== RESPONSIVE ======================================= */
@media (max-width: 1080px) {
  .detail-discussion {
    grid-template-columns: minmax(0, 1fr);
  }
  .related-col { display: none; }   /* mobile build handles this column separately */
  .media-grid { grid-template-columns: repeat(2, 1fr); }
}

/* ── 手機版元件（Figma Mobile v4 Video Page）：分頁列＋底部互動 sheet＋集數雙模式，桌機隱藏 ── */
.mtd-tabs, .mtd-bar, .mep, .mep-toggle { display: none; }

@media (max-width: 768px) {
  /* 照稿無 logo header：← ⋯ 列就是本頁頂欄 */
  body.rf-detail .header { display: none; }
  /* 底部固定互動列讓位 */
  body.rf-detail { padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px)); }
  /* 頂列上方間距收窄（Dolly 0727：container 頂 20 → 8，圓鈕離頂 ≈16） */
  body.rf-detail .detail-hero .container { padding-top: 8px; }
  /* 返回箭頭＝帶尾巴長箭頭（Dolly 0727，同短劇頁語彙；全型態含共創。
     桌機維持 chevron＝0724 返回鍵分派不動） */
  .detail-back__nav .icon-btn svg { display: none; }
  .detail-back__nav .icon-btn::before {
    content: '';
    width: 20px;
    height: 20px;
    background: currentColor;
    -webkit-mask: url('assets/icons/arrow_left.svg') center / contain no-repeat;
            mask: url('assets/icons/arrow_left.svg') center / contain no-repeat;
  }
  /* 「返回」字降一級（Dolly 0727） */
  .detail-back__title { font-size: 14px; }
  /* ⋯ 更多鈕補圓形玻璃底＝同返回圓鈕 32px（Dolly 0727；桌機維持裸 icon）。
     hover 變體要 :not() 抬到 0,4,0——ds.css 的 --ghost:hover 會把 background
     打回 none（ds.css 載在 components.css 後，同權重必輸） */
  .detail-back .icon-btn--ghost,
  .detail-back .icon-btn--ghost:hover:not(:disabled) {
    width: 32px;
    height: 32px;
    padding: 7px;
    background: var(--glass-bg-default);
  }
  .detail-back .icon-btn--ghost .ds-icon { width: 18px; height: 18px; }

  /* hero：海報 133×200 左、資訊欄右（照稿並排；皮膚走 2.0 token） */
  .detail-head { flex-direction: row; gap: 19px; }
  .detail-head__poster { width: 133px; flex-basis: 133px; border-radius: var(--radius-md); }
  .detail-head__main { min-width: 0; }
  .detail-title { font-size: 18px; line-height: 32px; gap: 10px; }
  .detail-subtitle { font-size: 14px; }
  .detail-scorerow { gap: 16px; }
  /* 我的評分卡緊湊版：右欄僅 ~198px，桌機尺寸會被擠去折行（要與環同列） */
  body[data-my-rating] .detail-myrating { padding: 8px 12px; gap: 4px; border-radius: var(--radius-lg); }
  .detail-myrating__stars { gap: 6px; }
  .detail-myrating__star.ds-icon { width: 18px; height: 18px; }
  .detail-scorerow .ztor-score { width: 32px; height: 32px; --ring-band: 3px; }
  .detail-scorerow .ztor-score__value { font-size: 12px; }
  .detail-review-link {
    border: 1px solid var(--border-default);
    border-radius: var(--radius-md);
    padding: 8px 16px 8px 12px;
    font-size: 14px;
  }
  .detail-meta { font-size: 12px; }

  /* 地區版權限制公告（?region=restricted）：桌機與分數同列靠右；手機照稿
     整塊搬到 head 之下、CTA 之上（節點由 title.html regionNotePlacement 搬）——
     滿版、ⓘ 靠左、文字左對齊自然換行（皮膚維持 2.0 warning token） */
  body[data-region="restricted"] .detail-region-note {
    display: flex;
    align-items: flex-start;
    gap: 8px;
    margin-top: var(--space-6);
  }
  body[data-region="restricted"] .detail-region-note__txt { text-align: left; }
  /* 折行成塊的提示膠囊 9999 太圓（Dolly 0727 晚場）→ 圓矩形；DB 附註同款同改 */
  .detail-region-note,
  .detail-db-note { border-radius: var(--radius-xl); }

  /* CTA：預告片／單次收費／入場券三顆等寬 48 高一排。
     節點由 JS 從右欄搬到 hero 之下（照稿獨立一排） */
  .detail-head + .detail-actions,
  .detail-region-note + .detail-actions { margin-top: var(--space-6); }   /* 公告插在 head/CTA 之間時 + 選擇器仍要中 */
  .detail-actions__primary { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; width: 100%; }  /* minmax(0,…)：不讓 min-content 撐爆軌道 */
  /* 無入場券型態（電影/影集 wp=off）兩顆填滿；DB 只剩預告片一顆滿版（Dolly 0727） */
  body[data-t-wp="off"] .detail-actions__primary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  body[data-t-source="db"] .detail-actions__primary { grid-template-columns: 1fr; }
  /* 地區受限只剩「觀看預告片」一顆（票價鈕 display:none）：三欄格會讓它縮在 1/3 寬，
     改單欄讓它 fill container */
  body[data-region="restricted"] .detail-actions__primary { grid-template-columns: 1fr; }
  .detail-actions__primary > * { height: 48px; min-width: 0; padding-inline: 8px; }  /* btn--lg 的 40px 會爆 1fr 格 */
  /* 蓋掉桌機 .detail-actions .btn--lg { min-width:168; height:58 }（同權重才贏） */
  .detail-actions .btn--lg, .detail-actions .detail-price { min-width: 0; height: 48px; width: 100%; }
  .detail-actions__primary .detail-price__label { font-size: 11px; white-space: nowrap; }
  /* CTA 文字整體縮一階：預告片鈕 16→14、金額 20→16（免費字樣同步） */
  .detail-actions__primary .btn { font-size: 14px; }
  .detail-actions__primary .detail-price__amount { font-size: 16px; }
  .detail-actions__primary .detail-price__amount--text { font-size: 15px; }
  .detail-actions__icons { display: none; }   /* 動作 icon 移駐底部互動列 */

  /* 分頁列：資訊/劇照/相關影片（active=品牌橘＋底線，同社群頁 feed-tab 慣例） */
  .mtd-tabs { display: flex; margin-top: var(--space-2); }
  .mtd-tabs__tab {
    flex: 1; height: 53px;
    background: none; border: 0; border-bottom: 1px solid var(--border-subtle);
    font-size: 15px; font-weight: var(--fw-medium); color: var(--text-secondary);
    position: relative; cursor: pointer;
  }
  .mtd-tabs__tab.is-active { color: var(--text-primary); }   /* 白字橘線（Dolly 0728） */
  .mtd-tabs__tab.is-active::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 2px;
    background: var(--brand-500);
  }

  /* 分頁內容：依 body[data-mtab] 只顯示對應區塊（桌機不吃這組規則、全部顯示） */
  [data-mtd-panel] { display: none; }
  body[data-mtab="info"]   [data-mtd-panel="info"],
  body[data-mtab="stills"] [data-mtd-panel="stills"],
  body[data-mtab="videos"] [data-mtd-panel="videos"] { display: block; }

  /* 簡介：JS 移入資訊分頁（crowdfund 卡之後，照稿順序） */
  .detail-body .detail-desc { font-size: 14px; }

  /* 創作團隊：照稿橫滑 rail（破 gutter 滿版滑動） */
  .cast-row {
    display: flex; flex-wrap: nowrap; overflow-x: auto;
    -webkit-overflow-scrolling: touch; scrollbar-width: none;
    margin-inline: -16px; padding-inline: 16px;
  }
  .cast-row::-webkit-scrollbar { display: none; }
  .cast-card { flex: none; }

  /* 留言/貼文欄位被 JS 搬進 sheet：留下的空殼藏掉 */
  .detail-discussion { display: none; }

  /* ── 集數雙模式（Figma 12794/12806）：數字格 ↔ 列表，rail 讓位 ── */
  .detail-section.has-mep .episode-rail { display: none; }
  .mep { display: block; }
  /* 集數區間距（Dolly 0728，照桌機選集面板節奏）：CTA→標題列 24、標題列→內容
     收 12（section 的 flex gap 24 歸零，改明確 margin 控制） */
  .detail-section[data-sec="episodes"] { margin-top: 24px; gap: 0; }
  .detail-section[data-sec="episodes"] .detail-section__head { margin-bottom: 12px; }
  /* 標題同桌機選集面板 .player__eps-count（小字灰）（Dolly 0728） */
  .detail-section[data-sec="episodes"] .detail-section__title {
    font-family: var(--font-cjk-text);
    font-size: var(--fs-body-sm);
    font-weight: var(--fw-regular);
    color: var(--text-tertiary);
  }
  .mep-toggle {
    display: grid; place-items: center;
    width: 32px; height: 32px; margin-left: auto;
    background: none; border: 0; color: var(--text-secondary); cursor: pointer;
  }
  .mep-toggle__ic { width: 20px; height: 20px; display: none; }
  [data-mep="grid"] .mep-toggle__ic--list { display: block; }   /* 顯示「切到列表」icon */
  [data-mep="list"] .mep-toggle__ic--grid { display: block; }
  .mep-range { font-size: var(--fs-caption); color: var(--text-tertiary); margin-bottom: var(--space-3); }
  [data-mep="list"] .mep-range { display: none; }
  .mep-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 8px; }
  [data-mep="list"] .mep-grid { display: none; }
  /* chip 樣式照短劇集數格 .shortpanel__epbtn（Dolly 0728 三輪），圓角照劇集 radius-md */
  .mep-chip {
    position: relative;
    aspect-ratio: 1; min-height: 44px;
    display: grid; place-items: center;
    background: var(--white-alpha-8);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    color: var(--text-primary); font-size: var(--fs-body-sm); cursor: pointer;
  }
  .mep-chip.is-on { border-color: var(--brand-500); color: var(--brand-500); }
  /* 未購買（body 無 data-owned，Dolly 0728）：右上角 12px 小鎖（同短劇，數字保持白）；
     current 圈收起（沒有可播的「當前集」）、觀看進度條一併收起 */
  .mep-chip__lock {
    position: absolute; top: 2px; right: 2px; width: 12px; height: 12px;
    color: var(--text-tertiary); display: none;
  }
  .mep-chip__lock svg { width: 100%; height: 100%; display: block; }
  body:not([data-owned]) .mep-chip__lock { display: block; }
  body:not([data-owned]) .mep-chip.is-on { border-color: transparent; color: var(--text-primary); }
  /* owned 子狀態（只買單集/先買未上映）：__applyEpsBuyMode 掛 .is-locked 逐格鎖 */
  .mep-chip.is-locked .mep-chip__lock { display: block; }
  .mep-row.is-locked .mep-row__lock { display: grid; }
  /* 未上映（分批上映，Dolly 0729 二輪）：鎖頭照掛（同 1.0）＋灰化＋日期提示、不可點不可買；
     列表檢視由 JS 換上「將於 X 上映」 */
  .mep-chip.is-coming { color: var(--text-tertiary); cursor: default; }
  .mep-chip.is-coming .mep-chip__lock { display: block; }
  /* tap 浮出上映日膠囊（同播放器選集 .player__eps-num tooltip 皮；show-air 由 JS 掛 2.4s） */
  .mep-chip.is-coming.show-air::after {
    content: attr(data-air);
    position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
    z-index: 5; white-space: nowrap;
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    background: var(--bg-tertiary); color: var(--text-primary);
    font-family: var(--font-cjk-text); font-size: var(--fs-caption); font-weight: var(--fw-regular);
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
  }
  .mep-chip.is-coming.show-air::before {
    content: ''; position: absolute; bottom: calc(100% + 2px); left: 50%; transform: translateX(-50%);
    z-index: 5; border: 6px solid transparent; border-top-color: var(--bg-tertiary);
  }
  .mep-row.is-coming { cursor: default; }
  .mep-row.is-coming .mep-row__lock { display: grid; }
  .mep-row.is-coming .mep-row__title { color: var(--text-secondary); }
  .mep-row__lock {
    position: absolute; inset: 0; display: none; place-items: center;
    color: var(--text-primary); background: rgba(0, 0, 0, 0.35);
  }
  .mep-row__lock svg { width: 16px; height: 16px; }
  body:not([data-owned]) .mep-row__lock { display: grid; }
  body:not([data-owned]) .mep-row__thumb--progress::after { display: none; }
  .mep-list { display: none; flex-direction: column; gap: var(--space-4); }
  [data-mep="list"] .mep-list { display: flex; }
  .mep-row { display: flex; gap: var(--space-3); align-items: flex-start; text-decoration: none; }
  .mep-row__thumb {
    position: relative; flex: none;
    width: 100px; aspect-ratio: 16 / 9;
    display: grid; place-items: center;
    background: var(--bg-tertiary); border-radius: var(--radius-sm);
    overflow: hidden;
  }
  .mep-row__thumb img { width: 48px; opacity: 0.35; }
  .mep-row__thumb--progress::after {
    content: ''; position: absolute; left: 0; bottom: 0; height: 2px; width: 62%;
    background: var(--brand-500);
  }
  .mep-row__txt { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .mep-row__title {
    font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--text-primary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .mep-row__desc {
    font-size: var(--fs-caption); color: var(--text-tertiary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* ── 底部互動 sheet（照稿 Video Footer＋3079-30030 展開態；皮膚 2.0）── */
  .mtd-bar {
    display: flex; flex-direction: column;
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 180;
    padding: 0 16px max(12px, env(safe-area-inset-bottom, 0px));   /* 疊加 12+34=46 底空過多（Dolly 0727 真機）→ 取大者 34 */
    background: var(--bg-primary);
    border-top: 1px solid var(--border-default);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    box-shadow: 0 -24px 24px rgba(20, 20, 20, 0.64);
    transition: transform 200ms var(--ease-out);
  }
  .mtd-bar__handle {
    height: 20px; flex: none;
    display: grid; place-items: center;
    background: none; border: 0; cursor: grab; touch-action: none;
  }
  .mtd-bar__handle span { width: 48px; height: 4px; border-radius: var(--radius-pill); background: rgba(255, 255, 255, 0.24); }
  .mtd-bar__row {
    display: flex; align-items: flex-start; justify-content: space-between;  /* 整列置頂：文字與 icon 同一條頂線，數字垂下 */
    padding: 6px 0 8px; flex: none; touch-action: none;
  }
  .mtd-bar__tabs { display: flex; gap: 16px; }
  .mtd-bar__tab {
    position: relative;
    display: flex; align-items: baseline; gap: 4px;
    background: none; border: 0; padding: 0; cursor: pointer;
    font-size: 16px; line-height: 24px; color: var(--text-primary);  /* 行高=icon 高，文字光學置中於 icon 列 */
  }
  .mtd-bar__count { font-size: 14px; color: var(--text-tertiary); }
  .mtd-bar__icons { display: flex; align-items: flex-start; gap: 16px; }  /* icon 置頂對齊，數字垂在下（照稿） */
  .mtd-bar .detail-iconbtn { flex-direction: column; gap: 6px; height: auto; padding: 0; }
  .mtd-bar .detail-iconbtn__count { font-size: 12px; }
  /* tap 不出全站 :focus-visible 橘框（觸控列慣例） */
  .mtd-bar .detail-iconbtn:focus,
  .mtd-bar .detail-iconbtn:focus-visible { outline: none; }
  /* 展開態：長高成 sheet、tab 有主從（白字＋橘底線） */
  .mtd-bar[data-mtd-sheet="open"] { height: calc(100dvh - 48px); }
  .mtd-bar[data-mtd-sheet="open"] .mtd-bar__tab { color: var(--text-tertiary); }
  .mtd-bar[data-mtd-sheet="open"] .mtd-bar__tab.is-active { color: var(--text-primary); }
  .mtd-bar[data-mtd-sheet="open"] .mtd-bar__tab.is-active::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: -8px; height: 2px;
    background: var(--brand-500);
  }
  .mtd-bar__body { display: none; }
  .mtd-bar[data-mtd-sheet="open"] .mtd-bar__body { display: block; flex: 1; min-height: 0; margin-top: var(--space-3); }
  .mtd-bar__panel { display: none; height: 100%; overflow-y: auto; overscroll-behavior: contain; }
  .mtd-bar[data-mtd-active="comments"] .mtd-bar__panel--comments,
  .mtd-bar[data-mtd-active="posts"] .mtd-bar__panel--posts { display: block; }
  /* 搬進 sheet 的欄位（Dolly 0727 定版＝照 Figma sheet mock）：頂列 tab＋icon
     保留（sheet header），欄位自己的標題/數字收掉、排序 chip 靠左自成一列 */
  .mtd-bar__panel .detail-col__title,
  .mtd-bar__panel .detail-col__count { display: none; }
  .mtd-bar__panel--comments .detail-col__head { min-height: 0; margin-bottom: 16px; }
  /* 排序 chip 靠左（Dolly 0727 定版）；選單錨向由 ds.js 依 chip 位置自動切 */
  .mtd-bar__panel--comments .comment-sort { margin-left: 0; }
  /* 捲軸貼齊 sheet 右緣：panel 用負 margin 破出 .mtd-bar 的 16px 側 padding、
     再用 padding-right 補回內容位置（內容不動、只有捲軸外移） */
  .mtd-bar__panel--comments { margin-right: -16px; padding-right: 16px; }
  .mtd-bar__panel--comments .comment-col { display: flex; flex-direction: column; min-height: 100%; }
  .mtd-bar__panel--comments .comment-composer {
    order: 99; margin-top: auto;
    position: sticky; bottom: 0;
    z-index: 5;   /* 留言動作鈕帶 z3（stretched-link 分層），composer 沒 z 會被穿透 */
    background: var(--bg-primary); padding-block: var(--space-2);
  }
  /* 留言列表在 sheet 內去卡片化（mock 無外框；桌機卡片樣式不動） */
  .mtd-bar__panel--comments .comment-card {
    border: 0;
    border-radius: 0;
    padding: 0;
    background: none;
  }
  .mtd-bar__panel .related-col { display: flex; }   /* 蓋掉 ≤1080 的 display:none */
  .mtd-bar__panel--posts .related-col { flex-direction: column; min-height: 100%; }
  /* 創建貼文 → 置底主 CTA（照稿；2.0 主色）。鈕在 .detail-col__head 裡，釘底掛 head */
  .mtd-bar__panel--posts .detail-col__head {
    order: 99; margin-top: auto;
    position: sticky; bottom: 0;
    z-index: 5;   /* 貼文卡分層鈕帶 z3，CTA 沒 z 會被卡片/圖片蓋住（同 composer） */
    background: var(--bg-primary); padding-block: var(--space-2);
  }
  .mtd-bar__panel--posts .related-create {
    width: 100%; height: 44px; justify-content: center;
    /* 白色玻璃樣式（Dolly 0727；原品牌橘填色改掉） */
    background: var(--glass-bg-default);
    border: 1px solid var(--border-default);
    color: var(--text-primary);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    font-size: 15px;   /* btn--sm 的 caption 12px 對 44px 滿版主 CTA 太小（Dolly 0727） */
  }
  /* 點按不出橘色焦點框（主 CTA 觸控慣例，同 gift 流程） */
  .mtd-bar__panel--posts .related-create:focus,
  .mtd-bar__panel--posts .related-create:focus-visible { outline: none; }
  /* 捲軸貼齊 sheet 右緣（同留言 panel） */
  .mtd-bar__panel--posts { margin-right: -16px; padding-right: 16px; }

  .crowdfund-banner { padding: 22px 20px; }
  .crowdfund-banner__foot { flex-wrap: wrap; }
  .crowdfund-banner__cta { margin-left: 0; }
  /* 劇照／相關影片：手機一張一列往下排（滿寬 16:9），不用兩欄 */
  .media-grid { grid-template-columns: 1fr; gap: 16px; }
  .detail-body { gap: 44px; }

  /* ── 間距節奏（手機規範：tab→內容 16-24、標題→內容 12）──
     量測值：tabs→首 section 曾 56（section padding-top）、section 標題 margin-bottom 曾 0 */
  .mtd-tabs + .section { padding-top: 24px; }
  .detail-section__head { margin-bottom: var(--space-3); }
  .mep-range { margin-top: calc(-1 * var(--space-1)); margin-bottom: var(--space-2); }

  /* ── footer 瘦身（照稿只留法務字）：本頁手機藏品牌區/連結欄，只留 © + 授權聲明 ── */
  body.rf-detail .site-footer__top { display: none; }

  /* ── 字級對齊 mobile 系列頁階層（section 標題 18／卡標 16／內文 14／tag 12）──
     桌機字級直下太重（section 24、卡標 20、內文 16） */
  .detail-section__title { font-size: 18px; line-height: 1.35; }
  .detail-section__count { font-size: 14px; }
  .detail-chip { font-size: 12px; }
  .crowdfund-banner__title { font-size: 16px; }
  .crowdfund-banner__desc { font-size: 14px; line-height: 24px; }
  .crowdfund-banner__supporters-label { font-size: 13px; }
  .comment__text { font-size: 14px; }
  .mtd-bar__panel .post-card__title { font-size: 16px; }
  .mtd-bar__panel .post-card__body { font-size: 14px; }
}

/* ═══════════ 型態變體（作品資料表 ?t= 驅動）═══════════
   body[data-t-form="film|series"] × body[data-t-source="ztor|db|cocreate"]，
   由 title.html 的 titleRegistry 設定；區塊顯隱一律在這裡收，JS 不碰樣式。 */

/* 電影＝無集數列 */
body[data-t-form="film"] [data-sec="episodes"] { display: none; }

/* ── Database 型（未於 Ztor 上架，僅觀影資訊）── */
/* 右上附註：與地區限制提示同款版位（分數列右端）＋同款膠囊皮膚（橘字，見上方共用規則） */
.detail-db-note { display: none; }
body[data-t-source="db"] .detail-scorerow { flex-wrap: wrap; }
body[data-t-source="db"] .detail-scorerow .detail-db-note {
  display: flex; align-items: center; gap: 8px; margin-left: auto; min-width: 0;
}
.detail-db-note__txt { font-size: var(--fs-body-sm); color: var(--brand-500); text-align: right; }
/* 不能購買/領票/送禮；集數列也收（未上架無可播集） */
body[data-t-source="db"] .detail-price,
body[data-t-source="db"] .detail-price--wpticket,
body[data-t-source="db"] .detail-iconbtn--gift,
body[data-t-source="db"] [data-sec="episodes"] { display: none; }

/* 觀影資訊（db 才出；與創作團隊併成雙欄） */
.detail-watchinfo { display: none; }
.detail-duo { display: contents; }
body[data-t-source="db"] .detail-watchinfo { display: block; }
body[data-t-source="db"] .detail-duo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(320px, 440px);
  gap: 48px;
  align-items: start;
}
.watchinfo-region {
  display: inline-flex; align-items: center; gap: 6px;
  margin-left: auto; padding: 8px 14px;
  background: var(--white-alpha-4); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md); color: var(--text-primary);
  font-size: var(--fs-body-sm); cursor: pointer;
}
.watchinfo-region svg { width: 16px; height: 16px; color: var(--text-secondary); }
.watchinfo-panel {
  background: var(--white-alpha-4); border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg); padding: 32px;
}
.watchinfo-providers { display: flex; gap: 24px; justify-content: center; align-items: center; min-height: 96px; }
.watchinfo-provider {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 64px; height: 64px; padding: 0 12px;
  border-radius: var(--radius-md);
  background: var(--white-alpha-8); border: 1px solid var(--border-subtle);
  color: var(--text-primary); font-weight: 700; font-size: 15px; letter-spacing: 0.02em;
}
.watchinfo-provider--cp { background: var(--brand-500); color: #141414; }
.watchinfo-provider--nf { color: #e50914; font-size: 13px; letter-spacing: 0.08em; }

/* footer 聲明雙版：預設站規版；db 換公平處理版 */
.site-footer__notice--db { display: none; }
body[data-t-source="db"] .site-footer__notice--std { display: none; }
body[data-t-source="db"] .site-footer__notice--db { display: block; }
.site-footer__notice-link { color: var(--text-primary); text-decoration: underline; text-underline-offset: 3px; }

/* ── 共創限定：作品的誕生之路（既有 .crowdfund-banner 掛 data-sec="birth" 收斂顯隱）──
   非共創的隱藏要用 body:not(...) 抬到 (0,2,1)：手機分頁的
   body[data-mtab] [data-mtd-panel] 顯示規則同權重，靠這條源序在後把非共創壓回去
   （否則誕生之路會漏到一念無明等非共創手機頁，2026-07-27 實錘） */
[data-sec="birth"] { display: none; }
body:not([data-t-source="cocreate"]) [data-sec="birth"] { display: none; }
body[data-t-source="cocreate"] [data-sec="birth"] { display: block; }

/* 無 backdrop 素材的作品：海報糊化當 hero 底（蓋原本清晰 backdrop 的用法） */
.detail-hero__bg.is-posterbg {
  filter: blur(28px) saturate(1.15) brightness(0.7);
  transform: scale(1.2);
}

/* 雙欄在窄幅收回單欄 */
@media (max-width: 1024px) {
  body[data-t-source="db"] .detail-duo { display: block; }
  body[data-t-source="db"] .detail-watchinfo { margin-top: 48px; }
}

/* 手機分頁 vs .detail-duo（2026-07-27 實錘）：上面的 display:contents（包裝層
   穿透＝子區塊永遠可見）與 db 的 block 都排在手機 panel 隱藏規則之後 →
   創作團隊/觀影資訊漏進劇照/相關影片 tab。此塊必須留在那些規則後面
   （同權重靠源序、db 顯示靠加權），依 mtab 重新收放。 */
@media (max-width: 768px) {
  body .detail-duo[data-mtd-panel] { display: none; }
  body[data-mtab="info"] .detail-duo[data-mtd-panel="info"] { display: contents; }
  body[data-mtab="info"][data-t-source="db"] .detail-duo[data-mtd-panel="info"] { display: block; }
  /* 誕生之路同病：共創顯示規則（0,2,1）源序在 panel 隱藏後 → 衝線的劇照/相關影片
     tab 也看得到。手機非資訊 tab 一律壓回去（Dolly 0727） */
  body:not([data-mtab="info"]) [data-sec="birth"] { display: none; }
}

/* ── 劇照瀏覽模式（Dolly 0727；1.0 套件版型 → 2.0 皮，demo 自刻）──
   結構：頂列（計數＋zoom/播放/全螢幕/格狀/✕）、舞台 ‹ ›、底部縮圖列。
   markup 由 ds.js stills-viewer 注入（單一來源），點劇照 .media-card 開啟。 */
.stillsviewer {
  position: fixed;
  inset: 0;
  z-index: 600;
  display: flex;
  flex-direction: column;
  background: rgba(10, 10, 10, 0.94);
}
.stillsviewer[hidden] { display: none; }
.stillsviewer__top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: calc(12px + env(safe-area-inset-top, 0px)) 20px 12px;
}
.stillsviewer__counter { font-size: 17px; font-weight: var(--fw-medium); color: var(--text-primary); }
.stillsviewer__tools { display: flex; align-items: center; gap: 8px; }
.stillsviewer__tool {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  padding: 0;
  background: none;
  border: 0;
  color: var(--text-primary);
  cursor: pointer;
}
.stillsviewer__tool svg { width: 22px; height: 22px; }
.stillsviewer__tool.is-on { color: var(--brand-500); }
.stillsviewer__tool:focus,
.stillsviewer__tool:focus-visible { outline: none; }
.stillsviewer__stage {
  position: relative;
  flex: 1;
  min-height: 0;
  display: grid;
  place-items: center;
}
.stillsviewer__img {
  max-width: 100%;
  max-height: 100%;
  transition: transform 200ms var(--ease-out);
}
.stillsviewer.is-zoom .stillsviewer__img { transform: scale(1.6); }
.stillsviewer__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  padding: 0;
  background: none;
  border: 0;
  color: rgba(255, 255, 255, 0.72);
  cursor: pointer;
}
.stillsviewer__nav:focus, .stillsviewer__nav:focus-visible { outline: none; }
.stillsviewer__nav svg { width: 28px; height: 28px; }
.stillsviewer__nav--prev { left: 8px; }
.stillsviewer__nav--next { right: 8px; }
.stillsviewer__thumbs {
  display: flex;
  gap: 6px;
  justify-content: center;
  padding: 12px 16px calc(16px + env(safe-area-inset-bottom, 0px));
  overflow-x: auto;
  scrollbar-width: none;
}
.stillsviewer__thumbs::-webkit-scrollbar { display: none; }
.stillsviewer__thumb {
  flex: none;
  width: 64px;
  height: 44px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 4px;
  overflow: hidden;
  background: none;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 150ms var(--ease-out), border-color 150ms var(--ease-out);
}
.stillsviewer__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.stillsviewer__thumb.is-active { opacity: 1; border-color: var(--brand-500); }
.stillsviewer__thumb:focus, .stillsviewer__thumb:focus-visible { outline: none; }
/* 格狀檢視：舞台換成全部劇照的網格 */
.stillsviewer__grid {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: none;
  align-content: start;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: 8px;
  padding: 8px 16px;
  overflow-y: auto;
  background: rgba(10, 10, 10, 0.98);
}
.stillsviewer.is-grid .stillsviewer__grid { display: grid; }
.stillsviewer__gcell {
  padding: 0;
  border: 0;
  border-radius: 6px;
  overflow: hidden;
  background: none;
  cursor: pointer;
  aspect-ratio: 16 / 10;
}
.stillsviewer__gcell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.stillsviewer__gcell:focus, .stillsviewer__gcell:focus-visible { outline: none; }

/* 手機劇照 tab：標題後不放 › 箭頭（1.0 版型；桌機照舊） */
@media (max-width: 768px) {
  [data-mtd-panel="stills"] .detail-section__more { display: none; }
}

/* ── 租借資訊（已購，mock 0727；Dolly 0727 二修＝桌機也要）：
   「您在 X年X月X日 租借」＋條款，body[data-owned] 才顯示 ── */
/* 未上映集（分批上映）：桌機 rail 卡灰化、藏單集價、卡底出上映日、不可點（handler 擋） */
.episode-card.is-coming { opacity: 0.55; cursor: default; }
.episode-card.is-coming .episode-card__price { display: none; }
.episode-card.is-coming::after {
  content: attr(data-air);
  margin-top: auto;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}

.detail-rentinfo { display: none; }
body[data-owned] .detail-rentinfo { display: block; margin-top: 20px; }
.detail-rentinfo__date {
  margin: 0 0 10px;
  font-size: 16px;
  color: var(--text-primary);
}
.detail-rentinfo__date strong { color: var(--brand-500); font-weight: var(--fw-medium); }
.detail-rentinfo__note {
  margin: 0;
  font-size: 14px;
  line-height: 1.8;
  color: var(--text-tertiary);
}
.detail-rentinfo__note strong { color: var(--text-secondary); }
@media (max-width: 768px) {
  /* 已購（watch 基準態）片名列照 mock：英文名跟片名同行（桌機不動） */
  body[data-watch] .detail-titlebar__lead {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
  }
  body[data-watch] .detail-titlebar__lead .detail-subtitle { margin: 0; }
}

/* ── 區塊空狀態（相關影片 0 部，Dolly 0727；桌機/手機共用）── */
.detail-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 48px 0 64px;
}
.detail-empty[hidden] { display: none; }
.detail-empty__ico {
  width: 40px;
  height: 40px;
  background: var(--text-disabled, rgba(255, 255, 255, 0.28));
  -webkit-mask: url('assets/icons/episode.svg') center / contain no-repeat;
          mask: url('assets/icons/episode.svg') center / contain no-repeat;
}
.detail-empty__text {
  margin: 0;
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}

/* 共看派對入場券：作品資料表 wp 旗標關閉時整顆收掉（目前僅我要衝線開賣） */
body[data-t-wp="off"] .detail-price--wpticket { display: none; }

/* ═══ 2026-07-16 Dolly 五項細調（一念無明起） ═══ */

/* 情境展示面板：無 WP 的頁只留 付費/已購買/地區不支援（免費/已領取/活動/登入群組收掉） */
body[data-t-wp="off"] [data-titledemo-grp] [data-demo="free"],
body[data-t-wp="off"] [data-titledemo-grp] [data-demo="owned-free"],
body[data-t-wp="off"] [data-titledemo-live],
body[data-t-wp="off"] [data-wp-titledemo] [data-roomdev-auth],
/* 分隔線是「共看派對維度 ↔ 作品維度」的界線——上半段整個收掉時它也要收，
   否則面板頂端會多一條沒有意義的浮線（Dolly 0731 二輪） */
body[data-t-wp="off"] .wp-roomdev__sep { display: none; }

/* 電影購買 pill 已購買標示（wp-off 頁吃 body[data-owned]；wpticket 已有自己那套不受影響） */
.detail-price__main--fowned { display: none; }
body[data-owned] .detail-actions__primary .detail-price:not(.detail-price--wpticket) .detail-price__main:not(.detail-price__main--fowned) { display: none; }
body[data-owned] .detail-actions__primary .detail-price:not(.detail-price--wpticket) .detail-price__main--fowned { display: inline-flex; }
body[data-owned] .detail-actions__primary .detail-price:not(.detail-price--wpticket) { pointer-events: none; opacity: 0.65; cursor: default; }

/* 已租借電影：再看一次主 CTA（略過評分退回詳情頁後的續看入口）。owned film 時
   顯示、並把價格 pill 收起（已擁有改由「再看一次」表達，留再看一次＋觀看預告片兩顆）。
   series 不動（走右側集數面板），故 scope 到 data-t-form="film"。 */
.detail-rewatch { display: none; align-items: center; gap: 8px; }
.detail-rewatch__ic { width: 20px; height: 20px; flex: none; }
body[data-t-form="film"][data-t-wp="off"][data-owned] .detail-rewatch { display: inline-flex; }
body[data-t-form="film"][data-t-wp="off"][data-owned] .detail-actions__primary .detail-price:not(.detail-price--wpticket) { display: none; }

/* 創作團隊：主要演員 label＋右側製作名單（截圖二 1.0 版型） */
.cast-sublabel {
  margin: 0 0 20px;                    /* 與頭像間距 20px（2026-07-16 Dolly 二調） */
  font-size: var(--fs-body-sm);        /* 14px（與演員名/職稱同級定版） */
  color: var(--text-secondary);
}
.detail-castwrap {
  display: flex;
  gap: 64px;
  align-items: flex-start;
  justify-content: space-between;
}
.detail-castwrap .cast-main { flex: 1 1 auto; min-width: 0; }
.detail-crew {
  flex: 0 0 auto;
  margin: 0;
  padding-right: 24px;                 /* 右欄內縮：長名單（監製 謝國豪、周銓）不貼版面右緣（Dolly 0729） */
  display: grid;
  grid-template-columns: auto auto;
  gap: 14px 28px;
  align-content: start;
}
/* 無演員（cast-main 藏、名單獨立靠左，如我要衝線）＝照 Dolly 0729 參考圖的
   緊湊兩行樣式：職稱貼名字、行距收近；有演員的右欄版型維持 0716 定版不動 */
[data-cast-main][hidden] + .detail-crew { gap: 10px 12px; padding-right: 0; }
.detail-crew dt {
  color: var(--text-secondary);
  font-size: var(--fs-body-sm);        /* 14px（同上定版） */
}
.detail-crew dd {
  margin: 0;
  color: var(--text-primary);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
}
@media (max-width: 1024px) {
  /* 直排時 align-items 必須 stretch：base 的 flex-start 在 column 方向會讓
     .cast-main 縮攏成內容寬（6 張卡 ≈ 800px）橫向撐爆整頁——手機底部列/捲動
     全被這個溢出弄壞（2026-07-27 一念無明手機版實錘） */
  .detail-castwrap { flex-direction: column; gap: 28px; align-items: stretch; }
  .detail-crew { grid-template-columns: auto 1fr; padding-right: 0; }   /* 直排靠左，不需右緣內縮 */
}

/* 留言回覆串（縮排一階、頭像小一號）＋隱藏切換 */
.comment__replylist {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-top: 16px;
}
.comment__replylist[hidden] { display: none; }
.comment--reply .comment__avatar { width: 28px; height: 28px; }

/* composer「正在回覆某人」狀態列 */
.comment-compose__replying {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 0 0 10px;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.comment-compose__replying[hidden] { display: none; }
.comment-compose__replying-name { color: var(--text-primary); }
.comment-compose__replying-x {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  margin-left: 4px;
  border: none;
  border-radius: 50%;
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
}
.comment-compose__replying-x:hover { color: var(--text-primary); background: var(--white-alpha-8); }
.comment-compose__replying-x svg { width: 12px; height: 12px; }

/* ===== 放映疊加版面（body[data-watch]，Figma 163:19082「已租借_整頁內容」）=====
   hero 就地變播放器：模糊底圖（稿 Background hidden）與左側海報收起、
   資訊區攤平全寬；評分圈由 titleWatch 搬進 h1 同排、「寫下你的影評」
   搬到標題列右側（DOM move，退出播放還原）。
   已租借（wp-off 頁 body[data-owned]）→ CTA 列（預告片/價格 pill）整排
   收起、僅留送禮/收藏/喜歡/分享 icon 列（稿 Action Button hidden）。 */
body[data-watch] .detail-hero__bg { display: none; }
body[data-watch] .detail-head__poster { display: none; }
body[data-watch] .detail-scorerow { display: none; }
/* 手機不搬 DOM（titleWatch gate ≥769）→ scorerow 續用：評分＋影評獨立一列（mock 0727） */
@media (max-width: 768px) {
  body[data-watch] .detail-scorerow { display: flex; }
}
/* 搬進標題列的評分圈維持詳情頁尺寸（原尺寸規則 scope 在 .detail-scorerow） */
body[data-watch] .detail-title .ztor-score { width: 50px; height: 50px; --ring-band: 4px; flex: none; }
body[data-watch] .detail-title .ztor-score .ztor-score__value { font-size: 20px; }
body[data-t-wp="off"][data-owned] .detail-actions__primary { display: none; }
/* wp-on 影集已購（Dolly 0728）：照影集邏輯收起 預告片/租借 pill，只留共看派對入場券
   （票有沒有領＝data-wp-owned 自己的態，與租借無關） */
body[data-t-wp="on"][data-t-form="series"][data-owned] .detail-actions__primary > :not(.detail-price--wpticket) { display: none; }
/* …但已租借電影的「詳情頁」（非播放中）要把 CTA 排放回來，改留「再看一次＋觀看預告片」
   （價格 pill 已由上方 display:none 收起）。播放疊加中 [data-watch] 仍只留 icon 列。 */
body[data-t-form="film"][data-t-wp="off"][data-owned]:not([data-watch]) .detail-actions__primary { display: flex; }
/* 已租借電影詳情頁手機（Dolly 0728）：預告片＋再看一次並排——鈕的 width:100%
   （1110，為 grid 軌道寫的）在 flex 容器裡會各佔一行 → flex-basis 0 壓過 width
   平分一行。租借資訊移到按鈕下方＝DOM 搬移（watch-party.js mtd-tabs placeRent，
   跨容器 CSS order 排不到），watch 態維持 head 原位（0727 mock）。 */
@media (max-width: 768px) {
  body[data-t-form="film"][data-t-wp="off"][data-owned]:not([data-watch]) .detail-actions__primary > .btn { flex: 1 1 0; }
}

/* ── watch 態手機：影片下方資訊排版緊縮（Dolly 0727 照 1.0 mock：各列間距 ~-40%）──
   放檔尾＝源序最後才蓋得過前面 head__main 的 gap（0,1,0）。 */
@media (max-width: 768px) {
  body[data-watch] .detail-head__main { gap: 14px; }
  body[data-watch] .detail-rentinfo { margin-top: 6px; }
  /* 影片區→標題再收近（0727：原 24+8=32 → 4+8=12）；body 前綴權重蓋 39-player 的 mb 24 */
  body[data-watch] .player--hero { margin-bottom: 4px; }
}

/* 37-search.css — 搜尋結果頁(feat/new-pages)
   ------------------------------------------------------------
   REVISED (correction pass): 1.0's search is NOT a full-screen
   takeover — it's the header search pill opening a dimmed-background
   + large rounded POPUP (host page stays visible behind a scrim).
   Extends the `.ds-modal` primitive (shell/behaviour in ds.css +
   ds.js — see COMPONENTS.md "Overlays") with a `.search-overlay`
   modifier that only widens the panel into a big dialog — it
   otherwise keeps `.ds-modal`'s native centered-card shape (scrim
   dim+blur, Esc/scrim-click/close, focus-trap, scroll-lock all
   already come free from ds.css + ds.js; nothing re-implemented here).
   Content composition reuses existing DS primitives — `.post-card`,
   `.film-card`, `.ztor-score`, `.glass-tabs` — skinned to 2.0 tokens.
   Info architecture mirrors Ztor 1.0 search 1:1 (see task brief);
   only the visual skin is new.
   ───────────────────────────────────────── */

/* ── shell override: large rounded DIALOG, not full-bleed.
   `#searchOverlay` (ID) is used for the panel-sizing rule so it wins
   over ds.css's own `.ds-modal[data-size="lg"] .ds-modal__panel` rule
   regardless of stylesheet load order/specificity ties — everything
   else (scrim dim+blur, default bg-secondary surface, box-shadow)
   is left as `.ds-modal`'s own default, per brief guidance that the
   default IS the intended shape here.
   CORRECTION PASS (Figma spec): panel widened to ~81vw/1170px (was
   72vw/1080) and pulled closer to the viewport top (~40px) per the
   1440 reference. ── */
.ds-modal.search-overlay {
  z-index: 2000; /* above header/side-rail */
  padding: var(--space-10) var(--space-6); /* ~40px top/bottom — panel sits close to viewport top per spec */
}

/* Frame wraps the panel so the ✕ close button can float OUTSIDE the
   panel, on the scrim (correction: 1.0's close lived inside the
   topbar — Figma has it floating past the panel's top-right corner).
   The frame carries no size of its own; it shrink-wraps the panel so
   `left:100%` on the button always lands exactly at the panel's right
   edge, at any viewport width. */
.search-overlay__frame {
  position: relative;
  width: fit-content;
  max-width: 100%;
}
#searchOverlay .ds-modal__panel {
  width: 81vw;
  max-width: 1170px;
  min-width: 600px;
  /* 高度固定（不隨內容縮放）：打字過程 空狀態↔結果↔查無結果 切換時
     面板高度跳動會嚇到用戶（Dolly 2026-07-09）——捲動改由 __body 承擔 */
  height: calc(100vh - var(--space-10) * 2);
  display: flex;
  flex-direction: column;
  overflow: hidden;
  padding: 0;
  border-radius: var(--radius-2xl); /* 24px — "大型圓角" per brief */
}
#searchOverlay .search-overlay__body {
  flex: 1 1 auto;
  overflow-y: auto;
}

/* ✕ close — floats outside the panel's top-right corner on the scrim
   (reuses `.ds-modal__close`'s circle/glass/icon look from ds.css,
   only the position is page-scoped here). Falls back to an inside
   top-right position at narrow widths (below) where the panel is
   full-bleed and there's no scrim margin to float into. */
.search-overlay__close-ext {
  position: absolute;
  top: 0;
  left: 100%;
  margin-left: var(--space-5);
}

/* ── top bar: input + close ── */
.search-overlay__topbar {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-8) var(--space-10) var(--space-4);
  /* 同 .ds-modal__panel 的 --bg-secondary：topbar 與內容區同色、無接縫（sticky 仍需不透明底） */
  background: var(--bg-secondary);
}
.search-overlay__form {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
  height: 56px;
  padding: 0 var(--space-5);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
}
.search-overlay__form:focus-within { border-color: var(--border-default, rgba(255,255,255,0.16)); }
.search-overlay__icon {
  width: 20px; height: 20px; flex: 0 0 auto;
  /* CJK 字形墨水偏上（em box 下方留降部空間），幾何居中時 icon 視覺偏低
     1.8px（PIL 墨水量測）——光學對齊補償 */
  transform: translateY(-2px);
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/search.svg') center / contain no-repeat;
          mask: url('assets/icons/search.svg') center / contain no-repeat;
}
.search-overlay__input {
  flex: 1 1 auto;
  min-width: 0;
  background: none;
  border: none;
  outline: none;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-lg);
  color: var(--text-primary);
  caret-color: var(--brand-500);
}
.search-overlay__input::placeholder { color: var(--text-tertiary); }
.search-overlay__input::-webkit-search-cancel-button { display: none; }
.search-overlay__clear {
  flex: 0 0 auto;
  width: 24px; height: 24px;
  display: grid; place-items: center;
  border: none; padding: 0; cursor: pointer;
  background: var(--glass-bg-default);
  border-radius: 50%;
  /* × 比外層關閉鈕淡一階，做出「清除輸入 vs 關閉視窗」的層級差（Dolly 2026-07-09） */
  color: var(--text-tertiary);
}
.search-overlay__clear[hidden] { display: none; } /* [hidden] vs .class is equal specificity — force it explicitly */
.search-overlay__clear:hover { background: var(--glass-bg-hover); color: var(--text-primary); }
.search-overlay__clear::before {
  content: '';
  width: 12px; height: 12px;
  background: currentColor;
  -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
          mask: url('assets/icons/close.svg') center / contain no-repeat;
}
.search-overlay__body { padding: 0 var(--space-10) var(--space-24); }

/* ── empty state (no query) ── */
.search-overlay__section { margin-top: var(--space-10); }
.search-overlay__section-title {
  margin: 0 0 var(--space-5);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.search-overlay__posts {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-5);
}
.search-overlay__posts .post-card { padding: var(--space-5); }
.search-overlay__posts .post-card__title { font-size: var(--fs-h5); margin-bottom: var(--space-2); }
.search-overlay__posts .post-card__body {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: var(--fs-body-sm);
}

/* "翻譯內容" — a 4th action alongside ♥/💬/分享 (per Figma; not part of the
   shared `.post-card` component elsewhere on the site, so page-scoped
   here rather than edited into 18-community-page.css). Sits inside
   `.post-card__stats` so it groups left with the counters, matching the
   spec — bookmark stays alone on the far right via the row's
   space-between. */
.post-card__translate {
  padding: 4px;
  margin: -4px;
  border: none;
  background: none;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 150ms ease;
}
.post-card__translate:hover { color: var(--text-secondary); }
/* 精選好片單＝固定 6 部（Dolly 2026-07-09）：桌機 6 欄等分撐滿容器寬、
   不橫滑；卡寬隨容器伸縮（minmax(0,…) 防 1fr 撐爆） */
.search-overlay__curated {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: var(--space-4);
}
.search-overlay__curated .film-card { width: auto; }

/* Price badge — CORRECTION: reverted to `.film-card__price`'s site-wide
   default (bottom-RIGHT). An earlier pass flipped it to bottom-left and
   invented a bespoke red "新上架" flag; the Figma spec instead reuses the
   existing site-wide `.film-card__ribbon` ("最新", top-left, on EVERY
   card here) — see markup, no page-local override needed for either. */

/* ── recent searches (新增 per brief) ──
   Edit flow (新增, per behaviour spec): default = label + chips + grey
   "編輯". Click 編輯 → `.is-editing` on the section → each chip grows an
   "×" (delete one) and the footer swaps to "全部清除"(red) + "完成"(white).
   All demo state, wired in the page script below — no persistence. */
/* 單行排列：label＋chips＋編輯（或 全部清除/完成）同一行，照 Figma 15937-462438 */
.search-overlay__recent {
  margin-top: var(--space-2);
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3) var(--space-4);
}
.search-overlay__recent-label {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.search-overlay__recent-edit {
  padding: 0;
  border: 0;
  background: none;
  color: var(--text-tertiary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.search-overlay__recent-edit:hover { color: var(--text-primary); }
.search-overlay__recent-editactions { display: none; align-items: center; gap: var(--space-5); }
.search-overlay__recent.is-editing .search-overlay__recent-edit { display: none; }
.search-overlay__recent.is-editing .search-overlay__recent-editactions { display: flex; }
.search-overlay__recent-clearall,
.search-overlay__recent-done {
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
}
.search-overlay__recent-clearall { color: var(--error-500); }
.search-overlay__recent-clearall:hover { color: var(--error-700); }
.search-overlay__recent-done { color: var(--text-primary); }
.search-overlay__recent-done:hover { color: var(--text-secondary); }

.search-overlay__recent-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.search-recent-chip {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius-pill);
  background: var(--glass-bg-default);
  border: 1px solid var(--border-subtle);
  transition: background 150ms var(--ease-out);
}
.search-recent-chip:hover { background: var(--glass-bg-hover); }
.search-recent-chip__text {
  padding: 8px 16px;
  border: 0;
  background: none;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.search-recent-chip:hover .search-recent-chip__text { color: var(--text-primary); }
.search-overlay__recent.is-editing .search-recent-chip__text { cursor: default; padding-right: 4px; }

/* "×" delete — hidden until editing; only takes space once shown so the
   default chip keeps its plain pill shape. */
.search-recent-chip__remove {
  display: none;
  width: 22px;
  height: 22px;
  margin-right: 8px;
  align-items: center;
  justify-content: center;
  border: 0;
  /* 裸 ×，無圓形底（Dolly 2026-07-09 拍板） */
  background: none;
  color: var(--text-tertiary);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
/* × 字形墨水在 em box 偏上，與 14px CJK 文字並排時視覺偏高 1.7px
   （PIL 量測）——光學補償（Dolly 2026-07-10） */
.search-recent-chip__remove span { transform: translateY(1.5px); }
/* hover 只提亮 × 本體，不加底（方形底效果不佳，Dolly 2026-07-09） */
.search-recent-chip__remove:hover { color: var(--text-primary); }
.search-overlay__recent.is-editing .search-recent-chip__remove { display: inline-flex; }

/* divider between 熱門討論 and 精選好片單 (item 4 per brief) */
.search-overlay__divider { margin: var(--space-10) 0 0; border: 0; border-top: 1px solid var(--border-subtle); }
.search-overlay__divider + .search-overlay__section { margin-top: var(--space-8); }

/* ── results state ── */
.search-tabs { margin: 0 0 var(--space-8); }
.search-tabs__count {
  margin-left: var(--space-2);
  font-size: 12px;
  font-weight: var(--fw-semibold);
  color: var(--text-tertiary);
}
.glass-tabs__item--active .search-tabs__count { color: var(--yellow-500); }

/* 分隔線畫在「可見區塊之間」（人物/用戶/電影…），不畫在列與列之間
   （Dolly 2026-07-09，照 1.0）。tab 篩選用 [hidden] 藏區塊，所以這裡用
   :not([hidden]) ~ :not([hidden])：篩選後的第一個可見區塊不畫線、
   間距歸零——與「全部」分頁第一區塊（人物）對齊 */
.search-overlay__group[hidden] { display: none; }
.search-overlay__group:not([hidden]) ~ .search-overlay__group:not([hidden]) {
  margin-top: var(--space-8);
  padding-top: var(--space-8);
  border-top: 1px solid var(--border-subtle);
}
.search-overlay__group-title {
  margin: 0 0 var(--space-5);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}

.search-result-row {
  display: flex;
  gap: var(--space-5);
  /* 四邊 padding＋負 margin-inline：hover 底色塊比內容大一圈、
     又不讓內容偏離段落標題的左緣 */
  padding: var(--space-4);
  margin-inline: calc(-1 * var(--space-4));
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: inherit;
  transition: background-color 160ms var(--ease-out);
}
.search-result-row__poster {
  position: relative;   /* 錨定 .film-card__price 爆米花價（Ztor 上架片） */
  flex: 0 0 120px;
  width: 120px;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
}
.search-result-row__poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
.search-result-row__body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.search-result-row__top { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4); }
.search-result-row__titles { min-width: 0; }
.search-result-row__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.search-result-row__original { margin: 2px 0 0; font-size: var(--fs-caption); color: var(--text-tertiary); }
.search-result-row__meta { margin: 0; font-size: var(--fs-body-sm); color: var(--text-secondary); }
.search-result-row__desc {
  margin: 0;
  max-width: 78ch;
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body);
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.search-result-row__platforms { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-1); }
.search-result-row__platforms-label { font-size: var(--fs-caption); color: var(--text-tertiary); margin-right: 2px; }

.search-platform-badge {
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
  font-size: 11px;
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  white-space: nowrap;
}
.search-platform-badge--brand {
  background: var(--yellow-500);
  border-color: var(--yellow-500);
  color: var(--brand-ink);
  font-family: var(--font-cjk-display);
}

/* 更多鈕：置中、無 pill、品牌橘（Dolly 2026-07-09，照 Figma 各段落的置中紅字樣式） */
.search-overlay__group-more {
  margin: var(--space-4) auto 0;
  height: 40px;
  padding: 0 var(--space-3);
  background: none;
  border: none;
  color: var(--brand-500);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  display: flex;
  align-items: center;
}
.search-overlay__group-more:hover { color: var(--text-primary); }
/* 明確 display 會蓋掉 [hidden] 的 UA display:none（同 __clear[hidden] 的坑）——強制之 */
.search-overlay__group-more[hidden] { display: none; }
.search-overlay__group-more::after {
  content: '';
  width: 12px; height: 12px;
  margin-left: 4px;
  display: inline-block;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
  transition: transform 200ms var(--ease-out);
}
.search-overlay__group-more.is-expanded::after { transform: rotate(180deg); }
/* generic collapse: any list/grid marked data-collapsed hides its .is-extra
   children — powers 電影/劇集/人物/貼文's "更多" buttons off ONE mechanism
   (see page script's generic [data-more-toggle] handler).
   只在「全部」分頁收合（data-active-tab 缺席＝初始態＝全部）；
   切到個別分頁＝該類別全展開、「更多」鈕一併藏（Dolly 2026-07-09） */
#searchResults:not([data-active-tab]) [data-collapsed="true"] > .is-extra,
#searchResults[data-active-tab="all"] [data-collapsed="true"] > .is-extra { display: none; }
#searchResults[data-active-tab]:not([data-active-tab="all"]) .search-overlay__group-more { display: none; }

/* ── 人物 (people) — circular avatar + name, per Figma. Reuses the site's
   flex-wrap rail pattern (no new grid system); "更多" reveals the extra
   avatars past the first row via the same [data-collapsed] mechanism as
   電影/劇集. ── */
.search-people {
  /* 頭像間距＝(容器寬 − 10×88px 頭像) ÷ 9 個縫（Dolly 2026-07-09 三改：
     由內容寬回推，一排 10 個恰好撐滿；不滿 10 個沿用同一間距靠左排）；
     「一排最多 10 個、超過收進更多」由資料規則（.is-extra）控制；
     max() 保底：窄版容器塞不下 10 顆時 calc 會變負，鎖最小 --space-2 */
  display: flex;
  flex-wrap: wrap;
  row-gap: var(--space-6);
  column-gap: max(var(--space-2), calc((100% - 10 * 88px) / 9));
}
.search-people__item {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  width: 88px;
  text-decoration: none;
  color: inherit;
}
.search-people__avatar {
  width: 88px;
  height: 88px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
}
.search-people__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.search-people__name {
  max-width: 100%;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── 用戶 (users) — avatar + name + role pill + post count, per Figma. ── */
.search-users {
  display: flex;
  flex-wrap: wrap;
  /* 與 .search-people 同一條間距公式——兩排頭像落在同一節奏上（Dolly 2026-07-09） */
  row-gap: var(--space-6);
  column-gap: max(var(--space-2), calc((100% - 10 * 88px) / 9));
}
.search-user-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  /* 卡寬＝頭像寬（與 .search-people__item 同 88px）——兩排頭像圓的視覺間距
     才會一樣是 --space-4；較寬的名字/身分 pill 置中懸挑出卡外（Dolly 2026-07-09） */
  width: 88px;
  text-decoration: none;
  color: inherit;
}
.search-user-card__avatar {
  position: relative;   /* 認證標章的定位參考 */
  width: 88px;
  height: 88px;
  border-radius: 50%;
  /* 裁切改由 img 自己的 border-radius 做（0806）：overflow:hidden 會把
     貼右下的認證標章一起剪掉 */
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
}
.search-user-card__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; border-radius: inherit; }
/* 認證標章（0806 Dolly 二改：放名字後面、不貼頭像）：verify.svg 的勾是挖空的——
   底下仍墊一顆深色小圓盤，勾在任何底色上都讀成深色 */
.search-user-card__verify {
  position: relative;
  display: inline-block;
  width: 18px;
  height: 18px;
  margin-left: 4px;
  vertical-align: -3px;   /* 對齊中文字面中線 */
  flex: 0 0 auto;
}
.search-user-card__verify::before {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: var(--radius-pill);
  background: var(--bg-secondary);
}
.search-user-card__verify::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--brand-500);
  -webkit-mask: url('assets/icons/verify.svg') center / contain no-repeat;
  mask: url('assets/icons/verify.svg') center / contain no-repeat;
}
/* 商店變體（0806 Dolly：搜尋新增「商店」類別；二改：照片改圓形與人物/用戶
   一致，不再用商城方角），其餘沿用 user-card 的名字/身分列 */
.search-user-card__name {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  /* 單行懸挑：比 88px 卡寬的名字置中溢出兩側；上限 100px（< 卡距 104px）
     保證相鄰長名不相碰，再長才截字 */
  white-space: nowrap;
  width: max-content;
  max-width: 100px;
  overflow: hidden;
  text-overflow: ellipsis;
}
.search-user-card__role {
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  background: var(--glass-bg-default);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  font-size: 11px;
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}
.search-user-card__posts { margin: 0; font-size: var(--fs-caption); color: var(--text-tertiary); }

/* ── no-results state (NOT in 1.0 — added per brief, exception 1) ── */
.search-overlay__noresult {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* 固定高面板內垂直置中（扣掉 body 的下 padding 感受置中） */
  min-height: 62vh;
  padding: var(--space-12) 0;
  color: var(--text-secondary);
}
.search-overlay__noresult-icon {
  width: 56px; height: 56px;
  margin-bottom: var(--space-5);
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/search.svg') center / contain no-repeat;
          mask: url('assets/icons/search.svg') center / contain no-repeat;
  opacity: 0.5;
}
.search-overlay__noresult-title {
  margin: 0 0 var(--space-2);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.search-overlay__noresult-hint { margin: 0; max-width: 40ch; font-size: var(--fs-body-sm); }

/* ── responsive: no horizontal overflow at narrow widths ── */
@media (max-width: 960px) {
  #searchOverlay .ds-modal__panel { width: 100%; min-width: 0; border-radius: var(--radius-xl); }
  .search-overlay__topbar { padding: var(--space-6) var(--space-4) var(--space-3); }
  .search-overlay__body { padding: 0 var(--space-4) var(--space-16); }
  .search-overlay__posts { grid-template-columns: 1fr; }
  .search-result-row__poster { flex-basis: 76px; width: 76px; }
}

/* ── hover 回饋（Dolly 2026-07-09）：
   貼文卡補與 .film-card 同款上浮（原本只有 cover 圖 scale 1.02，太含蓄）；
   結果列補底色 hover（原本完全無回饋）。數值對齊 15-film-card.css 的 DS 慣例 ── */
.search-overlay__posts .post-card { transition: transform 200ms var(--ease-out); }
.search-overlay__posts .post-card:hover { transform: translateY(-4px); }
/* 結果列 hover＝整塊底色微亮（Dolly 2026-07-09 二改，取代標題轉橘版；
   底色塊靠 .search-result-row 的 padding＋負 margin 比內容大一圈） */
.search-result-row:hover { background: var(--white-alpha-4); }

/* 海報 2:3 全站通則：flex 預設 stretch 會把 poster 拉成跟整列文字等高、
   蓋掉 aspect-ratio——鎖回自身高度 */
.search-result-row__poster { align-self: flex-start; }

/* 可觀看平台 icon（assets/images/ott/ 現成素材，取代文字徽章） */
.search-platform-icon {
  height: 24px;
  width: auto;
  display: block;
  border-radius: 6px;
}

/* ── 輸入後停留提示（結果列底部，1.5s 淡入）＋ 外側 ✕ 對齊面板頂
   （ds.css 的 .ds-modal__close 自帶 top:16px 且載入順序在 bundle 後，
   需 ID 提權蓋過）── */
.search-overlay__morehint {
  margin: var(--space-10) 0 0;
  text-align: center;
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  opacity: 0;
  transition: opacity 300ms var(--ease-out);
}
.search-overlay__morehint.is-visible { opacity: 1; }
/* 底部提示只在「全部」分頁出現（Dolly 2026-07-09）；其他分頁整個不佔位 */
#searchResults[data-active-tab]:not([data-active-tab="all"]) .search-overlay__morehint { display: none; }
#searchOverlay .search-overlay__close-ext { top: 0; }

/* ── 分類 tabs 改「獨立項目」排法（Dolly 2026-07-09，照 1.0 找片頁
   平台篩選列）：去掉整條玻璃膠囊容器、項目散開；
   選中膠囊維持 DS 原生 ghost 樣式（.glass-tabs__item--active＝
   btn--yellow-ghost 那套品牌光暈，不另蓋）。 ── */
#searchOverlay .glass-tabs {
  padding: 0;
  background: none;
  border: none;
  gap: var(--space-2);
}
/* 未選中也帶淡淡的 pill 底（Dolly 2026-07-09），hover 微提亮 */
#searchOverlay .glass-tabs__item {
  padding: 0 var(--space-5);
  background: var(--white-alpha-4);
}
#searchOverlay .glass-tabs__item:hover { background: var(--glass-bg-default); }
/* 選中膠囊＝中性 .btn--ghost 配方（白玻璃光暈，非品牌色；Dolly 2026-07-09 拍板）。
   數值照抄 02-button.css .btn--ghost——那是 DS 的 canonical ghost 配方 */
#searchOverlay .glass-tabs__item--active,
#searchOverlay .glass-tabs__item--active:hover {
  color: var(--text-primary);
  font-weight: var(--fw-bold);
  /* L-kit neutral glass (was a copy of the old ghost recipe) */
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  backdrop-filter: var(--lg-frost);
  -webkit-backdrop-filter: var(--lg-frost);
}
#searchOverlay .glass-tabs__item--active .search-tabs__count { color: var(--text-secondary); }

/* ═══════════════════════════════════════════
   手機版全螢幕接管 — .is-mobile（ds.js 依「視窗 ≤600」或「body.rf-mobile
   手機獨立頁」掛上；手機頁不論預覽視窗多寬都走這套）。
   結構照 1.0 /search 手機頁（Dolly 2026-07-09 指定參考）：
   ← 返回＋輸入列（sticky）、近期搜索橫滑一行、熱門討論單欄、
   精選好片單雙欄、結果態 tabs 橫滑＋人物/用戶改直列、
   區塊「更多」移到標題列右側。skin 全走 2.0 token。
   選擇器帶 #searchOverlay 提權：桌機 sizing 規則多為 ID 級。
   ═══════════════════════════════════════════ */
.search-overlay__back { display: none; } /* 桌機隱藏（桌機關閉＝外側 ✕） */

#searchOverlay.is-mobile { padding: 0; }
/* frame 桌機是 fit-content（讓外側 ✕ 貼面板右緣）——手機必須撐滿，
   否則 panel 的 width:100% 沒有定寬父層可解析，會隨內容伸縮
   （建議列一出、內容變窄，面板就跟著縮） */
#searchOverlay.is-mobile .search-overlay__frame { width: 100%; }
#searchOverlay.is-mobile .ds-modal__panel {
  width: 100%;
  min-width: 0;
  max-width: none;
  height: 100dvh;
  max-height: none; /* ds.css 的 .ds-modal__panel 帶 max-height(100vh−48)，不解除會上下留黑帶 */
  border-radius: 0;
}
/* 手機獨立頁在寬視窗預覽時，比照 430 預覽框置中（同 mobile.html 的 gate 慣例） */
@media (min-width: 480px) {
  #searchOverlay.is-mobile .ds-modal__panel { max-width: 430px; margin-inline: auto; }
}
#searchOverlay.is-mobile .search-overlay__close-ext { display: none; }
#searchOverlay.is-mobile .search-overlay__back {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: 40px;
  height: 40px;
  margin-left: calc(-1 * var(--space-2)); /* 箭頭墨水光學貼齊 16px gutter */
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-primary);
}
#searchOverlay.is-mobile .search-overlay__back::before {
  content: '';
  width: 22px;
  height: 22px;
  background: currentColor;
  -webkit-mask: url('assets/icons/arrow_left.svg') center / contain no-repeat;
          mask: url('assets/icons/arrow_left.svg') center / contain no-repeat;
}
#searchOverlay.is-mobile .search-overlay__topbar {
  gap: var(--space-2);
  padding: max(var(--space-3), env(safe-area-inset-top)) var(--space-4) var(--space-3);
}
#searchOverlay.is-mobile .search-overlay__form {
  height: 44px;
  /* 底 padding 4px＝整組內容（icon＋文字＋✕）垂直中心上移 2px
     （Dolly 2026-07-09：icon 跟字要在框內一起往上一點） */
  padding: 0 var(--space-4) 4px;
}
#searchOverlay.is-mobile .search-overlay__body { padding: 0 var(--space-4) var(--space-24); }

/* ── 空狀態：近期搜索一行橫滑（照 1.0），label＋編輯同列 ── */
#searchOverlay.is-mobile .search-overlay__recent {
  margin-top: var(--space-4);
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-3);
}
/* DOM 順序是 label→chips→編輯，grid 自動排會把「編輯」擠到第三列——
   明定行位：label＋編輯同第 1 列、chips 整行第 2 列 */
#searchOverlay.is-mobile .search-overlay__recent-label { grid-row: 1; grid-column: 1; }
#searchOverlay.is-mobile .search-overlay__recent-edit,
#searchOverlay.is-mobile .search-overlay__recent-editactions { grid-row: 1; grid-column: 2; justify-self: end; }
#searchOverlay.is-mobile .search-overlay__recent-chips {
  grid-row: 2;
  grid-column: 1 / -1;
  flex-wrap: nowrap;
  overflow-x: auto;
  /* 滿版滑出 gutter：左右負 margin＋補回 padding */
  margin-inline: calc(-1 * var(--space-4));
  padding-inline: var(--space-4);
  scrollbar-width: none;
}
#searchOverlay.is-mobile .search-overlay__recent-chips::-webkit-scrollbar { display: none; }
#searchOverlay.is-mobile .search-recent-chip { flex: 0 0 auto; }

#searchOverlay.is-mobile .search-overlay__section { margin-top: var(--space-8); }
#searchOverlay.is-mobile .search-overlay__section-title,
#searchOverlay.is-mobile .search-overlay__group-title { font-size: var(--fs-body-lg); } /* 18px＝手機標題慣例 */
#searchOverlay.is-mobile .search-overlay__posts { grid-template-columns: 1fr; gap: var(--space-4); }
/* 精選好片單：橫滑 rail 改雙欄格（照 1.0 手機） */
#searchOverlay.is-mobile .search-overlay__curated {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr)); /* minmax(0,…) 防 1fr 撐爆 */
  gap: var(--space-4);
  overflow: visible;
  padding-bottom: 0;
}
#searchOverlay.is-mobile .search-overlay__curated .film-card { width: auto; flex: none; }

/* ── 結果態 ── */
#searchOverlay.is-mobile .search-tabs { margin-bottom: var(--space-6); }
/* 區塊「更多」→ 標題列右側（照 1.0 手機；桌機是列表下方置中）。
   DOM 順序不動（title→list→button），用 grid 把按鈕放回第一列右欄 */
#searchOverlay.is-mobile .search-overlay__group {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
}
/* ⚠️ 上面明確 display:grid 會蓋掉 tab 篩選用的 [hidden]（UA display:none）——
   同權重補回，否則手機點人物/用戶 tab 沒有篩選效果（memory 老雷實錘） */
#searchOverlay.is-mobile .search-overlay__group[hidden] { display: none; }
#searchOverlay.is-mobile .search-overlay__group-title { grid-column: 1; grid-row: 1; }
#searchOverlay.is-mobile .search-overlay__group > :not(.search-overlay__group-title):not(.search-overlay__group-more) { grid-column: 1 / -1; }
#searchOverlay.is-mobile .search-overlay__group-more {
  grid-column: 2;
  grid-row: 1;
  margin: 0 0 var(--space-5); /* 與標題同列、同下距 */
  height: auto;
  padding: 0;
}

/* 人物/用戶：橫排頭像牆 → 直列（照 1.0 手機的列表式） */
#searchOverlay.is-mobile .search-people,
#searchOverlay.is-mobile .search-users {
  flex-direction: column;
  align-items: stretch;
  row-gap: 0;
  column-gap: 0;
}
#searchOverlay.is-mobile .search-people__item {
  flex-direction: row;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-2) 0;
}
#searchOverlay.is-mobile .search-people__avatar,
#searchOverlay.is-mobile .search-user-card__avatar { width: 48px; height: 48px; }
#searchOverlay.is-mobile .search-people__name {
  max-width: none;
  font-size: var(--fs-body);
  text-align: left;
}
#searchOverlay.is-mobile .search-user-card {
  display: grid;
  grid-template-columns: 48px auto 1fr;
  align-items: center;
  column-gap: var(--space-4);
  row-gap: var(--space-1);
  width: 100%;
  padding: var(--space-2) 0;
}
#searchOverlay.is-mobile .search-user-card__avatar { grid-column: 1; grid-row: 1 / span 2; }
#searchOverlay.is-mobile .search-user-card__name {
  grid-column: 2 / -1;
  grid-row: 1;
  justify-self: start;
  width: auto;
  max-width: none;
}
#searchOverlay.is-mobile .search-user-card__role { grid-column: 2; grid-row: 2; justify-self: start; }
#searchOverlay.is-mobile .search-user-card__posts { grid-column: 3; grid-row: 2; justify-self: start; }

/* 結果列：海報縮 64px（照 1.0 手機）、hover 塊負 margin 縮到手機 gutter 內 */
#searchOverlay.is-mobile .search-result-row {
  gap: var(--space-4);
  padding: var(--space-3);
  margin-inline: calc(-1 * var(--space-3));
}
/* 海報加大（64→96）；2:3 改鎖在 img 上，海報格變直欄——爆米花售價
   移到海報下方（Dolly 2026-07-09） */
#searchOverlay.is-mobile .search-result-row__poster {
  flex-basis: 96px;
  width: 96px;
  aspect-ratio: auto;   /* 高度改由 img＋價籤自然撐開 */
  overflow: visible;
  background: none;
  border: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
#searchOverlay.is-mobile .search-result-row__poster img {
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
}
#searchOverlay.is-mobile .search-result-row__poster .film-card__price {
  position: static;
  align-self: flex-start;
}
/* 年份/時長 13px；簡介 14px＋行距 1.7（Dolly 2026-07-09 二改；
   原 --lh-body 27px＝1.93 倍太鬆） */
#searchOverlay.is-mobile .search-result-row__meta { font-size: 13px; }
#searchOverlay.is-mobile .search-result-row__desc { font-size: var(--fs-body-sm); line-height: 1.7; }
/* 「更多」不帶箭頭（手機；桌機保留當展開/收合指示） */
#searchOverlay.is-mobile .search-overlay__group-more::after { display: none; }
#searchOverlay.is-mobile .search-result-row__desc { max-width: none; }
#searchOverlay.is-mobile .search-platform-icon { height: 20px; }

/* ── 手機輸入列字級（Dolly 2026-07-09 拍板）：引導字與輸入字統一 16px
   （文案改 A 案短句後放得下；16px 也是 iOS 聚焦不觸發自動縮放的下限） */
#searchOverlay.is-mobile .search-overlay__input { font-size: var(--fs-body); }
/* 放大鏡光學對齊：桌機的 translateY(-2px) 是對 56px 膠囊＋18px 字校的，
   手機 44px 膠囊＋16px 字下 icon 墨水中心偏高 2.8px（PIL 量測）——改 +1px 收斂。
   引導字與輸入字同級後兩態幾何相同，不再需要 :placeholder-shown 分態補償 */
#searchOverlay.is-mobile .search-overlay__icon { transform: translateY(1px); }
/* Chromium 會把 placeholder 畫得比輸入值高 ~2px（同 16px 同字體，PIL 實測）——
   空欄態把 4px 墊換到頂邊（整組下移回中）、icon 反向 -1px 對墨水；
   開始打字即回上面的常態值 */
#searchOverlay.is-mobile .search-overlay__form:has(.search-overlay__input:placeholder-shown) { padding: 4px var(--space-4) 0; }
#searchOverlay.is-mobile .search-overlay__form:has(.search-overlay__input:placeholder-shown) .search-overlay__icon { transform: translateY(-1px); }

/* ── 手機熱門討論：貼文卡改 1.0 緊湊列表版（Dolly 2026-07-09 指定參考）──
   無卡片底、縮圖左（120px 16:10）、右欄標題 1 行＋摘要 2 行截字、
   動作列整行在下；「更多」展開鈕與「翻譯內容」不進手機版；
   head 的 ⋯ 移到動作列右端（1.0 位置：收藏旁） */
#searchOverlay.is-mobile .search-overlay__posts { gap: var(--space-8); }
#searchOverlay.is-mobile .search-overlay__posts .post-card {
  position: relative;
  display: grid;
  grid-template-columns: 120px minmax(0, 1fr);
  grid-template-areas:
    "head  head"
    "cover title"
    "cover body"
    "actions actions";
  column-gap: var(--space-3);
  padding: 0;
  background: none;
  border: none;
  box-shadow: none;
  border-radius: 0;
}
#searchOverlay.is-mobile .search-overlay__posts .post-card:hover { transform: none; }
#searchOverlay.is-mobile .search-overlay__posts .post-card__head { grid-area: head; margin-bottom: var(--space-3); }
/* 第一排：頭像＋名稱置左、日期推到最右（Dolly 2026-07-09）；頭像縮 32px */
#searchOverlay.is-mobile .search-overlay__posts .post-card__author {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex: 1 1 auto;
  min-width: 0;
}
#searchOverlay.is-mobile .search-overlay__posts .post-card__avatar { width: 32px; height: 32px; }
#searchOverlay.is-mobile .search-overlay__posts .post-card__author-meta {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
#searchOverlay.is-mobile .search-overlay__posts .post-card__cover {
  grid-area: cover;
  align-self: start;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius-sm);
  overflow: hidden;
}
#searchOverlay.is-mobile .search-overlay__posts .post-card__cover-bg { display: none; } /* 縮圖用不到模糊底 */
#searchOverlay.is-mobile .search-overlay__posts .post-card__cover-img { width: 100%; height: 100%; object-fit: cover; }
#searchOverlay.is-mobile .search-overlay__posts .post-card__title {
  grid-area: title;
  align-self: start;
  margin: 0 0 2px; /* 標題貼近說明文字（Dolly 2026-07-09） */
  font-size: var(--fs-body);
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
#searchOverlay.is-mobile .search-overlay__posts .post-card__body {
  grid-area: body;
  align-self: start;
  margin: 0; /* <p> 基底上邊距會在標題與說明間多塞 16px（UA margin 老雷） */
  /* 行高收 1.5：標題＋兩行說明 ≈ 68px，視覺與左側 75px 縮圖等高
     （原繼承 1.8 會比圖高出 18px） */
  line-height: 1.5;
}
#searchOverlay.is-mobile .search-overlay__posts .post-card__expand,
#searchOverlay.is-mobile .search-overlay__posts .post-card__translate { display: none; }
#searchOverlay.is-mobile .search-overlay__posts .post-card__actions {
  grid-area: actions;
  margin-top: var(--space-3);
  padding-right: 40px; /* 讓出右端給絕對定位過來的 head ⋯ */
}
#searchOverlay.is-mobile .search-overlay__posts .post-card__more {
  position: absolute;
  right: 0;
  bottom: 0;
  transform: translateY(4px); /* 與收藏鈕中心對齊（rect 量測差 4px） */
}

/* ── 關鍵字建議列（手機打字的中間步驟，照 Figma Search_Typing；
      桌機不進此態）：放大鏡＋建議詞，打過的字亮、補全灰一階；
      第一列＝輸入回聲（全亮粗體） ── */
.search-overlay__suggest[hidden] { display: none; }
.search-overlay__suggest { padding-top: var(--space-2); }
.search-suggest__row {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-4) 0;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
}
.search-suggest__icon {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/search.svg') center / contain no-repeat;
          mask: url('assets/icons/search.svg') center / contain no-repeat;
}
.search-suggest__text {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.search-suggest__row--echo .search-suggest__text {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-semibold);
}
.search-suggest__dim { color: var(--text-tertiary); }

/* ── 手機結果態：每類別收合時只出前 3 個（Dolly 2026-07-09，照 Figma）──
   與桌機同一套 [data-collapsed] 機制，只是手機加「第 4 項起藏」；
   展開（data-collapsed=false）即全出。仍只在「全部」分頁收合。 */
#searchOverlay.is-mobile #searchResults:not([data-active-tab]) [data-collapsed="true"] > :nth-child(n+4),
#searchOverlay.is-mobile #searchResults[data-active-tab="all"] [data-collapsed="true"] > :nth-child(n+4) { display: none; }
/* >3 項的類別在手機要有「更多」——桌機 JS 只在有 .is-extra 時顯示按鈕
   （用戶 4 個桌機全排、無按鈕），手機用 :has 依「第 4 個子項存在」重新顯示 */
#searchOverlay.is-mobile:not(.is-catview) #searchResults:not([data-active-tab]) .search-overlay__group:has(> [data-collapsed] > :nth-child(4)) .search-overlay__group-more,
#searchOverlay.is-mobile:not(.is-catview) #searchResults[data-active-tab="all"] .search-overlay__group:has(> [data-collapsed] > :nth-child(4)) .search-overlay__group-more { display: flex; }

/* ── 手機分類 tabs 整體縮小（Dolly 2026-07-09）── */
#searchOverlay.is-mobile .glass-tabs__item {
  height: 32px;
  padding: 0 var(--space-3);
  font-size: 13px;
}
#searchOverlay.is-mobile .search-tabs__count { font-size: 11px; margin-left: var(--space-1); }

/* 底部提示手機固定兩行斷句（Dolly 2026-07-09）：
   「找不到想找的內容嗎？／試試輸入更完整的…」各佔一行，不讓瀏覽器亂斷 */
#searchOverlay.is-mobile .search-overlay__morehint span { display: block; }

/* 搜尋 overlay 開著時藏右下 dev 狀態展示膠囊（Dolly 2026-07-09，桌機手機皆然）——
   它 z-index 比 overlay 高會浮在搜尋內容上。:has 對 is-open 反應，關窗自動回來 */
body:has(#searchOverlay.is-open) .wp-roomdev { display: none; }

/* ── 類別子頁 .is-catview（手機點「更多」的第二層，照 Figma
      Search_Actor/Director / Search_User）：topbar 換成 ←＋置中類別標題、
      內容只留該類別全列表＋「搜尋：關鍵字」。⚠️ 本區多條規則與前面
      is-mobile 規則同特異度，靠檔案順序（在後者之後）取勝——別搬動位置 ── */
.search-overlay__cattitle,
.search-overlay__catquery { display: none; }
#searchOverlay.is-mobile .search-overlay__back--cat { display: none; } /* 平時藏（要寫在 is-mobile 顯示規則後） */
#searchOverlay.is-catview .search-overlay__form,
#searchOverlay.is-catview .search-overlay__back:not(.search-overlay__back--cat) { display: none; }
#searchOverlay.is-catview .search-overlay__back--cat { display: grid; }
#searchOverlay.is-catview .search-overlay__cattitle {
  display: block;
  flex: 1 1 auto;
  text-align: center;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body-lg); /* 18px＝m-pagebar 置中標題慣例 */
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
/* 右側補一段等於 ← 佔寬的 spacer（40−負8=32px），標題才是真置中 */
#searchOverlay.is-catview .search-overlay__topbar::after { content: ''; flex: 0 0 32px; }
#searchOverlay.is-catview .search-overlay__catquery {
  display: block;
  margin: var(--space-3) 0 var(--space-2);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
#searchOverlay.is-catview .search-tabs,
#searchOverlay.is-catview .search-overlay__morehint,
#searchOverlay.is-catview .search-overlay__group-more,
#searchOverlay.is-catview .is-cat-current .search-overlay__group-title { display: none; }
#searchOverlay.is-catview [data-search-group]:not(.is-cat-current) { display: none; }
/* 子頁的 group 是唯一可見區塊——它可能排在其他（display:none 但沒帶 [hidden]）
   group 之後，區塊分隔線規則仍會命中，明確歸零 */
#searchOverlay.is-catview .is-cat-current {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/* ═══ 貼文排序控制（Dolly 2026-07-10，照 Figma）═══
   只在「只看貼文」的視圖出現：桌機/手機的貼文分頁＋手機的貼文類別子頁。
   桌機＝標題列右側玻璃膠囊＋下拉（皮膚照 .header-dropdown 配方）；
   手機＝同一份 markup 變底部 sheet。 */
.search-overlay__group { position: relative; } /* 排序膠囊錨定用 */
.search-sort { display: none; }
#searchResults[data-active-tab="post"] .search-sort,
#searchOverlay.is-catview [data-search-group="post"].is-cat-current .search-sort { display: block; }
/* 桌機：貼到標題列右側 */
.search-sort {
  position: absolute;
  top: -4px;
  right: 0;
}
.search-sort__btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 34px;
  padding: 0 var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-pill);
  background: var(--white-alpha-4);
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: 13px;
  cursor: pointer;
}
.search-sort__btn:hover { background: var(--glass-bg-default); color: var(--text-primary); }
/* 「排序」前綴淡一階（2026-07-13 Dolly：跟片庫/資料庫排序膠囊的 label 同層級；「：」同場移除） */
.search-sort__btn-label { color: var(--text-tertiary); }
/* ⇅ 雙箭頭（assets/icons/arrow_down_up.svg，Dolly 2026-07-10 提供） */
.search-sort__updown {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask: url('assets/icons/arrow_down_up.svg') center / contain no-repeat;
          mask: url('assets/icons/arrow_down_up.svg') center / contain no-repeat;
}
.search-sort__menu {
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  min-width: 176px;
  padding: var(--space-1);
  background: rgba(19, 19, 19, 0.85); /* 照 .header-dropdown 皮膚配方 */
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-md);
  z-index: 20;
}
.search-sort__menu[hidden] { display: none; } /* [hidden] 老雷保險 */
.search-sort__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  width: 100%;
  height: 40px;
  padding: 0 var(--space-3);
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  text-align: left;
}
.search-sort__opt:hover { background: var(--white-alpha-8); color: var(--text-primary); }
.search-sort__opt.is-active { color: var(--text-primary); }
/* 選中鍵右側＝品牌橘勾勾（2026-07-13 Dolly：拿掉翻向，統一片庫「單選＋勾勾」；
   原方向箭頭/最高最低切換已移除——每個鍵只有一個合理方向） */
.search-sort__opt-mark { display: none; }
.search-sort__opt.is-active .search-sort__opt-mark {
  display: inline-block;
  width: 12px;
  height: 7px;
  border-left: 2px solid var(--brand-500);
  border-bottom: 2px solid var(--brand-500);
  transform: rotate(-45deg) translateY(-2px);
}
/* 頭列（排序方式標題）＝手機 sheet 專用 */
.search-sort__head { display: none; }
.search-sort__scrim { display: none; }

/* ── 手機：sheet 化 ── */
#searchOverlay.is-mobile .search-sort {
  position: static;
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  margin-bottom: var(--space-5); /* 與（隱藏中的）標題同列節奏 */
}
#searchOverlay.is-mobile .search-sort__btn { height: 32px; }
#searchOverlay.is-mobile .search-sort__scrim {
  display: block;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  z-index: 30;
}
#searchOverlay.is-mobile .search-sort__scrim[hidden] { display: none; } /* 明確 display 蓋 [hidden] 老雷 */
#searchOverlay.is-mobile .search-sort__menu {
  position: fixed;
  left: 0;
  right: 0;
  top: auto;
  bottom: 0;
  touch-action: none;             /* 讓 pointermove 收得到觸控拖曳（內容不捲動，安全） */
  transition: transform 200ms var(--ease-out); /* 沒過門檻的回彈 */
  min-width: 0;
  padding: var(--space-3) var(--space-4) max(var(--space-6), env(safe-area-inset-bottom));
  border: none;
  border-radius: var(--radius-2xl) var(--radius-2xl) 0 0;
  background: var(--bg-tertiary);
  z-index: 31;
}
/* 揪手把 */
#searchOverlay.is-mobile .search-sort__menu::before {
  content: '';
  display: block;
  width: 44px;
  height: 4px;
  margin: 0 auto var(--space-4);
  border-radius: var(--radius-pill);
  background: var(--white-alpha-16);
}
#searchOverlay.is-mobile .search-sort__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-2);
  padding: 0 var(--space-1);
}
#searchOverlay.is-mobile .search-sort__head-title {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
#searchOverlay.is-mobile .search-sort__opt { height: 48px; font-size: var(--fs-body); }
/* 手機選中＝橘字＋勾勾（勾勾樣式與桌機同一條規則） */
#searchOverlay.is-mobile .search-sort__opt.is-active { color: var(--brand-500); background: var(--white-alpha-4); }

/* 0807 Dolly：兩組 demo 搜尋結果（無間道／謝霆鋒 vs 周湯豪）互斥顯示。
   非當前組的列掛 .is-set-off。權重要壓過 `[data-collapsed="true"] > .is-extra`
   ＝(0,2,0)，所以寫成三段 (0,3,0)，不用 !important。 */
.search-overlay .search-overlay__group .is-set-off { display: none; }
/* ⚠️ .glass-tabs__item 有自己的 display（inline-flex），會蓋掉 [hidden] 屬性
   （家規：[hidden] 被 display 蓋，要用 .class[hidden] 補回）。沒有這條的話，
   周湯豪那組會留下「人物 0／劇集 0」兩顆空分頁。 */
.glass-tabs.search-tabs .glass-tabs__item[hidden] { display: none; }

/* ─────────────────────────────────────────
   VIDEO DETAIL · 影片詳情頁 (video.html)
   1.0 影片詳情頁的三種型態（電影已上架 / 劇集已上架 / 未上架資料庫）
   在 2.0 皮膚下的補件。功能與資訊架構照 1.0，只有 2.0 title.html
   既有元件覆蓋不到的部分才在這裡新增樣式。

   Reuses verbatim from 36-title-detail.css (title.html)：
   .detail-hero / .detail-head / .detail-titlebar / .detail-scorerow /
   .detail-meta / .detail-chip / .detail-desc / .detail-actions /
   .detail-price / .detail-iconbtn / .detail-section / .cast-row /
   .cast-card / .media-grid / .episode-rail / .episode-card /
   .comment-card / .comment-compose* / .post-card / .related-col。
   Reuses verbatim from components.css：.badge / .badge--urgent
   （紅色提示膠囊，這裡借來當「新上架」標籤）、.ztor-score--green
   （評分綠色變體，未上架型態用）、.wp-roomdev（情境展示 dev bar 殼）。

   New in this file：三型態切換的 [data-vd] 顯示規則、資料庫提示條
   （.detail-notice）、創作團隊主要演員列＋創作名單併排（.video-team）、
   留言空狀態（.comment-empty）、觀影資訊側欄（.watch-info，type=info 專用）。
   ───────────────────────────────────────── */

/* ===== 三型態切換（film / series / info）==================
   JS 用 body[data-video-type] 記狀態、[data-vd] 屬性標記「哪些型態該顯示」，
   顯示/隱藏一律走原生 hidden 屬性（不刪 DOM）。
   ⚠️ 部分 title.html 元件（如 .detail-head__main）本身有 display:flex 的
   author 規則，其 specificity 會贏過瀏覽器 UA 的 [hidden]{display:none}——
   這裡用 !important 保證切換一定生效。 ============================== */
[data-vd][hidden] { display: none !important; }

/* ===== 資料庫提示條（type=info 專用）======================
   對齊既有 .detail-region-note 的視覺語言（同一列、靠右），但語意不同
   （非地區限制，是「此片未上架」），獨立成自己的 class，不掛 data-region。 */
.detail-scorerow { flex-wrap: wrap; row-gap: 8px; }
.detail-notice {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-left: auto;
  min-width: 0;
}
.detail-notice__ico { flex: none; display: inline-flex; color: var(--warning-500); }
.detail-notice__ico .ds-icon { width: 16px; height: 16px; }
.detail-notice__txt { font-size: var(--fs-body-sm); color: var(--warning-500); }

/* ===== 創作團隊：主要演員列 + 創作名單併排 ==================
   電影／未上架型態＝頭像列（左）＋導演/編劇/監製（右，margin-left:auto）。
   劇集型態沒有主要演員列（data-vd="film info" 隱藏），此時創作名單
   自然吃掉整排寬度、落到最左——正好對齊 1.0 三型態各自的版面。 */
.video-team {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 24px 40px;
}
.video-team__cast { display: flex; flex-direction: column; gap: 16px; min-width: 0; flex: 1 1 auto; }
.video-team__cast-label {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.video-team__crew {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin: 0 0 0 auto;
  flex: 0 0 auto;
}
.video-team__crew-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
}
.video-team__crew-row dt { flex: 0 0 auto; width: 40px; color: var(--text-tertiary); }
.video-team__crew-row dd { margin: 0; color: var(--text-primary); font-weight: var(--fw-medium); }

/* ===== 留言空狀態（三型態皆 0 則留言，照 1.0）================ */
.comment-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 64px 24px;
  color: var(--text-tertiary);
}
.comment-empty__ico { width: 40px; height: 40px; }
.comment-empty__ico svg { width: 100%; height: 100%; }
.comment-empty__text {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}

/* ===== 觀影資訊側欄（type=info 專用，取代相關貼文欄）========
   全部／訂閱分頁 + 地區下拉 + 外部平台清單（類似 JustWatch）。 */
.watch-info { display: flex; flex-direction: column; gap: 16px; }
.watch-info__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.watch-info__tabs { display: flex; gap: 4px; }
.watch-info__tab {
  padding: 6px 14px;
  border-radius: var(--radius-pill);
  border: none;
  background: none;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.watch-info__tab:hover { color: var(--text-primary); }
.watch-info__tab.is-active { background: var(--white-alpha-16); color: var(--text-primary); font-weight: var(--fw-medium); }
.watch-info__region {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
  background: none;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  cursor: pointer;
}
.watch-info__region .ds-icon { width: 14px; height: 14px; }
.watch-info__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.watch-info__platform {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle);
  background: var(--bg-secondary);
}
.watch-info__platform-icon {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  background: var(--bg-tertiary);
  color: var(--text-primary);
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 14px;
}
.watch-info__platform-name {
  flex: 1 1 auto;
  min-width: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-primary);
}
.watch-info__platform-tag {
  flex: none;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-secondary);
}
.watch-info__platform-count {
  flex: none;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  min-width: 12px;
  text-align: right;
}

/* ===== RESPONSIVE ========================================= */
@media (max-width: 1080px) {
  .video-team { flex-direction: column; }
  .video-team__crew { margin-left: 0; }
}

/* ─────────────────────────────────────────
   PLAYER  ·  單人播放頁 (player.html)
   Functional parity with the Ztor 1.0 trailer/film player chrome
   (ref: docs/ref-10-player.png), re-skinned to the 2.0 design system.

   Control-bar look is DELIBERATELY modelled on the watch-party room
   player (.wproom__ctrl / .wproom__btn / .wproom__scrub / .wproom__vol
   in watch-party.css — floater chrome, hover/idle reveal, brand-orange
   fill) per the 2026-07 room-chrome decision. This file does NOT edit
   watch-party.css; it defines an independent `.player__*` namespace
   with the same token values, plus 1.0-only controls WP doesn't have
   (audio-enhance toggle, quality menu, CC toggle, speed menu, ±10s).
   Popovers reuse the shipped `.wp-pop` / `.wp-pop--up` primitive
   (watch-party.css) rather than inventing a 6th popover shell.
   ───────────────────────────────────────── */

/* ===== breadcrumb ("← 返回《片名》主頁") =====
   Structure reuses .detail-back / .detail-back__nav / .detail-back__title
   (36-title-detail.css) verbatim; only the brand-coloured film-name span
   is new here. */
.player-back { margin-bottom: var(--space-4); }
.player-back__film { color: var(--brand-500); font-weight: var(--fw-bold); }

/* ===== page shell ===== */
.player-page { max-width: 1120px; }

/* ===== stage ===== */
.player {
  width: 100%;
}
.player__stage {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--neutral-900);
  isolation: isolate;
}
.player__video {
  position: absolute;
  inset: 0;
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
  background-color: var(--neutral-900);
}
/* darken the poster slightly so white glyph controls always read */
.player__video::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.32);
}

/* trailer badge — top-left, mirrors .wproom__live pill look */
.player__badge {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 4px var(--space-3);
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.5);
  font-size: var(--fs-caption);
  color: var(--text-primary);
}
.player__badge[hidden] { display: none; }

/* ===== center transport (play/pause + ±10s) ===== */
.player__center {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-8);
}
.player__center-btn {
  flex: none;
  display: grid;
  place-items: center;
  border: 0;
  background: transparent;
  color: var(--text-primary);
  cursor: pointer;
  padding: 0;
  filter: drop-shadow(0 2px 10px rgba(0, 0, 0, 0.5));
  transition: opacity 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.player__center-btn:hover { opacity: 0.82; }
.player__center-btn:active { transform: scale(0.94); }
.player__center-btn--seek { width: 34px; height: 34px; }
.player__center-btn--seek svg { width: 34px; height: 34px; }
.player__center-btn--play { width: 56px; height: 56px; }
.player__center-btn--play svg { width: 56px; height: 56px; grid-area: 1 / 1; }
/* play ⇄ pause glyph swap — state lives on the button class only */
.player__center-btn--play .player__ic-pause { display: none; }
.player__center-btn--play.is-playing .player__ic-play { display: none; }
.player__center-btn--play.is-playing .player__ic-pause { display: block; }

/* ===== control bar (floater — hover/idle reveal, matches .wproom__ctrl) ===== */
.player__ctrl {
  position: absolute;
  left: var(--space-4);
  right: var(--space-4);
  bottom: 0;
  z-index: 3;
  padding: 56px var(--space-1) var(--space-3);
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.8));
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms var(--ease-out);
}
.player__stage:hover .player__ctrl,
.player__ctrl:focus-within,
.player__ctrl:has(.wp-pop:not([hidden])),
.player__ctrl:has(.player__scrub.is-dragging),
body[data-chrome="1"] .player__ctrl { opacity: 1; pointer-events: auto; }

/* time row — current / total, sits ABOVE the scrub track */
.player__timerow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-3);
  margin-bottom: var(--space-1);
  font-size: var(--fs-caption);
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}

/* scrub track */
.player__scrub {
  position: relative;
  height: 16px;
  margin: 0 var(--space-3);
  cursor: pointer;
  touch-action: none;
}
.player__scrub::before {
  content: '';
  position: absolute;
  inset: 6px 0;
  border-radius: var(--radius-pill);
  background: var(--border-default);
}
.player__scrub-fill {
  position: absolute;
  inset: 6px auto 6px 0;
  width: calc(var(--pp-pos, 0) * 1%);
  border-radius: var(--radius-pill);
  background: var(--brand-500);
}
.player__scrub-knob {
  position: absolute;
  top: 50%;
  left: calc(var(--pp-pos, 0) * 1%);
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--brand-500);
  transform: translate(-50%, -50%);
  opacity: 0;
  transition: opacity 150ms var(--ease-out);
}
.player__scrub:hover .player__scrub-knob,
.player__scrub:focus-visible .player__scrub-knob,
.player__scrub.is-dragging .player__scrub-knob { opacity: 1; }

/* bottom row: [vol + 音效增強]  [title]  [畫質 · CC · 速度 · 全螢幕] */
.player__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-1) 0;
}
.player__grp { display: flex; align-items: center; gap: var(--space-3); flex: none; }
.player__grp--left { min-width: 0; }
.player__grp--right { margin-left: auto; }

.player__title {
  flex: 1;
  min-width: 0;
  text-align: center;
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* generic 40px circular control button — same footprint as .wproom__btn,
   defined independently so this file never touches watch-party.css */
.player__btn {
  width: 40px;
  height: 40px;
  flex: none;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--glass-bg-default);
  color: var(--text-primary);
  cursor: pointer;
  position: relative;
}
.player__btn:hover { background: var(--glass-bg-hover); }
.player__btn[aria-expanded="true"],
.player__btn.is-on { background: var(--brand-500); color: var(--brand-ink); }
.player__ic {
  width: 20px;
  height: 20px;
  display: block;
  flex: none;
  background: currentColor;
  -webkit-mask: var(--pp-ic) center / contain no-repeat;
          mask: var(--pp-ic) center / contain no-repeat;
}

/* quality trigger — text label ("自動"), not an icon, per 1.0 reference */
.player__btn--quality {
  width: auto;
  padding: 0 var(--space-3);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  white-space: nowrap;
}

/* volume — icon (click = mute) + inline range, always visible (no fly-out;
   1.0 reference shows the slider permanently, unlike the WP room variant) */
.player__vol { display: flex; align-items: center; gap: var(--space-2); }
.player__vol-btn { background: transparent; }
.player__vol-btn:hover { background: var(--glass-bg-hover); }
.player__vol-ic { display: none; }
.player__vol-btn[data-vol="high"] .player__vol-ic--high,
.player__vol-btn[data-vol="low"]  .player__vol-ic--low,
.player__vol-btn[data-vol="mute"] .player__vol-ic--mute { display: block; }
.player__vol-range {
  width: 84px;
  height: 3px;
  -webkit-appearance: none;
  appearance: none;
  background: linear-gradient(90deg, var(--brand-500) calc(var(--pp-vol, 70) * 1%), var(--border-default) 0);
  border-radius: var(--radius-pill);
  outline: none;
}
.player__vol-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--text-primary); cursor: pointer;
}
.player__vol-range::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%; border: 0;
  background: var(--text-primary); cursor: pointer;
}

/* 音效增強 — toggle switch (1.0-only; no shared DS switch primitive yet,
   built locally per the ±10s / quality-menu convention above) */
.player__toggle-wrap {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  user-select: none;
}
.player__toggle-input {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip: rect(0 0 0 0); overflow: hidden;
}
.player__toggle {
  width: 32px; height: 18px; flex: none;
  border-radius: var(--radius-pill);
  background: var(--border-strong);
  position: relative;
  transition: background 150ms var(--ease-out);
}
.player__toggle::before {
  content: '';
  position: absolute;
  top: 2px; left: 2px;
  width: 14px; height: 14px;
  border-radius: 50%;
  background: var(--text-primary);
  transition: transform 150ms var(--ease-out);
}
.player__toggle-input:checked + .player__toggle { background: var(--brand-500); }
/* 開啟＝橘底白點（Dolly 0718；原 brand-ink 黑點棄用） */
.player__toggle-input:checked + .player__toggle::before { transform: translateX(14px); background: var(--text-primary); }
.player__toggle-input:focus-visible + .player__toggle { outline: 2px solid var(--brand-500); outline-offset: 2px; }
.player__toggle-label {
  font-size: var(--fs-caption);
  color: var(--text-secondary);
  white-space: nowrap;
}

/* ===== popovers (quality / speed) — reuse .wp-pop / .wp-pop--up shell ===== */
.player__pop-wrap { position: relative; }
.player__pop-title {
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin: 0 0 var(--space-2);
  padding: 0 var(--space-2);
}
.player__pop-list { display: flex; flex-direction: column; min-width: 152px; }
.player__pop-item {
  border: 0;
  background: transparent;
  text-align: left;
  padding: var(--space-2) var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
}
.player__pop-item:hover { background: var(--glass-bg-hover); color: var(--text-primary); }
.player__pop-item.is-current { color: var(--brand-500); font-weight: var(--fw-medium); }
.player__pop-item__note { font-size: var(--fs-caption); color: var(--text-tertiary); margin-left: 4px; }

/* ===== fullscreen fallback (headless / no user-gesture path) =====
   Real path = requestFullscreen(); fallback = in-page full-viewport,
   same pattern as .wproom__stage.is-fs-inpage. */
.player__stage.is-fs-fallback {
  position: fixed;
  inset: 0;
  z-index: 200;
  border-radius: 0;
  aspect-ratio: auto;
}
body:has(.player__stage.is-fs-fallback) { overflow: hidden; }
/* 舞台 z200 被 .detail-hero 的 isolation:isolate 關在自家 stacking context（整組
   以 z≈0 對外）→ mtd-bar(z180)/後續靜態內容蓋到全螢幕上。fallback 時抬整個 context */
.detail-hero:has(.player__stage.is-fs-fallback) { z-index: 200; }
.player__stage.is-fs .player__ic--fs,
.player__stage.is-fs-fallback .player__ic--fs { display: none; }
.player__stage:not(.is-fs):not(.is-fs-fallback) .player__ic--fs-exit { display: none; }

/* ===== title below player ===== */
.player-title {
  margin: var(--space-4) 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-lg);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}

/* ═════════════════════════════════════════
   HERO PLAYER · title.html 放映疊加（.player--hero）
   Figma「Ztor. Movie_已租借_整頁內容」163:19082 ＋ Dolly 0717 兩張
   hover／暫停狀態稿。詳情頁 hero 就地變播放器：
   · 暫停 → chrome 常駐、中央 ▶
   · 播放＋hover → chrome 顯示、中央 ⏸
   · 播放無 hover → 全 chrome 隱藏（乾淨畫面）
   行為＝ds.js `hero-player`（data-hplayer）。沿用 .player__stage／
   __video／__scrub 骨架，chrome 佈局照稿重排（頂列 icon／橘進度條
   ＋剩餘時間／音量·片名·設定·全螢幕列），與 player.html 舊 chrome
   （player.html 廢棄倒數中）互不影響。
   ═════════════════════════════════════════ */
.player--hero[hidden] { display: none; }   /* [hidden] 防 display 蓋掉（老雷） */
.player--hero { margin-bottom: var(--space-6); }
.player--hero .player__stage { border-radius: var(--radius-md); }
/* 稿的畫面不加暗（chrome 用漸層 scrim 保可讀），拿掉舊 chrome 的整片 32% 黑 */
.player--hero .player__video::after { content: none; }

/* chrome 三態顯隱：整組（頂列＋中央＋底列）一起 fade */
.player--hero .player__chrome {
  position: absolute;
  inset: 0;
  z-index: 2;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms var(--ease-out);
}
/* Dolly 0717 三輪：按鈕一律 hover 才出現（暫停態也不常駐）。
   只給真 hover 裝置——觸控的模擬 :hover 放手就散（＝停留過短病根），
   觸控節奏改由 .is-chrome（ds.js 計時器）單一管理 */
@media (hover: hover) {
  .player--hero .player__stage:hover .player__chrome { opacity: 1; pointer-events: auto; }
}
/* 觸控 chrome（Dolly 0727 二輪）：tap 舞台顯示、無互動自動收；
   狀態＝stage .is-chrome，節奏在 ds.js hero-player */
.player--hero .player__stage.is-chrome .player__chrome,
/* 鍵盤操作時保住 chrome；滑鼠點擊的殘留焦點不算（否則點過播放鍵
   chrome 永遠不隱） */
.player--hero .player__chrome:has(:focus-visible),
.player--hero .player__chrome:has(.wp-pop:not([hidden])),
.player--hero .player__chrome:has(.player__scrub.is-dragging),
body[data-chrome="1"] .player--hero .player__chrome {
  opacity: 1;
  pointer-events: auto;
}
/* 可讀性 scrim：上下淡漸層（稿：畫面本身不變暗、僅控制帶附近壓深） */
.player--hero .player__chrome::before {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.45), transparent 22%),
    linear-gradient(0deg, rgba(0, 0, 0, 0.55), transparent 30%);
}

/* ── 頂列：← ＋（子母畫面/投放/回報）── */
.player--hero .player__topbar {
  position: absolute;
  top: var(--space-6);
  left: var(--space-6);
  right: var(--space-6);
  display: flex;
  align-items: center;
  gap: var(--space-6);
}
.player--hero .player__topbar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-6);
}
/* 裸 glyph 鈕（稿：無圓底） */
.player--hero .player__flat {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-primary);
  cursor: pointer;
  filter: drop-shadow(0 1px 6px rgba(0, 0, 0, 0.45));
  transition: opacity 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.player--hero .player__flat:hover { opacity: 0.8; }
.player--hero .player__flat:active { transform: scale(0.94); }
.player--hero .player__flat .player__ic { width: 24px; height: 24px; }
/* screen-pinp 主框被右下小方塊吃掉 ~2px（墨水量測），24 盒顯得比 cast 小 → 光學補償 26 */
.player--hero .player__flat--pip .player__ic { width: 26px; height: 26px; }
.player--hero .player__flat--exit .player__ic { width: 28px; height: 28px; }

/* 離場鈕的兩個 icon：預設＝← 返回（離開去片庫，28px）；預告片臨時疊加＝✕ 關閉
   （縮到 24px 與其他 icon 齊，Dolly 0718 二輪）。由 body[data-watch="trailer"] 切換。 */
.player--hero .player__flat--exit .player__ic--exit-close { display: none; width: 24px; height: 24px; }
body[data-watch="trailer"] .player--hero .player__flat--exit .player__ic--exit-back { display: none; }
body[data-watch="trailer"] .player--hero .player__flat--exit .player__ic--exit-close { display: block; }
/* 預告片：✕ 移到右上、flag 右邊（order 排在 topbar-right 之後；Dolly 0718 二輪） */
body[data-watch="trailer"] .player--hero .player__flat--exit { order: 2; }

/* 桌機不出 stage 內 ←（Dolly 0717：頁面自己有「返回」列）；≤768 手機保留。
   但預告片臨時疊加＝桌機也要顯示 ✕（返回列只回片庫，關預告片靠這顆）。 */
@media (min-width: 769px) {
  .player--hero .player__flat--exit { display: none; }
  body[data-watch="trailer"] .player--hero .player__flat--exit { display: grid; }
  /* 預告片：topbar 內縮 20px，讓 32px 按鈕內的 24px icon（flag/✕）恰落在距邊 24px
     （Dolly 0718 二輪：以 icon 為對齊基準）。 */
  body[data-watch="trailer"] .player--hero .player__topbar { top: var(--space-5); right: var(--space-5); }
}

/* ── 左下 transport（Dolly 0718 二輪：⏸/↺10/↻10/🔊 裸 glyph 無襯底，
   與右側 player__flat 同語言；中央大播放鍵組已取消）── */
.player--hero .player__grp--left .player__flat svg,
.player--hero .player__grp--left .player__flat img { width: 24px; height: 24px; display: block; }
/* ±10（img）內縮多、視覺偏小 → 放大到 28 對齊其他 24px glyph（Dolly 0718） */
.player--hero .player__grp--left .player__flat img { width: 28px; height: 28px; }
.player--hero .player__grp--left .player__vol-btn .player__ic { width: 24px; height: 24px; }
/* 播放 ⇄ 暫停 glyph 切換（狀態＝按鈕 .is-playing，ds.js 同步）。
   權重壓過上面 svg 尺寸規則（0,4,0 > 0,3,1） */
.player--hero .player__grp--left .player__btn--play .player__ic-pause { display: none; }
.player--hero .player__grp--left .player__btn--play.is-playing .player__ic-play { display: none; }
.player--hero .player__grp--left .player__btn--play.is-playing .player__ic-pause { display: block; }
.player--hero .player__center-btn--seek,
.player--hero .player__center-btn--seek svg,
.player--hero .player__center-btn--seek img { width: 52px; height: 52px; }   /* markup 是 <img>，別只放大 svg */

/* ── 播放/暫停中央爆閃回饋（Dolly 0718：切換時畫面正中出大圖示）── */
.player--hero .player__burst {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  place-items: center;
  pointer-events: none;
}
.player__burst-badge {
  width: 96px;
  height: 96px;
  display: grid;
  place-items: center;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: var(--text-primary);
  opacity: 0;
}
.player__burst-badge svg { width: 44px; height: 44px; }
.player__burst.is-play .player__ic-pause { display: none; }
.player__burst.is-pause .player__ic-play { display: none; }
.player__burst.is-anim .player__burst-badge { animation: hp-burst 550ms var(--ease-out) forwards; }
@keyframes hp-burst {
  0% { opacity: 0.9; transform: scale(0.7); }
  100% { opacity: 0; transform: scale(1.3); }
}

/* ── 底部控制帶 ── */
.player--hero .player__ctrl {
  left: 0;
  right: 0;
  padding: 0 var(--space-6) var(--space-5);
  background: none;   /* scrim 由 .player__chrome::before 統一畫 */
  opacity: 1;         /* 顯隱交給 .player__chrome，這層不再各自 fade */
  pointer-events: inherit;
}

/* 進度列：[橘 scrub ————●] [剩餘時間] */
.player--hero .player__progress {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}
.player--hero .player__progress .player__scrub { flex: 1; margin: 0; }
/* chrome 顯示時 knob 常駐（稿） */
.player--hero .player__scrub-knob { opacity: 1; }
.player--hero .player__remain {
  flex: none;
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

/* 下排：[transport＋音量] ……片名（stage 正中）…… [選集 設定 子母 全螢幕] */
.player--hero .player__row {
  position: relative;   /* 片名絕對置中的定位基準 */
  padding: var(--space-2) 0 0;
  gap: var(--space-4);
}
.player--hero .player__grp { gap: var(--space-4); }   /* 0718：icon 間距再緊 */
/* 音量：hover/聚焦才展開滑桿（同 WP 播放室 fly-out；Dolly 0718） */
.player--hero .player__vol { gap: 0; }
/* 片名＝列的絕對中點（不受左右群組寬度影響；Dolly 0718） */
.player--hero .player__title {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  flex: none;
  max-width: 38%;
}
.player--hero .player__vol-range {
  width: 0;
  opacity: 0;
  margin-left: 0;
  background: linear-gradient(90deg, var(--text-primary) calc(var(--pp-vol, 70) * 1%), rgba(255, 255, 255, 0.28) 0);
  transition: width 0.18s ease, opacity 0.18s ease, margin-left 0.18s ease;
}
@media (hover: hover) {
  .player--hero .player__vol:hover .player__vol-range,
  .player--hero .player__vol:focus-within .player__vol-range {
    width: 108px;
    opacity: 1;
    margin-left: var(--space-2);
  }
}
/* 觸控無 hover fly-out（Dolly 0727 二輪：難控音量）：chrome 顯示時滑桿常駐展開。
   72px（四輪：96 太長收短）＝影集直屏底排帶選集鈕也塞得下 */
@media (hover: none) {
  .player--hero .player__vol-range {
    width: 72px;
    opacity: 1;
    margin-left: var(--space-2);
  }
  /* 觸控目標 32→44px（HIG）：實機手指差幾 px 打到舞台＝tap 收 chrome，
     體感「點 ⚙ 沒反應」（Dolly 0728 iOS Chrome 實機）。上排鈕距 44 剛好不重疊。
     relative＝給 ::after 錨點（absolute 變體晚載自然蓋回） */
  .player--hero .player__flat { position: relative; }
  .player--hero .player__flat::after {
    content: '';
    position: absolute;
    inset: -6px;
  }
}
.player--hero .player__title { font-size: var(--fs-body); }

/* 設定 popover：兩層式（1.0 線上版）——L1 列＝icon＋名稱＋現值＋› */
.player__setpop { min-width: 264px; }
.player__set-row--m { display: none; }   /* 手機設定 sheet 專用列（≤768 才開） */
.player--hero .player__pip-close,
.player--hero .player__pip-restore { display: none; }   /* 頁內子母畫面迷你窗控制：僅 .is-pip（≤768）顯示。
                                                           0,2,0＝同 .player--hero .player__flat 的 display:grid，靠源序在後蓋掉 */

/* ===== AirPlay 裝置選單（Dolly 0727；mock，1.0 iOS 面板版型＋2.0 皮膚）===== */
.airplay-pop__head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}
.airplay-pop__glyph { width: 28px; height: 28px; }
.airplay-pop__title { margin: 0; }
.airplay-pop__list { display: flex; flex-direction: column; }
.airplay-pop__dev {
  display: grid;
  grid-template-columns: 28px 1fr 24px;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-2);
  border: 0;
  background: transparent;
  color: var(--text-primary);
  cursor: pointer;
  text-align: left;
  border-radius: var(--radius-sm);
}
.airplay-pop__dev:hover { background: var(--glass-bg-hover); }
.airplay-pop__dev + .airplay-pop__dev { border-top: 1px solid var(--border-subtle); border-radius: 0 0 var(--radius-sm) var(--radius-sm); }
.airplay-pop__dev-ic { width: 24px; height: 24px; }
.airplay-pop__dev-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.airplay-pop__dev-name { font-size: var(--fs-body); font-weight: var(--fw-medium); }
.airplay-pop__dev-sub {
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.airplay-pop__check { width: 22px; height: 22px; visibility: hidden; }
.airplay-pop__dev.is-on .airplay-pop__check { visibility: visible; }

/* ===== 跨頁子母畫面浮動窗（Dolly 0727「帶著走」；ds.js pip-float 注入，≤768 才注入）=====
   離開播放頁時 pip 狀態存 session，其他頁面右下續播（mock 計時）；
   tap 影片區＝回播放頁續位、✕＝關閉；控制鍵閒置淡出同觸控 chrome 節奏 */
.pipfloat {
  position: fixed;
  right: 10px;
  bottom: 16px;   /* 預設貼底：無底欄的頁／底欄下滑隱藏（is-hidden）時 */
  width: min(340px, calc(100vw - 50px));   /* 同頁內 pip：1.0 參考大窗 */
  aspect-ratio: 16 / 9;
  z-index: 400;
  border-radius: 16px;
  overflow: hidden;
  background: #000 center / cover no-repeat;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55);
  cursor: pointer;
  transition: bottom 360ms cubic-bezier(0.22, 1, 0.36, 1);   /* 同 m-bottomnav 顯隱曲線，跟著滑 */
}
/* 底欄在場且可見（m-bottomnav 非 is-hidden）或 title 頁固定留言欄（mtd-bar）→ 墊高讓位 */
body:has(.m-bottomnav:not(.is-hidden)) .pipfloat,
body:has(.mtd-bar) .pipfloat { bottom: 96px; }
.pipfloat__chrome {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 180ms var(--ease-out);
  background:
    linear-gradient(180deg, rgba(0, 0, 0, 0.45), transparent 26%),
    linear-gradient(0deg, rgba(0, 0, 0, 0.45), transparent 26%);
}
.pipfloat.is-chrome .pipfloat__chrome { opacity: 1; pointer-events: auto; }
.pipfloat__btn {
  position: absolute;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-primary);
  cursor: pointer;
  filter: drop-shadow(0 1px 6px rgba(0, 0, 0, 0.45));
}
.pipfloat__close { top: 2px; left: 4px; width: 36px; height: 36px; }
.pipfloat__close svg { width: 20px; height: 20px; }
.pipfloat__expand { top: 2px; right: 4px; width: 36px; height: 36px; }   /* 回影片頁（同頁內迷你窗右上） */
.pipfloat__expand .player__ic { width: 20px; height: 20px; }
.pipfloat__play {
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 56px;
  height: 56px;
}
.pipfloat__play svg { width: 38px; height: 38px; }
.pipfloat__play:active { transform: translate(-50%, -50%) scale(0.94); }   /* 縮放合成置中（老雷） */
.pipfloat__seek { top: 50%; transform: translate(-50%, -50%); width: 44px; height: 44px; }
.pipfloat__seek:active { transform: translate(-50%, -50%) scale(0.94); }
.pipfloat__seek img { width: 26px; height: 26px; }
.pipfloat__seek[data-pf-seek="-10"] { left: calc(50% - 72px); }
.pipfloat__seek[data-pf-seek="10"]  { left: calc(50% + 72px); }
.pipfloat .pf-ic-pause { display: none; }
.pipfloat.is-playing .pf-ic-pause { display: block; }
.pipfloat.is-playing .pf-ic-play { display: none; }
.player__set-menu { display: flex; flex-direction: column; gap: var(--space-1); }
/* [hidden] 防 display:flex 蓋掉（老雷）：兩層切換靠這個 */
.player__set-menu[hidden],
.player__pop-list[hidden] { display: none; }
/* 固定 icon 欄（32px 置中）→ 三列 icon 尺寸/標籤起點一致（Dolly 0717） */
.player__set-row {
  display: grid;
  grid-template-columns: 32px max-content 1fr 16px;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-2);
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-primary);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  text-align: left;
}
.player__set-row:hover { background: var(--glass-bg-hover); }
.player__set-ic { width: 20px; height: 20px; justify-self: center; }
/* 墨水實測：speed 20×16、cc 18×15、badge 原生 27×17（實心白、視覺過重）
   → 縮到 24×15 對齊 glyph 墨水高（PIL 量測 0717） */
.player__set-badge { width: 24px; height: 15px; justify-self: center; }
.player__set-val {
  justify-self: end;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--text-primary);
}
.player__set-chev { width: 16px; height: 16px; color: var(--text-secondary); }
/* L2：‹ 群組名 ＋ 選項列 */
.player__set-back {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2);
  margin-bottom: var(--space-1);
  border: 0;
  background: transparent;
  color: var(--text-primary);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  cursor: pointer;
  text-align: left;
}
.player__set-back svg { width: 16px; height: 16px; color: var(--text-secondary); }
.player__set-back:hover svg { color: var(--text-primary); }
.player__set-back[hidden] { display: none; }   /* cc 層自帶頭列時收起（[hidden] 防 flex 蓋） */

/* ── 字幕層＝音頻＋字幕雙欄（1.0 線上版）── */
.player__set-cc[hidden] { display: none; }
.player__set-cc {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-6);
  min-width: 400px;
}
.player__set-cc-col { min-width: 0; }
.player__set-cc-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-2);
  margin: 0 0 var(--space-1);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
/* 字幕欄頭 ⚙（進字幕細節設定） */
.player__set-cc-head--sub { justify-content: space-between; }
.player__set-cc-gear {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-secondary);
  cursor: pointer;
}
.player__set-cc-gear:hover { color: var(--text-primary); }
.player__set-cc-gear .player__ic { width: 18px; height: 18px; }

/* ── 字幕細節設定（1.0 線上版；文案中譯、選中框＝品牌橘）──
   state attr（data-ccfont/-ccbg/-ccalpha）→ 預覽即時反應 */
.player__ccset[hidden] { display: none; }
.player__ccset {
  position: absolute;
  inset: 0;
  z-index: 5;   /* 蓋過 chrome/gate */
  display: flex;
  flex-direction: column;
  background: var(--bg-primary);
}
.player__ccset-close {
  position: absolute;
  top: var(--space-4);
  right: var(--space-4);
  z-index: 1;
}
/* ✕ 無尺寸規則時 svg 撐滿 32px 鈕盒偏大 → 明確 20px（鈕盒 32 觸控目標不變；Dolly 0727 晚場五輪：放右、縮小） */
.player__ccset-close svg { width: 20px; height: 20px; }
.player__ccset-preview {
  flex: none;
  height: 26%;
  display: grid;
  place-items: center;
  background-position: center;
  background-size: cover;
  background-color: var(--bg-tertiary);
}
.player__ccset-sample {
  padding: 6px 16px;
  border-radius: var(--radius-sm);
  font-family: var(--font-cjk-text);
  background: rgba(var(--ccset-bg-rgb, 0, 0, 0), var(--ccset-a, 0.75));
  color: var(--ccset-ink, #fff);
}
.player__ccset[data-ccbg="white"] { --ccset-bg-rgb: 255, 255, 255; --ccset-ink: #131313; }
.player__ccset[data-ccbg="black"] { --ccset-bg-rgb: 0, 0, 0; --ccset-ink: #fff; }
.player__ccset[data-ccalpha="100"] { --ccset-a: 1; }
.player__ccset[data-ccalpha="75"] { --ccset-a: 0.75; }
.player__ccset[data-ccalpha="50"] { --ccset-a: 0.5; }
.player__ccset[data-ccalpha="25"] { --ccset-a: 0.25; }
.player__ccset[data-ccalpha="0"] { --ccset-a: 0; }
.player__ccset[data-ccfont="sm"] .player__ccset-sample { font-size: 14px; }
.player__ccset[data-ccfont="md"] .player__ccset-sample { font-size: 18px; }
.player__ccset[data-ccfont="lg"] .player__ccset-sample { font-size: 24px; }
.player__ccset-body {
  flex: 1;
  width: 100%;
  max-width: 560px;
  margin: 0 auto;
  padding: var(--space-8) var(--space-6) var(--space-6);   /* 原寫 --space-7＝不存在的 token → 整條 padding 無效歸 0（潛伏 bug） */
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  overflow: auto;
  min-height: 0;
}
.player__ccset-group + .player__ccset-group {
  border-top: 1px solid var(--border-subtle);
  padding-top: var(--space-6);
}
.player__ccset-label {
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin-bottom: var(--space-4);
}
.player__ccset-sizes { display: flex; gap: var(--space-4); }
.player__ccset-size {
  flex: 1;
  padding: var(--space-4) 0;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: var(--bg-tertiary);
  color: var(--text-primary);
  cursor: pointer;
  font-family: var(--font-latin);
  font-weight: var(--fw-medium);
}
.player__ccset-size--sm { font-size: 14px; }
.player__ccset-size--md { font-size: 20px; }
.player__ccset-size--lg { font-size: 26px; }
.player__ccset-swatches { display: flex; gap: var(--space-4); margin-bottom: var(--space-4); }
.player__ccset-swatch {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  cursor: pointer;
  padding: 0;
}
.player__ccset-swatch--white { background: #fff; }
.player__ccset-swatch--black { background: #000; }
.player__ccset-alphas { display: flex; gap: var(--space-3); }
.player__ccset-alpha {
  flex: 1;
  padding: var(--space-3) 0;
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  background: var(--bg-tertiary);
  color: var(--text-primary);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
/* 選中框＝品牌橘（1.0 為紅/金褐，換 2.0 皮膚） */
.player__ccset-size.is-on,
.player__ccset-swatch.is-on,
.player__ccset-alpha.is-on { border-color: var(--brand-500); }
.player__ccset-actions {
  display: flex;
  gap: var(--space-4);
  margin-top: auto;
}
.player__ccset-actions .btn { flex: 1; }

/* 選中項帶 ✓（佈局照 1.0；色彩照 2.0 品牌橘）——留空位讓未選項對齊 */
.player__set-cc .player__pop-item { padding-left: calc(var(--space-2) + 22px); position: relative; }
.player__set-cc .player__pop-item.is-current::before {
  content: '';
  position: absolute;
  left: var(--space-2);
  top: 50%;
  width: 14px;
  height: 14px;
  transform: translateY(-50%);
  background: currentColor;
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 11' fill='none'%3E%3Cpath d='M1 5.5L5 9.5L13 1.5' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%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 14 11' fill='none'%3E%3Cpath d='M1 5.5L5 9.5L13 1.5' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}

/* 選集只給影集：電影影片不出現（1.0 行為，Dolly 0717） */
body[data-t-form="film"] .player__flat--episodes { display: none; }

/* ── 稍後看閒置畫面（已租借、未開始播放；Dolly 0717 情境 1-1）──
   底圖重糊壓暗、左＝片名+租借期文案、右＝圓框 ▶；此態不出 chrome。 */
.player--hero .player__idle[hidden] { display: none; }
.player--hero .player__idle {
  position: absolute;
  inset: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding-inline: 13% 18%;
}
.player--hero .player__stage.is-idle .player__video {
  filter: blur(36px) brightness(0.45);
  transform: scale(1.12);   /* 糊邊羽化不露底 */
}
.player--hero .player__stage.is-idle .player__chrome { display: none; }
.player__idle-info {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.player__idle-title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: 40px;
  line-height: var(--lh-h1);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.player__idle-days {
  margin: 0;
  font-size: 20px;
  color: var(--text-primary);
}
.player__idle-days strong { color: var(--brand-500); }
.player__idle-note {
  margin: 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-tertiary);
}
.player__idle-note strong { color: var(--text-secondary); }
.player__idle-play {
  flex: none;
  width: 96px;
  height: 96px;
  display: grid;
  place-items: center;
  border: 2px solid var(--text-primary);
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.3);
  color: var(--text-primary);
  cursor: pointer;
  transition: background 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.player__idle-play:hover { background: rgba(0, 0, 0, 0.55); transform: scale(1.04); }
.player__idle-play:active { transform: scale(0.97); }
.player__idle-play svg { width: 36px; height: 36px; margin-left: 4px; }

/* ── 年齡確認（開始觀看正片前；Dolly 0717 情境 1-2）──
   全 stage 深色覆蓋＋置中海報壓暗、文案置中＋主色確認鈕（皮膚 2.0）。 */
.player--hero .player__gate[hidden] { display: none; }
.player--hero .player__gate {
  position: absolute;
  inset: 0;
  z-index: 4;   /* 蓋過 chrome 與 idle */
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, 0.86);
}
.player__gate-poster {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  height: 88%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: var(--radius-md);
  opacity: 0.3;
}
.player__gate-body {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-5);
  max-width: 680px;
  padding: 0 var(--space-6);
  text-align: center;
}
.player__gate-title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.player__gate-desc {
  margin: 0;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-primary);
}
.player__gate-confirm { margin-top: var(--space-2); }
/* 年齡確認開著時，底下只留海報（Dolly 0717）：閒置文字與劇照畫面全收、
   hover 也不出 chrome，背景剩 stage 純黑；確認後直接進播放 */
.player--hero .player__stage:has(.player__gate:not([hidden])) .player__idle,
.player--hero .player__stage:has(.player__gate:not([hidden])) .player__video { visibility: hidden; }
.player--hero .player__stage:has(.player__gate:not([hidden])) .player__chrome { display: none; }

/* ── 手機（≤768）owned 播放疊加：影片區滿版＋年齡閘縮級（Dolly 0727）──
   桌機 16:9 舞台在 390 寬只有 197 高，閘門內容（24px 標題＋16px 內文＋48px 鈕）
   疊到 281 直接把「我確認」切一半——手機整組降級才塞得回舞台。 */
@media (max-width: 768px) {
  /* 影片區 fill container：破出頁面 gutter 滿版（不吃死 gutter 值）、圓角歸零。
     寬度被上游鎖住只靠負 margin 會單邊位移 → width 直接給 100vw */
  .player--hero { margin-inline: calc(50% - 50vw); width: 100vw; }
  .player--hero .player__stage { border-radius: 0; }
  .player__gate-body { gap: 12px; padding: 0 20px; }
  .player__gate-title { font-size: 18px; }
  .player__gate-desc { font-size: 13px; line-height: 1.6; }
  .player__gate-confirm { margin-top: 0; }
  .player__gate-confirm.btn { height: 40px; padding-inline: 28px; font-size: 14px; }
  /* 稍後看閒置畫面同縮（桌機 40px 片名＋96px ▶ 在 219 高的舞台會爆） */
  .player--hero .player__idle { gap: 20px; padding-inline: 20px; }
  .player__idle-info { gap: 8px; }
  .player__idle-title { font-size: 20px; line-height: 1.3; }
  .player__idle-days { font-size: 14px; }
  .player__idle-note { font-size: 12px; line-height: 1.6; }
  .player__idle-play { width: 64px; height: 64px; }
  .player__idle-play svg { width: 24px; height: 24px; margin-left: 3px; }

  /* ── 播放中 chrome 手機版（Dolly 0727；1.0 mobile mock 參考＋音效增強自排）──
     版型：↺10／⏸／↻10 置中大鍵（chrome 改 flex、ctrl 轉 static → 中央鍵
     直接錨 chrome inset:0，全螢幕高度變了也照樣置中）；子母畫面上移頂列
     （旗左側，同 mock）；進度條滿版＋剩餘時間靠右；
     底排＝[音量 音效增強] 置中片名（截斷）[選集(影集) ⚙ 全螢幕] */
  .player--hero .player__chrome { display: flex; flex-direction: column; justify-content: flex-end; }
  /* 進度條移到最下面（Dolly 0727 八輪）：控制列在上、橘進度條貼底 */
  .player--hero .player__ctrl { position: static; padding: 0 16px 10px; display: flex; flex-direction: column; }
  .player--hero .player__row { position: static; padding-top: 0; gap: 12px; order: 1; }
  .player--hero .player__progress { order: 2; margin-top: 8px; }
  .player--hero .player__grp { gap: 12px; }
  .player--hero .player__btn--play {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 56px;    /* 觸控目標跟著 icon 放大（原 .player__flat 32px 摸不準） */
    height: 56px;
  }
  /* icon 尺寸要 0,4,1 才蓋得過桌機 grp--left 的 24/28px 簇規則（0,3,1）——
     先前寫 0,2,1 從未生效、實渲一直是 24px（＝Dolly 覺得小的真兇） */
  .player--hero .player__grp--left .player__flat.player__btn--play svg { width: 48px; height: 48px; }   /* Dolly 0727 四輪：56 太大收 48 */
  .player--hero [data-player-seek] { position: absolute; top: 50%; transform: translate(-50%, -50%); width: 48px; height: 48px; }
  .player--hero [data-player-seek="-10"] { left: calc(50% - 120px); }
  .player--hero [data-player-seek="10"]  { left: calc(50% + 120px); }
  .player--hero .player__grp--left .player__flat[data-player-seek] img { width: 34px; height: 34px; }
  /* .player__flat:active 的 scale 會整條蓋掉上面的置中 translate → 按下往右下
     跳半身位（Dolly 0727 三輪「icon 飄走」）；置中鍵的按壓縮放要合成寫 */
  .player--hero .player__btn--play:active,
  .player--hero [data-player-seek]:active { transform: translate(-50%, -50%) scale(0.94); }
  /* 中央已有常駐 ⏸/▶，桌機的切換爆閃回饋收掉 */
  .player--hero .player__burst { display: none; }
  /* 0727 重排（Dolly 九〜十一輪）：
     非全螢幕＝上排右起 ⚙｜子母｜AirPlay、音量左上、全螢幕鈕在進度條右
     （全絕對定位錨 chrome，DOM 不動＝桌機不受影響）；
     全螢幕＝回桌機式排法（照 1.0 mock）：頂列右起 旗｜AirPlay｜子母，
     底排 [音量] 片名置中 [⚙ 縮全螢幕]、進度條在底排上方。 */
  .player--hero .player__pop-wrap { position: absolute; top: 10px; right: 16px; }
  .player--hero .player__flat--pip { position: absolute; top: 10px; right: 60px; }
  .player--hero .player__flat--airplay { position: absolute; top: 10px; right: 104px; }
  .player--hero .player__vol { position: absolute; top: 10px; left: 16px; }
  .player--hero [data-player-fullscreen] { position: absolute; right: 12px; bottom: 4px; }
  .player--hero .player__progress { margin-right: 44px; }
  /* ── 全螢幕：頂列只剩 AirPlay/子母（⚙ 下去底排），對齊 topbar 的 ←/旗（top 16） ── */
  .player--hero .player__stage.is-fs .player__flat--airplay,
  .player--hero .player__stage.is-fs-fallback .player__flat--airplay { top: 16px; right: 60px; }
  .player--hero .player__stage.is-fs .player__flat--pip,
  .player--hero .player__stage.is-fs-fallback .player__flat--pip { top: 16px; right: 104px; }
  /* 音量/⚙/全螢幕鈕解除絕對定位回列內（桌機式底排） */
  .player--hero .player__stage.is-fs .player__vol,
  .player--hero .player__stage.is-fs-fallback .player__vol { position: static; }
  .player--hero .player__stage.is-fs .player__pop-wrap,
  .player--hero .player__stage.is-fs-fallback .player__pop-wrap { position: static; }
  .player--hero .player__stage.is-fs [data-player-fullscreen],
  .player--hero .player__stage.is-fs-fallback [data-player-fullscreen] {
    position: relative;   /* 取消 absolute 回列內；relative 而非 static＝觸控 hit-area ::after 才有錨（0728） */
    right: auto;          /* 非全螢幕規則的 right/bottom 對 relative 變位移（icon 偏 4px 病根）→ 歸零 */
    bottom: auto;
  }
  /* 進度條回到底排上方（桌機式順序） */
  .player--hero .player__stage.is-fs .player__progress,
  .player--hero .player__stage.is-fs-fallback .player__progress { order: 1; margin-top: 0; margin-right: 0; }
  .player--hero .player__stage.is-fs .player__row,
  .player--hero .player__stage.is-fs-fallback .player__row { order: 2; padding-top: 8px; align-items: center; }
  /* 片名＝真置中（剩餘空間置中會被較寬的音量組推偏）。錨 chrome 貼底定位——
     不能把 row 設 relative，會搶走頂列 AirPlay/子母的絕對定位錨點 */
  .player--hero .player__stage.is-fs .player__title,
  .player--hero .player__stage.is-fs-fallback .player__title {
    position: absolute;
    left: 50%;
    top: auto;      /* 桌機規則的 top:50% 會殘留，與 bottom 同設把盒子拉高 */
    bottom: 16px;
    transform: translateX(-50%);
    flex: none;
    max-width: 52%;
  }
  /* 全螢幕底排音量滑桿收短（72→56） */
  .player--hero .player__stage.is-fs .player__vol-range,
  .player--hero .player__stage.is-fs-fallback .player__vol-range { width: 56px; }
  /* 直版不放劇名（Dolly 0727）；全螢幕才顯示 */
  .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__title { display: none; }
  /* 旗子＝回報，與頁面右上 ⋯ 同功能 → 直版收起、只在全螢幕可見（Dolly 0727） */
  .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__topbar-right [data-title-more] { display: none; }
  /* 舞台內 ←＝與頁面頂列返回重複 → 直版收起、全螢幕（頁面 chrome 不在）保留
     （Dolly 0727 二輪）。預告片 ✕（data-watch=trailer）是關預告唯一路，不收 */
  body:not([data-watch="trailer"]) .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__flat--exit { display: none; }
  .player--hero .player__title {
    position: static;
    transform: none;
    flex: 1 1 auto;
    min-width: 0;
    max-width: none;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 14px;
  }
  .player--hero .player__remain { font-size: 12px; }
  .player--hero .player__toggle-label { font-size: 12px; }
  /* ── 設定＝底部 sheet（Dolly 0727 五輪，照 1.0 手機稿）──
     手機 ⚙ 不再用舞台內小彈窗：滿寬貼底 sheet＋把手＋全幕毛玻璃 scrim；
     列序照稿＝音效增強(開關)/畫質/音頻/字幕/倍速，無現值/箭頭；
     延伸層同 sheet 內下鑽（‹ 標題＋大列高）。桌機兩層彈窗不動。 */
  .player--hero .player__setpop.wp-pop--up {   /* 0,3,0：wp-pop--up 的錨定（bottom:calc(100%+8)/right:0）
                                                  在 build 順序較後（main.css L64>L44），同權重必輸 → 疊 class 抬 */
    position: fixed;
    top: auto;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 210;
    min-width: 0;
    border: 0;
    border-top: 1px solid var(--border-default);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    background: var(--bg-primary);
    box-shadow: 0 -24px 24px rgba(20, 20, 20, 0.64);   /* 照留言 sheet（.mtd-bar）：圓角靠上緣陰影＋清晰背景襯出 */
    padding: 28px 20px calc(16px + env(safe-area-inset-bottom, 0px));
    max-height: 76vh;
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .player--hero .player__setpop::after {   /* 把手 */
    content: '';
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 48px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.24);   /* 同 .mtd-bar__handle */
  }
  .player--hero .player__setpop::before {   /* 全幕 scrim：負 z 藏到面板底下；點擊命中 setpop 自身＝關（ds.js） */
    content: '';
    position: fixed;
    inset: 0;
    z-index: -1;
    background: rgba(0, 0, 0, 0.4);   /* 壓暗 overlay、不模糊（Dolly 0727 拍板）：背景保持清晰輪廓，sheet 圓角靠對比讀 */
  }
  /* sheet/字幕面板 z210 被 .detail-hero 的 isolation 關住 → 開著時抬整個 context 蓋過 mtd-bar(z180) */
  .detail-hero:has(.player__setpop:not([hidden])),
  .detail-hero:has(.player__ccset:not([hidden])) { z-index: 210; }
  /* 舞台本身也要抬：stage 的 isolation SC z=auto，DOM 順序在後的 relative 元素
     （watch 態搬進 h1 的評分圈）paint order 蓋過 sheet → 開著時 stage 給 z */
  .player--hero .player__stage:has(.player__setpop:not([hidden])),
  .player--hero .player__stage:has(.player__ccset:not([hidden])) { z-index: 210; }
  /* 列：icon｜標籤｜（開關）三欄、大列高；桌機的現值/箭頭收掉（照稿）；手機專用列開 */
  .player--hero .player__setpop .player__set-row {
    grid-template-columns: 28px 1fr auto;
    gap: var(--space-4);
    padding: 15px 0;
  }
  .player--hero .player__setpop .player__set-row--m { display: grid; }
  .player--hero .player__setpop .player__set-row--enhance { cursor: pointer; }
  .player--hero .player__setpop .player__set-label { font-size: 16px; }
  .player--hero .player__setpop .player__set-val,
  .player--hero .player__setpop .player__set-chev { display: none; }
  /* 列序照 1.0 稿：音效增強/畫質/音頻/字幕/倍速 */
  .player--hero .player__setpop .player__set-row--enhance { order: 1; }
  .player--hero .player__setpop [data-set-open="quality"] { order: 2; }
  .player--hero .player__setpop [data-set-open="audio"] { order: 3; }
  .player--hero .player__setpop [data-set-open="cc"] { order: 4; }
  .player--hero .player__setpop [data-set-open="speed"] { order: 5; }
  /* 延伸層：大列高；字幕層收成單欄（音頻欄整欄藏——音頻有獨立列；欄頭連 ⚙ 一起藏，
     樣式入口改「字幕樣式設定」整列） */
  .player--hero .player__setpop .player__pop-item { padding: 14px var(--space-2); font-size: 16px; }
  .player--hero .player__setpop .player__set-back { font-size: 16px; padding-bottom: 10px; }
  .player--hero .player__setpop .player__set-cc { grid-template-columns: 1fr; min-width: 0; }
  .player--hero .player__setpop .player__set-cc-col:first-child { display: none; }
  .player--hero .player__setpop .player__set-cc-head--sub { display: none; }
  /* 底排音效增強收進 sheet（Dolly 0727 五輪拍板；原提案位＝音量旁） */
  .player--hero .player__row .player__toggle-wrap { display: none; }
  /* ── 字幕細節設定面板：舞台 219 高塞不下桌機整面板 → 同款底部 sheet
     （Dolly 0727 六輪爆版修）：把手＋✕ 收掉（點外面/儲存關）、預覽縮排圓角卡、
     區塊內距 20 */
  .player--hero .player__ccset {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 210;
    padding-top: 28px;
    max-height: 82vh;
    border: 0;
    border-top: 1px solid var(--border-default);
    border-radius: var(--radius-md) var(--radius-md) 0 0;
    box-shadow: 0 -24px 24px rgba(20, 20, 20, 0.64);   /* 同 setpop：照留言 sheet 配方 */
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .player--hero .player__ccset::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: -1;
    background: rgba(0, 0, 0, 0.4);   /* 壓暗 overlay、不模糊（Dolly 0727 拍板）：背景保持清晰輪廓，sheet 圓角靠對比讀 */
  }
  .player--hero .player__ccset::after {   /* 把手（同 setpop sheet） */
    content: '';
    position: absolute;
    top: 8px;
    left: 50%;
    transform: translateX(-50%);
    width: 48px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.24);   /* 同 .mtd-bar__handle */
  }
  .player--hero .player__ccset-close { display: none; }   /* 手機關窗＝點 sheet 外或儲存 */
  .player--hero .player__ccset-preview {
    height: 150px;
    margin: 0 20px;
    border-radius: 12px;
  }
  .player--hero .player__ccset-body {
    padding: 20px 20px calc(20px + env(safe-area-inset-bottom, 0px));
    gap: var(--space-5);
  }
  .player--hero .player__ccset-group + .player__ccset-group { padding-top: var(--space-5); }

  /* ── 頁內子母畫面（Figma spec「Scroll up 子母畫面」；觸發＝ds.js IO）──
     舞台變右下迷你窗：只留 ✕（左上）/回全版（右上）/↺10 ⏸ ↻10（置中縮級），
     其餘 chrome 收；控制鍵顯隱沿用觸控 chrome 計時（進 pip 顯示→閒置淡出） */
  .player--hero:has(.player__stage.is-pip) { aspect-ratio: 16 / 9; }   /* 舞台抽走後佔位（顯示「子母畫面播放中」卡） */
  /* 佔位卡（Dolly 0727 照 1.0；版型基於稍後看閒置畫面的資訊排法）：tap＝回全版。
     顯示條件綁 stage .is-pip——不能只靠 [hidden]（display:flex 會蓋掉它＝老雷，0727 實錘） */
  .player--hero .player__pipholder { display: none; }
  .player--hero:has(.player__stage.is-pip) .player__pipholder:not([hidden]) {
    display: flex;
    align-items: center;
    position: relative;
    overflow: hidden;
    height: 100%;
    padding-inline: 20px;
    cursor: pointer;
    background: var(--neutral-900);
  }
  /* 模糊劇照背景＝照稍後看閒置畫面配方（blur36＋壓暗 0.45＋scale 糊邊羽化）；
     圖由 ds.js 進 pip 時從舞台 video 帶入 --pipholder-bg */
  .player__pipholder::before {
    content: '';
    position: absolute;
    inset: 0;
    background: var(--pipholder-bg, none) center / cover no-repeat;
    filter: blur(36px) brightness(0.45);
    transform: scale(1.12);
  }
  .player__pipholder-info { display: flex; flex-direction: column; position: relative; z-index: 1; }
  .player__pipholder-title {
    margin: 0;
    font-family: var(--font-cjk-display);
    font-size: 22px;
    line-height: 1.3;
    font-weight: var(--fw-bold);
    color: var(--text-primary);
  }
  .player__pipholder-en { margin: 4px 0 0; font-size: 13px; color: var(--text-tertiary); }
  .player__pipholder-note { margin: 16px 0 0; font-size: 14px; color: var(--text-secondary); }
  .detail-hero:has(.player__stage.is-pip) { z-index: 185; }            /* 迷你窗要蓋過後續內容與 mtd-bar(180) */
  .player--hero .player__stage.is-pip {
    position: fixed;
    top: auto;
    left: auto;
    right: 10px;
    bottom: 96px;    /* mtd-bar 之上留呼吸距離（1.0 參考） */
    width: min(340px, calc(100vw - 50px));   /* 大窗 ~86% 寬（0727 Dolly 對參考圖二修：原 300 太小） */
    aspect-ratio: 16 / 9;
    z-index: 185;
    border-radius: 16px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.55);
  }
  .player--hero .player__stage.is-pip .player__topbar,
  .player--hero .player__stage.is-pip .player__progress,
  .player--hero .player__stage.is-pip .player__grp--right,
  .player--hero .player__stage.is-pip .player__title,
  .player--hero .player__stage.is-pip .player__vol { display: none; }
  .player--hero .player__stage.is-pip .player__ctrl { padding: 0; }
  .player--hero .player__stage.is-pip .player__btn--play { width: 56px; height: 56px; }
  .player--hero .player__stage.is-pip .player__grp--left .player__flat.player__btn--play svg { width: 38px; height: 38px; }   /* 0727 七輪：30→38 */
  .player--hero .player__stage.is-pip [data-player-seek] { width: 44px; height: 44px; }
  .player--hero .player__stage.is-pip .player__grp--left .player__flat[data-player-seek] img { width: 26px; height: 26px; }
  .player--hero .player__stage.is-pip [data-player-seek="-10"] { left: calc(50% - 72px); }
  .player--hero .player__stage.is-pip [data-player-seek="10"]  { left: calc(50% + 72px); }
  .player--hero .player__stage.is-pip .player__pip-close,
  .player--hero .player__stage.is-pip .player__pip-restore {
    display: grid;
    position: absolute;
    top: 2px;
    width: 36px;
    height: 36px;
  }
  .player--hero .player__stage.is-pip .player__pip-close { left: 4px; }
  .player--hero .player__stage.is-pip .player__pip-restore { right: 4px; }
  .player--hero .player__stage.is-pip .player__pip-close svg { width: 20px; height: 20px; }
  .player--hero .player__stage.is-pip .player__pip-restore .player__ic { width: 20px; height: 20px; }
}

/* ── 直屏全螢幕（Dolly 0727 四輪）：影片維持 16:9 置中 letterbox（不整片
   裁切放大，同 iOS 原生播放器）；片名直屏一律不放（含全螢幕）。
   chrome 照舊貼滿版（進度/底排在螢幕底、中央鍵在螢幕正中）。 */
@media (orientation: portrait) {
  .player--hero .player__stage.is-fs .player__video,
  .player--hero .player__stage.is-fs-fallback .player__video {
    inset: auto 0;
    top: 50%;
    transform: translateY(-50%);
    height: auto;
    aspect-ratio: 16 / 9;
  }
  /* 豎屏全螢幕不放片名（Dolly 0727 晚場）；片名只在橫屏全螢幕出現 */
  .player--hero .player__stage.is-fs .player__title,
  .player--hero .player__stage.is-fs-fallback .player__title { display: none; }
}
/* ── 橫屏全螢幕（手機）：左上 ←＋片名（Dolly 0727 晚場）──
   橫屏寬 ≥769 吃桌機規則：← 被桌機收掉、片名置中 → 觸控橫屏這裡蓋回。 */
@media (hover: none) and (pointer: coarse) and (orientation: landscape) {
  .player--hero .player__stage.is-fs .player__flat--exit,
  .player--hero .player__stage.is-fs-fallback .player__flat--exit { display: grid; }
  .player--hero .player__stage.is-fs .player__title,
  .player--hero .player__stage.is-fs-fallback .player__title {
    position: fixed;   /* absolute 會錨到底部控制列（桌機版型）；fixed 直接錨全螢幕視窗 */
    left: 68px;   /* topbar 左 24 + ← 鈕 32 + 12 */
    top: 12px;
    bottom: auto;
    transform: none;
    flex: none;
    height: 32px;         /* 對齊 ← 鈕盒高，垂直置中 */
    line-height: 32px;
    max-width: 45%;
    text-align: left;
  }
  /* 預告片橫屏 fs（Dolly 0728）：左上沒有 ←（✕ 在右上）→ 68 的預留位變空白，
     片名靠齊 topbar 左緣 24 */
  body[data-watch="trailer"] .player--hero .player__stage.is-fs .player__title,
  body[data-watch="trailer"] .player--hero .player__stage.is-fs-fallback .player__title { left: 24px; }
  /* 上排更貼頂、底排更貼底（Dolly 0727 晚場二輪） */
  .player--hero .player__stage.is-fs .player__topbar,
  .player--hero .player__stage.is-fs-fallback .player__topbar { top: 12px; }
  .player--hero .player__stage.is-fs .player__ctrl,
  .player--hero .player__stage.is-fs-fallback .player__ctrl { padding-bottom: 10px; }
  /* ↺10／⏸／↻10 置中大鍵（同豎屏手機配方；fixed 錨全螢幕視窗，
     absolute 會被底部控制列收編）。:active 縮放必須合成 translate（0727 老雷） */
  .player--hero .player__stage.is-fs .player__btn--play,
  .player--hero .player__stage.is-fs-fallback .player__btn--play {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 56px;
    height: 56px;
  }
  .player--hero .player__stage.is-fs .player__grp--left .player__flat.player__btn--play svg,
  .player--hero .player__stage.is-fs-fallback .player__grp--left .player__flat.player__btn--play svg { width: 48px; height: 48px; }
  .player--hero .player__stage.is-fs [data-player-seek],
  .player--hero .player__stage.is-fs-fallback [data-player-seek] {
    position: fixed;
    top: 50%;
    transform: translate(-50%, -50%);
    width: 48px;
    height: 48px;
  }
  .player--hero .player__stage.is-fs [data-player-seek="-10"],
  .player--hero .player__stage.is-fs-fallback [data-player-seek="-10"] { left: calc(50% - 120px); }
  .player--hero .player__stage.is-fs [data-player-seek="10"],
  .player--hero .player__stage.is-fs-fallback [data-player-seek="10"] { left: calc(50% + 120px); }
  .player--hero .player__stage.is-fs .player__grp--left .player__flat[data-player-seek] img,
  .player--hero .player__stage.is-fs-fallback .player__grp--left .player__flat[data-player-seek] img { width: 34px; height: 34px; }
  .player--hero .player__stage.is-fs .player__btn--play:active,
  .player--hero .player__stage.is-fs-fallback .player__btn--play:active,
  .player--hero .player__stage.is-fs [data-player-seek]:active,
  .player--hero .player__stage.is-fs-fallback [data-player-seek]:active { transform: translate(-50%, -50%) scale(0.94); }
  /* 中央已有常駐 ⏸/▶，桌機的切換爆閃回饋收掉（同豎屏理由） */
  .player--hero .player__stage.is-fs .player__burst,
  .player--hero .player__stage.is-fs-fallback .player__burst { display: none; }
  /* 橫屏底排已有音效增強開關 → ⚙ 設定裡的列收掉免重複（Dolly 0727 晚場三輪）。
     不掛 .is-fs：橫屏非全螢幕同樣重複；蓋掉 ≤768 sheet 的 display:grid（同權重靠源序） */
  .player--hero .player__setpop .player__set-row--enhance { display: none; }
  /* 字幕樣式面板：桌機直排在 390 高橫屏裝不下（swatch 切在摺線＝爆版）→
     整組壓縮成一屏放得下（Dolly 0727 晚場四輪） */
  .player--hero .player__ccset-preview { height: 64px; }
  .player--hero .player__ccset-body {
    max-width: 640px;
    padding: 12px var(--space-6) 8px;
    gap: var(--space-3);
  }
  .player--hero .player__ccset-group + .player__ccset-group { padding-top: var(--space-3); }
  .player--hero .player__ccset-label { margin-bottom: var(--space-2); }
  .player--hero .player__ccset-size { padding: var(--space-2) 0; }
  .player--hero .player__ccset-swatches { margin-bottom: var(--space-2); }
  .player--hero .player__ccset-swatch { width: 40px; height: 40px; }
  .player--hero .player__ccset-alpha { padding: var(--space-2) 0; }
  .player--hero .player__ccset-actions { margin-top: var(--space-2); }
}

/* ── 結尾導流 end-card（預告片播完；Dolly 0718）──
   末幀之上疊深色覆蓋＋置中「喜歡這部片嗎？」＋租借看正片／重播／關閉。
   已擁有（data-owned）時「租借看正片」收起。開著時 chrome 收（同 gate）。 */
.player--hero .player__endcard[hidden] { display: none; }
.player--hero .player__endcard {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: grid;
  place-items: center;
  background: rgba(0, 0, 0, 0.78);
}
.player--hero .player__stage:has(.player__endcard:not([hidden])) .player__chrome { display: none; }
/* 關閉＝右上 ✕（Dolly 0718 二輪；內縮 20px → 24px icon 距邊，與播放中 ✕ 一致） */
.player--hero .player__endcard-close {
  position: absolute;
  top: var(--space-5);
  right: var(--space-5);
}
/* ✕ icon＝close.svg（同播放中 ✕／全站 ds-modal 標準），24px 由 .player__flat .player__ic 規則統一 */
.player__endcard-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  max-width: 680px;
  padding: 0 var(--space-6);
  text-align: center;
}
.player__endcard-eyebrow {
  margin: 0;
  font-size: var(--fs-caption);
  letter-spacing: 0.08em;
  color: var(--text-tertiary);
}
.player__endcard-title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: 32px;
  line-height: var(--lh-h2);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
/* 立即租借／重播＝等寬兩顆（Dolly 0718 二輪）；關閉已移右上 ✕ */
.player__endcard-actions {
  display: flex;
  align-items: stretch;
  gap: var(--space-3);
  margin-top: var(--space-4);
  width: 100%;
  max-width: 380px;
  margin-inline: auto;
}
.player__endcard-actions .btn { flex: 1 1 0; min-width: 0; }
.player__endcard-replay { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); }
.player__endcard-ic { width: 20px; height: 20px; flex: none; }
body[data-owned] .player__endcard-rent { display: none; }

/* ══ 播完正片引導寫影評（Dolly 0718）══
   重糊底圖上：直式海報（左）＋喜好/評分/分潤 CTA（右）。三步走 data-review-step；
   tastemaker=★／filmmaker=開拍板 走 data-usertype。 */
.player--hero .player__review[hidden] { display: none; }
.player--hero .player__review {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6%;
  padding-inline: 8%;
  background: rgba(0, 0, 0, 0.55);
}
.player--hero .player__stage:has(.player__review:not([hidden])) .player__video { filter: blur(36px) brightness(0.4); transform: scale(1.12); }
.player--hero .player__stage:has(.player__review:not([hidden])) .player__chrome { display: none; }
.player__review-poster {
  flex: none;
  height: 62%;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: var(--radius-md);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}
.player__review-main {
  flex: 0 1 460px;
  min-width: 0;
  color: var(--text-primary);
}
.player__review-q {
  margin: 0 0 var(--space-6);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  line-height: var(--lh-h3);
}
.player__review-q em { font-style: normal; color: var(--brand-500); }
.player__review-skip {
  border: 0;
  background: transparent;
  color: var(--text-tertiary);
  font-size: var(--fs-body);
  cursor: pointer;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: 3px;
}
.player__review-skip:hover { color: var(--text-secondary); }
.player__review-q .player__review-skip { margin-left: var(--space-3); font-size: var(--fs-body); vertical-align: middle; }
.player__review-actions { display: flex; align-items: stretch; gap: var(--space-3); }
.player__review-like { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); }
.player__review-like .player__ic { width: 20px; height: 20px; }

/* 評分列（★／開拍板）：空心=灰、選到=橘實心；hover 預覽 */
.player__review-rating { display: flex; gap: var(--space-3); margin-bottom: var(--space-6); }
.player__rate-item {
  position: relative;
  width: 40px;
  height: 40px;
  padding: 0;
  border: 0;
  background: transparent;
  color: var(--text-primary);
  cursor: pointer;
  transition: transform 120ms var(--ease-out);
}
.player__rate-item:hover { transform: scale(1.08); }
/* 實心圖示，空心=淡透明度、選到=橘實心（Dolly 0718 二輪） */
/* ⚠️ opacity 不加過場：color 瞬切、opacity 若漸變 → 移開滑鼠瞬間會「白＋高不透明」閃白（Dolly 0719） */
.player__rate-item .player__ic { width: 40px; height: 40px; opacity: 0.18; }
.player__rate-item.is-on { color: var(--brand-500); }
.player__rate-item.is-on .player__ic { opacity: 1; }
/* 圖示切換：預設 ★，filmmaker=開拍板 */
.player__rate-ic { --pp-ic: url('assets/icons/star.svg'); }
[data-usertype="filmmaker"] .player__rate-ic { --pp-ic: url('assets/icons/filmmaker.svg'); }

.player__review-links { display: flex; gap: var(--space-4); }

/* 影評分潤 CTA 改用既有 .ds-modal primitive（padding/scrim/✕/footer 由 ds.css 提供），
   此處不再自刻卡片。步驟顯隱： */
.player__review-pane[hidden] { display: none; }

/* ══ 播完已購單集收尾詢問（Dolly 0728 照 1.0 mock）══
   評分引導結束後：片名＋下一集資訊＋[解鎖剩餘｜買下一集]＋重播上一集。
   同 review 疊加語彙：糊底、chrome 收起。 */
.player--hero .player__epend[hidden] { display: none; }
.player--hero .player__epend {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: flex;
  align-items: center;
  padding-inline: 8%;
  background: rgba(0, 0, 0, 0.55);
}
.player--hero .player__stage:has(.player__epend:not([hidden])) .player__video { filter: blur(36px) brightness(0.4); transform: scale(1.12); }
.player--hero .player__stage:has(.player__epend:not([hidden])) .player__chrome { display: none; }
.player__epend-main { color: var(--text-primary); }
.player__epend-title {
  margin: 0 0 var(--space-2);
  font-family: var(--font-cjk-display);
  font-size: 32px;
  line-height: var(--lh-h2);
  font-weight: var(--fw-bold);
}
.player__epend-next {
  margin: 0 0 var(--space-5);
  font-size: var(--fs-body);
  color: var(--text-secondary);
}
.player__epend-actions { display: flex; align-items: stretch; gap: var(--space-3); margin-bottom: var(--space-5); }
.player__epend-cta { height: 58px; min-width: 200px; }   /* 同詳情頁購買 pill（Dolly 0728 定 200） */
.player__epend-cta[hidden] { display: none; }
/* 白玻璃（買下一集）＝白爆米花＋灰白小字（Dolly 0728；橘色 token 只留給解鎖鈕） */
.player__epend-cta.btn--ghost .detail-price__coin { background-color: var(--text-primary); }
.player__epend-cta.btn--ghost .detail-price__label { color: var(--text-secondary); }
.player__epend-replay {
  display: inline-flex; align-items: center; gap: var(--space-2);
  border: 0; background: transparent; padding: 0;
  color: var(--text-primary); font-size: var(--fs-body); cursor: pointer;
}
.player__epend-replay svg { width: 20px; height: 20px; }
.player__epend-replay:hover { color: var(--text-secondary); }

/* 手機（Dolly 0728）：桌機左海報＋右 CTA 塞不進 219 高小舞台（btn--lg 直接爆出右緣）
   → 照短劇版收法：海報收起、內容置中、字級/按鈕縮一級。scope 在 .player--hero，
   短劇頁自己的置中版不受影響。※ 要開在桌機規則之後——手機主 block(1039) 在前面，
   同權重會被這段桌機規則反蓋 */
@media (max-width: 768px) {
  .player--hero .player__review { padding-inline: 20px; }
  .player--hero .player__review-poster { display: none; }
  .player--hero .player__review-main { flex: 0 1 auto; text-align: center; }
  .player--hero .player__review-q { margin-bottom: 14px; font-size: 18px; line-height: 1.4; }
  .player--hero .player__review-q .player__review-skip { font-size: 14px; }
  .player--hero .player__review-actions { justify-content: center; }
  .player--hero .player__review-actions .btn { height: 40px; padding-inline: 24px; font-size: 14px; }
  .player--hero .player__review-like .player__ic { width: 18px; height: 18px; }
  .player--hero .player__review-rating { justify-content: center; gap: 8px; margin-bottom: var(--space-4); }
  .player--hero .player__rate-item { width: 36px; height: 36px; }
  .player--hero .player__rate-item .player__ic { width: 36px; height: 36px; }
  .player--hero .player__review-links { justify-content: center; }

  /* 播完單集收尾詢問手機版（Dolly 0728）：219 高小舞台縮一級＋置中 */
  .player--hero .player__epend { justify-content: center; padding-inline: 16px; }
  .player--hero .player__epend-main { text-align: center; width: 100%; }   /* main 收縮到內容寬會夾住滿寬鈕 */
  .player--hero .player__epend-title { font-size: 20px; margin-bottom: 4px; }
  .player--hero .player__epend-next { font-size: var(--fs-body-sm); margin-bottom: var(--space-3); }
  /* 手機改直排滿寬（Dolly 0728 三輪：並排已吃滿仍不夠寬 → 上下兩顆各 ~320） */
  .player--hero .player__epend-actions { flex-direction: column; align-items: center; gap: 8px; margin-bottom: var(--space-3); }
  .player--hero .player__epend-cta { height: 48px; min-width: 0; width: 100%; max-width: 320px; padding-inline: 16px; }
  .player--hero .player__epend-cta .detail-price__amount { font-size: 15px; }
  .player--hero .player__epend-cta .detail-price__label { font-size: 11px; white-space: nowrap; }
  .player--hero .player__epend-replay { font-size: var(--fs-body-sm); }

  /* 預告片結尾導流卡（Dolly 0728）：32px 標題＋btn--lg 在 219 高小舞台過大
     → 照上面評分引導同款縮一級；✕ 對齊播放中 chrome 的右上角落（10/16） */
  .player--hero .player__endcard-body { gap: var(--space-2); padding: 0 20px; }
  .player__endcard-title { font-size: 20px; }
  .player--hero .player__endcard-close { top: 10px; right: 16px; }
  .player__endcard-actions { margin-top: var(--space-2); max-width: 300px; }
  .player__endcard-actions .btn { height: 40px; font-size: 14px; }
  .player__endcard-ic { width: 16px; height: 16px; }

  /* 預告片模式右上鈕位（Dolly 0728）：✕ 在 .player__topbar（自帶絕對定位錨點、
     top 24），⚙/子母/AirPlay 錨 chrome(inset:0) top:10——兩套座標系疊在同一角
     → topbar 歸零對齊 stage 座標、✕ 排最右，三顆各左移一格（間距同 44 步進） */
  body[data-watch="trailer"] .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__topbar { top: 0; left: 0; right: 0; padding-inline: 0; }
  body[data-watch="trailer"] .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__flat--exit { position: absolute; top: 10px; right: 16px; }
  body[data-watch="trailer"] .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__pop-wrap { right: 60px; }
  body[data-watch="trailer"] .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__flat--pip { right: 104px; }
  body[data-watch="trailer"] .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__flat--airplay { right: 148px; }

  /* 預告片＋直式全螢幕（Dolly 0728 二輪）：fs 上排＝旗(topbar)＋AirPlay/子母(chrome
     絕對鈕)，trailer 的 ✕ 進來又是兩套座標系撞同一角 → 同款歸零；右起
     ✕｜旗｜AirPlay｜子母（fs 上排 top:16、44 步進）。橫屏 fs 是桌機式底排不受影響 */
  body[data-watch="trailer"] .player--hero :is(.player__stage.is-fs, .player__stage.is-fs-fallback) .player__topbar { top: 0; left: 0; right: 0; padding-inline: 0; }
  body[data-watch="trailer"] .player--hero :is(.player__stage.is-fs, .player__stage.is-fs-fallback) .player__flat--exit { position: absolute; top: 16px; right: 16px; }
  body[data-watch="trailer"] .player--hero :is(.player__stage.is-fs, .player__stage.is-fs-fallback) .player__topbar [data-title-more] { position: absolute; top: 16px; right: 60px; }
  body[data-watch="trailer"] .player--hero :is(.player__stage.is-fs, .player__stage.is-fs-fallback) .player__flat--airplay { top: 16px; right: 104px; }
  body[data-watch="trailer"] .player--hero :is(.player__stage.is-fs, .player__stage.is-fs-fallback) .player__flat--pip { top: 16px; right: 148px; }
}

/* ══ 影集播放器右側集數面板（Dolly 0719）══
   owned wp-off 影集進播放器 → 影片縮左、右側集數面板（list 縮圖清單／grid 數字格）。
   面板高度貼齊 16:9 stage：內容放 absolute inner，不撐高 row。 */
.player__eps { display: none; }
body[data-t-form="series"][data-owned][data-watch] .player--hero {
  display: flex;
  gap: 0;                 /* Figma：影片與集數面板相鄰、面板靠自身內距分隔 */
  align-items: stretch;
}
body[data-t-form="series"][data-owned][data-watch] .player--hero .player__stage {
  flex: 1 1 auto;
  min-width: 0;
}
body[data-t-form="series"][data-owned][data-watch][data-eps-open] .player__eps {
  display: block;
  position: relative;
  flex: 0 0 22.75%;       /* Figma 273/1200：影片區 77.25% 為主、集數面板 22.75% */
  min-width: 0;
}
/* owned 影集 → 下方逐集租借 section 收起（右側面板已含清單） */
body[data-t-form="series"][data-owned] [data-sec="episodes"] { display: none; }
/* 手機（Dolly 0728 照 1.0 mock）：已租借影集不走「影片左＋集數右側面板」分割，
   影片滿版、集數維持頁內 mep 格（租借資訊之下、tabs 之上）。
   豎屏非全螢幕 chrome 不出「選集」icon（集數在下方頁面裡；全螢幕保留＝開
   stage 內選集覆蓋層） */
@media (max-width: 768px) {
  body[data-t-form="series"][data-owned][data-watch] .player--hero { display: block; }
  body[data-t-form="series"][data-owned][data-watch][data-eps-open] .player__eps { display: none; }
  body[data-t-form="series"][data-owned] [data-sec="episodes"] { display: flex; }
  .player--hero .player__stage:not(.is-fs):not(.is-fs-fallback) .player__flat--episodes { display: none; }
}

.player__eps-inner {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
}
.player__eps-head {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-4) var(--space-4) var(--space-3);
}
.player__eps-count { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-regular); color: var(--text-tertiary); }
.player__eps-toggle {
  width: 32px; height: 32px; flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: var(--radius-md);
  background: transparent; color: var(--text-secondary); cursor: pointer;
}
.player__eps-toggle:hover { color: var(--text-primary); background: var(--white-alpha-8); }
.player__eps-toggle svg { width: 20px; height: 20px; }
/* 切換 icon：list 檢視顯示 grid icon（可切去 grid）；grid 檢視顯示 list icon */
.player__eps[data-eps-view="list"] .player__eps-toicon--list,
.player__eps[data-eps-view="grid"] .player__eps-toicon--grid { display: none; }

.player__eps-list, .player__eps-grid { flex: 1 1 auto; overflow-y: auto; min-height: 0; padding: 0 var(--space-3) var(--space-3); }
.player__eps-list[hidden], .player__eps-grid[hidden] { display: none; }

/* list 檢視：縮圖＋集名＋描述，當前集紅條＋暫停 */
.player__eps-item {
  display: flex;
  align-items: stretch;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3);
  border: 0; border-radius: var(--radius-md);
  background: transparent; text-align: left; cursor: pointer;
  transition: background 150ms var(--ease-out);
}
.player__eps-item:hover { background: var(--white-alpha-8); }
.player__eps-thumb {
  position: relative;
  flex: none;
  align-self: flex-start;   /* 16:9 橫式框（後台上傳逐集劇照即此比例）；先用海報 cover 佔位 */
  height: 66px; aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm);
  background: var(--bg-tertiary) center / cover no-repeat;
  overflow: hidden;
}
.player__eps-thumb-bar {   /* 當前集紅色進度條 */
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: rgba(255, 255, 255, 0.25);
  z-index: 1;
}
.player__eps-thumb-bar::after { content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 45%; background: var(--brand-500); }
.player__eps-item:not(.is-current) .player__eps-thumb-bar { display: none; }
.player__eps-pause {   /* 當前集：暫停疊層 */
  position: absolute; inset: 0; z-index: 1;
  display: grid; place-items: center;
  background: rgba(0, 0, 0, 0.35);
  color: var(--text-primary);
}
.player__eps-item:not(.is-current) .player__eps-pause { display: none; }
.player__eps-pause svg { width: 22px; height: 22px; }
.player__eps-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: var(--space-1); }
.player__eps-title { font-size: var(--fs-caption); font-weight: var(--fw-medium); color: var(--text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player__eps-desc {   /* Figma：描述兩行截斷 */
  font-size: var(--fs-caption); color: var(--text-tertiary); line-height: var(--lh-tight, 1.4);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.player__eps-item.is-current .player__eps-title { color: var(--brand-500); }
/* 租借到期提示：列右下角膠囊；當前集不顯示 */
.player__eps-expire {
  margin-top: auto; align-self: flex-end;
  padding: 2px var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);
  font-family: var(--font-latin); font-size: var(--fs-caption); color: var(--text-secondary);
  white-space: nowrap;
}
.player__eps-item.is-current .player__eps-expire { display: none; }

/* grid 檢視：範圍標＋數字格 */
.player__eps-range { display: block; padding: var(--space-2) var(--space-3); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.player__eps-nums { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--space-2); padding: 0 var(--space-3); }
.player__eps-num {
  aspect-ratio: 1 / 1;
  display: grid; place-items: center;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-md);
  background: var(--bg-primary); color: var(--text-secondary);
  font-family: var(--font-latin); font-size: var(--fs-body); cursor: pointer;
}
.player__eps-num:hover { color: var(--text-primary); border-color: var(--border-default); }
.player__eps-num.is-current { border-color: var(--brand-500); color: var(--brand-500); }

/* ══ 已購買子狀態：只買單集（上鎖＋解鎖 CTA）／先買未上映（將於 X 上映）══ */

/* devbar「購買狀態」的選項變體（Dolly 0731 二輪：影集購買三態併入購買狀態）：
   電影＝未購買／已購買／已購買稍後看；
   影集＝未購買／完整已購／只買單集／先買未上映／已購買稍後看。 */
body:not([data-t-form="series"]) .wp-roomdev__opt--series { display: none; }
body[data-t-form="series"] .wp-roomdev__opt--film { display: none; }

/* devbar「共看派對」群組只對有入場券的作品有意義（目前只有我要衝線；
   一念無明／塑膠花等 data-t-wp=off 的作品隱藏，Dolly 0731） */
body:not([data-t-wp="on"]) .wp-roomdev__grp--wp { display: none; }


/* 一次解鎖剩餘集數 CTA（orange ghost 兩行）——只在 single 狀態顯示 */
.player__eps-unlock {
  flex: none;
  width: calc(100% - var(--space-3) * 2);
  margin: 0 var(--space-3) var(--space-3);   /* 對齊清單左右內距 */
  height: 58px;                              /* 同詳情頁購買 pill（.detail-actions .btn--lg，Dolly 0728） */
  padding: 0 var(--space-4);
}
.player__eps-unlock[hidden] { display: none; }

/* list：鎖／未上映的縮圖上鎖疊層 */
.player__eps-lock {
  position: absolute; inset: 0; z-index: 2;
  display: none; place-items: center;
  background: rgba(0, 0, 0, 0.55);
  color: var(--text-primary);
}
.player__eps-lock svg { width: 22px; height: 22px; }
.player__eps-item.is-locked .player__eps-lock,
.player__eps-item.is-coming .player__eps-lock { display: grid; }
/* 鎖住／未上映：整列淡化、游標非可點、藏當前集指示與到期膠囊 */
.player__eps-item.is-locked,
.player__eps-item.is-coming { cursor: default; }
.player__eps-item.is-locked .player__eps-title,
.player__eps-item.is-coming .player__eps-title { color: var(--text-secondary); }
.player__eps-item.is-locked:hover,
.player__eps-item.is-coming:hover { background: transparent; }
.player__eps-item.is-locked .player__eps-pause,
.player__eps-item.is-coming .player__eps-pause,
.player__eps-item.is-locked .player__eps-thumb-bar,
.player__eps-item.is-coming .player__eps-thumb-bar { display: none; }
/* 未上映：描述換成「將於 X 上映」（比描述更深一階，與描述文字區隔） */
.player__eps-coming { display: none; font-size: var(--fs-caption); color: var(--text-disabled); }
.player__eps-item.is-coming .player__eps-desc { display: none; }
.player__eps-item.is-coming .player__eps-coming { display: block; }
/* 到期膠囊只在完整已購狀態顯示（子狀態不顯示） */
body[data-eps-buy="single"] .player__eps-expire,
body[data-eps-buy="preorder"] .player__eps-expire,
.player__eps-item.is-locked .player__eps-expire,
.player__eps-item.is-coming .player__eps-expire { display: none; }

/* grid：數字格右上角鎖 icon＋淡化＋懸浮 tooltip */
.player__eps-num { position: relative; }
.player__eps-num-no { display: grid; place-items: center; width: 100%; height: 100%; }
.player__eps-numlock {
  position: absolute; top: 3px; right: 3px; z-index: 1;
  display: none; width: 12px; height: 12px;
  color: var(--text-tertiary);
}
.player__eps-numlock svg { width: 100%; height: 100%; display: block; }
.player__eps-num.is-locked .player__eps-numlock,
.player__eps-num.is-coming .player__eps-numlock { display: block; }
.player__eps-num.is-locked, .player__eps-num.is-coming { color: var(--text-tertiary); cursor: default; }
.player__eps-num.is-locked:hover, .player__eps-num.is-coming:hover { border-color: var(--border-subtle); color: var(--text-tertiary); }
/* 未上映數字格：懸浮顯示「將於 X 上映」tooltip */
.player__eps-num.is-coming:hover::after {
  content: attr(data-air);
  position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  z-index: 5; white-space: nowrap;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  background: var(--bg-tertiary); color: var(--text-primary);
  font-family: var(--font-cjk-text); font-size: var(--fs-caption); font-weight: var(--fw-regular);
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.45);
}
.player__eps-num.is-coming:hover::before {
  content: ''; position: absolute; bottom: calc(100% + 2px); left: 50%; transform: translateX(-50%);
  z-index: 5; border: 6px solid transparent; border-top-color: var(--bg-tertiary);
}

/* ══ 全螢幕右側集數覆蓋列表（Dolly 0721 二版）══
   影集全螢幕時外部右側側欄（.player__eps）在 stage 外看不到 → 選集鈕開關
   body[data-eps-open]，此列表在 stage 內、右側絕對定位覆蓋呈現（窗格模式仍用
   外部側欄）。只在全螢幕（is-fs / fallback）＋data-eps-open 顯示。
   狀態（當前/鎖/未上映）與右側面板共用；卡＝橫向列（縮圖左＋文字右）同側欄語彙。 */
.player__eppick {
  position: absolute; top: 0; right: 0; bottom: 0;
  width: min(380px, 34%); z-index: 6;
  display: none; flex-direction: column;
  background: color-mix(in srgb, var(--bg-secondary, #131313) 82%, transparent);
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  box-shadow: -12px 0 40px rgba(0, 0, 0, 0.5);
}
body[data-eps-open] .player__stage.is-fs .player__eppick,
body[data-eps-open] .player__stage.is-fs-fallback .player__eppick {
  display: flex;
  animation: eppick-in 240ms var(--ease-out) both;
}
@keyframes eppick-in { from { opacity: 0; transform: translateX(24px); } }

.player__eppick-head {
  flex: none; display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-2); padding: var(--space-5) var(--space-4) var(--space-3);
}
.player__eppick-count {
  font-family: var(--font-cjk-text); font-size: var(--fs-body-sm);
  font-weight: var(--fw-regular); color: var(--text-tertiary);
}
.player__eppick-headbtns { display: flex; align-items: center; gap: var(--space-1); }
/* 檢視切換 icon（沿用側欄 .player__eps-toggle），依 data-eppick-view 換顯示 */
.player__eppick[data-eppick-view="list"] [data-eppick-toggle] .player__eps-toicon--list,
.player__eppick[data-eppick-view="grid"] [data-eppick-toggle] .player__eps-toicon--grid { display: none; }
.player__eppick-close svg { width: 20px; height: 20px; }
.player__eppick-rail {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column; gap: var(--space-2);
  overflow-y: auto; overflow-x: hidden;
  padding: 0 var(--space-3) var(--space-4);
}
.player__eppick-rail[hidden] { display: none; }
.player__eppick-card {
  flex: none; display: flex; gap: var(--space-3); align-items: center;
  padding: var(--space-3) var(--space-2); border: 0; border-radius: var(--radius-md);
  background: none; text-align: left; cursor: pointer; color: var(--text-primary); font-family: inherit;
}
.player__eppick-card:hover { background: var(--white-alpha-8); }
.player__eppick-thumb {
  position: relative; flex: none; width: 112px; aspect-ratio: 16 / 9;
  border-radius: var(--radius-sm); overflow: hidden;
  background: var(--bg-tertiary) center / cover no-repeat;
}
.player__eppick-info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.player__eppick-title {
  font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--text-primary);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.player__eppick-card.is-current .player__eppick-title { color: var(--brand-500); }
.player__eppick-desc {
  font-size: var(--fs-caption); color: var(--text-tertiary); line-height: var(--lh-tight, 1.4);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* 當前集：縮圖暫停疊層（沿用右側面板語彙） */
.player__eppick-pause {
  position: absolute; inset: 0; z-index: 1; display: none; place-items: center;
  background: rgba(0, 0, 0, 0.35); color: var(--text-primary);
}
.player__eppick-pause svg { width: 22px; height: 22px; }
.player__eppick-card.is-current .player__eppick-pause { display: grid; }
/* 鎖／未上映：縮圖鎖疊層＋淡化＋停用 hover */
.player__eppick-lock {
  position: absolute; inset: 0; z-index: 2; display: none; place-items: center;
  background: rgba(0, 0, 0, 0.6); color: var(--text-primary);
}
.player__eppick-lock svg { width: 22px; height: 22px; }
.player__eppick-card.is-locked .player__eppick-lock,
.player__eppick-card.is-coming .player__eppick-lock { display: grid; }
.player__eppick-card.is-locked, .player__eppick-card.is-coming { cursor: default; }
.player__eppick-card.is-locked .player__eppick-title,
.player__eppick-card.is-coming .player__eppick-title { color: var(--text-secondary); }
.player__eppick-card.is-locked:hover, .player__eppick-card.is-coming:hover { background: transparent; }
.player__eppick-coming { display: none; font-size: var(--fs-caption); color: var(--text-disabled); }
.player__eppick-card.is-coming .player__eppick-desc { display: none; }
.player__eppick-card.is-coming .player__eppick-coming { display: block; }
/* 方格檢視（沿用 .player__eps-nums 網格＋.player__eps-num 格子；此處只加捲動/收合） */
.player__eppick-grid {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; align-content: start;
  gap: var(--space-2); padding: 0 var(--space-4) var(--space-4);
}
.player__eppick-grid[hidden] { display: none; }

/* 手機直式全螢幕（Dolly 0728 照短劇集數面板配方）：選集覆蓋層不走右側窄欄，
   改底部 sheet——滿寬貼底、頂圓角＋抓柄、由下往上滑入、上方影片透出、
   內容自適應（上限同短劇 100dvh-64）；方格照短劇 7 欄。橫屏 fs>768 不受影響 */
@media (max-width: 768px) {
  .player__stage.is-fs .player__eppick,
  .player__stage.is-fs-fallback .player__eppick {
    top: auto; left: 0; right: 0; bottom: 0;
    width: 100%;
    max-height: calc(100dvh - 64px);
    border-top: 1px solid var(--border-default);
    border-radius: 20px 20px 0 0;
    padding-top: 16px;
    padding-bottom: max(16px, env(safe-area-inset-bottom, 0px));
    background: var(--bg-primary);
    -webkit-backdrop-filter: none; backdrop-filter: none;
    box-shadow: 0 -16px 48px rgba(0, 0, 0, 0.5);
  }
  .player__stage.is-fs .player__eppick::before,
  .player__stage.is-fs-fallback .player__eppick::before {
    content: ''; position: absolute; top: 10px; left: 50%; transform: translateX(-50%);
    width: 48px; height: 4px; border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.24);
  }
  body[data-eps-open] .player__stage.is-fs .player__eppick,
  body[data-eps-open] .player__stage.is-fs-fallback .player__eppick { animation-name: eppick-up; }
  .player__stage.is-fs .player__eppick .player__eps-nums,
  .player__stage.is-fs-fallback .player__eppick .player__eps-nums { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
}
@keyframes eppick-up { from { opacity: 0; transform: translateY(48px); } }

/* 舊手機快修（0718）：hero 已有完整手機 chrome 版型（上面 0727 區塊），
   這裡只留給非 hero 的 player.html（待廢頁）；別再把 hero 片名藏掉/折行 */
@media (max-width: 768px) {
  .player--hero .player__topbar,
  .player--hero .player__ctrl { padding-inline: var(--space-4); }
  .player--hero .player__topbar { top: var(--space-4); left: 0; right: 0; padding-inline: var(--space-4); }
}

@media (max-width: 640px) {
  .player:not(.player--hero) .player__row { flex-wrap: wrap; row-gap: var(--space-2); }
  .player:not(.player--hero) .player__title { order: 3; flex-basis: 100%; text-align: left; }
  .player:not(.player--hero) .player__toggle-label { display: none; }
}

/* ══ 相關影片燈箱（Dolly 0718「按脈絡分兩路」）══
   隨手瀏覽的相關影片＝原地 16:9 彈窗（乾淨框，不搬整套播放器 chrome）。
   走 ds-modal primitive（scrim/✕/Esc/捲動鎖）；此處只管 16:9 框與 mock 播放。 */
.trailer-lb.ds-modal[data-size="lg"] .ds-modal__panel {   /* (0,4,0) 蓋過 ds.css data-size 寬 */
  width: min(92vw, 960px);
  max-width: 960px;
}
.trailer-lb__title { margin: 0 0 var(--space-4); }
/* 手機（Dolly 0729）：標題縮級（24→18）並與 ✕ 光學對齊（✕ 中心=16+16=32、
   標題中心=padding-top 19+行高 26/2=32）；框左右下間距 32→16 */
@media (max-width: 768px) {
  .trailer-lb.ds-modal .ds-modal__panel { padding: 19px 16px 16px; }
  /* 雙 class 抬權重：ds.css 的 .ds-modal__title 載在 components.css 後，同權重會反蓋 */
  .trailer-lb .trailer-lb__title { font-size: 18px; line-height: 26px; margin-bottom: var(--space-3); }
}
.trailer-lb__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #000;
  cursor: pointer;
}
.trailer-lb__poster {
  position: absolute;
  inset: 0;
  background: center / cover no-repeat;
}
.trailer-lb__frame::after {   /* 播放時稍微壓暗，凸顯這是「正在播」 */
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0);
  transition: background 200ms var(--ease-out);
  pointer-events: none;
}
.trailer-lb__frame.is-playing::after { background: rgba(0, 0, 0, 0.28); }
.trailer-lb__play {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 72px;
  height: 72px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.55);
  color: var(--text-primary);
  pointer-events: none;   /* 點擊由 frame 接（播放／暫停切換） */
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.trailer-lb__play svg { width: 30px; height: 30px; margin-left: 3px; }
.trailer-lb__frame.is-playing .trailer-lb__play { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
.trailer-lb__bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background: rgba(255, 255, 255, 0.22);
}
.trailer-lb__bar-fill {
  display: block;
  width: 0;
  height: 100%;
  background: var(--brand-500);
  transition: width 8s linear;
}

@media (max-width: 640px) {
  .trailer-lb__play { width: 60px; height: 60px; }
  .trailer-lb__play svg { width: 24px; height: 24px; }
}

/* ─────────────────────────────────────────
   SCROLLBAR — 全站統一捲軸（Dolly 2026-07-09 拍板：預設 UA 捲軸太醜）。
   2.0 語彙：無軌道底、細膠囊 thumb、白 alpha token 三階（靜/懸/按），
   透明外框讓 thumb 內縮成細條（background-clip 技法，不佔版面）。
   Firefox 走 scrollbar-width/color 對應。深色站不分頁面主題，一體適用。
   ───────────────────────────────────────── */
/* ⚠️ 標準屬性只給 Firefox：Chrome 121+ 一旦讀到 scrollbar-width/color
   就會整組忽略下面的 ::-webkit-scrollbar 自訂（細膠囊會失效、變回粗 thumb），
   所以用 -moz-appearance 這個只有 Firefox 認的條件圈起來 */
@supports (-moz-appearance: none) {
  html {
    scrollbar-width: thin;
    scrollbar-color: var(--white-alpha-8) transparent;
  }
}
*::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}
*::-webkit-scrollbar-track {
  background: transparent;
}
*::-webkit-scrollbar-thumb {
  /* 淡一階（Dolly 2026-07-09）：可見條 10−3×2＝4px（3px 太細，同日改回），
     熱區維持 10px 好抓 */
  background: var(--white-alpha-8);
  border-radius: var(--radius-pill);
  border: 3px solid transparent;
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover {
  background-color: var(--white-alpha-16);
  background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:active {
  background-color: var(--white-alpha-16);
  background-clip: padding-box;
}
*::-webkit-scrollbar-corner {
  background: transparent;
}

/* ══════════════════════════════════════════════════════════════
   41 · 短劇播放頁（short.html）
   Figma Desktop v4 · Short_Playing / Short_Episode（node 14964-70609）。
   直式 9:16 舞台置中，左返回鈕、右動作列、右側劇集面板。
   狀態：.is-playing（chrome 隱藏，hover 舞台喚回）、[data-short-panel] hidden。
   icon 一律 inline --icon ＋這裡的 .shortplay__ico 消費——本檔打包進
   components.css（repo 根）故 markup 寫 assets/icons/（CSS var url 規則）。
   ══════════════════════════════════════════════════════════════ */

.shortplay-page { overflow: hidden; }

.shortplay {
  position: relative;
  height: calc(100dvh - 64px);
  min-height: 560px;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* 模糊劇照滿版當底（Figma：blur 10 + 黑 90%） */
.shortplay__bg {
  position: absolute;
  inset: -40px;                          /* blur 邊緣不露底 */
  background: url('assets/images/titles/nu-zongcai/bg.webp') center / cover no-repeat;
  filter: blur(10px);
  z-index: 0;
}
.shortplay__bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.9);
}

.shortplay__cluster {
  position: relative;
  z-index: 1;
  display: flex;
  align-items: stretch;
  gap: 24px;
  /* 更滿版：影片填滿 header 以下高度（上下僅留 12px），大螢幕才吃 1300 上限 */
  height: min(calc(100dvh - 64px - 24px), 1300px);
}

/* 通用 icon 消費（mask 上色） */
.shortplay__ico {
  display: inline-block;
  width: 24px;
  height: 24px;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}

/* ── 返回鈕（舞台左，玻璃圓鈕）── */
.shortplay__back {
  align-self: flex-start;
  margin-top: 16px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--white-alpha-8);
  border: 1px solid var(--border-subtle);
  backdrop-filter: blur(12px);
  color: var(--text-primary);
}
.shortplay__back:hover { background: var(--white-alpha-12, rgba(255, 255, 255, 0.12)); }
.shortplay__back .shortplay__ico { width: 20px; height: 20px; }

/* ── 直式舞台 ── */
.shortplay__stage {
  position: relative;
  height: 100%;
  aspect-ratio: 529 / 940;
  border-radius: 16px;
  overflow: hidden;
  background: var(--neutral-900);
}
.shortplay__still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.shortplay__stagetop {
  position: absolute;
  top: 16px;
  right: 16px;
  display: flex;
  gap: 12px;
  /* minibtn 有 backdrop-filter：同 pill，用 visibility+位移淡入 */
  visibility: hidden;
  transform: translateY(-6px);
  transition: transform 0.25s ease, visibility 0s linear 0.25s;
}
.shortplay__minibtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: var(--glass-bg-default);               /* DS 毛玻璃（白霧 10%）同 eppill */
  border: 1px solid var(--glass-stroke-default);
  backdrop-filter: var(--glass-blur);                /* blur(20) saturate(1.4) */
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--text-primary);
  cursor: pointer;
}
.shortplay__minibtn:hover { background: var(--glass-bg-hover); }
.shortplay__minibtn .shortplay__ico { width: 20px; height: 20px; }

/* 音量控制（左上，Dolly 0721）：hover 展開滑桿調音量、點喇叭切靜音（同主播放器 fly-out）。
   minibtn 有 backdrop-filter → 跟 stagetop 一樣用 visibility+位移隨舞台 hover 淡入。 */
.shortplay__vol {
  position: absolute;
  top: 16px;
  left: 16px;
  display: flex;
  align-items: center;
  border: 1px solid transparent;           /* 預留 hover 膠囊邊框空間，避免位移 */
  border-radius: 999px;
  visibility: hidden;
  transform: translateY(-6px);
  transition: transform 0.25s ease, background 0.18s ease, padding 0.18s ease, visibility 0s linear 0.25s;
}
.shortplay__stage:hover .shortplay__vol {
  visibility: visible;
  transform: translateY(0);
  transition: transform 0.25s ease, background 0.18s ease, padding 0.18s ease, visibility 0s;
}
/* hover 展開＝玻璃膠囊包住喇叭＋滑桿（防背景太白看不清；同 DS 玻璃） */
.shortplay__vol:hover,
.shortplay__vol:focus-within {
  background: var(--glass-bg-default);
  border-color: var(--glass-stroke-default);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  padding-right: 14px;
}
.shortplay__vol:hover .shortplay__vol-btn,
.shortplay__vol:focus-within .shortplay__vol-btn {
  background: transparent;                  /* 喇叭融入膠囊，不再是獨立玻璃圈 */
  border-color: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.shortplay__vol-ic { display: none; }
.shortplay__vol-btn[data-vol="high"] .shortplay__vol-ic--on,
.shortplay__vol-btn[data-vol="low"]  .shortplay__vol-ic--on,
.shortplay__vol-btn[data-vol="mute"] .shortplay__vol-ic--mute { display: block; }
/* fly-out 滑桿：預設收起（width 0），hover 音量控制才展開 */
.shortplay__vol-range {
  width: 0;
  height: 3px;
  margin-left: 0;
  opacity: 0;
  -webkit-appearance: none;
  appearance: none;
  background: linear-gradient(90deg, var(--brand-500) calc(var(--pp-vol, 70) * 1%), rgba(0, 0, 0, 0.28) 0);
  border-radius: var(--radius-pill);
  outline: none;
  cursor: pointer;
  transition: width 0.18s ease, opacity 0.18s ease, margin-left 0.18s ease;
}
.shortplay__vol:hover .shortplay__vol-range,
.shortplay__vol:focus-within .shortplay__vol-range {
  width: 96px;
  margin-left: 10px;
  opacity: 1;
}
.shortplay__vol-range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--text-primary); cursor: pointer;
}
.shortplay__vol-range::-moz-range-thumb {
  width: 12px; height: 12px; border-radius: 50%; border: 0;
  background: var(--text-primary); cursor: pointer;
}

/* 中央播放／暫停 */
.shortplay__pp {
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.3);
  border: none;
  color: var(--neutral-white);
  cursor: pointer;
  transition: opacity 0.25s ease;
}
.shortplay__pp .shortplay__ico { width: 32px; height: 32px; }
.shortplay__ico--pause { display: none; }
.is-playing .shortplay__ico--play { display: none; }
.is-playing .shortplay__ico--pause { display: inline-block; }

/* 底部漸層＋標題＋集數 pill */
.shortplay__foot {
  position: absolute;
  inset: auto 0 0;
  padding: 48px 20px 20px;
  pointer-events: none;                    /* 空容器不擋影片點擊；可點的 pill 自行開 auto */
}
.shortplay__foot::before {                 /* 漸層獨立一層，才能自由 opacity 淡（無 backdrop-filter） */
  content: '';
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.55) 68%);
  opacity: 0.7;                            /* 常駐淡淡 overlay 防背景太白（Dolly 0721）；hover 再加深 */
  transition: opacity 0.25s ease;
}
.shortplay__title {
  margin: 0 0 10px;
  font-size: 18px;
  font-weight: 700;
  color: var(--neutral-white);
  opacity: 0;
  transition: opacity 0.25s ease;
}
.shortplay__eppill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  width: 100%;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  background: var(--glass-bg-default);              /* 淡毛玻璃（白霧 10%，非深黑底） */
  border: 1px solid var(--glass-stroke-default);
  backdrop-filter: var(--glass-blur);               /* blur(20) saturate(1.4) */
  -webkit-backdrop-filter: var(--glass-blur);
  color: var(--text-primary);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  pointer-events: auto;
  /* 有 backdrop-filter：用 visibility+位移淡入，不動 opacity（避免父/自身 opacity 讓毛玻璃閃白） */
  visibility: hidden;
  transform: translateY(8px);
  transition: transform 0.25s ease, background 0.15s ease, visibility 0s linear 0.25s;
}
.shortplay__eppill:hover { background: var(--glass-bg-hover); }
.shortplay__eppill-chev { width: 16px; height: 16px; margin-left: auto; }

/* 進度條（手機版稿；桌機不顯示——桌機交給 hover chrome，手機常駐）。
   紅＝語意 --error-500（照稿紅色，非品牌橘）；demo 靜態 --p。 */
.shortplay__progress { display: none; }

/* 右上功能鈕＋底部片名/集數：hover 舞台才顯示（Dolly 0720）。
   ⚠️ pill/minibtn 有 backdrop-filter，用「父層/自身 opacity 淡入」會閃白（0720 修）——
   有毛玻璃的元素改 visibility+位移，漸層/標題才用 opacity 淡。 */
.shortplay__stage:hover .shortplay__stagetop,
.shortplay__stage:hover .shortplay__eppill {
  visibility: visible;
  transform: translateY(0);
}
.shortplay__stage:hover .shortplay__stagetop { transition: transform 0.25s ease, visibility 0s; }
.shortplay__stage:hover .shortplay__eppill { transition: transform 0.25s ease, background 0.15s ease, visibility 0s; }
.shortplay__stage:hover .shortplay__foot::before,
.shortplay__stage:hover .shortplay__title { opacity: 1; }
/* 中央播放鍵：暫停時常駐（可按播放）、播放中隱、hover 喚回 */
.is-playing .shortplay__pp { opacity: 0; pointer-events: none; }
/* hover 喚回暫停鍵＝僅滑鼠裝置——觸控 tap 會黏 :hover，不 gate 的話手機播放中
   點一下暫停鍵就冒出來（0725 Dolly：播放中中央鍵永遠隱藏，點影片任一處暫停） */
@media (hover: hover) and (pointer: fine) {
  .is-playing .shortplay__stage:hover .shortplay__pp { opacity: 1; pointer-events: auto; }
}

/* ── 右側動作列 ── */
.shortplay__rail {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 16px 0 24px;
  width: 48px;
}
.shortplay__railgrp {
  display: flex;
  flex-direction: column;
  gap: 16px;
  align-items: center;
}
.shortplay__action {
  display: flex;
  flex-direction: column;
  gap: 4px;
  align-items: center;
  width: 100%;
}
.shortplay__actbtn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: var(--white-alpha-8);
  border: 1px solid var(--border-subtle);
  backdrop-filter: blur(12px);
  color: var(--text-primary);
  cursor: pointer;
}
.shortplay__actbtn:hover { background: var(--white-alpha-12, rgba(255, 255, 255, 0.12)); }

/* 上一集／下一集 hover 提示：tooltip 統一從鈕右側出現（與 ⋮ 更多選單同方向）。
   content 讀 aria-label → 文案單一來源、i18n 自動跟著翻。樣式沿用 side-rail data-tip 膠囊。 */
.shortplay__navbtn { position: relative; }
.shortplay__navbtn::after {
  content: attr(aria-label);
  position: absolute;
  left: calc(100% + 12px);
  top: 50%;
  transform: translate(-4px, -50%);
  padding: 7px 12px;
  border-radius: var(--radius-pill);
  background: rgba(10, 10, 10, 0.92);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--neutral-white);
  font-family: var(--font-cjk-display);
  font-size: 12px;
  line-height: 1;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms cubic-bezier(0.23, 1, 0.32, 1),
              transform 150ms cubic-bezier(0.23, 1, 0.32, 1);
}
@media (hover: hover) and (pointer: fine) {
  .shortplay__navbtn:hover::after { opacity: 1; transform: translate(0, -50%); }
}
@media (prefers-reduced-motion: reduce) {
  .shortplay__navbtn::after { transition: opacity 100ms ease; transform: translate(0, -50%); }
}
.shortplay__actlabel {
  font-size: 14px;
  color: var(--neutral-white);
  text-align: center;
}

/* 矮桌機視窗（如 1440×900）：右列鈕稍縮，給直式舞台兩側多點呼吸空間。
   限桌機寬度（min-width:769）→ 手機直式（≤768）的動作列不受影響。 */
@media (min-width: 769px) and (max-height: 920px) {
  .shortplay__rail { width: 44px; padding: 12px 0 20px; }
  .shortplay__rail .shortplay__railgrp { gap: 14px; }
  .shortplay__rail .shortplay__actbtn { width: 40px; height: 40px; }
  .shortplay__rail .shortplay__actbtn .shortplay__ico { width: 22px; height: 22px; }
  .shortplay__rail .shortplay__actlabel { font-size: 12px; }
}

/* ── 劇集面板（Short_Episode）── */
.shortplay__panel {
  width: 400px;
  padding: 16px 8px 0 16px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  overflow-y: auto;
}
.shortplay__panel[hidden] { display: none; }

.shortpanel__head {
  display: flex;
  gap: 14px;
  align-items: flex-start;
}
.shortpanel__thumb {
  flex: none;
  width: 64px;
  height: 90px;
  border-radius: var(--radius-md);
  object-fit: cover;
}
.shortpanel__headmain { flex: 1; min-width: 0; }
/* 標題列：片名＋評分環同排（評分環由 __loadShort 換片時可更新 data-short-score） */
.shortpanel__titlerow { display: flex; align-items: center; gap: 8px; margin: 0 0 6px; }
.shortpanel__titlerow .shortpanel__title { margin: 0; }
.shortpanel__title {
  margin: 0 0 6px;
  font-size: 16px;
  font-weight: 700;
  color: var(--text-primary);
}
.shortpanel__meta {
  margin: 0 0 8px;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.shortpanel__meta-sep {
  display: inline-block;
  width: 1px;
  height: 10px;
  margin: 0 8px;
  background: var(--border-subtle);
}
.shortpanel__tags { display: flex; gap: 6px; }
.shortpanel__tag {
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);
  color: var(--text-secondary);
  font-size: 12px;
}
/* Match the canonical modal close (.ds-modal__close): 32px pill, neutral glass fill,
   secondary→primary on hover. Same close.svg icon. */
.shortpanel__close {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  background: var(--glass-bg-default);
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 150ms var(--ease-out), color 150ms var(--ease-out);
}
.shortpanel__close:hover { background: var(--glass-bg-hover); color: var(--text-primary); }
.shortpanel__close .shortplay__ico { width: 16px; height: 16px; }

.shortpanel__desc {
  position: relative;
  margin: 0;
  font-size: var(--fs-body-sm);
  line-height: 1.7;
  color: var(--text-secondary);
}
/* collapsed = 2-line clamp; expanded (JS toggles .is-expanded) = full text.
   span goes inline when expanded so 「收合」flows right after the last word. */
.shortpanel__desctext {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.shortpanel__desc.is-expanded .shortpanel__desctext {
  display: inline;
  -webkit-line-clamp: unset;
  overflow: visible;
}
.shortpanel__morelink {
  display: none;                 /* shown only when the text really overflows 2 lines (JS adds .has-more) */
  border: none;
  background: none;
  padding: 0;
  color: var(--brand-500);
  font: inherit;
  cursor: pointer;
}
.shortpanel__desc.has-more .shortpanel__morelink { display: inline; }
/* collapsed: pin 「更多」to the end of line 2；被切斷的字尾用 mask 淡出（透明漸層），
   不畫實色 overlay——面板底是玻璃（rgba+blur，影片透出）沒有單一色碼，
   之前淡到 --neutral-900 會在鈕後浮出一塊比底色深的色塊 */
.shortpanel__desc.has-more:not(.is-expanded) .shortpanel__morelink {
  position: absolute;
  right: 0;
  bottom: 0;
  padding-left: 34px;
}
.shortpanel__desc.has-more:not(.is-expanded) .shortpanel__desctext {
  /* 上層＝末行以上全顯示；下層＝末行（1.7em＝自身 line-height）右端淡出，
     70px 前全顯 → 30px 處全透，正好讓位給右貼齊的「更多」 */
  -webkit-mask: linear-gradient(#000 0 0) top / 100% calc(100% - 1.7em) no-repeat,
    linear-gradient(90deg, #000 calc(100% - 70px), transparent calc(100% - 30px)) bottom / 100% 1.7em no-repeat;
  mask: linear-gradient(#000 0 0) top / 100% calc(100% - 1.7em) no-repeat,
    linear-gradient(90deg, #000 calc(100% - 70px), transparent calc(100% - 30px)) bottom / 100% 1.7em no-repeat;
}
.shortpanel__morelink:hover { text-decoration: underline; }

/* 價格鈕（Figma「Gold Gradient」：champagne 金——1.0 設計系統色，
   2.0 tokens 尚無對應 token，先在元件層落地、待收進 tokens.css） */
/* 付費鈕：2.0 orange ghost pill（沿用 .btn--yellow-ghost + .detail-price 兩行版型） */
.shortpanel__buy {
  width: 100%;
  border-radius: var(--radius-pill);
  padding: var(--space-3) var(--space-4);
}
.shortpanel__buy-orig {   /* 原價（刪節線、淡灰） */
  font-size: var(--fs-caption);
  font-weight: var(--fw-regular);
  color: var(--text-tertiary);
  text-decoration: line-through;
}

/* 分組 tabs（一次最多 40 集） */
.shortpanel__ranges { display: flex; gap: 20px; }
.shortpanel__range {
  border: none;
  background: none;
  padding: 4px 0;
  color: var(--text-secondary);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.shortpanel__range.is-active {
  color: var(--text-primary);
  font-weight: 700;
}

/* 集數格（6 欄；免費集帶綠標；當前集亮框） */
.shortpanel__grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 8px;
}
.shortpanel__epbtn {
  position: relative;
  overflow: hidden;                        /* 綠色免費帶靠底、圓角裁切 */
  aspect-ratio: 1;
  border-radius: var(--radius-lg);         /* 方塊圓角加大 */
  background: var(--white-alpha-8);
  border: 1px solid transparent;
  color: var(--text-primary);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
}
.shortpanel__epbtn:has(.shortpanel__epbtn-free) { padding-bottom: 22px; }   /* 數字上移，讓出底部綠帶 */
.shortpanel__epbtn:hover { background: var(--white-alpha-12, rgba(255, 255, 255, 0.12)); }
.shortpanel__epbtn.is-current { border-color: var(--brand-500); color: var(--brand-500); }
.shortpanel__epbtn-free {   /* 底部綠色免費色帶（深綠底＋亮綠字） */
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 22px;
  display: grid;
  place-items: center;
  background: rgba(var(--success-rgb), 0.3);    /* 淡綠底帶（再淡一階） */
  color: var(--success-500);
  font-size: 12px;
  line-height: 1;
}
/* 付費未解鎖集：右上角鎖 icon（免費集與當前集不加）；內縮 2px＝與劇集 .mep-chip__lock 統一（Dolly 0728） */
.shortpanel__epbtn-lock {
  position: absolute;
  top: 2px;
  right: 2px;
  width: 12px;
  height: 12px;
  color: var(--text-tertiary);
}
.shortpanel__epbtn-lock svg { width: 100%; height: 100%; display: block; }

.shortpanel__note {
  margin: 0 0 16px;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  border: 1px solid var(--border-subtle);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.shortpanel__note strong { color: var(--brand-500); }

/* ── RWD：窄幅面板轉滿版覆蓋、舞台縮 ── */
@media (max-width: 1024px) {
  .shortplay__cluster { gap: 16px; }
  .shortplay__panel,
  .shortplay__comments {
    position: fixed;
    right: 0;
    top: 64px;
    bottom: 0;
    width: min(400px, 92vw);
    background: rgba(10, 10, 10, 0.92);
    backdrop-filter: blur(16px);
    border-left: 1px solid var(--border-subtle);
    padding: 20px 16px 0;
    z-index: 40;
  }
  .shortplay__comments .comment-col { padding-left: 0; }
}
@media (max-width: 768px) {
  /* ── 手機 chrome（2026-07-24）：短劇＝沉浸式滿版（TikTok 式）。
     側欄＋桌機 header 整條收起——返回鈕已疊在舞台左上、動作全在右側 rail，
     頁面不需要任何 chrome。scope 全鎖 .shortplay-page 不影響他頁
     （同 watch-party.css 的 rf-detail gate 做法；短劇頁 body 無 rf-detail）。 */
  body.has-rail.shortplay-page { padding-left: 0; }
  .shortplay-page .side-rail { display: none; }
  .shortplay-page .header { display: none; }
  .shortplay { height: 100dvh; min-height: 0; }
  .shortplay__cluster { height: 100%; gap: 0; }
  /* margin-top 歸零＋vol 拿掉佔位邊框 → 兩鈕同 top=12、同 40px，左右對齊 */
  .shortplay__back { position: absolute; left: 12px; top: max(12px, env(safe-area-inset-top)); margin-top: 0; z-index: 5; }
  .shortplay__stage { border-radius: 0; aspect-ratio: auto; width: 100vw; }
  /* 面板手機滿版規則在檔尾（要蓋過位於最尾端的 ≤1024 面板塊，見該處註解） */

  /* ── 手機版稿（Figma Short mobile 0725）──
     桌機的 hover 顯隱在手機不存在 → chrome 常駐，改由 is-idle（2 秒閒置，
     ds.js short-play）隱藏。毛玻璃件（back/vol/eppill）用 visibility 切，
     其餘（rail/標題/漸層/pp）用 opacity 淡——backdrop-filter 動 opacity 會閃白。 */

  /* 音量：移右上（桌機在左上）、常駐；滑桿收起=點擊只切靜音 */
  .shortplay__vol {
    left: auto;
    right: 12px;
    top: max(12px, env(safe-area-inset-top));
    border: none;                 /* 桌機 hover 膠囊的佔位框，手機收掉才與返回鈕齊高 */
    visibility: visible;
    transform: none;
  }
  .shortplay__vol-range { display: none; }
  .shortplay__vol:hover, .shortplay__vol:focus-within { background: none; border-color: transparent; padding: 0; }
  /* 全螢幕鈕收起（頁面已滿版） */
  .shortplay__stagetop { display: none; }

  /* 右列＝裸 icon（照稿無圓底玻璃）；label/數字 12px */
  .shortplay__rail { right: 12px; bottom: calc(128px + env(safe-area-inset-bottom)); width: auto; }
  .shortplay__rail .shortplay__railgrp { gap: 20px; }
  .shortplay__rail .shortplay__actbtn {
    width: auto;
    height: auto;
    background: none;
    border: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .shortplay__rail .shortplay__actbtn .shortplay__ico { width: 28px; height: 28px; }
  .shortplay__rail .shortplay__actlabel { font-size: 12px; }
  /* 更多鈕照稿轉直式 ⋮（icon 檔是橫式 ⋯，轉 90 度） */
  .shortplay__rail [data-short-more-btn] .shortplay__ico { transform: rotate(90deg); }
  .shortplay__rail, .shortplay__foot::before, .shortplay__title, .shortplay__progress { transition: opacity 0.25s ease; }

  /* 底部片名＋集數 pill：常駐；右側讓出裸 rail 直欄、下方讓出進度條 */
  .shortplay__foot { padding: 48px 64px 32px 16px; }
  .shortplay__foot::before, .shortplay__title { opacity: 1; }
  .shortplay__eppill { visibility: visible; transform: none; }

  /* 中央鍵：播放中即收起（0725 Dolly，同 TikTok——tap 舞台即暫停，暫停態才出播放鍵）。
     沿用桌機 base 規則 .is-playing .shortplay__pp{opacity:0}，此處不另開覆寫。 */

  /* 進度條：貼舞台底、紅填充＋圓點（demo 靜態） */
  .shortplay__progress {
    display: block;
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: max(10px, env(safe-area-inset-bottom));
    height: 3px;
    border-radius: 2px;
    background: rgba(255, 255, 255, 0.28);
    z-index: 6;
  }
  .shortplay__progress-fill {
    position: absolute;
    inset: 0 auto 0 0;
    width: var(--p, 38%);
    border-radius: 2px;
    background: var(--error-500);
  }
  .shortplay__progress-dot {
    position: absolute;
    right: -5px;
    top: 50%;
    width: 11px;
    height: 11px;
    transform: translateY(-50%);
    border-radius: 50%;
    background: var(--error-500);
  }

  /* 2 秒閒置（.shortplay.is-idle，ds.js 計時）：僅留影片——功能鍵、片名/集數
     詳情、進度條全收（Dolly 0725 拍板：閒置=純影片，進度條也不留） */
  .shortplay.is-idle .shortplay__back,
  .shortplay.is-idle .shortplay__vol,
  .shortplay.is-idle .shortplay__eppill { visibility: hidden; }
  .shortplay.is-idle .shortplay__rail,
  .shortplay.is-idle .shortplay__foot::before,
  .shortplay.is-idle .shortplay__title,
  .shortplay.is-idle .shortplay__progress,
  .shortplay.is-idle .shortplay__pp { opacity: 0; pointer-events: none; }
  .shortplay__rail {
    position: absolute;
    right: 8px;
    bottom: 96px;
    top: auto;
    z-index: 5;
    height: auto;
    padding: 0;
  }
  .shortplay__railgrp + .shortplay__railgrp { display: none; }  /* 手機改上下滑切集（之後接） */
}

/* 播放器控制不畫焦點橘框（Dolly 0721）：全域 :focus-visible 品牌環（01-base-preamble）
   在點按/聚焦控制時會亮橘框，媒體控制不需要 → 沿用 .avatar 的無框慣例關掉。
   保留鍵盤可操作，只是不畫框。 */
.shortplay__cluster :is(button, a):focus,
.shortplay__cluster :is(button, a):focus-visible { outline: none; }

/* ── 全螢幕（Dolly 0722）──
   整個 .shortplay 送全螢幕（模糊底＋cluster＋動作列＋劇集面板都在其內故一起帶進去）。
   原生 requestFullscreen 走 .is-fs；被拒／無手勢 fallback 走 .is-fs-fallback（fixed 滿版，
   可被 headless 驗）。全螢幕時站台 header/側欄不在 .shortplay 內故自然退場、影片吃滿高度。 */
.shortplay.is-fs { height: 100dvh; }
.shortplay.is-fs-fallback {
  position: fixed;
  inset: 0;
  z-index: 200;
  height: 100dvh;
}
body:has(.shortplay.is-fs-fallback) { overflow: hidden; }
/* 全螢幕拿掉 header 高度扣除與 1300 上限，直式影片填滿可用高度（上下留 24px 呼吸） */
.shortplay.is-fs .shortplay__cluster,
.shortplay.is-fs-fallback .shortplay__cluster {
  height: calc(100dvh - 48px);
}
/* 全螢幕鈕 icon：預設顯示 fullscreen、全螢幕中換成 exit */
.shortplay__fsic--exit { display: none; }
.shortplay.is-fs .shortplay__fsic--fs,
.shortplay.is-fs-fallback .shortplay__fsic--fs { display: none; }
.shortplay.is-fs .shortplay__fsic--exit,
.shortplay.is-fs-fallback .shortplay__fsic--exit { display: inline-block; }

/* ── 留言面板（Dolly 0722；點右側動作列留言鈕開）──
   結構＝1.0 留言、皮膚＝2.0 .comment-col（沿用 ds.js title-comments 行為：排序／回覆串／
   正在回覆）。版型：head 頂／comment-list 捲動／composer 固定底（同 title.html 手機留言面板）。
   與劇集面板互斥（JS 控），並列在 cluster 右側。 */
/* 卡片容器（1.0 有明確面板包住；用 raised surface＋邊框＋圓角，和舞台同 16px 角） */
.shortplay__comments {
  width: 400px;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  padding: 20px;
}
.shortplay__comments[hidden] { display: none; }
.shortplay__comments .comment-col {
  --like-color: #b63a3a;                 /* 沿用 title.html：liked heart（尚無全域 token） */
  display: flex;
  flex-direction: column;
  min-height: 0;
  height: 100%;
}
.shortplay__comments .detail-col__head { flex: 0 0 auto; margin-bottom: 16px; }
.shortplay__comments .comment-list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  padding-right: 8px;                    /* 捲軸不貼邊 */
  margin-right: -8px;                    /* 捲軸貼卡片內緣，不佔內容寬 */
}
.shortplay__comments .comment-composer {
  flex: 0 0 auto;
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid var(--border-subtle);
  /* 分隔線滿版 fill container（0727 Dolly）：負 margin 抵銷面板左右 16px padding、
     內距補回，線貼到面板兩緣 */
  margin-left: -16px;
  margin-right: -16px;
  padding-left: 16px;
  padding-right: 16px;
}
/* 關閉 X — same canonical modal close as .shortpanel__close (.ds-modal__close spec) */
.shortcomments__close {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  background: var(--glass-bg-default);
  border: none;
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 150ms var(--ease-out), color 150ms var(--ease-out);
}
.shortcomments__close:hover { background: var(--glass-bg-hover); color: var(--text-primary); }
.shortcomments__close .shortplay__ico { width: 16px; height: 16px; }

/* ≤1024：留言面板轉滿版邊緣抽屜——覆蓋上面卡片的四邊框/圓角/內距
   （卡片 base 規則在本檔較後，需靠這個位在最尾端的 @media 才蓋得掉；
   position:fixed/top/bottom/right/z-index 由前面 RWD block 提供）。 */
@media (max-width: 1024px) {
  .shortplay__comments {
    width: min(400px, 92vw);
    background: rgba(10, 10, 10, 0.92);
    border: 0;
    border-left: 1px solid var(--border-subtle);
    border-radius: 0;
    padding: 20px 16px 0;
  }
}
/* ≤768（Figma Short mobile 0725）：集數/留言面板＝底部 sheet（由下往上展開）——
   頂部圓角＋抓柄、上緣留影片透出；蓋上面 ≤1024 的 92vw 右掛抽屜
   （同權重靠順序，必須留在該塊之後）。開合動畫＝ds.js WAAPI（display 切換
   transition 不會跑，[[feedback_display_toggle_transition_waapi]]）。 */
@media (max-width: 768px) {
  .shortplay__panel,
  .shortplay__comments {
    top: auto;
    bottom: 0;
    left: 0;
    right: 0;
    width: 100vw;
    max-height: calc(100dvh - 64px);
    border-left: none;
    border-radius: 20px 20px 0 0;
    padding-top: 36px;
    padding-bottom: max(16px, env(safe-area-inset-bottom, 0px));
    box-shadow: 0 -16px 48px rgba(0, 0, 0, 0.5);
  }
  /* 抓柄 */
  .shortplay__panel::before,
  .shortplay__comments::before {
    content: '';
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    width: 48px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.24);
  }
  /* 留言 sheet 高一點（清單型內容），集數面板依內容自適應 */
  .shortplay__comments { height: calc(100dvh - 64px); }
  /* 集數格照稿 7 欄 */
  .shortplay__panel .shortpanel__grid { grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
}

/* ── 分享彈窗（Dolly 0722；.ds-modal primitive 提供外框/scrim/✕/內距/標題）──
   六社群列（白 glyph <img> 直出）＋複製連結 pill。可重用（其他頁要分享也套同 id 內容）。 */
/* 標題比預設 modal 小一階（0722 Dolly）；媒體分享彈窗不需 h3 大標 */
.share-modal .ds-modal__title { font-size: var(--fs-h4, 20px); margin: 0; }
/* 標題與 ✕ 同一排（0722 Dolly）：包成 flex header，close 退出絕對定位進列 */
.share-modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.share-modal .ds-modal__close { position: static; flex: 0 0 auto; }
/* 分享彈窗內互動元件不畫 focus 橘框（0722 Dolly；ds-modal 開窗會 autofocus ✕→亮框）。
   沿用媒體控制無框慣例，只 scope 本彈窗、不動全域 a11y。 */
.share-modal :is(button, a):focus,
.share-modal :is(button, a):focus-visible { outline: none; }
.share-modal__apps {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 24px;
}
.share-modal__app {
  display: inline-flex;
  width: 32px;
  height: 32px;
  align-items: center;
  justify-content: center;
  transition: opacity 0.15s ease, transform 0.15s ease;
}
.share-modal__app img { width: 100%; height: 100%; display: block; }
.share-modal__app:hover { opacity: 0.82; transform: translateY(-1px); }
.share-modal__label {
  margin: 28px 0 8px;
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.share-modal__link {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  border: 1px solid var(--border-subtle);
}
.share-modal__link-ico { flex: 0 0 auto; width: 20px; height: 20px; color: var(--text-tertiary); }
.share-modal__link-ico svg { width: 100%; height: 100%; display: block; }
.share-modal__link-url {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  color: var(--text-secondary);
  font-size: var(--fs-body-sm);
}
.share-modal__copy {
  flex: 0 0 auto;
  border: none;
  background: none;
  padding: 0 0 0 8px;
  color: var(--brand-500);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  cursor: pointer;
}
.share-modal__copy:hover { opacity: 0.85; }
.share-modal__copy.is-copied { color: var(--success-500); }

/* 手機（Figma Short mobile 0725）：分享彈窗改底部 sheet——貼底滿寬、頂圓角＋抓柄、
   無 ✕（點 scrim／Esc 關，ds-modal primitive 內建）。全站分享共用此樣式（單一來源）。 */
@media (max-width: 768px) {
  .share-modal.ds-modal { align-items: flex-end; padding: 0; }
  /* 0,4,0：ds.css 後載的 .ds-modal[data-size=sm] panel max-width:400（0,3,0）
     會反蓋 max-width:none → >400px 視窗（iPhone Pro/Max 430）sheet 兩側縫（Dolly 0727 真機抓）*/
  .share-modal.ds-modal[data-size] .ds-modal__panel {
    width: 100vw;
    max-width: none;
    border: 0;
    border-radius: 20px 20px 0 0;
    padding-top: 36px;
    padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px));
  }
  .share-modal .ds-modal__panel::before {
    content: '';
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    width: 48px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.24);
  }
  .share-modal .ds-modal__close { display: none; }
}

/* ══ 確認租借彈窗（短劇；沿用 .ds-modal.rent-confirm 外框＋.rent-confirm__* 影片卡）══ */
.short-rent__mode {
  margin: 0 0 12px;
  font-size: var(--fs-body-sm);
  color: var(--brand-500);
}
.short-rent__mode strong { color: var(--brand-500); font-weight: var(--fw-regular); }
/* 影片卡「第 N 集」用白色（區別於灰色時長）；scope .short-rent 抬權重蓋 48-rent-modal 基礎灰 */
.short-rent .rent-confirm__filmmeta--ep { color: var(--text-primary); }
/* 自動解鎖下一集（check 圈＋標題同排置中、說明置中在下） */
.short-rent__auto {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  width: 100%;
  margin: 20px 0 16px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-primary);
  text-align: center;
}
.short-rent__auto-head { display: inline-flex; align-items: center; gap: 8px; }
.short-rent__auto-check { width: 22px; height: 22px; color: var(--text-tertiary); }
.short-rent__auto-check svg { width: 100%; height: 100%; display: block; }
.short-rent__auto-tick { opacity: 1; }   /* 勾恆顯（未勾＝灰勾，勾選＝橘勾） */
.short-rent__auto[aria-pressed="true"] .short-rent__auto-check { color: var(--brand-500); }
.short-rent__auto-title { font-size: var(--fs-body); font-weight: var(--fw-bold); }
.short-rent__auto-desc { font-size: var(--fs-caption); color: var(--text-tertiary); line-height: 1.6; }
/* 方案一：單集購買（白色 ghost pill；padding 配置對齊影片頁購買鈕 .btn--lg 16/36） */
.short-rent__opt {
  flex-direction: column;
  gap: 6px;
  width: 100%;
  height: auto;
  margin-bottom: 12px;
  padding: 16px 36px;
  border-radius: var(--radius-pill);
}
.short-rent__opt-price { display: inline-flex; align-items: center; gap: 6px; font-size: var(--fs-h4, 20px); font-weight: var(--fw-bold); }
.short-rent__opt-label { font-size: var(--fs-caption); color: var(--text-secondary); }
/* 方案二：全劇集／一次解鎖剩餘（沿用 .btn--yellow-ghost.detail-price 金漸層兩行版型） */
.short-rent__full { width: 100%; border-radius: var(--radius-pill); }
/* ⚠️ ds.css `.ds-modal .btn{height:50px;padding-block:0}`（0,2,0）會蓋高度；
   用 .short-rent.rent-modal（0,3,0）抬權重，對齊影片頁購買鈕＝58px 固定高、
   內容置中、行高收緊（line-height:1，單集鈕原本沒收緊才偏高）。 */
.short-rent.rent-modal .short-rent__opt,
.short-rent.rent-modal .short-rent__full {
  height: 58px;
  padding: 0 36px;
  gap: 6px;
  line-height: 1;
}

/* 面板購買鈕「已購買」狀態（已買全劇）：只留文字、不可再點 */
.shortpanel__buy.is-owned { pointer-events: none; opacity: 0.7; }
.shortpanel__buy.is-owned .shortpanel__buy-orig,
.shortpanel__buy.is-owned .detail-price__coin,
.shortpanel__buy.is-owned .detail-price__label { display: none; }
.shortpanel__buy.is-owned .detail-price__amount { font-size: var(--fs-body); }
.short-rent__orig {
  font-size: var(--fs-caption);
  font-weight: var(--fw-regular);
  color: var(--text-tertiary);
  text-decoration: line-through;
}
.short-rent__orig[hidden] { display: none; }

/* 爆米花不足（精簡版） */
.short-topup__cta { width: 100%; margin-top: 20px; justify-content: center; }
.short-topup__later {
  display: block;
  width: 100%;
  margin-top: 12px;
  padding: 8px;
  border: none;
  background: none;
  color: var(--text-tertiary);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.short-topup__later:hover { color: var(--text-secondary); }

/* ══ ⋮ 選單（回報＋解鎖設定：自動/手動；錨在右側動作列 ⋮ 鈕左側）══ */
.shortplay__action--more { position: relative; }
.shortmenu {
  position: absolute;
  left: calc(100% + 12px);         /* 統一往右側彈出（與上/下一集 tooltip 同方向） */
  right: auto;
  bottom: 0;
  min-width: 184px;
  padding: 6px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.5);
  z-index: 20;
}
.shortmenu[hidden] { display: none; }
.shortmenu__item {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 12px;
  border: none;
  background: none;
  border-radius: var(--radius-md);
  color: var(--text-primary);
  font-size: var(--fs-body);
  text-align: left;
  cursor: pointer;
}
.shortmenu__item:hover { background: var(--white-alpha-8); }
.shortmenu__item--parent { justify-content: space-between; }
.shortmenu__chev {
  width: 16px; height: 16px; flex: 0 0 auto;
  background: currentColor;
  color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
  mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
  transform: rotate(-90deg);              /* 展開＝朝上 */
}
.shortmenu__item--parent[aria-expanded="false"] .shortmenu__chev { transform: rotate(90deg); }
.shortmenu__sub[hidden] { display: none; }
/* 子選單層級：整段坐在較亮的底塊上（滿版）＋子項字級縮小，跟主項分層（取代導引線） */
.shortmenu__sub {
  margin: 4px -6px 0;               /* 抵銷 menu 6px padding → 底塊左右滿版 */
  padding: 4px 6px;
  background: var(--white-alpha-4);
}
.shortmenu__subitem {
  justify-content: space-between;
  padding-left: 12px;
  font-size: var(--fs-body-sm);     /* 子層比主項小一級（主 16 / 子 14） */
  color: var(--text-secondary);
}
.shortmenu__subitem.is-on { color: var(--brand-500); }
.shortmenu__tick { width: 16px; height: 16px; flex: 0 0 auto; opacity: 0; color: var(--brand-500); }
.shortmenu__tick svg { width: 100%; height: 100%; display: block; }
.shortmenu__subitem.is-on .shortmenu__tick { opacity: 1; }
/* 手機（Figma Short mobile 0725）：⋮ 選單改底部 sheet（回報／解鎖設定），
   頂圓角＋抓柄；桌機仍是錨在 ⋮ 旁的浮層 */
@media (max-width: 768px) {
  .shortmenu {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    min-width: 0;
    padding: 32px 12px calc(12px + env(safe-area-inset-bottom, 0px));
    border: 0;
    border-radius: 20px 20px 0 0;
    box-shadow: 0 -16px 48px rgba(0, 0, 0, 0.5);
    z-index: 60;
  }
  .shortmenu::before {
    content: '';
    position: absolute;
    top: 10px;
    left: 50%;
    transform: translateX(-50%);
    width: 48px;
    height: 4px;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.24);
  }
  .shortmenu__item { min-height: 52px; }
}

/* ══ 回報問題選擇（#shortReport，Figma Short mobile More_Report 0725）══
   ⋮ 選單「回報」→ 單選問題 → 下一步（選了才啟用）→ demo 送出＝toast。
   桌機手機同為置中卡（照稿）。 */
/* 資訊型彈窗慣例：開窗 autofocus ✕ 不畫橘框（同送禮/分享彈窗） */
.sreport :is(button):focus-visible { outline: none; }
.sreport__opts { margin-top: 20px; }
.sreport__opt {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  width: 100%;
  padding: 18px 4px;
  border: none;
  border-bottom: 1px solid var(--white-alpha-8);
  background: none;
  text-align: left;
  cursor: pointer;
}
.sreport__opt:last-child { border-bottom: none; }
.sreport__check {
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  margin-top: 2px;
  border-radius: 50%;
  border: 1.5px solid var(--white-alpha-24, rgba(255, 255, 255, 0.24));
  color: var(--text-tertiary);
  transition: border-color 150ms var(--ease-out), color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.sreport__check svg { width: 13px; height: 13px; display: block; }
.sreport__opt.is-on .sreport__check {
  border-color: var(--brand-500);
  color: var(--brand-500);
  background: rgba(255, 163, 63, 0.12);
}
.sreport__name {
  display: block;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.sreport__desc {
  display: block;
  margin-top: 4px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.6;
  color: var(--text-tertiary);
}
.sreport__next { width: 100%; margin-top: 20px; }
.sreport__next:disabled { opacity: 0.4; cursor: default; }

/* 未登入：短劇頁不顯示右上爆米花餘額（after-login header 由 body[data-auth] 控，已內建） */

/* ══ 看完引導（喜好→評分→影評分潤）覆蓋層 ══
   沿用 title.html 的 .player__review 2.0 元件（問句/★/按鈕全域樣式）。短劇版＝
   直式舞台上置中、無海報、still 模糊變暗，並暫時藏起播放/音量/全螢幕控制。
   觸發＝dev 面板「播完短劇」；行為＝ds.js short-review register。 */
.shortplay__stage .player__review {
  position: absolute;
  inset: 0;
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background: rgba(0, 0, 0, 0.55);
}
.shortplay__stage .player__review[hidden] { display: none; }
.shortplay__review .player__review-poster { display: none; }        /* 短劇版無海報 */
.shortplay__review .player__review-main { text-align: center; max-width: 460px; }
.shortplay__review .player__review-actions,
.shortplay__review .player__review-rating,
.shortplay__review .player__review-links { justify-content: center; }
/* 引導出現時：still 退到背景（模糊變暗）、控制暫時收起，避免干擾 */
.shortplay__stage:has(.player__review:not([hidden])) .shortplay__still {
  filter: blur(24px) brightness(0.4);
  transform: scale(1.1);
}
.shortplay__stage:has(.player__review:not([hidden])) .shortplay__pp,
.shortplay__stage:has(.player__review:not([hidden])) .shortplay__stagetop,
.shortplay__stage:has(.player__review:not([hidden])) .shortplay__vol,
.shortplay__stage:has(.player__review:not([hidden])) .shortplay__foot { opacity: 0; pointer-events: none; }

/* 切換下一部短劇：still 短暫淡出→換圖→淡入（ds.js nextShort 加/removed .is-switching） */
.shortplay__still { transition: opacity 200ms ease, filter 200ms ease, transform 200ms ease; }
.shortplay__stage.is-switching .shortplay__still { opacity: 0; }

/* ══════════════════════════════════════════════════════════════
   42 · 片庫 hover 展開卡（.filmpop）
   2026-07-16 Dolly（1.0 截圖規格）：hover 海報 → 浮出放大資訊卡。
   有預告片＝上下佈局（16:9 影片靜音自動播）；無＝海報居左橫式佈局
   （.filmpop--side；設計註記：若無預告片，則顯示海報圖片）。
   單例 portal 掛 body（rail overflow 會裁），定位由 ds.js film-hover 供給。
   桌機（hover:hover + pointer:fine）限定。
   ══════════════════════════════════════════════════════════════ */

.filmpop {
  position: fixed;
  z-index: 340;                      /* rail 卡之上、選單彈窗之下 */
  width: 300px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  /* 玻璃底（1.0 Library v4 hover 卡規格換 2.0 色系）：
     1.0＝#2b2829 @88%＋blur12 → 2.0＝bg-secondary @88%＋blur12，
     色用 color-mix 吃 token 不寫死 */
  background: color-mix(in srgb, var(--bg-secondary, #131313) 88%, transparent);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  /* 陰影加厚（2026-07-16 Dolly）：近距離定義邊界＋遠距離大範圍沉底 */
  box-shadow:
    0 4px 12px rgba(0, 0, 0, 0.45),
    0 16px 40px rgba(0, 0, 0, 0.60),
    0 48px 110px rgba(0, 0, 0, 0.80);
  /* 進場＝從海報那格「長成」大卡（2026-07-21 Dolly：原本大卡憑空冒出，感覺抖一下突然變大）。
     FLIP：ds.js show() 依海報 rect 算起點 transform（縮回海報大小/位置）＋ transform-origin，
     rAF 後放大到 scale(1)。毛玻璃卡 opacity 恆 1（用 opacity 淡入會閃白，見
     feedback_backdrop_filter_opacity_flash）→ 全靠 transform 帶動、visibility 控顯隱。 */
  opacity: 1;
  visibility: hidden;
  transition: visibility 0s linear 300ms;   /* transform 由 ds.js WAAPI 帶（FLIP 展開） */
  pointer-events: none;
  will-change: transform;
}
.filmpop.is-open {
  visibility: visible;
  transition: visibility 0s linear 0s;
  pointer-events: auto;
}
.filmpop[hidden] { display: none; }

.filmpop__inner {
  display: flex;
  flex-direction: column;
  text-decoration: none;
  color: inherit;
}
.filmpop--side { width: 460px; }
.filmpop--side .filmpop__inner { flex-direction: row; }

/* ── 媒體區：預告片 16:9／海報 2:3 ── */
.filmpop__media {
  position: relative;
  flex: none;
  aspect-ratio: 16 / 9;
  background: var(--bg-tertiary);
}
/* 海報版：海報內縮留邊（上/下/左 padding，2026-07-16 Dolly）＋自帶圓角 */
.filmpop--side .filmpop__media {
  aspect-ratio: 2 / 3;
  width: 186px;
  margin: 14px 0 14px 14px;
  border-radius: var(--radius-md);
  overflow: hidden;
}
.filmpop__video,
.filmpop__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.filmpop__price {
  position: absolute;
  right: 12px;
  bottom: 12px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(8px);
  color: var(--text-primary);
  font-size: var(--fs-body-sm);
  font-weight: 600;
}
.filmpop__price[hidden] { display: none; }
.filmpop__price-ico {
  display: inline-block;
  width: 14px;
  height: 14px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
  mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
}

/* ── 資訊區 ── */
.filmpop__body {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px 14px;
  min-width: 0;
}
.filmpop__titlerow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.filmpop__title {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  color: var(--text-primary);
  min-width: 0;
}
.filmpop__titlerow .ztor-score { flex: none; }
.filmpop__meta {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--text-secondary);
  font-size: var(--fs-body-sm);
}
.filmpop__meta-sep {
  width: 1px;
  height: 11px;
  background: var(--border-subtle);
}
.filmpop__chips { display: flex; flex-wrap: wrap; gap: 6px; }
.filmpop__chip {
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);
  color: var(--text-secondary);
  font-size: 12px;
}
.filmpop__desc {
  margin: 0;
  font-size: var(--fs-body-sm);
  line-height: 1.75;
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* 海報版：資訊欄與海報同高（上下對齊 14px 內縮），動作列推到底＝與海報置底 */
.filmpop--side .filmpop__body { padding: 14px 18px 14px 16px; }
.filmpop--side .filmpop__stats { margin-top: auto; }

/* 動作列：讚/留言/分享＋右端收藏 */
.filmpop__stats {
  display: flex;
  align-items: center;
  gap: 18px;
  margin-top: 2px;
  color: var(--text-secondary);
  font-size: var(--fs-body-sm);
}
.filmpop__stat {
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.filmpop__stat--bookmark { margin-left: auto; }
.filmpop__stat-ico {
  display: inline-block;
  width: 18px;
  height: 18px;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
  mask: var(--icon) center / contain no-repeat;
}

/* ── 43-bottom-nav.css — mobile floating pill navigation ──────────────────
   A detached, centered, safe-area-aware pill shown only on phones (<768).
   At rest: icon + label. On scroll-down it condenses to icons-only
   (mobile-nav.js toggles .is-condensed); re-expands on scroll-up / at top.

   Five primary destinations. The Community cell becomes "Create" on the
   Community page (JS adds .botnav__item--create + swaps icon/label/action).
   Tablet (icon-rail) and desktop (side-rail) hide this entirely.
   Owns its own responsive behaviour — no page-level overrides. */

.botnav { display: none; }

/* HELD FOR PHASE 2 — inert-migration note (framework-migration foundation):
   the original body-level reserve-space rule was unconditional —
   `body { padding-bottom: 104px }` at <768px — which would add blank bottom
   padding under EVERY existing page the moment it's viewed at phone width,
   whether or not the .botnav pill markup is actually on that page. That's a
   real, immediate visual change to shipped pages, so it's commented out here
   rather than migrated. Re-enable once .botnav is wired onto specific pages
   (scope it there, e.g. `body:has(.botnav) { padding-bottom: … }`, so it only
   reserves space where the pill actually renders).
@media (max-width: 767.98px) {
  body { padding-bottom: calc(104px + env(safe-area-inset-bottom, 0px)); }
}
*/

@media (max-width: 767.98px) {
  .botnav {
    display: block;
    position: fixed;
    left: 50%;
    transform: translateX(-50%);
    bottom: calc(12px + env(safe-area-inset-bottom, 0px));
    z-index: 1000;
    width: min(92vw, 390px);         /* equal columns → middle cell (the +) is dead-centre */
    pointer-events: none;            /* outer wrapper is click-through */
    transition: width 260ms cubic-bezier(0.23, 1, 0.32, 1);
  }
  .botnav.is-condensed { width: min(74vw, 330px); }   /* narrows on scroll-down */

  .botnav__list {
    position: relative;                 /* for the specular sheen ::before */
    pointer-events: auto;
    display: flex;
    align-items: stretch;
    gap: 0;
    margin: 0;
    width: 100%;
    padding: 4px;
    list-style: none;
    /* Liquid Glass — translucent dark glass (legible) with a FAINT cool→warm
       hue. The fill is kept LOW-opacity so the real backdrop blur is visible
       through it (content behind the pill actually frosts). */
    isolation: isolate;
    /* Liquid-glass MATERIAL pulled from shared tokens (single source of truth —
       same frost as the stuck filter shelf). Pill geometry stays local below. */
    background: var(--glass-fill);
    /* Real frosted-glass blur — works in EVERY engine. (An earlier
       url(#svg-filter) override silently disabled this in stable Chrome; the
       refraction is now layered separately so it can never kill the blur.) */
    -webkit-backdrop-filter: var(--glass-backdrop);
            backdrop-filter: var(--glass-backdrop);
    border: var(--glass-border);
    border-radius: var(--radius-pill, 999px);
    box-shadow: var(--glass-shadow);   /* shared depth token (= the stuck shelf) */
    transition: padding 220ms cubic-bezier(0.23, 1, 0.32, 1);
  }
  /* Specular highlight sheen across the top — the glass shine (cool-tinted). */
  .botnav__list::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    background: var(--glass-sheen);   /* shared sheen token (= the shelf's top cap) */
  }
  /* Iridescent rim — a thin spectral gradient ring that follows the pill
     radius (gradient-border mask trick). The dispersion edge that makes web
     glass read as glass. */
  .botnav__list::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;                                    /* ring thickness */
    background: var(--glass-rim);                    /* shared spectral-ring token */
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;                 /* punch the centre → ring only */
    opacity: 0.30;                                   /* subtle spectral edge */
    pointer-events: none;
  }

  .botnav__cell { display: flex; flex: 1 1 0; min-width: 0; }   /* 5 equal columns → + dead-centre */

  .botnav__item {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 2px;
    min-width: 0;
    min-height: 45px;
    padding: 5px 2px;
    border-radius: 16px;
    color: var(--text-secondary, rgba(255, 255, 255, 0.58));
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    transition: color 160ms ease, padding 200ms ease, gap 200ms ease, min-height 200ms ease;
  }
  .botnav__item:active { transform: scale(0.94); }

  .botnav__icon {
    width: 20px; height: 20px;
    flex: none;
    background: currentColor;
    -webkit-mask: var(--i) center / contain no-repeat;
            mask: var(--i) center / contain no-repeat;
    transition: width 200ms ease, height 200ms ease;
  }

  /* Label shown at rest; collapses smoothly when the bar condenses on scroll. */
  .botnav__label {
    font-family: var(--font-cjk-text);
    font-size: 9px;
    line-height: 1.35;               /* room for descenders (g/p/y) */
    font-weight: var(--fw-medium, 500);
    letter-spacing: 0.01em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    opacity: 1;
    max-height: 16px;
    transition: opacity 150ms ease, max-height 200ms ease;
  }

  /* Active destination — accent colour only (no highlight background). */
  .botnav__item.is-active,
  .botnav__item[aria-current="page"] { color: var(--accent-500, var(--yellow-500, #ffa33f)); }
  .botnav__item--create { color: var(--accent-500, var(--yellow-500, #ffa33f)); }

  /* Condensed (scroll-down): drop the labels → icon-only + shorter. */
  .botnav.is-condensed .botnav__list { padding: 3px; }
  .botnav.is-condensed .botnav__item { gap: 0; min-height: 35px; padding: 6px 2px; }
  .botnav.is-condensed .botnav__icon { width: 19px; height: 19px; }
  .botnav.is-condensed .botnav__label { opacity: 0; max-height: 0; pointer-events: none; }

  @media (max-width: 360px) {
    .botnav__label { font-size: 8.5px; }
  }

  @media (prefers-reduced-motion: reduce) {
    .botnav__list, .botnav__item, .botnav__icon, .botnav__label { transition: none; }
    .botnav__item:active { transform: none; }
  }
}

/* Short landscape phones: hug the bottom a little tighter */
@media (max-width: 926px) and (orientation: landscape) and (max-height: 460px) {
  .botnav { bottom: calc(8px + env(safe-area-inset-bottom, 0px)); }
  .botnav.is-condensed .botnav__item { padding: 7px 12px; }
}

/* ── 44-mobile-drawer.css — hamburger menu + mobile/tablet header chrome ───
   The drawer holds every global destination NOT in the 5-item bottom nav
   (Library, News, Events) plus account/utility links and language. Opens
   from a burger in the compact mobile header. Focus-trapped, Esc/scrim/close
   dismissible, background scroll-locked (mobile-nav.js drives behaviour).
   Phone (<768) only — tablet/desktop use the rail + full header. */

/* Burger button — only on phone */
.header__burger {
  display: none;
  width: 40px; height: 40px;
  align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer;
  color: var(--text-primary, #fff);
  border-radius: var(--radius-md, 12px);
  -webkit-tap-highlight-color: transparent;
}
.header__burger-icon { position: relative; }
.header__burger-icon, .header__burger-icon::before, .header__burger-icon::after {
  content: ''; display: block;
  width: 20px; height: 2px; border-radius: 2px;
  background: currentColor;
  /* Back-out curve = responsive start + a little overshoot at the end (the
     "cool" snap). Vertical move uses translateY (not top) so one transform
     transition handles move + rotate together — no two-step jump. */
  transition: transform 300ms cubic-bezier(0.34, 1.56, 0.64, 1),
              background-color 160ms ease;
}
.header__burger-icon::before { position: absolute; left: 0; top: 0; transform: translateY(-6px); }
.header__burger-icon::after  { position: absolute; left: 0; top: 0; transform: translateY(6px); }

/* Burger → X: the bars spin 180° as they collapse into the X (parent rotates,
   the two bars cross). Springy overshoot makes it feel alive. */
.header__burger:active { transform: scale(0.88); transition: transform 120ms ease-out; }
.header__burger.is-open .header__burger-icon { background: transparent; transform: rotate(180deg); }
.header__burger.is-open .header__burger-icon::before { transform: translateY(0) rotate(45deg); }
.header__burger.is-open .header__burger-icon::after  { transform: translateY(0) rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
  .header__burger-icon, .header__burger-icon::before, .header__burger-icon::after {
    transition: transform 140ms ease, background-color 120ms ease;
  }
  .header__burger.is-open .header__burger-icon { transform: none; }   /* X without the spin */
  .header__burger:active { transform: none; }
}

/* ── Drawer ── */
.mdrawer { position: fixed; inset: 0; z-index: 1200; display: none; }
.mdrawer.is-open { display: block; }
.mdrawer__scrim {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0; transition: opacity 200ms ease;   /* scrim leads the 260ms panel — world dims, then sheet lands */
}
.mdrawer.is-open .mdrawer__scrim { opacity: 1; }
.mdrawer__panel {
  position: absolute; top: 0; left: 0; bottom: 0;     /* opens from the LEFT — same side as the burger */
  width: min(76vw, 360px);
  display: flex; flex-direction: column;
  background: var(--bg-elevated, #141416);
  border-right: 1px solid var(--border-subtle, rgba(255,255,255,0.1));
  box-shadow: 16px 0 48px rgba(0,0,0,0.5);
  padding: calc(env(safe-area-inset-top, 0px) + 12px) 20px calc(env(safe-area-inset-bottom, 0px) + 20px);
  transform: translateX(-100%);
  transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1);   /* close: quicker exit (user decided to leave) */
  overflow-y: auto; overscroll-behavior: contain;
}
.mdrawer.is-open .mdrawer__panel {
  transform: translateX(0);
  transition-duration: 260ms;                                   /* open: calm settle */
}

.mdrawer__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 8px; }
.mdrawer__logo img { height: 26px; display: block; }
.mdrawer__close {
  width: 40px; height: 40px; display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; cursor: pointer; color: var(--text-secondary, #aaa);
  font-size: 22px; line-height: 1; border-radius: var(--radius-md, 12px);
}
.mdrawer__close:hover { color: var(--text-primary, #fff); }

.mdrawer__group { padding: 14px 0; }
.mdrawer__group + .mdrawer__group { border-top: 1px solid var(--border-subtle, rgba(255,255,255,0.08)); }
.mdrawer__label {
  font-family: var(--font-cjk-text); font-size: 11px; font-weight: var(--fw-semibold, 600);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--text-tertiary, rgba(255,255,255,0.45)); margin: 0 0 6px;
}
.mdrawer__link {
  display: flex; align-items: center; gap: 10px;
  width: 100%; text-align: left;
  min-height: 48px; padding: 6px 8px; margin: 0 -8px;
  background: transparent; border: 0; cursor: pointer; text-decoration: none;
  font-family: var(--font-cjk-display); font-size: 16px; font-weight: var(--fw-medium, 500);
  color: var(--text-primary, #fff); border-radius: var(--radius-md, 12px);
  transition: background-color 120ms ease, transform 120ms cubic-bezier(0.23, 1, 0.32, 1);
}
.mdrawer__link:hover, .mdrawer__link:focus-visible { background: rgba(255,255,255,0.06); }
.mdrawer__link:active { transform: scale(0.985); background: rgba(255,255,255,0.06); }
.mdrawer__link[aria-current="page"], .mdrawer__link.is-active { color: var(--accent-500, var(--yellow-500, #ffa33f)); }
.mdrawer__foot { margin-top: auto; padding-top: 16px; }
.mdrawer__login { width: 100%; }

@media (prefers-reduced-motion: reduce) {
  .mdrawer__panel, .mdrawer__scrim, .mdrawer__link { transition: none; }
  .mdrawer__link:active { transform: none; }
}

/* HELD FOR PHASE 2 — inert-migration note (framework-migration foundation):
   the original block below unconditionally restyled the SHIPPING desktop
   header at <768px — .header__inner display:grid, and .header-search /
   .header__actions / .header-switch set to display:none. Those are real
   classes rendered on every existing page today, so this is an immediate
   visible change the moment any page is viewed under 768px (resize, tablet,
   devtools, or a viewport-bridge redirect gap) — not inert. Commented out
   rather than migrated; this is exactly 08-header.css's territory and needs
   an explicit, page-by-page Phase-2 evaluation (see task scope boundary),
   not a silent side-effect of this foundation drop.

@media (max-width: 767.98px) {
  .header,
  body.has-rail .header { margin-left: 0; }
  .header__inner {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--space-3);
    min-height: 56px;
    padding-inline: var(--space-4);
  }
  .header__burger {
    display: inline-flex; grid-column: 1; justify-self: start;
    justify-content: flex-start;
    width: 40px; height: 40px; padding: 0;
    margin: 0 0 0 -16px;
  }
  .header__nav    { grid-column: 2; justify-self: center; }
  .header__logo-img { height: 28px; }
  .header-search,
  .header__actions,
  .header-switch { display: none; }
  body.mdrawer-open { overflow: hidden; }
}
*/

/* ── Profile shortcut button (injected by mobile-nav.js into the header).
   Phone only — desktop has its own profile/account affordances. Sits in the
   header grid's right cell, mirroring the burger's 16px gutter on the left. ── */
.header__profile { display: none; }
@media (max-width: 767.98px) {
  .header__profile {
    display: inline-flex; grid-column: 3; justify-self: end;
    align-items: center; justify-content: flex-end;
    width: 40px; height: 40px; padding: 0; margin: 0 -16px 0 0;   /* icon hugs the 16px right gutter */
    color: var(--text-primary, #fff);
    border-radius: var(--radius-md, 12px);
    -webkit-tap-highlight-color: transparent;
    transition: transform 120ms ease-out, opacity 150ms ease;
  }
  .header__profile:active { transform: scale(0.9); }
  .header__profile-icon {
    width: 24px; height: 24px; flex: none;
    background: currentColor;
    -webkit-mask: url('assets/icons/nav/user.svg') center / contain no-repeat;
            mask: url('assets/icons/nav/user.svg') center / contain no-repeat;
  }
}

/* ── Drawer search field (injected by mobile-nav.js at the top of the panel).
   Mirrors the desktop persistent search; submits to the Library. ── */
.mdrawer__search {
  display: flex; align-items: center; gap: 10px;
  height: 44px; margin: 6px 0 10px; padding: 0 14px;
  border-radius: var(--radius-pill, 999px);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, 0.12));
  transition: border-color 150ms ease, background-color 150ms ease;
}
.mdrawer__search:focus-within {
  border-color: var(--yellow-500, #ffa33f);
  background: rgba(255, 255, 255, 0.09);
}
.mdrawer__search-icon {
  width: 18px; height: 18px; flex: none;
  background: var(--text-tertiary, rgba(255, 255, 255, 0.5));
  -webkit-mask: url('assets/icons/search.svg') center / contain no-repeat;
          mask: url('assets/icons/search.svg') center / contain no-repeat;
}
.mdrawer__search:focus-within .mdrawer__search-icon { background: var(--yellow-500, #ffa33f); }
.mdrawer__search-input {
  flex: 1; min-width: 0; border: 0; background: none; outline: none;
  color: var(--text-primary, #fff);
  font-family: var(--font-cjk-display); font-size: 15px;
}
.mdrawer__search-input::placeholder { color: var(--text-tertiary, rgba(255, 255, 255, 0.5)); }
.mdrawer__search-input::-webkit-search-cancel-button { display: none; }

/* ─────────────────────────────────────────
   MY LIBRARY (my-library.html) — 我的片庫
   1.0 框架（Figma Desktop 438:40139）換 2.0 皮：
   主 tab = .feed-tabs（等寬滑動底線）、狀態列 = .library-genres chips、
   租借卡 = .mylib-card（海報 96×144 + 到期倒數 + hover 動作列）。
   行為在 ds.js 'mylib' 模組（面板切換／狀態篩選／到期排序）。
   ───────────────────────────────────────── */
.section--mylib { padding-top: 24px; }

/* 入口分流：預設（片庫 toggle 進來）顯示三膠囊；profile menu 進來
   （?src=profile → html[data-mylib-entry="profile"]，head script 繪製前設好）
   改顯示「我的片庫」頁標題、藏 toggle。 */
.mylib-pagehead { display: none; }
html[data-mylib-entry="profile"] .mylib-pagehead { display: flex; }
html[data-mylib-entry="profile"] .section--mylib .library-topbar { display: none; }

/* ── 主 tab 列：feed-tabs 撐滿 container，坐在 hairline 上 ── */
.mylib-tabs-row {
  margin-top: 8px;
  border-bottom: 1px solid var(--white-alpha-8);
  /* 桌機吸頂（2026-07-15 Dolly「淡淡遮罩」追查收尾）：tabbar 頁的完整模式＝
     header 平面化（08-header :has(.mylib-tabs-row)）＋ tab 吸在 header 正下方
     （同 .community-tabbar top/z），tab 永不捲進 header 的玻璃/veil 底下。
     底色用實色 --bg-primary 非社群的 82% 玻璃——Dolly 0715 拍板此列不透底保視讀。
     手機層（my-library-mobile.css）覆寫 top/z 接 headroom，行為不變。 */
  position: sticky;
  top: var(--header-height);
  z-index: 20;
  background: var(--bg-primary);
}
/* 收到的禮物未兌換數 badge */
.mylib-tab-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  margin-left: 6px;
  border-radius: var(--radius-pill);
  background: var(--error-500);
  color: var(--neutral-white);
  font-family: var(--font-latin);
  font-size: 11px;
  font-weight: var(--fw-semibold);
  line-height: 1;
}
/* 數字光學置中 = Dolly 2026-07-13 用診斷頁（badge-test，4 字體×5 位移編號格）
   親眼選定「Proxima 600、translateY(-0.5px)」。不要用 headless 量測值改這裡：
   本機安裝的 Proxima Nova 與 Typekit 網頁版同名但垂直度量不同，headless 載到的
   是後者，量出來的補償對真機失準（本輪五次來回的根因）。 */
.mylib-tab-badge__n {
  display: block;
  transform: translateY(-0.5px);
}

/* 面板/卡片：display 由 class 自帶，[hidden] 必須贏（[hidden] 會被 display 蓋掉的坑） */
.mylib-panel[hidden] { display: none !important; }
.mylib-card[hidden]  { display: none !important; }
/* 取消收藏/喜愛 → 卡片淡出再藏（ds.js mylib 加 .is-removing、220ms 後設 hidden） */
.mylib-card.is-removing {
  opacity: 0;
  transform: scale(0.98);
  transition: opacity 200ms ease, transform 200ms ease;
  pointer-events: none;
}

/* ── 繼續觀賞：最多 5 張，五等分＋間距剛好填滿 container（右緣對齊格線，
   2026-07-13 Dolly 拍板）；不足 5 張靠左、卡寬不變。 ── */
.mylib-cw-rail {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
  padding: 16px 0 8px;
}

/* ── 影片卡（租借/禮物/繼續觀賞共用骨架）── */
.mylib-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  text-decoration: none;
  color: inherit;
}
/* 多集租借的堆疊層：卡頂兩片逐層內縮的「牌背」。
   spec：單集不出現 layer 但高度保留（opacity 0），單集/多集卡同高 176px。 */
.mylib-card__stack {
  display: none;
  flex-direction: column;
}
.mylib-card--stacked .mylib-card__stack { display: flex; }
.mylib-card:not(.mylib-card--stacked) { padding-top: 8px; }
.mylib-card__stack-layer {
  height: 4px;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
  background: var(--white-alpha-4);
}
.mylib-card__stack-layer:first-child { margin-inline: 16px; }
.mylib-card__stack-layer:last-child  { margin-inline: 8px; background: var(--white-alpha-8); }

.mylib-card__main {
  position: relative;
  display: flex;
  gap: 12px;
  flex: 1;
  padding: 12px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow: hidden;
  /* 自成 stacking context：負 z 的 __backdrop 才會畫在「本層底色之上、內容之下」。
     沒有這行時 backdrop 掉到外層 context、被 --bg-secondary 蓋死
     （繼續觀賞/禮物卡的海報模糊卡底整批隱形——2026-07-13 實錘）。 */
  isolation: isolate;
  transition: border-color 160ms ease, background-color 160ms ease;
}
.mylib-card:hover .mylib-card__main { border-color: var(--white-alpha-16); }

/* 繼續觀賞卡底：海報模糊放大 + 壓暗（文字直接落在上面） */
.mylib-card__backdrop {
  position: absolute;
  inset: -12px;
  /* 負 z：畫在父層背景之上、所有內容（含一般文字流）之下，
     內容就不必逐一墊 z-index（墊了會形成 stacking context，
     把動作鈕困在整卡 stretched link 底下——2026-07-13 實錘）。 */
  z-index: -1;
  pointer-events: none;
}
.mylib-card__backdrop img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: blur(12px);
}
.mylib-card__backdrop::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
}
/* 點擊範圍（spec）：整張卡＝資訊頁（stretched link 蓋滿卡面），
   海報＝播放頁、動作鈕各自獨立——疊在 link 之上；
   文字維持一般流（在 link 之下，點文字＝點整卡）。 */
.mylib-card__link {
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
}
.mylib-card__main > .mylib-card__poster,
.mylib-card__actions { position: relative; z-index: 3; }

/* 圖片不可用 → Ztor placeholder（ds.js mylib 模組在 img error 時注入） */
.mylib-card__ph {
  position: absolute;
  inset: 0;
  background: var(--neutral-800);
}
.mylib-card__ph::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 56%;
  height: 22%;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/ztor_logo.svg') center / contain no-repeat;
          mask: url('assets/icons/ztor_logo.svg') center / contain no-repeat;
}

/* 海報 96×144（2:3），rail 卡縮一號 */
.mylib-card__poster {
  position: relative;
  flex: 0 0 96px;
  width: 96px;
  height: 144px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--neutral-800);
}
.mylib-card--cw .mylib-card__poster {
  flex-basis: 88px;
  width: 88px;
  height: 132px;
}
.mylib-card__poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* 即將過期 tag：實色貼海報左上、跟著海報圓角裁（不自畫弧、不用玻璃） */
.mylib-card__tag {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  padding: 3px 8px;
  border-radius: 0 0 var(--radius-md) 0;
  background: var(--error-500);
  color: var(--neutral-white);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-bold);
  line-height: 18px;
}
/* 海報底進度條（鏡像 .film-card__progress） */
.mylib-card__pbar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 4px;
  background: rgba(255, 255, 255, 0.25);
  z-index: 2;
}
.mylib-card__pbar-fill {
  display: block;
  height: 100%;
  background: var(--yellow-500);
}

/* hover 海報右下浮出 play 鈕（Figma 438:52629：32px 玻璃圓＋白邊）
   純 CSS 兩層偽元素：::before=圓、::after=play icon，免動每張卡 markup */
.mylib-card__poster::before {
  content: "";
  position: absolute;
  right: 8px;
  bottom: 12px;
  z-index: 3;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--neutral-white);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.mylib-card__poster::after {
  content: "";
  position: absolute;
  right: 16px;
  bottom: 20px;
  z-index: 3;
  width: 16px;
  height: 16px;
  /* play 三角墨水偏左，往右推 1px 才視覺置中 */
  transform: translateX(1px);
  background-color: var(--neutral-white);
  -webkit-mask: url('assets/icons/media-play.svg') center / contain no-repeat;
          mask: url('assets/icons/media-play.svg') center / contain no-repeat;
}
/* 已看完的影片：hover 圓鈕換重播 icon（playback），圓形箭頭本身置中、不用 +1px 補償 */
.mylib-card[data-status="watched"] .mylib-card__poster::after {
  transform: none;
  -webkit-mask-image: url('assets/icons/media/playback.svg');
          mask-image: url('assets/icons/media/playback.svg');
}
@media (hover: hover) {
  .mylib-card__poster::before,
  .mylib-card__poster::after { opacity: 0; transition: opacity 160ms ease; }
  .mylib-card:hover .mylib-card__poster::before,
  .mylib-card:hover .mylib-card__poster::after { opacity: 1; }
}

/* 文字欄 */
.mylib-card__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  padding-top: 4px;
}
.mylib-card__title {
  display: block;
  margin: 0 0 8px;
  text-decoration: none;
  font-family: var(--font-cjk-display);
  font-size: 18px;
  font-weight: var(--fw-bold);
  line-height: 28px;
  color: var(--text-primary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mylib-card__meta {
  display: block;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  line-height: 21px;
  color: var(--text-tertiary);
}
.mylib-card--cw .mylib-card__meta { color: var(--text-secondary); }
/* 到期倒數數字：1.0 的棕橘強調 → 2.0 品牌橘 */
.mylib-card__num {
  color: var(--yellow-500);
  font-weight: var(--fw-semibold);
  font-style: normal;
}

/* 動作列：評分/再租一次 靠左（margin-right:auto 把 icon 群推到右邊）、
   收藏/愛心/更多 靠右 */
.mylib-card__actions {
  margin-top: auto;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 12px;
}
.mylib-card__rate,
.mylib-card__boxrev,
.mylib-card__rerent { margin-right: auto; }
.mylib-card__rate,
.mylib-card__boxrev {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 34px;
  padding: 0 16px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--white-alpha-8);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: background-color 150ms ease;
}
.mylib-card__rate:hover,
.mylib-card__boxrev:hover { background: var(--white-alpha-16); }
.mylib-card__rate-icon {
  width: 16px;
  height: 16px;
  /* 星星淡灰（spec）：比文字退階，不用 currentColor
     （2026-07-14 Dolly：tertiary 45% 還太亮 → disabled 35%） */
  background-color: var(--text-disabled);
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
/* 已評分狀態（2026-07-15 Dolly 拍板加）：星星轉品牌橘（同 bookmark active 的
   語意色階；star.svg 本身是實心），文字換「已評分」（在 markup）。pill 外觀
   不變、仍可點——之後可接「查看/編輯我的評分」。 */
.mylib-card__rate--done .mylib-card__rate-icon { background-color: var(--yellow-500); }
.mylib-card__iconbtn {
  width: 20px;
  height: 20px;
  padding: 0;
  border: none;
  background-color: var(--text-secondary);
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
  cursor: pointer;
  transition: background-color 150ms ease;
}
.mylib-card__iconbtn:hover { background-color: var(--text-primary); }
/* 已收藏/已喜歡（spec）：實心 icon＋語意色，且 default 常駐（不隨 hover 隱藏）。
   --icon 蓋 inline style 需 !important；收藏=品牌橘、喜歡=DS Highlight rose。 */
.mylib-card__iconbtn--bookmark.is-active,
.mylib-card__iconbtn--bookmark.is-active:hover {
  --icon: url('assets/icons/bookmark_active.svg') !important;
  background-color: var(--yellow-500);
}
.mylib-card__iconbtn--heart.is-active,
.mylib-card__iconbtn--heart.is-active:hover {
  --icon: url('assets/icons/heart_fill.svg') !important;
  background-color: var(--highlight-500);
}
/* 桌機：icon 群（收藏/愛心/更多）hover/focus 才浮出；觸控裝置常駐。
   評分/再租一次 提示鈕不在此列。我的租借頁連 is-active 也是 hover 才出現
   （2026-07-13 Dolly）；收藏影片/喜愛的影片兩頁的 active icon 才常駐。
   2026-07-13 Dolly 追加（照 1.0 spec）：收藏/喜愛兩頁每卡只有單顆常駐 icon
   （收藏=橘書籤、喜愛=玫紅愛心，靠最右），hover 不再浮出其他選項——
   markup 已移除該兩頁的 heart/more、bookmark/more，別加回來。 */
@media (hover: hover) {
  .mylib-card__actions .mylib-card__iconbtn { opacity: 0; transition: opacity 160ms ease; }
  .mylib-card:hover .mylib-card__actions .mylib-card__iconbtn,
  .mylib-card:focus-within .mylib-card__actions .mylib-card__iconbtn,
  [data-mylib-panel="saved"] .mylib-card__iconbtn.is-active,
  [data-mylib-panel="liked"] .mylib-card__iconbtn.is-active { opacity: 1; }
}

/* ── 已過期（spec Desktop 狀態圖）──
   海報壓黑掛「已過期」＋左下爆米花價格；無進度條、無播放鍵（不能直接播）；
   meta 只剩總集數/片長；CTA=實色「再租一次」。 */
.mylib-card--expired .mylib-card__poster::before,
.mylib-card--expired .mylib-card__poster::after { content: none; }
/* 未租過的卡也不出 hover 播放鍵（不能播；海報連資訊頁），
   同時把右下角讓給價格 pill（2026-07-13 統一右下）。 */
.mylib-card[data-status="unrented"] .mylib-card__poster::before,
.mylib-card[data-status="unrented"] .mylib-card__poster::after { content: none; }
.mylib-card__expired-scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.6);
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-medium);
}
/* 爆米花價格＝深色 pill，統一貼海報右下（2026-07-13 Dolly；原為左下純文字） */
.mylib-card__price {
  position: absolute;
  right: 6px;
  bottom: 6px;
  z-index: 3;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 8px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.55);
  color: var(--text-primary);
  font-family: var(--font-latin);
  font-size: 12px;
  font-weight: var(--fw-semibold);
}
.mylib-card__price-icon {
  width: 14px;
  height: 14px;
  background-color: var(--neutral-white);
  -webkit-mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
}
/* 再租一次：實色品牌鈕（1.0 稿的棕金 → 2.0 品牌橘），同 __rate 尺寸 */
.mylib-card__rerent {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 34px;
  padding: 0 16px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--yellow-500);
  color: var(--neutral-900);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-bold);
  cursor: pointer;
  transition: filter 150ms ease;
}
.mylib-card__rerent:hover { filter: brightness(1.08); }

/* 禮物卡（spec）：海報模糊當卡底（同 --cw）、來源列在標題上方、
   gift icon 跟旁邊文字同色（2026-07-13 Dolly，原為白色） */
.mylib-card__gift {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  order: -1;
  margin-bottom: 2px;
  color: var(--text-secondary);
}
.mylib-card__gift-icon {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/gift.svg') center / contain no-repeat;
          mask: url('assets/icons/gift.svg') center / contain no-repeat;
}
.mylib-card--gift .mylib-card__meta { color: var(--text-secondary); }

/* ── 盲盒影片（2026-07-14 Dolly spec 圖）──
   抽盲盒獲得的租借：卡上多「盲盒影評」鈕（同 __rate 配方，見上方共用 selector）
   ＋hover 動作列多一顆送禮 icon。排隊中＝抽到已在觀看中的同一部：
   中性 pill 貼卡右上（不佔「即將過期」的海報左上位——排隊中效期照樣倒數，
   兩個 tag 理論上可並存），海報點擊改開「需等待前一份觀看結束」彈窗。 */
.mylib-card__queue {
  position: absolute;
  top: 12px;
  right: 12px;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  /* 品牌橘 12% 底＋品牌橘字（2026-07-14 Dolly 拍板，取代中性灰） */
  background: rgba(var(--brand-rgb), 0.12);
  color: var(--yellow-500);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-medium);
  line-height: 18px;
  /* z 不設：absolute 自然畫在文字流之上、stretched link(z2) 之下——點 tag＝點整卡 */
}
/* 標題讓出右上 pill 的位置（nowrap ellipsis 會鑽到 pill 底下） */
.mylib-card--queued .mylib-card__title { padding-right: 64px; }
/* 排隊中的海報與整卡 link 都是 <button>（開等待彈窗，不進播放頁/資訊頁；
   2026-07-14 Dolly 拍板整卡也開彈窗）：抹掉 UA 外觀，其餘吃原 class 樣式 */
button.mylib-card__poster {
  padding: 0;
  border: none;
  cursor: pointer;
}
button.mylib-card__link {
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

/* ── 狀態列：chips 左、排序/歷史鈕右 ── */
.mylib-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 24px;
}
.mylib-toolbar .library-genres { margin: 0; }
.mylib-toolbar__actions {
  display: flex;
  align-items: center;
  gap: 12px;
  flex: 0 0 auto;
}
.mylib-toolbar .icon-btn .ds-icon,
.mylib-toolbar__icon {
  width: 24px;
  height: 24px;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
/* ── 排序下拉（2026-07-13 Dolly：樣式照搜尋貼文排序 .search-sort、
   尺寸照資料庫 .rf-sort＝40px 膠囊/188px 選單）──
   pill=「排序 值 ⇅」（無冒號，2026-07-13 Dolly）；選單=四鍵單選＋品牌橘勾勾。 */
.mylib-sort { position: relative; }
.mylib-sort__btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 0 14px 0 16px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-4);
  border: 1px solid var(--neutral-700);
  color: var(--text-secondary);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: color 160ms var(--ease-out), border-color 160ms var(--ease-out);
}
.mylib-sort__btn:hover,
.mylib-sort.is-open .mylib-sort__btn { color: var(--text-primary); border-color: var(--border-default); }
.mylib-sort__label { color: var(--text-tertiary); font-weight: var(--fw-regular); }
.mylib-sort__updown {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask: url('assets/icons/arrow_down_up.svg') center / contain no-repeat;
          mask: url('assets/icons/arrow_down_up.svg') center / contain no-repeat;
}
.mylib-sort__menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  min-width: 188px;
  padding: 6px;
  border-radius: var(--radius-lg);
  background: rgba(19, 19, 19, 0.92);
  border: 1px solid var(--border-default);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
  z-index: 40;
  opacity: 0;
  transform: translateY(-6px) scale(0.98);
  transform-origin: top right;
  pointer-events: none;
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}
.mylib-sort.is-open .mylib-sort__menu {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}
.mylib-sort__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  height: 40px;
  padding: 0 12px;
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  text-align: left;
}
.mylib-sort__opt:hover { background: var(--white-alpha-8); color: var(--text-primary); }
.mylib-sort__opt.is-active { color: var(--text-primary); }
/* 選中鍵右側的勾勾（2026-07-13 Dolly 截圖：四鍵單選＋勾，取代方向箭頭） */
.mylib-sort__opt-check {
  width: 14px;
  height: 11px;
  flex: 0 0 auto;
  color: var(--brand-500);
  opacity: 0;
}
.mylib-sort__opt.is-active .mylib-sort__opt-check { opacity: 1; }

/* 觀看歷史時鐘鈕：跟左側排序膠囊同配方（icon-btn 預設 glass 描邊太白、icon 太亮） */
.mylib-toolbar .icon-btn {
  background: var(--white-alpha-4);
  border-color: var(--neutral-700);
  color: var(--text-secondary);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.mylib-toolbar .icon-btn:hover:not(:disabled) {
  background: var(--white-alpha-4);
  border-color: var(--border-default);
  color: var(--text-primary);
}

/* ── 租借卡格：3 欄，>1680 進 4 欄（container--wide-grow 1840）── */
.mylib-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px;
  padding-top: 24px;
}
@media (min-width: 1680px) {
  .mylib-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 1279.98px) {
  .mylib-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 767.98px) {
  .mylib-grid { grid-template-columns: repeat(1, minmax(0, 1fr)); }
}

/* ── 收藏/喜愛：海報格（重用 .film-card）── */
.mylib-poster-grid {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 12px;
  padding-top: 24px;
}
@media (min-width: 1680px) {
  .mylib-poster-grid { grid-template-columns: repeat(8, minmax(0, 1fr)); }
}
@media (max-width: 1279.98px) {
  .mylib-poster-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@media (max-width: 767.98px) {
  .mylib-poster-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ── 我的租借空狀態（2026-07-14 Dolly spec 圖，1.0 版型換 2.0 皮）──
   卡格空狀態＝篩選無結果（ds.js 自動）與情境「全部過期」共用；
   --first＝完全未租過（置中文案＋立即探索）。display 自帶 → [hidden] 必須贏。 */
.mylib-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 96px 0 120px;
  color: var(--text-tertiary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
}
.mylib-empty[hidden] { display: none !important; }
.mylib-empty__icon {
  width: 40px;
  height: 40px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/episode.svg') center / contain no-repeat;
          mask: url('assets/icons/episode.svg') center / contain no-repeat;
}
.mylib-empty--first { padding: 140px 0 180px; gap: 24px; }
.mylib-empty__copy {
  margin: 0;
  text-align: center;
  font-size: var(--fs-body);
  line-height: 1.9;
  color: var(--text-secondary);
}

/* 情境展示（ds.js 寫 data-mylib-scene）：全部過期→收繼續觀賞＋卡格、留工具列；
   未租過→連工具列一起收，只剩 --first 空狀態；
   分頁無影片（tab）→只收卡格，rail/工具列照舊（模擬篩選 0 張的活狀態） */
.mylib-panel[data-mylib-scene] [data-mylib-grid] { display: none; }
.mylib-panel[data-mylib-scene="expired"] .page-head,
.mylib-panel[data-mylib-scene="expired"] .mylib-cw-rail,
.mylib-panel[data-mylib-scene="never"] .page-head,
.mylib-panel[data-mylib-scene="never"] .mylib-cw-rail,
.mylib-panel[data-mylib-scene="never"] .mylib-toolbar { display: none; }

/* 面板內 section 標題（沿用 page-head 節奏） */
.mylib-panel .page-head { margin: 24px 0 0; }
/* 標題與 排序/時鐘 同列的工具列（收到的禮物，2026-07-14 spec）：
   標題邊距歸零、由 toolbar 自己的 margin-top 控節奏 */
.mylib-toolbar--head { margin-top: 40px; }
.mylib-toolbar .page-head { margin: 0; }

/* ── 已過期影片彈窗（2026-07-13 Dolly 照 Figma：工具列時鐘鈕開啟）──
   列沿用 .mylib-card--expired；鉛筆＝編輯模式（列尾常駐紅色刪除鈕）。 */
.mylib-expired .ds-modal__panel {
  display: flex;
  flex-direction: column;
}
.mylib-expired__tools {
  display: flex;
  align-items: center;
  margin: 4px 0 8px;
}
.mylib-expired__edit {
  display: inline-flex;
  /* 鉛筆恆靠右；選取模式的「全部選取」佔左（spec） */
  margin-left: auto;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 150ms ease, background-color 150ms ease;
}
.mylib-expired__edit[hidden] { display: none !important; } /* 明確 display 蓋 [hidden] 老雷 */
.mylib-expired__edit:hover { background: var(--white-alpha-8); color: var(--text-primary); }
.mylib-expired__edit[aria-pressed="true"] {
  color: var(--yellow-500);
  background: rgba(var(--brand-rgb), 0.12);
}
.mylib-expired__edit .mylib-toolbar__icon { width: 18px; height: 18px; }
.mylib-expired__list {
  display: flex;
  flex-direction: column;
  gap: 12px;
  overflow-y: auto;
  max-height: min(56vh, 560px);
  padding-right: 4px;
}
/* ── 選取模式（2026-07-14 Dolly 照 Figma flow）──
   鉛筆進入：左上「全部選取」＋每列 checkbox＋footer 返回/刪除影片。 */
.mylib-expired__selectall {
  padding: 0;
  border: none;
  background: none;
  color: var(--text-tertiary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.mylib-expired__selectall:hover { color: var(--text-primary); }
/* 每列 checkbox：平時不佔位，選取模式才出；貼列左上、疊在 stretched link 之上 */
.mylib-expired__check {
  display: none;
  flex: 0 0 auto;
  align-self: flex-start;
  position: relative;
  z-index: 3;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 1.5px solid var(--border-strong);
  border-radius: 6px;
  background: none;
  cursor: pointer;
}
.mylib-expired__list.is-selecting .mylib-expired__check { display: inline-flex; }
.mylib-expired__check[aria-pressed="true"] {
  background: var(--yellow-500);
  border-color: var(--yellow-500);
}
.mylib-expired__check[aria-pressed="true"]::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 10px;
  height: 6px;
  border-left: 2px solid var(--yellow-ink);
  border-bottom: 2px solid var(--yellow-ink);
  transform: rotate(-45deg) translateY(-1px);
}
/* 選取模式：整卡連結停用（點列＝點勾選，不跳頁） */
.mylib-expired__list.is-selecting .mylib-card__link,
.mylib-expired__list.is-selecting .mylib-card__poster { pointer-events: none; }
/* footer：鈕用 2.0 DS（btn--quiet／btn--destructive，各態 DS 自帶，勿自刻——
   2026-07-14 Dolly 糾正），這裡只管排版 */
.mylib-expired__footer {
  display: flex;
  gap: 12px;
  margin-top: 16px;
}
.mylib-expired__footer[hidden] { display: none; }
.mylib-expired__footer .btn { flex: 1; }
/* 清單刪光的空狀態 */
.mylib-expired__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 96px 0;
  color: var(--text-tertiary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
}
.mylib-expired__empty[hidden] { display: none; }
.mylib-expired__empty-icon {
  width: 40px;
  height: 40px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/episode.svg') center / contain no-repeat;
          mask: url('assets/icons/episode.svg') center / contain no-repeat;
}
/* 刪除完成 toast（fixed 底部置中；z 高於 ds-modal 1000）
   0729 綜合版定案：材質統一成 白16%玻璃膠囊＋glass 立體感套件、底距 28px，
   同配方=56-ds-toast.css / 48-rent-modal.css（.rent-toast） */
.mylib-toast {
  position: fixed;
  left: 50%;
  bottom: calc(28px + env(safe-area-inset-bottom, 0px));
  transform: translateX(-50%);
  z-index: 1100;
  padding: 10px 24px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-16);
  border: var(--glass-border);
  backdrop-filter: var(--glass-backdrop);
  -webkit-backdrop-filter: var(--glass-backdrop);
  box-shadow: var(--glass-shadow);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}
.mylib-toast[hidden] { display: none; }
.mylib-toast.is-show { opacity: 1; }
/* 手機：抬過浮動底部導覽（同 56-ds-toast.css 的讓位規則） */
@media (max-width: 767.98px) {
  .mylib-toast { bottom: var(--botnav-clearance, calc(84px + env(safe-area-inset-bottom, 0px))); }
}

/* 取消喜愛/收藏提示（2026-07-14 Dolly spec）：貼在被點按鈕下方的深色 pill，
   ds.js 注入單一 portal、1.6s 自動淡出；同 toast 配方但跟著按鈕定位 */
.mylib-tip {
  position: fixed;
  z-index: 1100;
  padding: 10px 22px;
  border-radius: var(--radius-pill);
  background: rgba(19, 19, 19, 0.96);
  border: 1px solid var(--border-default);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-4px);
  transition: opacity 180ms ease, transform 180ms ease;
  pointer-events: none;
}
.mylib-tip[hidden] { display: none !important; }
.mylib-tip.is-show { opacity: 1; transform: translateY(0); }

/* ⋮ 選單（fixed portal，開在被點的 ⋮ 下方；z 高於 ds-modal 的 1000） */
.mylib-cardmenu {
  position: fixed;
  z-index: 1001;
  min-width: 176px;
  padding: 6px;
  border-radius: var(--radius-lg);
  background: rgba(19, 19, 19, 0.92);
  border: 1px solid var(--border-default);
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.55);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}
.mylib-cardmenu[hidden] { display: none; } /* 明確 display 蓋 [hidden] 老雷 */
.mylib-cardmenu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  height: 40px;
  padding: 0 12px;
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  text-align: left;
  text-decoration: none;
}
.mylib-cardmenu__item:hover { background: var(--white-alpha-8); color: var(--text-primary); }
.mylib-cardmenu__icon {
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  background-color: currentColor;
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
.mylib-cardmenu__item--danger,
.mylib-cardmenu__item--danger:hover { color: var(--error-500); }

/* ── 盲盒專屬影評彈窗（2026-07-14 Dolly spec 圖）──
   官方影評列：頭像列（Ztor. 圓標＋名稱＋日期）＋ 16:9 縮圖＋標題＋兩行摘要。
   內文頁未建 → 列先純視覺（hover 有底、不跳頁）。 */
.mylib-boxrev__list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 24px;
  overflow-y: auto;
  max-height: min(56vh, 560px);
  padding-right: 4px;
}
.mylib-boxrev__item {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px;
  border-radius: var(--radius-md);
  transition: background-color 150ms ease;
}
.mylib-boxrev__item:hover { background: var(--white-alpha-4); }
.mylib-boxrev__head {
  display: flex;
  align-items: center;
  gap: 8px;
}
/* Ztor. 官方圓標：黑底圓＋白 logo（同 __ph 的 logo mask 配方） */
.mylib-boxrev__avatar {
  position: relative;
  width: 28px;
  height: 28px;
  flex: 0 0 auto;
  border-radius: 50%;
  background: var(--neutral-900);
  border: 1px solid var(--border-subtle);
}
.mylib-boxrev__avatar::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 62%;
  height: 34%;
  background-color: var(--neutral-white);
  -webkit-mask: url('assets/icons/ztor_logo.svg') center / contain no-repeat;
          mask: url('assets/icons/ztor_logo.svg') center / contain no-repeat;
}
.mylib-boxrev__name {
  font-family: var(--font-latin);
  font-size: 14px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.mylib-boxrev__date {
  font-family: var(--font-latin);
  font-size: 12px;
  color: var(--text-tertiary);
}
.mylib-boxrev__row {
  display: flex;
  gap: 12px;
}
.mylib-boxrev__thumb {
  flex: 0 0 148px;
  width: 148px;
  height: 84px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--neutral-800);
}
.mylib-boxrev__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.mylib-boxrev__copy {
  flex: 1;
  min-width: 0;
}
.mylib-boxrev__title {
  display: block;
  margin-bottom: 6px;
  font-family: var(--font-cjk-display);
  font-size: 16px;
  font-weight: var(--fw-bold);
  line-height: 24px;
  color: var(--text-primary);
}
.mylib-boxrev__excerpt {
  margin: 0;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 24px;
  color: var(--text-secondary);
}

/* ── 需等待前一份觀看結束彈窗：spec 金色強調行 → 品牌橘 ── */
.mylib-wait__hl {
  color: var(--yellow-500);
  font-weight: var(--fw-bold);
}

/* ── 影集展開彈窗（2026-07-14 Dolly spec 圖，1.0 版型換 2.0 皮）──
   影集卡整卡點擊開啟；清單可捲、範圍 tabs 在 ds.js mylib 切換。 */
/* 寬度要贏 ds.css 的 .ds-modal[data-size="lg"]（0-3-0，且載入序在後）→ 加權重 */
.mylib-series.ds-modal[data-size="lg"] .ds-modal__panel {
  max-width: 720px;
}
.mylib-series .ds-modal__panel {
  display: flex;
  flex-direction: column;
}
.mylib-series__head {
  display: flex;
  gap: 20px;
  margin-top: 8px;
}
.mylib-series__poster {
  position: relative;
  flex: 0 0 120px;
  width: 120px;
  height: 180px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--neutral-800);
}
.mylib-series__poster img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* 海報 hover 播放圓鈕（同卡片配方：玻璃圓＋白邊＋play 墨水右推 1px） */
.mylib-series__poster-play {
  position: absolute;
  right: 10px;
  bottom: 10px;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0.35);
  border: 1px solid var(--neutral-white);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
}
.mylib-series__poster-play::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 16px;
  height: 16px;
  transform: translateX(1px);
  background-color: var(--neutral-white);
  -webkit-mask: url('assets/icons/media-play.svg') center / contain no-repeat;
          mask: url('assets/icons/media-play.svg') center / contain no-repeat;
}
.mylib-series__info { flex: 1; min-width: 0; }
.mylib-series__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: 24px;
  font-weight: var(--fw-bold);
  line-height: 34px;
  color: var(--text-primary);
}
.mylib-series__alt {
  margin: 2px 0 0;
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
/* 年份｜分級｜片長：直線分隔 */
.mylib-series__meta {
  display: flex;
  align-items: center;
  margin: 14px 0 0;
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.mylib-series__meta span + span {
  margin-left: 12px;
  padding-left: 12px;
  border-left: 1px solid var(--white-alpha-16);
}
/* 評分＝title.html 評分環＋寫下你的影評同列（.detail-scorerow 同款，樣式吃 36 檔），
   這裡只補在 popup 頭部的節奏 */
.mylib-series__scorerow { margin: 14px 0 2px; }
/* 類型 chips＝title.html 的 .detail-chip（中性玻璃，36-title-detail.css 已進 bundle） */
.mylib-series__tags {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-top: 14px;
}

/* CTA 列（2026-07-14 Dolly：對齊 2.0）：解鎖全劇集＝title.html 價格膠囊同款
   （btn--yellow-ghost＋.detail-price 兩行）｜繼續觀看＝btn--ghost＋play */
.mylib-series__cta {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 16px;
  margin-top: 16px;
}
/* 兩行價格膠囊在 modal 預設 50px 會擠；.btn 連寫贏 ds.css 的 .ds-modal .btn（載入序在後） */
.mylib-series .btn.mylib-series__ctabtn { height: 64px; }
.mylib-series__continue { gap: 10px; }
/* 已全買情境：無解鎖鈕 → 繼續觀看獨佔一列（spec 260629） */
.mylib-series__cta--single { grid-template-columns: 1fr; }
.mylib-series__play {
  width: 16px;
  height: 16px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/media-play.svg') center / contain no-repeat;
          mask: url('assets/icons/media-play.svg') center / contain no-repeat;
}
/* 集數列頭＋範圍 tabs */
.mylib-series__listhead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: 28px;
}
.mylib-series__count {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
/* 清單⇄格狀檢視切換：icon＝要切去的模式（list 模式→category.svg、grid 模式→list.svg），
   ds.js 切 .mylib-series[data-epview] */
.mylib-series__view {
  position: relative;
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 150ms ease, background-color 150ms ease;
}
.mylib-series__view:hover { background-color: var(--white-alpha-8); color: var(--text-primary); }
.mylib-series__view::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 20px;   /* 舊 icon 20 太大→16；Dolly 換新 category.svg 後再放大回 20 */
  height: 20px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/category.svg') center / contain no-repeat;
          mask: url('assets/icons/category.svg') center / contain no-repeat;
}
.mylib-series[data-epview="grid"] .mylib-series__view::after {
  -webkit-mask-image: url('assets/icons/list.svg');
          mask-image: url('assets/icons/list.svg');
}
/* cube 模式不分段：範圍 tabs 藏起（高度由下方數字格補償，切模式不跳） */
.mylib-series[data-epview="grid"] .mylib-series__ranges { display: none; }
.mylib-series__ranges {
  display: flex;
  gap: 24px;
  margin-top: 12px;
  border-bottom: 1px solid var(--white-alpha-8);
}
.mylib-series__range {
  padding: 0 2px 10px;
  border: none;
  background: none;
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  transition: color 150ms ease;
}
.mylib-series__range:hover { color: var(--text-secondary); }
.mylib-series__range.is-active {
  color: var(--text-primary);
  font-weight: var(--fw-semibold);
  border-bottom-color: var(--yellow-500);
}
/* 集數清單（可捲）：定高（非 max-height）——清單⇄格狀切換彈窗高度不跳（Dolly 點名） */
.mylib-series__list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin-top: 14px;
  overflow-y: auto;
  height: min(40vh, 480px);
  padding-right: 4px;
}
.mylib-series__list[hidden] { display: none !important; }
/* 格狀檢視（數字格）：一排固定 8 集（2026-07-14 Dolly），
   等分格用 minmax(0,1fr)（1fr 撐爆軌道雷）、方格用 aspect-ratio 撐高。
   高度：有範圍 tabs 的彈窗才補 43px（tabs 在 cube 模式藏起，總高不跳；
   無 tabs 的彈窗（如我要衝線 8 集）不補，否則反而多 43px） */
.mylib-series__numgrid {
  display: grid;
  grid-template-columns: repeat(8, minmax(0, 1fr));
  align-content: flex-start;
  gap: 12px;
  margin-top: 14px;
  overflow-y: auto;
  height: min(40vh, 480px);
  padding-right: 4px;
}
.mylib-series__ranges ~ .mylib-series__numgrid { height: calc(min(40vh, 480px) + 43px); }
.mylib-series__numgrid[hidden] { display: none !important; }
.mylib-epnum {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  border: 1px solid transparent;
  font-family: var(--font-latin);
  font-size: 18px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  text-decoration: none;
  transition: background-color 150ms ease;
}
a.mylib-epnum:hover { background: var(--white-alpha-8); }
.mylib-epnum.is-current {
  border-color: var(--border-strong);
}
.mylib-epnum--locked { color: var(--text-tertiary); }
.mylib-epnum--locked::after {
  content: "";
  position: absolute;
  right: 6px;
  bottom: 6px;
  width: 11px;
  height: 11px;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/lock.svg') center / contain no-repeat;
          mask: url('assets/icons/lock.svg') center / contain no-repeat;
}
.mylib-ep {
  position: relative;
  display: flex;
  gap: 14px;
  padding: 12px;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  border: 1px solid transparent;
  text-decoration: none;
  color: inherit;
  transition: background-color 150ms ease;
}
a.mylib-ep:hover { background: var(--white-alpha-8); }
/* 上次觀看的那集：無彩色描邊（2026-07-14 Dolly：不用品牌橘；
   neutral-300 實色太突兀 → border-strong 白 22%） */
.mylib-ep.is-current { border-color: var(--border-strong); }
.mylib-ep__media {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 0 0 128px;
}
.mylib-ep__thumb {
  position: relative;
  width: 128px;
  height: 72px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--neutral-800);
}
.mylib-ep__thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.mylib-ep__pbar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 3px;
  background: rgba(255, 255, 255, 0.25);
}
.mylib-ep__pbar-fill {
  display: block;
  height: 100%;
  background: var(--yellow-500);
}
/* 已全買但未上映（spec 260629）：整列壓淡、無簡介/價格，共用縮圖鎖頭 */
.mylib-ep--soon .mylib-ep__thumb img { opacity: 0.55; }
.mylib-ep--soon .mylib-ep__title { color: var(--text-tertiary); }

/* 未解鎖：縮圖壓暗＋鎖頭 */
.mylib-ep__lock {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
}
.mylib-ep__lock::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 18px;
  height: 18px;
  background-color: var(--text-secondary);
  -webkit-mask: url('assets/icons/lock.svg') center / contain no-repeat;
          mask: url('assets/icons/lock.svg') center / contain no-repeat;
}
.mylib-ep__lastseen {
  font-family: var(--font-cjk-text);
  font-size: 11px;
  color: var(--yellow-500); /* 品牌橘（2026-07-14 Dolly，原 text-tertiary） */
  text-align: center;
}
.mylib-ep__body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.mylib-ep__title {
  font-family: var(--font-cjk-display);
  font-size: 15px;
  font-weight: var(--fw-bold);
  line-height: 24px;
  color: var(--text-primary);
}
.mylib-ep__dur {
  margin-top: 1px;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}
.mylib-ep__desc {
  margin-top: 4px;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  line-height: 20px;
  color: var(--text-secondary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* 到期 chip：貼列右下（同海報價格 pill 配方） */
.mylib-ep__chip {
  margin-top: auto;
  align-self: flex-end;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-secondary);
}
/* 即將到期 tag：列右上（同卡片 tag 色） */
.mylib-ep__tag {
  position: absolute;
  top: 0;
  right: 0;
  padding: 3px 10px;
  border-radius: 0 var(--radius-md) 0 var(--radius-md);
  background: var(--error-500);
  color: var(--neutral-white);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-bold);
  line-height: 18px;
}
/* 未解鎖單集價：白框 pill 靠右置中 */
.mylib-ep__price {
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
  padding: 7px 14px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-strong);
  font-family: var(--font-latin);
  font-size: 13px;
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.mylib-ep__price-icon {
  width: 14px;
  height: 14px;
  background-color: var(--neutral-white);
  -webkit-mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
}

/* ════════════════════════════════════════════════════════════════════════
   CATEGORY PAGE · 分類列表頁（category.html）                    [COMPONENT]
   ════════════════════════════════════════════════════════════════════════
   1.0 分類頁（選擇類型篩選＋排序＋海報格）換 2.0 皮。
   進入點＝片庫內容分類「電影」tab。行為在 ds.js DS.register('cat-grid')。
   reuse：.film-card（格內 width:auto 同 .rf-saved-grid 配方）、.rf-sort、
   .glass-tabs。篩選面板＝本檔 .catfilter。
   ════════════════════════════════════════════════════════════════════════ */

/* ── 標題列：電影＋選擇類型｜排序 ── */
.catpage__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin: 8px 0 26px;
}
.catpage__lead {
  display: flex;
  align-items: center;
  gap: 20px;
  min-width: 0;
}
.catpage__title {
  margin: 0;
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}

/* ── 選擇類型鈕＋下拉篩選面板 ── */
.catfilter { position: relative; }
.catfilter__btn {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 10px 16px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--white-alpha-8);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  cursor: pointer;
  transition: background 150ms var(--ease-out);
}
.catfilter__btn:hover,
.catfilter.is-open .catfilter__btn { background: var(--white-alpha-12, rgba(255, 255, 255, 0.12)); }
.catfilter__ico { width: 18px; height: 18px; flex: none; }

.catfilter__panel {
  position: absolute;
  top: calc(100% + 10px);
  left: 0;
  z-index: 60;                     /* 高於卡格與 sticky 群組 */
  width: min(540px, calc(100vw - 48px));
  max-height: min(70vh, 720px);
  overflow-y: auto;
  padding: 28px 32px;
  border-radius: var(--radius-lg);
  background: var(--bg-secondary);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.55);
}
.catfilter__panel[hidden] { display: none; }

.catfilter__group + .catfilter__group { margin-top: 28px; }
.catfilter__label {
  margin: 0 0 14px;
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-regular);
  color: var(--text-secondary);
}
.catfilter__chips {
  display: flex;
  flex-wrap: wrap;
  gap: 12px 8px;
}
.catfilter__chip {
  padding: 8px 16px;
  border: none;
  border-radius: var(--radius-pill);
  background: none;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  transition: color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.catfilter__chip:hover { color: var(--text-primary); }
.catfilter__chip.is-active {
  background: var(--white-alpha-8);
  color: var(--text-primary);
}

.catfilter__foot {
  display: flex;
  gap: 12px;
  margin-top: 32px;
}

/* ── 海報格（reuse .film-card；同 .rf-saved-grid 配方）── */
.catpage__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: clamp(16px, 2vw, 24px);
}
.catpage__grid .film-card { width: auto; flex: initial; }
.catpage__grid .film-card[hidden] { display: none; }

/* ── 篩選無結果空狀態（照 mylib-empty 配方；display 自帶 → [hidden] 必須贏）── */
.catpage__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  padding: 96px 0 120px;
}
.catpage__empty[hidden] { display: none !important; }
.catpage__empty-title {
  margin: 0;
  font-size: var(--fs-body);
  color: var(--text-secondary);
}
.catpage__empty-desc {
  margin: 0;
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}

/* ── 清單結尾標記（產品語氣、低調）── */
.catpage__end {
  margin: 56px 0 8px;
  text-align: center;
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-regular);
  color: var(--text-tertiary);
}
.catpage__end[hidden] { display: none; }

/* ── RWD ── */
@media (max-width: 768px) {
  .catpage__head { margin: 4px 0 18px; }
  .catpage__lead { gap: 12px; }
  .catfilter__panel { padding: 22px 20px; }
  .catpage__grid { grid-template-columns: repeat(auto-fill, minmax(130px, 1fr)); gap: 12px; }
  /* 排序鈕貼右——選單維持右錨，蓋掉 refine-library 手機規則的 left:0
     （那條是給貼左的 rf-toolbar 用的，這裡照抄會往右爆出 42px） */
  body.rf-library .catpage__head .rf-sort__menu { left: auto; right: 0; transform-origin: top right; }
}

/* ════════════════════════════════════════════════════════════════════════
   RENT MODAL · 選擇租借畫質（#rentQuality）                      [COMPONENT]
   ════════════════════════════════════════════════════════════════════════
   付費流程 step 1（Figma Desktop [Pop Up] 選擇畫質 161:18721 換 2.0 皮）。
   底座＝.ds-modal primitive；本檔只放內容層。行為＝ds.js DS.register('title-rent')。
   稿上 Semantic/Warning #F2970A → 2.0 用品牌橘（同 0717 公告膠囊拍板方向）。
   ════════════════════════════════════════════════════════════════════════ */

.rent-modal .ds-modal__panel {
  width: 400px;
  max-width: calc(100vw - 48px);
  padding: 24px 24px 40px;
}
/* 標題與 ✕ 同一排（稿 Title row：title flex-1＋Close 30px；primitive 的 ✕ 預設
   吸在 panel 右上 16px，這裡對齊標題行高） */
.rent-modal .ds-modal__close { top: 24px; right: 24px; }
.rent-modal .ds-modal__title { margin: 0; padding-right: 44px; }
/* 爆米花不足窗較寬（2026-07-17 Dolly：560px）。
   連寫 class 抬權重：要贏 ds.css（載入序在後）data-size="sm" 的 max-width:400。
   gate 桌機（0725）：權重 0,4,0 會壓過 .ds-modal--m-full 的 0,3,0——不 gate 的話
   短劇手機滿版被它夾回 100vw-48（手機本來也輪不到 560）。 */
@media (min-width: 769px) {
  .rent-topup.ds-modal[data-size="sm"] .ds-modal__panel {
    width: 560px;
    max-width: min(560px, calc(100vw - 48px));
  }
  /* ← 是手機滿版頁的隱喻；桌機彈窗用 ✕，兩個 topup（title/short）一致（0728） */
  .rent-topup .gift-friends__back { display: none; }
}

/* 範圍行：作品名・全劇集 - 共 N 集（灰） */
.rent-modal__scope {
  margin: 4px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-tertiary);
}

/* 限制級警示（僅限制級內容出現；?r18=1 可預覽） */
.rent-modal__warning {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 20px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--brand-500);
}
.rent-modal__warning[hidden] { display: none; }
/* 全彩漸層 svg（金三角＋深色驚嘆號）→ img 直出，勿 mask（會壓成單色） */
.rent-modal__warning-ico {
  width: 24px;
  height: 24px;
  flex: none;
}

/* 畫質選項列（白 8% 玻璃列，左畫質徽章右價格） */
.rent-modal__options {
  display: flex;
  flex-direction: column;
  gap: 16px;
  margin-top: 24px;
  width: 100%;
}
.rent-modal__opt {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 75px;
  padding: 0 24px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--white-alpha-8);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  cursor: pointer;
  transition: background 150ms var(--ease-out);
}
.rent-modal__opt:hover { background: var(--white-alpha-12, rgba(255, 255, 255, 0.12)); }

/* 畫質徽章：assets/icons/resolution/ 正式 svg（白徽章鏤空字；0717 Dolly 放大） */
.rent-modal__res {
  display: block;
  height: 22px;
  width: auto;
}

.rent-modal__price {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--font-latin);
  font-size: var(--fs-body);
  color: var(--text-primary);
}
.rent-modal__coin {
  width: 20px;
  height: 20px;
  background-color: var(--text-primary);
  /* 打包進根 components.css → 路徑以根為準（勿寫 ../../） */
  -webkit-mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
}

/* 平台理念文案（14px 灰兩行） */
.rent-modal__note {
  margin: 24px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.8;
  color: var(--text-tertiary);
}

/* 送禮給朋友：透明底白 24% 邊框膠囊（稿上 Button 173:21577） */
.rent-modal__gift {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 100%;
  height: 48px;
  margin-top: 24px;
  border: 1px solid var(--white-alpha-24, rgba(255, 255, 255, 0.24));
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.1);
  color: var(--text-primary);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  cursor: pointer;
  transition: background 150ms var(--ease-out), border-color 150ms var(--ease-out);
}
.rent-modal__gift:hover { background: var(--white-alpha-8); border-color: var(--white-alpha-40, rgba(255, 255, 255, 0.4)); }
.rent-modal__gift-ico {
  width: 28px;
  height: 28px;
  background-color: currentColor;
}

/* ═══ 確認租借（#rentConfirm・step 2A）═══ */
.rent-confirm__lead {
  margin: 20px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: 1.9;
  color: var(--text-tertiary);
}
.rent-confirm__lead strong { color: var(--text-primary); font-weight: var(--fw-bold); }

.rent-confirm__item {
  margin-top: 24px;
  padding: 20px;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
}
.rent-confirm__film {
  display: flex;
  align-items: center;
  gap: 16px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--white-alpha-8);
}
.rent-confirm__poster {
  width: 48px;
  aspect-ratio: 2 / 3;
  object-fit: cover;
  border-radius: 4px;
  flex: none;
}
.rent-confirm__filmtitle {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.rent-confirm__filmmeta {
  margin: 4px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.rent-confirm__totalrow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-top: 18px;
}
.rent-confirm__totallabel {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.rent-confirm__balance {
  margin: 8px 0 0;
  text-align: right;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}

.rent-confirm__cta { width: 100%; margin-top: 24px; }
/* 次要 CTA：暗棕漸層膠囊＋品牌橘字（稿上「現在租，稍後看」） */
.rent-confirm__later {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 48px;
  margin-top: 12px;
  border: 1px solid rgba(255, 163, 63, 0.28);
  border-radius: var(--radius-pill);
  background: linear-gradient(180deg, rgba(255, 163, 63, 0.16), rgba(255, 163, 63, 0.05));
  color: var(--brand-500);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  cursor: pointer;
  transition: background 150ms var(--ease-out), border-color 150ms var(--ease-out);
}
.rent-confirm__later:hover { border-color: rgba(255, 163, 63, 0.5); background: linear-gradient(180deg, rgba(255, 163, 63, 0.22), rgba(255, 163, 63, 0.08)); }

/* ═══ 爆米花不足（#rentTopup・step 2B）═══ */
.rent-topup__balance {
  margin: 16px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.rent-topup__copy {
  margin: 12px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: 1.8;
  color: var(--text-secondary);
}
.rent-topup__copy strong { color: var(--text-primary); font-weight: var(--fw-bold); }
.rent-topup__label {
  margin: 24px 0 8px;
  font-family: var(--font-cjk-text);
  font-size: 12px;          /* 稿 TC Regular 12 */
  color: var(--text-tertiary);
}

/* 訂單內容：爆米花包選擇器（列 h56＋展開浮層；稿 Dropdown_Popcorn 2810:12954） */
.rent-pack { position: relative; }
.rent-pack__row,
.rent-pack__opt {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  height: 56px;
  padding: 16px 24px;
  border: none;
  cursor: pointer;
  text-align: left;
  transition: background 150ms var(--ease-out);
}
.rent-pack__row {
  border-radius: var(--radius-md);
  background: var(--white-alpha-8);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
}
.rent-pack__row:hover { background: var(--white-alpha-12, rgba(255, 255, 255, 0.12)); }
.rent-pack__ico {
  width: 24px;
  height: 24px;
  flex: none;
  background-color: var(--text-primary);
  -webkit-mask: url('assets/icons/popcorn-md.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn-md.svg') center / contain no-repeat;
}
.rent-pack__name {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-primary);
  white-space: nowrap;
}
.rent-pack__count {
  flex: 1;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  white-space: nowrap;
  transition: color 150ms var(--ease-out);
}
.rent-pack__price {
  font-family: var(--font-latin);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--brand-500);
  margin-left: 16px;
}
.rent-pack__chev {
  width: 9px;
  height: 9px;
  margin-left: 8px;
  border-right: 2px solid var(--text-tertiary);
  border-bottom: 2px solid var(--text-tertiary);
  transform: rotate(45deg) translateY(-2px);
  flex: none;
}
/* 展開清單＝浮層蓋在下方內容上（稿：overlay），項目 hover 琥珀染＋數字轉橘 */
.rent-pack__list {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  z-index: 6;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-tertiary);
  box-shadow: 0 16px 48px rgba(0, 0, 0, 0.5);
}
.rent-pack__list[hidden] { display: none; }
.rent-pack__opt { background: transparent; }
.rent-pack__opt + .rent-pack__opt { border-top: 1px solid var(--white-alpha-4); }
.rent-pack__opt:hover { background: rgba(255, 163, 63, 0.14); }
.rent-pack__opt:hover .rent-pack__count { color: var(--brand-500); }
/* 展開清單的選項是葉節點 → 右側不出箭頭（箭頭只在收合觸發列，Dolly 0719） */
.rent-pack__opt .rent-pack__chev { display: none; }

/* ══ 付款方式 Stripe → Antom（2026-08-05 Dolly：「改全站的付款方式，改用 Antom，
   此處 f2e 線已經做過了，可以直接取用」）══════════════════════════════════
   本段整塊取自 f2e 主線 commit 455334bc／ec5463a9，逐字搬入未改寫——
   staging 這條設計線刻意不 merge main（見 memory ztor_staging_isolation_line），
   所以用「逐檔搬」而不是合併。搬進來的包含：16 種付款方式的版型、
   純 CSS 的地區過濾、白卡 logo、Antom 掛名、前往付款轉場（#rentRedirect）。
   被換掉的是 staging 原本那組「列高 78、兩個選項、border-top 分隔線」的舊版。 */
/* 付款方式：單張圓角卡直疊（稿 2938:14461）；✓＝選中。
   0803 Antom 改版：選項從 2 個變 16 個（地區過濾後 4–6 個），列高 78→60、logo 上白卡。 */
.rent-pay {
  border-radius: var(--radius-md);
  overflow: hidden;
  /* 分隔線用 gap 露出容器底色，不用 .row + .row 的 border-top——
     地區過濾把列 display:none 之後，相鄰選擇器會把線畫在「第一個看得見的列」頂端
     （被藏起來的那份仍算兄弟）。gap 對 display:none 的子項不生效，天然免疫。 */
  display: flex;
  flex-direction: column;
  gap: 1px;
  background: var(--white-alpha-8);
}
.rent-pay__row {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  height: 60px;
  padding: 12px 20px;
  border: none;
  background: var(--bg-tertiary);
  cursor: pointer;
  text-align: left;
  transition: background 150ms var(--ease-out);
}
.rent-pay__row:hover { background: var(--white-alpha-8); }

/* ── 地區過濾（Antom 各支付方式有 IP 限制）──
   規則：預設只顯示 [data-pm-region~="all"]，容器 data-pay-region 再放行該地專屬的。
   純 CSS，不用 JS 設樣式（同 CLAUDE.md 第 7 條）。data-pay-region="_all" ＝ demo 用不過濾。 */
.rent-pay[data-pay-region] .rent-pay__row { display: none; }
.rent-pay[data-pay-region] .rent-pay__row[data-pm-region~="all"],
.rent-pay[data-pay-region="tw"] .rent-pay__row[data-pm-region~="tw"],
.rent-pay[data-pay-region="hk"] .rent-pay__row[data-pm-region~="hk"],
.rent-pay[data-pay-region="mo"] .rent-pay__row[data-pm-region~="mo"],
.rent-pay[data-pay-region="id"] .rent-pay__row[data-pm-region~="id"],
.rent-pay[data-pay-region="my"] .rent-pay__row[data-pm-region~="my"],
.rent-pay[data-pay-region="ph"] .rent-pay__row[data-pm-region~="ph"],
.rent-pay[data-pay-region="sg"] .rent-pay__row[data-pm-region~="sg"],
.rent-pay[data-pay-region="th"] .rent-pay__row[data-pm-region~="th"],
.rent-pay[data-pay-region="_all"] .rent-pay__row { display: flex; }

/* logo：官方商標一律原樣放在白卡上（各家品牌規範都禁止改色／改形，
   深色版多半不存在——Apple Pay 只有黑標與白描邊兩版——所以只能給底、不能動標）。
   0803 試過半透明＋淡框線，但這批素材是給淺底用的：Apple Pay 整個標是黑的（2.95:1）、
   Alipay 黑字（2.95:1）、Google Pay 灰字幾乎溶進卡片（1.16:1）、Visa 深藍（1.26:1）——
   台灣預設那 5 種有 4 種讀不出來，Dolly 拍板回白卡。 */
.rent-pay__logo {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 52px;
  height: 34px;
  padding: 4px 6px;
  flex: none;
  background: #fff;
  border-radius: 6px;
}
.rent-pay__logo img {
  display: block;
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
}
/* 信用卡：一張白卡裡並排兩個卡組織標（其餘寫在說明文字） */
.rent-pay__logo--pair img { max-width: calc(50% - 2px); }
/* min-width:0 ＝ flex 子項預設 min-width:auto 會被內容撐開，長說明（信用卡那列）會頂爆列寬 */
.rent-pay__info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* 支付品牌名 14px（0803 Dolly 調小）：拿掉灰色小字之後只剩單行，16px 顯得過重，
   而且這是「選項標籤」不是內文——比訂單內容那些數字退一階才對。 */
.rent-pay__name {
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.rent-pay__desc {
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.rent-pay__check {
  width: 14px;
  height: 8px;
  border-left: 2.5px solid var(--brand-500);
  border-bottom: 2.5px solid var(--brand-500);
  transform: rotate(-45deg);
  opacity: 0;
  flex: none;
}
.rent-pay__row.is-on .rent-pay__check { opacity: 1; }

/* 金流服務商掛名：只是一行小字，使用者要認的是上面那些錢包商標 */
.rent-pay__provider {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin-top: 12px;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}
/* 反白標直接放在深色底上、不加托底卡（0803 Dolly）。
   ⚠️ 高度別再往下調：13px 時字腔糊成一團（1x／2x 都糊，實測與 mask 無關、純粹是小）。
   16px 是「還讀得出字形」與「維持註腳分量」的下限。 */
.rent-pay__provider-logo {
  display: block;
  height: 16px;
  width: auto;
}

/* ── 情境展示 dev bar 與付款彈窗的關係（0803 Dolly）──
   付款地區只在付款彈窗裡有意義，所以：
   ① 外層（沒開付款彈窗）不出現「付款地區」這組；
   ② 進到付款彈窗就只留這一組，其他情境組與「重設資料」讓開；
   ③ 同時把 dev bar 抬到彈窗之上——原本 .wp-roomdev z-index:200 < .ds-modal 1000，
      開窗後被 scrim 蓋住也點不到，等於「要關窗才能切地區」，那就看不到差異了。
   全部純 CSS 依 #rentTopup.is-open 切換，不用 JS 開關樣式（CLAUDE.md 第 7 條）；
   只掛在付款彈窗上，別動全站其他彈窗的層級。
   ⚠️ 選擇器用 .wp-roomdev 不用 [data-wp-titledemo]——短劇頁的面板是 [data-short-dev]，
   同一套規則要兩頁都吃得到。 */
body:not(:has(#rentTopup.is-open)) [data-dev-payregion] { display: none; }
body:has(#rentTopup.is-open) .wp-roomdev { z-index: 1100; }
body:has(#rentTopup.is-open) .wp-roomdev__grp:not([data-dev-payregion]),
body:has(#rentTopup.is-open) .wp-roomdev__reset { display: none; }

/* ── 前往 Antom 付款的轉場（#rentRedirect）── */
.rent-redirect .ds-modal__panel { text-align: center; }
/* .rent-modal .ds-modal__title 有 padding-right:44px 是為了讓開右上角的 ✕，
   但轉場窗不可關、沒有 ✕——那塊留白會把 text-align:center 的字往左推 22px
   （量過：墨水中心距面板中心 -22px）。歸零才是真的置中。 */
.rent-redirect .ds-modal__title { padding-right: 0; }
.rent-redirect__mark {
  position: relative;
  width: 72px;
  height: 72px;
  margin: 8px auto 20px;
  display: grid;
  place-items: center;
}
.rent-redirect__logo {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 52px;
  height: 34px;
  padding: 4px 6px;
  background: #fff;
  border-radius: 6px;
}
.rent-redirect__logo img { display: block; max-width: 100%; max-height: 100%; object-fit: contain; }
/* 轉圈環：只有一段是品牌橘，其餘透明 → 旋轉時讀得出方向 */
.rent-redirect__spin {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--white-alpha-8);
  border-top-color: var(--brand-500);
  animation: rent-redirect-spin 900ms linear infinite;
}
@keyframes rent-redirect-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .rent-redirect__spin { animation-duration: 2400ms; }
}
.rent-redirect__title { margin-bottom: 8px; }
.rent-redirect__copy {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  margin-bottom: 20px;
}
.rent-redirect__amount {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: 8px;
  padding: 12px 16px;
  border-radius: var(--radius-md);
  background: var(--bg-tertiary);
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}
.rent-redirect__amount strong {
  font-family: var(--font-latin);
  font-size: var(--fs-body);
  color: var(--text-primary);
}

/* 促銷代碼＋金額＝同一張卡（稿 2810:11467：上半 promo、下半 totals、中間 hairline） */
.rent-topup__bill {
  margin-top: 24px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-tertiary);
}
.rent-topup__promo-wrap { padding: 16px 24px; }
.rent-topup__promo {
  width: 100%;
  padding: 16px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
}
.rent-topup__promo::placeholder { color: var(--text-tertiary); }
.rent-topup__promo:focus { outline: 1px solid var(--white-alpha-24, rgba(255, 255, 255, 0.24)); }

/* 金額列 */
.rent-topup__totals {
  padding: 16px 24px;
  border-top: 1px solid var(--white-alpha-8);
}
.rent-topup__trow {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  padding: 6px 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.rent-topup__trow--mpay[hidden] { display: none; }
.rent-topup__trow--total {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.rent-topup__deduct { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; }
.rent-topup__points { font-size: 12px; color: var(--text-tertiary); }

/* ═══ 購買成功 toast（稿 Toast-update 154:16961：白 16% 玻璃膠囊）═══
   0729 綜合版定案（Dolly）：位置右下→底部置中（全站三顆 toast 統一）；
   立體感升級成 ds-toast 的玻璃套件（--glass-backdrop/border/shadow）。
   同配方=56-ds-toast.css（.ds-toast）、46-my-library.css（.mylib-toast）。
   置中用 translate(-50%,…)，進場位移必須合成寫在同一條 transform。 */
.rent-toast {
  position: fixed;
  left: 50%;
  bottom: calc(28px + env(safe-area-inset-bottom, 0px));
  z-index: 1200;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 16px 24px 16px 20px;
  border: var(--glass-border);
  border-radius: var(--radius-pill);
  background: var(--white-alpha-16);
  backdrop-filter: var(--glass-backdrop);
  -webkit-backdrop-filter: var(--glass-backdrop);
  box-shadow: var(--glass-shadow);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  opacity: 0;
  transform: translate(-50%, 8px);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.rent-toast.is-show { opacity: 1; transform: translate(-50%, 0); }
/* 手機：抬過浮動底部導覽（同 56-ds-toast.css 的讓位規則） */
@media (max-width: 767.98px) {
  .rent-toast { bottom: var(--botnav-clearance, calc(84px + env(safe-area-inset-bottom, 0px))); }
}
.rent-toast[hidden] { display: none; }
/* 0731 全站統一 ds-success-tick（彈入＋描繪）＋ L 版盤面：實心綠圓盤＋深色勾
   （同 56-ds-toast／cc-success，toast＝迷你版；svg 尺寸走 57 檔元件預設 62%）。
   [hidden]=display:none ↔ 顯示切換會讓元件動畫每次顯示自動重播，不需 JS 配合。 */
.rent-toast__check {
  width: 24px; height: 24px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  background: var(--success-500);
  color: var(--neutral-900);
}
/* 24px 迷你盤的光學補償（0731 Dolly）：勾放大到 20px，三顆 toast 同值（同 56 檔） */
.rent-toast__check .ds-success-tick__svg { width: 20px; height: 20px; }
/* 手機（Figma Short mobile 0725）：toast 改底部置中、由下往上浮出（桌機維持右下）。
   置中用 left:50% translateX —— is-show 的 transform:none 會清掉位移，要一併覆寫。 */
@media (max-width: 768px) {
  .rent-toast {
    right: auto;
    left: 50%;
    bottom: max(24px, calc(env(safe-area-inset-bottom, 0px) + 16px));
    width: max-content;
    max-width: calc(100vw - 24px);
    padding: 14px 18px 14px 14px;
    /* 長訊息（送禮成功）要能折行——nowrap 會撐爆螢幕；折行後圓角收 24 免膠囊端太肥 */
    border-radius: var(--radius-2xl);
    line-height: 1.5;
    transform: translate(-50%, 8px);
  }
  .rent-toast.is-show { transform: translate(-50%, 0); }
}

/* ═══ 送禮流程（gift-modal，沿用 rent-modal / rent-confirm 樣式）═══ */
/* 送禮流程沿用 rent-modal 預設 400px；「送給好友」＋「送出禮物」較寬 560px
   （2026-07-23 Dolly；同一子流程等寬不跳，同 rent-topup 的 560）。
   連寫 class 抬權重：要贏 ds.css（載入序在後）data-size="sm" 的 max-width:400。
   ⚠️ gate ≥769：高權重寬度規則會反蓋手機 m-full 滿版（m-full 變體老雷，0728 實錘） */
@media (min-width: 769px) {
  :is(.gift-friends, .gift-send).ds-modal[data-size="sm"] .ds-modal__panel {
    width: 560px;
    max-width: min(560px, calc(100vw - 48px));
  }
}
/* step 1 無「下一步」鈕：點畫質即進 step 2（2026-07-23 Dolly，2.0 樣式）。
   開窗 autofocus 落在 ✕ → 不畫橘框（資訊型彈窗慣例，同分享／綜合指數彈窗）。 */
.gift-modal .ds-modal__close:focus-visible { outline: none; }

/* 全集／單集切換：只在影集出現（body[data-t-form=series]），純 CSS 顯隱。
   glass-tabs 是 inline-flex → 用 display 切、非 [hidden]（避免被 inline-flex 蓋）。 */
.gift-scope-toggle { display: none; margin: 16px 0 0; }
body[data-t-form="series"] .gift-scope-toggle { display: inline-flex; }

/* 單集集數 dropdown（沿用 rent-pack 展開列語言：列＋chevron＋浮層清單）。
   顯隱由 gift-flow 隨 scope 切（.btn/inline 無關，用 [hidden] 即可，本身是 div/block）。 */
.gift-ep { position: relative; margin-top: 16px; }
.gift-ep[hidden] { display: none; }
.gift-ep__row {
  display: flex;
  align-items: center;
  width: 100%;
  height: 56px;
  padding: 0 24px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--white-alpha-8);
  cursor: pointer;
  transition: background 150ms var(--ease-out);
}
.gift-ep__row:hover { background: var(--white-alpha-12, rgba(255, 255, 255, 0.12)); }
.gift-ep__label {
  flex: 1;
  text-align: left;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-primary);
}
.gift-ep__chev {
  width: 9px;
  height: 9px;
  flex: none;
  border-right: 2px solid var(--text-tertiary);
  border-bottom: 2px solid var(--text-tertiary);
  transform: rotate(45deg) translateY(-2px);
  transition: transform 150ms var(--ease-out);
}
.gift-ep__row[aria-expanded="true"] .gift-ep__chev { transform: rotate(-135deg) translateY(-2px); }
/* 展開清單＝浮層蓋在下方畫質列上（同 rent-pack overlay），10 集可捲 */
.gift-ep__list {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  right: 0;
  z-index: 6;
  max-height: 244px;
  overflow-y: auto;
  border-radius: var(--radius-md);
  background: var(--bg-tertiary);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
.gift-ep__list[hidden] { display: none; }
.gift-ep__opt {
  display: block;
  width: 100%;
  height: 48px;
  padding: 0 24px;
  border: none;
  background: none;
  text-align: left;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-primary);
  cursor: pointer;
  transition: background 150ms var(--ease-out);
}
.gift-ep__opt:hover { background: var(--white-alpha-8); }
.gift-ep__opt.is-selected { color: var(--brand-500); }

/* ═══ 短劇送禮 step 1：贈送方案卡（單集／全劇集，無畫質）。沿用 rent-modal__opt
   玻璃卡，label 左／價右；單集卡多集數 dropdown（沿用 .gift-ep__list/__opt/__chev）。═══ */
.sgift-single { position: relative; }
.sgift-opt__label {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-primary);
}
.sgift-opt__right { display: inline-flex; align-items: center; gap: 16px; }
.sgift-opt__chev { cursor: pointer; }
.sgift-opt__chev[aria-expanded="true"] { transform: rotate(-135deg) translateY(-2px); }
/* 促銷提示（贈送全集享更高 CP 值）：品牌橘小字 */
.sgift-cp {
  margin: 20px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--brand-500);
}

/* step 2 確認贈送：不可退款註（灰）＋次要膠囊鈕（取消／前往我的媒體庫） */
.gift-confirm__note {
  margin: 20px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.8;
  color: var(--text-tertiary);
}
.gift-confirm__note[hidden] { display: none; }
/* 次要鈕排版（純 layout，視覺走 .btn modifier：媒體庫=ghost、取消=quiet） */
.gift-confirm__btn { width: 100%; margin-top: 12px; }
/* .btn 是 inline-flex → 會蓋掉 [hidden] 屬性；補這條才真的收起（正常變體無「前往媒體庫」） */
.gift-confirm__btn[hidden] { display: none; }

/* ═══ 送禮 step 4：禮物購買完成（gift-success）═══ */
/* head：禮物圖示＋標題＋✕ 同一排（✕ 靠右） */
.gift-success__head {
  display: flex;
  align-items: center;
  gap: 12px;
}
.gift-success .ds-modal__close { position: static; flex: 0 0 auto; margin-left: auto; }
.gift-success .ds-modal__title { margin: 0; padding: 0; }
/* 開窗 autofocus 落在 ✕ → 不畫橘框（同資訊型彈窗慣例） */
.gift-success :is(button, a):focus-visible { outline: none; }
/* 雙 class 抬權重：贏 ds.css（載入序在後）的 .ds-icon{width:1em}，否則 icon 只有 1em */
.gift-success__head-ico.ds-icon {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  background-color: var(--text-primary);
}
/* 爆米花使用／剩餘（灰標＋白數字） */
.gift-success__stat {
  margin: 8px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.gift-success__stat:first-of-type { margin-top: 24px; }
.gift-success__stat span { color: var(--text-primary); font-weight: var(--fw-bold); }
/* 數字後的單顆爆米花 icon（沿用 .rent-modal__coin mask，改成 inline 對齊文字） */
.gift-success__coin { display: inline-block; width: 15px; height: 15px; margin-left: 3px; vertical-align: middle; }
.gift-success__item { margin-top: 20px; }
/* 成功頁卡片沒有「總共」列 → 去掉 __film 用來隔總共的底線與下留白（免孤兒線） */
.gift-success__item .rent-confirm__film { border-bottom: none; padding-bottom: 0; }
/* 說明文字（灰，關鍵字白 strong） */
.gift-success__desc {
  margin: 20px 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.8;
  color: var(--text-tertiary);
}
.gift-success__desc strong { color: var(--text-primary); font-weight: var(--fw-bold); }
/* QR：白底圓角方塊置中 */
.gift-success__qr {
  display: flex;
  justify-content: center;
  margin: 24px 0;
}
.gift-success__qr img {
  width: 200px;
  height: 200px;
  border-radius: var(--radius-md);
  background: #fff;
  padding: 12px;
}
.gift-success__link { margin-top: 0; }
/* 「或」分隔線 */
.gift-success__or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 24px 0;
  color: var(--text-tertiary);
  font-size: var(--fs-body-sm);
}
.gift-success__or::before,
.gift-success__or::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--border-subtle);
}
.gift-success__send { width: 100%; }

/* ═══ 送禮 step 5：送給好友（gift-friends）═══ */
/* head：返回箭頭＋標題＋✕（✕ 靠右） */
.gift-friends__head {
  display: flex;
  align-items: center;
  gap: 12px;
}
/* flex head 內的 ✕ 收成靜態、推到最右（涵蓋 friends／send／confirm 三窗） */
.gift-friends__head .ds-modal__close { position: static; flex: 0 0 auto; margin-left: auto; }
.gift-friends__head .ds-modal__title { margin: 0; padding: 0; }
.gift-friends :is(button, a):focus-visible, .gift-confirm :is(button, a):focus-visible, .gift-send :is(button, a):focus-visible { outline: none; }
/* 返回＝與 .ds-modal__close 同款圓底玻璃鈕（Dolly 2026-07-24），icon 換左箭頭 */
.gift-friends__back {
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--glass-bg-default);
  color: var(--text-secondary);
  cursor: pointer;
  transition: background 150ms var(--ease-out), color 150ms var(--ease-out);
}
.gift-friends__back::before {
  content: '';
  width: 20px;
  height: 20px;
  background: currentColor;
  -webkit-mask: url('assets/icons/chevron_left.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_left.svg') center / contain no-repeat;
}
.gift-friends__back:hover { background: var(--glass-bg-hover); color: var(--text-primary); }
/* 作品卡：無「總共」列 → 去掉分隔底線 */
.gift-friends__item { margin-top: 20px; }
.gift-friends__item .rent-confirm__film { border-bottom: none; padding-bottom: 0; }
/* 留言給好友 */
.gift-friends__msg {
  display: block;
  width: 100%;
  margin-top: 16px;
  padding: 16px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: 1.7;
  resize: vertical;
}
.gift-friends__msg::placeholder { color: var(--text-tertiary); }
.gift-friends__msg:focus { outline: 1px solid var(--white-alpha-24, rgba(255, 255, 255, 0.24)); }
/* 搜尋好友（右側放大鏡） */
.gift-friends__search { position: relative; margin-top: 16px; }
.gift-friends__searchinput {
  width: 100%;
  padding: 14px 48px 14px 20px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-4);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
}
.gift-friends__searchinput::placeholder { color: var(--text-tertiary); }
.gift-friends__searchinput:focus { outline: 1px solid var(--white-alpha-24, rgba(255, 255, 255, 0.24)); }
.gift-friends__searchico {
  position: absolute;
  right: 18px;
  top: 50%;
  width: 20px;
  height: 20px;
  transform: translateY(-50%);
  pointer-events: none;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/search.svg') center / contain no-repeat;
          mask: url('assets/icons/search.svg') center / contain no-repeat;
}
/* 分頁（沿用 2.0 glass-tabs；只改外距） */
.gift-friends__tabs { margin: 20px 0 12px; }
/* 好友列表（可捲動） */
/* 背景在「列」不在容器（Dolly 0728 照 1.0 稿）：搜尋濾剩幾人時卡片貼合內容、
   下方不留整片空底（手機容器 flex 撐滿只當捲動區）。容器留 radius 供捲動裁切 */
.gift-friends__list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-radius: var(--radius-md);
  max-height: 320px;
  overflow-y: auto;
}
/* 手機滿版：好友列表長到貼底（Dolly 0728）——panel 轉 flex 欄、列表吃剩餘高；
   桌機浮卡維持 320 內捲 */
@media (max-width: 768px) {
  .gift-friends.ds-modal--m-full .ds-modal__panel { display: flex; flex-direction: column; }
  /* flex 子項預設會被壓縮（分頁列 32→25 上下被裁＝0728 實錘）→ 一律不縮 */
  .gift-friends.ds-modal--m-full .ds-modal__panel > * { flex: 0 0 auto; }
  .gift-friends.ds-modal--m-full .gift-friends__list { flex: 1 1 auto; max-height: none; min-height: 200px; }
  /* 頁首＝實色同 panel 背景、下距拉近（去掉頁首自身 16，留卡片自身 20；Dolly 0728 二輪）。
     .ds-modal 疊權重（0,4,0）：共用頁首規則與 is-scrolled 變體在檔案後段同權重 */
  .gift-friends.ds-modal.ds-modal--m-full .gift-friends__head {
    background: var(--bg-secondary);
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    margin-bottom: 0;
  }
  /* 頁首→影片卡再拉近（20→12；Dolly 0728 三輪） */
  .gift-friends.ds-modal--m-full .gift-friends__item { margin-top: 12px; }
}
.gift-friend {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  background: var(--white-alpha-4);
}
/* 分隔線＝兩個「可見」列相鄰才畫（過濾用 [hidden] 藏列，:last-child 追不到）；
   濾剩一人＝零分隔線。最後一個可見列補下圓角（手機容器撐滿貼底，裁不到下緣） */
.gift-friend:not([hidden]) ~ .gift-friend:not([hidden]) { border-top: 1px solid var(--white-alpha-8); }
.gift-friend:not([hidden]):not(:has(~ .gift-friend:not([hidden]))) {
  border-bottom-left-radius: var(--radius-md);
  border-bottom-right-radius: var(--radius-md);
}
/* avatar.svg 是雙色完整圖（深底圓＋人像）→ 用 <img> 直出，勿 mask（會壓成實心圓塊） */
.gift-friend__avatar {
  flex: 0 0 auto;
  display: block;
  width: 40px;
  height: 40px;
  border-radius: 50%;
}
.gift-friend__info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.gift-friend__name {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.gift-friend__mail {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.gift-friend__send {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  cursor: pointer;
  background-color: var(--text-primary);
  -webkit-mask: url('assets/icons/send.svg') center / contain no-repeat;
          mask: url('assets/icons/send.svg') center / contain no-repeat;
}
.gift-friend__send:hover:not(:disabled) { background-color: var(--brand-500); }
/* 清除搜尋（有輸入才出現；圓形淡底＋✕） */
.gift-friends__clear {
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--white-alpha-12, rgba(255, 255, 255, 0.12));
  cursor: pointer;
}
.gift-friends__clear[hidden] { display: none; }
.gift-friends__clear::before,
.gift-friends__clear::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 11px;
  height: 1.5px;
  background: var(--text-secondary);
}
.gift-friends__clear::before { transform: translate(-50%, -50%) rotate(45deg); }
.gift-friends__clear::after { transform: translate(-50%, -50%) rotate(-45deg); }
.gift-friends__searchico[hidden] { display: none; }
/* 收禮者情境：已擁有該片 → 紅字提示＋送出鍵停用 */
.gift-friend__err {
  display: none;
  margin-top: 2px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--error-500);
}
.gift-friend[data-friend-state="owned"] .gift-friend__err { display: block; }
.gift-friend__send:disabled { opacity: 0.3; cursor: not-allowed; }
/* 搜尋無結果 */
.gift-friends__empty {
  margin: 16px 0 0;
  text-align: center;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.gift-friends__empty[hidden] { display: none; }
.gift-friend[hidden] { display: none; }

/* ═══ 送禮 step 6：送出禮物（gift-send）═══ */
.gift-send__label {
  margin: 24px 0 8px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
/* 收禮者列：沿用好友列樣式，但為單張卡片（有底、無分隔線、不可點） */
.gift-send__who {
  border-bottom: none;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
}
.gift-send__whoico { pointer-events: none; }
/* 地區限制警語＝語意警告色（--warning-500 淡底＋黃字，同 .wp-envwarn 警告框慣例） */
.gift-send__warn {
  margin: 12px 0 0;
  padding: 12px 16px;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--warning-500) 12%, transparent);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.7;
  color: var(--warning-500);
}
.gift-send__warn[hidden] { display: none; }
/* 留言（step 5 輸入內容；空則整塊收起） */
.gift-send__msg {
  margin: 16px 0 0;
  padding: 16px;
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: 1.7;
  color: var(--text-primary);
  white-space: pre-wrap;
}
.gift-send__msg[hidden] { display: none; }

/* ═══ 短劇爆米花不足（#shortTopup）：手機轉全頁（Figma Short mobile 0725）═══
   稿＝滿版頁：← 返回列＋置中標題（無 ✕）、內容縱向捲動。桌機維持一般彈窗
   （← 與 ✕ 並存，同 gift confirm 的 head 模式）。
   ⚠️ 滿版本體＝DS 官方變體 .ds-modal--m-full（掛在 modal 根，ds.css）——
   別在這裡手刻 panel 100vw/100dvh：ds.css 載入序在後，容器 padding 蓋不掉會四周留邊。 */
@media (max-width: 768px) {
  /* 選包列窄幅改兩行（名稱上／顆數下、價格靠右不換行）——單行擠不下 390 */
  .short-topup .rent-pack__row,
  .short-topup .rent-pack__opt {
    display: grid;
    grid-template-columns: auto 1fr auto auto;
    grid-template-areas: "ico name price chev" "ico count price chev";
    align-items: center;
    row-gap: 2px;
    height: auto;
    min-height: 56px;
    padding: 10px 16px;
  }
  .short-topup .rent-pack__ico { grid-area: ico; }
  .short-topup .rent-pack__name { grid-area: name; }
  .short-topup .rent-pack__count { grid-area: count; }
  .short-topup .rent-pack__price { grid-area: price; white-space: nowrap; }
  .short-topup .rent-pack__chev { grid-area: chev; }
  /* ── 頁首照 m-pagebar 慣例（Dolly 0727）：貼頂吸住＋毛玻璃底、裸 ← 返回
     （墨水離螢幕邊 20px，同 0715「拆盒」法）、標題 18px 置中、✕ 收起、
     捲動 >8px 出 hairline（.is-scrolled 由 ds.js 聽 panel 捲動掛）。
     ⚠️ sticky 會被 clamp 在父層 content box 內＝縱向負 margin 拉不出 panel
     的 padding-top → 手機時 panel 頂 padding 歸零（疊 class 0,4,0 蓋過
     ds.css m-full 的 0,3,0），head 只用「橫向」負 margin 破出側邊。 ── */
  .short-topup.ds-modal--m-full.ds-modal--m-full .ds-modal__panel,
  .gift-modal.ds-modal--m-full.ds-modal--m-full:has(.gift-friends__head) .ds-modal__panel { padding-top: 0; }
  /* 頁首規則共用 scope：.short-topup（topup）＋滿版 gift 窗（送給好友等，Dolly 0728） */
  .short-topup .gift-friends__head,
  .gift-modal.ds-modal--m-full .gift-friends__head {
    position: sticky;
    top: 0;
    z-index: 5;
    justify-content: flex-start;
    box-sizing: border-box;
    min-height: calc(56px + env(safe-area-inset-top, 0px));
    margin: 0 calc(-1 * var(--space-4)) var(--space-4);
    padding: calc(16px + env(safe-area-inset-top, 0px)) 16px 8px;
    background: rgba(10, 10, 10, 0.78);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid transparent;
    transition: background 200ms var(--ease-out), border-color 200ms var(--ease-out);
  }
  .short-topup .gift-friends__head.is-scrolled,
  .gift-modal.ds-modal--m-full .gift-friends__head.is-scrolled {
    background: rgba(10, 10, 10, 0.92);
    border-bottom-color: var(--border-default);
  }
  /* 返回「拆盒」＝裸長箭頭：玻璃圓底去掉、盒收成 glyph＋8px 熱區；
     墨水離邊 20 ＝ 16(頁首 pad) −7(margin) ＋8(鈕 pad) ＋~3(arrow_left 墨水左留白) */
  .short-topup .gift-friends__back,
  .short-topup .gift-friends__back:hover,
  .gift-modal.ds-modal--m-full .gift-friends__back,
  .gift-modal.ds-modal--m-full .gift-friends__back:hover {
    width: auto;
    height: auto;
    padding: 8px;
    margin: -8px;
    margin-left: -7px;
    background: none;
    color: var(--text-primary);
  }
  .short-topup .gift-friends__back::before,
  .gift-modal.ds-modal--m-full .gift-friends__back::before {
    width: 24px;
    height: 24px;
    -webkit-mask-image: url('assets/icons/arrow_left.svg');
            mask-image: url('assets/icons/arrow_left.svg');
  }
  .short-topup .gift-friends__head .ds-modal__title,
  .gift-modal.ds-modal--m-full .gift-friends__head .ds-modal__title {
    position: absolute;
    left: 50%;
    transform: translateX(-50%);
    white-space: nowrap;
    font-family: var(--font-cjk-text);
    font-size: 18px;              /* = .m-pagebar__title 手機子頁頂欄標題 */
  }
  .short-topup .gift-friends__head .ds-modal__close,
  .gift-modal.ds-modal--m-full .gift-friends__head .ds-modal__close { display: none; }
}


/* ── hand-extracted single-source components ── */
/* CROWDFUNDING / PRESALE — .cocreation-grid + .cocreation-card (+states/--overlay). [COMPONENT] */
.cocreation-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 24px;
}

.cocreation-card {
  display: flex;
  flex-direction: column;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
  transition: transform 250ms ease, border-color 250ms ease;
  text-decoration: none;
  color: inherit;
}

.cocreation-card:hover {
  transform: translateY(-4px);
  border-color: var(--border-default);
  box-shadow: 0 8px 30px rgba(0, 0, 0, 0.35);
}

.cocreation-card__poster {
  position: relative;
  display: block;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--neutral-900);
}

.cocreation-card__poster img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.cocreation-card__body {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 22px;
  flex: 1;
}

.cocreation-card__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 22px;
  line-height: 1.3;
  color: var(--text-primary);
  margin: 0;
}

.cocreation-card__desc {
  font-family: var(--font-cjk-text);
  /* Figma spec: Body/Small/zh · Regular · 14/23 · Text/Tertiary */
  font-weight: var(--fw-regular);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--text-tertiary);
  margin: 0;
}

.cocreation-card__metrics {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.cocreation-card__amount {
  font-family: var(--font-cjk-text);
  font-size: 18px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}

/* Funded-percent header — the hero figure above the bar (Kickstarter /
   Buy Me a Coffee convention). Promoted from refine-engagement (.rf-cocreate)
   to the component so it renders identically on every page the card appears
   (home 影視/音樂共創 sections + cocreate.html), keyed to the class = single source. */
.rf-fund {
  display: flex;
  align-items: baseline;
  gap: 8px;
}
.rf-fund__pct {
  font-family: var(--font-latin);
  font-size: 30px;
  line-height: 1;
  font-weight: var(--fw-bold);
  /* white until 100% (round-4 grammar: numbers white, colour lives in the
     bar); --done green below keeps the 目標達成 moment */
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.5px;
}
.rf-fund__pct small {
  font-size: 17px;
  font-weight: var(--fw-bold);
  margin-left: 1px;
}
.rf-fund--done .rf-fund__pct { color: var(--success-500); }
/* Unified fund block (2026-07-16 team feedback 2a): every metric-bearing card
   shows the coloured pct figure — ended/cancelled go neutral grey (not a
   status colour, keeps the 3-colour rule) alongside their muted bar. */
.rf-fund--muted .rf-fund__pct { color: var(--text-tertiary); }
.rf-fund__label {
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
}

.cocreation-card__bar {
  height: 3px;
  background: var(--white-alpha-8);
  border-radius: var(--radius-pill);
  overflow: hidden;
}

.cocreation-card__bar-fill {
  height: 100%;
  background: linear-gradient(90deg, var(--yellow-600), var(--yellow-400));
  box-shadow: 0 0 12px rgba(var(--brand-rgb), 0.35);
  border-radius: var(--radius-pill);
  transition: width 1s ease;
}

/* Goal reached → the fill goes green (matches the 目標達成 badge--success).
   Keyed off the existing data-bar-fill="100" state, authored once. The
   :not(--muted) keeps deliberately-greyed 已結束/未達標 bars grey even at 100%. */
.cocreation-card__bar-fill[data-bar-fill="100"]:not(.cocreation-card__bar-fill--muted) {
  background: var(--success-500);
  box-shadow: 0 0 8px rgba(34, 197, 94, 0.40);
}

.cocreation-card__bar-fill--muted {
  background: var(--white-alpha-24);
  box-shadow: none;
}

.cocreation-card__meta-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-family: var(--font-cjk-text);
  font-size: 12.5px;
  font-weight: var(--fw-regular);
  color: var(--text-tertiary);
}
.cocreation-card__meta-row span:first-child {
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

/* ── Crowdfunding card · OVERLAY variant ───────────────────────────────────
   Opt-in via .cocreation-grid--overlay. The poster shows at its NATURAL ratio
   (no crop) and the funding block (badge → title → desc → amount → bar → meta)
   sits in a .media-glass panel that rides up onto the poster's lower edge and
   flows onto the card surface below — same text-on-photo treatment as the
   community posts, and it never clips regardless of poster shape. The body
   carries .media-glass (added in markup). ──────────────────────────────────── */
.cocreation-grid--overlay .cocreation-card {
  position: relative;                 /* natural height: poster + funding panel below */
}
.cocreation-grid--overlay .cocreation-card__poster { position: relative; aspect-ratio: auto; line-height: 0; }
.cocreation-grid--overlay .cocreation-card__poster img { position: relative; width: 100%; height: auto; }
/* Funding block FLOWS (not absolute → never clips). Negative margin lifts it
   onto the poster's lower edge for the glass dissolve; the dense data simply
   continues onto the card surface below when the poster is short/landscape. */
.cocreation-grid--overlay .cocreation-card__body {
  position: relative;
  z-index: 1;
  margin-top: -110px;
  padding: 64px 20px 18px;            /* top ~64px = the blur dissolve zone over the poster */
  flex: 0 0 auto;
  gap: 7px;
}
.cocreation-grid--overlay .cocreation-card__desc {
  display: -webkit-box;
  -webkit-line-clamp: 1;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
/* Text → light on the glass. */
.cocreation-grid--overlay .cocreation-card__title { color: #fff; font-size: 18px; }
.cocreation-grid--overlay .cocreation-card__amount { color: #fff; }
.cocreation-grid--overlay .cocreation-card__desc,
.cocreation-grid--overlay .cocreation-card__meta-row { color: rgba(255, 255, 255, 0.85); }
/* Progress track needs more presence over a photo. */
.cocreation-grid--overlay .cocreation-card__bar { background: rgba(255, 255, 255, 0.28); }

/* Generic badge — yellow tinted pill (used inside cards / labels)
   Modifier .badge--success → green tinted + ✓ icon (for 目標達成 / 已完成 狀態) */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: flex-start;
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  background: rgba(var(--brand-rgb), 0.12);
  border: 1px solid rgba(var(--brand-rgb), 0.25);
  color: var(--yellow-500);
  font-family: var(--font-cjk-text);
  font-size: 11px;
  font-weight: var(--fw-semibold);
  letter-spacing: 0.05em;
}

/* 目標達成 / 已完成. --success-500 (#22c55e) is documented (see tokens.css
   score-ink note) to read too dark for small text on near-black, which made
   this badge invisible on the dark card. Use the brighter system green and
   a stronger tint/border so the pill reads clearly. */
.badge--success {
  background: rgba(48, 209, 88, 0.16);
  border-color: rgba(48, 209, 88, 0.42);
  color: #30d158;
}

.badge--urgent {
  background: rgba(239, 68, 68, 0.14);
  border-color: rgba(239, 68, 68, 0.32);
  color: var(--error-500);
}

.badge--ended {
  background: rgba(255, 255, 255, 0.08);
  border-color: rgba(255, 255, 255, 0.16);
  color: var(--text-tertiary);
}

/* ─────────────────────────────────────────
   HERO CAROUSEL
   Full-bleed image-led banner. Each slide stacks:
     <bg image>  +  <darken gradients>  +  <foreground content bottom-left>
   Auto-advance, pause-on-hover, dots, prev/next.

   ─── BANNER UPLOAD SPEC (for backend / content team) ───
     Aspect ratio:  3 : 1  (wide cinematic, balances height + minimal cropping)
     Master size:   2400 × 800  (retina-ready up to 1200px CSS at 2× DPR)
     Format:        JPG (≤ 700 KB).  WebP (≤ 500 KB) optional later for ~30% size win.
     Subject safe-zone:
       central 1080 × 800 area must contain the hero subject so the
       browser's center-crop survives portrait-phone viewports without
       cutting off faces / titles.
     If a separate mobile asset is supported later:
       1080 × 1350  (4:5 portrait)  — same content, recropped.
   ───────────────────────────────────────────────────────── */

.hero-carousel {
  position: relative;
  width: 100%;
  /* Aspect-aware height: tracks viewport width to match image's 3:1 ratio.
     - On 1680–2400px viewports: hero height ≈ viewport_width / 3 → image fits with no vertical crop.
     - Min 560px on narrow viewports (≤ 1680 wide) — slight horizontal crop, subject in safe zone survives.
     - Max 800px on ultra-wide (≥ 2400 wide) — image native height, no upscale.
     - Beyond 2400 CSS px (true 4K+ at 1× DPR) image still crops vertically;
       fix would be a larger source asset (e.g. 4800×1600). */
  height: clamp(560px, 33.33vw, 800px);
  overflow: hidden;
  background: var(--bg-primary);
}

.hero-carousel__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity 800ms ease;
}

.hero-carousel__slide.is-active {
  opacity: 1;
  pointer-events: auto;
}

.hero-carousel__image {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center 30%;
  transform: scale(1.04);
}

/* Slide 1 — constellation canvas background (replaces static image)
   Many small dots with thin connecting lines, slowly drifting; some
   dots are brand yellow with a soft halo. Drawn at devicePixelRatio
   for crisp lines. Pauses when slide is not active to save CPU.
   Layered background: warm yellow ambient near top + dark purple-blue
   radial centre + black edges → gives depth before particles draw. */
.hero-carousel__particles {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  background:
    radial-gradient(ellipse 70% 50% at 50% 25%, rgba(var(--brand-rgb), 0.06), transparent 70%),
    radial-gradient(ellipse 60% 60% at 30% 80%, rgba(80, 40, 120, 0.08), transparent 70%),
    radial-gradient(ellipse at 50% 50%, #15141c 0%, #0a0a0a 75%);
}

/* Slide 1 only — soften darken-left so the constellation shows through
   on the copy side. Darken-bottom keeps its full strength for section join. */
.hero-carousel__slide[data-slide="0"] .hero-carousel__darken-left {
  background: linear-gradient(90deg,
    rgba(0, 0, 0, 0.45) 0%,
    rgba(0, 0, 0, 0.15) 40%,
    transparent         70%);
}

.hero-carousel__darken-bottom,
.hero-carousel__darken-left {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* Bottom-up darken — fades into page bg for smooth section join.
   2026-07-16 lightened (banner art was losing detail); the 100% solid stop
   stays — it IS the section seam. */
.hero-carousel__darken-bottom {
  background: linear-gradient(180deg,
    rgba(0, 0, 0, 0.1)  0%,
    rgba(0, 0, 0, 0)    30%,
    rgba(0, 0, 0, 0.3)  70%,
    var(--bg-primary)   100%);
}

/* Left-side darken — gives copy on the bottom-left enough contrast */
.hero-carousel__darken-left {
  background: linear-gradient(90deg,
    rgba(0, 0, 0, 0.45) 0%,
    rgba(0, 0, 0, 0.25) 35%,
    transparent         60%);
}

.hero-carousel__content {
  position: relative;
  z-index: 1;
  height: 100%;
  display: flex;
  align-items: flex-end;
  /* Lift the content block up off the section join — bottom-aligned, so a
     larger bottom padding rides the whole block (eyebrow→title→sub→CTA)
     higher and keeps the CTA clear of the next zebra stripe. */
  padding-bottom: clamp(120px, 15vh, 190px);
}

.hero-carousel__copy {
  display: flex;
  flex-direction: column;
  gap: 22px;
  max-width: 880px;
}

.hero-carousel__eyebrow {
  font-family: var(--font-cjk-text);
  font-size: 12px;
  font-weight: var(--fw-medium);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand-500);
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.5);
}

.hero-carousel__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: clamp(44px, 5.4vw, 76px);
  line-height: 1.18;
  letter-spacing: -0.02em;
  color: var(--text-primary);
  margin: 0;
  text-shadow: 0 2px 24px rgba(0, 0, 0, 0.4);
  /* Default to single line (cinematic 1-line feel).
     Slides that want a 2-line title use explicit <br> — <br> overrides nowrap. */
  white-space: nowrap;
}

.hero-carousel__sub {
  font-family: var(--font-cjk-text);
  font-weight: var(--fw-regular);
  font-size: 18px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.85);
  margin: 0;
  max-width: 520px;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.4);
}

/* Meta line — used for countdown / status meta on event-style slides
   (e.g. AI 競賽：「參賽截止還有 1 天」) */
.hero-carousel__meta {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  font-family: var(--font-cjk-text);
  font-size: 16px;
  line-height: 1.7;
  color: rgba(255, 255, 255, 0.85);
  margin: 0;
  text-shadow: 0 1px 12px rgba(0, 0, 0, 0.4);
}

.hero-carousel__countdown {
  font-family: var(--font-latin);
  font-size: 24px;
  font-weight: var(--fw-bold);
  color: var(--neutral-white);
  margin: 0 6px;
  font-style: normal;
}

.hero-carousel__ctas {
  display: flex;
  gap: 12px;
  margin-top: 8px;
  flex-wrap: wrap;
}

/* Prev / next nav buttons — Artlist-style glass (matches popcorn / notification)
   Hidden by default, fade in when carousel is hovered or nav is focused. */
.hero-carousel__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  border: none;
  padding: 0;
  cursor: pointer;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  pointer-events: none;
  /* L-kit neutral glass (was a copy of the old ghost recipe) */
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  backdrop-filter: var(--lg-frost);
  -webkit-backdrop-filter: var(--lg-frost);
  transition: opacity 250ms ease, background 200ms ease;
}

.hero-carousel:hover .hero-carousel__nav,
.hero-carousel__nav:focus-visible {
  opacity: 1;
  pointer-events: auto;
}

.hero-carousel__nav:hover {
  background: linear-gradient(0deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.30) 2.45%,
    rgba(255, 255, 255, 0) 126.14%);
}

.hero-carousel__nav--prev { left: 24px; }
.hero-carousel__nav--next { right: 24px; }

.hero-carousel__nav img {
  width: 22px;
  height: 22px;
  display: block;
  filter: drop-shadow(0 1px 3px rgba(0, 0, 0, 0.5));
}

/* Dots — bottom center; active = wider yellow + glow */
.hero-carousel__dots {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 32px;
  display: flex;
  justify-content: center;
  gap: 14px;
  /* 下一個 section 以負 margin 疊上 hero 底部、且 z-index:2，會蓋住 dots；
     dots 拉到其上，導覽點在登入前才不會消失。 */
  z-index: 3;
}

.hero-carousel__dot {
  height: 4px;
  width: 16px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.3);
  cursor: pointer;
  transition: width 350ms ease, background 250ms ease, box-shadow 250ms ease;
}

.hero-carousel__dot.is-active {
  width: 36px;
  background: var(--yellow-500);
  box-shadow: 0 0 12px rgba(var(--brand-rgb), 0.55);
}

/* MEDIA-GLASS — .media-glass shared text-on-photo primitive. [COMPONENT] */
/* ════════════════════════════════════════════════════════════════════════
   .media-glass — THE canonical "text on a photo" treatment (shared primitive)
   ════════════════════════════════════════════════════════════════════════
   One frosted-glass band for any text overlaid on an image: a PROGRESSIVE
   backdrop-blur (strong at the foot, dissolving upward via a mask) + a quiet
   ink tint + a text-shadow safety net — so copy reads on ANY photo (bright,
   dark, or busy). Lifted from the news hero (the original implementation).

   USAGE: put .media-glass on the TEXT PANEL that overlays the photo. The panel
   MUST come AFTER the <img>/cover in the DOM (backdrop-filter blurs whatever is
   painted behind it). Give the panel a tall TOP padding — that empty zone is
   where the blur dissolves into the photo. Children ride above the glass
   automatically. Adopters: community --overlay, ai-mosaic tiles. (The news
   hero keeps its inline copy as the origin, pending migration to this class.)
   ──────────────────────────────────────────────────────────────────────── */
.media-glass { position: relative; isolation: isolate; }
.media-glass > * { position: relative; z-index: 1; text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45); }
.media-glass::before {              /* progressive blur, masked to fade in upward */
  content: "";
  position: absolute;
  inset: 0;
  z-index: -2;
  -webkit-backdrop-filter: blur(22px) saturate(1.35);
          backdrop-filter: blur(22px) saturate(1.35);
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.5) 30%, #000 62%);
          mask-image: linear-gradient(to bottom, transparent 0%, rgba(0, 0, 0, 0.5) 30%, #000 62%);
  pointer-events: none;
}
.media-glass::after {               /* quiet ink tint ramp */
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to bottom, transparent 0%, rgba(10, 10, 10, 0.30) 34%, rgba(10, 10, 10, 0.62) 100%);
  pointer-events: none;
}
/* No backdrop-filter (old engines): the tint alone carries legibility. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .media-glass::after {
    background: linear-gradient(to bottom, transparent 0%, rgba(10, 10, 10, 0.62) 30%, rgba(10, 10, 10, 0.88) 100%);
  }
}

/* FEED TABS (.feed-tabs / .feed-tab) — primary feed filter. [COMPONENT] */
/* Primary feed filter. ONE yellow underline SLIDES between equal-width
   segments (not a per-tab border that teleports) — the slide itself tells the
   eye where the filter went. --feed-active (0-based index) + --feed-count are
   set by ds.js on tab click; the CSS below derives the bar from them. */
.feed-tabs {
  position: relative;
  display: flex;
}
/* The single sliding underline. width = one segment; the transform moves it by
   whole segments (translateX is relative to the bar's OWN width = one segment).
   bottom:-1px seats it on the tabbar hairline, like the old per-tab border. */
.feed-tabs::after {
  content: "";
  position: absolute;
  bottom: -1px;
  left: 0;
  width: calc(100% / var(--feed-count, 4));
  height: 2px;
  background: var(--yellow-500);
  transform: translateX(calc(var(--feed-active, 0) * 100%));
  transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1);
}
/* Each tab is an equal segment, label centred within it. */
.feed-tab {
  position: relative;
  flex: 1;
  text-align: center;
  padding: 14px 6px;
  background: none;
  border: none;
  color: var(--text-tertiary);
  font-family: var(--font-cjk-display);   /* 0728 Dolly 試版：16px＋LINE Seed（≥16 合字體規範） */
  font-size: 16px;
  font-weight: var(--fw-regular);
  cursor: pointer;
  /* colour crossfade (150ms) resolves just before the slide lands, so the slide
     leads and colour confirms; press dims the label to 0.6 for 100ms on tap
     (opacity, not scale — scale would jiggle a full-width segment). */
  transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1), opacity 100ms ease-out;
}
.feed-tab:hover { color: var(--text-secondary); }
.feed-tab:active { opacity: 0.6; }
.feed-tab--active,
.feed-tab--active:hover {
  color: var(--neutral-white);   /* 0728 Dolly：橘字→白字（線維持橘） */
}
/* Reduced motion: the bar snaps to the active segment; colour + press stay
   (comprehension aids, no vestibular cost). */
@media (prefers-reduced-motion: reduce) {
  .feed-tabs::after { transition: none; }
}

/* ─────────────────────────────────────────
   STICKY FILTER PILL  (.tabstick)  +  MOBILE HEADER HIDE/SHOW
   Behaviour: this file + assets/sticky-tabs.js. Each primary tab row is wrapped in
   <div class="tabstick">. Spotlight (index) is excluded.

   POSITION: NATIVE position:sticky. The nav moves WITH the scroll and holds at the pin
   line — continuous, no teleport. (A relative→fixed swap is an INSTANT position change = a
   hard JUMP that no animation can hide; that teleport survived many attempts. Sticky is
   continuous AND behaves identically in Safari and Chrome → no "works in dev-tools, jumps on
   iPhone" bug. It self-reserves its flow box → zero CLS, no spacer.)

   THE STICKY HOST: a sticky element only sticks WITHIN its containing block (its parent). Some
   rows sit in a too-short flex row (e.g. a "nav + button" .tabs-row only as tall as the nav) →
   sticky has no room. So the controller picks a HOST per row at measure time — the .tabstick
   itself when its parent is tall, else the parent (whose own containing block is the tall
   section) — and marks it `.tabstick-host`. Everything positional keys off the host; the glass
   pill is always the descendant .glass-tabs.

   The controller only toggles a STYLE class (.is-pinned) as the host sticks; the glass material
   MORPHS in via a CSS transition. The header-follow transform (--tabs-shift) rides the header
   hide and lives ONLY on the stuck states.
   ───────────────────────────────────────── */

/* ── The sticky host (set by the controller). Natively sticky → no teleport, no spacer. */
.tabstick-host {
  position: -webkit-sticky;
  position: sticky;
  top: var(--header-height);
  z-index: 20;
}
/* DESKTOP (≥768): the snapped nav stays LEFT-aligned with the content grid (centring below is
   mobile-only — no horizontal shift on snap), and pins BELOW the full desktop header. That header
   carries the search row, so its visible height exceeds --header-height; offset by the header height
   + a small gap so it never covers the nav. Mobile keeps its top:var(--header-height) full-width pill. */
@media (min-width: 768px) {
  .tabstick-host { top: calc(var(--header-height) + var(--space-8)); }   /* header height + a small gap */
}
/* MOBILE ONLY — when the host IS the tabstick, centre the floating pill. */
@media (max-width: 767.98px) {
  .tabstick.tabstick-host { display: flex; justify-content: center; }
}

/* CONSISTENT CENTERING: when the host is a "nav + button" row (tabs-row / library-topbar /
   popcorn-navrow) the pill-nav `.tabstick` would otherwise sit LEFT. Give it a full line and centre
   the pill so EVERY snapped nav is centred (matches the centred bottom nav); the sibling button
   wraps below (these rows are flex-wrap on mobile). Scoped to `:has(> .glass-tabs)` so it targets the
   pill navs only — the shop category bar (.shop-filter) keeps its own stuck-centring + count-hide.
   Same alignment at rest AND stuck → no horizontal jump on snap. */
@media (max-width: 767.98px) {
  .tabstick-host > .tabstick:has(> .glass-tabs) {
    flex: 1 1 100%;
    min-width: 0;
    display: flex;
    justify-content: center;
  }
}

.tabstick > .glass-tabs { margin-bottom: 0; }
.tabstick--filter { margin-bottom: var(--space-8); }
@media (max-width: 767.98px) { .tabstick--filter { margin-bottom: var(--space-6); } }

/* ── The PILL — same box in flow and stuck (no width/position jump). As the host sticks, ONLY
   the glass MATERIAL resolves in, eased: backdrop-blur, depth shadow, border transition; the
   fill+sheen+rim ride pseudo OPACITY so the gradients fade rather than pop. Descendant
   selectors so it works whether the host is the .tabstick or the parent row. */
.tabstick-host .glass-tabs {
  position: relative;
  isolation: isolate;
  width: auto;                       /* hug → transparent sides (overrides phone width:100%) */
  max-width: 100%;
  margin-bottom: 0;
  padding-inline: 10px;              /* clear the pill's rounded ends — first/last tab not jammed */
  scroll-padding-inline: 10px;       /* same breathing room as a wide nav scrolls */
  border-color: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: blur(0px) saturate(1);
          backdrop-filter: blur(0px) saturate(1);
  transition: box-shadow      var(--dur-nav-morph) var(--ease-out),
              border-color    var(--dur-nav-morph) var(--ease-out),
              border-radius   var(--dur-nav-morph) var(--ease-out),
              -webkit-backdrop-filter var(--dur-nav-morph) var(--ease-out),
                      backdrop-filter var(--dur-nav-morph) var(--ease-out);
}
/* SHARED glass pseudos — the fill+sheen (::before) and spectral rim (::after) for BOTH the
   .glass-tabs pill AND the .shop-filter pill. Opacity-faded so the gradients resolve, not pop.
   Invisible (opacity 0) in flow, so adding them to .shop-filter costs nothing until it sticks. */
.tabstick-host .glass-tabs::before,
.tabstick-host .shop-filter::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: var(--glass-sheen), var(--glass-fill);
  opacity: 0;
  transition: opacity var(--dur-nav-morph) var(--ease-out);
}
.tabstick-host .glass-tabs::after,
.tabstick-host .shop-filter::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  padding: 1px;
  background: var(--glass-rim);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
          mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
          mask-composite: exclude;
  opacity: 0;
  pointer-events: none;              /* CRITICAL: this rim covers the whole pill — without this it
                                        eats every tab click (the nav links became unclickable) */
  transition: opacity var(--dur-nav-morph) var(--ease-out);
}

/* ── STUCK: the shared glass MATERIAL resolves in on BOTH pill types (.is-pinned = currently stuck;
   .is-buried = replaced earlier row). Layout-specific bits (hug/scroll) stay per-component below. */
.tabstick-host.is-pinned .glass-tabs,
.tabstick-host.is-buried .glass-tabs,
.tabstick-host.is-pinned .shop-filter,
.tabstick-host.is-buried .shop-filter {
  border-color: rgba(255, 255, 255, 0.10);   /* = --glass-border colour */
  border-radius: var(--radius-pill);          /* always a PILL when snapped — some navs (e.g.
                                                 .library-genres 探索/AI/…) have a square base radius */
  box-shadow: var(--glass-shadow);
  -webkit-backdrop-filter: var(--glass-backdrop);
          backdrop-filter: var(--glass-backdrop);
}
.tabstick-host.is-pinned .glass-tabs::before,
.tabstick-host.is-buried .glass-tabs::before,
.tabstick-host.is-pinned .shop-filter::before,
.tabstick-host.is-buried .shop-filter::before { opacity: 1; }
.tabstick-host.is-pinned .glass-tabs::after,
.tabstick-host.is-buried .glass-tabs::after,
.tabstick-host.is-pinned .shop-filter::after,
.tabstick-host.is-buried .shop-filter::after  { opacity: 0.30; }

/* header-follow transform — ONLY when stuck (an in-flow host must not ride the header). */
.tabstick-host.is-pinned,
.tabstick-host.is-buried {
  transform: translateY(var(--tabs-shift, 0px));
}

/* ── BURIED: the earlier row fades out as the later one sticks (sequential replace). */
.tabstick-host.is-buried {
  opacity: 0;
  pointer-events: none;
  transition: opacity 360ms var(--ease-out);
}

/* ── 2nd-type nav (.shop-filter — the category bar). In flow it's the normal WRAPPING text filter;
   when its host sticks it morphs (eased — same transition + SHARED glass material/pseudos as the
   type-nav above) into a glass pill — single line, scrolls. Only the LAYOUT is per-component here;
   the material is shared, so the two navs read as one component on the sequential handoff. */
.tabstick-host .shop-filter {
  position: relative;
  isolation: isolate;
  border: var(--glass-border);       /* 0.8px solid, colour transparent until stuck (mirrors .glass-tabs) */
  border-color: transparent;
  box-shadow: none;
  -webkit-backdrop-filter: blur(0px) saturate(1);
          backdrop-filter: blur(0px) saturate(1);
  transition: box-shadow      var(--dur-nav-morph) var(--ease-out),
              border-color    var(--dur-nav-morph) var(--ease-out),
              border-radius   var(--dur-nav-morph) var(--ease-out),
              -webkit-backdrop-filter var(--dur-nav-morph) var(--ease-out),
                      backdrop-filter var(--dur-nav-morph) var(--ease-out);
}
.tabstick-host.is-pinned .shop-filter,
.tabstick-host.is-buried .shop-filter {
  flex-wrap: nowrap;                 /* was wrap → single scrolling line when stuck */
  width: auto;                       /* hug → floating pill */
  max-width: 100%;
  gap: 2px;
  padding: 5px 8px;
  overflow-x: auto;
  border-radius: var(--radius-pill);
}
.tabstick-host.is-pinned .shop-filter .shop-filter__item,
.tabstick-host.is-buried .shop-filter .shop-filter__item {
  padding: 6px 12px;
  white-space: nowrap;               /* don't let a label wrap inside the pill */
  text-decoration: none;             /* drop the in-flow underline; active = pill highlight below */
  border-radius: var(--radius-pill);
}
.tabstick-host.is-pinned .shop-filter .shop-filter__item--active,
.tabstick-host.is-buried .shop-filter .shop-filter__item--active {
  background: var(--white-alpha-10, rgba(255,255,255,0.10));
  color: var(--neutral-white);
}
/* The host row is the whole .shop-toolbar (it carries the filter + a count, so a sticky element
   can only be this row). MOBILE: when stuck, centre the pill, constrain it to the viewport (it
   scrolls inside), drop the in-flow divider/spacing, and hide the count so only the floating pill
   shows. DESKTOP keeps the toolbar's natural left-aligned layout (the pill stays at the grid's left
   edge in both states — no horizontal shift on snap). */
@media (max-width: 767.98px) {
  .shop-toolbar.tabstick-host.is-pinned,
  .shop-toolbar.tabstick-host.is-buried {
    justify-content: center;
    border-bottom-color: transparent;
    margin-top: 0;
    padding-bottom: 0;
  }
  .shop-toolbar.tabstick-host.is-pinned  > .tabstick,
  .shop-toolbar.tabstick-host.is-buried  > .tabstick { min-width: 0; max-width: 100%; }
  .shop-toolbar.tabstick-host.is-pinned  .shop-count,
  .shop-toolbar.tabstick-host.is-buried  .shop-count { display: none; }
}

/* ════════════════════════════════════════════════════════════════════
   COLLAPSE-TO-DROPDOWN  (nav-collapse.js) — a SNAPPED nav wider than the screen becomes a compact
   "[active] ⌄" trigger; tap drops the full list down as a glass panel. Only active when the module
   adds .is-collapsed (snapped + overflowing), so this never affects fit navs or the rest state.
   ════════════════════════════════════════════════════════════════════ */
.nav-trigger { display: none; }                 /* only shown when collapsed */
.tabstick.is-collapsed { position: relative; }  /* anchor the dropdown */
.tabstick.is-collapsed > .glass-tabs,
.tabstick.is-collapsed > .shop-filter { display: none; }   /* hide the chip row; trigger stands in */
.tabstick.is-collapsed > .nav-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  background: var(--glass-fill);                 /* same glass material as the pill */
  border: var(--glass-border);
  box-shadow: var(--glass-shadow);
  -webkit-backdrop-filter: var(--glass-backdrop);
          backdrop-filter: var(--glass-backdrop);
  color: var(--neutral-white);
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 1.3;
  cursor: pointer;
}
.nav-trigger__label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-trigger__chev {
  width: 9px; height: 9px; flex: none; opacity: 0.7;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);     /* ⌄ */
  transition: transform var(--dur-nav-show) var(--ease-out);
}
.tabstick.is-open > .nav-trigger .nav-trigger__chev { transform: translateY(1px) rotate(-135deg); }  /* ⌃ */

/* OPEN: the real item list drops down below the trigger as a vertical glass panel (handlers intact). */
.tabstick.is-collapsed.is-open > .glass-tabs,
.tabstick.is-collapsed.is-open > .shop-filter {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 88vw;
  max-height: 60vh;
  overflow-y: auto;
  z-index: 40;
  padding: 6px;
  gap: 2px;
  border-radius: 16px;
}
.tabstick.is-collapsed.is-open .glass-tabs__item,
.tabstick.is-collapsed.is-open .shop-filter__item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 11px 16px;
  border-radius: 10px;
  white-space: nowrap;
}

/* HELD FOR PHASE 2 — inert-migration note (framework-migration foundation):
   the original "MOBILE HEADER HIDE/SHOW" block below adds properties directly
   to .header and .community-tabbar — both real classes rendered on shipping
   pages today (08-header.css / 18-community-page.css / community.html /
   mobile-community.html). In practice it is a behavioural no-op right now
   (nothing sets body.header-hidden or --tabs-shift, since assets/sticky-tabs.js
   is not referenced by any page — confirmed zero HTML references), but it does
   land a permanent `will-change: transform` + new transition timing on a
   pre-existing element every time a real page is viewed at <768px, which is a
   real (if currently invisible) change to those pages' render behaviour. Held
   out rather than migrated so nothing about .header/.community-tabbar changes
   until sticky-tabs.js is actually wired onto a page in Phase 2.

@media (max-width: 767.98px) {
  .header {
    transition: transform var(--dur-nav-show) var(--ease-out),
                opacity   var(--dur-nav-show) var(--ease-out);
  }
  body.header-hidden .header {
    transform: translateY(-100%);
    opacity: 0;
    transition: transform var(--dur-nav-hide) var(--ease-emphasized),
                opacity   var(--dur-nav-hide) var(--ease-emphasized);
  }

  body.header-hidden .tabstick-host.is-pinned,
  body.header-hidden .tabstick-host.is-buried,
  body.header-hidden .community-tabbar {
    transition: transform var(--dur-nav-hide) var(--ease-emphasized);
  }
  .community-tabbar {
    transform: translateY(var(--tabs-shift, 0px));
    will-change: transform;
    transition: transform var(--dur-nav-show) var(--ease-out);
  }
}
*/

/* ── Reduced motion / snap: the material resolves instantly, no eased morph, no drift.
   .header / .community-tabbar entries dropped from these two selector lists — they only
   need reduced-motion coverage once the HELD block above is actually wired (Phase 2). */
@media (prefers-reduced-motion: reduce) {
  .tabstick-host .glass-tabs,
  .tabstick-host .glass-tabs::before,
  .tabstick-host .glass-tabs::after,
  .tabstick-host.is-pinned,
  .tabstick-host.is-buried { transition: none !important; }
}
body.motion-snap .tabstick-host .glass-tabs,
body.motion-snap .tabstick-host .glass-tabs::before,
body.motion-snap .tabstick-host .glass-tabs::after,
body.motion-snap .tabstick-host.is-pinned,
body.motion-snap .tabstick-host.is-buried { transition: none !important; }

/* BADGE SHELF (.badge-shelf / .badge-medal). [COMPONENT] */
/* ───────────────────────────────────────────
   BADGE SHELF  (.badge-shelf / .badge-medal)
   A trophy row for 我的片庫. Each medal = a circular disc (brand-gold when
   earned, muted when locked) + label + optional unlock hint. Scrolls
   horizontally like the film rails; no own motion (reveal handled by the
   page's [data-reveal-stack]). Reuses the masked-icon (--icon) convention.
   ─────────────────────────────────────────── */
.badge-shelf {
  display: flex;
  gap: 28px;
  overflow-x: auto;
  scrollbar-width: none;
  padding: 4px 2px 8px;
  scroll-snap-type: x proximity;
}
.badge-shelf::-webkit-scrollbar { display: none; }

.badge-medal {
  flex: 0 0 auto;
  width: 104px;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  scroll-snap-align: start;
}
.badge-medal__disc {
  width: 84px;
  height: 84px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: radial-gradient(circle at 50% 28%,
    rgba(255, 207, 92, 0.98), var(--yellow-500) 58%, #c98f00 100%);
  border: 1px solid rgba(255, 255, 255, 0.16);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, 0.45),
    inset 0 -3px 8px rgba(0, 0, 0, 0.28),
    0 6px 18px rgba(var(--brand-rgb), 0.18);
}
.badge-medal__icon {
  width: 38px;
  height: 38px;
  background-color: var(--yellow-ink);
  -webkit-mask: var(--icon) center / contain no-repeat;
          mask: var(--icon) center / contain no-repeat;
}
.badge-medal__label {
  margin-top: 12px;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.badge-medal__hint {
  margin-top: 3px;
  font-size: 11px;
  line-height: 1.4;
  color: var(--text-tertiary);
}
/* Locked: greyed disc, no glow, dimmed glyph + label. */
.badge-medal--locked .badge-medal__disc {
  background: rgba(255, 255, 255, 0.05);
  border-color: rgba(255, 255, 255, 0.08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
}
.badge-medal--locked .badge-medal__icon { background-color: rgba(255, 255, 255, 0.26); }
.badge-medal--locked .badge-medal__label { color: var(--text-tertiary); }

/* SECTION-EYEBROW TAG (.section-eyebrow--tag) — category pill. [COMPONENT] */
/* ───────────────────────────────────────────
   SECTION-EYEBROW TAG  (.section-eyebrow--tag)
   Turns a section eyebrow into a colour-coded category PILL. The COLOUR
   encodes the content vertical (影視 / 音樂 / 原力), so future labels like
   「影視 - 共創項目」 vs 「音樂 - 共創項目」 read as different families at a
   glance. Solid saturated fill + white text (every variant WCAG AA) stays
   legible on BOTH zebra stripes (yellow + black); a hairline keeps the pill
   edge on dark. Add ONE vertical modifier; default (none) = neutral pill.
   To add a vertical: one line in the colour-code list below.
   ─────────────────────────────────────────── */
.section-eyebrow--tag {
  /* A colour pill is a signal, never a faded label — beat the home-page
     eyebrow dim in refine-home.css. */
  opacity: 1 !important;
  /* Hug the text and CENTRE the label both axes. inline-flex + line-height:1
     fixes the mixed CJK/Latin baseline drift that made the text sit high. */
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: fit-content;
  max-width: 100%;
  padding: 6px 12px;               /* symmetric → text optically centred */
  line-height: 1;
  border-radius: var(--radius-pill);
  letter-spacing: 0.06em;          /* it's a chip now, not a wide-tracked label */
  font-weight: var(--fw-bold);
  color: #fff;
  /* Plain flat fill — NO liquid-glass sheen (dropped the inset highlight +
     white border). A hair of transparency + a soft frost keeps it modern
     without the glossy 3D look. */
  background: color-mix(in srgb, var(--cat, var(--neutral-700)) 92%, transparent);
  backdrop-filter: blur(8px) saturate(1.05);
  -webkit-backdrop-filter: blur(8px) saturate(1.05);
}
@supports not (background: color-mix(in srgb, red, blue)) {
  .section-eyebrow--tag { background: var(--cat, var(--neutral-700)); }  /* solid fallback */
}
/* ── Vertical colour codes — set --cat. Extend by adding one line. ──
   Hues pulled from yellow's split-complementary + triadic arc (blue / violet /
   green) so they complement the yellow+black brand without reading "off-brand".
   Tuned to the mid-luminance balance point: NO solid colour can clear 3:1
   surface contrast against bright yellow AND near-black at the same time (the
   luminance bands don't overlap — it's mathematically impossible), so each is
   balanced (~2.7–4:1 on each stripe) and the WHITE label (AA 5–7:1 on every
   fill) is what carries legibility on both. Functional/cross = neutral. */
.section-eyebrow--film    { --cat: var(--cat-film); }    /* 影視    — blue   · PRIMARY */
.section-eyebrow--music   { --cat: var(--cat-music); }   /* 音樂    — red    · PRIMARY */
.section-eyebrow--origin  { --cat: var(--cat-origin); }  /* 原力/AI — orange · secondary */
.section-eyebrow--events  { --cat: var(--cat-events); }  /* 活動    — green  · secondary */
.section-eyebrow--neutral { --cat: var(--cat-neutral); } /* 功能/跨類 — charcoal */

/* ════════════════════════════════════════════════════════════════════════
   SKELETON LOADER  ·  .ds-skeleton (+ shape/width modifiers)   [COMPONENT]
   ════════════════════════════════════════════════════════════════════════
   The ONE skeleton primitive. Replaces 4 fragmented, page-keyed copies
   (.rf-skel / .m-skeleton / refine-engagement / refine-library shimmers).
   Pure-CSS shimmer (animated background-position, off the main thread — NOT
   GSAP: a loading state is exactly when the main thread is busy, so JS-driven
   shimmer drops frames). background-attachment:fixed → every skeleton samples
   one shared light field and shimmers as a single surface.

   USE: give any placeholder `.ds-skeleton` + a shape + (optional) width.
   The skeleton DOM must MIRROR the real component's box → zero layout shift
   on swap. Only data-driven components get one; static (header/footer/nav/
   buttons/hero) never do — see COMPONENTS.md "skeleton cut".

   First component extracted into the per-component structure (2026-06-16).
   ════════════════════════════════════════════════════════════════════════ */
.ds-skeleton {
  background-color: var(--skeleton-surface);       /* sits back on the dark canvas */
  background-image: linear-gradient(100deg,
    transparent 40%, rgba(255, 255, 255, 0.05) 50%, transparent 60%);  /* white sheen, never brand yellow */
  background-repeat: no-repeat;
  background-size: 200% 100%;
  background-attachment: fixed;                    /* shared field → all shimmer as one */
  animation: ds-shimmer 1.4s linear infinite;      /* linear = constant motion; slow = calm */
  border-radius: 6px;
}
@keyframes ds-shimmer {                             /* background-position only — no layout/paint */
  from { background-position: 150% 0; }
  to   { background-position: -150% 0; }
}
/* shapes — match the real component's box */
.ds-skeleton--media   { width: 100%; aspect-ratio: 16 / 10; border-radius: var(--radius-md, 10px); }
.ds-skeleton--poster  { width: 100%; aspect-ratio: 2 / 3;  border-radius: var(--radius-md, 10px); }
.ds-skeleton--line    { height: 0.82em; border-radius: 4px; }
.ds-skeleton--circle  { aspect-ratio: 1; width: 40px; border-radius: 50%; }
/* line widths */
.ds-skeleton--title   { width: 84%; }
.ds-skeleton--cat     { width: 38%; }
.ds-skeleton--price   { width: 30%; }
.ds-skeleton--short   { width: 60%; }
.ds-skeleton--full    { width: 100%; }
/* card-shaped wrappers (mirror a card column / a row) */
.ds-skeleton-card { display: flex; flex-direction: column; gap: 10px; }
.ds-skeleton-row  { display: flex; align-items: center; gap: 12px; }
.ds-skeleton-row > .ds-skeleton--line { flex: 1; }
/* crossfade on swap (renderer sets [data-exiting] just before replacing) */
.ds-skeleton[data-exiting] { opacity: 0; transition: opacity 150ms cubic-bezier(0.23, 1, 0.32, 1); }
/* reduced motion → gentle pulse (a frozen grey block reads as broken) */
@media (prefers-reduced-motion: reduce) {
  .ds-skeleton { animation: ds-skeleton-pulse 2s ease-in-out infinite; background-image: none; }
  @keyframes ds-skeleton-pulse { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; } }
}

/* ════════════════════════════════════════════════════════════════════════
   SAVED GRID · "My List" 收藏  (.rf-saved-grid)                  [COMPONENT]
   ════════════════════════════════════════════════════════════════════════
   Poster grid with remove-on-hover. Reuses .film-card; container-scoped to
   .rf-saved-grid so it travels to ANY page that drops the markup.
   Consolidated 2026-06-16 out of refine-library.css (was body.rf-library-keyed
   split-brain — verified zero-diff: .rf-saved-grid renders only inside the grid,
   so dropping the body prefix selects the identical elements).
   ════════════════════════════════════════════════════════════════════════ */
.rf-saved-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: clamp(16px, 2vw, 24px);
  margin-top: 8px;
}
.rf-saved-grid .film-card { width: auto; flex: initial; }

.rf-saved__remove {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 3;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: none;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--text-primary);
  cursor: pointer;
  opacity: 0;
  transform: scale(0.85);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out), background-color 160ms var(--ease-out);
}
.film-card:hover .rf-saved__remove,
.rf-saved__remove:focus-visible {
  opacity: 1;
  transform: scale(1);
}
.rf-saved__remove:hover { background: var(--error-500); }
.rf-saved__remove:focus-visible {
  outline: 2px solid var(--yellow-500);
  outline-offset: 2px;
}
.rf-saved__remove svg { width: 15px; height: 15px; display: block; }

/* A removed card collapses out with a quiet fade (JS toggles .is-removing) */
.rf-saved-grid .film-card.is-removing {
  opacity: 0;
  transform: scale(0.94);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
  pointer-events: none;
}

/* ─────────────────────────────────────────
   LIST BANNER — homepage "list recommendation" hero (e.g. 愛有千百種樣子)
   Left copy column + right 16:9 media, on a pure-CSS warm gradient.
   Ported from main's 42-creator-subpages bundle (the standalone homepage
   banner, separated here). Background is a multi-layer radial-gradient —
   0 KB, retina-sharp — NOT the 41 MB JPEG the older build shipped.
   ───────────────────────────────────────── */

/* Layers paint top→bottom; the linear base sits last (behind).
   Double-class beats index.html's inline `.section-bg-surface
   { background: var(--yellow-500) }` (same specificity, later source order). */
.section.section--list-banner {
  background-color: #ef6f3d;
  background-image:
    /* magenta mass, upper-left — large so its rounded BOTTOM-RIGHT edge
       sweeps across and forms the TOP of the gold swoosh */
    radial-gradient(46% 60% at 26% 26%, #e2255a 0%, #e2255a 14%, rgba(226, 37, 90, 0) 54%),
    /* deep maroon mass rising from the bottom-centre — its rounded TOP edge
       forms the BOTTOM of the swoosh; sits right of the magenta so the gap
       between them opens up-and-to-the-right (the comma curl) */
    radial-gradient(66% 54% at 62% 98%, #c01f29 0%, rgba(192, 31, 41, 0) 58%),
    /* gold hook TAIL — small, low-left: pulls the bright band down into a
       curl at the comma's point */
    radial-gradient(20% 30% at 45% 55%, #f9b042 0%, rgba(249, 176, 66, 0) 62%),
    /* gold swoosh CORE — luminous, sweeping up to the right */
    radial-gradient(50% 30% at 68% 38%, #ffc551 0%, #f7a83e 38%, rgba(247, 168, 62, 0) 66%),
    /* gold continuation into the top-right */
    radial-gradient(48% 40% at 90% 20%, #f8b948 0%, rgba(248, 185, 72, 0) 54%),
    /* pink hint, top-right */
    radial-gradient(42% 48% at 78% 1%, #e63f63 0%, rgba(230, 63, 99, 0) 50%),
    /* red-orange, bottom-left */
    radial-gradient(58% 72% at 6% 94%, #ef5535 0%, rgba(239, 85, 53, 0) 60%),
    linear-gradient(120deg, #f6a23f 0%, #ef6f3d 56%, #e54b2d 100%);
}

.list-banner {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 600px);
  align-items: center;
  gap: 64px;
}

.list-banner__copy {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  /* Stretch to the media column's height; space-between pins the title
     to the image's top edge, the CTA to its bottom edge, and floats the
     description midway between them. */
  align-self: stretch;
  justify-content: space-between;
}

.list-banner__title {
  margin: 0;  /* kill the h2 UA margin (0.83em ≈ 63px per side at 76px) */
  padding-top: var(--space-2);  /* optical nudge: title cap sits ~8px below image top */
  font-family: var(--font-cjk-display);
  font-size: var(--fs-display);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}

.list-banner__sub {
  margin: 0;  /* clear p UA margins; spacing comes from space-between */
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-lg);
  line-height: var(--lh-body-lg);
  color: var(--neutral-100);
}

/* Ink-filled CTA — yellow primary has no contrast on the warm gradient. */
.list-banner__cta {
  background: var(--yellow-ink);
  color: var(--text-primary);
  border-color: transparent;
}

.list-banner__cta:hover:not(:disabled) {
  box-shadow: 0 8px 24px rgba(26, 23, 0, 0.35);
}

.list-banner__media {
  display: block;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-xl);
  overflow: hidden;
  box-shadow: 0 24px 48px rgba(26, 23, 0, 0.18);
}

.list-banner__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 300ms ease;
}

.list-banner__media:hover img { transform: scale(1.03); }

@media (max-width: 768px) {
  .list-banner {
    grid-template-columns: 1fr;
    gap: 32px;
  }
  .list-banner__title { font-size: clamp(28px, 7vw, 36px); line-height: 1.3; }
  /* Single column: copy height is natural, so space-between collapses —
     restore fixed gaps instead. */
  .list-banner__sub { margin-top: 12px; }
  .list-banner__cta { margin-top: 24px; }
}

/* watch-party.css — Watch Party 共看派對 feature components.
   Hand-extracted single-source component (imported last in css/main.css).
   Token-driven only; no hardcoded colour/size. Styles follow component class
   (.wp-*), never a body.rf-* page key. Behaviour lives in assets/watch-party.js
   via DS.register — never inline. Build: node tools/build-css.mjs */

/* ════════════════════════════════════════════════════════════════════
   1 · DIALOG (modal)
   ════════════════════════════════════════════════════════════════════
   PROMOTED to the DS primitive `.ds-modal` (assets/ds.css). `.wp-dialog*`
   is now a legacy ALIAS styled from that single source — do NOT re-add the
   rules here. Open/close behaviour for `.wp-dialog` still lives in
   watch-party.js; new modals should use `.ds-modal` (+ data-ds-modal-open/
   -close, behaviour in ds.js). See COMPONENTS.md → Overlays.
   ════════════════════════════════════════════════════════════════════ */

/* ════════════════════════════════════════════════════════════════════
   2 · FORM FIELDS (setup form)
   ════════════════════════════════════════════════════════════════════ */
.wp-field { margin-bottom: var(--space-5); }
.wp-field > label {
  display: block;
  margin-bottom: var(--space-2);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
}
.wp-input {
  width: 100%;
  padding: var(--space-3) var(--space-4);
  font-size: var(--fs-body);
  /* <input> doesn't inherit the page font by default (falls back to Arial) —
     pull in the Proxima Nova stack so inputs match the date/time pickers */
  font-family: inherit;
  color: var(--text-primary);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
.wp-input::placeholder { color: var(--text-tertiary); }
.wp-input:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 1px; border-color: transparent; }

/* 已選影片（可多部）：一部一列（.wp-movie）堆疊＋列尾「調整影片」；
   列由 watch-party.js 依 picker 勾選狀態渲染 */
.wp-movies[hidden] { display: none; }
.wp-movies { display: grid; gap: var(--space-3); }
.wp-movies__list { display: grid; gap: var(--space-3); }
.wp-movies__edit { justify-self: start; }

/* selected movie row — [hidden] must win over display:flex */
.wp-movie[hidden] { display: none; }
.wp-movie {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
.wp-movie__poster { width: 44px; aspect-ratio: 2 / 3; object-fit: cover; border-radius: var(--radius-sm); flex: none; }
.wp-movie__text { flex: 1; min-width: 0; }
.wp-movie__t { font-size: var(--fs-body); font-weight: var(--fw-medium); }
.wp-movie__d { font-size: var(--fs-caption); color: var(--text-tertiary); margin-top: 2px; }

/* chosen-film actions: 更換 (re-select) + 移除 (clear back to dashed empty) */
.wp-movie__actions { display: flex; align-items: center; gap: var(--space-2); flex: none; }
.wp-movie__remove {
  width: 32px; height: 32px; flex: none;
  display: grid; place-items: center;
  border: 1px solid var(--border-subtle); border-radius: var(--radius-pill);
  background: transparent; color: var(--text-tertiary); cursor: pointer;
  transition: color 150ms var(--ease-out), border-color 150ms var(--ease-out);
}
.wp-movie__remove:hover { color: var(--error-500); border-color: var(--error-500); }
.wp-movie__remove::before {
  content: ''; width: 14px; height: 14px; background: currentColor;
  -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
          mask: url('assets/icons/close.svg') center / contain no-repeat;
}

/* segmented control (免費 / 自訂價格) */
.wp-seg {
  display: inline-flex;
  padding: 3px;
  background: var(--bg-primary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
}
.wp-seg__btn {
  border: 0;
  padding: var(--space-2) var(--space-5);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
  background: transparent;
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: background 150ms var(--ease-out), color 150ms var(--ease-out);
}
.wp-seg__btn.is-on { background: var(--brand-500); color: var(--brand-ink); }
.wp-field__flag { margin-top: var(--space-2); font-size: var(--fs-caption); color: var(--text-tertiary); }

/* copy row (share link / short code) */
.wp-copy { display: flex; gap: var(--space-2); align-items: stretch; }
.wp-copy .wp-input { flex: 1; }

/* ════════════════════════════════════════════════════════════════════
   3 · MANAGEMENT PAGE (scheduled event hub — host)
   ════════════════════════════════════════════════════════════════════ */
.wp-manage {
  max-width: 720px;
  margin: 0 auto;
  /* header→container 間距對齊共看派對列表頁（.wp-list top = space-2 + section--flush-top） */
  padding: var(--space-2) 0 var(--space-16);
}

/* 返回（子頁左上）— 2.0 icon-btn 圓鈕 + label；圓圈無邊框，比照 .wp-dialog__close */
.wp-back {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-6);
  text-decoration: none;
  color: var(--text-primary);
}
.wp-back__btn { width: 32px; height: 32px; padding: 7px; border-color: transparent; }
.wp-back__btn svg { width: 18px; height: 18px; }
.wp-back__label { font-size: var(--fs-body-lg); font-weight: var(--fw-bold); }
.wp-back:hover .wp-back__btn { background: var(--glass-bg-hover); }
.wp-back:active .wp-back__btn { transform: scale(0.95); }
/* 未領票（免費/付費）、已額滿、地區不符：訪客多由邀請連結進來、無來源頁可返回 → 隱藏返回鍵。
   （已領票/可進場/官方管理員才有返回：上一層＝頭像選單→共看派對） */
body[data-claim="open"] .wp-back,
body[data-claim="open-paid"] .wp-back,
body[data-claim="full"] .wp-back,
body[data-claim="blocked"] .wp-back { display: none; }

.wp-manage__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-1);
}
.wp-manage__eyebrow {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--brand-500);
}

/* status badge — scheduled / live / ended */
.wp-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 2px var(--space-3);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  border-radius: var(--radius-pill);
  border: 1px solid currentColor; /* 外框線跟隨文字顏色 */
  color: var(--text-secondary);
}
.wp-status__dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: currentColor; }
.wp-status--scheduled { color: var(--brand-500); } /* 排程中 = 品牌橘（列表與詳情頁一致） */
.wp-status--live { color: var(--success-500); }
.wp-status--ended { color: var(--text-tertiary); }
/* 放大版：放在卡片標題列右側時，字級對齊 film 頁的入場狀態徽章（fs-body-sm） */
.wp-status--lg { font-size: var(--fs-body-sm); padding: 4px var(--space-3); }

/* event hero — poster + meta */
.wp-event {
  display: flex;
  gap: var(--space-6);
  margin: 0 0 var(--space-8);
}
.wp-event__poster { width: 160px; aspect-ratio: 2 / 3; object-fit: cover; border-radius: var(--radius-lg); flex: none; box-shadow: 0 16px 40px -16px rgba(0, 0, 0, 0.7); }
.wp-event__title { font-size: var(--fs-h1); font-weight: var(--fw-bold); line-height: var(--lh-h1); }
/* 影片資訊組（標題→說明→查看連結）緊靠；角色行拉開間距 */
.wp-event__name { margin-top: var(--space-1); font-size: var(--fs-body); color: var(--text-secondary); }
.wp-event__when { margin-top: var(--space-3); font-size: var(--fs-body); color: var(--text-primary); font-weight: var(--fw-medium); }
.wp-event__host { margin-top: var(--space-5); font-size: var(--fs-body-sm); color: var(--text-tertiary); }
.wp-event__tags { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-3); }

/* countdown panel */
.wp-count {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-5) var(--space-6);
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-6);
}
.wp-count__label { font-size: var(--fs-body-sm); color: var(--text-secondary); }
.wp-count__time {
  margin-left: auto; /* 時間資訊一律置右，與下方 wp-block 的數值對齊 */
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
  color: var(--text-primary);
}
.wp-count.is-window .wp-count__time { color: var(--success-500); }

/* stat / share blocks */
.wp-blocks { display: grid; gap: var(--space-3); margin-bottom: var(--space-8); }
.wp-block {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
}
.wp-block__label { font-size: var(--fs-body-sm); color: var(--text-secondary); }
.wp-block__value { margin-left: auto; font-size: var(--fs-body); font-weight: var(--fw-medium); color: var(--text-primary); }
/* 活動類型列：付費/公開 chips 當作 value 靠右並排 */
.wp-block__value--chips { display: inline-flex; gap: var(--space-2); }

/* action zone */
.wp-actions { display: flex; flex-direction: column; gap: var(--space-3); }
.wp-actions__row { display: flex; gap: var(--space-3); }
.wp-actions__row .btn { flex: 1; justify-content: center; }
.wp-hint { text-align: center; font-size: var(--fs-caption); color: var(--text-tertiary); }

/* 開播環境警示（管理頁）：預設隱藏，body[data-env]（wp-hostenv 偵測/dev bar 覆寫）
   反應顯示。Safari＝技術限制走 error；手機＝Phase 1 時程限制走 warning。
   12% 色底配方同 .wp-tag-lock。 */
.wp-envwarn { display: none; margin-bottom: var(--space-4); padding: var(--space-4) var(--space-5); border-radius: var(--radius-md); }
body[data-env="safari"] .wp-envwarn,
body[data-env="mobile"] .wp-envwarn { display: block; }
body[data-env="safari"] .wp-envwarn {
  background: color-mix(in srgb, var(--error-500) 12%, transparent);
  color: var(--error-500);
}
body[data-env="mobile"] .wp-envwarn {
  background: color-mix(in srgb, var(--warning-500) 12%, transparent);
  color: var(--warning-500);
}
.wp-envwarn__msg { display: none; margin: 0; font-size: var(--fs-body-sm); line-height: 1.7; }
body[data-env="safari"] .wp-envwarn__msg--safari { display: block; }
body[data-env="mobile"] .wp-envwarn__msg--mobile { display: block; }
/* 主要 CTA 說明收進卡片時，補上與按鈕的間距 */
.wp-card > .wp-hint { margin-top: var(--space-3); }

/* full-width block button inside dialogs / action zones */
.wp-btn-block { width: 100%; justify-content: center; }

/* ════════════════════════════════════════════════════════════════════
   7 · WATCH ROOM (immersive — host + guest views, lifecycle states)
   ════════════════════════════════════════════════════════════════════ */
.wproom { min-height: 100vh; display: flex; flex-direction: column; background: var(--bg-primary); }

/* top bar */
.wproom__top {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
  flex: none;
}
.wproom__logo { display: inline-flex; align-items: center; text-decoration: none; flex: none; }
.wproom__logo img { height: 22px; width: auto; display: block; }
.wproom__title { font-weight: var(--fw-medium); color: var(--text-primary); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 身分 pill：樣式對齊私密場 tag（2026-07-04 定案）——12% 色底＋同色字、
   無邊框、一般字重；主持人＝品牌橘、官方管理員＝綠（見 634 行） */
.wproom__pill {
  margin-left: auto; flex: none;
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 7px 14px; line-height: 1;
  border-radius: var(--radius-pill);
  font-size: var(--fs-body-sm); color: var(--text-secondary);
  white-space: nowrap;
}
.wproom__pill--host {
  color: var(--brand-500);
  background: color-mix(in srgb, var(--brand-500) 12%, transparent);
}
@supports not (background: color-mix(in srgb, red, blue)) {
  .wproom__pill--host { background: rgba(var(--brand-rgb), 0.12); }
}
.wproom__pill__dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: currentColor; }

/* 結束派對（host-only；等候室/直播時可按，sync/已結束時隱藏） */
.wproom__end {
  flex: none; display: none;
  padding: 4px var(--space-3); border-radius: var(--radius-pill);
  border: 1px solid color-mix(in srgb, var(--error-500) 50%, transparent);
  background: transparent; color: var(--error-500);
  font-size: var(--fs-body-sm); font-weight: var(--fw-medium); cursor: pointer;
}
.wproom__end:hover { background: color-mix(in srgb, var(--error-500) 14%, transparent); }
body[data-role="host"][data-state="lobby"] .wproom__end,
body[data-role="host"][data-state="live"] .wproom__end { display: inline-flex; }

/* 離開（觀眾專屬）：中性樣式，非破壞性（離開後可再回來），別用 end 的紅色 */
.wproom__leave {
  margin-left: auto; flex: none; display: none;
  padding: 4px var(--space-3); border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: transparent; color: var(--text-secondary);
  font-size: var(--fs-body-sm); font-weight: var(--fw-medium); cursor: pointer;
}
.wproom__leave:hover { background: var(--glass-bg-hover); color: var(--text-primary); }
body[data-role="guest"] .wproom__leave { display: inline-flex; }
body[data-role="guest"][data-state="ended"] .wproom__leave { display: none; }

/* body: stage + chat */
.wproom__body { flex: 1; display: grid; grid-template-columns: minmax(0, 1fr) 360px; min-height: 0; }
.wproom__stage { position: relative; display: flex; flex-direction: column; padding: var(--space-5); min-width: 0; gap: var(--space-3); }

/* video surface */
.wproom__video {
  flex: 1; min-height: 380px;
  position: relative;
  border-radius: var(--radius-lg);
  background:
    radial-gradient(120% 80% at 50% 0%, rgba(255, 163, 63, 0.08), transparent 60%),
    var(--neutral-900);
  border: 1px solid var(--border-subtle);
  display: grid; place-items: center;
  color: var(--text-tertiary); font-size: var(--fs-body-sm);
}
.wproom__live {
  position: absolute; top: var(--space-3); left: var(--space-3);
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 4px var(--space-3); border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.5); font-size: var(--fs-caption); color: var(--text-secondary);
}
.wproom__live__dot { width: 7px; height: 7px; border-radius: var(--radius-pill); background: var(--success-500); }
/* guest resume flash — "已同步至最新進度". Shown briefly (JS adds .is-show) when a
   guest presses play after a local pause, then fades. */
.wproom__synced {
  position: absolute; left: 50%; bottom: var(--space-4); transform: translateX(-50%) translateY(6px);
  padding: 6px var(--space-4); border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.72); color: var(--text-primary); font-size: var(--fs-caption);
  white-space: nowrap; pointer-events: none;
  opacity: 0; transition: opacity 0.28s ease, transform 0.28s ease;
}
.wproom__synced.is-show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* control bar */
.wproom__ctrl { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) 0; }
/* 群組內 gap 跟外層 .wproom__ctrl 同階（12px）：左簇播放/±10/音量
   與右簇鏡頭/語音/設定節奏一致（音量鈕在 grp 外，同吃外層 gap） */
.wproom__ctrl-grp { display: flex; align-items: center; gap: var(--space-3); }
.wproom__btn {
  width: 40px; height: 40px; flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: var(--radius-pill);
  background: var(--glass-bg-default); color: var(--text-primary);
  cursor: pointer; font-size: var(--fs-body);
}
.wproom__btn:hover { background: var(--glass-bg-hover); }
/* masked control-bar/player icons — inherit currentColor so state colours work
   automatically (mic/cam .is-on → orange bg + brand-ink icon; guest lock → dim). */
.wproom__ic {
  width: 20px; height: 20px; display: block; flex: none;
  background: currentColor;
  -webkit-mask: var(--wp-ic) center / contain no-repeat;
          mask: var(--wp-ic) center / contain no-repeat;
}
/* seek buttons read optically smaller than the solid play glyph — bump them up */
.wproom__ic--seek { width: 26px; height: 26px; }
/* optical size balance in the control bar's right-hand group */
.wproom__btn--cam .wproom__ic { width: 22px; height: 22px; }
.wproom__btn--mic .wproom__ic { width: 18px; height: 18px; }
/* play ⇄ pause: state lives on the button class, mask swaps in CSS (no inline JS style) */
.wproom__ic--play { --wp-ic: url('assets/icons/media/play.svg'); }
.wproom__btn--play.is-playing .wproom__ic--play { --wp-ic: url('assets/icons/media/pause.svg'); }
/* settings trigger: host = ⚙ (換片 + 音量); guest = 🔊 (音量 only, see popover rules) */
.wproom__ic--set { --wp-ic: url('assets/icons/setting.svg'); }
/* 時間資訊依角色分流（2026-07-03 定案）：host＝影片位置/總長＋可拖進度條
   （播放的控制者，播完影片要能拉回片段講解）；guest/assistant＝只記錄
   已直播多久，沒有總長、沒有進度條（同步進度由主持人控制，無從 seek） */
.wproom__time { font-size: var(--fs-caption); color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
/* 播放資訊組（host-only）：影片名在上（設定 ⚙ 換片會連動改字）、時間在下，
   疊兩行後整組靠 .wproom__ctrl 的 align-items:center 與進度條垂直置中 */
.wproom__playinfo { display: flex; flex-direction: column; gap: 2px; flex: none; }
.wproom__nowplay { font-size: var(--fs-caption); color: var(--text-secondary); white-space: nowrap; }
body[data-role="guest"] .wproom__playinfo,
body[data-role="assistant"] .wproom__playinfo { display: none; }
.wproom__time--guest { display: none; flex: 1; }
body[data-role="guest"] .wproom__time--guest,
body[data-role="assistant"] .wproom__time--guest { display: block; }
body[data-role="guest"] .wproom__time--host,
body[data-role="assistant"] .wproom__time--host,
body[data-role="guest"] .wproom__scrub,
body[data-role="assistant"] .wproom__scrub { display: none; }
/* 可拖進度條（host-only）：4px 軌道置中在 16px 觸擊高度內；
   fill/knob 位置吃 --wp-pos（0–100，wp-scrub hook 寫入）；knob hover/拖曳才現身 */
.wproom__scrub { flex: 1; position: relative; height: 16px; cursor: pointer; touch-action: none; }
.wproom__scrub::before { content: ''; position: absolute; inset: 6px 0; border-radius: var(--radius-pill); background: var(--border-default); }
.wproom__scrub-fill { position: absolute; inset: 6px auto 6px 0; width: calc(var(--wp-pos, 0) * 1%); border-radius: var(--radius-pill); background: var(--brand-500); }
.wproom__scrub-knob { position: absolute; top: 50%; left: calc(var(--wp-pos, 0) * 1%); width: 16px; height: 16px; border-radius: 50%; background: var(--brand-500); transform: translate(-50%, -50%); opacity: 0; transition: opacity 0.15s ease; }
.wproom__scrub:hover .wproom__scrub-knob,
.wproom__scrub:focus-visible .wproom__scrub-knob,
.wproom__scrub.is-dragging .wproom__scrub-knob { opacity: 1; }
/* guest = read-only controls */
.wproom__ctrl.is-locked { opacity: 0.5; pointer-events: none; }

/* control bar is a persistent stage footer — show only where it makes sense:
   直播 = 完整工具列；等候室 = host 的開演前準備（只露 鏡頭／語音／設定） */
.wproom__ctrl { display: none; }
body[data-state="live"] .wproom__ctrl,
body[data-state="lobby"][data-role="host"] .wproom__ctrl { display: flex; }
/* 等候室：隱藏播放軸（播放／快轉／時間／進度條），播放交給中央 CTA；其餘靠右 */
body[data-state="lobby"] .wproom__ctrl { justify-content: flex-end; }
body[data-state="lobby"] .wproom__ctrl-grp,
body[data-state="lobby"] .wproom__time,
body[data-state="lobby"] .wproom__playinfo,
body[data-state="lobby"] .wproom__scrub { display: none; }

/* ── 直播 chrome 浮層（2026-07-04 定案）：控制列與標籤疊在影片上，hover 影片區
   才顯示、滑開＝無干擾純播放。等候室不在此列（開播前準備工具要常駐好找）。
   焦點在控制列/設定彈窗開著/拖曳進度中不隱藏；?chrome=1（body[data-chrome]）
   強制常駐——畫板快照與設計預覽用。 ── */
body[data-state="live"] .wproom__ctrl {
  position: absolute; left: var(--space-5); right: var(--space-5); bottom: var(--space-5);
  padding: 56px var(--space-4) var(--space-3);
  background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.78));
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  opacity: 0; pointer-events: none; transition: opacity 0.18s ease;
}
body[data-state="live"] .wproom__stage:hover .wproom__ctrl,
body[data-state="live"] .wproom__ctrl:focus-within,
body[data-state="live"] .wproom__ctrl:has(.wp-pop:not([hidden])),
body[data-state="live"] .wproom__ctrl:has(.wproom__scrub.is-dragging),
body[data-chrome="1"] .wproom__ctrl { opacity: 1; pointer-events: auto; }
/* 標籤（共看中＋已直播／語音中）同節奏隱現 */
body[data-state="live"] .wproom__live,
body[data-state="live"] .wproom__voice { opacity: 0; transition: opacity 0.18s ease; }
body[data-state="live"] .wproom__stage:hover .wproom__live,
body[data-state="live"] .wproom__stage:hover .wproom__voice,
body[data-chrome="1"] .wproom__live,
body[data-chrome="1"] .wproom__voice { opacity: 1; }
/* chrome 出現時主持人 PiP 上移讓位（避開底部控制列），滑開回貼底 */
body[data-state="live"] .wproom__cam { transition: bottom 0.18s ease; }
body[data-state="live"] .wproom__stage:hover .wproom__cam,
body[data-state="live"][data-chrome="1"] .wproom__cam { bottom: 88px; }

/* 已直播搬進共看中 badge、音量獨立左簇後：guest 右側已無元素；
   assistant 右簇（cam/mic 唯讀展示）首顆補 margin-left:auto */
body[data-role="assistant"] .wproom__btn--cam { margin-left: auto; }

/* ── 聊天室收合：chat-head 的 » 收起、stage 右緣把手 ‹ 展開（body[data-chat="off"]） ── */
.wproom__chat-collapse {
  flex: none; width: 28px; height: 28px; display: grid; place-items: center;
  border: 0; border-radius: var(--radius-pill); background: transparent;
  color: var(--text-tertiary); cursor: pointer;
}
.wproom__chat-collapse:hover { background: var(--glass-bg-default); color: var(--text-primary); }
body[data-chat="off"] .wproom__body { grid-template-columns: minmax(0, 1fr); }
body[data-chat="off"] .wproom__chat { display: none; }
.wproom__chatopen {
  display: none; position: absolute; right: 0; top: 50%; transform: translateY(-50%);
  width: 26px; height: 72px; place-items: center;
  border: 1px solid var(--border-subtle); border-right: 0;
  border-radius: var(--radius-md) 0 0 var(--radius-md);
  background: var(--glass-bg-default); color: var(--text-secondary); cursor: pointer;
}
.wproom__chatopen:hover { color: var(--text-primary); }
body[data-chat="off"] .wproom__chatopen { display: grid; }

/* ── host settings popover (⚙ → 更換影片 + 音量) ──────────────────────── */
.wproom__settings { position: relative; }
/* 設定（⚙ 更換播放影片）回歸 host-only：音量已獨立成左簇飛出鈕（三角色都有），
   guest/assistant 沒有其他設定內容 → 整組藏 */
body[data-role="guest"] .wproom__settings,
body[data-role="assistant"] .wproom__settings { display: none; }

/* open upward (the control bar sits at the bottom of the screen).
   Double-class so this wins over the base .wp-pop regardless of source order. */
.wp-pop.wp-pop--up { top: auto; bottom: calc(100% + var(--space-2)); left: auto; right: 0; }

.wproom__set { width: 300px; display: flex; flex-direction: column; gap: var(--space-4); }
.wproom__set-sec { display: flex; flex-direction: column; gap: var(--space-2); }
.wproom__set-title {
  font-size: var(--fs-caption); color: var(--text-tertiary);
  font-weight: var(--fw-medium); letter-spacing: 0.04em;
}

/* 更換影片 list */
.wproom__filmlist { display: flex; flex-direction: column; gap: var(--space-1); max-height: 248px; overflow-y: auto; }
.wproom__filmopt {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2); border: 0; background: transparent;
  border-radius: var(--radius-md); cursor: pointer; text-align: left; width: 100%;
}
.wproom__filmopt:hover { background: var(--glass-bg-hover); }
.wproom__filmopt-poster {
  width: 34px; height: 50px; flex: none; object-fit: cover;
  border-radius: var(--radius-sm); background: var(--bg-tertiary);
}
.wproom__filmopt-meta { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.wproom__filmopt-title { font-size: var(--fs-body-sm); color: var(--text-primary); }
.wproom__filmopt-sub { font-size: var(--fs-caption); color: var(--text-tertiary); }
.wproom__filmopt-check {
  width: 18px; height: 18px; flex: none; position: relative;
  border: 1.5px solid var(--border-default); border-radius: var(--radius-pill);
}
.wproom__filmopt.is-current .wproom__filmopt-check,
.wproom__epopt.is-current .wproom__filmopt-check { border-color: var(--brand-500); background: var(--brand-500); }
.wproom__filmopt.is-current .wproom__filmopt-check::after,
.wproom__epopt.is-current .wproom__filmopt-check::after {
  content: ''; position: absolute; inset: 0; margin: auto;
  width: 6px; height: 6px; border-radius: 50%; background: var(--brand-ink);
}
.wproom__filmopt.is-current .wproom__filmopt-title { color: var(--brand-500); font-weight: var(--fw-medium); }

/* 影集分組：標題列為分組標籤（不可選），集數縮排在下、與電影同屬一組 radio */
.wproom__series { display: flex; flex-direction: column; }
.wproom__series-head { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2); }
.wproom__eplist { display: flex; flex-direction: column; gap: var(--space-1); padding-left: calc(34px + var(--space-3)); }
.wproom__epopt {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-2); border: 0; background: transparent;
  border-radius: var(--radius-md); cursor: pointer; text-align: left; width: 100%;
}
.wproom__epopt:hover { background: var(--glass-bg-hover); }
.wproom__epopt-label { flex: 1; min-width: 0; font-size: var(--fs-body-sm); color: var(--text-secondary); }
.wproom__epopt.is-current .wproom__epopt-label { color: var(--brand-500); font-weight: var(--fw-medium); }

/* 音量 */
.wproom__vol { display: flex; align-items: center; gap: var(--space-3); }
.wproom__vol-btn {
  width: 32px; height: 32px; flex: none; display: grid; place-items: center;
  border: 0; background: var(--glass-bg-default); border-radius: var(--radius-pill);
  cursor: pointer; font-size: var(--fs-body-sm);
}
.wproom__vol-btn:hover { background: var(--glass-bg-hover); }
.wproom__vol-range {
  flex: 1; height: 4px; -webkit-appearance: none; appearance: none; cursor: pointer;
  border-radius: var(--radius-pill);
  background: linear-gradient(to right,
    var(--brand-500) var(--wp-vol, 70%), var(--border-default) var(--wp-vol, 70%));
}
/* 進度/音量拖點統一品牌橘（2026-07-04 定案，同 .wproom__scrub-knob） */
.wproom__vol-range::-webkit-slider-thumb {
  -webkit-appearance: none; width: 14px; height: 14px; border-radius: 50%;
  background: var(--brand-500); cursor: pointer; border: 0;
}
.wproom__vol-range::-moz-range-thumb {
  width: 14px; height: 14px; border: 0; border-radius: 50%;
  background: var(--brand-500); cursor: pointer;
}

/* 飛出式音量（控制列左簇、三角色同款）：hover/focus-within 滑出音量條；
   點喇叭＝靜音（icon 換 volume-off、記住前值）。.is-open（?vol=open）強制展開 */
.wproom__vol--fly { gap: 0; }
.wproom__vol--fly .wproom__vol-btn { width: 40px; height: 40px; }
.wproom__vol-flyout {
  display: flex; align-items: center; gap: var(--space-2);
  /* 高度對齊 40px 按鈕：不寫高的話容器只有內容高（~15px），
     overflow:hidden 會把 14px 圓形拖點上下裁平 */
  height: 40px;
  width: 0; opacity: 0; overflow: hidden;
  transition: width 0.18s ease, opacity 0.18s ease;
}
.wproom__vol--fly:hover .wproom__vol-flyout,
.wproom__vol--fly:focus-within .wproom__vol-flyout,
.wproom__vol--fly.is-open .wproom__vol-flyout { width: 168px; opacity: 1; }
.wproom__vol-flyout .wproom__vol-range { flex: none; width: 108px; margin-left: var(--space-2); }
/* 數字貼著 bar：拿掉基底的 28px 右對齊欄寬（那是舊面板版式，會撐出視覺空隙） */
.wproom__vol-flyout .wproom__vol-val { width: auto; text-align: left; }
body[data-state="lobby"] .wproom__vol { display: none; }

/* ── 全螢幕（三角色統一、控制列最右）──────────────────────────────
   stage 進原生全螢幕：控制列本來就 absolute 在 stage 內，chrome 浮層
   hover 隱現機制在全螢幕內照常運作——hover 同位置就是縮小鈕。
   .is-fs 由 wp-fullscreen hook 於 fullscreenchange 寫回（含 webkit 前綴）。
   等候室沒有影片，不給全螢幕（同音量鈕）。 */
.wproom__ic--fs { --wp-ic: url('assets/icons/media/fullscreen.svg'); }
.wproom__ic--fs-exit { --wp-ic: url('assets/icons/media/fullscreen-exit.svg'); display: none; }
.wproom__stage.is-fs .wproom__ic--fs { display: none; }
.wproom__stage.is-fs .wproom__ic--fs-exit { display: block; }
/* stage 本身沒鋪底色（平常吃頁面背景），全螢幕要自己鋪滿 */
.wproom__stage.is-fs { background: var(--neutral-900); }

/* ── 全螢幕內容＝上下左右不留白（原生 .is-fs 與 iPhone 頁內滿版 .is-fs-inpage 共用）──
   stage 去內距、影片去圓角/邊框填滿、聊天把手藏（把手在 stage 內、全螢幕會外露）。
   原生全螢幕由瀏覽器把 stage 撐滿螢幕，這裡只管內容邊到邊。 */
.wproom__stage.is-fs { padding: 0; }
.wproom__stage.is-fs .wproom__screen--live { height: 100%; }
.wproom__stage.is-fs .wproom__video {
  flex: 1; min-height: 0;
  aspect-ratio: auto;               /* 解掉手機 16:9 固定，讓影片填滿全螢幕 */
  border-radius: 0; border: 0;
}
.wproom__stage.is-fs .wproom__chatopen { display: none; }

/* 頁內滿版 fallback（iPhone Safari 無元素全螢幕 API；wp-mchrome 掛 .is-fs-inpage）：
   原生全螢幕由瀏覽器定位，fallback 得自己把 stage 釘滿視口（任何寬度，橫版手機
   >768 不能被手機 query 關住）＋鎖背景捲動。內容邊到邊已由上面的 .is-fs 規則處理。 */
.wproom__stage.is-fs-inpage {
  position: fixed; inset: 0; z-index: 500; background: var(--neutral-900);
}
/* 直式按全螢幕＝直接轉成橫屏（視訊 app 慣例）。mock 拿不到
   screen.orientation.lock（要先進真原生全螢幕才鎖得住方向），改用 CSS 假橫屏：
   stage 以左上角為軸右轉 90°、寬高吃視口的高寬 → 正好蓋滿整個直式視口。
   只在觸控裝置（pointer:coarse）生效，桌機直式窄窗不轉。
   正式版接真 <video> 後由原生全螢幕＋orientation lock 取代（見
   docs/watch-party-fullscreen.md）。 */
@media (orientation: portrait) and (pointer: coarse) {
  .wproom__stage.is-fs-inpage {
    inset: auto; top: 0; left: 100vw;
    width: 100vh; height: 100vw;
    left: 100dvw; width: 100dvh; height: 100dvw; /* iOS 網址列：dv* 蓋掉上一行 */
    transform: rotate(90deg);
    transform-origin: 0 0;
  }
}
body.rf-wproom:has(.wproom__stage.is-fs-inpage) { overflow: hidden; }
body[data-state="lobby"] .wproom__btn--fs { display: none; }
/* guest/assistant 控制列沒有 playinfo/scrub 撐空間（cam/mic 對這兩角色
   display:none，撐不了），要自己推到最右——與 host 的右簇同落點 */
body[data-role="guest"] .wproom__btn--fs,
body[data-role="assistant"] .wproom__btn--fs { margin-left: auto; }
/* （?fs=1 快照/預覽已改走真實路徑同一個 .is-fs-inpage，無獨立規則——
   之前 body[data-fs] 的 inset:0 specificity 較高，會蓋掉上方假橫向的
   left 定位、rotate 後 stage 飛出視口，勿加回） */
.wproom__vol-val {
  width: 28px; text-align: right; font-size: var(--fs-caption);
  color: var(--text-secondary); font-variant-numeric: tabular-nums;
}

/* placeholder icons (Feather, MIT) — swap for final art later.
   The volume button shows one of three icons based on its data-vol state. */
.wproom__btn svg { display: block; }
.wproom__vol-ic { display: none; width: 18px; height: 18px; background: var(--text-primary);
  -webkit-mask: var(--wp-ic) center / contain no-repeat; mask: var(--wp-ic) center / contain no-repeat; }
.wproom__vol-btn[data-vol="high"] .wproom__vol-ic--high,
.wproom__vol-btn[data-vol="low"]  .wproom__vol-ic--low,
.wproom__vol-btn[data-vol="mute"] .wproom__vol-ic--mute { display: block; }

/* chat panel */
.wproom__chat { border-left: 1px solid var(--border-subtle); display: flex; flex-direction: column; min-height: 0; }
.wproom__chat-head {
  position: relative;
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-4) var(--space-4) var(--space-3);
  border-bottom: 1px solid var(--border-subtle); flex: none;
}
.wproom__chat-title { font-weight: var(--fw-medium); }
.wproom__presence {
  margin-left: auto; display: inline-flex; align-items: center; gap: var(--space-1);
  font-size: var(--fs-caption); color: var(--text-secondary);
  background: none; border: 1px solid transparent; border-radius: var(--radius-pill);
  padding: 3px var(--space-2); cursor: pointer; transition: background var(--ease-out, 0.15s), border-color var(--ease-out, 0.15s);
}
.wproom__presence:hover { background: var(--glass-bg-hover); color: var(--text-primary); }
.wproom__presence[aria-expanded="true"] { background: var(--glass-bg-hover); border-color: var(--border-default); color: var(--text-primary); }
.wproom__presence__caret {
  width: 14px; height: 14px; margin-left: 2px; flex: none;
  background: currentColor; opacity: 0.7;
  -webkit-mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
  transition: transform var(--ease-out, 0.15s);
}
.wproom__presence[aria-expanded="true"] .wproom__presence__caret { transform: rotate(180deg); }
.wproom__presence__dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--success-500); }
.wproom__msgs { flex: 1; overflow-y: auto; padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-4); }
.wproom__msg-who { display: flex; align-items: baseline; gap: var(--space-2); margin-bottom: 2px; }
.wproom__msg-name { font-size: var(--fs-body-sm); font-weight: var(--fw-medium); }
.wproom__msg-host { font-size: 10px; font-weight: var(--fw-bold); color: var(--brand-ink); background: var(--brand-500); padding: 0 5px; border-radius: var(--radius-xs); }
.wproom__msg-time { font-size: var(--fs-caption); color: var(--text-tertiary); }
.wproom__msg-body { font-size: var(--fs-body-sm); color: var(--text-secondary); line-height: var(--lh-body); }
.wproom__msg--me .wproom__msg-body { color: var(--text-primary); }
.wproom__chat-input { display: flex; gap: var(--space-2); padding: var(--space-3) var(--space-4); border-top: 1px solid var(--border-subtle); flex: none; }
.wproom__chat-box { flex: 1; padding: var(--space-3) var(--space-4); background: var(--bg-primary); border: 1px solid var(--border-subtle); border-radius: var(--radius-pill); color: var(--text-primary); font-size: var(--fs-body-sm); }
.wproom__chat-box::placeholder { color: var(--text-tertiary); }

/* lifecycle: only the active state shows (toggled by body[data-state]) */
.wproom__screen { display: none; flex: 1; flex-direction: column; gap: var(--space-3); min-height: 0; }
body[data-state="lobby"] .wproom__screen--lobby,
body[data-state="sync"] .wproom__screen--sync,
body[data-state="live"] .wproom__screen--live,
body[data-state="ended"] .wproom__screen--ended { display: flex; }

/* lobby / sync / ended centered panels */
.wproom__center {
  flex: 1; display: grid; place-items: center; text-align: center;
  border-radius: var(--radius-lg);
  background: var(--neutral-900); border: 1px solid var(--border-subtle);
}
.wproom__center-inner { display: flex; flex-direction: column; align-items: center; gap: var(--space-3); padding: var(--space-8); }
.wproom__center-eyebrow { font-size: var(--fs-caption); letter-spacing: 0.12em; text-transform: uppercase; color: var(--brand-500); font-weight: var(--fw-bold); }
.wproom__center-title { font-size: var(--fs-h3); font-weight: var(--fw-bold); }
.wproom__center-count { font-size: var(--fs-display); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; color: var(--text-primary); }
.wproom__center-sub { font-size: var(--fs-body-sm); color: var(--text-secondary); max-width: 36ch; }
/* 手動斷行的版本：解除 36ch 上限，讓 <br> 控制的兩行各自完整不再被截斷 */
.wproom__center-sub--wide { max-width: none; }
/* guest 暖場文案：兩句各一行（<br> 分句），36ch 會在句中折行 */
.wproom__lobby-guest { max-width: none; }
.wproom__spinner {
  width: 44px; height: 44px; border-radius: var(--radius-pill);
  border: 3px solid var(--border-default); border-top-color: var(--brand-500);
  animation: wproom-spin 0.9s linear infinite;
}
@keyframes wproom-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .wproom__spinner { animation: none; } }

/* preview toggle (design-review only — engineers strip [data-wp-roomdev]) */
/* 浮動預覽面板（源自 feat/watch-party-mobile-rwd 的 wp-devbar；桌機定案無拖曳）：
   固定右下角 chip（播放室抬高避開送出鈕），展開＝直立面板＋群組小標。 */
.wp-roomdev {
  position: fixed; right: var(--space-6); bottom: var(--space-6);  /* 固定右下角 */
  z-index: 200;
  display: flex; flex-direction: column; align-items: stretch; gap: var(--space-4);
  width: min(360px, calc(100vw - 24px));
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  padding: var(--space-5);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xl);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
}
/* 標題列＝收合開關 */
.wp-roomdev__label {
  display: flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-body); font-weight: var(--fw-bold); color: var(--text-primary);
  cursor: pointer; user-select: none; -webkit-user-select: none;
}
.wp-roomdev__label::after {
  content: ''; width: 14px; height: 14px; flex: none;
  background: currentColor;
  -webkit-mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
  transform: rotate(-90deg);             /* 展開＝箭頭朝上（點了收） */
  transition: transform 160ms var(--ease-out);
}
/* 群組間分隔線（把共看派對相關的維度與作品本身的維度分開；配方同
   .profile-menu__divider，值收在 token）。面板是 flex column，hr 預設
   margin 要歸零才吃 gap。 */
.wp-roomdev__sep {
  flex: none;               /* ⚠️ 面板 max-height 溢位時 flex-shrink 會把 1px 這項壓成 0 */
  height: 1px;
  margin: 0;
  border: none;
  background: var(--white-alpha-8);
}
/* 群組＝小標＋選項兩欄排 */
.wp-roomdev__grp { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-2); }
.wp-roomdev__grp::before { grid-column: 1 / -1; font-size: var(--fs-caption); color: var(--text-tertiary); }
.wp-roomdev__grp[data-roomdev-role]::before,
.wp-roomdev__grp[data-listdev-host]::before     { content: '身分'; }
.wp-roomdev__grp[data-roomdev-state]::before    { content: '階段'; }
.wp-roomdev__grp[data-roomdev-mic]::before      { content: '語音'; }
.wp-roomdev__grp[data-roomdev-playgate]::before { content: '播放時間'; }
.wp-roomdev__grp[data-roomdev-claim]::before,
.wp-roomdev__grp[data-titledemo-grp]::before    { content: '情境'; }
/* 自訂群組小標（優先於上面的預設）：<div data-devlabel="…"> */
.wp-roomdev__grp[data-devlabel]::before { content: attr(data-devlabel); }
.wp-roomdev__grp button {
  border: 1px solid var(--border-subtle);
  background: var(--bg-primary);
  cursor: pointer;
  padding: var(--space-3);
  border-radius: var(--radius-lg);
  font-size: var(--fs-body-sm); color: var(--text-secondary);
  text-align: left;   /* 長 label（未領票（免費））換行時靠左，不鎖 nowrap */
}
.wp-roomdev__grp button:hover { color: var(--text-primary); border-color: var(--border-default); }
.wp-roomdev__grp button.is-on {
  border-color: var(--brand-500);
  background: rgba(var(--brand-rgb), 0.12);
  color: var(--text-primary); font-weight: var(--fw-medium);
}
/* 重設鈕（demo-only）：重整回付費初始狀態；收合成 chip 時不出 */
.wp-roomdev__reset {
  margin-top: var(--space-2);
  padding: var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-primary);
  color: var(--text-secondary);
  font-size: var(--fs-body-sm);
  cursor: pointer;
}
.wp-roomdev__reset:hover { color: var(--text-primary); border-color: var(--brand-500); }
.wp-roomdev.is-collapsed .wp-roomdev__reset { display: none; }

/* 收合＝右下角小 chip（預設） */
.wp-roomdev.is-collapsed {
  flex-direction: row; align-items: center;
  width: auto; max-height: none; overflow: visible;
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-pill);
  cursor: pointer;
}
.wp-roomdev.is-collapsed .wp-roomdev__label {
  font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--text-secondary);
}
.wp-roomdev.is-collapsed .wp-roomdev__label::after { width: 12px; height: 12px; transform: rotate(90deg); }  /* 收合＝朝下（點了展） */
.wp-roomdev.is-collapsed .wp-roomdev__grp { display: none; }
/* 播放室：聊天輸入列（送出鈕）就在右下角，chip 抬到輸入列上方避開。
   max-height 要扣掉這個 96px 底部錨點＋頂部留 16——否則短橫版（如 1024×600）
   展開時面板比「錨點以上的空間」還高，會往上長出視口頂（身分那組被切掉、搆不到）。
   夾住後 overflow-y:auto 改內捲。 */
.rf-wproom .wp-roomdev { bottom: 96px; max-height: calc(100vh - 112px); }

/* role-driven visibility (set body[data-role="host|guest"]) */
.wproom__pill--host, .wproom__pill--guest { display: none; }
body[data-role="host"] .wproom__pill--host { display: inline-flex; }
body[data-role="guest"] .wproom__pill--guest { display: inline-flex; }
/* 官方管理員 = 唯讀播放器（純管理、不控播放）——只鎖播放類控制（播放鍵/鏡頭/語音
   半透明鎖點擊），已直播計時照亮、音量（settings 喇叭）可操作（opacity 蓋整條
   會連子元素一起壓，無法對 settings 單獨解鎖 → 改逐項鎖）。
   guest = 可本地暫停 + 控音量，但不能拖時間軸（與主持人同步）→ 藏倒退／快轉。 */
body[data-role="assistant"] .wproom__ctrl-grp,
body[data-role="assistant"] .wproom__btn--cam,
body[data-role="assistant"] .wproom__btn--mic { opacity: 0.5; pointer-events: none; }
body[data-role="guest"] .wproom__btn--seek,
body[data-role="assistant"] .wproom__btn--seek { display: none; }
.wproom__host-only { display: none; }
body[data-role="host"] .wproom__host-only { display: inline-flex; }

/* 官方管理員 pill（綠，與 host 橘區隔） */
.wproom__pill--assistant { display: none; }
.wproom__pill--assistant {
  color: var(--success-500);
  background: color-mix(in srgb, var(--success-500) 12%, transparent);
}
@supports not (background: color-mix(in srgb, red, blue)) {
  .wproom__pill--assistant { background: rgba(var(--success-rgb), 0.12); }
}
body[data-role="assistant"] .wproom__pill--assistant { display: inline-flex; }

/* ════════════════════════════════════════════════════════════════════
   4 · SETUP PAGE (活動設定頁 — empty, editable form)
   ════════════════════════════════════════════════════════════════════ */
.wp-create {
  max-width: 720px; /* matches the community post-stream column; lives inside .container */
  margin: 0 auto;
  padding: 0 0 var(--space-16); /* top spacing comes from section--flush-top + .page-head */
}
/* title row uses the shared .page-head component (h3, same as cocreate) */

/* ════════════════════════════════════════════════════════════════════
   6 · ACTIVITY LIST (我的活動 — overview; click a row → detail page)
   ════════════════════════════════════════════════════════════════════ */
.wp-list { max-width: 720px; margin: 0 auto; padding: var(--space-2) 0 var(--space-16); }
.wp-listcard {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4);
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-4);
  text-decoration: none;
  color: inherit;
  transition: border-color 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.wp-listcard:hover { border-color: var(--border-default); transform: translateY(-1px); }
/* 已結束的派對：唯讀，不可再點進去 — 移除點擊/hover；chevron 保留但淡化，維持整排對齊 */
.wp-listcard:has(.wp-status--ended) { pointer-events: none; cursor: default; }
.wp-listcard:has(.wp-status--ended) .wp-listcard__chevron { opacity: 0.3; }
.wp-listcard__poster { width: 52px; aspect-ratio: 2 / 3; object-fit: cover; border-radius: var(--radius-sm); flex: none; align-self: flex-start; }
.wp-listcard__body { flex: 1; min-width: 0; }
.wp-listcard__title { font-size: var(--fs-body-lg); font-weight: var(--fw-bold); }
.wp-listcard__meta { margin-top: 2px; font-size: var(--fs-body-sm); color: var(--text-secondary); }
.wp-listcard__when { margin-top: var(--space-1); font-size: var(--fs-body-sm); color: var(--text-tertiary); }

/* card tags — 免費/付費 · 公開/私密 (host overview, quick scan) */
.wp-listcard__tags { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-2); }
/* 免費/付費/公開/私密 = 統一中性標籤（白 8% 底、無邊框、不分色） */
.wp-chip {
  display: inline-flex; align-items: center;
  padding: 1px var(--space-2);
  font-size: var(--fs-caption); font-weight: var(--fw-medium);
  color: var(--text-secondary);
  background: var(--white-alpha-8);
  border: none;
  border-radius: var(--radius-pill);
}
.wp-listcard__chevron {
  width: 20px; height: 20px; flex: none;
  background: var(--text-tertiary);
  -webkit-mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
}

/* view toggle (我發起的 / 我參與的) — reuses the AI-contest glass-tabs jelly
   pill (.glass-tabs); only shown to authorised hosts */
[data-wp-listtoggle] { margin-bottom: var(--space-6); }

/* two views, switched by body[data-wp-view] */
.wp-listview { display: none; }
body[data-wp-view="hosted"] .wp-listview--hosted { display: block; }
body[data-wp-view="joined"] .wp-listview--joined { display: block; }

/* regular viewer (data-wp-host="no"): no host privileges → hide the toggle and
   the create button, and force the joined view */
body[data-wp-host="no"] [data-wp-listtoggle],
body[data-wp-host="no"] .wp-list__create { display: none; }
body[data-wp-host="no"] .wp-listview--hosted { display: none; }
body[data-wp-host="no"] .wp-listview--joined { display: block; }

/* official moderator (data-wp-host="admin"): not a host → no toggle, no
   create button; sees only the parties they're assigned to assist. */
.wp-listview--admin { display: none; }
body[data-wp-host="admin"] [data-wp-listtoggle],
body[data-wp-host="admin"] .wp-list__create { display: none; }
body[data-wp-host="admin"] .wp-listview--hosted,
body[data-wp-host="admin"] .wp-listview--joined { display: none; }
body[data-wp-host="admin"] .wp-listview--admin { display: block; }

/* 無視角切換時（一般用戶 / 官方管理員），標題與列表稍微拉開（沒有 tab 補間距） */
body[data-wp-host="no"] .wp-listview--joined,
body[data-wp-host="admin"] .wp-listview--admin { margin-top: var(--space-4); }

/* 列表空狀態（沿用 rf-library 的 .rf-empty）：view 裡沒有活動卡時顯示。
   selector 需壓過 refine-library 的 body.rf-library .rf-empty{display:flex}（載入較晚），
   故一律帶 body.rf-library 前綴拉高 specificity。只有 active view（display:block）
   的空狀態會露出，非 active view 由父層 display:none 蓋掉。
   demo：dev bar「空狀態」寫 body[data-wp-empty]，隱藏卡片、強制露出當前 view 的空狀態 */
body.rf-library .wp-listview .rf-empty { display: none; }
body.rf-library .wp-listview:not(:has(.wp-listcard)) .rf-empty,
body.rf-library[data-wp-empty] .wp-listview .rf-empty { display: flex; }
body[data-wp-empty] .wp-listcard { display: none; }

/* standalone view heading (used when no toggle precedes the list, e.g. 我協助的) */
.wp-list__viewhead {
  font-size: var(--fs-h4); font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin: 0 0 var(--space-5);
}

/* grouped section card (blind-box style — solid elevated, generous padding)
   上內距多一階：標題列離卡頂 32，呼吸感（同 ≤768 的 24>20 比例） */
.wp-card {
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  padding: var(--space-8) var(--space-6) var(--space-6);
  margin-bottom: var(--space-5);
}
.wp-card__title { font-size: var(--fs-h4); font-weight: var(--fw-bold); line-height: var(--lh-h4); margin-bottom: var(--space-5); }
.wp-card .wp-field:last-child { margin-bottom: 0; }
/* 收進卡片的資訊列/倒數，最後一項不留額外底距 */
.wp-card > .wp-count:last-child, .wp-card > .wp-blocks:last-child { margin-bottom: 0; }
.wp-create__submit { margin-top: var(--space-6); }

/* native date/time pickers: dark chrome + brand-orange accent (selected day /
   today / scroller highlight) instead of the OS default blue */
.wp-input { color-scheme: dark; accent-color: var(--brand-500); }

/* two inputs side by side (date | time) */
.wp-field__row { display: flex; gap: var(--space-3); }
.wp-field__row > * { flex: 1; }

/* ── custom date / time picker (brand-tinted; replaces un-brandable native) ── */
.wp-pickerfield { position: relative; }
.wp-pickerfield__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  font-size: var(--fs-body);
  /* pin the line box so CJK placeholder ("時：分") and Latin values ("2026/07/02")
     render the same height, and match the plain .wp-input fields (44px) on this page */
  line-height: 1.125;
  color: var(--text-primary);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  cursor: pointer;
  text-align: left;
}
.wp-pickerfield__trigger.is-placeholder { color: var(--text-tertiary); }
.wp-pickerfield__trigger:focus-visible { outline: 2px solid var(--brand-500); outline-offset: 1px; }
.wp-pickerfield__icon {
  width: 18px; height: 18px; flex: none;
  background: var(--text-tertiary);
  -webkit-mask: var(--wp-ic) center / contain no-repeat;
          mask: var(--wp-ic) center / contain no-repeat;
}

/* popover shell */
.wp-pop {
  position: absolute;
  top: calc(100% + var(--space-2));
  left: 0;
  z-index: 60;
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.7);
  padding: var(--space-4);
}
.wp-pop[hidden] { display: none; }

/* calendar */
.wp-cal__head { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--space-3); }
.wp-cal__month { font-size: var(--fs-body); font-weight: var(--fw-bold); }
.wp-cal__nav {
  width: 28px; height: 28px; flex: none;
  display: grid; place-items: center;
  border: 0; background: transparent;
  color: var(--text-secondary);
  border-radius: var(--radius-pill);
  cursor: pointer; font-size: var(--fs-body-lg);
}
.wp-cal__nav:hover { background: var(--glass-bg-default); color: var(--text-primary); }
.wp-cal__nav:disabled { opacity: 0.25; pointer-events: none; }
.wp-cal__grid { display: grid; grid-template-columns: repeat(7, 34px); gap: 2px; }
.wp-cal__wd { text-align: center; font-size: var(--fs-caption); color: var(--text-tertiary); padding: 4px 0; }
.wp-cal__day {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  font-size: var(--fs-body-sm); color: var(--text-primary);
  border: 0; background: transparent;
  border-radius: var(--radius-pill); cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.wp-cal__day:hover { background: var(--glass-bg-default); }
.wp-cal__day:empty { pointer-events: none; }
.wp-cal__day.is-disabled { color: var(--text-tertiary); opacity: 0.3; pointer-events: none; }
.wp-cal__day.is-today { color: var(--brand-500); font-weight: var(--fw-bold); }
.wp-cal__day.is-selected { background: var(--brand-500); color: var(--brand-ink); font-weight: var(--fw-bold); }

/* time list — single scrollable column (Google-Meet style) */
[data-wp-timepicker] .wp-pop { padding: var(--space-2); }
.wp-time {
  min-width: 132px; max-height: 240px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 1px;
  padding-right: 2px;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.wp-time::-webkit-scrollbar { width: 6px; }
.wp-time::-webkit-scrollbar-track { background: transparent; margin: var(--space-1) 0; }
.wp-time::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); }
.wp-time::-webkit-scrollbar-thumb:hover { background: var(--text-tertiary); }
.wp-time__opt {
  padding: var(--space-2) var(--space-3);
  text-align: left;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  border: 0; background: transparent;
  border-radius: var(--radius-sm); cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.wp-time__opt:hover { background: var(--glass-bg-default); color: var(--text-primary); }
.wp-time__opt.is-on { background: var(--brand-500); color: var(--brand-ink); font-weight: var(--fw-medium); }

/* empty film selector (dashed, before a film is chosen) */
.wp-pick-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-5);
  font-size: var(--fs-body);
  color: var(--text-secondary);
  background: transparent;
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color 150ms var(--ease-out), color 150ms var(--ease-out);
}
.wp-pick-empty:hover { border-color: var(--brand-500); color: var(--text-primary); }
/* [hidden] must beat display:flex once a film is chosen (mirrors .wp-movie[hidden]) */
.wp-pick-empty[hidden] { display: none; }

/* chosen-episode tags on the selected-movie row */
.wp-movie__eps { display: flex; flex-wrap: wrap; gap: var(--space-1); margin-top: var(--space-2); }
.wp-ep-tag {
  font-size: var(--fs-caption);
  padding: 1px var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--brand-500);
  color: var(--brand-ink);
  font-weight: var(--fw-medium);
}

/* custom price input (revealed when 自訂價格) */
.wp-price-custom { display: none; align-items: center; gap: var(--space-2); margin-top: var(--space-3); }
.wp-seg[data-mode="custom"] ~ .wp-price-custom { display: flex; }
.wp-price-custom__pop { width: 20px; height: 20px; flex: none; }
.wp-price-custom .wp-input { max-width: 160px; }
/* hide the number spinner on the price input for a calm field */
.wp-price-custom .wp-input::-webkit-outer-spin-button,
.wp-price-custom .wp-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.wp-price-custom .wp-input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* ════════════════════════════════════════════════════════════════════
   5 · FILM PICKER (backend-allowed films + episode checkboxes)
   ════════════════════════════════════════════════════════════════════ */
.wp-pick { display: flex; flex-direction: column; gap: var(--space-3); margin: var(--space-2) 0 var(--space-5); }
.wp-pick__item {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.wp-pick__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  cursor: pointer;
}
.wp-pick__poster { width: 40px; aspect-ratio: 2 / 3; object-fit: cover; border-radius: var(--radius-sm); flex: none; }
.wp-pick__text { flex: 1; min-width: 0; }
.wp-pick__t { font-size: var(--fs-body); font-weight: var(--fw-medium); }
.wp-pick__d { font-size: var(--fs-caption); color: var(--text-tertiary); margin-top: 2px; }
.wp-pick__item.is-selected { border-color: var(--brand-500); }

/* unified selection indicator — grey ring → brand circle + white check
   (matches the site's 盲盒電影 / 單抽 selection style) */
.wp-check {
  width: 22px; height: 22px; flex: none;
  border-radius: var(--radius-pill);
  border: 2px solid var(--border-default);
  display: grid; place-items: center;
  transition: background 150ms var(--ease-out), border-color 150ms var(--ease-out);
}
.wp-check::before {
  content: '';
  width: 11px; height: 6px;
  border-left: 2px solid var(--neutral-white);
  border-bottom: 2px solid var(--neutral-white);
  transform: translateY(-1px) rotate(-45deg);
  opacity: 0;
}
.is-selected > .wp-pick__head > .wp-check,
.wp-ep.is-checked .wp-check {
  background: var(--brand-500);
  border-color: var(--brand-500);
}
.is-selected > .wp-pick__head > .wp-check::before,
.wp-ep.is-checked .wp-check::before { opacity: 1; }

/* episode list (series only) */
.wp-eps { padding: 0 var(--space-3) var(--space-4) calc(var(--space-3) + 52px); display: none; flex-direction: column; gap: var(--space-2); }
.wp-pick__item.is-selected .wp-eps { display: flex; }
.wp-eps__note { font-size: var(--fs-caption); color: var(--text-tertiary); margin-bottom: var(--space-2); }
.wp-ep {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  font-size: var(--fs-body-sm);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  cursor: pointer;
}
.wp-ep__label { flex: 1; }

/* btn disabled state (open-room before window) */
.btn[disabled], .btn[aria-disabled="true"] {
  opacity: 0.45;
  pointer-events: none;
  cursor: not-allowed;
}

/* ════════════════════════════════════════════════════════════════════
   8 · GUEST INVITE / TICKET (來賓入場券流程 — watch-party-film.html)
   The landing page a guest reaches from a shared link. State machine on
   body[data-claim]: open (未領票) → claimed (已領票，待開演) → warmup (可進場).
   Reuses .wp-card / .wp-blocks / .wp-count / .wp-dialog / .wp-roomdev.
   ════════════════════════════════════════════════════════════════════ */
.wp-film { max-width: 720px; margin: 0 auto; padding: 0 0 var(--space-16); }

/* invite hero — poster + film/host */
/* align-items 必須 flex-start：右欄行數變多（私密 tag/提示）時海報才不會被 stretch 拉高 */
.wp-invite { display: flex; align-items: flex-start; gap: var(--space-6); margin-bottom: var(--space-8); }
.wp-invite__poster {
  width: 160px; aspect-ratio: 2 / 3; flex: none;
  object-fit: cover;
  border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px -16px rgba(0, 0, 0, 0.7);
}
.wp-invite__body { flex: 1; min-width: 0; }
.wp-invite__eyebrow {
  display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-caption); font-weight: var(--fw-bold);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--brand-500);
}
/* margin 寫定：h1 的 UA 預設下邊距是 em 制、跟著大標字級放大，會把私密場 pill 推很遠 */
.wp-invite__title { margin: 0 0 var(--space-3); font-size: var(--fs-h1); font-weight: var(--fw-bold); line-height: var(--lh-h1); }
/* 邀請區三段節奏：①大標＋私密場 pill ②播放內容＋完整資訊 ③邀請人。
   區與區 32px（meta / host 的 margin-top），區內 4px 緊排。 */
.wp-invite__meta { margin: var(--space-8) 0 0; font-size: var(--fs-body); color: var(--text-secondary); }
.wp-invite__host { margin-top: var(--space-8); display: inline-flex; align-items: center; gap: var(--space-2); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.wp-invite__host img { width: 28px; height: 28px; border-radius: var(--radius-pill); flex: none; }
.wp-invite__host b { color: var(--text-primary); font-weight: var(--fw-medium); }
/* 另開分頁看影片完整資訊（票券內頁一律提供） */
/* 「查看影片完整資訊」外連（共用於 wp-invite 邀請頁 header 與 wp-event 主持人頁 header） */
.wp-filmlink { display: flex; align-items: center; gap: var(--space-1); margin-top: var(--space-1); width: fit-content; font-size: var(--fs-body-sm); color: var(--brand-500); text-decoration: none; }
.wp-filmlink:hover { text-decoration: underline; }
.wp-filmlink__ic {
  width: 1em; height: 1em; flex: none; background: currentColor;
  -webkit-mask: url('assets/icons/arrow_right.svg') center / contain no-repeat;
          mask: url('assets/icons/arrow_right.svg') center / contain no-repeat;
}

/* 手機選單子頁頂欄 .m-pagebar：桌機隱藏（桌機走各頁 page-head）。
   泛用版型——共看派對列表先用，之後兌換中心等選單頁沿用 */
.m-pagebar { display: none; }

/* 手機 chrome 注入件（watch-party.js）：漢堡/drawer/＋建立/sheet 桌機一律藏。
   必須 scope —— 本檔會 build 進 components.css、mobile-*.html 也載，
   裸選擇器會把 mobile.html 自己的 .m-drawer/.m-create 打壞 */
body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn,
body:is(.rf-detail, :has(main.rf-watchparty)) .header__icon-btn--m-create,
body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer,
body:is(.rf-detail, :has(main.rf-watchparty)) .m-create { display: none; }

/* 頭像選單 close X／我的爆米花 row／觀看歷史（watch-party.js 手機需求注入）：
   桌機 dropdown 不顯示，≤768 全螢幕選單才開。
   ⚠️ 必須 scope 到 watch-party/title 頁——裸選擇器曾把 mobile.html 自己選單的
   X 與爆米花列一起藏掉（components.css 全站都載、specificity 又壓過其 inline） */
body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile .profile-menu__close,
body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile .profile-menu__popcorn,
body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile .profile-menu__li--history { display: none; }

/* free-ticket value colour on the reused .wp-block row */
.wp-block__value--free { color: var(--success-500); }

/* official-moderator (data-claim="admin") swaps on the invite + ticket info:
   host line → "你是此活動的官方管理員"; 入場券 row → "官方管理員 · 免領票". */
.wp-invite__host-line--admin,
.wp-block__value--ticket-admin { display: none; }
body[data-claim="admin"] .wp-invite__host-line:not(.wp-invite__host-line--admin),
body[data-claim="admin"] .wp-block__value--ticket-default { display: none; }
body[data-claim="admin"] .wp-invite__host-line--admin { display: inline; }
body[data-claim="admin"] .wp-block__value--ticket-admin { display: inline-flex; }
/* 官方管理員頁：person-line 前的頭像是主持人 Gray 的，管理員頁不顯示 */
body[data-claim="admin"] .wp-invite__host img { display: none; }
.wp-block__value .wp-block__pop { width: 16px; height: 16px; vertical-align: -3px; margin-right: 2px; }

/* claim (open / open-paid) vs ready (claimed/warmup) blocks — toggled by body[data-claim] */
.wp-ticket__claim, .wp-ticket__ready, .wp-ticket__admin { display: none; }
body[data-claim="open"] .wp-ticket__claim,
body[data-claim="open-paid"] .wp-ticket__claim { display: block; }

/* 付費未領票（data-claim="open-paid"）：版型與免費完全相同（須知＋同意都在頁面上），
   只差 入場券=80 爆米花、按鈕字=立即購票、hint 用付費版。勾同意→立即購票 直接購票成功。 */
.wp-block__value--ticket-paid,
.wp-ticket__claimlabel--paid,
.wp-ticket__hint--paid { display: none; }
body[data-claim="open-paid"] .wp-block__value--ticket-default { display: none; }
body[data-claim="open-paid"] .wp-block__value--ticket-paid { display: inline-flex; }
body[data-claim="open-paid"] .wp-ticket__claimlabel--free { display: none; }
body[data-claim="open-paid"] .wp-ticket__claimlabel--paid { display: inline; }
body[data-claim="open-paid"] .wp-ticket__hint--free { display: none; }
body[data-claim="open-paid"] .wp-ticket__hint--paid { display: block; }
/* 入場券 row 的售價：數字 + 爆米花 icon（付費態才顯示，見上方 --ticket-paid 切換） */
.wp-block__value--ticket-paid { align-items: center; gap: 4px; }
.wp-block__coin { width: 18px; height: 18px; flex: none; }
/* 退款條款：免費 vs 付費，依 data-claim 切換（付費文案比照 title.html 購票版） */
.wp-notice__li--paid { display: none; }
body[data-claim="open-paid"] .wp-notice__li--free { display: none; }
body[data-claim="open-paid"] .wp-notice__li--paid { display: list-item; }
body[data-claim="claimed"] .wp-ticket__ready,
body[data-claim="warmup"] .wp-ticket__ready { display: block; }
/* official moderator: no ticket to claim — direct room entry */
body[data-claim="admin"] .wp-ticket__admin { display: block; }

/* 已領票（付費）：claimed/warmup + body[data-pay="paid"] → 入場券顯示爆米花價（比照 open-paid 切換） */
body[data-pay="paid"] .wp-block__value--ticket-default { display: none; }
body[data-pay="paid"] .wp-block__value--ticket-paid { display: inline-flex; }
/* 官方管理員免領票：即使疊上付費票也不顯示爆米花價（放在 pay 規則後蓋掉） */
body[data-claim="admin"] .wp-block__value--ticket-paid { display: none; }

/* 私密疊加層（body[data-room="private"]）：私密＝布林修飾，疊在任何 claim 狀態上。
   代碼在邀請連結裡完成任務（管領票），進房只驗票＋地區 → 頁上不再展示入場代碼。 */
/* 私密場 pill：lock icon（mask）＋文字，獨立一行在標題下方（不放 h1 內——
   會吃到標題行高、也會被 data-i-title 改字時洗掉）。
   樣式對齊 title.html 共創 banner 的 .section-eyebrow--tag：品牌橘字＋12% 橘底。 */
.wp-tag-lock {
  display: none; align-items: center; gap: 8px;
  width: fit-content; margin: 0;
  padding: 7px 14px; line-height: 1; border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--brand-500) 12%, transparent);
  color: var(--brand-500);
  font-size: var(--fs-body-sm); font-weight: var(--fw-bold); letter-spacing: 0.06em;
  white-space: nowrap;
}
@supports not (background: color-mix(in srgb, red, blue)) {
  .wp-tag-lock { background: rgba(var(--brand-rgb), 0.12); }
}
body[data-room="private"] .wp-tag-lock { display: inline-flex; }
.wp-tag-lock__ic {
  width: 14px; height: 14px; flex: none; background: currentColor;
  -webkit-mask: url('assets/icons/lock.svg') center / contain no-repeat;
          mask: url('assets/icons/lock.svg') center / contain no-repeat;
}

/* 入場券 row 的領取狀態字（已領票/可進場才出現；未領票的 open/open-paid 不顯示） */
.wp-block__claimtag { display: none; }
body[data-claim="claimed"] .wp-block__claimtag,
body[data-claim="warmup"] .wp-block__claimtag { display: inline; }

/* ready-state "ticket ready" badge (outline like .wp-status) */
.wp-ticket__badge {
  display: inline-flex; align-items: center; gap: var(--space-2);
  margin-bottom: var(--space-4);
  padding: 4px var(--space-3);
  border-radius: var(--radius-pill);
  border: 1px solid var(--success-500); color: var(--success-500);
  font-size: var(--fs-body-sm); font-weight: var(--fw-medium);
}
.wp-ticket__badge__dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: currentColor; }

/* 標題列：入場資訊 靠左、入場券狀態徽章 靠右 */
.wp-ticket__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-bottom: var(--space-5); }
/* 標題與狀態膠囊垂直置中：h2 的 UA margin-top（~16px）要一起歸零，
   flex 置中算的是含 margin 的盒（同 ≤768 的修法，base 對齊） */
.wp-ticket__head .wp-card__title { margin: 0; }
/* header-placed badges: drop body margin; show the one matching data-claim */
.wp-ticket__badge--head { display: none; margin-bottom: 0; flex: none; }
body[data-claim="blocked"] .wp-ticket__badge--blocked,
body[data-claim="full"] .wp-ticket__badge--full,
body[data-claim="admin"] .wp-ticket__badge--adminhead { display: inline-flex; }

/* 倒數面板只給持票者：未領票/額滿/地區不符還沒有票，「可進場」在那裡是錯的
   （已領票/可進場/官方管理員照常；管理頁 watch-party.html 無 data-claim 不受影響） */
body[data-claim="open"] .wp-count,
body[data-claim="open-paid"] .wp-count,
body[data-claim="blocked"] .wp-count,
body[data-claim="full"] .wp-count { display: none; }

/* enter-room CTA — 三段時間軸（body[data-wp-phase] 由 wp-countdown 寫入）：
   pre 置灰；window/live 亮起（claim=warmup 預覽由 JS 強制 window，同樣亮起） */
.wp-ticket__enter { width: 100%; justify-content: center; }
body[data-claim="claimed"]:not([data-wp-phase="window"]):not([data-wp-phase="live"]) .wp-ticket__enter { opacity: 0.45; pointer-events: none; }
/* 「進入房間（已開演）」後綴：已開演才顯示；遲到者進房自動對齊進度 */
.wp-enterlive { display: none; }
body[data-wp-phase="live"] .wp-enterlive { display: inline; }
/* window/live 時倒數面板的「距離開演還有」標籤收起（值已是「可進場」「已開演」狀態字） */
.wp-count.is-window .wp-count__label { display: none; }

/* state-specific hints under the CTA — 間距統一 12px（不用相鄰選擇器：
   隱藏的提示仍算相鄰，會讓第一行可見提示忽近忽遠） */
.wp-ticket__hint { text-align: center; margin-top: var(--space-3); font-size: var(--fs-caption); color: var(--text-tertiary); }
.wp-ticket__hint--claimed, .wp-ticket__hint--warmup, .wp-ticket__hint--refund { display: none; }
body[data-claim="claimed"] .wp-ticket__hint--claimed { display: block; }
body[data-claim="warmup"] .wp-ticket__hint--warmup { display: block; }
/* 可進場/已開演後「開演前 15 分鐘開放進場」不再適用，收起 */
body[data-wp-phase="window"] .wp-ticket__hint--claimed,
body[data-wp-phase="live"] .wp-ticket__hint--claimed { display: none; }
/* 付費票（body[data-pay="paid"]）：按鈕下加「已購票不予退款」小字 */
body[data-pay="paid"] .wp-ticket__hint--refund { display: block; }
/* 已領票詳情頁：開演 24 小時內才開始倒數——更早只有「X 天」沒資訊量（完整日期
   在開演時間欄），面板整個收起。主持人管理頁不在此列（排程中仍看 X 天）。 */
body[data-wp-phase="far"] .wp-film .wp-count { display: none; }
/* 申請退款鈕：僅付費票顯示（免費票沒有可退的爆米花；admin 走 .wp-ticket__admin 不經此區） */
.wp-refundbtn-row { display: none; text-align: center; margin-top: var(--space-4); }
body[data-pay="paid"] .wp-refundbtn-row { display: block; }

/* stacked block buttons inside a dialog (e.g. 前往我的活動 + 預覽來賓頁) */
.wp-dialog .wp-btn-block + .wp-btn-block { margin-top: var(--space-3); }

/* hero-carousel sales slide — branded gradient (no banner image needed,
   retina-sharp, 0 KB). Copy + CTAs reuse .hero-carousel__* verbatim. */
.hero-carousel__slide--wp {
  background:
    radial-gradient(78% 120% at 12% 16%, rgba(255, 163, 63, 0.38), transparent 56%),
    radial-gradient(72% 108% at 92% 94%, rgba(226, 37, 90, 0.20), transparent 60%),
    linear-gradient(118deg, #1c1407 0%, var(--neutral-900) 62%);
}

/* ════════════════════════════════════════════════════════════════════
   10 · DEMO FLOW HUB (watch-party-demo.html — internal walkthrough)
   A single entry that lays out both journeys (host / guest) as ordered,
   clickable step sequences. Review aid only; not a production page.
   ════════════════════════════════════════════════════════════════════ */
.wp-demo { max-width: 720px; margin: 0 auto; padding: 0 0 var(--space-16); }
.wp-demo__canvaslink { margin-top: var(--space-4); }

.wp-journey { margin-top: var(--space-8); }
.wp-journey__head { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-4); }
.wp-journey__badge {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 4px var(--space-3); border-radius: var(--radius-pill);
  font-size: var(--fs-body-sm); font-weight: var(--fw-bold);
  border: 1px solid var(--border-default); color: var(--text-secondary);
}
.wp-journey__badge--host { color: var(--brand-500); border-color: var(--brand-500); }
.wp-journey__badge--guest { color: var(--success-500); border-color: var(--success-500); }
.wp-journey__badge__dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: currentColor; }
.wp-journey__sub { font-size: var(--fs-body-sm); color: var(--text-tertiary); }

.wp-journey__steps { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }

.wp-step {
  display: flex; align-items: center; gap: var(--space-4);
  padding: var(--space-4);
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  text-decoration: none; color: inherit;
  transition: border-color 150ms var(--ease-out), transform 150ms var(--ease-out);
}
.wp-step:hover { border-color: var(--border-default); transform: translateY(-1px); }
.wp-step__num {
  width: 30px; height: 30px; flex: none;
  display: grid; place-items: center;
  border-radius: var(--radius-pill);
  background: var(--bg-primary); border: 1px solid var(--border-default);
  font-size: var(--fs-body-sm); font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums; color: var(--text-secondary);
}
.wp-journey--host .wp-step__num { color: var(--brand-500); border-color: var(--brand-500); }
.wp-journey--guest .wp-step__num { color: var(--success-500); border-color: var(--success-500); }
.wp-step__body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.wp-step__title { font-size: var(--fs-body); font-weight: var(--fw-medium); }
.wp-step__desc { margin-top: 2px; font-size: var(--fs-caption); color: var(--text-tertiary); }
.wp-step__chevron {
  width: 20px; height: 20px; flex: none;
  background: var(--text-tertiary);
  -webkit-mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
}

/* ════════════════════════════════════════════════════════════════════
   12 · PRIVACY + ENTRY CODE + CAPACITY (create) · CODE GATE + FULL (film)
        · COMMENT REMOVAL + HOST AUDIO (room)
   New batch — extends the existing wp-* vocabulary. Behaviour in
   assets/watch-party.js (DS.register), one hook per element.
   ════════════════════════════════════════════════════════════════════ */

/* zero out the bottom margin on the last field in a card (was inline-only) */
.wp-field--last { margin-bottom: 0; }

/* created-confirmation event summary — small poster + 片名/集數 + 資訊列 */
.wp-summary {
  margin-bottom: var(--space-5);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  padding: var(--space-4) var(--space-5);
}
.wp-summary__head { display: flex; align-items: center; gap: var(--space-3); }
.wp-summary__poster { width: 48px; aspect-ratio: 2 / 3; flex: none; object-fit: cover; border-radius: var(--radius-sm); }
.wp-summary__poster[hidden] { display: none; }
.wp-summary__head-text { min-width: 0; }
.wp-summary__title { font-size: var(--fs-body-lg); font-weight: var(--fw-medium); color: var(--text-primary); }
.wp-summary__eps { margin-top: 2px; font-size: var(--fs-caption); color: var(--text-tertiary); }
.wp-summary__eps:empty { display: none; }
.wp-summary__rows {
  display: grid; gap: var(--space-2); margin: var(--space-4) 0 0;
  padding-top: var(--space-4); border-top: 1px solid var(--border-subtle);
}
.wp-summary__row { display: flex; align-items: baseline; gap: var(--space-3); }
.wp-summary__row[hidden] { display: none; }
.wp-summary__row dt { margin: 0; font-size: var(--fs-body-sm); color: var(--text-secondary); }
.wp-summary__row dd { margin: 0 0 0 auto; font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--text-primary); text-align: right; }
/* 免費入場 強調綠（與 film 頁 .wp-block__value--free 一致） */
.wp-free { color: var(--success-500); }

/* claim 彈窗狀態（title.html）：免費領票（預設）↔ 額滿 ↔ 付費購買，由 body[data-claim] 切換 */
.wp-claim-state--full, .wp-claim-state--paid { display: none; }
body[data-claim="full"] .wp-claim-state--open { display: none; }
body[data-claim="full"] .wp-claim-state--full { display: block; }
body[data-claim="paid"] .wp-claim-state--open { display: none; }
body[data-claim="paid"] .wp-claim-state--paid { display: block; }

/* 費用區塊（付費入場券；參考「確認租借」）— 總共 N 爆米花 + 剩餘 */
.wp-cost {
  margin-bottom: var(--space-5);
  padding: var(--space-4) var(--space-5);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
}
.wp-cost__row { display: flex; align-items: center; }
.wp-cost__label { font-size: var(--fs-body-lg); font-weight: var(--fw-bold); color: var(--text-primary); }
.wp-cost__amount {
  margin-left: auto; display: inline-flex; align-items: center; gap: var(--space-2);
  font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
.wp-cost__coin { width: 24px; height: 24px; flex: none; }
.wp-cost__balance { margin-top: var(--space-2); text-align: right; font-size: var(--fs-caption); color: var(--text-tertiary); }

/* 入場須知（claim 彈窗內；內容團隊後續調整） */
.wp-notice {
  margin-bottom: var(--space-4);
  padding: var(--space-4) var(--space-5);
  background: var(--bg-primary);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
.wp-notice__title {
  font-size: var(--fs-caption); font-weight: var(--fw-bold);
  letter-spacing: 0.06em; color: var(--text-secondary); margin-bottom: var(--space-2);
}
/* 內容=3 條關鍵摘要（完整條款在 watch-party-terms.html 另開視窗）；上限 280px 讓
   英文版（行數較多）也整組可見，仍保底不撐爆彈窗（超出時內部捲動） */
.wp-notice__list {
  margin: 0; padding-left: var(--space-4); display: grid; gap: var(--space-2);
  max-height: 280px; overflow-y: auto; padding-right: var(--space-2);
  scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent; overscroll-behavior: contain;
}
.wp-notice__list li { font-size: var(--fs-body-sm); color: var(--text-tertiary); line-height: var(--lh-body); }
.wp-notice__list::-webkit-scrollbar { width: 4px; }
.wp-notice__list::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: var(--radius-pill); }
.wp-notice__list::-webkit-scrollbar-track { background: transparent; }
.wp-claim-full__badge {
  display: inline-flex; align-items: center; gap: var(--space-2);
  margin-bottom: var(--space-3); padding: 4px var(--space-3);
  border-radius: var(--radius-pill); border: 1px solid var(--brand-500);
  font-size: var(--fs-caption); font-weight: var(--fw-medium); color: var(--brand-500);
}
.wp-claim-full__dot { width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--brand-500); }

/* 人數上限 — seg 選「設定上限」才展開輸入欄（mirror 私密 seg reveal） */
.wp-capbox { display: none; margin-top: var(--space-4); }
.wp-seg[data-wp-caplimit][data-mode="limited"] ~ .wp-capbox { display: block; }

/* 可觀看地區：僅指定地區→白名單 box、排除特定地區→黑名單 box
   （皆為 chips＝已選國家＋國家選單，同 capbox 揭示法） */
.wp-regionbox { display: none; margin-top: var(--space-4); }
.wp-seg[data-wp-region][data-mode="specific"] ~ .wp-regionbox--allow { display: block; }
.wp-seg[data-wp-region][data-mode="exclude"] ~ .wp-regionbox--block { display: block; }
.wp-chips { display: flex; flex-wrap: wrap; gap: var(--space-2); }
/* 沒半個已選國家時整排收起，別留一條空 gap（國家選單自己往上靠） */
.wp-chips:empty { display: none; }
.wp-chip {
  display: inline-flex; align-items: center;
  padding: 7px var(--space-4) 8px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-subtle);
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.wp-chip.is-on {
  background: rgba(var(--brand-rgb), 0.12);
  border-color: var(--brand-500);
  color: var(--brand-500);
}
/* chips＝已選清單，點一下＝移除 → hover 轉紅預告刪除（✕ 只是 affordance，整顆可點） */
.wp-chip.is-on:hover {
  background: rgba(var(--error-rgb), 0.1);
  border-color: var(--error-500);
  color: var(--error-500);
}
.wp-chip__x {
  width: 10px; height: 10px; flex: none;
  margin-left: var(--space-2);
  background: currentColor;
  -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
          mask: url('assets/icons/close.svg') center / contain no-repeat;
}
/* 黑名單（排除地區）chips＝紅色，讀作「這些被擋掉」，與白名單的橘（開放）區隔 */
.wp-regionbox--block .wp-chip.is-on {
  background: rgba(var(--error-rgb), 0.12);
  border-color: var(--error-500);
  color: var(--error-500);
}
.wp-regionbox--block .wp-chip.is-on:hover { background: rgba(var(--error-rgb), 0.2); }
.wp-regionbox .wp-field__error { margin-top: var(--space-3); }

/* 國家選單（比照後台「選擇國家」）：輸入過濾＋下拉多選清單（.wp-pop 殼） */
.wp-cselect { position: relative; margin-top: var(--space-3); }
.wp-chips:empty + .wp-cselect { margin-top: 0; }
.wp-cselect__input { width: 100%; padding-right: 44px; }
.wp-cselect__caret {
  position: absolute; top: 50%; right: var(--space-4);
  width: 16px; height: 16px; pointer-events: none;
  translate: 0 -50%;
  background: var(--text-tertiary);
  -webkit-mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
  transition: transform 0.15s ease;
}
.wp-cselect:has(.wp-pop:not([hidden])) .wp-cselect__caret { transform: rotate(180deg); }
.wp-cselect__pop { width: 100%; padding: var(--space-2); }
.wp-cselect__list {
  max-height: 280px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 1px;
  padding-right: 2px;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
.wp-cselect__opt {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  text-align: left;
  font-size: var(--fs-body-sm); color: var(--text-secondary);
  border: 0; background: transparent;
  border-radius: var(--radius-sm); cursor: pointer;
}
.wp-cselect__opt:hover { background: var(--glass-bg-default); color: var(--text-primary); }
/* 已在清單中的國家＝橘字＋勾勾；再點一下＝移除 */
.wp-cselect__opt.is-picked { color: var(--brand-500); }
.wp-cselect__opt.is-picked::after {
  content: ''; width: 14px; height: 14px; flex: none;
  background: var(--brand-500);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M2.5 8.5l3.5 3.5 7.5-8' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%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 16 16'%3E%3Cpath d='M2.5 8.5l3.5 3.5 7.5-8' fill='none' stroke='black' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
}
.wp-cselect__none { padding: var(--space-3); font-size: var(--fs-body-sm); color: var(--text-tertiary); }
/* 黑名單的選單：已排除的國家＝紅字＋紅勾，跟上方紅 chips 同語意 */
.wp-regionbox--block .wp-cselect__opt.is-picked { color: var(--error-500); }
.wp-regionbox--block .wp-cselect__opt.is-picked::after { background: var(--error-500); }

/* 地區說明依 seg mode 只顯示當前那句（同 wp-flag--public/private 揭示法） */
.wp-rflag { display: none; }
.wp-seg[data-wp-region][data-mode="global"] ~ .wp-rflag--global { display: block; }
.wp-seg[data-wp-region][data-mode="specific"] ~ .wp-rflag--specific { display: block; }
.wp-seg[data-wp-region][data-mode="exclude"] ~ .wp-rflag--exclude { display: block; }

/* number input + trailing 「人」 unit, hide the spinner for calm UI */
.wp-capfield { display: flex; align-items: center; gap: var(--space-3); }
.wp-capfield .wp-input { flex: 1; min-width: 0; }
.wp-capfield__unit { flex: none; font-size: var(--fs-body-sm); color: var(--text-tertiary); }
.wp-capfield .wp-input::-webkit-outer-spin-button,
.wp-capfield .wp-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.wp-capfield .wp-input[type="number"] { -moz-appearance: textfield; appearance: textfield; }

/* inline field error（如：必填缺漏、人數上限超過平台限制）——通用表單錯誤態 */
.wp-field__error { margin-top: var(--space-2); font-size: var(--fs-caption); color: var(--error-500); }
.wp-input.is-error { border-color: var(--error-500); }
/* 影片未選：選片虛線鈕同樣走錯誤紅框 */
.wp-pick-empty.is-error { border-color: var(--error-500); }
/* 入場券切回「免費」時，自訂價格的必填錯誤一併收起（欄位本身已隱藏） */
.wp-seg[data-wp-price][data-mode="free"] ~ .wp-field__error { display: none; }

/* privacy flag swap — pure CSS off the privacy seg's data-mode */
.wp-flag--private { display: none; }
.wp-seg[data-wp-privacy][data-mode="private"] ~ .wp-flag--public { display: none; }
.wp-seg[data-wp-privacy][data-mode="private"] ~ .wp-flag--private { display: block; }

/* （原「需要分享代碼」switch／codebox／輸碼彈窗樣式已隨手動輸碼路徑移除——
   私密房第一版＝連結即門票；需要時從 git 歷史找回） */
.wp-input.is-error:focus-visible { outline-color: var(--error-500); }

/* ── film: capacity-full ticket state (body[data-claim="full"]) ──────── */
.wp-ticket__full { display: none; }
body[data-claim="full"] .wp-ticket__full { display: block; }
.wp-ticket__badge--full { border-color: var(--warning-500); color: var(--warning-500); }

/* ── room: comment moderation (host/官方管理員 hover 置頂 + 刪除 on each message) ── */
.wproom__msg { position: relative; }
.wproom__msg-acts {
  position: absolute; top: 0; right: 0;
  display: none; align-items: center; gap: var(--space-1);
  opacity: 0; transition: opacity 120ms var(--ease-out);
}
.wproom__msg-pin, .wproom__msg-del {
  width: 24px; height: 24px; flex: none;
  display: grid; place-items: center;
  border: 0; background: var(--bg-tertiary); border-radius: var(--radius-pill);
  color: var(--text-tertiary); cursor: pointer;
  transition: color 120ms var(--ease-out);
}
.wproom__msg-pin svg { display: block; }
.wproom__msg-del::before {
  content: ''; width: 12px; height: 12px; background: currentColor;
  -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
          mask: url('assets/icons/close.svg') center / contain no-repeat;
}
.wproom__msg-pin:hover { color: var(--brand-500); }
.wproom__msg-del:hover { color: var(--error-500); }
/* host + 官方管理員 only — and only on hover/focus of the message */
body[data-role="host"] .wproom__msg-acts,
body[data-role="assistant"] .wproom__msg-acts { display: flex; }
body[data-role="host"] .wproom__msg:hover .wproom__msg-acts,
body[data-role="assistant"] .wproom__msg:hover .wproom__msg-acts,
.wproom__msg-acts:focus-within { opacity: 1; }

/* ── room: pinned message / 宣傳 (host/官方管理員 pin one; everyone sees it) ──── */
.wproom__pinned {
  flex: none;
  display: flex; align-items: flex-start; gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
  background: color-mix(in srgb, var(--brand-500) 8%, var(--bg-secondary));
}
.wproom__pinned[hidden] { display: none; }
.wproom__pinned-icon { flex: none; color: var(--brand-500); margin-top: 1px; }
.wproom__pinned-icon svg { display: block; }
.wproom__pinned-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.wproom__pinned-label {
  font-size: 10px; font-weight: var(--fw-bold); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--brand-500);
}
.wproom__pinned-text { font-size: var(--fs-body-sm); color: var(--text-primary); line-height: var(--lh-body); }
.wproom__pinned-unpin {
  flex: none; width: 22px; height: 22px; display: none; place-items: center;
  border: 0; background: transparent; color: var(--text-tertiary);
  border-radius: var(--radius-pill); cursor: pointer;
}
.wproom__pinned-unpin:hover { background: var(--glass-bg-hover); color: var(--text-primary); }
.wproom__pinned-unpin::before {
  content: ''; width: 12px; height: 12px; background: currentColor;
  -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
          mask: url('assets/icons/close.svg') center / contain no-repeat;
}
/* only host/官方管理員 can unpin; guests just read */
body[data-role="host"] .wproom__pinned-unpin,
body[data-role="assistant"] .wproom__pinned-unpin { display: grid; }

/* ── room: host front camera PiP (bottom-right of the video) ───────────── */
.wproom__cam {
  position: absolute; right: var(--space-4); bottom: var(--space-4);
  display: none; flex-direction: column;
  width: 280px;
  border-radius: var(--radius-md); overflow: hidden;
  border: 1px solid var(--border-default);
  box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.6);
}
body[data-cam="on"] .wproom__cam { display: flex; }
.wproom__cam-feed {
  height: 166px; display: grid; place-items: center;
  color: var(--text-tertiary);
  background:
    radial-gradient(120% 120% at 70% 20%, rgba(255, 163, 63, 0.18), transparent 60%),
    var(--bg-tertiary);
}
.wproom__cam-feed svg { width: 38px; height: 38px; }
.wproom__cam-label {
  display: flex; align-items: center; gap: var(--space-1);
  padding: 3px var(--space-2);
  font-size: var(--fs-caption); color: var(--text-secondary);
  background: rgba(0, 0, 0, 0.55);
}
.wproom__cam-label__dot {
  width: 6px; height: 6px; border-radius: var(--radius-pill); background: var(--success-500); flex: none;
}

/* ── room: lobby host start-play prompt ───────────────────────────────── */
.wproom__lobby-host { display: none; flex-direction: column; align-items: center; gap: var(--space-3); }
body[data-role="host"] .wproom__lobby-host { display: flex; }

/* lobby is the positioning context for the host's self-cam preview (PiP) */
.wproom__screen--lobby { position: relative; }

/* 即將播放：影集預設第一集，可由設定齒輪更換 */
.wproom__upnext { display: flex; flex-direction: column; align-items: center; gap: 2px; margin-top: var(--space-2); }
.wproom__upnext-label { font-size: var(--fs-caption); color: var(--text-tertiary); letter-spacing: 0.04em; }
.wproom__upnext-title { font-size: var(--fs-body); font-weight: var(--fw-medium); color: var(--text-primary); }
.wproom__upnext-note { font-size: var(--fs-caption); color: var(--text-tertiary); }
body[data-role="host"] .wproom__lobby-guest { display: none; }

/* 開始播放影片：時間到（data-playgate=open）才開放；未到時間鎖住並顯示提示 */
.wproom__gate-hint { font-size: var(--fs-caption); color: var(--text-tertiary); }
body[data-playgate="locked"] [data-wp-startgate] { opacity: 0.5; pointer-events: none; }
body[data-playgate="open"] .wproom__gate-hint { display: none; }

/* ── room: host audio (mic toggle + 語音中 indicator) ─────────────────── */
.wproom__btn--mic.is-on { background: var(--brand-500); color: var(--brand-ink); }
/* guests + 官方管理員 can't speak / open camera; only the host controls these */
body[data-role="guest"] .wproom__btn--mic,
body[data-role="assistant"] .wproom__btn--mic,
body[data-role="guest"] .wproom__btn--cam,
body[data-role="assistant"] .wproom__btn--cam { display: none; }
.wproom__btn--cam.is-on { background: var(--brand-500); color: var(--brand-ink); }

.wproom__voice {
  position: absolute; top: var(--space-3); right: var(--space-3);
  display: none; align-items: center; gap: var(--space-2);
  padding: 4px var(--space-3); border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.5);
  font-size: var(--fs-caption); color: var(--text-primary);
}
body[data-mic="on"] .wproom__voice { display: inline-flex; }
.wproom__voice__dot {
  width: 7px; height: 7px; border-radius: var(--radius-pill);
  background: var(--brand-500);
  animation: wproom-mic-pulse 1.2s ease-in-out infinite;
}
@keyframes wproom-mic-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }
@media (prefers-reduced-motion: reduce) { .wproom__voice__dot { animation: none; } }
.wproom__voice-host, .wproom__voice-guest { display: none; }
body[data-role="host"] .wproom__voice-host { display: inline; }
body[data-role="guest"] .wproom__voice-guest { display: inline; }

/* ── room: participant roster + host kick ─────────────────────────────
   Presence count opens a roster popover. Host sees a 踢除 on every other
   row (two-step confirm, no accidental kick); guests see the list read-only.
   Mock — a real build emits a kick on the Ably presence channel. */
.wproom__roster {
  position: absolute; top: calc(100% - var(--space-2)); right: var(--space-4);
  z-index: 20; width: 248px;
  background: var(--bg-secondary);
  border: 1px solid var(--border-default); border-radius: var(--radius-lg);
  box-shadow: 0 16px 40px -12px rgba(0, 0, 0, 0.7);
  overflow: hidden;
  animation: wproom-roster-in 0.14s var(--ease-out, ease);
}
.wproom__roster[hidden] { display: none; }
@keyframes wproom-roster-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: translateY(0); } }
@media (prefers-reduced-motion: reduce) { .wproom__roster { animation: none; } }

.wproom__roster-head {
  display: flex; align-items: baseline; justify-content: space-between;
  padding: var(--space-3) var(--space-4) var(--space-2);
}
.wproom__roster-title { font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--text-primary); }
.wproom__roster-meta { font-size: var(--fs-caption); color: var(--text-tertiary); }
.wproom__roster-list { max-height: 256px; overflow-y: auto; padding-bottom: var(--space-1); }

.wproom__roster-row {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
}
.wproom__roster-row.is-leaving { opacity: 0; transform: translateX(8px); transition: opacity 0.18s ease, transform 0.18s ease; }
.wproom__roster-av {
  flex: none; width: 26px; height: 26px; border-radius: var(--radius-pill);
  display: grid; place-items: center;
  background: var(--glass-bg-hover); color: var(--text-secondary);
  font-size: var(--fs-caption); font-weight: var(--fw-medium);
}
.wproom__roster-name { font-size: var(--fs-body-sm); color: var(--text-primary); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.wproom__roster-host {
  flex: none; font-size: 10px; font-weight: var(--fw-bold); color: var(--brand-ink);
  background: var(--brand-500); padding: 0 5px; border-radius: var(--radius-xs);
}
.wproom__roster-you { flex: none; font-size: var(--fs-caption); color: var(--text-tertiary); }

/* block action — host/官方管理員 only; lives at the end of the row */
.wproom__roster-act { margin-left: auto; flex: none; display: none; }
body[data-role="host"] .wproom__roster-act,
body[data-role="assistant"] .wproom__roster-act { display: flex; align-items: center; }
.wproom__roster-block {
  font-size: var(--fs-caption); color: var(--text-tertiary);
  background: none; border: 1px solid var(--border-default); border-radius: var(--radius-pill);
  padding: 2px var(--space-2); cursor: pointer;
  transition: color var(--ease-out, 0.15s), border-color var(--ease-out, 0.15s), background var(--ease-out, 0.15s);
}
.wproom__roster-block:hover { color: var(--error-500); border-color: var(--error-500); }
.wproom__roster-confirm { display: none; align-items: center; gap: var(--space-1); }
.wproom__roster-row.is-confirming .wproom__roster-block { display: none; }
.wproom__roster-row.is-confirming .wproom__roster-confirm { display: inline-flex; }
.wproom__roster-yes {
  font-size: var(--fs-caption); color: var(--white, #fff); background: var(--error-500);
  border: none; border-radius: var(--radius-pill); padding: 3px var(--space-2); cursor: pointer; font-weight: var(--fw-medium);
}
.wproom__roster-yes:hover { filter: brightness(1.08); }
.wproom__roster-no {
  font-size: var(--fs-caption); color: var(--text-secondary); background: none;
  border: none; padding: 3px var(--space-1); cursor: pointer;
}
.wproom__roster-no:hover { color: var(--text-primary); }

.wproom__roster-foot {
  font-size: var(--fs-caption); color: var(--text-tertiary);
  padding: var(--space-2) var(--space-4) var(--space-3); border-top: 1px solid var(--border-subtle);
}
/* management note removed for every role (guests found it noise) */
body[data-role="host"] .wproom__roster-foot,
body[data-role="assistant"] .wproom__roster-foot,
body[data-role="guest"] .wproom__roster-foot { display: none; }

/* ── room: 黑名單 (已封鎖名單，host/官方管理員) ──────────────────────────────── */
.wproom__blocklist { border-top: 1px solid var(--border-subtle); }
.wproom__blocklist[hidden] { display: none; }
/* guests never block, but gate the panel anyway */
body[data-role="guest"] .wproom__blocklist { display: none; }
.wproom__blocklist-toggle {
  width: 100%; display: flex; align-items: center; justify-content: space-between;
  padding: var(--space-2) var(--space-4);
  background: none; border: 0; cursor: pointer;
  font-size: var(--fs-caption); color: var(--text-secondary);
}
.wproom__blocklist-toggle:hover { color: var(--text-primary); }
.wproom__blocklist-caret {
  width: 14px; height: 14px; flex: none;
  background: currentColor; opacity: 0.7;
  -webkit-mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
  transition: transform var(--ease-out, 0.15s);
}
.wproom__blocklist-toggle[aria-expanded="true"] .wproom__blocklist-caret { transform: rotate(180deg); }
.wproom__blocklist-body { padding-bottom: var(--space-1); }
.wproom__blocklist-body[hidden] { display: none; }
.wproom__blocked-row {
  display: flex; align-items: center; gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
}
.wproom__blocked-row .wproom__roster-name { flex: 1; }
.wproom__blocked-unblock {
  flex: none; font-size: var(--fs-caption); color: var(--text-secondary);
  background: none; border: 1px solid var(--border-default); border-radius: var(--radius-pill);
  padding: 2px var(--space-2); cursor: pointer;
  transition: color var(--ease-out, 0.15s), border-color var(--ease-out, 0.15s);
}
.wproom__blocked-unblock:hover { color: var(--brand-500); border-color: var(--brand-500); }

/* system line in chat (e.g. "X 已被封鎖") */
.wproom__msg--sys { align-items: center; }
.wproom__msg--sys .wproom__msg-body {
  font-size: var(--fs-caption); color: var(--text-tertiary); text-align: center;
  width: 100%; font-style: normal;
}

/* ════════════════════════════════════════════════════════════════════
   13 · REGION + RULE CONSENT (film)
   Region notice + rule-consent gate + blocked state on the ticket page.
   Behaviour in assets/watch-party.js (DS.register), one hook per element.
   ════════════════════════════════════════════════════════════════════ */

/* ── film: rule-consent checkbox (gates 領取入場券) ───────────────────── */
.wp-consent {
  display: flex; align-items: flex-start; gap: var(--space-3);
  margin-bottom: var(--space-4); cursor: pointer;
}
.wp-consent__input { position: absolute; width: 1px; height: 1px; opacity: 0; }
.wp-consent__check {
  flex: none; width: 20px; height: 20px; margin-top: 1px;
  border: 2px solid var(--border-default); border-radius: var(--radius-sm);
  display: grid; place-items: center;
  transition: background 150ms var(--ease-out), border-color 150ms var(--ease-out);
}
.wp-consent__check::before {
  content: ''; width: 10px; height: 6px;
  border-left: 2px solid var(--brand-ink); border-bottom: 2px solid var(--brand-ink);
  transform: translateY(-1px) rotate(-45deg); opacity: 0;
}
.wp-consent__input:checked + .wp-consent__check { background: var(--brand-500); border-color: var(--brand-500); }
.wp-consent__input:checked + .wp-consent__check::before { opacity: 1; }
.wp-consent__input:focus-visible + .wp-consent__check { outline: 2px solid var(--brand-500); outline-offset: 2px; }
.wp-consent__text { font-size: var(--fs-body-sm); color: var(--text-secondary); line-height: var(--lh-body); }
.wp-consent__link {
  border: 0; background: none; padding: 0; cursor: pointer; font: inherit;
  color: var(--brand-500); text-decoration: underline; text-underline-offset: 2px;
}
/* i18n 逐文字節點換字，「我已閱讀並同意」與條款連結間的空隙由 CSS 給（英文才需要；
   中文《》自帶視覺間隔）——別把空格藏在 en.json 值尾（trim 就壞） */
html[lang="en"] .wp-consent__link { margin-left: 0.3em; }

/* ── film: region-blocked ticket state (body[data-claim="blocked"]) ───── */
.wp-ticket__blocked { display: none; }
body[data-claim="blocked"] .wp-ticket__blocked { display: block; }
.wp-ticket__badge--blocked { border-color: var(--error-500); color: var(--error-500); }
/* 地區不符＝經私密連結抵達、從未領票 → 不會在「我參與的」列表，返回箭頭沒有去處 */
body[data-claim="blocked"] .wp-back { display: none; }
/* 已領票＋地區不符（?geo=blocked 疊加）：有票但開演時地區驗證不符、進不了房——
   標題列亮地區不符徽章、倒數與進房組收起、改示 blocked 面板（其文案已涵蓋
   「已持有入場券」情境）。入場券欄維持「已領取」＝保留持票脈絡 */
body[data-claim="claimed"][data-geo="blocked"] .wp-ticket__badge--blocked,
body[data-claim="warmup"][data-geo="blocked"] .wp-ticket__badge--blocked { display: inline-flex; }
body[data-claim="claimed"][data-geo="blocked"] .wp-count,
body[data-claim="warmup"][data-geo="blocked"] .wp-count,
body[data-claim="claimed"][data-geo="blocked"] .wp-ticket__ready,
body[data-claim="warmup"][data-geo="blocked"] .wp-ticket__ready { display: none; }
body[data-claim="claimed"][data-geo="blocked"] .wp-ticket__blocked,
body[data-claim="warmup"][data-geo="blocked"] .wp-ticket__blocked { display: block; }

/* ── film: rules dialog (numbered list) ───────────────────────────────── */
.wp-rules { list-style: none; margin: 0 0 var(--space-5); padding: 0; display: grid; gap: var(--space-4); }
.wp-rules__item { display: flex; gap: var(--space-3); }
.wp-rules__num {
  flex: none; width: 24px; height: 24px; display: grid; place-items: center;
  border-radius: var(--radius-pill); background: var(--brand-500); color: var(--brand-ink);
  font-size: var(--fs-caption); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums;
}
.wp-rules__text { font-size: var(--fs-body-sm); color: var(--text-secondary); line-height: var(--lh-body); }
.wp-rules__text b { color: var(--text-primary); font-weight: var(--fw-medium); }

/* (移除：原「建立活動 → 添加助手」會員 picker。官方管理員改為平台指派角色，
   不再由主持人於建立頁加入。房間端的 data-role="assistant"（官方管理員）保留。) */

/* ═════════════════════════════════════════════════════════════════
   MOBILE RWD · guest flow（≤768 斷點、390 設計基準）
   ------------------------------------------------------------------
   watch-party 頁自己的 RWD 實驗線（其餘頁面仍走 mobile-*.html 分檔）。
   Chrome 規則一律以 body:is(.rf-detail, :has(main.rf-watchparty)) 圈住，side-rail /
   header 只在 watch-party 頁收合 — 其他桌機頁在窄視窗完全不受影響。
   >768 零改動：桌機版面凍結，以下規則只在窄視窗重排。
   ═════════════════════════════════════════════════════════════════ */
@media (max-width: 768px) {

  /* ── chrome：收 side-rail，header 對齊 mobile.html 手機規格 ──
     mobile.html 正確組成（logged-in）＝漢堡＋logo｜搜尋、＋建立、鈴鐺、頭像，
     爆米花 pill 不在 header（已收進頭像選單）。此處 markup 是桌機 header，
     漢堡與＋建立沒有對應元素（先缺），其餘尺寸/組成照抄 mobile.html。 */
  body.has-rail:is(.rf-detail, :has(main.rf-watchparty)) { padding-left: 0; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .side-rail { display: none; }
  body.has-rail:is(.rf-detail, :has(main.rf-watchparty)) .header { margin-left: 0; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-search { display: none; }
  /* 語言切換與觀看歷史手機收起（mobile.html 亦同）。dropdown panel 會被 JS
     搬離 anchor，:has 要錨定「留在原位的 trigger 按鈕」，不能錨 panel 本身 */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__popover-anchor:has([data-dropdown-trigger="lang"]),
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__icon-btn--watch { display: none; }

  /* 高度/內距（mobile.html：header 高 auto、inner min-height 56、8/16 內距。
     33-mobile-tweaks 給 .header padding-inline:16 要歸零，只留 inner 的 16 ——
     [[feedback_header_double_padding_768]] 同款雷） */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header {
    height: auto;
    padding-top: env(safe-area-inset-top);
    padding-inline: 0;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__inner {
    height: auto;
    min-height: 56px;
    padding: 8px max(16px, env(safe-area-inset-right, 0px)) 8px max(16px, env(safe-area-inset-left, 0px));
    gap: 6px;
  }

  /* logo 24px（正典＝refine-mobile.css .m-header__logo-img 24px；has-rail 桌機是 32） */
  body.has-rail:is(.rf-detail, :has(main.rf-watchparty)) .header__logo,
  body.has-rail:is(.rf-detail, :has(main.rf-watchparty)) .header__logo-img { height: 24px; }

  /* 搜尋 icon 手機要顯示（has-rail 桌機藏它、改用整條 header-search；手機相反）。
     用 --search modifier，不用 [aria-label="搜尋"]：i18n 英文會把 aria-label 換成
     "Search"，屬性選擇器會失效。 */
  body.has-rail:is(.rf-detail, :has(main.rf-watchparty)) .header__icon-btn--search { display: inline-block; }

  /* 36px 觸控格、glyph 維持 24px mask（mobile.html 實際渲染即 24px；
     其 inline 的 background-size:18px 是死碼——icon 是 mask 不是 background） */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__icon-btn { width: 36px; height: 36px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__notification-btn { width: 36px; height: 36px; margin-right: 0; }
  /* 鈴鐺換 notification_active.svg（紅點缺口烙在圖裡）。桌機那套「regular ∩
     radial 挖洞」的挖洞座標按 24px 鈕算，放大到 36 會跑位 → 紅點貼線沒縫 */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__notification-btn::before {
    -webkit-mask-image: url('assets/icons/notification_active.svg');
            mask-image: url('assets/icons/notification_active.svg');
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__notification-btn::after { top: 6px; right: 6px; box-shadow: none; }
  /* avatar 30px 實心圓＋前置 6px：其他鈕是 36 框內縮 glyph，等 gap 下 avatar
     視覺上會貼太近（refine-mobile.css 的視覺等距補償，照抄） */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header .avatar { width: 30px; height: 30px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__buttons--logged-in .avatar { margin-left: 6px; }

  /* 間距統一 6px、登入鈕（logged-out 態）照 mobile 正典 */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__actions { gap: 6px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__buttons { gap: 6px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__buttons--logged-in { gap: 6px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__login-btn { height: 36px; padding: 0 10px; font-size: 13px; }
  /* 爆米花 pill：手機不出現在 header（≤1080 收窄階梯已藏，維持即可，勿再放回） */

  /* header→內容間距對齊手機規範 16px（桌機 flush-top 是 32） */
  main.rf-watchparty .section--flush-top { padding-top: 16px; }

  /* ── 狀態式 header：有返回動線（已領票/可進場/官方管理員）→ 1.0 詳情頁式
     back header（只有 ←），logo 那條整條收掉；無返回狀態走上面的 logo header。 */
  body[data-claim="claimed"]:has(main.rf-watchparty) .header,
  body[data-claim="warmup"]:has(main.rf-watchparty) .header,
  body[data-claim="admin"]:has(main.rf-watchparty) .header { display: none; }
  body[data-claim="claimed"] main.rf-watchparty .wp-back,
  body[data-claim="warmup"] main.rf-watchparty .wp-back,
  body[data-claim="admin"] main.rf-watchparty .wp-back {
    display: flex;
    align-items: center;
    position: sticky;
    top: 0;
    z-index: 400;
    box-sizing: border-box;
    /* 整條 bar 對齊手機首頁 header：56 高（16+32+8）；底色交給 ::before 的
       Artlist veil（頁首全透明、捲動才浮現），本體不畫底/線 */
    min-height: calc(56px + env(safe-area-inset-top));
    /* 破出 container 的 16px gutter 滿版成一條 bar（比照 1.0 詳情頁頂欄） */
    margin: 0 -16px var(--space-4);
    padding: calc(16px + env(safe-area-inset-top)) 16px 8px;
  }
  /* Artlist veil（照手機首頁 header／08-header.css 配方）：tint+blur 往下
     feather 24px、mask 淡出無接縫；scroll-timeline 在前 120px 淡入——頁首
     全透明、捲了才「若隱若現」。不支援 scroll-timeline 的瀏覽器 veil 常駐（安全）。 */
  body[data-claim="claimed"] main.rf-watchparty .wp-back::before,
  body[data-claim="warmup"] main.rf-watchparty .wp-back::before,
  body[data-claim="admin"] main.rf-watchparty .wp-back::before {
    content: '';
    position: absolute;
    inset: 0 0 -24px 0;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(180deg, rgba(23, 23, 23, 0.7) 0%, rgba(23, 23, 23, 0) 100%);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    -webkit-mask-image: linear-gradient(180deg,
      rgba(0,0,0,1) 0%, rgba(0,0,0,1) 66%,
      rgba(0,0,0,0.96) 74%, rgba(0,0,0,0.82) 81%,
      rgba(0,0,0,0.58) 88%, rgba(0,0,0,0.28) 94%, rgba(0,0,0,0) 100%);
            mask-image: linear-gradient(180deg,
      rgba(0,0,0,1) 0%, rgba(0,0,0,1) 66%,
      rgba(0,0,0,0.96) 74%, rgba(0,0,0,0.82) 81%,
      rgba(0,0,0,0.58) 88%, rgba(0,0,0,0.28) 94%, rgba(0,0,0,0) 100%);
  }
  @supports (animation-timeline: scroll()) {
    body[data-claim="claimed"] main.rf-watchparty .wp-back::before,
    body[data-claim="warmup"] main.rf-watchparty .wp-back::before,
    body[data-claim="admin"] main.rf-watchparty .wp-back::before {
      animation: header-veil-in linear both;   /* keyframes 在 08-header.css（同 build） */
      animation-timeline: scroll(root);
      animation-range: 0 120px;
    }
  }
  body[data-claim="claimed"] main.rf-watchparty .wp-back .wp-back__label,
  body[data-claim="warmup"] main.rf-watchparty .wp-back .wp-back__label,
  body[data-claim="admin"] main.rf-watchparty .wp-back .wp-back__label { display: none; }  /* 純 ←，同 1.0 */
  /* 圓鈕尺寸對齊 .m-pagebar 的 32px（16+32+8 = 56 正好吃滿 min-height） */
  body:is(.rf-detail, :has(main.rf-watchparty)) .wp-back__btn { width: 32px; height: 32px; padding: 7px; }

  /* ── 頭像選單 → 全螢幕 overlay（照 mobile.html 行為；!important 蓋掉
     portal JS 每次開啟寫入的 inline top/right/maxHeight 定位） ── */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile {
    position: fixed !important;
    inset: 0 !important;
    top: 0 !important; right: 0 !important;
    width: 100% !important;
    max-width: none !important;
    height: 100dvh !important;
    max-height: none !important;
    border: none !important;
    border-radius: 0 !important;
    z-index: 500;
    padding: calc(env(safe-area-inset-top) + 68px) 20px calc(env(safe-area-inset-bottom) + 32px);
    background: rgba(10, 10, 12, 0.92);
    backdrop-filter: blur(28px) saturate(1.2);
    -webkit-backdrop-filter: blur(28px) saturate(1.2);
    overflow-y: auto;
    /* 全螢幕 blur 面板不做位移動畫（半透明面板滑入會閃過亮底），內容各自 fade-up */
    transform: none !important;
    transition: none !important;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile > * {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 260ms ease, transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile.is-open > * { opacity: 1; transform: none; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile.is-open .profile-menu__head    { transition-delay: 40ms; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile.is-open .profile-menu__section { transition-delay: 120ms; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile .profile-menu__close { transform: none; }

  /* close X（watch-party.js 注入）：右上圓鈕，同 mobile.html */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile .profile-menu__close {
    display: grid;
    position: absolute;
    top: calc(env(safe-area-inset-top) + 16px);
    right: 16px;
    width: 32px; height: 32px;
    place-items: center;
    border: none;
    border-radius: var(--radius-pill);
    background: rgba(255, 255, 255, 0.08);
    cursor: pointer;
    z-index: 1;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .profile-menu__close-icon {
    width: 18px; height: 18px;
    background-color: var(--text-tertiary);
    -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
            mask: url('assets/icons/close.svg') center / contain no-repeat;
  }

  /* 我的爆米花 row／觀看歷史（注入列，樣式照 mobile.html；字級 px＝mobile token 值） */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile .profile-menu__li--history { display: list-item; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile .profile-menu__popcorn {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .profile-menu__popcorn .popcorn-section--logged-in {
    display: inline-flex;          /* ≤1080 收窄階梯藏 header pill，選單內這顆要開回來 */
    height: 48px;
    padding: 0 8px 0 16px;
    gap: 8px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .profile-menu__popcorn .popcorn-section__icon { width: 22px; height: 22px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .profile-menu__popcorn .popcorn-section__amount { font-size: 16px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .profile-menu__popcorn .popcorn-section__deposit { width: 32px; height: 32px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .profile-menu__popcorn-label {
    font-family: var(--font-cjk-text);
    font-size: 14px;
    color: var(--text-secondary);
  }
  /* stagger：爆米花 row 跟 mobile.html 一樣排在 head 之後那拍 */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header-dropdown--profile.is-open .profile-menu__popcorn { transition-delay: 80ms; }

  /* ═══ 手機 chrome（watch-party.js 注入）：漢堡＋drawer、＋建立＋底部 sheet ═══
     樣式照 mobile.html 移植；--m-fs-* token 換 px（h2=18/caption=12/body-sm=13/
     body=14/eyebrow=10）。drawer 滿版（RWD 無 430 預覽框）。 */

  /* header 疊層：drawer(300) 之上要看得到 logo＋X → header 提到 400（桌機是 100） */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header { z-index: 400; }

  /* menu-open：鎖背景捲動；header 轉透明（drawer 毛玻璃是唯一玻璃層）、藏右側動作 */
  body.menu-open:is(.rf-detail, :has(main.rf-watchparty)) { overflow: hidden; }
  body.menu-open:is(.rf-detail, :has(main.rf-watchparty)) .header {
    background: transparent;
    border-bottom-color: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  body.menu-open:is(.rf-detail, :has(main.rf-watchparty)) .header__actions {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
  }

  /* ── 漢堡（開合原地變 X）── */
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn {
    position: relative;
    width: 36px; height: 36px;
    display: grid; place-items: center;
    background: transparent;
    border: none;
    color: var(--neutral-300);
    padding: 0;
    flex-shrink: 0;
    z-index: 1;
    margin-left: -8px;   /* 20px icon 內縮 8px → 線條對齊 16px gutter */
    cursor: pointer;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn__icon,
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn__icon::before,
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn__icon::after {
    content: '';
    display: block;
    width: 20px; height: 2px;
    border-radius: 2px;
    background: currentColor;
    transition: transform 300ms cubic-bezier(0.22, 1, 0.36, 1), opacity 180ms ease;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn__icon { position: relative; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn__icon::before { position: absolute; left: 0; top: -6px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn__icon::after  { position: absolute; left: 0; top:  6px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn.is-open .m-menu-btn__icon { background: transparent; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn.is-open .m-menu-btn__icon::before { transform: translateY(6px)  rotate(45deg); }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-menu-btn.is-open .m-menu-btn__icon::after  { transform: translateY(-6px) rotate(-45deg); }

  /* logo 光學微調：貼著漢堡右移 4、上移 2（refine-mobile.css 同款） */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__nav { gap: 6px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__nav .header__logo { margin-left: 4px; transform: translateY(-2px); }

  /* ＋建立 icon 鈕：手機顯示 */
  body:is(.rf-detail, :has(main.rf-watchparty)) .header__icon-btn--m-create { display: inline-block; }

  /* ── drawer（Artlist 式深色毛玻璃全頁選單）── */
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer {
    display: block;
    position: fixed;
    top: 0; bottom: 0; left: 0; right: 0;
    z-index: 300;
    background:
      radial-gradient(120% 55% at 50% 36%, rgba(var(--brand-rgb), 0.06), transparent 72%),
      rgba(10, 10, 12, 0.72);
    backdrop-filter: blur(26px) saturate(1.2);
    -webkit-backdrop-filter: blur(26px) saturate(1.2);
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 300ms ease, visibility 0s linear 300ms;
    overflow: hidden;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity 300ms ease, visibility 0s;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__panel {
    position: absolute;
    inset: 0;
    background: transparent;
    display: flex;
    flex-direction: column;
    padding: calc(env(safe-area-inset-top) + 72px) 0 40px;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    -webkit-mask-image: linear-gradient(to bottom,
      transparent calc(env(safe-area-inset-top) + 60px),
      #000        calc(env(safe-area-inset-top) + 84px));
            mask-image: linear-gradient(to bottom,
      transparent calc(env(safe-area-inset-top) + 60px),
      #000        calc(env(safe-area-inset-top) + 84px));
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group,
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__intro,
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__footer {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity 420ms ease, transform 420ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__group,
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__intro,
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__footer {
    opacity: 1;
    transform: translateY(0);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__group:nth-of-type(1) { transition-delay: 60ms; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__group:nth-of-type(2) { transition-delay: 110ms; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__group:nth-of-type(3) { transition-delay: 160ms; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__group:nth-of-type(4) { transition-delay: 210ms; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__intro  { transition-delay: 260ms; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer.is-open .m-drawer__footer { transition-delay: 300ms; }
  @media (prefers-reduced-motion: reduce) {
    body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group,
    body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__intro,
    body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__footer { transition: opacity 200ms ease; transform: none; }
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group { padding: 18px 0; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group + .m-drawer__group { border-top: 1px solid var(--border-subtle); }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group--primary { padding: 32px 0 40px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__link {
    display: block;
    padding: 13px 40px;
    font-family: var(--font-cjk-text);
    font-size: 18px;
    color: var(--text-primary);
    text-decoration: none;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group--primary .m-drawer__link {
    font-family: var(--font-cjk-display);
    font-weight: var(--fw-bold);
    font-size: 20px;
    line-height: 26px;
    padding: 15px 40px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__lang-section {
    border-top: 1px solid var(--border-subtle);
    padding: 22px 40px 32px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__lang-label {
    color: rgba(255, 255, 255, 0.30);
    font-family: var(--font-cjk-text);
    font-weight: var(--fw-medium);
    font-size: 12px;
    margin-bottom: 12px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__lang {
    display: flex;
    align-items: baseline;
    gap: 10px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__lang-opt {
    background: none;
    border: none;
    padding: 0;
    cursor: pointer;
    font-family: var(--font-cjk-text);
    font-size: 18px;
    font-weight: var(--fw-regular);
    color: var(--text-secondary);
    transition: color 150ms ease;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__lang-opt--latin { font-size: 21px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__lang-opt.is-active {
    color: var(--yellow-500);
    font-weight: var(--fw-bold);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__lang-sep { color: var(--text-tertiary); font-size: 18px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__legal-cols {
    display: flex;
    gap: 24px;
    padding: 22px 40px 15px;
    border-top: 1px solid var(--border-subtle);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__legal-cols .m-drawer__group--legal {
    flex: 1;
    min-width: 0;
    padding: 0;
    border-top: none;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group-label {
    color: var(--yellow-500);
    font-weight: var(--fw-bold);
    font-size: 14px;
    padding: 0 40px;
    margin-bottom: 6px;
    font-family: var(--font-cjk-text);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group--legal .m-drawer__group-label {
    color: rgba(255, 255, 255, 0.30);
    font-weight: var(--fw-medium);
    font-size: 12px;
    padding: 0;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__group--legal .m-drawer__link {
    font-family: var(--font-cjk-text);
    font-weight: var(--fw-regular);
    font-size: 13px;
    color: var(--text-secondary);
    padding: 7px 0;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__intro {
    padding: 22px 40px 16px;
    border-top: 1px solid var(--border-subtle);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__tagline {
    font-family: var(--font-cjk-display);
    font-size: 18px;
    font-weight: var(--fw-bold);
    color: var(--text-primary);
    margin: 0 0 8px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__desc {
    font-family: var(--font-cjk-text);
    font-size: 12px;
    line-height: 1.55;
    color: var(--text-secondary);
    margin: 0 0 14px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__contact {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__contact-icon {
    width: 44px; height: 44px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--radius-pill);
    border: 1px solid var(--border-subtle);
    color: var(--text-secondary);
    transition: color 150ms ease, border-color 150ms ease, background 150ms ease;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__contact-icon .site-footer__social-icon { width: 22px; height: 22px; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__email {
    font-family: var(--font-cjk-text);
    font-size: 12px;
    color: var(--text-secondary);
    margin-left: 4px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__footer {
    padding: 16px 40px 24px;
    border-top: 1px solid var(--border-subtle);
    margin-top: 4px;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__copy,
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__disclaimer {
    font-family: var(--font-cjk-text);
    font-size: 10px;
    line-height: 1.55;
    color: var(--text-tertiary);
    margin: 0;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-drawer__copy { margin-bottom: 6px; }

  /* ── 建立 sheet（底部彈出，user 變體 5 項）── */
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 450;   /* header(400)/drawer(300) 之上 */
    opacity: 0;
    pointer-events: none;
    transition: opacity 240ms ease;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create.is-open { opacity: 1; pointer-events: auto; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__scrim {
    position: absolute;
    inset: 0;
    background: rgba(6, 6, 8, 0.62);
    -webkit-backdrop-filter: blur(4px);
    backdrop-filter: blur(4px);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__sheet {
    position: absolute;
    left: 0; right: 0; bottom: 0;
    background: var(--neutral-800);
    border-radius: 20px 20px 0 0;
    padding: 10px 20px calc(env(safe-area-inset-bottom, 0px) + 20px);
    box-shadow: 0 -12px 40px rgba(0, 0, 0, 0.5);
    transform: translateY(100%);
    transition: transform 320ms cubic-bezier(0.22, 1, 0.36, 1);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create.is-open .m-create__sheet { transform: translateY(0); }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__handle {
    display: block;
    width: 40px; height: 4px;
    margin: 6px auto 16px;
    padding: 0; border: 0;
    border-radius: 999px;
    background: var(--white-alpha-24);
    cursor: pointer;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__item {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px 0;
    text-decoration: none;
    color: inherit;
    -webkit-tap-highlight-color: transparent;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__item:active { opacity: 0.6; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__icon {
    flex-shrink: 0;
    width: 56px; height: 56px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--white-alpha-8);
    color: var(--text-secondary);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__glyph {
    width: 32px; height: 32px;
    background-color: currentColor;
    -webkit-mask: var(--icon) center / contain no-repeat;
            mask: var(--icon) center / contain no-repeat;
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__text { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__title {
    font-family: var(--font-cjk-display);
    font-weight: var(--fw-bold);
    font-size: 18px;
    color: var(--text-primary);
  }
  body:is(.rf-detail, :has(main.rf-watchparty)) .m-create__desc {
    font-family: var(--font-cjk-text);
    font-size: 14px;
    line-height: 1.4;
    color: var(--text-secondary);
  }

  /* ═══ 房間頁 wproom（body.rf-wproom；獨立 scope——它沒有標準 header/side-rail，
     不吃上面的 chrome 規則）：桌機「舞台｜聊天 360px」雙欄 → 手機直疊，
     舞台在上、聊天填滿剩餘高、輸入框釘底（YouTube Live 手機式配置）。 ═══ */
  body.rf-wproom .wproom { height: 100dvh; min-height: 0; overflow: hidden; }  /* 聊天內部捲，頁面不捲 */
  body.rf-wproom .wproom__top { padding: 8px 12px; gap: var(--space-2); }
  body.rf-wproom .wproom__title { font-size: 14px; }
  body.rf-wproom .wproom__body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);   /* 舞台 hug、聊天吃剩餘 */
  }
  body.rf-wproom .wproom__stage { padding: 12px; gap: var(--space-2); }

  /* 直播畫面：桌機 flex 填高 → 手機固定 16:9 */
  body.rf-wproom .wproom__video { flex: none; min-height: 0; aspect-ratio: 16 / 9; }
  /* 等候室/同步/已結束的置中面板：手機給固定高（桌機 flex:1 會把聊天擠掉） */
  body.rf-wproom .wproom__screen { flex: none; }
  body.rf-wproom .wproom__center { flex: none; min-height: 300px; padding: var(--space-5) var(--space-4); }
  /* 等候室暖場文案照斷句走兩行（<br> 在「暖暖場。」後）：第一句 25 全形字
     在 268px 內容盒放不下會折在「聊聊/天」→ 字級降一級＋向兩側出血，
     一句一行。390 實測 25×12=300 ≤ 268+40。 */
  body.rf-wproom .wproom__lobby-guest { font-size: var(--fs-caption); margin-inline: -20px; }
  body.rf-wproom .wproom__center-count { font-size: 40px; }

  /* 控制列：稍縮、時間字讓位給 scrub */
  body.rf-wproom .wproom__ctrl { gap: var(--space-2); }
  body.rf-wproom .wproom__btn { width: 36px; height: 36px; }

  /* 主持人進度條：390 一列擠不下（六顆鈕＋播放資訊把 flex:1 壓成 0 寬）→
     控制列允許換行，scrub 折成自己的一整行（排最後、佔滿寬） */
  body.rf-wproom .wproom__ctrl { flex-wrap: wrap; }
  body.rf-wproom .wproom__scrub { flex: 1 1 100%; order: 10; }

  /* ── 直播＝手機播放器語彙（tap-to-toggle，2026-07-04 定案）──
     桌機 hover 浮層在觸控上沒有入口 → 手機改：影片滿版（stage 歸零內距、
     圓角/側邊框拿掉）、播放/音量疊在影片底部漸層上、幾秒無互動自動隱藏、
     點影片區再浮現（wp-mchrome hook 切 .is-mchrome；?chrome=1 常駐照舊） */
  body.rf-wproom[data-state="live"] .wproom__stage { padding: 0; }
  body.rf-wproom[data-state="live"] .wproom__video { border-radius: 0; border-inline: 0; border-top: 0; }
  body.rf-wproom[data-state="live"] .wproom__ctrl {
    position: absolute; left: 0; right: 0; bottom: 0;
    padding: 40px var(--space-3) var(--space-3);
    background: linear-gradient(180deg, transparent, rgba(0, 0, 0, 0.78));
    border-radius: 0;
    opacity: 0; pointer-events: none; transition: opacity 0.18s ease;
  }
  body.rf-wproom[data-state="live"] .wproom__stage.is-mchrome .wproom__ctrl,
  body.rf-wproom[data-state="live"][data-chrome="1"] .wproom__ctrl { opacity: 1; pointer-events: auto; }
  /* 標籤（共看中＋已直播／語音中）與 chrome 同節奏隱現 */
  body.rf-wproom[data-state="live"] .wproom__stage.is-mchrome .wproom__live,
  body.rf-wproom[data-state="live"] .wproom__stage.is-mchrome .wproom__voice { opacity: 1; }
  /* chrome 出現時主持人 PiP 上移讓出控制列（同桌機讓位邏輯，量照手機鈕高） */
  /* Dolly 拍板：手機 PiP 完全貼齊右下角、不讓位控制列（chrome 會自動隱藏） */
  body.rf-wproom[data-state="live"] .wproom__stage.is-mchrome .wproom__cam { bottom: 0; }

  /* 音量飛出條＝hover/focus 語彙：手機喇叭鈕只做點擊靜音（iOS 網頁不允許
     JS 調音量、音量交給硬體鍵）→ 觸控不展開條，tap 靜音時才不會彈條干擾；
     .is-open（?vol=open 快照強制展開）不在此列，照常可用 */
  body.rf-wproom .wproom__vol--fly:hover .wproom__vol-flyout,
  body.rf-wproom .wproom__vol--fly:focus-within .wproom__vol-flyout { width: 0; opacity: 0; }
  body.rf-wproom .wproom__vol--fly.is-open .wproom__vol-flyout { width: 164px; opacity: 1; }

  /* ── 影片頁領票彈窗（#wpClaim）：手機滿版接管——外框無圓角、貼齊四邊、
     內容內捲；X 釘在視口右上不隨內容捲走（panel＝全螢幕，fixed 即視口） */
  body.rf-detail #wpClaim { padding: 0; }
  body.rf-detail #wpClaim .wp-dialog__panel {
    max-width: none;
    height: 100dvh;
    max-height: none;
    border: 0;
    border-radius: 0;
    padding: calc(var(--space-8) + env(safe-area-inset-top, 0px)) var(--space-4)
             calc(var(--space-6) + env(safe-area-inset-bottom, 0px));
  }
  body.rf-detail #wpClaim .wp-dialog__close {
    position: fixed;
    top: calc(var(--space-4) + env(safe-area-inset-top, 0px));
    right: var(--space-4);
  }

  /* ── create「可觀看地區」三段：390 一排放不下（「排除特定地區」6 字被
     折成兩行斷字）→ 膠囊組滿寬三等分、字級收一級、禁止折字 ── */
  main.rf-watchparty .wp-seg[data-wp-region] { display: flex; width: 100%; }
  main.rf-watchparty .wp-seg[data-wp-region] .wp-seg__btn {
    flex: 1 1 0;
    padding-inline: 0;
    font-size: 13px;
    white-space: nowrap;
    text-align: center;
  }

  /* ── 聊天室收合＝桌機右欄語彙（» 收起、右緣 ‹ 把手）──
     手機聊天在下半屏、把手掛 stage 右緣會浮在影片上 → 收合功能整組關閉，
     聊天常駐；?chat=off 深連結也強制還原，否則被藏又沒把手可展開 */
  body.rf-wproom .wproom__chat-collapse,
  body.rf-wproom .wproom__chatopen { display: none; }
  body.rf-wproom[data-chat="off"] .wproom__body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto minmax(0, 1fr);
  }
  body.rf-wproom[data-chat="off"] .wproom__chat { display: flex; }

  /* 主持人鏡頭 PiP（Dolly 拍板）：完全貼齊影片區右下角（零間距、外緣角不留
     圓角，只留內側左上角）、比桌機小一號。⚠️ 影像高度要跟著等比縮（桌機
     280×166）——沿用桌機高再疊 bottom 讓位會把 PiP 推出矮 stage 頂端蓋住頁首
     （2026-07-06 踩過）。選擇器要含 hover/data-chrome/is-mchrome 變體，
     否則桌機的 bottom:88 讓位規則（specificity 較高）在手機也生效。 */
  body.rf-wproom .wproom__cam,
  body.rf-wproom[data-state="live"] .wproom__stage:hover .wproom__cam,
  body.rf-wproom[data-state="live"][data-chrome="1"] .wproom__cam {
    width: 104px; right: 0; bottom: 0;
    border-radius: var(--radius-md) 0 0 0;
    border-right: 0; border-bottom: 0;
  }
  body.rf-wproom .wproom__cam-feed { height: 62px; }
  body.rf-wproom .wproom__cam-feed svg { width: 22px; height: 22px; }

  /* 聊天：右欄 → 下欄；輸入框吃 safe-area */
  body.rf-wproom .wproom__chat {
    border-left: none;
    border-top: 1px solid var(--border-subtle);
    min-height: 0;
  }
  body.rf-wproom .wproom__chat-input {
    padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  }

  /* 設定彈出層（host 用）：手機夾進視口、內部捲 */
  body.rf-wproom .wproom__set {
    width: min(300px, calc(100vw - 48px));
    max-height: 55vh;
    overflow-y: auto;
  }

  /* 管理操作（刪留言/置頂）：桌機靠 hover 浮現，觸控沒有 hover →
     管理角色（host/官方管理員）常駐顯示，訊息右側預留按鈕位不壓字 */
  body.rf-wproom:is([data-role="host"], [data-role="assistant"]) .wproom__msg-acts { opacity: 1; }
  body.rf-wproom:is([data-role="host"], [data-role="assistant"]) .wproom__msg { padding-right: 60px; }

  /* 手機 gutter：容器 48 → 16 */
  main.rf-watchparty .container { padding-left: 16px; padding-right: 16px; }

  /* ── 邀請主視覺：海報縮、標題降為手機 h3（24px，跨頁慣例）── */
  .wp-film { padding-bottom: var(--space-12); }
  .wp-invite { gap: var(--space-4); margin-bottom: var(--space-6); }
  .wp-invite__poster { width: 104px; border-radius: var(--radius-md); }
  .wp-invite__title { font-size: 24px; line-height: 1.35; }
  .wp-invite__meta { font-size: var(--fs-body-sm); }
  .wp-invite__host { margin-top: var(--space-3); }

  /* ── 主持人排程詳情（watch-party.html .wp-manage）：hero 對齊 wp-invite 的手機
       處理。Phase 1「手機不能開房」改由 main 的 wp-hostenv 機制處理（UA 偵測
       → body[data-env=mobile] → .wp-envwarn 警示＋開房鈕鎖住），不再依斷點藏 CTA ── */
  .wp-event { gap: var(--space-4); }
  .wp-event__poster { width: 104px; border-radius: var(--radius-md); }
  .wp-event__title { font-size: 24px; line-height: 1.35; }
  .wp-event__name { font-size: var(--fs-body-sm); }
  .wp-event__host { margin-top: var(--space-3); }

  /* ── 入場資訊卡 ──（上內距多一階：標題列離卡頂 24，呼吸感） */
  .wp-card { padding: var(--space-6) var(--space-4) var(--space-5); border-radius: var(--radius-lg); }
  /* 已領票（claimed/warmup）標題列不掛「入場券已就緒」徽章：能到這頁＝已持票，
     資訊冗餘（票種列已寫「已領取」）。地區不符/已額滿/官方管理員照常。 */
  body[data-claim="claimed"] .wp-ticket__badge--ready,
  body[data-claim="warmup"] .wp-ticket__badge--ready { display: none; }
  .wp-ticket__head { flex-wrap: wrap; row-gap: var(--space-2); margin-bottom: var(--space-4); }
  /* 標題與狀態膠囊垂直置中：h2 的 UA margin-top（~16px）沒被 base 歸零，
     flex 置中算的是含 margin 的盒 → 標題被推低 8px。base 只歸了 bottom。 */
  .wp-ticket__head .wp-card__title { margin-top: 0; }
  /* 卡標降一階（20→18）：對齊手機 section 標題節奏（mobile 系列頁 18/16/14） */
  .wp-card__title { font-size: 18px; }
  .wp-blocks { margin-bottom: var(--space-6); }
  .wp-block { padding: var(--space-3) var(--space-4); }
  .wp-count { padding: var(--space-4); margin-bottom: var(--space-5); }
  .wp-count__time { font-size: var(--fs-h4); }
  .wp-notice { padding: var(--space-4); }

  /* ── 選單子頁版型 .m-pagebar：無 logo header，← 置左｜標題置中｜動作置右 ──
     高度對齊手機 header 那條（min-height 56 + 8px 直向 padding）；
     底部 hairline「若隱若現」照手機首頁 header：靜止透明、下滑 >8px 才浮現
     （.is-scrolled 由 watch-party.js m-pagebar hook 切，bg 0.78→0.92）。
     不 scope rf-watchparty：泛用給之後的選單頁（兌換中心…）。 */
  body:has(main.rf-watchparty .wp-list) .header { display: none; }
  main.rf-watchparty .wp-list .page-head { display: none; }  /* 標題/發起鈕移入頂欄 */
  /* 頂欄要貼頂：歸零頁面頂部留白（section flush-top 16 + .wp-list 上緣 8 疊在
     bar 上方，讓整塊讀起來像 90px 的胖 header）。內容間距由 bar 的 margin-bottom 給 */
  main.rf-watchparty:has(.m-pagebar) .section--flush-top { padding-top: 0; }
  main.rf-watchparty .wp-list { padding-top: 0; }
  .m-pagebar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: sticky;
    top: 0;
    z-index: 400;
    box-sizing: border-box;
    min-height: calc(56px + env(safe-area-inset-top));  /* 對齊手機 header 高度 */
    margin: 0 -16px var(--space-4);   /* 破出 16px gutter 滿版 */
    /* 上 16 / 下 8：內容列往下坐一點（16+32+8 = 56 正好吃滿 min-height，
       無置中鬆動，上下間距即所見值） */
    padding: calc(16px + env(safe-area-inset-top)) 16px 8px;
    background: rgba(10, 10, 10, 0.78);
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    border-bottom: 1px solid transparent;
    transition: background 200ms var(--ease-out), border-color 200ms var(--ease-out);
  }
  .m-pagebar.is-scrolled {
    background: rgba(10, 10, 10, 0.92);
    border-bottom-color: var(--border-default);
  }
  .m-pagebar .icon-btn { width: 32px; height: 32px; padding: 7px; border-color: transparent; }
  .m-pagebar .icon-btn svg { width: 18px; height: 18px; }
  /* 返回鈕/動作鈕「裸化」（2026-07-15 Dolly 拍板；同日二修：還是太小＋間距不對——
     去了底色的 32px 圓鈕「盒子」仍把 glyph 關在中間，svg viewBox 內建留白再吃一圈，
     墨水離螢幕邊 ~22px 讀起來又小又內縮）。改法＝拆盒：
     盒=glyph+8px 熱區外擴（負 margin 吐回、不佔排版位，熱區 40/35px 仍比 32 大）；
     glyph ←24px、＋19px（plus 墨水佔 viewBox 18/24、arrow 只佔 14/24，19 的＋
     與 24 的 ← 墨水量才等大；0715 三修：28/22 拆盒後偏大，Dolly 收一階）；
     svg 留白用額外負 margin 抵掉，墨水貼齊 16px gutter。
     選擇器帶 .m-pagebar 前綴＝壓過上面 .m-pagebar .icon-btn 的 32px 盒與 18px svg。 */
  .m-pagebar .m-pagebar__back,
  .m-pagebar .m-pagebar__back:hover,
  .m-pagebar .m-pagebar__action,
  .m-pagebar .m-pagebar__action:hover {
    width: auto;
    height: auto;
    padding: 8px;
    margin: -8px;
    background: none;
    border: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  /* 墨水離螢幕邊 20px（0715 Dolly：原對齊 16px gutter，拍板改 20）——
     ← ink 左留白 24×(5/24)＝5px、＋ ink 右留白 19×(3/24)≈2px，負 margin 各含
     8px 熱區外擴的吐回量 */
  .m-pagebar .m-pagebar__back { margin-left: -9px; }
  .m-pagebar .m-pagebar__action { margin-right: -6px; }
  .m-pagebar .m-pagebar__back svg { width: 24px; height: 24px; }
  .m-pagebar__title {
    position: absolute;               /* 絕對置中，不受左右鈕寬影響 */
    left: 50%;
    transform: translateX(-50%);
    font-size: 18px;                  /* 子頁頂欄標題（= 手機 h2） */
    font-weight: var(--fw-bold);
    color: var(--text-primary);
  }
  .m-pagebar__action-icon {
    width: 19px; height: 19px;
    background-color: currentColor;
    -webkit-mask: var(--icon) center / contain no-repeat;
            mask: var(--icon) center / contain no-repeat;
  }

  /* ── 共看列表 toggle：照 mobile-screening 式樣（兩格均分滿版、字級 14），
     不帶 icon（設計決定 2026-07-02） ── */
  main.rf-watchparty [data-wp-listtoggle] { width: 100%; }
  main.rf-watchparty [data-wp-listtoggle] .glass-tabs__item {
    flex: 1 1 0;
    min-width: 0;
    padding-inline: 8px;
    font-size: 14px;
    line-height: 20px;
  }

  /* ── 共看列表卡：狀態膠囊移右上、chevron 收掉，body 用滿寬 ──
     桌機是 poster｜body｜狀態膠囊｜chevron 四欄 flex；390 下膠囊擠壓
     標題致亂斷行 → 膠囊絕對定位到卡片右上（與標題同排），整卡即點擊
     目標，chevron 省下 → body 文字用滿剩餘寬。 */
  .wp-listcard { position: relative; align-items: flex-start; }
  .wp-listcard .wp-status { position: absolute; top: var(--space-4); right: var(--space-4); }
  .wp-listcard__title { padding-right: 104px; }  /* 讓出右上膠囊（最寬「即將開始」4字+點 ≈ 100px） */
  .wp-listcard__chevron { display: none; }

  /* ── 彈窗：留 16 邊距、panel 內距收一級 ── */
  .wp-dialog { padding: var(--space-4); }
  .wp-dialog__panel { padding: var(--space-6); }

  /* ── 預覽面板（dev-only）：吸右下、展開高度夾在視口內，不擋 CTA ──
     頂部留 80px 呼吸：全高展開會頂到吸頂返回鈕（.wp-back），標題列被壓住 */
  .wp-roomdev {
    right: 12px;
    bottom: calc(env(safe-area-inset-bottom, 0px) + var(--space-4));
    max-height: calc(100dvh - 96px - env(safe-area-inset-bottom, 0px));
  }
  /* title.html 有固定底部互動列（.mtd-bar，z-index 180）：chip 抬到列的上方，
     不遮愛心/分享 icon；展開高度同步扣掉底部列 */
  body.rf-detail .wp-roomdev {
    bottom: calc(env(safe-area-inset-bottom, 0px) + 88px);
    max-height: calc(100dvh - 168px - env(safe-area-inset-bottom, 0px));
  }
}

/* 橫持手機（viewport 寬 >768 吃不到上面的手機斷點，但螢幕矮＋觸控）：
   主持人鏡頭 PiP 沿用手機規格——貼齊右下角、104px 等比縮小，
   否則桌機 280px 版本會佔掉 390 高螢幕的一半。 */
@media (max-height: 500px) and (pointer: coarse) {
  body.rf-wproom .wproom__cam,
  body.rf-wproom[data-state="live"] .wproom__stage:hover .wproom__cam,
  body.rf-wproom[data-state="live"][data-chrome="1"] .wproom__cam {
    width: 132px;  /* 橫持比直式（104）大一階——螢幕寬、鏡頭可以更清楚（Dolly 拍板） */ right: 0; bottom: 0;
    border-radius: var(--radius-md) 0 0 0;
    border-right: 0; border-bottom: 0;
  }
  body.rf-wproom .wproom__cam-feed { height: 78px; }
  body.rf-wproom .wproom__cam-feed svg { width: 26px; height: 26px; }
}

/* ─────────────────────────────────────────
   SESSION ALERT — single-active-session specifics
   ------------------------------------------------------------
   The shell (.ds-modal) and the alert layout (.ds-modal--alert / .ds-alert)
   live in assets/ds.css (the DS layer). Both viewpoints are
   `.ds-modal.ds-modal--alert` instances:
     • .session-alert  — 2nd-device conflict panel (+ loading / error)
     • .session-ended  — 1st-device signed-out screen

   This file only adds what the primitive doesn't own:
     1. the conflict panel's loading + error sub-states, reacted to in CSS
        via `.is-loading` / `.is-error` on the .session-alert modal, and
     2. the kicked screen's full-screen-on-mobile treatment.
   A DEV-ONLY demo script toggles the classes; real auth / ds.js drives it.
   See docs/second-device-alert/spec.md.
   ───────────────────────────────────────── */

/* ── DEV-ONLY · demo trigger (grep "session-alert" to remove) ──
   The trigger reuses the watch-party dev-bar shell (.wp-roomdev) and folds in
   the auth toggle as its 「登入狀態」group. This variant only lifts it over the
   modal overlay so scenarios stay switchable while a dialog is open (position is
   the .wp-roomdev default: bottom-right). Compound selector so it wins
   regardless of bundle order vs watch-party.css. */
.wp-roomdev.wp-roomdev--session {
  z-index: 9999;  /* above the session-alert modal overlay (~2000) */
}

/* ── conflict: error message — only in the error sub-state ── */
.session-alert__error {
  display: none;
  margin: 0;
  color: var(--error-500);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
}
.session-alert.is-error .session-alert__error { display: block; }

/* ── conflict: loading row — spinner + text, only while signing in ── */
.session-alert__loading {
  display: none;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  color: var(--text-secondary);
  font-size: var(--fs-body-sm);
}
.session-alert.is-loading .session-alert__loading { display: flex; }
/* dim + block the footer buttons while loading (prevents double-taps) */
.session-alert.is-loading .ds-modal__footer { opacity: 0.5; pointer-events: none; }

/* Scoped spinner: no shared spinner primitive exists yet (COMPONENTS.md lists
   only .ds-progress / .ds-skeleton). Minimal + reduced-motion safe; a future
   .ds-spinner would replace this. */
.session-alert__spinner {
  width: 18px;
  height: 18px;
  border: 2px solid var(--white-alpha-16);
  border-top-color: var(--text-primary);
  border-radius: var(--radius-pill);
  animation: session-alert-spin 0.7s linear infinite;
}
@keyframes session-alert-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .session-alert__spinner { animation-duration: 2s; }
}

/* ── kicked screen (.session-ended): full-screen + opaque on mobile ── */
@media (max-width: 768px) {
  .session-ended.ds-modal { padding: 0; }
  .session-ended .ds-modal__panel {
    max-width: 100%;
    min-height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: center;
    border: none;
    border-radius: 0;
    box-shadow: none;
    background: transparent;
  }
  .session-ended .ds-modal__footer { width: 100%; }
  .session-ended .ds-modal__scrim { background: var(--bg-primary); }
}

/* ── 52-liquid-glass.css ───────────────────────────────────────────────────
   LIQUID GLASS — L's adjusted "Apple-Tahoe" glass material, ported from the
   liquid-glass-kit (2026-07-22). Three co-located pieces make the effect; read
   all three or it looks like "just frost":
     1. KNOBS → the :root --lg-* control panel below (tune here, one source).
     2. CLASS → this file rewires .btn--ghost / .btn--yellow-ghost / .botnav__list
                to WEAR the material (no markup change → every instance upgrades).
     3. SVG   → #liquid-glass refraction filter + per-element size-matched lenses,
                injected at RUNTIME by the engine appended to assets/ds.js. Present
                in NO .css/.html file — so don't read "transparent" just because the
                CSS only shows frost.

   Refraction (backdrop-filter: url() displacement) is CHROMIUM-ONLY by design;
   Safari / Firefox / iOS keep the clean frost + specular shine (engine gates
   itself, never adds .glass-refract). Verify on a REAL screen — headless/devtools
   screenshots lie about backdrop-filter.

   Loads AFTER 02-button.css and 43-bottom-nav.css so it overrides their recipes.
   ─────────────────────────────────────────────────────────────────────────── */

:root {
  /* TINT — the colour wash */
  --lg-fill:        rgba(255, 255, 255, 0.05);      /* default white-5% glass */
  --lg-fill-orange: rgba(var(--brand-rgb), 0.22);   /* orange (yellow-ghost) variant — brand-token driven */
  --lg-fill-nav:    var(--lg-fill);                 /* mobile bottom-nav — 0730 Dolly：改回 L 原版淡色（=kit 預設白 5%）。
                                                       之前為了蓋過雜背景改深（rgba(20,21,27,.58)），拍板退回；
                                                       knob 保留，日後個別頁要深再開。 */
  --lg-fill-menu:   rgba(255, 255, 255, 0.088);     /* 頂部選單膠囊底 — L 站是雙層（膠囊白4% ＋ tabstick 系統
                                                       ::before 白5% 蓋層）疊出來的奶白，這裡取合成值單層寫
                                                       （1−0.96×0.95=0.088）。active 橘水洗是透明的，就靠這層
                                                       墊白才會像 L 一樣發亮（0730 Dolly 拍板跟進；底導仍是
                                                       --lg-fill-nav 白5%，選單刻意亮一階）。 */

  /* FROST — the blur */
  --lg-blur: 8px;
  --lg-sat:  1.5;
  --lg-frost: blur(var(--lg-blur)) saturate(var(--lg-sat));

  /* EDGE — hairline rim（kit 原 token；0730 補港——先前漏移植，
     用到它的 border 整條會因 var 解不開而作廢） */
  --lg-rim: 0.51px solid rgba(255, 255, 255, 0.14);

  /* ACTIVE TAB — L 版 active 膠囊配方（0730 自 endgame.ztor.lx7.com 逐值抄）：
     透明底＋18% 橘漸層水洗＋兩道 0.9 極細橘高光＋淡內陰影、無 blur。
     舊版（--lg-fill-orange 實色底＋10 層 --lg-shine-orange）在暗底上又深又粗
     ——那組 token 留給 .btn--yellow-ghost，tab active 一律改用這組。 */
  --lg-fill-active: linear-gradient(0deg,
      rgba(var(--brand-rgb), 0) 0%,
      rgba(var(--brand-rgb), 0.18) 2.45%,
      rgba(var(--brand-rgb), 0) 126.14%);
  --lg-shine-active:
    inset 1.1px 2.2px 0.5px -1.8px rgba(var(--brand-rgb), 0.9),
    inset -1px -2.2px 0.5px -1.8px rgba(var(--brand-rgb), 0.9),
    inset 2px 3px 2px 0 rgba(0, 0, 0, 0.1);

  /* SHEEN — broad glossy top-light. Kept at 0 to match L's kit: sheen adds a
     top-weighted gloss that reads as a raised/domed button; L's material is FLAT
     frosted glass, so sheen stays off. (Tradeoff: flatter glass is subtler on
     near-black surfaces — that's inherent to L's look; dial this up slightly only
     if a specific dark-bg button needs to pop.) */
  --lg-sheen: 0;

  /* SHINE — 10-layer specular box-shadow: 8 inset gloss/contour layers + 2 outer
     depth drops. This stack IS the visible glossy bevel. */
  /* SHINE — 10-layer specular box-shadow (L's kit spec, verbatim). This stack IS the
     visible glossy bevel. (Reverted 2026-07-22: the「再細一點」thinning experiments are
     rolled back to L's original edge at Dolly's request.) */
  --lg-shine:
    inset 0 0 0 0.64px rgba(255, 255, 255, 0.063),
    inset 1.15px 1.92px 0 -1.28px rgba(255, 255, 255, 0.567),
    inset -1.28px -1.28px 0 -1.28px rgba(255, 255, 255, 0.504),
    inset -1.92px -5.12px 0.64px -3.84px rgba(255, 255, 255, 0.378),
    inset -0.19px -0.64px 2.56px 0 rgba(0, 0, 0, 0.076),
    inset -0.96px 1.6px 0 -1.28px rgba(0, 0, 0, 0.126),
    inset 0 1.92px 2.56px -1.28px rgba(0, 0, 0, 0.126),
    inset 1.28px -4.16px 0.64px -2.56px rgba(0, 0, 0, 0.063),
    0 1px 5px 0 rgba(0, 0, 0, 0.10),
    0 6px 16px 0 rgba(0, 0, 0, 0.08);

  /* SHINE (orange) — same 10-layer bevel, but the 4 specular highlights are a warm
     champagne (255,214,168) instead of pure white, so the rim on the orange button
     reads orange-tinted. Dark depth layers + outer drops stay neutral. (2026-07-22 Dolly) */
  --lg-shine-orange:
    inset 0 0 0 0.64px rgba(255, 214, 168, 0.063),
    inset 1.15px 1.92px 0 -1.28px rgba(255, 214, 168, 0.567),
    inset -1.28px -1.28px 0 -1.28px rgba(255, 214, 168, 0.504),
    inset -1.92px -5.12px 0.64px -3.84px rgba(255, 214, 168, 0.378),
    inset -0.19px -0.64px 2.56px 0 rgba(0, 0, 0, 0.076),
    inset -0.96px 1.6px 0 -1.28px rgba(0, 0, 0, 0.126),
    inset 0 1.92px 2.56px -1.28px rgba(0, 0, 0, 0.126),
    inset 1.28px -4.16px 0.64px -2.56px rgba(0, 0, 0, 0.063),
    0 1px 5px 0 rgba(0, 0, 0, 0.10),
    0 6px 16px 0 rgba(0, 0, 0, 0.08);
}

/* ══════════════════════════════════════════════════════════════════════════
   BUTTONS — .btn--ghost (neutral) + .btn--yellow-ghost (orange CTA)
   Buttons aren't transformed at rest, so fill + frost + shine paint directly on
   the element (kit's ::before split is only needed for FIXED/transformed bars).
   ══════════════════════════════════════════════════════════════════════════ */
.btn--ghost,
.btn--yellow-ghost {
  border-color: transparent;
  text-shadow: none;
  box-shadow: var(--lg-shine);
  -webkit-backdrop-filter: var(--lg-frost);   /* frost-only fallback; refraction added under .glass-refract */
          backdrop-filter: var(--lg-frost);
}
.btn--ghost        { background: var(--lg-fill);        color: var(--text-primary); }
/* orange button wears the warm-tinted specular so its edge highlight reads orange */
.btn--yellow-ghost { background: var(--lg-fill-orange); color: var(--yellow-500); box-shadow: var(--lg-shine-orange); }

/* SHEEN gloss — a broad white top-light on its own layer (behind the label, above
   the frosted fill; .btn already sets isolation:isolate so z-index:-1 stays inside
   the button). This is what makes the glass read on a DARK background, where the
   refraction has no content to bend. Tune with --lg-sheen. */
.btn--ghost::before,
.btn--yellow-ghost::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(255, 255, 255, var(--lg-sheen)) 0%,
    rgba(255, 255, 255, calc(var(--lg-sheen) * 0.32)) 16%,
    rgba(255, 255, 255, 0) 48%);
}

/* Refraction (Chromium): each button gets its OWN size-matched lens via --lg-refract */
.glass-refract .btn--ghost,
.glass-refract .btn--yellow-ghost {
  -webkit-backdrop-filter: blur(var(--lg-blur)) var(--lg-refract, url(#liquid-glass)) saturate(var(--lg-sat));
          backdrop-filter: blur(var(--lg-blur)) var(--lg-refract, url(#liquid-glass)) saturate(var(--lg-sat));
}

/* State deltas — raise the fill + add a faint text glow so hover/press still read.
   (matches the specificity of 02-button's :hover:not(:disabled) rules; loads later → wins) */
.btn--ghost:hover:not(:disabled)  { background: rgba(255, 255, 255, 0.10); text-shadow: rgba(255, 255, 255, 0.45) 0 0 10px; }
.btn--ghost:active:not(:disabled) { background: rgba(255, 255, 255, 0.14); }

.btn--yellow-ghost:hover:not(:disabled)  { background: rgba(var(--brand-rgb), 0.30); text-shadow: rgba(var(--brand-rgb), 0.40) 0 0 10px; }
.btn--yellow-ghost:active:not(:disabled) { background: rgba(var(--brand-rgb), 0.40); }

/* Header popcorn/price pill — the one glass button in the header. It carried the OLD
   ghost recipe (bottom-gradient + blur-20 + old shine), so after .btn--ghost moved to
   L's kit the header no longer matched the page. Bring it onto the SAME neutral glass.
   (Loads after 08-header.css → these win by order.) */
.popcorn-section {
  background: var(--lg-fill);
  box-shadow: var(--lg-shine);
  -webkit-backdrop-filter: var(--lg-frost);
          backdrop-filter: var(--lg-frost);
}
.popcorn-section:hover { background: rgba(255, 255, 255, 0.10); }

/* disabled — leave 02-button.css's muted disabled recipe in force (it drops the
   frost and de-tints; higher specificity :disabled selectors win). */

/* ── HONEST FALLBACK — no frosting without contrast (extends 31-material-fallbacks;
   this file loads later so it must re-assert the guard for the buttons above). */
@media (prefers-reduced-transparency: reduce) {
  .btn--ghost,
  .btn--yellow-ghost {
    -webkit-backdrop-filter: none; backdrop-filter: none;
    background: rgba(20, 20, 20, 0.95);
  }
  .btn--yellow-ghost { color: var(--yellow-500); }
}

/* ── MOBILE TOP TABS（0730 Dolly）：手機版 片庫/比賽/共創 頂部 .glass-tabs 膠囊
   改穿 L 淡色玻璃——只動材質，尺寸/間距/字級不動；桌機 gate 在外不受影響。
   結構差異：cocreate/zorigin 系的描邊在外層 .cocreate-tabbar::after（膠囊被橫滑
   淡出 mask，框搬外層才不被吃）→ 那層同步 L 描邊＋shine，雙寫 class 抬權重
   贏 page rwd css；片庫的膠囊直接被 mask，shine 在淡出側會跟底色一起柔化
   （＝該頁描邊既有的取捨，接受）。 */
@media (max-width: 767.98px) {
  /* :not([data-mylib-status-nav])——my-library 的兩排狀態 chips（全部/尚未觀看…、
     收藏篩選）0730 Dolly 拍板維持舊設計裸 chips（29 檔的裸化配方），不穿玻璃；
     排除在皮膚外讓它回原 cascade，別在頁面層再蓋一層反向規則。
     :not(:where(.glass-tabs--plain))——0803 Dolly：這層皮膚是給「頁面頂部導覽
     膠囊」的，彈窗內的次級篩選列（送禮全集/單集、好友分頁）用的 --plain 變體
     本來就無框無底，被掃進來會在手機出現一塊 radius 0 的奶白長方框。同前例
     排除回原 cascade。⚠️ 必須包 :where()：裸 :not() 會把權重抬到 (0,3,0)，
     下面「shine 單層化」的 .cocreate-tabbar .glass-tabs (0,2,0) 就關不掉
     內層 shine，cocreate/zorigin 又疊回雙層（ui-parity 抓包）。 */
  .glass-tabs:not([data-mylib-status-nav]):not(:where(.glass-tabs--plain)) {
    /* 0730 Dolly：底改 --lg-fill-menu（L 的雙層合成奶白）——active 橘水洗透明，
       墊白不夠橘就沉進黑底，與 endgame 對不上 */
    background: var(--lg-fill-menu);
    /* 0730 Dolly 拍板：描邊以底部導航為準——無 rim（蓋掉 25 的 1px 外框），
       輪廓感只靠 shine 第一層的細內圈，柔邊 */
    border: none;
    box-shadow: var(--lg-shine);
    -webkit-backdrop-filter: var(--lg-frost);
            backdrop-filter: var(--lg-frost);
  }
  .glass-refract .glass-tabs:not([data-mylib-status-nav]):not(:where(.glass-tabs--plain)) {
    -webkit-backdrop-filter: blur(var(--lg-blur)) var(--lg-refract, url(#liquid-glass)) saturate(var(--lg-sat));
            backdrop-filter: blur(var(--lg-blur)) var(--lg-refract, url(#liquid-glass)) saturate(var(--lg-sat));
  }
  /* cocreate/zorigin 系外框層：只留不被 mask 的 shine，描邊同樣拿掉（底導基準） */
  .cocreate-tabbar.cocreate-tabbar::after {
    border: none;
    box-shadow: var(--lg-shine);
  }
  /* shine 單層化（0730 Dolly 抓包：上下高光比片庫亮）——外層 ::after 已帶 shine，
     內層膠囊必須關掉，否則內外疊加雙倍亮（比賽頁膠囊沒被 mask 時整條全疊）。
     fill/frost 仍在內層（::after 沒畫底，不重複）。 */
  .cocreate-tabbar .glass-tabs {
    box-shadow: none;
  }

  /* ── 片庫類型列（探索/AI/…、資料庫 OTT 列）：靜止時裸 chips、下滑才長出玻璃
     （0730 Dolly）。scope 在 .library-stickytop 包裝內＝只影響會吸頂的那條；
     my-library 頁中段的同 class 狀態列不在 wrapper 裡，維持恆玻璃。
     狀態鍵＝.is-scrolled（頁面 JS y>2 才給）——不能用 .is-stuck：這組群本來就
     貼著 header，top<=headerH 從第 0px 就成立，靜止時也是 stuck。
     做法＝only 壓掉未捲動態：.is-scrolled 時本條不匹配，材質自動回上面
     .glass-tabs 基準（含 .glass-refract 折射鏈，不在捲動態重寫它才不搶權重）。
     backdrop-filter 用 blur(0) sat(1) 當「關」——none↔filter 清單不可插值，
     同型清單才有淡入（折射 url() 鏈仍會跳變，試開中先接受）。 */
  .library-stickytop:not(.is-scrolled) .glass-tabs.library-genres {
    background: transparent;
    box-shadow: none;
    -webkit-backdrop-filter: blur(0px) saturate(1);
            backdrop-filter: blur(0px) saturate(1);
  }
  /* transition 放恆匹配的選擇器上，兩個方向（出現/收掉）都吃得到 */
  .library-stickytop .glass-tabs.library-genres {
    transition: background-color 260ms var(--ease-out),
                box-shadow 260ms var(--ease-out),
                -webkit-backdrop-filter 260ms var(--ease-out),
                backdrop-filter 260ms var(--ease-out);
  }
}

/* ── MOBILE BOTTOM NAV — the live bar is `body.rf-mobile .m-bottomnav`, whose
   material is inlined (verbatim) into each assets/rwd/<page>-mobile.css copy
   (refine-mobile.css layer, higher specificity + loads last). Its liquid-glass
   material is rewritten THERE — reading the --lg-* panel above — so it can beat
   that layer's specificity/order. The dead `.botnav` (43-bottom-nav.css) Phase-2
   foundation is never rendered, so it's intentionally NOT skinned here. */

/* ── 全站通用「喜歡 / 收藏」互動效果（DS 行為 data-like / data-save）──
   1.0 Figma 分鏡 → 2.0 配色。點擊：
     切 is-active → icon 外框↔實心＋換色（喜歡=highlight rose、收藏=brand 橘）
     ＋pop 彈跳（is-bounce）＋噴發爆裂光線/粒子（.dsburst，ds.js 注入、動畫結束移除）。
   行為在 assets/ds.js（DS.register like / save）。

   商城願望清單愛心（data-wish／data-pdp-wish，assets/wishlist.js＋
   shop-render.js／shop-detail-render.js 驅動,「非」ds.js）0728 統一特效：
   選擇跟 like 同色（rose highlight-500），不是原本 save 的橘——
   Dolly 0728 拍板「一個站不要有兩種特效」，這裡只是延伸下面既有的
   selector／keyframe 去涵蓋 data-wish/data-pdp-wish，不是另開一份新效果。
   data-wish/data-pdp-wish 刻意不掛 data-like（ds.js 的 DS.register('like', …)
   會自動抓 [data-like] 並掛上自己的 click handler，商城愛心已有自己的
   click 邏輯(store/aria-pressed)，兩邊都掛等於一元素雙 hook 互踩）。 */

/* 觸發鈕需為定位祖先，burst overlay 才能以中心錨定；burst 會超出鈕範圍→別 overflow:hidden */
[data-like],
[data-save],
[data-wish],
[data-pdp-wish] { position: relative; }

/* 作用色（icon 是 mask + currentColor → 換 color 即換色） */
[data-like].is-active { color: var(--highlight-500); }
[data-save].is-active { color: var(--brand-500); }
/* 商城願望清單愛心：統一走 like 的 rose（非 save 的橘）。實心切換靠下面
   is-active svg 規則（商城心是裸 inline SVG，不是 mask icon）。 */
[data-wish].is-active,
[data-pdp-wish].is-active { color: var(--highlight-500); }
[data-wish].is-active svg,
[data-pdp-wish].is-active svg { fill: currentColor; stroke: currentColor; }

/* pop 彈跳作用在帶 inline icon 變數的元素——[style*="--ico"] 子字串同時命中
   --ico（detail/留言）與 --icon（short rail/商城），別改回 --icon（會漏 detail 排）。
   商城願望清單的心＝裸 svg（無 inline 變數），另列 [data-wish]/[data-pdp-wish] svg（0730 兩線彙整聯集） */
[data-like] [style*="--ico"],
[data-save] [style*="--ico"],
[data-wish] svg,
[data-pdp-wish] svg {
  transform-origin: center;
  will-change: transform;
}
[data-like].is-bounce [style*="--ico"],
[data-save].is-bounce [style*="--ico"],
[data-wish].is-bounce svg,
[data-pdp-wish].is-bounce svg {
  animation: ds-like-pop 0.45s var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1));
}
@keyframes ds-like-pop {
  0%   { transform: scale(1); }
  15%  { transform: scale(0.78); }
  45%  { transform: scale(1.28); }
  70%  { transform: scale(0.92); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  [data-like].is-bounce [style*="--ico"],
  [data-save].is-bounce [style*="--ico"],
  [data-wish].is-bounce svg,
  [data-pdp-wish].is-bounce svg { animation: none; }
}

/* ── CSS-class mask 型 surface：icon 無 inline var，is-active 時在這裡換實心 ──
   （inline --icon/--ico 型如 short rail / detail / 留言由 ds.js 直接換 var，不需這段）
   顏色一律走 [data-like/save].is-active 的 color → icon currentColor 自動上色。 */
.post-card__stat.is-active .post-card__stat-icon--heart,
.community-card__stat.is-active .community-card__stat-icon--heart {
  -webkit-mask-image: url('assets/icons/heart_fill.svg');
          mask-image: url('assets/icons/heart_fill.svg');
}
.post-card__bookmark.is-active .post-card__bookmark-icon,
.community-card__action.is-active .community-card__action-icon--bookmark {
  -webkit-mask-image: url('assets/icons/bookmark_active.svg');
          mask-image: url('assets/icons/bookmark_active.svg');
}

/* ── 爆裂 overlay：鈕中心噴發的光線 + 粒子 ── */
.dsburst {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
  pointer-events: none;
  z-index: 3;
  color: var(--burst-color, var(--highlight-500));
}
.dsburst__ray,
.dsburst__dot {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  background: currentColor;
}
/* 光線：從中心往外的短棒（--a 由 ds.js 逐支設角度） */
.dsburst__ray {
  width: 2px;
  height: 7px;
  margin: -3.5px 0 0 -1px;
  border-radius: 2px;
  transform: rotate(var(--a)) translateY(-16px) scaleY(0.5);
}
.dsburst.is-firing .dsburst__ray {
  animation: ds-burst-ray 0.5s var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards;
}
/* 光線：從心外圍（半徑16）往外放射，不疊在心上 → 乾淨 */
@keyframes ds-burst-ray {
  0%   { opacity: 0; transform: rotate(var(--a)) translateY(-16px) scaleY(0.5); }
  30%  { opacity: 1; }
  100% { opacity: 0; transform: rotate(var(--a)) translateY(-27px) scaleY(1); }
}
/* 粒子：小圓點，起點就在心外圍、均勻往外飛散 */
.dsburst__dot {
  width: 4px;
  height: 4px;
  margin: -2px 0 0 -2px;
  border-radius: 50%;
  transform: rotate(var(--a)) translateY(-17px) scale(0.4);
}
.dsburst.is-firing .dsburst__dot {
  animation: ds-burst-dot 0.6s var(--ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards;
}
@keyframes ds-burst-dot {
  0%   { opacity: 0; transform: rotate(var(--a)) translateY(-17px) scale(0.4); }
  35%  { opacity: 1; transform: rotate(var(--a)) translateY(-24px) scale(1); }
  100% { opacity: 0; transform: rotate(var(--a)) translateY(-30px) scale(0.2); }
}
@media (prefers-reduced-motion: reduce) {
  .dsburst { display: none; }
}

/* 喜歡/收藏/願望愛心不畫焦點橘框（Dolly 0728）：全域 :focus-visible 品牌環
   （01-base-preamble）在點擊/觸控後殘留在 icon 鈕上是噪音——狀態本身（實心
   ＋彈起＋粒子）就是回饋。做法照 staging 線 41-short-play 的既有文法：局部
   關閉、不動全域環。 */
:is([data-like], [data-save], [data-wish], [data-pdp-wish]):focus-visible { outline: none; }


/* ── eShop port (step 1/4 — foundation only, no pages wired yet) ──
   Ported from L/Jeff's endgame prototype (draft/eshop/FE/prototype),
   renumbered 55+ per ztor_ds_one_component_per_element / f2e numbering
   convention — PROTO's own 36/45-48/52 numbers are already other pages
   in f2e, so this is a fresh block, not a slot-in. See
   scratchpad/eshop-port-step1.md for the full PROTO→f2e mapping table. */
/* DS-DRAWER — canonical reusable slide-over drawer primitive. [COMPONENT]
   ════════════════════════════════════════════════════════════════════════
   The COMPONENTS.md "drawer consolidation target", realised. Right slide-over
   on desktop, bottom-sheet on phone. Scrim + panel; behaviour (open/close/
   focus-trap/scroll-lock) in assets/ds-drawer.js. Depth via border + glass
   per the brand "no drop shadows" rule (uses the sanctioned --glass-shadow).
   Stacks at --z-drawer (above sticky chrome, below modal).
   ──────────────────────────────────────────────────────────────────────── */
.ds-drawer {
  position: fixed;
  inset: 0;
  z-index: var(--z-drawer, 300);   /* PORT FIX (step3): --z-drawer was never defined in f2e
                                       tokens.css (PROTO's z-index token system wasn't adopted —
                                       f2e hardcodes literal z-index per component). Without a
                                       fallback this resolved to auto, so the pdp-ref size-guide
                                       drawer wasn't reliably stacked above page content. Fallback
                                       matches PROTO's --z-drawer:300 (same tier as watch/notif
                                       drawers, css/components/10-drawer.css & 11-drawer-2.css). */
  visibility: hidden;
  pointer-events: none;
}
.ds-drawer.is-open { visibility: visible; pointer-events: auto; }

.ds-drawer__scrim {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  -webkit-backdrop-filter: blur(2px);
          backdrop-filter: blur(2px);
  opacity: 0;
  /* scrim trails the panel out (light returns after the sheet clears) */
  transition: opacity calc(var(--dur-panel-exit, 280ms) + 60ms) ease;
}
.ds-drawer.is-open .ds-drawer__scrim {
  opacity: 1;
  /* and leads it in — the room finishes dimming while the sheet is still landing */
  transition: opacity calc(var(--dur-panel, 480ms) * 0.8) ease;
}

.ds-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: min(440px, 92vw);
  display: flex;
  flex-direction: column;
  background: var(--bg-secondary);
  /* 0802 Dolly：右側抽屜只有一種長相——對齊購物車／結帳抽屜（64-cart.css）。
     原本兩處不一樣的地方：① 左緣線用 --border-strong（白 22%）比購物車的
     --border-default（白 12%）亮一倍，是畫面上唯一一條會被看見的亮線；
     ② --glass-shadow 帶一道 rgba(225,238,255,.3) 的 1px inset 內光（那是給
     玻璃件調的），疊在左緣線外側變成第二條邊。改成購物車那道純外陰影。 */
  border-left: 1px solid var(--border-default);
  box-shadow: -28px 0 70px rgba(0, 0, 0, 0.6);
  transform: translateX(100%);
  /* asymmetric: base = EXIT (quick — users want gone); .is-open = ENTER (slow, mass) */
  transition: transform var(--dur-panel-exit, 280ms) var(--ease-emphasized);
}
.ds-drawer.is-open .ds-drawer__panel {
  transform: none;
  transition: transform var(--dur-panel, 480ms) var(--ease-panel, cubic-bezier(0.32, 0.72, 0, 1));
}

.ds-drawer__head {
  display: flex;
  /* 標題底下可能還有一行副標（尺寸指南＝商品名）。置中會讓關閉鈕對齊「標題
     ＋副標」整塊的中線，看起來比標題低半行（實測差 20px）——購物車與選規格
     面板都是靠上對齊標題那一行。 */
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-6) var(--space-4);
  border-bottom: 1px solid var(--border-subtle);
}

/* 標題／副標從來沒有規則，吃的是瀏覽器預設：h2 = 24px ＋ 上下各 19.92px 的
   UA margin，把 head 撐到 160px（購物車是 80），標題也被推離關閉鈕。對齊
   購物車的 .ztor-cart__title（22/700/1.3）與選規格面板的副標。 */
.ds-drawer__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 22px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
.ds-drawer__sub {
  margin: 4px 0 0;
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}

.ds-drawer__close {
  flex-shrink: 0;
  width: 32px;                              /* = 購物車的關閉鈕；靠上對齊時
                                               也讓它的中線最接近標題那一行 */
  height: 32px;
  border: none;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);   /* = 購物車／選規格面板的關閉鈕 */
  color: var(--text-secondary);
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 150ms var(--ease-out), color 150ms var(--ease-out);
}
.ds-drawer__close:hover { background: rgba(255, 255, 255, 0.14); color: var(--text-primary); }
.ds-drawer__close:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.ds-drawer__close svg { width: 16px; height: 16px; }

.ds-drawer__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: var(--space-5) var(--space-6) var(--space-6);   /* 左右 24 = 購物車 body */
}

/* ── Pinned foot (the CTA region) ──
   A flex-shrink:0 sibling of __body that composes .ds-actionbar
   (css/components/ds-actionbar.css). The primitive (assets/ds-drawer.js)
   relocates each step's .cc-stepview__foot / .completion-foot out of the
   scrolling __body and into here, so the primary CTA is pinned to the panel
   bottom on desktop AND phone while the body scrolls under it. Created +
   populated by the registry; hidden until it holds a CTA. */
.ds-drawer__foot { flex: 0 0 auto; }   /* layout/skin comes from .ds-actionbar */

/* ── Step indicator (numbered) — reuses brand accent only ── */
.cc-step {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  padding: 0;
  list-style: none;
}
.cc-step__item {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.cc-step__dot {
  width: 22px;
  height: 22px;
  border-radius: var(--radius-pill);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-latin);
  font-size: 12px;
  font-weight: var(--fw-bold);
  background: var(--white-alpha-8);
  color: var(--text-tertiary);
  border: 1px solid var(--border-default);
}
.cc-step__item--active { color: var(--text-primary); }
.cc-step__item--active .cc-step__dot {
  background: var(--yellow-500);
  color: var(--yellow-ink);
  border-color: var(--yellow-500);
}
.cc-step__item--done { color: var(--text-secondary); }
.cc-step__item--done .cc-step__dot {
  background: rgba(255, 163, 63, 0.16);
  color: var(--yellow-500);
  border-color: rgba(255, 163, 63, 0.36);
}
.cc-step__label { white-space: nowrap; }

/* ── Step views inside the drawer ── */
.cc-stepview { display: flex; flex-direction: column; gap: var(--space-4); }
.cc-stepview__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.cc-stepview__lead {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--text-secondary);
}
.cc-stepview__foot {
  margin-top: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.cc-stepview__foot .btn { width: 100%; }

/* reassurance / info callout — reward-only + no-charge messaging */
.cc-callout {
  position: relative;
  padding: var(--space-3) var(--space-4);
  padding-left: calc(var(--space-4) + 24px);   /* gutter for the absolute icon */
  border-radius: var(--radius-md);
  background: rgba(255, 163, 63, 0.10);
  border: 1px solid rgba(255, 163, 63, 0.28);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  line-height: var(--lh-body-sm);
  color: var(--text-secondary);
}
.cc-callout--info {
  background: rgba(59, 130, 246, 0.10);
  border-color: rgba(59, 130, 246, 0.28);
}
.cc-callout strong { color: var(--text-primary); font-weight: var(--fw-bold); }
.cc-callout__icon {
  position: absolute;
  left: var(--space-4);
  /* 0807：與第一行文字水平置中。padding-top 12 ＋(行框 23 − icon 16)/2 ≒ 4
     （原本的 +2 讓 icon 中心比中文字中心高 1.5px）。CJK 字身在 em 框裡置中，
     所以對齊行框中心即可，不必再補光學位移。 */
  top: calc(var(--space-3) + 4px);
  width: 16px;
  height: 16px;
  background-color: var(--yellow-500);
  -webkit-mask: url('assets/icons/info.svg') center / contain no-repeat;
          mask: url('assets/icons/info.svg') center / contain no-repeat;
}
.cc-callout--info .cc-callout__icon { background-color: var(--info-500); }
.cc-callout--err { background: rgba(239, 68, 68, 0.10); border-color: rgba(239, 68, 68, 0.30); }
.cc-callout--err .cc-callout__icon { background-color: var(--error-500); }
.cc-callout--err strong { color: var(--error-500); font-variant-numeric: tabular-nums; }
.cc-sca { background: var(--bg-card); border: 1px solid var(--border-default); border-radius: var(--radius-md); padding: var(--space-4); display: flex; flex-direction: column; gap: 4px; }
.cc-sca__bank { font-family: var(--font-cjk-display); font-weight: var(--fw-bold); color: var(--text-primary); }
.cc-sca__row { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }

/* tier picker (radio list) */
.cc-pick { margin: 0; padding: 0; border: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.cc-pick__legend { padding: 0; font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.cc-pick__opt {
  display: grid;
  grid-template-columns: auto 1fr;
  column-gap: var(--space-3);
  align-items: start;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: border-color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.cc-pick__opt:hover { border-color: var(--border-strong); }
.cc-pick__radio { margin-top: 3px; accent-color: var(--yellow-500); }
.cc-pick__radio:checked ~ .cc-pick__row .cc-pick__name { color: var(--text-primary); }
.cc-pick__opt:has(.cc-pick__radio:checked) {
  border-color: var(--yellow-500);
  background: rgba(255, 163, 63, 0.07);
}
.cc-pick__row { grid-column: 2; display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.cc-pick__name { font-family: var(--font-cjk-display); font-weight: var(--fw-bold); font-size: var(--fs-body); color: var(--text-secondary); }
.cc-pick__price { font-family: var(--font-latin); font-weight: var(--fw-bold); color: var(--yellow-500); font-variant-numeric: tabular-nums; }
.cc-pick__perks { grid-column: 2; margin-top: 4px; font-family: var(--font-cjk-text); font-size: var(--fs-caption); line-height: var(--lh-body-sm); color: var(--text-tertiary); }
/* perks render as separate nodes (so each localises); CSS supplies the separator */
.cc-pick__perk:not(:first-child)::before { content: " · "; }
.cc-pick__meta { grid-column: 2; margin-top: var(--space-1); }
.cc-pick__slots { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); font-variant-numeric: tabular-nums; }
.cc-pick__slots--full { color: var(--warning-500); font-weight: var(--fw-medium); }
/* sold-out tiers (or in-stock tiers in waitlist mode) are shown but not selectable */
.cc-pick__opt--disabled { opacity: 0.55; }
.cc-pick__opt--disabled,
.cc-pick__opt--disabled .cc-pick__radio { cursor: not-allowed; }
.cc-pick__opt--disabled:hover { border-color: var(--border-default); background: transparent; }

/* ── Phone — bottom sheet ── */
@media (max-width: 767.98px) {
  .ds-drawer__panel {
    top: auto;
    bottom: 0;
    right: 0;
    left: 0;
    width: 100%;
    height: auto;
    max-height: 90vh;
    border-left: none;
    border-top: 1px solid var(--border-strong);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    transform: translateY(100%);
  }
  .ds-drawer.is-open .ds-drawer__panel {
    transform: none;
    transition: transform var(--dur-sheet-m, 550ms) var(--ease-panel, cubic-bezier(0.32, 0.72, 0, 1));   /* an 85vh panel needs the settle */
  }
  .ds-drawer__panel { transition: transform 300ms var(--ease-emphasized); }  /* mobile exit */
  /* grab pill — and unlike a decoration, it drags (ds.js sheet-drag) */
  .ds-drawer__head { position: relative; padding-top: calc(var(--space-4) + 10px); touch-action: none; }
  .ds-drawer__head::before {
    content: "";
    position: absolute; top: 8px; left: 50%; transform: translateX(-50%);
    width: 40px; height: 4px; border-radius: 4px;
    background: rgba(255, 255, 255, 0.22);
  }
}

/* while a finger owns the panel, the transition lets go (1:1, interruptible) */
.ds-drawer__panel.is-dragging { transition: none; }
/* the body's scroll never chains into the page (kills the iOS arrival rubber-band) */
.ds-drawer__body { overscroll-behavior: contain; }

/* While a drawer is open, suppress the floating bottom-nav pill so a phone
   bottom-sheet's footer CTA is never overlapped by it. */
body.ds-drawer-open .botnav { display: none; }

/* PORT FIX (step4): background scroll-lock while any overlay is open.
   PROTO's own ds.js has a generic LOCK_CLASSES scroll-lock utility keyed off
   'cart-open'/'ds-drawer-open' (its assets/ds.js ~line 419) — NOT ported,
   ds.js is out of scope for this port (site-wide file, do-not-touch). This
   codebase's existing convention for the same job is a plain CSS
   `body.<state>-open { overflow: hidden }` rule (see 44-mobile-drawer.css's
   body.mdrawer-open, ds.css's html.wp-dialog-open) — no JS position:fixed/
   scrollY dance. Follows that convention so the shop cart drawer (cart.js
   toggles body.cart-open) and the auth login-gate (auth.js/ds-drawer.js
   toggle body.ds-drawer-open) both stop background scroll while open. */
body.cart-open,
body.ds-drawer-open { overflow: hidden; }

/* 觀看歷史 / 通知 header drawers (10-drawer.css / 11-drawer-2.css) toggle
   `.is-open` on their own wrapper, not a body class — their open/close JS is
   inline per-page (shop.html/zorigin.html), out of scope for this CSS-only
   pass. :has() reads that existing state to apply the same scroll-lock
   convention as the line above, without adding any JS. (2026-07-28, header
   drawer geometry-unification pass.) */
body:has(> .watch-drawer.is-open),
body:has(> .notif-drawer.is-open) { overflow: hidden; }

@media (prefers-reduced-motion: reduce) {
  .ds-drawer__scrim,
  .ds-drawer__panel { transition: none; }
}

/* ════════════════════════════════════════════════════════════════
   ds-toast.css — single global toast (window.DSToast). Bottom-centre,
   glass material, slides up + fades. Reused by the PDP and checkout.
   ════════════════════════════════════════════════════════════════ */
.ds-toast {
  position: fixed;
  left: 50%;
  bottom: calc(28px + env(safe-area-inset-bottom, 0px));
  z-index: var(--z-modal, 1200);   /* PORT FIX (step3): --z-modal undefined in f2e tokens.css
                                       (see 55-ds-drawer.css note) — fallback matches PROTO's
                                       --z-modal:1200 so the add-to-cart toast reliably stacks
                                       above everything, including the pdp-sheet (z-index:1400
                                       is still higher, which is correct: the toast should read
                                       under an open configurator sheet, not over it). */
  display: flex;
  align-items: center;
  gap: var(--space-3);
  max-width: min(92vw, 440px);
  padding: var(--space-3) var(--space-4);
  /* 0729 unified toast recipe (Dolly): white-16 glass PILL base (matches the
     .rent-toast Figma spec) + this file's glass depth kit (backdrop/border/
     shadow). --radius-pill is deliberately NOT zeroed by theme-sharp, so the
     toast stays rounded on the sharp shop pages too. Same recipe in
     48-rent-modal.css (.rent-toast) and 46-my-library.css (.mylib-toast). */
  border-radius: var(--radius-pill);
  background: var(--white-alpha-16);
  -webkit-backdrop-filter: var(--glass-backdrop);
          backdrop-filter: var(--glass-backdrop);
  border: var(--glass-border);
  box-shadow: var(--glass-shadow);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  opacity: 0;
  transform: translate(-50%, 14px);
  pointer-events: none;
  transition: opacity 220ms var(--ease-out), transform 220ms var(--ease-out);
}
.ds-toast.is-shown { opacity: 1; transform: translate(-50%, 0); pointer-events: auto; }
.ds-toast__dot {
  flex: none;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  /* 0731 Dolly：對齊 L 版示範卡——實心綠圓盤＋深色勾（0729 的裸綠勾定版作廢）。
     盤=success-500、勾=neutral-900（currentColor），toast＝完成頁 cc-success 的
     迷你版；svg 尺寸回歸 57 檔元件預設 62%，比例與完成頁一致。
     同步 .rent-toast__check（48）與 .rf-toast__dot（refine-shop.css）。 */
  border-radius: var(--radius-pill);
  background: var(--success-500);
  color: var(--neutral-900);
}
/* 24px 迷你盤的光學補償（0731 Dolly）：勾照元件預設 62%（≈15px）在小盤上偏小，
   放大到 20px（勾跨距≈盤的 37%）；三顆 toast（ds/rent/rf）同值。 */
.ds-toast__dot .ds-success-tick__svg { width: 20px; height: 20px; }
.ds-toast__msg { min-width: 0; }
.ds-toast__link {
  flex: none;
  margin-left: var(--space-1);
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--yellow-500);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}
.ds-toast__link:hover { color: var(--yellow-400); }
.ds-toast__link:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; border-radius: var(--radius-xs); }

@media (prefers-reduced-motion: reduce) {
  .ds-toast { transition: opacity 180ms ease; transform: translate(-50%, 0); }
}

/* Phones: lift above the floating bottom-nav pill (43-bottom-nav.css) so the
   toast never sits under it. Desktop/tablet hide the pill → keep the 28px rest. */
@media (max-width: 767.98px) {
  .ds-toast { bottom: var(--botnav-clearance, calc(84px + env(safe-area-inset-bottom, 0px))); }
}

/* ════════════════════════════════════════════════════════════════
   ds-success-tick.css — ONE reusable animated success tick.

   Drop-in for any SUCCESS confirmation (not selection / status).
   Plays on mount: the glyph pops in with a slight overshoot, then the
   checkmark strokes itself on in one confident pass. No loop, no
   confetti — a small celebration, then stillness.

   ── Markup contract ──────────────────────────────────────────────
   Host owns the disc (background + colour, from the success-green
   token). Drop this SVG INSIDE the host element:

     <span class="cc-success__check ds-success-tick" aria-hidden="true">
       <svg class="ds-success-tick__svg" viewBox="0 0 36 36">
         <path class="ds-success-tick__check" fill="none"
               d="M10 18.5 l5 5 l11 -12"/>
       </svg>
     </span>

   The check stroke inherits the host's `color` (currentColor) so it
   matches whatever disc it sits on — green ✓ on the cc-success tinted
   disc, dark ✓ on the toast's solid dot. Override with --ds-tick-check
   if a specific contrast colour is needed.

   GPU-safe: animates transform / opacity / stroke-dashoffset only —
   never layout. Reduced-motion → fully visible, no pop, no draw.
   ════════════════════════════════════════════════════════════════ */

.ds-success-tick {
  /* glyph pop: scale in 0→1 with a slight overshoot, on mount */
  transform: scale(0);
  transform-origin: center;
  animation: ds-tick-pop 260ms cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
  /* opacity rides in fast so nothing flashes at scale 0 */
  opacity: 0;
}

.ds-success-tick__svg {
  width: 62%;
  height: 62%;
  display: block;
  overflow: visible;
}

/* The checkmark — strokes on via dashoffset after the pop settles.
   pathLength is normalised to 1 in markup so the dash math is
   resolution-independent. */
.ds-success-tick__check {
  stroke: var(--ds-tick-check, currentColor);
  stroke-width: 4;
  stroke-linecap: round;
  stroke-linejoin: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: ds-tick-draw 300ms var(--ease-out, cubic-bezier(0.23, 1, 0.32, 1)) 240ms forwards;
}

@keyframes ds-tick-pop {
  0%   { transform: scale(0);   opacity: 0; }
  60%  { opacity: 1; }
  100% { transform: scale(1);   opacity: 1; }
}

@keyframes ds-tick-draw {
  from { stroke-dashoffset: 1; }
  to   { stroke-dashoffset: 0; }
}

/* Reduced motion — appear statically, fully visible, no pop / no draw. */
@media (prefers-reduced-motion: reduce) {
  .ds-success-tick {
    transform: none;
    opacity: 1;
    animation: none;
  }
  .ds-success-tick__check {
    stroke-dashoffset: 0;
    animation: none;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   IMAGE-LOAD SHIMMER  ·  .ds-imgwait (+ grid entrance)   [SHOP DELTA]
   ════════════════════════════════════════════════════════════════════════
   PORT NOTE (2026-07-27, eShop step1): this is NOT the full PROTO
   ds-skeleton.css — f2e already has an identical `.ds-skeleton` base
   primitive at css/components/ds-skeleton.css (hand-extracted, byte-for-byte
   the same core rules: shapes, widths, @keyframes ds-shimmer, reduced-motion
   fallback). Re-importing the whole PROTO file would duplicate that base and
   fight it in the cascade. Only the genuinely NEW pieces PROTO added on top
   are ported here:
     1. `.ds-imgwait` — an image-decode shimmer overlay (PROTO's `imgskel` DS
        behaviour toggles it; that JS behaviour is NOT ported in this step —
        see step1 report, "待下一步" — so this CSS is inert until a future
        step registers the `imgskel` DS component or wires it manually).
     2. `[data-entrance]` grid-card entrance keyframes, used by shop-render's
        product/creator grids (`.shop-card` / `.creator-card`).
   Depends on tokens already present in f2e: --skeleton-surface, --ease-settle,
   and the shared `@keyframes ds-shimmer` from the existing ds-skeleton.css
   (which loads earlier in the cascade — safe, @keyframes are global).
   ════════════════════════════════════════════════════════════════════════ */

/* ── image-load shimmer ──────────────────────────────────────────────
   A media box wears .ds-imgwait while its <img> is still decoding —
   ds.js's `imgskel` behaviour adds it at attach time and fades it out
   on the img's load/error (`--done` → 240ms opacity → classes removed).
   Overlay via ::after: the img keeps its box (zero layout shift), the
   shimmer samples the SAME fixed light field as .ds-skeleton, so page
   skeletons and image mattes move as one surface. */
.ds-imgwait { position: relative; }
.ds-imgwait::after {
  content: "";
  position: absolute; inset: 0; z-index: 1;
  pointer-events: none;
  border-radius: inherit;                            /* clip to the media shape */
  background-color: var(--skeleton-surface);
  background-image: linear-gradient(100deg,
    transparent 40%, rgba(255, 255, 255, 0.05) 50%, transparent 60%);
  background-repeat: no-repeat;
  background-size: 200% 100%;
  background-attachment: fixed;
  animation: ds-shimmer 1.4s linear infinite;
  opacity: 1;
  transition: opacity 450ms var(--ease-settle, cubic-bezier(0.22, 1, 0.36, 1));
}
.ds-imgwait--done::after { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .ds-imgwait::after { animation: none; background-image: none; }
}

/* ── grid card entrance — the skeleton-to-content moment ─────────────
   The renderer marks the grid [data-entrance] on its FIRST fill only
   (filter/sort re-renders stay instant — a repeated action must not
   replay theatre). Keyframes are safe here because the gate makes the
   animation one-shot. First six cards cascade; the rest (below the
   fold at phone widths) share one delay so nothing queues offscreen. */
@media (prefers-reduced-motion: no-preference) {
  [data-entrance] > .shop-card,
  [data-entrance] > .creator-card { animation: ds-card-in 600ms var(--ease-settle) both; }
  [data-entrance] > :nth-child(2) { animation-delay: 55ms; }
  [data-entrance] > :nth-child(3) { animation-delay: 110ms; }
  [data-entrance] > :nth-child(4) { animation-delay: 165ms; }
  [data-entrance] > :nth-child(5) { animation-delay: 220ms; }
  [data-entrance] > :nth-child(6) { animation-delay: 275ms; }
  [data-entrance] > :nth-child(n+7) { animation-delay: 330ms; }
}
@keyframes ds-card-in {
  from { opacity: 0; transform: translateY(18px); }
}

/* PACKAGE-TIER — co-create reward-tier card (.pkg). [COMPONENT]
   ════════════════════════════════════════════════════════════════════════
   A reward/pledge tier on the co-create inner page: name + price + perks
   checklist + slots-left + CTA (支持方案 / 額滿→加入候補). Reward-only — these
   are co-creation rewards, never investments. Composes from existing tokens
   (accent, success/state colours, spacing, radius); no new token needed.
   data-size aware via the --ds-scale convention.
   ──────────────────────────────────────────────────────────────────────── */
.pkg-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--gap-card, var(--space-5));
}
@media (max-width: 767.98px) {
  .pkg-grid { grid-template-columns: 1fr; }
}

.pkg {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: calc(var(--space-5) * var(--ds-scale, 1));
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  cursor: pointer;
  text-align: left;
  transition: border-color 200ms var(--ease-out), transform 160ms var(--ease-out);
}
.pkg:hover { border-color: var(--border-default); transform: translateY(-3px); }
.pkg:active { transform: translateY(-1px); }
.pkg:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
/* hovering the card brings the quiet cue forward — the card is the button */
.pkg:hover .pkg__select { color: var(--yellow-400); }
.pkg:hover .pkg__select-arrow { transform: translateX(3px); }

.pkg--popular { border-color: rgba(255, 163, 63, 0.4); }
.pkg__flag {
  position: absolute;
  top: 0;
  right: var(--space-5);
  transform: translateY(-50%);
  padding: 4px var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--yellow-500);
  color: var(--yellow-ink);
  font-family: var(--font-cjk-text);
  font-size: 11px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
}

.pkg__head {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
}
.pkg__name {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.pkg__price {
  font-family: var(--font-latin);
  font-size: 26px;
  line-height: 1.1;
  font-weight: var(--fw-bold);
  color: var(--yellow-500);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.5px;
}

.pkg__perks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  flex: 1;
}
.pkg__perk {
  position: relative;
  padding-left: var(--space-6);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: var(--lh-body-sm);
  color: var(--text-secondary);
}
.pkg__perk::before {
  content: "";
  position: absolute;
  left: 0;
  top: 3px;
  width: 16px;
  height: 16px;
  background-color: var(--yellow-500);
  -webkit-mask: url('assets/icons/movie.svg') center / contain no-repeat;
          mask: url('assets/icons/movie.svg') center / contain no-repeat;
}

.pkg__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: auto;
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-subtle);
}
.pkg__slots {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.pkg__slots--full { color: var(--warning-500); font-weight: var(--fw-medium); }

/* Quiet selectable cue — NOT a primary button. Four of these read as a list,
   not as competing CTAs; the single orange action lives in the funding band. */
.pkg__select {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  flex-shrink: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  color: var(--yellow-500);
}
.pkg__select-arrow { transition: transform 160ms var(--ease-out); }
.pkg__select--wait { color: var(--text-secondary); }

/* Full tier — quiet the card so the live tiers lead the eye. */
.pkg--full { opacity: 0.92; }
.pkg--full .pkg__price { color: var(--text-secondary); }
.pkg--full .pkg__perk::before { background-color: var(--text-tertiary); }

@media (prefers-reduced-motion: reduce) {
  .pkg { transition: none; }
  .pkg:hover { transform: none; }
}

/* ════════════════════════════════════════════════════════════════
   pdp-sheet.css — bundle configurator, slide-up sheet (mock parity).
   Every bundle component is a row (thumb + name + option radios);
   configurable rows carry the same data-cidx/data-vval controls as the
   inline picker, so shop-detail-render.js's wire() handlers drive both.
   Recovered 2026-07-02 from hand-edits made directly in the generated
   components.css (the rebuild had silently dropped them) — this file is
   now the single source. Rendered by bundleSheetHtml().
   ════════════════════════════════════════════════════════════════ */
/* ════════ Bundle configurator — slide-up sheet (mock parity) ════════ */
.pdp-sheet-scrim {
  position: fixed; inset: 0; z-index: 1400;
  background: rgba(0, 0, 0, 0.62);
  -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
  opacity: 0; pointer-events: none;
  transition: opacity 360ms ease;                                /* exit: light returns after the sheet clears */
}
.pdp-sheet-scrim.is-open { opacity: 1; pointer-events: auto; transition: opacity 450ms ease; }
.pdp-sheet {
  position: fixed; left: 50%; bottom: 0; z-index: 1410;
  /* 高度＝內容高，上限留 70px 給頂部（0804 Dolly 拍板）。
     ⚠️ 這跟 L 的 endgame 版不同：他那版是**固定** min(80vh,720px)，一列規格的單品
        也撐滿 720，下面一大片空白（我們手機版一直是這個症狀）。改成內容高之後
        單品是矮矮一條、四件套組長到接近滿版，超過才由 __body 捲動。
     dvh 寫在 vh 後面：不支援 dvh 的瀏覽器吃前一條，支援的吃後一條（手機網址列
     收合時 vh 不會變、dvh 會，用 dvh 才不會被瀏覽器 chrome 蓋住 CTA）。 */
  width: min(780px, 100%); height: auto;
  max-height: calc(100vh - 70px);
  max-height: calc(100dvh - 70px);
  display: flex; flex-direction: column;
  background: var(--bg-secondary, #141414);
  border: 1px solid var(--border-subtle, rgba(255, 255, 255, 0.08)); border-bottom: 0;
  border-radius: 26px 26px 0 0;
  box-shadow: 0 -24px 80px rgba(0, 0, 0, 0.6);
  transform: translate(-50%, 100%);
  transition: transform 300ms cubic-bezier(0.32, 0.72, 0, 1);   /* exit — users want gone */
  will-change: transform;
  /* __foot 是實色條，不裁切會把面板的圓角畫成方的（桌機置中彈窗四角都圓，
     一眼就露）；面板內沒有需要外溢的浮層，安全。 */
  overflow: hidden;
}
.pdp-sheet.is-open {
  transform: translate(-50%, 0);
  transition: transform 560ms cubic-bezier(0.32, 0.72, 0, 1);   /* enter — mass, settle */
}
.pdp-sheet__grab { width: 40px; height: 4px; border-radius: 4px; background: rgba(255, 255, 255, 0.22); margin: 12px auto 4px; flex: none; }
/* the grab + head are the drag surface (ds.js sheet-drag) — a promise kept */
.pdp-sheet__grab, .pdp-sheet__head { touch-action: none; }
.pdp-sheet.is-dragging { transition: none; }
.pdp-sheet__body { overscroll-behavior: contain; }
.pdp-sheet__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; padding: 6px 24px 16px; border-bottom: 1px solid var(--border-subtle, rgba(255, 255, 255, 0.07)); flex: none; }
.pdp-sheet__title { margin: 0; font-family: var(--font-cjk-display, inherit); font-weight: 700; font-size: 20px; color: var(--text-primary); }
.pdp-sheet__sub { margin: 4px 0 0; font-size: 13px; color: var(--text-tertiary, #888); }
.pdp-sheet__close { flex: none; width: 38px; height: 38px; display: grid; place-items: center; border: 0; border-radius: var(--radius-pill, 999px); background: rgba(255, 255, 255, 0.06); color: var(--text-secondary, #bbb); cursor: pointer; transition: background 0.2s; }
.pdp-sheet__close:hover { background: rgba(255, 255, 255, 0.12); }
.pdp-sheet__close svg { width: 18px; height: 18px; }
.pdp-sheet__body { flex: 1 1 auto; overflow-y: auto; padding: 20px 24px 24px; display: flex; flex-direction: column; gap: 14px; -webkit-overflow-scrolling: touch; }

.pdp-sheet-row { position: relative; display: grid; grid-template-columns: 84px 1fr; gap: 16px; padding: 14px; border: 1px solid var(--border-subtle, rgba(255, 255, 255, 0.08)); border-radius: var(--radius-xl, 20px); background: var(--bg-primary, #101010); transition: border-color 0.25s cubic-bezier(0.32, 0.72, 0, 1); }
/* 「這列選好了」原本另外畫一條左側橘線；每列本來就有 .pdp-sheet-row__status
   會把選好的規格摘要寫出來並轉橘（單品還有底部 CTA 亮起），橘線是同一件事講
   第二次。0802 Dolly 拍板拿掉。 */
.pdp-sheet-row.is-flagged { border-color: var(--error-500, #e5484d); animation: pdp-shake 320ms var(--ease-out, ease); }
.pdp-sheet-row.is-included { opacity: 0.82; }
.pdp-sheet-row__thumb { width: 84px; height: 105px; border-radius: var(--radius-md, 12px); overflow: hidden; background: radial-gradient(120% 90% at 50% 15%, #262626, #0f0f0f); align-self: start; }
.pdp-sheet-row__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pdp-sheet-row__body { min-width: 0; display: flex; flex-direction: column; gap: 10px; }
.pdp-sheet-row__name-line { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }
.pdp-sheet-row__name { font-weight: 600; font-size: 15px; color: var(--text-primary); }
/* 樣式來自商品頁的 .pdp-variant__guide（同一顆按鈕、同一套視覺，見
   63-shop-detail.css）；這裡只補面板版型需要的兩件事：商品名長的時候不准被
   壓縮、跟商品名對齊視覺中線（name-line 是 baseline，inline-flex 的
   baseline 落在 icon 上會浮高）。 */
.pdp-sheet-row__guide { flex: none; align-self: center; }
.pdp-sheet-row__field { display: flex; flex-direction: column; gap: 8px; }
.pdp-sheet-row__flabel { font-size: 12px; color: var(--text-secondary, #9a9a9a); }
.pdp-sheet-row__flabel b { color: var(--text-tertiary, #888); font-weight: 400; }
.pdp-sheet-row__opts { display: flex; flex-wrap: wrap; gap: 8px; }
.pdp-sheet-row__status { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-tertiary, #888); }
.pdp-sheet-row__status.is-done { color: var(--yellow-500, #FFA33F); }
.pdp-sheet-row__status.is-included { color: var(--text-tertiary, #888); }

.pdp-sheet__foot { flex: none; display: flex; align-items: center; gap: 16px; padding: 16px 24px; padding-bottom: max(16px, env(safe-area-inset-bottom)); border-top: 1px solid var(--border-subtle, rgba(255, 255, 255, 0.08)); background: rgba(14, 14, 14, 0.92); -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px); }
.pdp-sheet__info { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.pdp-sheet__dots { display: flex; gap: 5px; }
.pdp-sheet-dot { width: 22px; height: 4px; border-radius: 2px; background: rgba(255, 255, 255, 0.16); transition: background 0.25s var(--ease-out); }
.pdp-sheet-dot.is-on { background: var(--yellow-500, #FFA33F); }
.pdp-sheet__count { font-size: 13px; color: var(--text-secondary, #a8a8a8); font-variant-numeric: tabular-nums; }
.pdp-sheet__count b { color: var(--text-primary, #ececec); }
.pdp-sheet__cta { flex: none; min-height: 50px; padding: 0 26px; border: 0; border-radius: var(--radius-pill, 999px); font-family: var(--font-cjk-text, inherit); font-weight: 700; font-size: 15px; cursor: pointer; background: rgba(255, 255, 255, 0.14); color: var(--text-secondary, #9a9a9a); transition: background 0.25s cubic-bezier(0.32, 0.72, 0, 1), color 0.25s, transform 0.12s ease; }
.pdp-sheet__cta:active { transform: scale(0.98); }
.pdp-sheet__cta.is-ready { background: #fff; color: #0c0c0c; box-shadow: 0 8px 28px rgba(0, 0, 0, 0.4); }
.pdp-buy__add-chev { transition: transform 0.25s cubic-bezier(0.32, 0.72, 0, 1); }
.pdp-buy__add:hover .pdp-buy__add-chev { transform: translateX(3px); }
/* ── 尺寸指南推入層 ──────────────────────────────────────────────────
   bsPushRef() 從 0729 就在切 [hidden] / .is-pushed，但這一層**從來沒有
   CSS**（0802 實測：position:static、無底色，尺寸表直接接在選項下面把
   面板撐到 1491px、CTA 被擠出視窗、返回鈕是個裸的系統方鈕）——這就是
   「點尺寸指南很卡」的實體。照 shop-detail-render.js 檔頭原本宣告的行為
   做出來：蓋在面板內容之上、自己捲動、可返回。
   ⚠️ [hidden] 會被 display:flex 蓋掉，必須明寫（站上重複踩過的雷）。 */
.pdp-sheet__ref {
  position: absolute; inset: 0; z-index: 2;
  display: flex; flex-direction: column;
  background: var(--bg-secondary, #141414);
  border-radius: inherit;
}
/* 進出都不做滑入動態——像換頁一樣直接切（0803 Dolly）。
   用 :not(.is-pushed) 而非只靠 [hidden]：bsPopRef() 移除 .is-pushed 後要等
   400ms 才設 hidden（原本留給退場動畫的），沒有這條返回時指南會多蓋 0.4 秒。 */
.pdp-sheet__ref:not(.is-pushed) { display: none; }
.pdp-sheet__ref-head {
  flex: none; display: flex; align-items: center; gap: 12px;
  padding: 18px 24px 16px;
  border-bottom: 1px solid var(--border-subtle, rgba(255, 255, 255, 0.07));
}
.pdp-sheet__ref-back {
  flex: none; width: 38px; height: 38px; display: grid; place-items: center;
  border: 0; border-radius: var(--radius-pill, 999px);
  background: rgba(255, 255, 255, 0.06); color: var(--text-secondary, #bbb);
  cursor: pointer; transition: background 0.2s;
}
.pdp-sheet__ref-back:hover { background: rgba(255, 255, 255, 0.12); }
.pdp-sheet__ref-back svg { width: 18px; height: 18px; }
.pdp-sheet__ref-title { margin: 0; font-family: var(--font-cjk-display, inherit); font-weight: 700; font-size: 18px; color: var(--text-primary); }
.pdp-sheet__ref-sub { margin: 2px 0 0; font-size: 13px; color: var(--text-tertiary, #888); }
.pdp-sheet__ref-body { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: 20px 24px 28px; -webkit-overflow-scrolling: touch; }

/* ── 桌機也是由下往上的 sheet（0804 Dolly：改回 L 的版本）────────────────
   0802 二輪曾把桌機改成畫面置中的彈窗（560 寬），0804 拍板改回——L 的
   endgame（商品頁「選擇你的組合」）一直都是這個由下往上的 sheet，780 寬、
   底部貼齊、上緣圓角 26。整段 @media (min-width:769px) 覆寫已移除，桌機與
   手機共用上面那組基準規則，不再分岔。
   ⚠️ 拖曳把手 .pdp-sheet__grab 跟著回到桌機（L 那版也有）。站上目前**沒有**
      任何腳本綁它的拖曳關窗（CSS 註解提到的 ds.js sheet-drag 查無實作），
      兩個平台都只是視覺把手——之後真的要做手勢時，桌機那顆再一起決定去留。 */

/* 尺寸指南推進來時把面板拉到上限高度（0804 Dolly）。
   那一層是 position:absolute; inset:0，會被面板**當下**的高度框住：單品面板
   只有 380 高的話，整張尺寸表會被擠進一條窄縫。推入時直接吃滿可用高度。
   ⚠️ 用 :has() 而不是讓 JS 加 class——顯隱狀態已經寫在 .is-pushed 上了，
      再讓腳本寫一個只為了外觀的 hook 就是把樣式邏輯搬進 JS（站規禁止）。 */
.pdp-sheet:has(> .pdp-sheet__ref.is-pushed) {
  height: calc(100vh - 70px);
  height: calc(100dvh - 70px);
}

/* 單品沒有「已選 N / M」的進度資訊，左側那格是空的——CTA 若維持內容寬度會縮在
   右下角像個附註。沒有資訊要並排時就讓它獨佔整列。 */
.pdp-sheet__info:empty { display: none; }
.pdp-sheet__info:empty + .pdp-sheet__cta { flex: 1 1 auto; }

@media (prefers-reduced-motion: reduce) {
  .pdp-sheet, .pdp-sheet-scrim, .pdp-sheet-row, .pdp-sheet-dot, .pdp-sheet__cta { transition: none; }
  .pdp-sheet-row.is-flagged { animation: none; }
}

/* ════════════════════════════════════════════════════════════════
   shop-buybar.css — mobile sticky buy-bar for the PDP. Phone only.
   Revealed (IntersectionObserver) once the in-page add button scrolls
   out of view; sits ABOVE the floating bottom-nav. Glass material.
   ════════════════════════════════════════════════════════════════ */
.shop-buybar { display: none; }

@media (max-width: 767.98px) {
  .shop-buybar {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    position: fixed;
    left: 0;
    right: 0;
    /* sit above the floating bottom-nav pill (≈68px tall + 12px gap) */
    bottom: calc(78px + env(safe-area-inset-bottom, 0px));
    z-index: var(--z-fab, 400);   /* PORT FIX (step3): --z-fab undefined in f2e tokens.css
                                      (see 55-ds-drawer.css note); fallback matches PROTO. Only
                                      visible ≤767.98px (see rule above) — mobile PDP isn't in
                                      this step's scope, fixed for correctness/consistency. */
    padding: var(--space-3) var(--space-4);
    background: var(--glass-fill);
    -webkit-backdrop-filter: var(--glass-backdrop);
            backdrop-filter: var(--glass-backdrop);
    border-top: var(--glass-border);
    transform: translateY(120%);
    opacity: 0;
    pointer-events: none;
    transition: transform 280ms var(--ease-out), opacity 200ms var(--ease-out);
  }
  .shop-buybar.is-shown { transform: translateY(0); opacity: 1; pointer-events: auto; }

  /* a modal surface owns the screen — the bar yields. The size-guide
     drawer is z-300, the bar z-400: without this the bar paints OVER
     the open drawer. Hiding on the body lock classes beats reshuffling
     the z-scale (the bar must still sit above the bottom-nav normally). */
  body.ds-drawer-open .shop-buybar,
  body.cart-open .shop-buybar,
  body.mdrawer-open .shop-buybar,
  body.ds-zoom-open .shop-buybar,
  body.sheet-open .shop-buybar {
    transform: translateY(120%);
    opacity: 0;
    pointer-events: none;
  }

  .shop-buybar__info { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
  .shop-buybar__name {
    font-family: var(--font-cjk-text);
    font-size: var(--fs-caption);
    color: var(--text-tertiary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .shop-buybar__price {
    font-family: var(--font-latin);
    font-size: var(--fs-body);
    font-weight: var(--fw-bold);
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
  }
  .shop-buybar__add { flex: 0 0 auto; min-height: 44px; }

  /* give the page room so the related rail never hides behind the bar */
  .pdp { padding-bottom: calc(var(--space-section) + 64px); }
}

@media (prefers-reduced-motion: reduce) {
  .shop-buybar { transition: opacity 180ms ease; transform: translateY(0); }
  .shop-buybar:not(.is-shown) { transform: translateY(120%); }
}

/* ════════════════════════════════════════════════════════════════
   shop-quickadd.css — PLP add-to-cart, relocated out of the media frame.

   The old quick-add lived INSIDE .shop-card__media (aspect-ratio:2/3;
   overflow:hidden). On touch, refine-shop.css flipped it to position:static
   but it stayed inside that clip box → clipped to zero → invisible AND
   untappable on phones. It now lives in the card FOOTER beside the price:
   quiet + hover-revealed on desktop (keeps the grid editorial), and always
   visible + a real tap target on touch. Add behaviour stays cart.js's
   delegated [data-add]; this file is presentation only. Tokens only.
   ════════════════════════════════════════════════════════════════ */

.shop-card__foot {
  display: flex;
  /* 0729 Dolly：整體收緊——價格改貼列頂（原 center 讓 40px 加購鈕把價格
     推出 ~11px 上空）；加購鈕以負 margin 抵掉多出的列高，卡底不留鈕的空位 */
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  margin-top: var(--space-3);
}
.shop-card__foot .shop-card__price { min-width: 0; }   /* price takes the row's left */

.shop-card__qa {
  flex: none;
  width: 40px;
  height: 40px;
  /* 0729：負 margin 讓 40px 鈕不佔列高（列高=價格行），鈕自身仍對價格行視覺置中 */
  margin-block: -10px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  /* 0728 Dolly：底對齊 2.0 玻璃圓鈕家族（.icon-button 同材質）；橘 icon＋hover
     橘實心保留＝商業 CTA 身分。同族不同職。
     0729 Dolly：去掉玻璃白描邊（透明佔位 1px，hover 轉橘實心時不跳尺寸）。 */
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
  background: var(--glass-bg-default);
  color: var(--yellow-500);
  cursor: pointer;
  transition: opacity 200ms var(--ease-out), transform 160ms var(--ease-out),
              background 160ms ease, border-color 160ms ease, color 160ms ease;
}
.shop-card__qa-icon { width: 18px; height: 18px; display: grid; place-items: center; }
.shop-card__qa-icon svg { width: 100%; height: 100%; display: block; }

.shop-card__qa:hover { background: var(--yellow-500); color: var(--yellow-ink); border-color: var(--yellow-500); }
.shop-card__qa:active { transform: scale(0.92); }
.shop-card__qa:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }

/* added confirmation — brief success swap (cart.js + shop-render drive the timing) */
.shop-card__qa.is-added {
  background: var(--success-500);
  border-color: var(--success-500);
  color: var(--neutral-white);
}

/* Desktop (hover-capable, WIDE): quiet — revealed on card hover / keyboard
   focus, so the resting grid stays editorial. NEVER display:none (keeps it
   focusable); opacity + transform only. */
@media (hover: hover) and (pointer: fine) {
  .shop-card__qa { opacity: 0; transform: translateY(4px); }
  .shop-card:hover .shop-card__qa,
  .shop-card:focus-within .shop-card__qa { opacity: 1; transform: translateY(0); }
}

/* Touch OR narrow viewport: always visible — THIS is the mobile fix. Declared
   AFTER the desktop rule so it wins wherever both could match (a real phone is
   pointer:coarse; a narrow window is ≤767.98px even with a fine pointer). The
   site's mobile breakpoint is 767.98px. */
@media (hover: none), (pointer: coarse), (max-width: 767.98px) {
  .shop-card__qa { opacity: 1; transform: none; }
}

/* Reduced motion on a wide desktop: never trap the control behind a reveal
   transition. (Narrow/touch already force it visible above.) */
@media (prefers-reduced-motion: reduce) and (hover: hover) and (pointer: fine) and (min-width: 768px) {
  .shop-card__qa {
    opacity: 1;
    transform: none;
    transition: background 160ms ease, color 160ms ease, border-color 160ms ease;
  }
}

/* ════════════════════════════════════════════════════════════════
   shop-detail.css — Product Detail Page (PDP) layout.

   Desktop (≥768px): 3-pane gallery (vertical thumb strip + main image) in
   left column; sticky buy column in right. Details + recommendations are
   full-width sections below the grid.

   Mobile (≤767px): single column. Main image above a horizontal thumb
   carousel. Buy column stacks. Sticky bottom buy-bar (shop-buybar.css).

   Tokens only: --space-*, --radius-*, --yellow-500, --bg-secondary/tertiary,
   --border-*, --text-*, --font-cjk-display/cjk-text/latin.
   ════════════════════════════════════════════════════════════════ */

/* ── screen-reader only (a11y utility) ── */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

.pdp { padding-block: var(--space-10) 0; }

/* page wrapper — contains the full-width details + recs outside the container */
.pdp__page { display: flex; flex-direction: column; }

/* ── breadcrumb ── */
.page-head__crumbs {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-6);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.page-head__crumbs a { color: var(--text-tertiary); transition: color 160ms var(--ease-out); }
.page-head__crumbs a:hover { color: var(--text-secondary); }
.page-head__crumb-sep { color: var(--text-disabled); }
.page-head__crumb-cur {
  color: var(--text-secondary);
  max-width: 46ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── two-column page grid ── */
.pdp__grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-8);
  align-items: start;
  margin-bottom: var(--space-section);
}
@media (min-width: 768px) {
  .pdp__grid {
    /* left = gallery (3-pane); right = buy column */
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: var(--space-12);
  }
}

/* ══════════════════════════════════════════════════════════════
   GALLERY
   Desktop: [strip | main-image]   ← 3-pane, strip is left column
   Mobile:  [main-image]           ← thumbs below as horizontal scroll
   ══════════════════════════════════════════════════════════════ */
.pdp-gallery {
  display: grid;
  /* Mobile: single column — strip hidden, thumbs-mobile shown */
  grid-template-columns: 1fr;
  grid-template-areas:
    "stage";
  gap: var(--space-3);
  min-width: 0;
}

@media (min-width: 768px) {
  .pdp-gallery {
    /* Desktop: [strip] [stage] */
    grid-template-columns: 68px minmax(0, 1fr);
    grid-template-areas:
      "strip stage";
    gap: var(--space-3);
    align-items: start;
  }
}

/* ── Vertical thumb strip (desktop left) ── */
.pdp-gallery__strip {
  display: none; /* hidden on mobile */
  grid-area: strip;
  flex-direction: column;
  gap: var(--space-2);
  /* scrollable if many thumbs */
  overflow-y: auto;
  scrollbar-width: none;
  max-height: min(560px, calc(100vh - 160px));
}
.pdp-gallery__strip::-webkit-scrollbar { display: none; }

@media (min-width: 768px) {
  .pdp-gallery__strip {
    display: flex;
  }
}

/* ── Stage (main image + mobile carousel) ── */
.pdp-gallery__stage {
  grid-area: stage;
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
}

/* ── Main image frame ── */
.pdp-gallery__frame {
  position: relative;
  display: block;
  /* 0730 Dolly：內頁圖全站統一 2:3（=shop-card 卡片標準）。直式框以高度為錨
     （寬 100% 會爆高），max-height 改成實高、寬由比例反推、水平置中 */
  width: auto;
  aspect-ratio: 2 / 3;
  height: min(560px, calc(100vh - 160px));
  margin-inline: auto;
  padding: 0;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-2xl);
  background: var(--bg-secondary);
  overflow: hidden;
  cursor: zoom-in;
}
.pdp-gallery__img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}
.pdp-gallery__zoom-hint {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
  width: 38px;
  height: 38px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  color: var(--text-primary);
  background: var(--glass-fill);
  -webkit-backdrop-filter: var(--glass-backdrop);
          backdrop-filter: var(--glass-backdrop);
  border: var(--glass-border);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.pdp-gallery__zoom-hint svg { width: 18px; height: 18px; }
@media (hover: hover) and (pointer: fine) {
  .pdp-gallery__frame:hover .pdp-gallery__zoom-hint,
  .pdp-gallery__frame:focus-visible .pdp-gallery__zoom-hint {
    opacity: 1;
    transform: translateY(0);
  }
}
@media (hover: none), (pointer: coarse) {
  .pdp-gallery__zoom-hint { opacity: 1; transform: none; }
}
.pdp-gallery__frame:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 3px; }

/* ── angle/view arrows over the image — liquid glass, hover-reveal on desktop, hidden on mobile (swipe instead) ── */
.pdp-gallery__arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  width: 42px;
  height: 42px;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  background: transparent;            /* glass comes from .liquid-glass ::before — the button's own bg must be clear */
  border-radius: var(--radius-pill);
  color: #fff;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 200ms var(--ease-out), transform 160ms var(--ease-out);
}
.pdp-gallery__arrow svg { position: relative; z-index: 1; width: 18px; height: 18px; }
.pdp-gallery__arrow--prev { left: var(--space-3); }
.pdp-gallery__arrow--next { right: var(--space-3); }
.pdp-gallery__arrow:hover { transform: translateY(-50%) scale(1.06); }
.pdp-gallery__arrow:active { transform: translateY(-50%) scale(0.95); }
@media (hover: hover) and (pointer: fine) {
  .pdp-gallery__stage:hover .pdp-gallery__arrow { opacity: 1; pointer-events: auto; }
}
@media (hover: none), (pointer: coarse) {
  .pdp-gallery__arrow { display: none; }
}

/* ── Shared thumb appearance (strip + mobile carousel) ── */
.pdp-gallery__thumb {
  position: relative;
  flex: 0 0 auto;
  width: 64px;
  aspect-ratio: 2 / 3;   /* 0730：縮圖同步 2:3（64×96） */
  padding: 0;
  border: 1.5px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-secondary);
  overflow: hidden;
  cursor: pointer;
  transition: border-color 160ms var(--ease-out), transform 160ms var(--ease-out);
}
.pdp-gallery__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pdp-gallery__thumb.is-active {
  border-color: var(--yellow-500);
  border-width: 2px;
}
.pdp-gallery__thumb:hover { transform: scale(1.04); border-color: var(--border-strong); }
.pdp-gallery__thumb.is-active:hover { border-color: var(--yellow-500); }
.pdp-gallery__thumb:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }

/* Video thumb overlay */
.pdp-gallery__thumb--video .pdp-gallery__thumb-play {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--text-primary);
  background: rgba(10, 10, 10, 0.48);
}
.pdp-gallery__thumb--video .pdp-gallery__thumb-play svg { width: 18px; height: 18px; }

/* In the strip: thumbs are 68px and fill the strip column */
.pdp-gallery__strip .pdp-gallery__thumb {
  width: 100%;
  aspect-ratio: 2 / 3;
}

/* ── Mobile horizontal thumbnail carousel ── */
.pdp-gallery__thumbs-mobile {
  display: flex;
  gap: var(--space-2);
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  padding-bottom: var(--space-1);
}
.pdp-gallery__thumbs-mobile::-webkit-scrollbar { display: none; }
.pdp-gallery__thumbs-mobile .pdp-gallery__thumb {
  width: 60px;
  scroll-snap-align: start;
}

/* On desktop: hide the mobile carousel */
@media (min-width: 768px) {
  .pdp-gallery__thumbs-mobile { display: none; }
}

/* ══════════════════════════════════════════════════════════════
   BUY COLUMN
   ══════════════════════════════════════════════════════════════ */
.pdp-buy {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
@media (min-width: 768px) {
  .pdp-buy { position: sticky; top: var(--space-10); }
}
/* 0807 Dolly：徽章（限量／新品…）與分類同一列、徽章在前。原本兩者都是 .pdp-buy
   的直接子元素，被 flex-direction: column 排成上下兩行。橫列容器由
   shop-detail-render.js 的 buyMetaHtml() 產生，五個 buy 欄變體共用。
   下邊距搬到容器上（原本是 eyebrow 的 --space-2），子項各自歸零。 */
.pdp-buy__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
}
.pdp-buy__badge { align-self: flex-start; margin-bottom: var(--space-3); }
.pdp-buy__eyebrow {
  display: block;
  margin-bottom: var(--space-2);
  color: var(--text-tertiary);
}
.pdp-buy__meta .pdp-buy__badge,
.pdp-buy__meta .pdp-buy__eyebrow { margin-bottom: 0; }
.pdp-buy__title {
  margin: 0 0 var(--space-4);
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: var(--fs-h3);
  line-height: var(--lh-tight, 1.25);
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
@media (min-width: 768px) { .pdp-buy__title { font-size: var(--fs-h2); } }

/* ── price ── */
.pdp-price {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}
/* short lead under the price (buy column) — full description, clamped */
.pdp-buy__lead {
  margin: 0 0 var(--space-6);
  color: var(--text-secondary);
  font-size: var(--fs-body-sm, 14px);
  line-height: 1.65;
  max-width: 48ch;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.pdp-price__now {
  font-family: var(--font-latin);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  color: var(--yellow-500);
  font-variant-numeric: tabular-nums;
}
.pdp-price__sub {
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.pdp-price__was {
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  text-decoration: line-through;
}
.pdp-price__save {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  color: var(--yellow-500);
  padding: 2px var(--space-2);
  border: 1px solid var(--yellow-500);
  border-radius: var(--radius-pill);
}

/* ── variant header row (label + guide link) ── */
.pdp-variants {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}
.pdp-variant__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  margin-bottom: var(--space-2);
}
.pdp-variant__label {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
}
.pdp-variant__colour-name {
  font-weight: var(--fw-regular);
  color: var(--text-tertiary);
}
.pdp-variant__guide {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0;
  border: none;
  background: none;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--yellow-500);
  cursor: pointer;
  transition: color 160ms var(--ease-out);
}
.pdp-variant__guide:hover { color: var(--yellow-400); }
.pdp-variant__guide:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; border-radius: var(--radius-sm); }
.pdp-variant__guide-icon { display: inline-grid; place-items: center; }
.pdp-variant__guide-icon svg { width: 14px; height: 14px; }
/* 0807 Dolly：沒有尺寸選項、只有單獨一顆「尺寸指南」的情況（refLoneTriggerHtml）。
   這個 modifier 原本完全沒有樣式：按鈕靠左、到下一排（數量＋加入購物車）只剩 3px。
   ① 固定靠右——有尺寸選項時 header 是 space-between，指南本來就貼容器右緣；
      這裡沒有標籤可以撐開，所以自己 flex + flex-end 才對得齊，兩種情況同一個位置。
   ② 補下邊距把它跟下一排拉開。 */
.pdp-variant--reflone {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--space-3);
}

/* ── Colour swatches ── */
.pdp-variant__opts--swatches {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.pdp-swatch {
  position: relative;
  width: 32px;
  height: 32px;
  padding: 0;
  border-radius: var(--radius-pill);
  border: 2px solid transparent;
  background: transparent;
  cursor: pointer;
  transition: border-color 160ms var(--ease-out), transform 160ms var(--ease-out), box-shadow 160ms var(--ease-out);
  /* ring gap via outline-offset trick */
}
.pdp-swatch__fill {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--swatch-bg, var(--border-strong));
  /* slight inner shadow for depth */
  box-shadow: inset 0 1px 2px rgba(0,0,0,0.3);
}
.pdp-swatch:hover { transform: scale(1.1); }
.pdp-swatch:focus-visible {
  outline: 2px solid var(--yellow-500);
  outline-offset: 3px;
}
.pdp-swatch.is-selected {
  border-color: var(--yellow-500);
  box-shadow: 0 0 0 1px var(--bg-primary), 0 0 0 3px var(--yellow-500);
}
.pdp-swatch.is-oos {
  opacity: 0.4;
  cursor: not-allowed;
}
.pdp-swatch.is-oos::after {
  content: '';
  position: absolute;
  inset: 2px;
  border-radius: var(--radius-pill);
  background: linear-gradient(135deg, transparent 44%, var(--text-disabled) 44%, var(--text-disabled) 56%, transparent 56%);
}

/* ── Size grid ── */
.pdp-variant__opts--sizes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}
.pdp-size-chip {
  position: relative;
  min-height: 44px;
  min-width: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-2) var(--space-4);
  white-space: nowrap;            /* long size labels (A2（420 × 594 mm）) stay on one line */
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: border-color 160ms var(--ease-out), background 160ms var(--ease-out);
}
.pdp-size-chip:hover { border-color: var(--border-strong); background: var(--white-alpha-8); }
.pdp-size-chip.is-selected {
  border-color: var(--yellow-500);
  border-width: 2px;
  background: rgba(255, 163, 63, 0.08);
  color: var(--text-primary);
}
.pdp-size-chip:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.pdp-size-chip.is-oos {
  color: var(--text-disabled);
  cursor: not-allowed;
  text-decoration: line-through;
  opacity: 0.6;
}
/* struck line for sold-out */
.pdp-size-chip__oos {
  position: absolute;
  inset: 0;
  border-radius: var(--radius-md);
  background: linear-gradient(to bottom right, transparent calc(50% - 0.5px), var(--border-default) calc(50% - 0.5px), var(--border-default) calc(50% + 0.5px), transparent calc(50% + 0.5px));
  pointer-events: none;
}

/* ── Generic chip (fallback for other variant types) ── */
.pdp-chip {
  position: relative;
  min-width: 48px;
  min-height: 44px;
  padding: 0 var(--space-4);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: var(--white-alpha-4);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  transition: border-color 160ms var(--ease-out), background 160ms var(--ease-out), color 160ms var(--ease-out);
}
.pdp-chip:hover { border-color: var(--border-strong); }
.pdp-chip.is-selected {
  border-color: var(--yellow-500);
  background: rgba(255, 163, 63, 0.08);
  color: var(--text-primary);
}
.pdp-chip:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.pdp-chip.is-oos {
  color: var(--text-disabled);
  cursor: not-allowed;
  text-decoration: line-through;
  opacity: 0.7;
}
.pdp-chip__oos { font-size: var(--fs-caption); text-decoration: none; color: var(--text-disabled); }
.pdp-variant.is-flagged .pdp-variant__label { color: var(--error-500); }
.pdp-variant.is-flagged .pdp-variant__opts,
.pdp-variant.is-flagged .pdp-variant__opts--swatches,
.pdp-variant.is-flagged .pdp-variant__opts--sizes {
  animation: pdp-shake 320ms var(--ease-out);
}
@keyframes pdp-shake {
  0%,100% { transform: translateX(0); }
  25%      { transform: translateX(-4px); }
  75%      { transform: translateX(4px); }
}

/* guard note */
.pdp-buy__guard { margin: 0 0 var(--space-4); }

/* ── Actions row: Add-to-Cart + Heart icon ── */
.pdp-buy__actions {
  display: flex;
  gap: var(--space-2);
  align-items: stretch;
  margin-bottom: var(--space-4);
  /* 這一排的統一高度，數量器與愛心都讀它（0802 Dolly：三顆要等高）。
     59＝主 CTA .btn--lg 的內在高度（padding 16+16 ＋ 行高 27）——btn 沒有可以
     引用的高度 token，所以在這裡定一次給整排共用，不要各寫各的。
     改 .btn--lg 的 padding／字級時，這個值要跟著校一次。 */
  --pdp-action-h: 59px;
}
.pdp-qty {
  display: inline-flex;
  align-items: center;
  flex: 0 0 auto;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  background: var(--white-alpha-4);
}
.pdp-qty__btn {
  width: 44px;
  /* 不要改成 --pdp-action-h：這顆的 height 不含 1px 框，設 59 會變 61 反而把
     整排撐高。它靠 align-items:stretch 就會自己長到排高，維持原值即可。 */
  height: 52px;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-secondary);
  border-radius: var(--radius-pill);
  transition: color 160ms var(--ease-out), background 160ms var(--ease-out);
}
.pdp-qty__btn:hover { color: var(--text-primary); background: var(--white-alpha-8); }
.pdp-qty__btn:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: -2px; }
.pdp-qty__btn svg { width: 16px; height: 16px; }
.pdp-qty__val {
  min-width: 36px;
  text-align: center;
  font-family: var(--font-latin);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

/* Add-to-Cart: fills remaining width */
.pdp-buy__add {
  flex: 1 1 auto;
  gap: var(--space-2);
}
.pdp-buy__add-glyph { display: inline-grid; place-items: center; }
.pdp-buy__add-glyph svg { width: 18px; height: 18px; }

/* Heart icon button: circular, matches the row height */
.pdp-buy__heart {
  flex: 0 0 auto;
  /* 0802 Dolly：跟同排的數量器／加入購物車等高。原本寫死 52×52，而那一排是
     align-items:stretch、另外兩顆是 59 高——愛心不會被拉伸，於是矮 7px 又貼在
     上緣，中線比別人高 3.5px。
     改成讀整排共用的 --pdp-action-h（見 .pdp-buy__actions），寬高同值＝正圓。
     ⚠️ 試過 align-self:stretch + aspect-ratio 讓寬度自動跟著高度：不行。排高
     是內容決定的（非定值），flex 先用內容算完 basis，aspect-ratio 追不回來，
     寬度會塌成 22px（icon 20＋框 2）。 */
  width: var(--pdp-action-h);
  height: var(--pdp-action-h);
  display: grid;
  place-items: center;
  padding: 0;
  border: 1px solid var(--border-default);
  /* 0801 Dolly：跟旁邊的數量器與「加入購物車」同一種形狀。原本用 --radius-md，
     在商城頁被 theme-sharp 歸零成直角，只有它一個是方的。 */
  border-radius: var(--radius-pill);
  background: var(--white-alpha-4);
  color: var(--text-secondary);
  cursor: pointer;
  transition: border-color 160ms var(--ease-out), color 160ms var(--ease-out), background 160ms var(--ease-out);
}
.pdp-buy__heart svg { width: 20px; height: 20px; display: block; }
.pdp-buy__heart:hover {
  border-color: var(--yellow-500);
  color: var(--yellow-500);
  background: rgba(255, 163, 63, 0.08);
}
.pdp-buy__heart:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
/* Active chrome (border/bg) is PDP-only container decoration, kept here —
   but recoloured rose to match the site-wide like/save effect (0728, Dolly:
   "一個站不要有兩種特效"). Icon fill/bounce/burst now come from
   css/components/54-like-save.css's [data-pdp-wish].is-active rules;
   this button intentionally does NOT wire to the wishlist store (PDP
   heart stays a visual mock — see shop-detail-render.js). */
.pdp-buy__heart.is-active {
  border-color: var(--highlight-500);
  color: var(--highlight-500);
  background: color-mix(in srgb, var(--highlight-500) 10%, transparent);
}

/* ── Delivery micro-line ── */
.pdp-delivery {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--white-alpha-4);
  margin-bottom: var(--space-4);
}
.pdp-delivery__icon { display: inline-grid; place-items: center; color: var(--text-tertiary); flex: none; }
.pdp-delivery__icon svg { width: 16px; height: 16px; }
.pdp-delivery__text {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  line-height: 1.4;
}
.pdp-delivery__sep { color: var(--border-default); font-size: var(--fs-caption); }

/* ── popcorn balance note ── */
.pdp-buy__note { margin-top: var(--space-3); }

/* ══════════════════════════════════════════════════════════════
   DETAILS SECTION — full-width below the 2-col grid
   ══════════════════════════════════════════════════════════════ */
.pdp-details {
  border-top: 1px solid var(--border-subtle);
  background: var(--bg-secondary);
  padding-block: var(--space-section);
}
.pdp-details__inner {
  /* container padding applied via container utility class */
}
.pdp-details__heading {
  margin: 0 0 var(--space-8);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  letter-spacing: var(--ls-h4);
}
.pdp-details__cols {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-10);
}
@media (min-width: 768px) {
  .pdp-details__cols {
    grid-template-columns: 1fr 1fr;
    gap: var(--space-12);
  }
}

/* Left column: description + highlights */
.pdp-details__desc {
  margin: 0 0 var(--space-6);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  line-height: 1.75;
  color: var(--text-secondary);
}
.pdp-details__highlights {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}
.pdp-details__highlight {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  line-height: 1.6;
}
.pdp-details__highlight::before {
  content: '';
  display: inline-block;
  flex: none;
  width: 4px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--yellow-500);
  margin-top: 0.5em;
  vertical-align: middle;
}

/* Right column: specs + shipping */
.pdp-details__sub-heading {
  margin: 0 0 var(--space-4);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.pdp-details__specs-block { margin-bottom: var(--space-8); }
.pdp-details__shipping-block { }
.pdp-details__shipping-text {
  margin: 0 0 var(--space-3);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.7;
  color: var(--text-secondary);
}

/* Specs definition list (used in details section) */
.pdp-specs { display: flex; flex-direction: column; gap: var(--space-2); margin: 0; }
.pdp-specs__row {
  display: grid;
  grid-template-columns: 88px 1fr;
  gap: var(--space-3);
  padding-block: var(--space-2);
  border-bottom: 1px solid var(--border-subtle);
}
.pdp-specs__row:last-child { border-bottom: none; }
.pdp-specs__row dt {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.pdp-specs__row dd {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}

/* ══════════════════════════════════════════════════════════════
   RECOMMENDATIONS RAIL
   ══════════════════════════════════════════════════════════════ */
.pdp-recs {
  padding-block: var(--space-section);
}
.pdp-recs__inner { }
.pdp-recs__heading {
  margin: 0 0 var(--space-6);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
/* Desktop: 4-up grid */
.pdp-recs__rail {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5);
}
@media (min-width: 768px) {
  .pdp-recs__rail {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}
/* Mobile: horizontal scroll instead of grid */
@media (max-width: 767.98px) {
  .pdp-recs__rail {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
    gap: var(--space-4);
    padding-bottom: var(--space-2);
  }
  .pdp-recs__rail::-webkit-scrollbar { display: none; }
  .pdp-rec-card { flex: 0 0 200px; scroll-snap-align: start; }
}

/* Rec card */
.pdp-rec-card {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-xl);
  background: var(--bg-secondary);
  border: 1px solid var(--border-subtle);
  overflow: hidden;
  transition: transform 180ms var(--ease-out), border-color 180ms var(--ease-out);
  text-decoration: none;
}
.pdp-rec-card:hover {
  transform: translateY(-3px);
  border-color: var(--border-default);
}
.pdp-rec-card:focus-visible {
  outline: 2px solid var(--yellow-500);
  outline-offset: 3px;
}
.pdp-rec-card__media {
  aspect-ratio: 2 / 3;   /* 0730：推薦卡=卡片標準 2:3 */
  overflow: hidden;
  background: var(--bg-tertiary);
}
.pdp-rec-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 300ms var(--ease-out);
}
.pdp-rec-card:hover .pdp-rec-card__media img { transform: scale(1.03); }
.pdp-rec-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4) var(--space-4);
}
.pdp-rec-card__cat {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.pdp-rec-card__title {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
  line-height: 1.4;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.pdp-rec-card__price {
  margin-top: var(--space-2);
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  color: var(--yellow-500);
  font-variant-numeric: tabular-nums;
}

/* ══════════════════════════════════════════════════════════════
   BUNDLE 套組 — "what's inside"
   ══════════════════════════════════════════════════════════════ */
.pdp-bundle { margin-top: var(--space-section); }
.pdp-bundle__title {
  margin: 0 0 var(--space-6);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.pdp-bundle__grid { display: grid; grid-template-columns: 1fr; gap: var(--space-5); }
@media (min-width: 768px) {
  .pdp-bundle__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--space-6); }
}
.pdp-bundle-item {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-secondary);
}
.pdp-bundle-item.is-flagged { border-color: var(--error-500); animation: pdp-shake 320ms var(--ease-out); }
.pdp-bundle-item__media {
  position: relative;
  aspect-ratio: 2 / 3;   /* 0730：套組件同步 2:3 */
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-tertiary);
}
.pdp-bundle-item__img { width: 100%; height: 100%; object-fit: cover; display: block; transition: opacity 200ms var(--ease-out); }
.pdp-bundle-item__img[data-comp-zoom] { cursor: zoom-in; }
/* zoom affordance (mock bcard__zoom) */
.pdp-bundle-item__zoom {
  position: absolute; right: 8px; bottom: 8px; width: 28px; height: 28px;
  display: grid; place-items: center; border-radius: var(--radius-pill);
  color: #fff; background: rgba(0, 0, 0, 0.5); pointer-events: none;
  opacity: 0; transition: opacity 180ms var(--ease-out);
}
.pdp-bundle-item__zoom svg { width: 15px; height: 15px; }
@media (hover: hover) { .pdp-bundle-item__media:hover .pdp-bundle-item__zoom { opacity: 1; } }
@media (hover: none) { .pdp-bundle-item__zoom { opacity: 1; } }
/* per-item colour swatches */
.pdp-bundle-item__sws { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.pdp-bundle-item__sw { width: 22px; height: 22px; }
.pdp-bundle-item__thumbs { display: flex; gap: var(--space-2); }
.pdp-bundle-item__thumb {
  width: 44px;
  aspect-ratio: 2 / 3;
  padding: 0;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-tertiary);
  cursor: pointer;
  transition: border-color 160ms var(--ease-out);
}
.pdp-bundle-item__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pdp-bundle-item__thumb.is-active { border-color: var(--yellow-500); }
.pdp-bundle-item__thumb:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 1px; }
.pdp-bundle-item__body { display: flex; flex-direction: column; gap: var(--space-2); }
.pdp-bundle-item__name {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
  line-height: 1.4;
}
.pdp-bundle-item__variant { margin-top: var(--space-1); }
.pdp-bundle-item__variant .pdp-chip { min-height: 36px; min-width: 40px; }
.pdp-bundle-item__link {
  margin-top: var(--space-1);
  align-self: flex-start;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--yellow-500);
}
.pdp-bundle-item__link:hover { color: var(--yellow-400); }

/* ══════════════════════════════════════════════════════════════
   SKELETON / EMPTY
   ══════════════════════════════════════════════════════════════ */
.pdp-skel__hero { aspect-ratio: 2 / 3; height: min(560px, calc(100vh - 160px)); margin-inline: auto; border-radius: var(--radius-2xl); }   /* 0730：骨架同主圖框 */
.pdp-skel__buy { display: flex; flex-direction: column; gap: var(--space-3); }
.pdp-empty { padding-block: var(--space-20); gap: var(--space-4); }

/* ══════════════════════════════════════════════════════════════
   POPCORN
   ══════════════════════════════════════════════════════════════ */
.pdp-pop { display: flex; align-items: center; gap: var(--space-2); margin-bottom: var(--space-2); }
.pdp-pop__icon {
  width: 22px; height: 22px; flex: none;
  -webkit-mask: url('assets/icons/popcorn-md.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn-md.svg') center / contain no-repeat;
  background: var(--yellow-500);
}
.pdp-pop__val { font-family: var(--font-latin); font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--yellow-500); font-variant-numeric: tabular-nums; }
.pdp-pop__unit { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.pdp-pop__balance { margin-bottom: var(--space-6); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-tertiary); }
.pdp-pop__balance b { color: var(--text-primary); font-family: var(--font-latin); font-variant-numeric: tabular-nums; }

/* ══════════════════════════════════════════════════════════════
   EVENT
   ══════════════════════════════════════════════════════════════ */
.pdp-event-meta {
  display: flex; flex-direction: column; gap: var(--space-2);
  margin-bottom: var(--space-6); padding: var(--space-4);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-lg); background: var(--bg-secondary);
}
.pdp-event-meta__row { display: flex; gap: var(--space-3); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.pdp-event-meta__k { flex: 0 0 48px; color: var(--text-tertiary); }
.pdp-tiers { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-5); }
.pdp-tier {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  min-height: 56px; padding: var(--space-3) var(--space-4); text-align: left;
  border: 1px solid var(--border-default); border-radius: var(--radius-md);
  background: var(--white-alpha-4); color: var(--text-primary); cursor: pointer;
  transition: border-color 160ms var(--ease-out), background 160ms var(--ease-out);
}
.pdp-tier:hover { border-color: var(--border-strong); }
.pdp-tier.is-selected { border-color: var(--yellow-500); background: rgba(255, 163, 63, 0.08); }
.pdp-tier:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.pdp-tier.is-oos { color: var(--text-disabled); cursor: not-allowed; opacity: 0.7; }
.pdp-tier__label { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-medium); }
.pdp-tier__oos { color: var(--text-disabled); font-size: var(--fs-caption); }
.pdp-tier__price { font-family: var(--font-latin); font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); white-space: nowrap; }
.pdp-tier__sub { color: var(--text-tertiary); font-weight: var(--fw-regular); }
.pdp-lineup { margin-top: var(--space-section); }
.pdp-lineup__title { margin: 0 0 var(--space-5); font-family: var(--font-cjk-display); font-size: var(--fs-h4); font-weight: var(--fw-bold); color: var(--text-primary); }
.pdp-lineup__list { display: flex; flex-wrap: wrap; gap: var(--space-2); }
.pdp-lineup__chip {
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--border-default); border-radius: var(--radius-pill);
  background: var(--white-alpha-4);
  font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary);
}

/* ══════════════════════════════════════════════════════════════
   AUCTION
   ══════════════════════════════════════════════════════════════ */
.pdp-auction {
  margin-bottom: var(--space-5); padding: var(--space-4);
  border: 1px solid var(--border-default); border-radius: var(--radius-lg); background: var(--bg-secondary);
}
/* 0807：左邊「小標＋大金額」直排、右邊倒數靠右對齊底線（原本 label／金額被
   space-between 拉到兩端，中間一大塊空白）。 */
.pdp-auction__bidrow { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--space-4); margin-bottom: var(--space-4); }
.pdp-auction__bidcol { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.pdp-auction__bidlabel { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-tertiary); }
.pdp-auction__bid { font-family: var(--font-latin); font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--yellow-500); font-variant-numeric: tabular-nums; }
.pdp-auction__meta { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3); }
.pdp-auction__bidders, .pdp-auction__time, .pdp-auction__top { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-secondary); }
/* 0807 Dolly：目前最高出價人。標籤維持次要色，名字提一階＋等寬數字對齊，
   讓「誰在領先」在一排 meta 裡讀得出來，但不跟狀態膠囊搶第一眼。
   跟「N 次出價」之間用一條分隔線（0807 Dolly）——兩段都是灰色小字，只靠 gap
   會黏成一句。線畫在自己的左緣，所以沒有最高出價人時（結標前資料還沒進來）
   不會留下一條孤線。 */
.pdp-auction__top {
  padding-left: var(--space-3);
  border-left: 1px solid var(--border-default);
}
.pdp-auction__top b {
  font-family: var(--font-latin);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}
/* 倒數在第一行右側：跟大金額切齊底線，數字等寬才不會每分鐘跳動 */
.pdp-auction__time { font-variant-numeric: tabular-nums; flex: 0 0 auto; padding-bottom: 3px; }
.pdp-provenance { margin-top: var(--space-4); padding-top: var(--space-4); border-top: 1px solid var(--border-subtle); }
.pdp-provenance__k { display: block; margin-bottom: var(--space-2); font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.pdp-provenance__text { margin: 0; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); line-height: 1.7; color: var(--text-secondary); }

/* ══════════════════════════════════════════════════════════════
   REDUCED MOTION
   ══════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  .pdp-gallery__zoom-hint,
  .pdp-gallery__thumb,
  .pdp-chip,
  .pdp-swatch,
  .pdp-size-chip,
  .pdp-qty__btn,
  .pdp-buy__heart,
  .pdp-rec-card,
  .pdp-rec-card__media img { transition: none; }
  .pdp-variant.is-flagged .pdp-variant__opts,
  .pdp-variant.is-flagged .pdp-variant__opts--swatches,
  .pdp-variant.is-flagged .pdp-variant__opts--sizes { animation: none; }
  .pdp-gallery__zoom-hint { opacity: 1; transform: none; }
  .pdp-swatch:hover { transform: none; }
  .pdp-rec-card:hover { transform: none; }
  .pdp-gallery__thumb:hover { transform: none; }
}

/* ══════════════════════════════════════════════════════════════
   EDITORIAL RESKIN — HODINKEE/Aesop tier
   Scoped to .pdp where possible so nothing outside PDP is touched.
   LEVER 1: Display headings via the brand --font-cjk-display token (LINE Seed TW)
   LEVER 2: Monochrome CTA + quiet price (accent kept only on detail elements)
   LEVER 3: Airier gallery + spotlight background
   LEVER 4: Editorial lead paragraph (styled via .pdp-details__lead)
   ══════════════════════════════════════════════════════════════ */

/* ── LEVER 1: Headings use the brand display token (LINE Seed TW) — NOT a serif.
   Premium comes from weight/scale/spacing + the other levers, not a foreign face. ── */
.pdp-buy__title,
.pdp-details__heading,
.pdp-details__sub-heading,
.pdp-recs__heading,
.pdp-related__title,
.pdp-bundle__title,
.pdp-lineup__title {
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  letter-spacing: -0.01em;
}

/* ── LEVER 2: 單色主 CTA ──
   0802 已收編為正式變體 .btn--mono（02-button.css），這裡原本那段 `.pdp` 前綴的
   覆寫整段退役——外觀跟著元件走，不再綁在頁面上。markup 改掛 btn--mono。 */

/* Price headline — near-white instead of brand orange */
.pdp .pdp-price__now {
  color: var(--text-primary);
}

/* Accent retained on: save badge, size-chip selected ring, swatch selected ring,
   heart-active state, size guide link, delivery link — these rules are already
   token-correct in the base CSS and are NOT overridden here. */

/* ── LEVER 3: Airier gallery + spotlight frame ── */
.pdp-gallery {
  max-width: 600px;
}

.pdp-gallery__frame {
  max-height: min(620px, calc(100vh - 140px));
  background: radial-gradient(120% 120% at 50% 38%, var(--bg-tertiary), var(--bg-secondary));
}

/* More breathing room on the page level */
.pdp {
  padding-block: var(--space-12) 0;
}

@media (min-width: 768px) {
  .pdp__grid {
    gap: var(--space-12);
  }
}

/* Details section — relax the card (more padding, hairline dividers) */
.pdp-details {
  padding-block: var(--space-section);
  background: transparent; /* let the page bg breathe instead of the heavier bg-secondary */
  border-top: 1px solid var(--border-subtle);
}
.pdp-details__inner {
  padding-block: 0;
}

/* ── LEVER 4: Editorial lead paragraph (brand text font, enlarged — no serif) ── */
.pdp-details__lead {
  font-family: var(--font-cjk-text);
  /* 0801 Dolly：原本 --fs-h4(20) 在這一區太搶——同一片裡規格／取貨說明／重點
     條列全是 14。降一階到 --fs-body(16)：仍比周圍大、看得出是導言，但不再像標題。
     停在 16 而不是 14 有兩個理由：①降到 14 導言就跟附註齊平、層次整個消失；
     ②中文字體規範以 16 為界（≥16 走 LINE Seed），16 是最後一階不換字體的。 */
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: 1.7;
  color: var(--text-secondary);
  max-width: 62ch;
  margin-bottom: var(--space-8);
}
/* 0801 Dolly「斷行寬度有點怪」：62ch≈678px 沒有對齊任何一條線——比下方左欄
   右緣多 74px，還越過右欄起點，參差邊剛好卡在兩欄中間的縫裡。
   改成跟下方 grid 同一套算式（兩欄等分、扣掉同一個 gap），導言的右緣就落在
   左欄右緣上。用 calc 而不是寫死 604px：欄數、gap、container 之後改了會自己跟。
   ≤767 兩欄併成一欄，維持滿寬。 */
@media (min-width: 768px) {
  .pdp-details__lead { max-width: calc((100% - var(--space-12)) / 2); }
}

/* 單色 CTA 的 reduced-motion 已跟著 .btn--mono 搬進 02-button.css */

/* ════════════════════════════════════════════════════════════════
   Ztor 2.0 — Shopping Cart
   A persistent header cart icon + right slide-over drawer (CART ↔
   CHECKOUT ↔ CONFIRMATION). All tokens reused from tokens.css; nothing
   redefined here. Scoped under .header__cart (icon) and .ztor-cart
   (overlay + drawer + everything inside). Mobbin-benchmarked:
   Hers (line-item rows + summary block), Instacart (sticky footer CTA
   with live total, inline qty steppers), Apple (address form + radio
   payment rows + "all set" confirmation), Walmart/Shop (collapsed
   summary on checkout). Brand skin: dark canvas, orange accent, CJK
   type, --ease-out, --radius-*, --space-*.
   Perf: only transform/opacity animate; reduced-motion honored.
   ════════════════════════════════════════════════════════════════ */

/* ─────────────────────────────────────────
   HEADER ICON  (.header__cart)
   Matches .header__icon-btn rhythm (24×24 masked glyph) but carries a
   count badge, so it's a positioned wrapper rather than a bare mask.
   ───────────────────────────────────────── */
.header__cart {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  flex-shrink: 0;
  color: var(--neutral-300);
  transition: color 200ms ease;
}
.header__cart:hover { color: var(--neutral-white); }
.header__cart:focus-visible {
  outline: 2px solid var(--yellow-500);
  outline-offset: 4px;
  border-radius: var(--radius-sm);
}

.header__cart__glyph {
  width: 24px;
  height: 24px;
  display: block;
  color: inherit;
}
.header__cart__glyph svg { width: 100%; height: 100%; display: block; }

/* Count badge — amber pill top-right, ringed in canvas so it lifts off
   the icon. Hidden at zero (an empty badge reads as a bug). */
.header__cart__badge {
  position: absolute;
  top: -7px;
  right: -9px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  display: none;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-pill);
  background: var(--yellow-500);
  color: var(--yellow-ink);
  font-family: var(--font-latin);
  font-size: 11px;
  font-weight: var(--fw-bold);
  line-height: 1;
  letter-spacing: 0;
}
.header__cart.has-items .header__cart__badge { display: inline-flex; }

/* 徽章與 icon 之間的縫＝真的挖空（0801 Dolly）。
   原本是 box-shadow 畫一圈 --neutral-900 的實色環：在深色頁看不出來，一旦 header
   壓在淺色／漸層底上（商品頁 hero、片庫劇照）那圈就變成一條黑線。
   改成在 icon 上挖洞——mask 把徽章位置的圖形去掉，縫隙透出的是後面真正的底色，
   不是塗上去的顏色，所以任何背景都對。
   幾何：徽章 17px 高、right:-9px → 左側圓心在 calc(100% + 0.5px) / 1.5px，
   半徑 8.5＋2px 縫＝10.5px。0.6px 羽化避免鋸齒。
   ⚠️ 三位數以上徽章會往左長、圓心跟著位移，這個洞是照一～二位數算的（demo 的
   購物車不會到 100 件）；真要支援就得把徽章寬度餵成 CSS 變數。 */
.header__cart.has-items .header__cart__glyph {
  -webkit-mask: radial-gradient(circle 10.5px at calc(100% + 0.5px) 1.5px,
                #0000 0 calc(100% - 0.6px), #000 100%);
  mask: radial-gradient(circle 10.5px at calc(100% + 0.5px) 1.5px,
        #0000 0 calc(100% - 0.6px), #000 100%);
}

/* Badge bump — a single transform pulse when an item lands. */
.header__cart__badge.is-bumping { animation: ztor-cart-bump 360ms var(--ease-out); }
@keyframes ztor-cart-bump {
  0%   { transform: scale(1); }
  40%  { transform: scale(1.32); }
  100% { transform: scale(1); }
}

/* ─────────────────────────────────────────
   OVERLAY + PANEL  (.ztor-cart)
   Full-height right slide-over (Shopify/Hers). z-index above the
   header (header is < 300; watch/notif drawers are 300 — cart sits at
   400 so its modal owns the screen during checkout).
   ───────────────────────────────────────── */
.ztor-cart {
  position: fixed;
  inset: 0;
  z-index: 400;
  pointer-events: none;
  visibility: hidden;
}
.ztor-cart.is-open { pointer-events: auto; visibility: visible; }

.ztor-cart__overlay {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.55);
  opacity: 0;
  transition: opacity 280ms var(--ease-out);
}
.ztor-cart.is-open .ztor-cart__overlay { opacity: 1; }

.ztor-cart__panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: min(440px, 100%);
  background: rgba(14, 14, 14, 0.98);
  backdrop-filter: blur(16px) saturate(1.2);
  -webkit-backdrop-filter: blur(16px) saturate(1.2);
  border-left: 1px solid var(--border-default);
  box-shadow: -28px 0 70px rgba(0, 0, 0, 0.6);
  transform: translateX(100%);
  transition: transform 360ms var(--ease-out);
  overflow: hidden;
}
.ztor-cart.is-open .ztor-cart__panel { transform: translateX(0); }

/* ── View stage: CART and CHECKOUT live side by side, slid by transform.
   Absolutely positioned to fill the panel (NOT a column-flex child — that
   let the 200% width be shrunk by the flex algorithm, which mis-landed the
   checkout view a panel-width off). As an out-of-flow box, width:200% is
   honored against the panel, and translateX(-50%) = exactly one panel
   width. Each view is 50% of the stage = 100% of the panel. */
.ztor-cart__stage {
  position: absolute;
  inset: 0;
  display: flex;
  width: 200%;
  transform: translateX(0);
  transition: transform 360ms var(--ease-out);
}
.ztor-cart[data-view="checkout"] .ztor-cart__stage,
.ztor-cart[data-view="confirm"]  .ztor-cart__stage { transform: translateX(-50%); }

.ztor-cart__view {
  flex: 0 0 50%;
  width: 50%;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* ── Shared head ── */
.ztor-cart__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-6) var(--space-4);
  flex-shrink: 0;
  border-bottom: 1px solid var(--border-subtle);
}
.ztor-cart__back {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  cursor: pointer;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  color: var(--text-secondary);
  transition: background 200ms ease, color 200ms ease;
}
.ztor-cart__back:hover { background: rgba(255, 255, 255, 0.12); color: var(--text-primary); }
.ztor-cart__back svg { width: 18px; height: 18px; }

.ztor-cart__title {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 22px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  color: var(--text-primary);
}
/* 數字圓章（Dolly 0728：原「(1)」括號文字 → 外框章，對齊全站 count chip 視覺）。
   抽屜裡的數量一律用這顆：購物車標題與願望清單標題共用，尺寸各自覆寫。 */
.ztor-cart__title-count,
.ztor-cart__wish-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 24px;
  height: 24px;
  padding: 0 7px;
  margin-left: 10px;
  border-radius: 999px;
  background: var(--white-alpha-8);
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-regular);
  line-height: 1;
  vertical-align: 3px;
}
.ztor-cart__title-count:empty { display: none; }

.ztor-cart__close {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  cursor: pointer;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  transition: background 200ms ease;
}
.ztor-cart__close:hover { background: rgba(255, 255, 255, 0.14); }
.ztor-cart__panel:focus { outline: none; }
.ztor-cart__close:focus-visible,
.ztor-cart__back:focus-visible {
  outline: 2px solid var(--yellow-500);
  outline-offset: 2px;
}
.ztor-cart__close-icon {
  width: 16px;
  height: 16px;
  background-color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/close.svg') center / contain no-repeat;
          mask: url('assets/icons/close.svg') center / contain no-repeat;
}
.ztor-cart__close:hover .ztor-cart__close-icon { background-color: var(--text-primary); }

/* ── Scrollable body ── */
.ztor-cart__body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--space-2) var(--space-6) var(--space-4);
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.08) transparent;
}
.ztor-cart__body::-webkit-scrollbar { width: 6px; }
.ztor-cart__body::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.08);
  border-radius: var(--radius-pill);
}

/* ─────────────────────────────────────────
   LINE ITEMS  (Hers / Instacart row anatomy)
   ───────────────────────────────────────── */
.ztor-cart__items { display: flex; flex-direction: column; }

.ztor-cart-line {
  display: grid;
  grid-template-columns: 64px 1fr auto;
  gap: var(--space-4);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.ztor-cart-line__media {
  width: 64px;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-secondary);
  flex-shrink: 0;
}
.ztor-cart-line__media img { width: 100%; height: 100%; object-fit: cover; display: block; }

.ztor-cart-line__main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.ztor-cart-line__title {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  line-height: 1.45;
  color: var(--text-primary);
  /* clamp to two lines so a long zh title never blows out the row */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.ztor-cart-line__price {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
}

/* ══ 願望清單橫條（Dolly 2026-08-01）════════════════════════════════
   購物車列之下、小計之上：把「已收藏但還沒加進購物車」的東西列出來，
   一鍵併進同一筆結帳。版型沿用 .ztor-cart-line 的三欄骨架，只是縮一號
   （縮圖 56、無數量列），視覺上是附屬區不是第二個購物車。 */
.ztor-cart__wish {
  padding-top: var(--space-5);
  border-top: 1px solid var(--border-subtle);
}
.ztor-cart__wish-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}
.ztor-cart__wish-toggle {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.ztor-cart__wish-heart {
  display: inline-flex;
  width: 18px;
  height: 18px;
  color: var(--brand-500);   /* 實心橘心＝與商品卡已收藏態同一個訊號 */
}
.ztor-cart__wish-heart svg { width: 100%; height: 100%; display: block; }
/* 數量＝與購物車標題同一顆膠囊徽章（.ztor-cart__title-count），只縮一號配合
   願望清單較小的標題字級（22px 標題→24 徽章／16px 標題→20 徽章）。
   共用宣告見上面的 .ztor-cart__title-count 群組。 */
.ztor-cart__wish-count {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  margin-left: 2px;
  font-size: 12px;
}
.ztor-cart__wish-chev {
  display: inline-flex;
  width: 18px;
  height: 18px;
  color: var(--text-secondary);
  transition: transform 160ms var(--ease-out);
}
.ztor-cart__wish-chev svg { width: 100%; height: 100%; display: block; }
/* 收合＝朝下（點了展開）／展開＝朝上（點了收合）——同站上 .wp-roomdev__label 的慣例。
   預設是收合態，所以基準造型就是朝下的 ⌄，展開才翻面。 */
.ztor-cart__wish:not(.is-collapsed) .ztor-cart__wish-chev { transform: rotate(180deg); }
.ztor-cart__wish.is-collapsed .ztor-cart__wish-list { display: none; }
.ztor-cart__wish-all {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  text-decoration: none;
  transition: color 150ms var(--ease-out);
}
.ztor-cart__wish-all:hover { color: var(--text-primary); }

.ztor-cart-wish {
  display: grid;
  grid-template-columns: 56px 1fr auto;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) 0 0;
}
.ztor-cart-wish__media {
  width: 56px;
  aspect-ratio: 2 / 3;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--bg-secondary);
}
.ztor-cart-wish__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ztor-cart-wish__main {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}
.ztor-cart-wish__title {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  line-height: 1.45;
  color: var(--text-primary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.ztor-cart-wish__price {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
/* 加入購物車＝外框膠囊（次要動作，不與「前往結帳」搶主色） */
.ztor-cart-wish__add {
  flex-shrink: 0;
  height: 40px;
  padding: 0 var(--space-5);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  background: none;
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  white-space: nowrap;
  transition: border-color 150ms var(--ease-out), background-color 150ms var(--ease-out);
}
.ztor-cart-wish__add:hover {
  border-color: var(--brand-500);
  background: rgba(255, 163, 63, 0.1);
}
.ztor-cart-wish__add:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }

/* qty stepper  −  N  +  */
.ztor-cart-line__stepper {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  margin-top: var(--space-1);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.02);
}
.ztor-cart-step {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-secondary);
  border-radius: var(--radius-pill);
  transition: color 160ms ease, background 160ms ease;
}
.ztor-cart-step:hover { color: var(--text-primary); background: rgba(255, 255, 255, 0.06); }
.ztor-cart-step:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: -2px; }
.ztor-cart-step svg { width: 14px; height: 14px; }
.ztor-cart-line__qty {
  min-width: 28px;
  text-align: center;
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
}

/* right column: unit/line price + remove × */
.ztor-cart-line__aside {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  justify-content: space-between;
}
.ztor-cart-line__line-total {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
  white-space: nowrap;
}
.ztor-cart-line__remove {
  width: 26px;
  height: 26px;
  display: grid;
  place-items: center;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  color: var(--text-tertiary);
  border-radius: var(--radius-pill);
  transition: color 160ms ease, background 160ms ease;
}
.ztor-cart-line__remove:hover { color: var(--error-500); background: rgba(239, 68, 68, 0.1); }
.ztor-cart-line__remove:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 0; }
.ztor-cart-line__remove svg { width: 15px; height: 15px; }

/* item enter / leave */
.ztor-cart-line { animation: ztor-cart-line-in 320ms var(--ease-out); }
@keyframes ztor-cart-line-in {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: translateY(0); }
}
.ztor-cart-line.is-leaving {
  animation: ztor-cart-line-out 240ms var(--ease-out) forwards;
  pointer-events: none;
}
@keyframes ztor-cart-line-out {
  to { opacity: 0; transform: translateX(16px); }
}

/* ─────────────────────────────────────────
   EMPTY STATE
   ───────────────────────────────────────── */
.ztor-cart__empty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-4);
  padding: var(--space-16) var(--space-6);
}
.ztor-cart__empty-icon {
  width: 64px;
  height: 64px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.04);
  color: var(--text-tertiary);
}
.ztor-cart__empty-icon svg { width: 30px; height: 30px; }
.ztor-cart__empty-title {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.ztor-cart__empty-desc {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  line-height: var(--lh-body-sm);
  max-width: 240px;
}
.ztor-cart__empty-cta {
  margin-top: var(--space-2);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 44px;
  padding: 0 var(--space-6);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--text-primary);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  cursor: pointer;
  transition: border-color 200ms ease, background 200ms ease;
}
.ztor-cart__empty-cta:hover { border-color: var(--border-strong); background: rgba(255, 255, 255, 0.04); }

/* ─────────────────────────────────────────
   FOOTER  (summary + CTA, sticky — Instacart)
   ───────────────────────────────────────── */
.ztor-cart__foot {
  flex-shrink: 0;
  padding: var(--space-5) var(--space-6) max(var(--space-6), env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border-default);
  background: rgba(10, 10, 10, 0.6);
}

.ztor-cart__summary { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-5); }
.ztor-cart__sum-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.ztor-cart__sum-row span:last-child {
  font-family: var(--font-latin);
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
}
.ztor-cart__sum-row--total {
  padding-top: var(--space-3);
  border-top: 1px solid var(--border-subtle);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.ztor-cart__sum-row--total span:last-child {
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.ztor-cart__sum-free { color: var(--score-ink-high) !important; }

/* 抽屜裡唯一的實心鈕（前往結帳／確認下單／完成）。
   0802 Dolly：跟商品頁「加入購物車」一樣走白色——顏色交給 .btn--mono
   （02-button.css），這裡只留版型。原本是 --yellow-500 實心＋橘色光暈 hover，
   會讓同一條購買動線走到一半換色。 */
.ztor-cart__cta {
  width: 100%;
  height: 52px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border: none;
  border-radius: var(--radius-pill);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  cursor: pointer;
}
.ztor-cart__cta:active { transform: scale(0.985); }
.ztor-cart__cta:focus-visible { outline: 2px solid var(--neutral-white); outline-offset: 3px; }
.ztor-cart__cta:disabled {
  background: rgba(255, 255, 255, 0.08);
  color: var(--text-disabled);
  cursor: not-allowed;
  box-shadow: none;
}
.ztor-cart__cta svg { width: 18px; height: 18px; }

.ztor-cart__foot-note {
  margin-top: var(--space-3);
  text-align: center;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}

/* ─────────────────────────────────────────
   CHECKOUT VIEW
   ───────────────────────────────────────── */
.ztor-cart__section { padding: var(--space-5) 0; border-bottom: 1px solid var(--border-subtle); }
.ztor-cart__section:first-child { padding-top: var(--space-3); }
.ztor-cart__section-title {
  margin: 0 0 var(--space-4);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}

/* collapsed order summary (Walmart/Shop) — item thumbs + count, totals */
.ztor-cart__co-items {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.ztor-cart__co-thumbs { display: flex; }
.ztor-cart__co-thumb {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  border: 2px solid var(--neutral-900);
  background: var(--bg-secondary);
  margin-left: -10px;
}
.ztor-cart__co-thumb:first-child { margin-left: 0; }
.ztor-cart__co-thumb img { width: 100%; height: 100%; object-fit: cover; }
.ztor-cart__co-thumb--more {
  display: grid;
  place-items: center;
  font-family: var(--font-latin);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  color: var(--text-secondary);
  background: var(--bg-tertiary);
}
.ztor-cart__co-itemcount {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}

/* form fields */
.ztor-cart__field { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.ztor-cart__field-row { display: flex; gap: var(--space-3); }
.ztor-cart__field-row .ztor-cart__field { flex: 1; }
.ztor-cart__label {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  letter-spacing: var(--ls-label);
  color: var(--text-tertiary);
}
.ztor-cart__input {
  width: 100%;
  height: 46px;
  padding: 0 var(--space-4);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.03);
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  transition: border-color 180ms ease, background 180ms ease;
}
.ztor-cart__input::placeholder { color: var(--text-disabled); }
.ztor-cart__input:focus {
  outline: none;
  border-color: var(--yellow-500);
  background: rgba(255, 255, 255, 0.05);
}

/* payment radio rows (Apple) */
.ztor-cart__pay { display: flex; flex-direction: column; gap: var(--space-3); }
.ztor-cart__pay-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.02);
  cursor: pointer;
  transition: border-color 180ms ease, background 180ms ease;
}
.ztor-cart__pay-row:hover { border-color: var(--border-strong); }
.ztor-cart__pay-row.is-selected {
  border-color: var(--yellow-500);
  background: rgba(255, 163, 63, 0.06);
}
.ztor-cart__pay-radio {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  border-radius: var(--radius-pill);
  border: 2px solid var(--border-strong);
  display: grid;
  place-items: center;
  transition: border-color 180ms ease;
}
.ztor-cart__pay-row.is-selected .ztor-cart__pay-radio { border-color: var(--yellow-500); }
.ztor-cart__pay-radio::after {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: var(--radius-pill);
  background: var(--yellow-500);
  transform: scale(0);
  transition: transform 180ms var(--ease-out);
}
.ztor-cart__pay-row.is-selected .ztor-cart__pay-radio::after { transform: scale(1); }
.ztor-cart__pay-label {
  flex: 1;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.ztor-cart__pay-hint {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.ztor-cart__pay-mark { color: var(--text-secondary); display: grid; place-items: center; }
.ztor-cart__pay-mark svg { width: 22px; height: 22px; }

/* ─────────────────────────────────────────
   CONFIRMATION VIEW
   ───────────────────────────────────────── */
.ztor-cart__confirm {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: var(--space-4);
  padding: var(--space-12) var(--space-6);
}
.ztor-cart__confirm-mark {
  width: 76px;
  height: 76px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  /* 0731 Dolly：盤面對齊 L 版示範卡——實心綠盤＋深色勾（原：橘 12% 底＋橘勾），
     與結帳完成頁 cc-success 同盤面（兩處都是「訂單已成立」）。
     彈入交給元件（host 掛雙 class，57 檔的 pop＋描繪），原 ztor-cart-pop 移除。 */
  background: var(--success-500);
  color: var(--neutral-900);
}
/* 36-viewBox 字形有內縮：svg 放大到 56px，筆畫跨距≈原 38px 滿版勾 */
.ztor-cart__confirm-mark svg { width: 56px; height: 56px; }
.ztor-cart__confirm-title {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h3);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.ztor-cart__confirm-desc {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  line-height: var(--lh-body);
  max-width: 280px;
}
.ztor-cart__confirm-order {
  margin-top: var(--space-2);
  padding: var(--space-3) var(--space-5);
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-md);
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  letter-spacing: 0.04em;
}
.ztor-cart__confirm-order b { color: var(--yellow-500); font-weight: var(--fw-bold); }

/* ─────────────────────────────────────────
   RESPONSIVE  (site mobile breakpoint = 768px)
   Full-bleed panel on phones.
   ───────────────────────────────────────── */
@media (max-width: 768px) {
  .ztor-cart__panel { width: 100%; border-left: none; }
}

/* ─────────────────────────────────────────
   REDUCED MOTION — kill transforms/animations, keep state changes.
   ───────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .ztor-cart__overlay,
  .ztor-cart__panel,
  .ztor-cart__stage,
  .ztor-cart__cta { transition: none; }
  .ztor-cart__panel { transform: translateX(0); }
  .ztor-cart:not(.is-open) .ztor-cart__panel { transform: translateX(100%); }
  .header__cart__badge.is-bumping,
  .ztor-cart-line,
  .ztor-cart-line.is-leaving,
  .ztor-cart__confirm-mark { animation: none; }
  .ztor-cart-line.is-leaving { opacity: 0; }
}

/* ─────────────────────────────────────────
   STICKY FILTER PILL  (.tabstick)  +  MOBILE HEADER HIDE/SHOW
   Behaviour: this file + assets/sticky-tabs.js. Each primary tab row is wrapped in
   <div class="tabstick">. Spotlight (index) is excluded.

   POSITION: NATIVE position:sticky. The nav moves WITH the scroll and holds at the pin
   line — continuous, no teleport. (A relative→fixed swap is an INSTANT position change = a
   hard JUMP that no animation can hide; that teleport survived many attempts. Sticky is
   continuous AND behaves identically in Safari and Chrome → no "works in dev-tools, jumps on
   iPhone" bug. It self-reserves its flow box → zero CLS, no spacer.)

   THE STICKY HOST: a sticky element only sticks WITHIN its containing block (its parent). Some
   rows sit in a too-short flex row (e.g. a "nav + button" .tabs-row only as tall as the nav) →
   sticky has no room. So the controller picks a HOST per row at measure time — the .tabstick
   itself when its parent is tall, else the parent (whose own containing block is the tall
   section) — and marks it `.tabstick-host`. Everything positional keys off the host; the glass
   pill is always the descendant .glass-tabs.

   The controller only toggles a STYLE class (.is-pinned) as the host sticks; the glass material
   MORPHS in via a CSS transition. The header-follow transform (--tabs-shift) rides the header
   hide and lives ONLY on the stuck states.
   ───────────────────────────────────────── */

/* ── The sticky host (set by the controller). Natively sticky → no teleport, no spacer. */
.tabstick-host {
  position: -webkit-sticky;
  position: sticky;
  top: var(--header-height);
  z-index: var(--z-sticky, 20);   /* PORT FIX (step4): --z-sticky undefined in f2e tokens.css;
                                     fallback matches PROTO's --z-sticky:20 (above page content,
                                     below header/drawers). */
}
/* DESKTOP (≥768): the snapped nav stays LEFT-aligned with the content grid (centring below is
   mobile-only — no horizontal shift on snap), and pins BELOW the full desktop header. That header
   carries the search row, so its visible height exceeds --header-height; offset by the header height
   + a small gap so it never covers the nav. Mobile keeps its top:var(--header-height) full-width pill. */
@media (min-width: 768px) {
  .tabstick-host { top: calc(var(--header-height) + var(--space-8)); }   /* header height + a small gap */
}
/* MOBILE ONLY — when the host IS the tabstick, centre the floating pill. */
@media (max-width: 767.98px) {
  .tabstick.tabstick-host { display: flex; justify-content: center; }
}

/* CONSISTENT CENTERING: when the host is a "nav + button" row (tabs-row / library-topbar /
   popcorn-navrow) the pill-nav `.tabstick` would otherwise sit LEFT. Give it a full line and centre
   the pill so EVERY snapped nav is centred (matches the centred bottom nav); the sibling button
   wraps below (these rows are flex-wrap on mobile). Scoped to `:has(> .glass-tabs)` so it targets the
   pill navs only — the shop category bar (.shop-filter) keeps its own stuck-centring + count-hide.
   Same alignment at rest AND stuck → no horizontal jump on snap. */
@media (max-width: 767.98px) {
  .tabstick-host > .tabstick:has(> .glass-tabs) {
    flex: 1 1 100%;
    min-width: 0;
    display: flex;
    justify-content: center;
  }
}

.tabstick > .glass-tabs { margin-bottom: 0; }
.tabstick--filter { margin-bottom: var(--space-8); }
@media (max-width: 767.98px) { .tabstick--filter { margin-bottom: var(--space-6); } }

/* ── The PILL — same box in flow and stuck (no width/position jump). As the host sticks, ONLY
   the glass MATERIAL resolves in, eased: backdrop-blur, depth shadow, border transition; the
   fill+sheen+rim ride pseudo OPACITY so the gradients fade rather than pop. Descendant
   selectors so it works whether the host is the .tabstick or the parent row. */
.tabstick-host .glass-tabs {
  position: relative;
  isolation: isolate;
  width: auto;                       /* hug → transparent sides (overrides phone width:100%) */
  max-width: 100%;
  margin-bottom: 0;
  padding-inline: 10px;              /* clear the pill's rounded ends — first/last tab not jammed */
  scroll-padding-inline: 10px;       /* same breathing room as a wide nav scrolls */
  border-color: rgba(255, 255, 255, 0.10);   /* always-glass: matches 我的投票 at rest too, not only when stuck */
  box-shadow: var(--glass-shadow);
  -webkit-backdrop-filter: var(--lg-frost);
          backdrop-filter: var(--lg-frost);
  transition: box-shadow      var(--dur-nav-morph) var(--ease-out),
              border-color    var(--dur-nav-morph) var(--ease-out),
              border-radius   var(--dur-nav-morph) var(--ease-out),
              -webkit-backdrop-filter var(--dur-nav-morph) var(--ease-out),
                      backdrop-filter var(--dur-nav-morph) var(--ease-out);
}
/* SHARED glass FILL (::before) for BOTH the .glass-tabs pill AND the .shop-filter
   pill — the white-5% lens that fades in on stick (the morph). NO sheen, NO spectral
   rim — exactly 21st.dev's recipe (the ::after rim was removed). */
.tabstick-host .glass-tabs::before,
.tabstick-host .shop-filter::before {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  background: var(--lg-fill); /* bright liquid glass (white-5%) — same material as 我的投票; shine + frost keep it visible */
  opacity: 1;    /* always-glass: the fill shows at rest too (not only on stick) */
  transition: opacity var(--dur-nav-morph) var(--ease-out);
}

/* ── STUCK: the shared glass MATERIAL resolves in on BOTH pill types (.is-pinned = currently stuck;
   .is-buried = replaced earlier row). Layout-specific bits (hug/scroll) stay per-component below. */
.tabstick-host.is-pinned .glass-tabs,
.tabstick-host.is-buried .glass-tabs,
.tabstick-host.is-pinned .shop-filter,
.tabstick-host.is-buried .shop-filter {
  border-color: rgba(255, 255, 255, 0.10);   /* = --glass-border colour */
  border-radius: var(--radius-pill);          /* always a PILL when snapped — some navs (e.g.
                                                 .library-genres 探索/AI/…) have a square base radius */
  box-shadow: var(--glass-shadow);
  -webkit-backdrop-filter: var(--lg-frost);
          backdrop-filter: var(--lg-frost);
}
.tabstick-host.is-pinned .glass-tabs::before,
.tabstick-host.is-buried .glass-tabs::before,
.tabstick-host.is-pinned .shop-filter::before,
.tabstick-host.is-buried .shop-filter::before { opacity: 1; }
.tabstick-host.is-pinned .glass-tabs::after,
.tabstick-host.is-buried .glass-tabs::after,
.tabstick-host.is-pinned .shop-filter::after,
.tabstick-host.is-buried .shop-filter::after  { opacity: 0.30; }

/* header-follow transform — ONLY when stuck (an in-flow host must not ride the header). */
.tabstick-host.is-pinned,
.tabstick-host.is-buried {
  transform: translateY(var(--tabs-shift, 0px));
}

/* ── BURIED: the earlier row fades out as the later one sticks (sequential replace). */
.tabstick-host.is-buried {
  opacity: 0;
  pointer-events: none;
  transition: opacity 360ms var(--ease-out);
}

/* ── 2nd-type nav (.shop-filter — the category bar). In flow it's the normal WRAPPING text filter;
   when its host sticks it morphs (eased — same transition + SHARED glass material/pseudos as the
   type-nav above) into a glass pill — single line, scrolls. Only the LAYOUT is per-component here;
   the material is shared, so the two navs read as one component on the sequential handoff. */
.tabstick-host .shop-filter {
  position: relative;
  isolation: isolate;
  border: var(--glass-border);       /* 0.8px solid, colour transparent until stuck (mirrors .glass-tabs) */
  border-color: rgba(255, 255, 255, 0.10);   /* always-glass: matches 我的投票 at rest too, not only when stuck */
  box-shadow: var(--glass-shadow);
  -webkit-backdrop-filter: var(--lg-frost);
          backdrop-filter: var(--lg-frost);
  transition: box-shadow      var(--dur-nav-morph) var(--ease-out),
              border-color    var(--dur-nav-morph) var(--ease-out),
              border-radius   var(--dur-nav-morph) var(--ease-out),
              -webkit-backdrop-filter var(--dur-nav-morph) var(--ease-out),
                      backdrop-filter var(--dur-nav-morph) var(--ease-out);
}
.tabstick-host.is-pinned .shop-filter,
.tabstick-host.is-buried .shop-filter {
  flex-wrap: nowrap;                 /* was wrap → single scrolling line when stuck */
  width: auto;                       /* hug → floating pill */
  max-width: 100%;
  gap: 2px;
  padding: 5px 8px;
  overflow-x: auto;
  border-radius: var(--radius-pill);
}
.tabstick-host.is-pinned .shop-filter .shop-filter__item,
.tabstick-host.is-buried .shop-filter .shop-filter__item {
  padding: 6px 12px;
  white-space: nowrap;               /* don't let a label wrap inside the pill */
  text-decoration: none;             /* drop the in-flow underline; active = pill highlight below */
  border-radius: var(--radius-pill);
}
.tabstick-host.is-pinned .shop-filter .shop-filter__item--active,
.tabstick-host.is-buried .shop-filter .shop-filter__item--active {
  background: var(--white-alpha-10, rgba(255,255,255,0.10));
  color: var(--neutral-white);
}
/* The host row is the whole .shop-toolbar (it carries the filter + a count, so a sticky element
   can only be this row). MOBILE: when stuck, centre the pill, constrain it to the viewport (it
   scrolls inside), drop the in-flow divider/spacing, and hide the count so only the floating pill
   shows. DESKTOP keeps the toolbar's natural left-aligned layout (the pill stays at the grid's left
   edge in both states — no horizontal shift on snap). */
@media (max-width: 767.98px) {
  .shop-toolbar.tabstick-host.is-pinned,
  .shop-toolbar.tabstick-host.is-buried {
    justify-content: center;
    border-bottom-color: transparent;
    margin-top: 0;
    padding-bottom: 0;
  }
  .shop-toolbar.tabstick-host.is-pinned  > .tabstick,
  .shop-toolbar.tabstick-host.is-buried  > .tabstick { min-width: 0; max-width: 100%; }
  .shop-toolbar.tabstick-host.is-pinned  .shop-count,
  .shop-toolbar.tabstick-host.is-buried  .shop-count { display: none; }
}

/* ════════════════════════════════════════════════════════════════════
   COLLAPSE-TO-DROPDOWN  (nav-collapse.js) — a SNAPPED nav wider than the screen becomes a compact
   "[active] ⌄" trigger; tap drops the full list down as a glass panel. Only active when the module
   adds .is-collapsed (snapped + overflowing), so this never affects fit navs or the rest state.
   ════════════════════════════════════════════════════════════════════ */
.nav-trigger { display: none; }                 /* only shown when collapsed */
.tabstick.is-collapsed { position: relative; }  /* anchor the dropdown */
.tabstick.is-collapsed > .glass-tabs,
.tabstick.is-collapsed > .shop-filter { display: none; }   /* hide the chip row; trigger stands in */
.tabstick.is-collapsed > .nav-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  max-width: 100%;
  padding: 7px 14px;
  border-radius: var(--radius-pill);
  background: var(--lg-fill);             /* bright liquid glass (white-5%) — same material as 我的投票; shine + frost keep it visible */
  border: var(--glass-border);
  box-shadow: var(--glass-shadow);
  -webkit-backdrop-filter: var(--lg-frost);
          backdrop-filter: var(--lg-frost);
  color: var(--neutral-white);
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 1.3;
  cursor: pointer;
}
.nav-trigger__label { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nav-trigger__chev {
  width: 9px; height: 9px; flex: none; opacity: 0.7;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-2px) rotate(45deg);     /* ⌄ */
  transition: transform var(--dur-nav-show) var(--ease-out);
}
.tabstick.is-open > .nav-trigger .nav-trigger__chev { transform: translateY(1px) rotate(-135deg); }  /* ⌃ */

/* OPEN: the real item list drops down below the trigger as a vertical glass panel (handlers intact). */
.tabstick.is-collapsed.is-open > .glass-tabs,
.tabstick.is-collapsed.is-open > .shop-filter {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  position: absolute;
  top: calc(100% + 8px);
  left: 50%;
  transform: translateX(-50%);
  width: max-content;
  max-width: 88vw;
  max-height: 60vh;
  overflow-y: auto;
  z-index: var(--z-nav-pop, 40);   /* PORT FIX (step4): --z-nav-pop undefined in f2e tokens.css;
                                       fallback matches PROTO's --z-nav-pop:40 (above sticky pill,
                                       below sheets/drawers). */
  padding: 6px;
  gap: 2px;
  border-radius: 16px;
}
.tabstick.is-collapsed.is-open .glass-tabs__item,
.tabstick.is-collapsed.is-open .shop-filter__item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 11px 16px;
  border-radius: 10px;
  white-space: nowrap;
}

/* ════════════════════════════════════════════════════════════════════
   MOBILE HEADER HIDE/SHOW  (<768)
   ════════════════════════════════════════════════════════════════════ */
@media (max-width: 767.98px) {
  /* Header hide/show. CRITICAL: transform/will-change apply ONLY in the hidden state —
     a permanent transform on the header promotes a layer that breaks its backdrop-filter. */
  .header {
    transition: transform var(--dur-nav-show) var(--ease-out),
                opacity   var(--dur-nav-show) var(--ease-out);
  }
  body.header-hidden .header {
    transform: translateY(-100%);
    opacity: 0;
    transition: transform var(--dur-nav-hide) var(--ease-emphasized),
                opacity   var(--dur-nav-hide) var(--ease-emphasized);
  }

  /* The stuck host + community tab bar ride the header via --tabs-shift, same curve. */
  body.header-hidden .tabstick-host.is-pinned,
  body.header-hidden .tabstick-host.is-buried,
  body.header-hidden .community-tabbar {
    transition: transform var(--dur-nav-hide) var(--ease-emphasized);
  }
  .community-tabbar {
    transform: translateY(var(--tabs-shift, 0px));
    will-change: transform;
    transition: transform var(--dur-nav-show) var(--ease-out);
  }
}

/* ── Reduced motion / snap: the material resolves instantly, no eased morph, no drift. */
@media (prefers-reduced-motion: reduce) {
  .header,
  .community-tabbar,
  .tabstick-host .glass-tabs,
  .tabstick-host .glass-tabs::before,
  .tabstick-host .glass-tabs::after,
  .tabstick-host.is-pinned,
  .tabstick-host.is-buried { transition: none !important; }
}
body.motion-snap .header,
body.motion-snap .community-tabbar,
body.motion-snap .tabstick-host .glass-tabs,
body.motion-snap .tabstick-host .glass-tabs::before,
body.motion-snap .tabstick-host .glass-tabs::after,
body.motion-snap .tabstick-host.is-pinned,
body.motion-snap .tabstick-host.is-buried { transition: none !important; }

/* ─────────────────────────────────────────
   SHOP v2 — 商店 (hub / 爆米花商店 / 創作者商店 / 活動)
   Rebuilt 2026-06-11 on luxury-PLP research (SSENSE / Net-a-Porter /
   Mr Porter / Farfetch / END live-render audit):
   · cards are chromeless — no border, no panel, no radius, no shadow,
     no hover lift. The image IS the card, locked 2:3 on one matte field.
   · text block: stacked left-aligned lines at ONE size; hierarchy from
     case + color only.
   · grid: 4 columns, tight column gap / wide row gap (editorial bands).
   · filters: plain text links + quiet result count over a hairline.
   Creator storefront re-ordered per Weverse/Fourthwall patterns;
   leaderboard per podium→table→my-rank canon.
   ───────────────────────────────────────── */

/* .glass-tabs--push margin removed 2026-06-23: it added ~36px above the tab bar
   on top of the section's own padding-top, opening the void above the tabs on the
   creator/leaderboard pages. The section padding-top is the single owner of that
   gap now. Class kept on the markup (inert) so ~13 pages need no edit. */
.glass-tabs--push { margin-top: 0; }

/* 商店家族 tab 排（0806 endgame，Dolly 拍板）：願望清單移出 tab 群＝
   同一排最右側白玻璃鈕（btn--ghost btn--sm），tab 群靠左 */
.shop-typerow {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}
.shop-typerow__wish { flex: 0 0 auto; }

/* PLP head — quiet, never a hero */
.shop-head {
  display: flex;
  align-items: baseline;
  gap: var(--space-4);
  margin-top: var(--space-2);
}
.shop-head__title {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h3);   /* 24px — 0729 對齊 .page-head__title（共創頁首規格） */
  font-weight: var(--fw-bold);
  line-height: var(--lh-h3);
  letter-spacing: -0.5px;
}
.shop-head__desc {
  font-size: 13px;    /* 原 PROTO --fs-page-desc；f2e 無該 token，13px 與次選單同級 */
  color: var(--text-tertiary);
}
/* 作品專頁模式的「前往作品頁」推到同一排最右（Dolly 0801）。
   只在桌機的 row 版生效——手機 .shop-head 是 column，auto margin 會把它推到
   右邊落單，維持原本跟在標題下方靠左。 */
@media (min-width: 768px) {
  .shop-head [data-shop-work-link] { margin-left: auto; }
}

/* Toolbar — text-link filters left, count right, one hairline below */
.shop-toolbar {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-6);
  margin-top: 30px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border-subtle);
}
.shop-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 22px;
}
.shop-filter__item {
  background: none;
  border: none;
  padding: 0;
  font-family: var(--font-cjk-text);
  font-size: 14px;   /* 0728 Dolly：13→14，對齊片庫 films-rail__tab */
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 150ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .shop-filter__item:hover { color: var(--neutral-white); }
}
.shop-filter__item--active {
  /* 0728 Dolly 終版：全站文字選單統一 14px/400/白字橘線（2px） */
  color: var(--neutral-white);
  text-decoration: underline;
  text-decoration-color: var(--yellow-500);
  text-decoration-thickness: 2px;
  text-underline-offset: 8px;
}
.shop-count {
  display: none;   /* 0728 Dolly：件數/人數資訊先移除（markup/JS 保留，要恢復刪此行） */
  font-family: var(--font-latin);
  font-size: 12px;
  color: var(--text-tertiary);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* The grid — editorial bands */
.shop-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--space-4);
  row-gap: var(--space-14);   /* 0729 Dolly：44→56——卡底砍掉鈕撐的空之後，上下排要補回距離 */
  margin-top: 36px;
}
@media (max-width: 1180px) { .shop-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* Chromeless product card */
.shop-card {
  display: block;
  text-decoration: none;
  color: inherit;
}
.shop-card__media {
  position: relative;
  aspect-ratio: 2 / 3;
  background: var(--bg-secondary);   /* one matte field for every image */
  overflow: hidden;
}
.shop-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.shop-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);           /* 0729 Dolly：兩輪迭代定案（UA margin 已砍，見 __title） */
  padding-top: var(--space-3);   /* 圖下維持原 12；收緊主要靠砍 UA margin＋價格列不被鈕撐高 */
}
.shop-card__flag {
  font-family: var(--font-latin);
  font-size: 11px;
  letter-spacing: 0.08em;
  color: var(--yellow-500);
}
.shop-card__cat {
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--text-tertiary);
}
.shop-card__title {
  margin: 0;   /* 0729：砍 h3 UA margin（上下各 ~13px），間距全交給 body 的 flex gap */
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-regular);
  line-height: 1.5;
  color: var(--neutral-white);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.shop-card__price {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: 2px;
  font-family: var(--font-latin);
  font-size: 13px;
  font-weight: var(--fw-medium);
  color: var(--neutral-white);
  font-variant-numeric: tabular-nums;
}
.shop-card__price-sub {
  font-weight: var(--fw-regular);
  color: var(--text-tertiary);
}
.shop-card__price-pop-icon {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  background: var(--yellow-500);
  -webkit-mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
          mask: url('assets/icons/popcorn_single.svg') center / contain no-repeat;
}
.shop-card__note {
  font-size: 12px;
  color: var(--text-tertiary);
  line-height: 1.5;
}
.shop-card__redeem {
  margin-left: auto;
  font-size: 12px;
  color: var(--yellow-500);
}
/* Sold out: honest dimming, a word instead of a price */
.shop-card--soldout .shop-card__media img,
.shop-card--soldout .brand-tile { filter: grayscale(1) brightness(0.5); }
.shop-card--soldout .shop-card__title { color: var(--text-tertiary); }

/* Brand voucher tiles — typographic gradient tiles, never hotlinked logos */
.brand-tile {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px;
  text-align: center;
  font-family: var(--font-cjk-display);
  font-weight: var(--fw-bold);
  font-size: 18px;
  line-height: 1.4;
  color: var(--neutral-white);
}
.brand-tile--1 { background: linear-gradient(135deg, var(--neutral-700), var(--neutral-900)); }
.brand-tile--2 { background: linear-gradient(135deg, var(--yellow-800), var(--neutral-900)); }
.brand-tile--3 { background: linear-gradient(135deg, var(--neutral-600), var(--neutral-800)); }
.brand-tile--4 { background: linear-gradient(160deg, var(--yellow-900), var(--neutral-800)); }
.brand-tile--5 { background: linear-gradient(135deg, var(--neutral-800), var(--yellow-900)); }
.brand-tile--6 { background: linear-gradient(160deg, var(--neutral-700), var(--yellow-800)); }

/* ─────────────────────────────────────────
   RESPONSIVE — shop hub
   ───────────────────────────────────────── */

/* ── Tablet: 768–1023px ── */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .shop-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--space-3);
    row-gap: var(--space-10);
  }

  .shop-toolbar {
    gap: var(--space-4);
  }

  .shop-filter {
    gap: var(--space-4);
  }
}

/* ── Phone: ≤767px ── */
@media (max-width: 767.98px) {
  /* Head */
  .shop-head {
    flex-direction: column;
    gap: var(--space-1);
    align-items: flex-start;
  }

  /* Toolbar: filters wrap below count */
  .shop-toolbar {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-3);
    margin-top: var(--space-5);
    padding-bottom: var(--space-3);
  }

  .shop-filter {
    gap: var(--space-4);
    flex-wrap: wrap;
  }

  /* Touch targets: ensure filter buttons are at least 44px tall */
  .shop-filter__item {
    padding-block: 10px;
    font-size: var(--fs-body-sm);
  }

  /* Grid: 2-up on phone */
  .shop-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-3);
    row-gap: var(--space-8);
    margin-top: var(--space-6);
  }

  /* Card body: tighter but legible */
  .shop-card__body {
    gap: var(--space-1);
    padding-top: var(--space-2);
  }

  .shop-card__title {
    font-size: var(--fs-caption);
  }

  .shop-card__price {
    font-size: var(--fs-caption);
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-top: var(--space-1);
  }

  .shop-card__price-sub {
    white-space: nowrap;
  }

  .shop-card__flag,
  .shop-card__cat {
    font-size: 10px;
  }

  .shop-card__note {
    font-size: 11px;
  }

  /* Brand tiles: reduce internal padding so text fits narrow cards */
  .brand-tile {
    font-size: var(--fs-body-sm);
    padding: var(--space-3);
  }
}

/* ── Small phone: ≤480px — 1-up for content-rich cards ── */
@media (max-width: 480px) {
  .shop-grid {
    grid-template-columns: minmax(0, 1fr);
    row-gap: var(--space-8);
  }

  /* Single-column: image can be shorter; preserve 2:3 or switch to 3:4 */
  .shop-card__media {
    aspect-ratio: 3 / 4;
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   F14 WISHLIST — head-row entry link + list-page states
   Ported from L's endgame components.css (curl https://endgame.ztor.lx7.com/
   components.css, 2026-07-28) — source rules there: .shop-head__wish*,
   .shop-toolbar--wish, .shop-card--wish-removed, .wish-undo*, .wish-empty*,
   .wish-gate*. Verbatim except: (1) the `.shop-head { position: relative; }`
   base rule is dropped — this file's .shop-head is already the flex/baseline
   row above, and the phone rule below only needs position on the phone
   column layout, which already gets it from that block's own flex context
   (added inline there instead of re-declaring the shared selector here);
   (2) .wish-gate is ported for completeness but UNUSED by our wishlist.html —
   our spec (checkout.md) gates login at checkout only, so the page never
   shows a logged-out block state (see port report).
   Entry: ♥ 願望清單 · N on every shop-family head row. Count hides at 0 (a
   zero count advertises emptiness). Cards are the shared shop-card — no new
   card CSS needed for the list page itself.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── entry link on .shop-head (all 5 shop-family hub pages) ── */
.shop-head__wish {
  position: relative;
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 1;
  color: var(--text-secondary);
  text-decoration: none;
  white-space: nowrap;
  transition: color 160ms ease;
}
.shop-head__wish:hover { color: var(--text-primary); }
.shop-head__wish-icon { width: 16px; height: 16px; display: inline-grid; place-items: center; }
.shop-head__wish-icon svg { width: 100%; height: 100%; display: block; }
.shop-head__wish-count {
  font-variant-numeric: tabular-nums;   /* the count changes live — no width jitter */
  color: var(--yellow-500);
  font-weight: var(--fw-bold);
}
.shop-head__wish-count::before {
  content: '· ';
  color: var(--text-tertiary);
  font-weight: var(--fw-regular);
}
/* Phone: .shop-head is already flex-direction:column above — pin the link to
   the TITLE row, top-right (matches L's sketch: same row as the H1). */
@media (max-width: 767.98px) {
  .shop-head__wish { position: absolute; right: 0; top: 4px; margin-left: 0; }
}

/* ── wishlist page (body[data-feature="wishlist"]) ── */
/* Hearts on the list ARE the remove control — always visible. The shop's
   desktop hover-reveal (refine-shop.css) would hide the primary action here. */
body[data-feature="wishlist"] .rf-wish { opacity: 1 !important; transform: none !important; }
body[data-feature="wishlist"] .shop-card { position: relative; }

/* toolbar carries only the count — no filters on a self-curated list */
.shop-toolbar--wish { justify-content: flex-end; margin-top: var(--space-5); }

/* SIGNATURE — removed-in-place: the card dims and goes inert; the 復原 chip
   stays live. Eviction happens on the next render, never on the tap. */
.shop-card--wish-removed { pointer-events: none; }
.shop-card--wish-removed .shop-card__media,
.shop-card--wish-removed .shop-card__body {
  opacity: 0.35;
  filter: grayscale(0.4);
  transition: opacity 300ms var(--ease-settle), filter 300ms var(--ease-settle);
}
.wish-undo {
  position: absolute;
  inset: auto 10px 10px 10px;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 9px 12px 9px 14px;
  border-radius: var(--radius-pill);
  background: rgba(10, 10, 10, 0.78);
  border: var(--glass-border);
  border-color: rgba(255, 255, 255, 0.10);
  box-shadow: var(--glass-shadow);
  -webkit-backdrop-filter: var(--lg-frost);
          backdrop-filter: var(--lg-frost);
  font-family: var(--font-cjk-text);
  font-size: 13px;
  color: var(--text-secondary);
  pointer-events: auto;                 /* the card is inert; the chip is not */
}
.wish-undo__btn {
  border: none;
  background: none;
  cursor: pointer;
  padding: 6px 8px;
  margin: -6px -8px;                    /* 復原 keeps a real touch target */
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-bold);
  color: var(--yellow-500);
}
.wish-undo__btn:hover { text-decoration: underline; }

/* ── empty state — the first thing most people see; designed, never a void ── */
.wish-empty,
.wish-gate {
  text-align: center;
  padding: 72px 24px 84px;
}
.wish-empty__icon,
.wish-gate__icon {
  width: 44px;
  height: 44px;
  margin: 0 auto 18px;
  color: var(--text-tertiary);
  opacity: 0.85;
}
.wish-empty__icon svg,
.wish-gate__icon svg { width: 100%; height: 100%; display: block; }
.wish-empty__title,
.wish-gate__title {
  font-family: var(--font-cjk-display);
  font-size: 19px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.wish-empty__desc,
.wish-gate__desc {
  margin-top: 10px;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-tertiary);
}
.wish-empty__cta,
.wish-gate__cta { margin-top: 26px; }

/* ─────────────────────────────────────────
   SHOP FILTER SHEET — mobile-only nav restructure for the shop umbrella.

   On phones (≤767.98px): the shop-TYPE nav (商店/爆米花商店/…) shares one
   PINNED row with a filter icon (nav left, scrolls; icon pinned right). The
   per-page category filter (the old 2nd nav, .shop-toolbar) is hidden inline
   and its options move into a glass BOTTOM SHEET. On 爆米花商店, 兌換紀錄 is
   lifted above the pinned row (non-sticky). Active filters show as chips.

   DESKTOP IS UNTOUCHED: every layout rule is inside @media (max-width: 767.98px),
   and the always-on pieces (trigger icon, sheet, scrim, chips, prerow) are
   display:none above the phone breakpoint. The JS-added .shop-typenav-row class
   has no desktop rules. Loaded LAST in main.css so it wins over 45-sticky-tabs.
   Reuses glass + motion tokens — no new magic numbers.
   ───────────────────────────────────────── */

/* Injected by shop-filter-sheet.js — hidden until the phone breakpoint. */
.shop-filter-trigger { display: none; }
.shop-active-filters { display: none; }

/* The glass bottom sheet + scrim are a phone-only control. */
@media (min-width: 768px) {
  .shop-sheet, .shop-sheet-scrim { display: none !important; }
}

@media (max-width: 767.98px) {

  /* ── Pinned row: shop-type nav (left, scrolls) + filter icon (right) ── */
  /* Beats 45-sticky-tabs' `.tabstick.tabstick-host{justify-content:center}`
     by equal specificity + later source order (this file imports last). */
  .tabstick.shop-typenav-row {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: var(--space-2);
    justify-content: flex-start;
  }
  .tabstick.shop-typenav-row > .glass-tabs--push {
    flex: 1 1 auto;                  /* FILL the row up to the filter icon (no dead gap) */
    min-width: 0;
    margin-bottom: 0;
    justify-content: space-between;  /* spread the 5 items across the full pill width */
  }

  .shop-filter-trigger {
    flex: 0 0 auto;
    margin-left: auto;       /* pinned hard right */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-pill);
    border: var(--glass-border);
    background: var(--glass-fill);
    -webkit-backdrop-filter: var(--glass-backdrop);
            backdrop-filter: var(--glass-backdrop);
    box-shadow: var(--glass-shadow);
    color: var(--text-primary);
    position: relative;
  }
  .shop-filter-trigger svg { width: 20px; height: 20px; }
  .shop-filter-trigger__count {
    position: absolute;
    top: -3px;
    right: -3px;
    min-width: 17px;
    height: 17px;
    padding: 0 4px;
    border-radius: var(--radius-pill);
    background: var(--yellow-500);
    color: var(--yellow-ink);
    font-size: 10px;
    line-height: 17px;
    font-weight: var(--fw-bold);
    text-align: center;
    display: none;
  }
  .shop-filter-trigger.has-active .shop-filter-trigger__count { display: block; }

  /* ── Main nav fit: condense the shop-type items so all 5 fit IN-PILL without
     horizontal scroll. Scoped to .shop-typenav-row so other glass-tab navs
     (lifecycle / platform / creator tabs) keep their roomier spacing. The pill
     keeps overflow-x:auto (from .glass-tabs) as the LAST-RESORT fallback: it
     only scrolls if a device is too narrow to fit even condensed. ── */
  .shop-typenav-row .glass-tabs__item {
    padding-inline: var(--space-2);   /* comfortable interior; space-between adds the breathing room */
    font-size: var(--fs-caption);
  }

  /* ── 2nd nav (category toolbar) — moved into the sheet, hidden inline ── */
  .shop-toolbar { display: none; }

  /* ── 兌換紀錄 (popcorn) — on the TITLE row: title left, link right-aligned.
     JS moves the link into .shop-head and adds .shop-head--with-action; the
     desc drops to its own line below. (Supersedes the prerow placement.) ── */
  .shop-head--with-action {
    display: flex;
    flex-direction: row;          /* base .shop-head is column — title+link must be a row */
    flex-wrap: wrap;
    align-items: center;
    column-gap: var(--space-3);
  }
  .shop-head--with-action .popcorn-history-link { margin-left: auto; }
  .shop-head--with-action .shop-head__desc { flex: 1 1 100%; }

  /* ── Active-filter chips (below the pinned row, above the grid) ── */
  .shop-active-filters {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin: var(--space-4) 0 var(--space-2);
  }
  .shop-active-filters:empty { display: none; }
  .shop-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 8px 6px 12px;
    border-radius: var(--radius-pill);
    background: var(--white-alpha-8);
    border: 0.8px solid var(--border-default);
    color: var(--text-primary);
    font-size: var(--fs-body-sm);
    line-height: 1;
  }
  .shop-chip__x {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 18px;
    height: 18px;
    border: none;
    border-radius: 50%;
    background: var(--white-alpha-8);
    color: var(--text-secondary);
    font-size: 13px;
    line-height: 1;
    cursor: pointer;
  }
  .shop-chip__x:hover { color: var(--text-primary); background: var(--white-alpha-16); }
}

/* ── The TOP sheet + scrim (rendered into <body> by JS). Full-width, slides
   DOWN from under the header; capped so it never reaches the fixed bottom nav.
   z-index sits BELOW the header (which is 100) so the panel emerges from under
   the header instead of sliding over it; the header stays crisp above the dim. */
.shop-sheet-scrim {
  position: fixed;
  inset: var(--header-height) 0 0 0;   /* dim the content BELOW the header */
  z-index: var(--z-sheet-scrim, 90);   /* PORT FIX (step4): --z-sheet-scrim undefined in f2e
                                          tokens.css; fallback matches PROTO's --z-sheet-scrim:90. */
  background: rgba(0, 0, 0, 0.5);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-nav-show) var(--ease-out);
}
.shop-sheet-scrim.is-open { opacity: 1; pointer-events: auto; }

.shop-sheet {
  position: fixed;
  left: 0;
  right: 0;
  top: var(--header-height);           /* clears the header */
  z-index: var(--z-sheet, 95);         /* below the header (100) → slides from under it.
                                           PORT FIX (step4): --z-sheet undefined in f2e
                                           tokens.css; fallback matches PROTO's --z-sheet:95. */
  /* never reach the bottom-nav zone */
  max-height: calc(100dvh - var(--header-height) - var(--space-24));
  display: flex;
  flex-direction: column;
  border-radius: 0 0 var(--radius-2xl) var(--radius-2xl);
  background: var(--glass-fill);
  -webkit-backdrop-filter: var(--glass-backdrop);
          backdrop-filter: var(--glass-backdrop);
  border-bottom: var(--glass-border);
  box-shadow: var(--glass-shadow);
  transform: translateY(-110%);        /* parked above the header, slides down to 0 */
  /* visibility:hidden guarantees the sheet is FULLY gone when closed — translate
     alone leaves ~0.1×height peeking below the viewport top (only masked while
     the header is shown; it reappears once the header hides on scroll). The 0s
     visibility delay = the slide duration on close (stays visible through the
     slide-out), instant on open. */
  visibility: hidden;
  transition: transform var(--dur-panel-exit, 280ms) var(--ease-emphasized),
              visibility 0s linear var(--dur-panel-exit, 280ms);
  padding: var(--space-4) var(--space-5);
}
.shop-sheet.is-open {
  transform: translateY(0);
  visibility: visible;
  transition: transform var(--dur-nav-morph) var(--ease-out),
              visibility 0s;
}

.shop-sheet__grip {
  width: 36px;
  height: 4px;
  border-radius: 2px;
  background: var(--white-alpha-16);
  margin: var(--space-3) auto 0;       /* handle at the BOTTOM edge — swipe up to close */
  flex: 0 0 auto;
}
.shop-sheet__title {
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin: 0 0 var(--space-4);
  flex: 0 0 auto;
}
.shop-sheet__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  overflow-y: auto;
  margin: 0;
  padding: 2px;
}
.shop-sheet__opt {
  padding: 9px 15px;
  border-radius: var(--radius-pill);
  border: 0.8px solid var(--border-default);
  background: transparent;
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  transition: background var(--dur-nav-show) var(--ease-out),
              border-color var(--dur-nav-show) var(--ease-out),
              color var(--dur-nav-show) var(--ease-out);
}
.shop-sheet__opt[aria-pressed="true"] {
  background: var(--white-alpha-16);
  border-color: var(--border-strong);
  color: var(--text-primary);
}
.shop-sheet__apply {
  flex: 0 0 auto;
  margin-top: var(--space-4);
  width: 100%;
  padding: 14px;
  border: none;
  border-radius: var(--radius-pill);
  background: var(--yellow-500);
  color: var(--yellow-ink);
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  cursor: pointer;
}

/* Reduced motion: the sheet appears without the slide. */
@media (prefers-reduced-motion: reduce) {
  .shop-sheet,
  .shop-sheet-scrim { transition: none; }
}

/* ════════════════════════════════════════════════════════════════
   47-checkout.css — multi-step checkout (rendered by checkout.js into the
   cart drawer's checkout view). Reuses cc-field / cc-pay / btn / status-tag;
   this file adds the stepper progress, delivery rows, review, promo, and the
   order tracker. Tokens only.
   ════════════════════════════════════════════════════════════════ */

/* progress steps */
.checkout-steps { display: flex; align-items: center; gap: var(--space-1); margin: 0 0 var(--space-5); padding: 0; list-style: none; }
.checkout-steps__item { display: flex; align-items: center; gap: var(--space-2); flex: 1; min-width: 0; }
.checkout-steps__item:not(:last-child)::after { content: ''; flex: 1; height: 1px; background: var(--border-default); }
.checkout-steps__dot {
  width: 24px; height: 24px; flex: none; display: grid; place-items: center;
  border-radius: var(--radius-pill); border: 1px solid var(--border-default);
  font-family: var(--font-latin); font-size: var(--fs-caption); font-weight: var(--fw-bold);
  color: var(--text-tertiary); background: var(--bg-secondary);
}
.checkout-steps__item.is-active .checkout-steps__dot { border-color: var(--yellow-500); color: var(--yellow-ink); background: var(--yellow-500); }
.checkout-steps__item.is-done .checkout-steps__dot { border-color: var(--yellow-500); color: var(--yellow-500); background: transparent; }
.checkout-steps__label { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); white-space: nowrap; }
.checkout-steps__item.is-active .checkout-steps__label { color: var(--text-primary); }

/* form */
.checkout-form { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-5); }
.checkout-form__row { display: flex; gap: var(--space-3); }
.checkout-form__row .cc-field { flex: 1; min-width: 0; }

/* delivery methods */
.checkout-methods { display: flex; flex-direction: column; gap: var(--space-3); margin-bottom: var(--space-5); }
.checkout-method {
  display: flex; align-items: center; gap: var(--space-3); text-align: left;
  padding: var(--space-4); border: 1px solid var(--border-default); border-radius: var(--radius-md);
  background: var(--white-alpha-4); color: var(--text-primary); cursor: pointer;
  transition: border-color 160ms var(--ease-out), background 160ms var(--ease-out);
}
.checkout-method:hover { border-color: var(--border-strong); }
.checkout-method.is-selected { border-color: var(--yellow-500); background: rgba(255, 163, 63, 0.06); }
.checkout-method:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.checkout-method__radio { width: 18px; height: 18px; flex: none; border-radius: var(--radius-pill); border: 2px solid var(--border-strong); display: grid; place-items: center; }
.checkout-method.is-selected .checkout-method__radio { border-color: var(--yellow-500); }
.checkout-method.is-selected .checkout-method__radio::after { content: ''; width: 9px; height: 9px; border-radius: var(--radius-pill); background: var(--yellow-500); }
.checkout-method__main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.checkout-method__label { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-medium); }
.checkout-method__eta { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.checkout-method__fee { font-family: var(--font-latin); font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); white-space: nowrap; }

/* summary */
.checkout-sum { margin-top: var(--space-4); }
.checkout-sum__lines { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); padding-bottom: var(--space-4); border-bottom: 1px solid var(--border-subtle); }
.checkout-sum__line { display: flex; justify-content: space-between; gap: var(--space-3); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.checkout-sum__line-title { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.checkout-sum__discount span:last-child { color: var(--success-500); }

/* review */
.checkout-review { display: flex; flex-direction: column; gap: 0; margin-bottom: var(--space-5); }
.checkout-review__block { display: grid; grid-template-columns: 48px 1fr auto; gap: var(--space-3); align-items: start; padding: var(--space-3) 0; border-bottom: 1px solid var(--border-subtle); }
.checkout-review__k { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.checkout-review__v { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-primary); line-height: 1.5; }
.checkout-review__edit { padding: 0; border: none; background: none; cursor: pointer; color: var(--yellow-500); font-family: var(--font-cjk-text); font-size: var(--fs-caption); }

/* promo */
.checkout-promo { margin-bottom: var(--space-5); }
.checkout-promo__row { display: flex; gap: var(--space-2); }
.checkout-promo__input { flex: 1; min-width: 0; }
.checkout-promo__msg { margin: var(--space-2) 0 0; font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.checkout-promo__msg.is-error { color: var(--error-500); }

.checkout-cta { width: 100%; }
.checkout-foot__hint { margin: 0 0 var(--space-3); text-align: center; font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }

/* ── review hub (single-screen checkout) ── */
.checkout-hub { display: flex; flex-direction: column; gap: var(--space-5); }
.checkout-hub__group { display: flex; flex-direction: column; gap: var(--space-2); }
.checkout-hub__label { margin: 0; font-family: var(--font-cjk-text); font-size: var(--fs-caption); font-weight: var(--fw-medium); letter-spacing: 0.02em; color: var(--text-tertiary); }
.checkout-hub__row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-4); border: 1px solid var(--border-default); border-radius: var(--radius-md);
  background: var(--white-alpha-4);
}
.checkout-hub__row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.checkout-hub__row-k { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.checkout-hub__row-v { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-primary); line-height: 1.5; }
.checkout-hub__edit { flex: none; padding: 0; border: none; background: none; cursor: pointer; color: var(--yellow-500); font-family: var(--font-cjk-text); font-size: var(--fs-caption); font-weight: var(--fw-medium); }
.checkout-hub__edit:hover { text-decoration: underline; }
.checkout-hub__add {
  display: flex; align-items: center; gap: var(--space-3); width: 100%; text-align: left;
  padding: var(--space-4); border: 1px dashed rgba(255, 163, 63, 0.5); border-radius: var(--radius-md);
  background: rgba(255, 163, 63, 0.06); color: var(--yellow-500);
  font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-medium); cursor: pointer;
  transition: background 160ms var(--ease-out), border-color 160ms var(--ease-out);
}
.checkout-hub__add:hover { background: rgba(255, 163, 63, 0.1); border-color: var(--yellow-500); }
.checkout-hub__add-icon { width: 22px; height: 22px; flex: none; display: grid; place-items: center; border-radius: var(--radius-pill); background: rgba(255, 163, 63, 0.18); font-size: 15px; line-height: 1; }
.checkout-hub__group .checkout-methods,
.checkout-hub__group .checkout-sum { margin: 0; }
.checkout-hub__group .checkout-promo { margin: 0; }
.checkout-hub > .checkout-sum { margin-top: 0; }

/* confirmation */
.checkout-confirm__order { margin-top: var(--space-3); padding: var(--space-3) var(--space-5); border: 1px dashed var(--border-default); border-radius: var(--radius-md); font-family: var(--font-latin); font-size: var(--fs-body-sm); color: var(--text-primary); letter-spacing: 0.04em; }
/* 完成頁 footer 雙鈕：等寬等高一排（0729 Dolly） */
.checkout-confirm__actions { display: flex; gap: var(--space-3); }
.checkout-confirm__actions .btn { flex: 1 1 0; }
.checkout-confirm__order b { color: var(--yellow-500); font-weight: var(--fw-bold); }

/* order tracker (mini, horizontal) */
.order-tracker { display: flex; align-items: flex-start; gap: var(--space-1); margin-top: var(--space-5); width: 100%; }
.order-tracker__node { flex: 1; display: flex; flex-direction: column; align-items: center; gap: var(--space-2); position: relative; }
/* 未實現段：實色灰（0729 Dolly——半透明白疊深底顯髒，改 neutral 實色） */
.order-tracker__node:not(:last-child)::after { content: ''; position: absolute; top: 6px; left: 50%; width: 100%; height: 2px; background: var(--neutral-600); }
.order-tracker__dot { width: 14px; height: 14px; border-radius: var(--radius-pill); background: var(--neutral-600); position: relative; z-index: 1; }
.order-tracker__node.is-current .order-tracker__dot { background: var(--yellow-500); }
.order-tracker__node.is-done .order-tracker__dot { background: var(--yellow-500); }
/* 已完成節點 → 下一點的連線＝橘（0729 Dolly）；current 之後的段維持灰 */
.order-tracker__node.is-done:not(.is-current)::after { background: var(--yellow-500); }
.order-tracker__label { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.order-tracker__node.is-current .order-tracker__label { color: var(--text-primary); }

@media (prefers-reduced-motion: reduce) {
  .checkout-method { transition: none; }
}

/* ════════════════════════════════════════════════════════════════
   48-order-tracker.css — the "我的訂單" surface (orders / tickets / bids /
   rentals) in my-library.html, rendered by my-orders.js. Cards + ticket-QR
   placeholder. The mini order-tracker lives in 47-checkout.css and is reused.
   Tokens only.
   ════════════════════════════════════════════════════════════════ */

.orders-tabs { margin-bottom: var(--space-6); }
.orders-list { display: grid; grid-template-columns: 1fr; gap: var(--space-4); }
@media (min-width: 768px) { .orders-list { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-5); } }

.orders-card {
  display: flex; flex-direction: column; gap: var(--space-3);
  padding: var(--space-5);
  border: 1px solid var(--border-subtle); border-radius: var(--radius-xl);
  background: var(--bg-secondary);
}
.orders-card__head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); }
.orders-card__id { font-family: var(--font-latin); font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--text-primary); letter-spacing: 0.03em; }
.orders-card__title { font-family: var(--font-cjk-display); font-size: var(--fs-body); font-weight: var(--fw-bold); color: var(--text-primary); min-width: 0; }
.orders-card__date { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); white-space: nowrap; flex: none; }
.orders-card__meta { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }

.orders-card__thumbs { display: flex; align-items: center; gap: var(--space-2); }
.orders-card__thumb { width: 44px; height: 44px; flex: none; border-radius: var(--radius-sm); overflow: hidden; background: var(--bg-tertiary); }
.orders-card__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.orders-card__count { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); margin-left: var(--space-1); }

.orders-card__foot { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); margin-top: var(--space-1); }
.orders-card__total { font-family: var(--font-latin); font-size: var(--fs-body); font-weight: var(--fw-bold); color: var(--text-primary); font-variant-numeric: tabular-nums; }
.orders-card__sim { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* ticket card — QR left, details right */
.orders-card--ticket { flex-direction: row; align-items: stretch; gap: var(--space-4); }
.orders-card__qr { flex: none; }
.orders-card__main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); justify-content: space-between; }
.ticket-qr { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); padding: var(--space-2); background: var(--neutral-white); border-radius: var(--radius-md); }
.ticket-qr__svg { width: 84px; height: 84px; display: block; image-rendering: pixelated; }
.ticket-qr__svg rect { fill: var(--neutral-900); }
.ticket-qr__code { font-family: var(--font-latin); font-size: 9px; color: var(--neutral-900); letter-spacing: 0.03em; }

.orders-empty { grid-column: 1 / -1; padding-block: var(--space-16); gap: var(--space-4); }

/* ════ 0729 全站訂單整合（Dolly 拍板：列表式共用基本欄＋型別詳情往下展開）════ */

/* 型別徽章（共用：商城/盲盒/共創） */
.orders-card__type {
  display: inline-block;
  padding: 2px 8px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-pill);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--text-secondary);
}

/* ── 訂單列表（基本欄：類型｜項目｜訂單內容｜付款方式｜狀態｜日期）── */
.olist {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-secondary);
  overflow: hidden;
}
.olist__head,
.olist__main {
  display: grid;
  /* 類型｜項目｜訂單編號｜訂單內容｜付款方式｜狀態｜日期｜箭頭 */
  /* 狀態欄 108：要裝得下最寬的「⚠ 待完成付款」徽章（實測 102px）——88 會溢出、
     吃掉與日期的間距（0806 Dolly 報 1440 下貼死） */
  grid-template-columns: 64px 1.7fr 140px 1.8fr 140px 108px 88px 20px;   /* 0729：內容欄加寬、付款縮成定寬 140 */
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  padding: var(--space-4) var(--space-5);
  text-align: left;
}
.olist__head {
  padding-block: var(--space-3);
  /* 0729 Dolly：表頭用填色帶定義、線退到最淡——原 border-default 太突兀 */
  background: var(--bg-tertiary);
  border-bottom: 1px solid var(--border-subtle);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.olist__row { border-bottom: 1px solid var(--border-subtle); }
.olist__row:last-child { border-bottom: none; }
/* 0731 Dolly：整列展開鈕滿版寬，全域橘 focus 環只露出上下兩條線、讀起來像跑版
   ——本元件族豁免（同 icon 狀態鈕族慣例，全域環不動） */
.olist__main:focus-visible { outline: none; }
.olist__main {
  border: none; background: none; cursor: pointer; color: inherit;
  transition: background 160ms ease;
}
.olist__main:hover { background: rgba(255, 255, 255, 0.03); }

.olist__cell { min-width: 0; }
/* 資料列的欄樣式一律 scope 在 .olist__main 底下——表頭同名 cell 才不會吃到
   （0729 修：表頭「訂單內容」被列樣式放大） */
.olist__cell--item { display: flex; align-items: center; gap: var(--space-3); }
.olist__title { min-width: 0; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-medium); color: var(--text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* 商品圖／型別佔位 icon（0729 Dolly：一律 2:3 直式、直角） */
.olist__thumb {
  width: 40px; height: 60px; flex: none;
  display: grid; place-items: center;
  border-radius: 0;
  background: var(--bg-tertiary);
  overflow: hidden;
}
.olist__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.olist__thumb .olist__thumb-ic { width: 22px; height: 22px; object-fit: contain; opacity: 0.75; }
.olist__main .olist__cell--id { font-family: var(--font-latin); font-size: var(--fs-caption); color: var(--text-tertiary); letter-spacing: 0.03em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.olist__main .olist__cell--content { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.olist__cell--pay { display: flex; flex-direction: column; gap: 2px; }
.olist__paym { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); white-space: nowrap; }
.olist__amount { font-family: var(--font-latin); font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--text-primary); font-variant-numeric: tabular-nums; white-space: nowrap; }
.olist__cell--status { justify-self: start; }
.olist__main .olist__cell--date { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); white-space: nowrap; }

/* 展開箭頭 */
.olist__chev { width: 14px; height: 14px; flex: none; justify-self: end; background: currentColor; color: var(--text-tertiary);
  -webkit-mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
          mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
  transform: rotate(90deg); transition: transform 160ms var(--ease-out); }
.olist__head .olist__chev { visibility: hidden; }
.olist__row.is-open .olist__chev { transform: rotate(-90deg); }

/* 無詳情的列（如爆米花儲值）：不出箭頭、不當按鈕 */
.olist__row--static .olist__chev { visibility: hidden; }
.olist__row--static .olist__main { cursor: default; }
.olist__row--static .olist__main:hover { background: none; }

/* 爆米花 tab 次選單（儲值訂單／交易紀錄）：樣式沿用 .shop-filter，這裡只補間距 */
.orders-popcorn-tabs { margin-bottom: var(--space-8); }   /* 0729：20→32，跟表格拉開 */

/* 展開的型別詳情（頂部留 12px——列 hover 底色會塗滿整列含內距，沒這段會跟面板貼死） */
/* 0731 Dolly：四邊一致 20px（原上 12＋首個子項 order-tracker 自帶 margin-top 20
   疊成上 32、與左右下 20 不齊）；tracker 的 margin 在本範圍歸零，checkout 頁不動 */
.olist__detail { padding: var(--space-5); display: flex; flex-direction: column; gap: var(--space-4); }
.olist__detail .order-tracker { margin-top: 0; }
.olist__detail .pickup-qr-list { margin: 0; }   /* 71 檔結帳脈絡自帶 14px 下邊距，這裡歸零 */
.olist__detail[hidden] { display: none; }   /* 元件 display:flex 會蓋 [hidden]，明寫 */
.olist__detail-actions { display: flex; gap: var(--space-2); }

/* 展開區塊統一規格（0729 Dolly 定版）：淡填色（白 3%）＋外框線白 4%
   （=--border-subtle，同卡片邊框 token）＋同圓角。
   蓋掉 pickup-qr（71 檔）與 oc-detail__panel 的原樣式。 */
.olist__detail .pickup-qr,
.olist__detail .olist__subitems,
.olist__detail .oc-detail__panel {
  background: rgba(255, 255, 255, 0.03);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
}
/* 0731 Dolly：內距也統一——取貨卡原 12×14（71 檔）、共創卡原 16（space-4），
   兩者在訂單展開區一律「上下 16、左右 20」（全 20 上下顯厚，Dolly 二改收斂） */
.olist__detail .pickup-qr,
.olist__detail .oc-detail__panel {
  padding: var(--space-4) var(--space-5);
}
.olist__detail .olist__subitems { padding: var(--space-2) var(--space-4); }

/* 盲盒展開：抽到的票券/影評清單（內容照 1.0） */
.olist__subitems { display: flex; flex-direction: column; }
.olist__subitem {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.olist__subitem:last-child { border-bottom: none; }
.olist__subitem-title { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-primary); }
.olist__subitem-kind { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); white-space: nowrap; }

/* 外顯告警條（共創「待完成付款」、競標「得標待付款」）：不收在展開詳情裡——
   列渲染完就看得到；狀態 tag 各自照型別語意，條本身一律 warning。
   ⚠️ 0806 改色：原本寫 --yellow-500，但那是 --brand-500 的別名 → 實際渲染成品牌橘,
   與註解說的「黃」不符。DS「顏色與收益標籤」定案＝狀態色只有三種、
   **待辦提醒（需要你做點什麼的提醒）＝--warning-500 真黃 #fed200**，故改吃該 token。
   底色/框線用 color-mix 調透明度（此處沒有 --warning-rgb，也不寫死 rgba）。 */
.olist__alert {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-5);
  margin: 0 var(--space-5) var(--space-4);
  padding: var(--space-3) var(--space-5);
  background:
    linear-gradient(color-mix(in srgb, var(--warning-500) 8%, transparent),
                    color-mix(in srgb, var(--warning-500) 8%, transparent)),
    rgba(0, 0, 0, 0.4);
  border: 1px solid color-mix(in srgb, var(--warning-500) 30%, transparent);
  border-radius: var(--radius-lg);
}
.olist__alert-body { min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.olist__alert-title {
  margin: 0;
  display: flex; align-items: center; gap: 6px;
  font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-bold);
  color: var(--warning-500);
}
.olist__alert-desc { margin: 0; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.olist__alert-link {
  flex: none;
  padding: 0; border: none; background: none; cursor: pointer;
  font-family: var(--font-cjk-display); font-size: var(--fs-body-sm); font-weight: var(--fw-bold);
  color: var(--warning-500);
  white-space: nowrap;
}
/* warning 沒有 700/400 階（token 註記：深黃會變濁），hover 往白提亮 */
.olist__alert-link:hover { color: color-mix(in srgb, var(--warning-500) 80%, #fff); }

/* 窄幅：先收「訂單內容」欄，再收付款＋編號欄（商城家族手機版另案） */
@media (max-width: 1279px) {
  .olist__head, .olist__main { grid-template-columns: 64px 1fr 140px 140px 108px 88px 20px; }
  .olist__cell--content { display: none; }
}
@media (max-width: 899px) {
  .olist__head, .olist__main { grid-template-columns: 64px 1.6fr 108px 88px 20px; }
  .olist__cell--pay, .olist__cell--id { display: none; }
}

/* ── 禮物中心欄位變體（0806 Dolly：gift-center.html＝1.0 GIFT CENTER 換 2.0 皮，
      基底沿用本檔 olist）。欄位：類型｜項目(＋來自/送給)｜時間｜狀態｜動作；
      禮物列全靜態不展開（olist__main 是 <div>，動作欄放真按鈕）。 ── */
.olist--gift .olist__head,
.olist--gift .olist__main { grid-template-columns: 64px 1.5fr 0.9fr 200px 108px 120px; }   /* 0806：來自/送給 獨立一欄 */
/* ⚠️ 欄樣式必須 scope 在 .olist__main 底下——表頭同名 cell 才不會吃到
   （0729 訂單頁實錘過；0806 這裡又踩一次：表頭「時間」被放大）。 */
.olist--gift .olist__main .olist__cell--who,
.olist--gift .olist__main .olist__cell--time {
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-secondary);
}
.olist--gift .olist__main .olist__cell--time { font-variant-numeric: tabular-nums; }
@media (max-width: 899px) {
  .olist--gift .olist__head,
  .olist--gift .olist__main { grid-template-columns: 64px 1.4fr 0.8fr 108px 96px; }
  .olist--gift .olist__cell--time { display: none; }
}

/* ── 取貨 QR（0729 Dolly：文字左、QR 靠右；點擊放大）── */
.pickup-qr__meta { flex: 1; min-width: 0; }
.pickup-qr__zoom {
  flex: none;
  width: 72px; height: 72px;
  padding: 5px;
  border: none;
  border-radius: var(--radius-md);
  background: var(--neutral-white);
  display: block;
}
.pickup-qr__zoom img { width: 100%; height: 100%; display: block; }
button.pickup-qr__zoom { cursor: zoom-in; }

/* QR 放大 popup */
.orders-qr-zoom .ds-modal__panel {
  display: flex; flex-direction: column; align-items: center; gap: var(--space-4);
  padding: var(--space-6);
  text-align: center;
}
.orders-qr-zoom__head { display: flex; flex-direction: column; gap: var(--space-1); }
.orders-qr-zoom__title { margin: 0; font-family: var(--font-cjk-display); font-size: var(--fs-body); font-weight: var(--fw-bold); color: var(--text-primary); }
/* 品名第二排：小灰字（0729 Dolly） */
.orders-qr-zoom__name { margin: 0; font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.orders-qr-zoom__name[hidden] { display: none; }
.orders-qr-zoom__img {
  width: min(320px, 70vw);
  aspect-ratio: 1;
  padding: var(--space-4);
  background: var(--neutral-white);
  border-radius: var(--radius-lg);
}
.orders-qr-zoom__img img { width: 100%; height: 100%; display: block; }
.orders-qr-zoom__dl { width: 100%; }

/* 共創展開詳情（計畫資訊/付款資訊） */
.oc-detail { display: grid; grid-template-columns: 1fr; gap: var(--space-3); }
@media (min-width: 1024px) { .oc-detail { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.oc-detail__panel {
  padding: var(--space-4);
  background: var(--bg-tertiary);
  border-radius: var(--radius-lg);
  display: flex; flex-direction: column; gap: var(--space-2);
}
.oc-detail__heading { margin: 0 0 var(--space-1); font-family: var(--font-cjk-display); font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--text-primary); }
.oc-detail__row { display: flex; align-items: baseline; justify-content: space-between; gap: var(--space-3); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); }
.oc-detail__row span:first-child { color: var(--text-tertiary); flex: none; }
.oc-detail__row span:last-child { color: var(--text-primary); text-align: right; font-variant-numeric: tabular-nums; }

/* 爆米花交易流水（儲值＋消費整條；單欄清單，不進卡片格線） */
.pcx {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-secondary);
  overflow: hidden;
}
.pcx__row {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--border-subtle);
}
.pcx__row:last-child { border-bottom: none; }
.pcx__main { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.pcx__amount { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-medium); font-variant-numeric: tabular-nums; }
.pcx__amount--in { color: var(--success-500); }
.pcx__amount--out { color: var(--error-500); }
.pcx__desc { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.pcx__date { font-family: var(--font-latin); font-size: var(--fs-caption); color: var(--text-tertiary); white-space: nowrap; flex: none; }

/* ════════════════════════════════════════════════════════════════
   ds-zoom.css — fullscreen product-image viewer (window.DSZoom).
   Farfetch-parity INTERACTION on our DARK chrome (a white field would
   seam against #0a0a0a). Pinch / drag-pan zoom, swipe carousel, progress
   scrubber. The gesture surface is .ds-zoom__viewport (touch-action:none
   — JS owns every touch). Scroll-lock is handled globally by ds.js via the
   body.ds-zoom-open class (iOS-safe position:fixed) — not here.

   PORT NOTE (2026-07-27, eShop step3): step1 ported 55–69 but missed this
   file (it wasn't in the reencoding table) — shop-item.html's gallery hero
   opens via window.DSZoom (see assets/shop-detail-render.js, the engine is
   defined inline in that file, not a separate JS). Numbered 70 (next free
   slot after step1's 55–69 run) and added to css/main.css + rebuilt into
   components.css. Byte-identical to PROTO except the z-index fallback
   below (f2e never adopted PROTO's --z-* token system — see the same fix
   in 55-ds-drawer.css / 56-ds-toast.css / 61-shop-buybar.css).
   ════════════════════════════════════════════════════════════════ */
.ds-zoom {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal, 1200);
  visibility: hidden;
  pointer-events: none;
  background: rgba(10, 10, 10, 0.94);          /* dark field — no seam vs the product */
  opacity: 0;
  transition: opacity 220ms var(--ease-emphasized);              /* exit — quick */
}
.ds-zoom[aria-hidden="false"] { visibility: visible; pointer-events: auto; opacity: 1; transition: opacity 320ms var(--ease-settle); }

/* ── gesture surface + horizontal slide track ── */
.ds-zoom__viewport {
  position: absolute;
  inset: 0;
  overflow: hidden;
  touch-action: none;                          /* JS owns pan / pinch / swipe */
  cursor: zoom-in;
}
.ds-zoom__track {
  display: flex;
  height: 100%;
  will-change: transform;
}
.ds-zoom__slide {
  flex: 0 0 100%;
  height: 100%;
  display: grid;
  place-items: center;
  padding: clamp(16px, 4vw, 56px);
  box-sizing: border-box;
}
.ds-zoom__img {
  /* mock parity: cap so a portrait/square product image fits the viewport height
     AND leaves ~80px each side for the hugging glass arrows (see .lb__img). */
  max-width: calc(100vw - 160px);
  max-height: 88vh;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  transform: translate3d(0, 0, 0) scale(1);
  outline: 1px solid rgba(255, 255, 255, 0.08); /* a defined edge vs the dark field */
}
/* touch pages by swipe (arrows hidden) → let the image use more width */
@media (hover: none) { .ds-zoom__img { max-width: 94vw; } }
/* promote to its own compositor layer ONLY while the viewer is open */
.ds-zoom[aria-hidden="false"] .ds-zoom__img { will-change: transform; }

/* ── chrome (glass, on our tokens) ── */
.ds-zoom__close,
.ds-zoom__nav {
  position: absolute;
  display: grid;
  place-items: center;
  padding: 0;
  border: var(--glass-border);
  border-radius: var(--radius-pill);
  background: var(--glass-fill);
  -webkit-backdrop-filter: var(--glass-backdrop);
          backdrop-filter: var(--glass-backdrop);
  color: var(--text-primary);
  cursor: pointer;
  z-index: 2;
}
.ds-zoom__close { top: clamp(12px, 3vw, 28px); right: clamp(12px, 3vw, 28px); width: 44px; height: 44px; }
.ds-zoom__close svg { width: 20px; height: 20px; }

.ds-zoom__nav {
  top: 50%;
  transform: translateY(-50%);
  width: 48px;
  height: 48px;
  /* the glass is the .liquid-glass ::before — the button itself must be clear
     (an opaque button bg would paint over the lens). JS positions left/right so
     the arrows HUG the image, not the screen edge. */
  border: 0;
  background: transparent;
  -webkit-backdrop-filter: none;
          backdrop-filter: none;
}
.ds-zoom__nav svg { width: 22px; height: 22px; position: relative; z-index: 1; }
.ds-zoom__nav--prev { left: clamp(8px, 2vw, 24px); }   /* fallback until JS hugs the image */
.ds-zoom__nav--next { right: clamp(8px, 2vw, 24px); }
.ds-zoom__nav:disabled { opacity: 0; pointer-events: none; }   /* fade out at the ends */
.ds-zoom--single .ds-zoom__nav { display: none; }
.ds-zoom.is-zoomed .ds-zoom__nav { opacity: 0; pointer-events: none; }   /* mock: hide arrows while zoomed-in */
/* touch devices page by swipe — hide the arrows there (Farfetch parity) */
@media (hover: none) { .ds-zoom__nav { display: none; } }

/* ── progress scrubber (Farfetch signature) — a position thumb on a thin track ── */
.ds-zoom__scrub {
  position: absolute;
  left: 50%;
  bottom: clamp(16px, 4vw, 32px);
  transform: translateX(-50%);
  width: min(60vw, 360px);
  height: 3px;
  border-radius: 2px;
  background: var(--white-alpha-16, rgba(255, 255, 255, 0.16));
  overflow: hidden;
  z-index: 2;
}
.ds-zoom--single .ds-zoom__scrub { display: none; }
.ds-zoom__scrub-thumb {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 25%;
  border-radius: 2px;
  background: var(--text-primary, #fff);
  transition: left 280ms var(--ease-out), width 280ms var(--ease-out);
}

.ds-zoom__close:focus-visible,
.ds-zoom__nav:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .ds-zoom { transition: none; }
  .ds-zoom__scrub-thumb { transition: none; }
}

/* ════════════════════════════════════════════════════════════════
   71-shop-checkout-forms.css — eShop port, step 4.

   Fills the ".cc-pay / .cc-auth / .cc-login2 / .cc-field" style gap flagged
   by step1 (§4) and step3 (§5): assets/auth.js, assets/mock-pay.js and
   assets/checkout.js all render markup built around these classes (the
   login-gate overlay, the mock card-payment form, and the checkout form
   fields), but their DEFINITIONS live in PROTO's cocreate-detail.css — a
   613-line file for the co-creation/crowdfunding page that was intentionally
   NOT ported wholesale (it carries a large amount of cocreate-only UI this
   build doesn't use: cc-overlay, cc-pv gallery, cc-sh share sheet, etc).

   ONLY the blocks actually referenced by the shop checkout/auth/pay flow are
   extracted below, verbatim (values unchanged — same tokens, same rules) from
   PROTO's css/components/cocreate-detail.css lines ~331-496. Verified against
   both the source file AND the live reference build's compiled bundle
   (https://ztor-eshop-fe.vercel.app/components.css) to confirm no drift.

   Two extra blocks (marked below) are NOT in cocreate-detail.css at all —
   .checkout-pickup / .pickup-qr (used by checkout.js's confirm screen +
   my-orders.js's order cards) only exist in the LIVE site's compiled CSS as a
   hand-appended "eShop scope overrides" patch at the very end of its
   components.css (hardcoded hex/rgba, not tokens — a genuine authoring
   shortcut in the source itself, not a porting miss). Ported here verbatim
   from that live patch so the pickup-QR rows in the order-confirmation /
   my-orders cards aren't raw unstyled text. `.checkout-confirm` (base) and
   `.order-tracker--mini` / `.orders-card--bid` / `.orders-card--rental`
   were checked against the SAME live bundle and are genuinely undefined
   there too (the live reference itself falls back to the base
   `.cc-success` / `.order-tracker` / `.orders-card` rules for those) — so
   nothing was invented for them here; this port matches the live behaviour
   exactly, gaps included.
   ════════════════════════════════════════════════════════════════ */

/* ── mock payment form (Stripe-style) + saved-card fast path ──
   Consumed by assets/mock-pay.js (window.ZtorPay) and assets/checkout.js
   (order summary rows reuse .cc-pay__summary/__sumrow inside the hub). */
.cc-pay, .cc-login { display: flex; flex-direction: column; gap: var(--space-4); }
.cc-pay__summary { background: var(--bg-card); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }
.cc-pay__sumrow { display: flex; justify-content: space-between; gap: var(--space-3); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.cc-pay__sumrow--total { padding-top: var(--space-2); border-top: 1px solid var(--border-subtle); color: var(--text-primary); font-weight: var(--fw-bold); }
.cc-pay__sumrow--total span:last-child { font-family: var(--font-latin); color: var(--yellow-500); font-variant-numeric: tabular-nums; }
.cc-pay__express { display: flex; gap: var(--space-2); }
.cc-pay__express-btn { flex: 1; height: 44px; border-radius: var(--radius-md); border: 1px solid var(--border-default); background: var(--white-alpha-8); color: var(--text-primary); font-family: var(--font-latin); font-weight: var(--fw-bold); cursor: pointer; transition: border-color 150ms var(--ease-out); }
.cc-pay__express-btn:hover { border-color: var(--border-strong); }
.cc-pay__or { display: flex; align-items: center; gap: var(--space-3); color: var(--text-tertiary); font-family: var(--font-cjk-text); font-size: var(--fs-caption); }
.cc-pay__or::before, .cc-pay__or::after { content: ""; flex: 1; height: 1px; background: var(--border-subtle); }
.cc-pay__form { display: flex; flex-direction: column; gap: var(--space-3); }
.cc-pay__grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-3); }
.cc-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cc-field__label { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.cc-field__input { width: 100%; height: 44px; padding: 0 var(--space-3); border-radius: var(--radius-md); border: 1px solid var(--border-default); background: var(--bg-tertiary); color: var(--text-primary); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); }
.cc-field__input:focus { outline: none; border-color: var(--yellow-500); box-shadow: 0 0 0 3px rgba(255, 163, 63, 0.18); }
/* iOS Safari auto-zooms (and breaks the layout) when a focused input is < 16px.
   Bump to 16px on touch pointers only — desktop keeps the tighter rhythm. */
@media (pointer: coarse) { .cc-field__input { font-size: 16px; } }
.cc-pay__save { display: flex; align-items: center; gap: var(--space-2); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.cc-pay__save input { accent-color: var(--yellow-500); }
/* saved-card fast path (dev-state hasCard) — used by ZtorPay */
.cc-pay__saved { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); padding: var(--space-4); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); background: var(--bg-secondary); }
.cc-pay__saved-card { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.cc-pay__saved-brand { font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
.cc-pay__saved-num { font-family: var(--font-latin, inherit); font-size: var(--fs-body); color: var(--text-primary); font-variant-numeric: tabular-nums; letter-spacing: 0.04em; }
.cc-pay__saved-new { border: 0; background: none; padding: 0; color: var(--yellow-500); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-medium); cursor: pointer; white-space: nowrap; }
.cc-pay__saved-new:hover { color: var(--yellow-400, #ffb866); }
.cc-pay__secure { display: flex; align-items: center; gap: var(--space-2); font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); line-height: var(--lh-body-sm); }
.cc-pay__lock { width: 8px; height: 8px; border-radius: var(--radius-pill); background: var(--score-ink-high); flex-shrink: 0; }
.cc-pay__err { margin: 0; color: var(--error-500); font-family: var(--font-cjk-text); font-size: var(--fs-caption); }
.cc-pay__err[hidden] { display: none; }

/* ── login gate (identifier-first: email/phone → password OR register) ──
   Consumed by assets/auth.js (window.ZtorAuth, the [data-cc-auth] modal). */
.cc-login2 { display: flex; flex-direction: column; gap: var(--space-3); padding: var(--space-2) 0; }
.cc-login2__title { margin: 0 0 var(--space-2); text-align: center; font-family: var(--font-cjk-display); font-size: var(--fs-h4); font-weight: var(--fw-bold); color: var(--text-primary); }
/* Subtitle — the one line that tells a logged-out visitor WHY to sign in. */
.cc-login2__sub { margin: 0 0 var(--space-4); text-align: center; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); line-height: var(--lh-body-sm); color: var(--text-secondary); }
.cc-login2__method {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  height: 56px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-default);
  background: var(--bg-card);
  color: var(--text-primary);
  font-family: var(--font-cjk-text); font-size: var(--fs-body); font-weight: var(--fw-medium);
  cursor: pointer;
  transition: border-color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.cc-login2__method:hover { border-color: var(--border-strong); background: var(--bg-tertiary); }
.cc-login2__method:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.cc-login2__method svg { position: absolute; left: var(--space-5); width: 20px; height: 20px; }
.cc-login2__socials { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-2); }
.cc-login2__social {
  height: 56px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-default);
  background: var(--bg-card);
  color: var(--text-primary);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: border-color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.cc-login2__social:hover { border-color: var(--border-strong); background: var(--bg-tertiary); }
.cc-login2__social:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.cc-login2__social svg { width: 22px; height: 22px; }
.cc-login2__glyph { font-family: var(--font-latin); font-weight: var(--fw-bold); font-size: 20px; line-height: 1; }
.cc-login2__glyph--line { font-size: 12px; letter-spacing: 0.02em; }
.cc-login2__signup { width: 100%; margin-top: var(--space-1); }
.cc-login2__redeem { margin: var(--space-1) 0 0; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.cc-login2__redeem a { color: var(--yellow-500); text-decoration: underline; }
.cc-login2__legal { margin: var(--space-2) 0 0; text-align: center; font-family: var(--font-cjk-text); font-size: var(--fs-caption); line-height: var(--lh-body-sm); color: var(--text-tertiary); }
/* identifier-first form + full-width SSO rows */
.cc-login2__form { display: flex; flex-direction: column; gap: var(--space-3); }
.cc-login2__continue { width: 100%; }
.cc-login2__sso {
  position: relative;
  display: flex; align-items: center; justify-content: center;
  height: 52px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-default);
  background: var(--bg-card);
  color: var(--text-primary);
  font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-medium);
  cursor: pointer;
  transition: border-color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.cc-login2__sso:hover { border-color: var(--border-strong); background: var(--bg-tertiary); }
.cc-login2__sso:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.cc-login2__sso svg { position: absolute; left: var(--space-5); width: 20px; height: 20px; }
.cc-login2__sso .cc-login2__glyph { position: absolute; left: var(--space-5); width: 20px; text-align: center; font-family: var(--font-latin); font-weight: var(--fw-bold); font-size: 18px; line-height: 1; }
.cc-login2__sso .cc-login2__glyph--line { font-size: 11px; }
/* identifier-first sub-steps: back link, identifier chip, forgot link, consent */
.cc-login2__back { align-self: flex-start; padding: 0; background: none; border: none; cursor: pointer; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); }
.cc-login2__back::before { content: "‹ "; }
.cc-login2__back:hover { color: var(--text-primary); }
.cc-login2__ident { margin: 0; padding: var(--space-2) var(--space-3); border: 1px solid var(--border-subtle); border-radius: var(--radius-md); background: var(--bg-tertiary); font-family: var(--font-latin); font-size: var(--fs-body-sm); color: var(--text-secondary); word-break: break-all; }
.cc-login2__link { align-self: center; padding: 0; background: none; border: none; cursor: pointer; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--yellow-500); text-decoration: underline; }
.cc-check { display: flex; align-items: flex-start; gap: var(--space-2); cursor: pointer; font-family: var(--font-cjk-text); font-size: var(--fs-caption); line-height: var(--lh-body-sm); color: var(--text-secondary); }
.cc-check input { margin-top: 2px; flex-shrink: 0; accent-color: var(--yellow-500); }

/* ── auth overlay: desktop = spacious box sliding in from top-right;
      mobile = full-width sheet sliding down from the top ──
   Appended directly to <body> by assets/auth.js's ccAuth() — position:fixed
   is its own containing block, so it is never nested inside a
   backdrop-filter ancestor (the house rule this codebase is careful about). */
.cc-auth {
  position: fixed; inset: 0;
  z-index: var(--z-modal, 1200);   /* PORT FIX (step4): --z-modal undefined in f2e tokens.css;
                                       fallback matches PROTO's --z-modal:1200 (same tier as
                                       ds-toast/ds-zoom — see 56-ds-toast.css / 70-ds-zoom.css). */
  visibility: hidden; pointer-events: none;
}
.cc-auth.is-open { visibility: visible; pointer-events: auto; }
.cc-auth__scrim {
  position: absolute; inset: 0;
  background: rgba(0, 0, 0, 0.5);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  opacity: 0; transition: opacity var(--dur-nav-hide, 250ms) var(--ease-out);
}
.cc-auth.is-open .cc-auth__scrim { opacity: 1; }
.cc-auth__panel {
  position: absolute;
  top: calc(var(--header-height, 64px) + var(--space-3));
  right: var(--space-5);
  width: 408px;
  max-width: calc(100vw - 2 * var(--space-5));
  max-height: calc(100dvh - var(--header-height, 64px) - var(--space-6));
  overflow-y: auto;
  padding: var(--space-6);
  background: var(--bg-secondary);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-xl);
  box-shadow: var(--glass-shadow);
  transform-origin: top right;
  transform: translateY(-10px) scale(0.97);
  opacity: 0;
  transition: transform var(--dur-nav-show, 320ms) var(--ease-out), opacity var(--dur-nav-show, 320ms) var(--ease-out);
}
.cc-auth.is-open .cc-auth__panel { transform: none; opacity: 1; }
.cc-auth__close {
  position: absolute; top: var(--space-3); right: var(--space-3);
  width: 36px; height: 36px; border: none; border-radius: var(--radius-pill);
  background: var(--white-alpha-8); color: var(--text-secondary);
  font-size: 22px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background 150ms var(--ease-out), color 150ms var(--ease-out);
}
.cc-auth__close:hover { background: var(--white-alpha-16); color: var(--text-primary); }
.cc-auth__close:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }

@media (max-width: 767.98px) {
  .cc-auth__panel {
    top: 0; left: 0; right: 0; width: 100%; max-width: none;
    max-height: 92dvh;
    padding: calc(var(--header-height, 64px) + var(--space-4)) var(--space-4) var(--space-6);
    border-left: none; border-right: none; border-top: none;
    border-radius: 0 0 var(--radius-xl) var(--radius-xl);
    transform-origin: top center;
    transform: translateY(-100%);
  }
  .cc-auth.is-open .cc-auth__panel { transform: none; }
}
@media (prefers-reduced-motion: reduce) {
  .cc-auth__scrim, .cc-auth__panel { transition: none; }
}

/* ── generic success state (order confirm reuses this + .checkout-confirm) ──
   Consumed by assets/checkout.js's renderConfirm(). */
.cc-success { display: flex; flex-direction: column; align-items: center; text-align: center; gap: var(--space-3); padding: var(--space-6) 0 var(--space-2); }
/* 0731 Dolly：盤面對齊 L 版示範卡——實心綠盤＋深色勾（原：淡綠 16% 底＋綠勾）。
   勾的顏色走 host currentColor（57 檔規範）。 */
.cc-success__check { width: 56px; height: 56px; border-radius: var(--radius-pill); display: inline-flex; align-items: center; justify-content: center; background: var(--success-500); color: var(--neutral-900); font-size: 28px; font-weight: var(--fw-bold); }
.cc-success__title { margin: 0; font-family: var(--font-cjk-display); font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--text-primary); }
.cc-success__msg { margin: 0; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); line-height: 1.7; color: var(--text-secondary); max-width: 34em; }
.cc-success .cc-stepview__foot { width: 100%; margin-top: var(--space-2); }

/* ── pickup / QR-collection blocks (NOT in cocreate-detail.css — see file
      header note: ported verbatim from the LIVE reference bundle's tail
      "eShop scope overrides" patch, values kept as hardcoded hex/rgba to
      match that live source exactly rather than re-tokenising it here) ──
   .checkout-pickup* : checkout.js renderHub()'s "取貨方式" pickup-location box.
   .pickup-qr*       : checkout.js renderConfirm() + my-orders.js orderCard()'s
                       per-item pickup-QR rows. */
/* 0805 Dolly：「下方間距特別高」。實測 上13／下25，幾乎是兩倍。
   根因不是 padding 寫錯，是下面 .checkout-pickup__note 這個 <p> 只寫了 margin-top，
   **瀏覽器預設的 margin-bottom:1em（12px）整條跑出來**、疊在容器 padding 上
   （memory「<p> UA margin」；同檔的 .pickup-qr__line／__note 都有寫 margin:0，
   只有這一條漏了）。順手把 padding 四邊拉齊成 14。 */
.checkout-pickup { margin-top: 10px; padding: 14px; border: 1px solid rgba(255, 255, 255, .12); border-radius: var(--radius-md); background: rgba(255, 255, 255, .03); }
.checkout-pickup__row { display: flex; justify-content: space-between; gap: 12px; font-size: 13px; line-height: 1.7; }
.checkout-pickup__k { color: rgba(255, 255, 255, .55); flex: none; }
.checkout-pickup__v { color: rgba(255, 255, 255, .9); text-align: right; }
/* margin 四邊寫定：只寫 margin-top 會留著 UA 的 margin-bottom:1em（見上方註解） */
.checkout-pickup__note { margin: 8px 0 0; font-size: 12px; line-height: 1.6; color: rgba(255, 255, 255, .5); }
.pickup-qr-list { display: flex; flex-direction: column; gap: 10px; margin: 14px 0; }
.pickup-qr-list .pickup-qr { margin: 0; }
/* 0731 Dolly：內距統一 12×14→上下 16、左右 20（--space-4/--space-5），與我的訂單
   展開區（69 檔 olist 規則）同值——結帳完成頁與訂單頁的取貨卡才一致 */
.pickup-qr { display: flex; gap: 14px; align-items: center; text-align: left; margin: 14px 0; padding: var(--space-4) var(--space-5); border: 1px solid rgba(255, 255, 255, .12); border-radius: var(--radius-md); background: rgba(255, 255, 255, .03); }
.pickup-qr__svg, .pickup-qr .ticket-qr__svg { width: 88px; height: 88px; flex: none; background: #fff; border-radius: 8px; padding: 6px; fill: #111; }
.pickup-qr--sm .pickup-qr__svg, .pickup-qr--sm .ticket-qr__svg { width: 68px; height: 68px; }
/* 0731 Dolly：margin 四邊寫定——這批是 <p>，UA 預設上下邊距（~1em）讓卡片
   字到框 31/28px、比共創卡（div/span，16+框=17）厚一圈，padding 相同也不齊。
   行距改由 meta 的 flex gap 供給（gap 不會外溢到頭尾，字到框維持 17）。 */
.pickup-qr__meta { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.pickup-qr__title { font-weight: 700; font-size: 13.5px; margin: 0; }
.pickup-qr__line { font-size: 12px; color: rgba(255, 255, 255, .72); line-height: 1.55; margin: 0; }
.pickup-qr__note { font-size: 11px; color: rgba(255, 255, 255, .5); margin: 0; line-height: 1.5; }


/* ── eShop port batch 2 (2026-07-27) — creator/events/auction/popcorn
   branch pages. PROTO components not covered by the step-1 foundation
   list (shop.html/shop-item.html/my-orders.html only needed 55-71); these
   two are needed by shop-creators.html/shop-auction.html specifically.
   See scratchpad/eshop-port-batch2.md for the full port report. */
/* eShop port — batch 2 (2026-07-27). PROTO source: css/components/auction-card.css
   (unnumbered in PROTO). Renumbered 72 here — f2e's own numbering already has
   65-71 taken by the step-1/step-2 eShop port block; no collision risk.
   Needed by: shop-auction.html (batch 2) + creator storefront pages (later
   batch) — anything rendering .shop-card--auction via shop-render.js's
   auctionCardHtml(). Zero url()/token gaps — grep confirmed every custom
   property used here (white-alpha-16, text-primary/tertiary/secondary,
   neutral-white/700, yellow-500/400/ink, fs-body and fs-body-sm, space-2,
   font-latin/cjk-text, success-500, error-500, ease-out) already resolves
   in tokens.css. Content below is byte-for-byte the PROTO file. */
/* AUCTION CARD (.shop-card--auction + .rf-auc-* + .rf-bid). [COMPONENT] */
/* ════════════════════════════════════════════════════════════════════════
   AUCTION CARD  ·  .shop-card--auction + .rf-auc-* + .rf-bid   [SHARED]
   ════════════════════════════════════════════════════════════════════════
   Rarible/Foundation timed-auction card: live countdown pill, current bid as
   the price line, bid count, leading/outbid/ending-soon state, hover 出價 CTA.
   Built on the shared .shop-card base.

   MOVED here from refine-shop.css (2026-06-16). It was keyed `body.rf-shop`,
   so it ONLY styled the shop pages — the creator 商店›拍賣 reused the markup but
   got none of the CSS (bid row collapsed). Keyed to the COMPONENT CLASS now, so
   it renders on EVERY page with .shop-card--auction: shop-auction.html, all 12
   creator pages, and anything built later. See COMPONENTS.md usage map.
   ════════════════════════════════════════════════════════════════════════ */
/* ⚠️ 底部漸層遮罩已於 2026-08-07 由 Dolly 拍板**整個移除**。
   原本是 `.shop-card--auction .shop-card__media::after`（軟性壓暗，讓倒數膠囊在
   雜亂商品照上讀得到）。Chrome hover 時它會整段消失、露出商品照白底（Safari 正常、
   CDP 截圖照不出來，因為截圖會重繪、不走合成器）。查了五輪（外溢 1px／z-index／
   拔 backdrop-filter／停 transform／改成真 DOM 元素）都沒解掉，Dolly 決定不再投入，
   直接拿掉——倒數膠囊自己有 72% 黑底＋邊框，沒有遮罩也讀得到。
   若日後要重做壓暗，別再用疊在圖片上的第二個框；請直接讓照片本身帶暗角，
   或把倒數膠囊移出圖框。相關記錄見 memory ztor_auction_bid_line。 */

/* live / status pill — bottom-left over the media */
.rf-auc-pill {
  position: absolute;
  left: 10px;
  bottom: 10px;
  z-index: 2;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 24px;
  padding: 0 10px;
  border-radius: 999px;
  font-family: var(--font-latin);
  font-size: 11px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  /* 0807：原本帶 backdrop-filter: blur(4px)，已移除（遮罩已不存在，毛玻璃在這裡
     只是多一個合成層，沒有可讀性貢獻）。底色從 66% 補到 72% 黑——遮罩砍掉之後，
     膠囊自己就是唯一的可讀性保障，要壓得住白底商品照。 */
  background: rgba(10,10,10,0.72);
  border: 1px solid var(--white-alpha-16);
  color: var(--text-primary);
  font-variant-numeric: tabular-nums;
  /* 0807 膠囊與出價鈕的交叉過場。
     這顆已經沒有 backdrop-filter 了，所以 [[feedback_backdrop_filter_opacity_flash]]
     那條「毛玻璃別碰 opacity」不再適用，可以正常淡入淡出。
     ⚠️ 位移只能往上（-4px→0，回程是往下飄進定位）。膠囊在 bottom:10px、出價鈕在
     bottom:8px，只要膠囊往下位移就會有黑底從按鈕下緣探出一截，交叉那瞬像閃一下。
     這條是「回來」的方向：延遲 90ms 讓按鈕先開始淡出，兩者交疊成 cross-dissolve。 */
  opacity: 1;
  transform: none;
  transition: opacity 220ms var(--ease-out, ease) 90ms,
              transform 220ms var(--ease-out, ease) 90ms;
}
.rf-auc-pill__tick {
  width: 6px; height: 6px; border-radius: 999px;
  background: var(--success-500);
  box-shadow: 0 0 0 0 rgba(34,197,94,0.55);
  animation: rf-ping 1.6s ease-out infinite;
}
@keyframes rf-ping {
  0% { box-shadow: 0 0 0 0 rgba(34,197,94,0.5); }
  70%,100% { box-shadow: 0 0 0 7px rgba(34,197,94,0); }
}
.rf-auc-pill--soon { color: var(--yellow-500); }
.rf-auc-pill--soon .rf-auc-pill__tick { background: var(--yellow-500); animation: none; box-shadow: none; }
.rf-auc-pill--ended { color: var(--text-tertiary); }
.rf-auc-pill--ended .rf-auc-pill__tick { background: var(--text-tertiary); animation: none; box-shadow: none; }

/* status row under the title: 目前出價 left, bid-count + state right */
.rf-auc-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: 2px;
}
.rf-auc-bid { display: flex; flex-direction: column; gap: 1px; }
.rf-auc-bid__label { font-size: 11px; letter-spacing: 0.04em; color: var(--text-tertiary); }
.rf-auc-bid__val {
  font-family: var(--font-latin);
  font-size: 16px;
  font-weight: var(--fw-bold);
  color: var(--neutral-white);
  font-variant-numeric: tabular-nums;
}
.rf-auc-meta { text-align: right; display: flex; flex-direction: column; gap: 2px; }
.rf-auc-meta__bids {
  font-family: var(--font-latin);
  font-size: 12px;
  color: var(--text-secondary);
  font-variant-numeric: tabular-nums;
}
.rf-auc-state { font-family: var(--font-cjk-text); font-size: 11px; font-weight: var(--fw-bold); }
.rf-auc-state--leading { color: var(--success-500); }
.rf-auc-state--outbid { color: var(--error-500); }

/* bid CTA — appears on hover like the storefront quick-add */
.rf-bid {
  position: absolute;
  left: 8px;
  right: 8px;
  bottom: 8px;
  z-index: 2;
  height: 38px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  border: none;
  border-radius: 10px;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-bold);
  color: var(--yellow-ink);
  background: var(--yellow-500);
  cursor: pointer;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 200ms var(--ease-out, ease), transform 200ms var(--ease-out, ease), background 140ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .shop-card--auction:hover .rf-bid { opacity: 1; transform: translateY(0); }
  /* 0807：膠囊直接收起，不滑動也不淡出。
     ⚠️ 一定要「立刻」消失、不能有延遲或位移——膠囊在 bottom:10px，出價鈕在
     bottom:8px、高 38px，兩者位置幾乎重疊。只要膠囊在按鈕淡入期間還留著，
     黑底就會從按鈕邊緣探出一截，交叉那一瞬看起來像閃一下（0807 Dolly 回報）。
     也不用 opacity 淡出＝[[feedback_backdrop_filter_opacity_flash]] 的病根。 */
  /* 去程：快一點收掉（140ms、無延遲），讓位給按鈕；往上位移不會探出按鈕下緣。 */
  .shop-card--auction:hover .rf-auc-pill {
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity 140ms var(--ease-out, ease), transform 140ms var(--ease-out, ease);
  }
  /* 保險：即使 visibility:hidden，部分 Chrome 版本仍會為 box-shadow 動畫排程
     raster 作廢。hover 期間直接停掉，反正看不見。 */
  .shop-card--auction:hover .rf-auc-pill__tick { animation-play-state: paused; }
}
/* The gavel glyph had NO sizing rule anywhere (only .rf-wish/.rf-quickadd svgs
   were sized), so an unconstrained <svg> ballooned to ~200px on every auction
   card — latent on shop-auction (hover-only), visible once reused. Size it here. */
.rf-bid svg { width: 16px; height: 16px; flex: 0 0 auto; display: block; }
.rf-bid:hover { background: var(--yellow-400); }
.rf-bid:focus-visible { opacity: 1; transform: translateY(0); outline: 2px solid var(--neutral-white); outline-offset: 2px; }
.rf-bid--ended { background: var(--neutral-700); color: var(--text-tertiary); cursor: default; }
@media (hover: none), (pointer: coarse) {
  .rf-bid { position: static; opacity: 1; transform: none; margin-top: 10px; width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .rf-bid { transition: none !important; opacity: 1; transform: none; }
  .rf-auc-pill__tick { animation: none !important; }
}

/* ─────────────────────────────────────────
   RESPONSIVE — auction card
   ───────────────────────────────────────── */

/* ── Tablet: 768–1023px ── */
@media (min-width: 768px) and (max-width: 1023.98px) {
  /* Bid value can be slightly smaller so label doesn't crowd */
  .rf-auc-bid__val {
    font-size: var(--fs-body);
  }
}

/* ── Phone: ≤767px ── */
@media (max-width: 767.98px) {
  /* Status row: allow wrapping so bid + meta don't collide */
  .rf-auc-row {
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: flex-start;
  }

  /* Bid val: step down one notch on narrow */
  .rf-auc-bid__val {
    font-size: var(--fs-body-sm);
  }

  .rf-auc-meta {
    text-align: left;
  }

  /* Pill: ensure it never overflows the card image */
  .rf-auc-pill {
    max-width: calc(100% - 20px);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }

  /* Bid CTA: already handled by (hover: none) rule above,
     but enforce full width and adequate height on phone */
  .rf-bid {
    height: 44px;
    font-size: var(--fs-body-sm);
  }
}

/* ── Small phone: ≤480px ── */
@media (max-width: 480px) {
  /* Pill text: abbreviate by letting ellipsis kick in sooner */
  .rf-auc-pill {
    max-width: calc(100% - 12px);
    font-size: 10px;
  }

  .rf-auc-bid__label {
    font-size: 10px;
  }

  .rf-auc-meta__bids,
  .rf-auc-state {
    font-size: 10px;
  }
}

/* eShop port — batch 2 (2026-07-27). PROTO source: css/components/37-creator-directory.css.
   Renumbered 73 (not 37) — f2e's OWN "37" slot is already 37-search.css, an
   unrelated existing page component; keeping PROTO's number would silently
   overwrite/duplicate that slot. This file defines .creator-grid and
   .creator-card, which shop-creators.html's #creatorGrid (rendered by
   shop-render.js's creatorCardHtml()) needs — refine-shop.css only adds the
   hover/focus states on top of this base, it never defined the base layout
   itself (checked: grep for the base selectors across css/components and
   assets/refine-shop.css before this file existed — zero hits). Zero url()
   references; custom properties used here (space scale, text-tertiary,
   bg-secondary, font-cjk-display, fs-body-sm) all resolve in tokens.css.
   Content below is byte-for-byte the PROTO file. */
/* ─────────────────────────────────────────
   CREATOR DIRECTORY — chromeless portrait cards
   ───────────────────────────────────────── */
.creator-grid {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  column-gap: var(--space-4);
  row-gap: 44px;
  margin-top: 36px;
}
@media (max-width: 1180px) { .creator-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.creator-card {
  display: block;
  text-decoration: none;
  color: inherit;
}
.creator-card__media {
  position: relative;
  /* 0804 Dolly：圖改成創作者大頭照（來源＝ds.js ZTOR_CREATOR_PROFILE，同個人頁那張），
     所以從 2:3 直式海報比例改成 1:1 方形——人臉照裁成直式會把構圖切壞。
     刻意保持方形不切圓：圓形是個人頁頭部那顆的語彙，這裡是目錄卡的陳列面。 */
  aspect-ratio: 1 / 1;
  background: var(--bg-secondary);
  overflow: hidden;
}
.creator-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.creator-card__cta { display: none; }
.creator-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding-top: var(--space-3);
}
.creator-card__name {
  font-family: var(--font-cjk-display);
  font-size: 15px;
  font-weight: var(--fw-bold);
}
.creator-card__meta {
  display: flex;
  gap: 10px;
  font-size: 12px;
  color: var(--text-tertiary);
}
.creator-card__meta .badge { font-size: 11px; }

/* ─────────────────────────────────────────
   RESPONSIVE — creator directory
   ───────────────────────────────────────── */

/* ── Tablet: 768–1023px ── */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .creator-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    column-gap: var(--space-3);
    row-gap: var(--space-10);
  }
}

/* ── Phone: ≤767px ── */
@media (max-width: 767.98px) {
  /* 2-up portrait grid */
  .creator-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    column-gap: var(--space-3);
    row-gap: var(--space-6);
    margin-top: var(--space-6);
  }

  .creator-card__body {
    padding-top: var(--space-2);
    gap: var(--space-1);
  }

  .creator-card__name {
    font-size: var(--fs-body-sm);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .creator-card__meta {
    font-size: 11px;
    gap: var(--space-2);
    flex-wrap: wrap;
  }
}

/* Container query — card in a narrow sidebar/slot (e.g. creator storefront) */
@supports (container-type: inline-size) {
  .creator-grid {
    container-type: inline-size;
  }

  @container (max-width: 480px) {
    .creator-grid {
      grid-template-columns: repeat(2, minmax(0, 1fr));
      column-gap: var(--space-2);
      row-gap: var(--space-5);
    }
  }
}


/* ── eShop port batch 3 (2026-07-27) — creator storefront pages
   (creator-<slug>.html + -events/-posts/-projects, 12 creators x 4
   page types = 48 pages). 74-75 are verbatim PROTO ports; 76 is a
   DELTA extraction (see its own file header for what was dropped and
   why). See scratchpad/eshop-port-batch3.md for the full report. */
/* ─────────────────────────────────────────
   CREATOR STOREFRONT — fan destination, one scroll
   identity header → pickup slot → product grid → events rows →
   top-fans strip → about. No anchor tabs.

   Ported verbatim from PROTO css/components/38-creator-storefront.css
   as part of eShop port batch 3 (creator-*.html, 12 creators x 4 page
   types). Renumbered 74 (74-76 = batch-3 delta on top of 55-73 from
   batches 1-2) — see scratchpad/eshop-port-batch3.md for the full
   class-usage cross-check that confirmed this file is needed as-is
   (creator-hero/creator-pickup/creator-sec/creator-about all render
   on every creator-<slug>.html page).
   ───────────────────────────────────────── */
/* ⚠️ 2026-08-04 Dolly：hero 主視覺（背景照片）移除。
   ・照片、兩道遮罩、以及「撐住照片」的那些 min-height（300／340／平板 240／手機 200、
     還有 refine-creator.css 的 360）整組一起拿掉——只砍圖不砍高度會留下一塊空的黑帶。
     hero 現在的高度＝識別區內容 ＋ 上下 32，跟個人頁的識別區同一種語彙。
   ・#creatorHeroBg 那個空層刻意留著（見 shop-render.js fillCreatorHead）＝之後要換回
     封面圖時的掛點；這裡先整層關掉，不留看不見的疊層。 */
.creator-hero {
  position: relative;
  display: flex;
  align-items: flex-end;
  overflow: hidden;
}
.creator-hero__bg { display: none; }

/* hero 裡的識別區＝個人頁頭部那組元件（.profile-head*，見 12-profile-header.css）。
   這裡只調「放在 hero 裡」才需要的差異，不重刻任何一個子元素：
   ・上下內距：個人頁是獨立區塊自己給 8/36，hero 已有自己的 padding-bottom。
   ・動作區：個人頁桌機是絕對定位在右上；這裡讓它跟著 flex 排在最右、頂端對齊。 */
.creator-hero__ident { padding: 0; width: 100%; }
.creator-hero__actions {
  position: static;
  flex: 0 0 auto;
  align-self: flex-start;
  display: flex;
  /* ⚠️ 0805 加了分享 icon 鈕（40 高）之後必須明寫 center：預設的 stretch 會把
     旁邊的「加入社群」（.btn--sm，36 高）一起撐成 40（memory「塞進 actions
     會被 flex stretch 撐成 40」，個人頁 0803 踩過同一個）。 */
  align-items: center;
  gap: var(--space-2);
}
/* 沒有背景圖之後 hero 不再需要「撐住照片」的最小高度，交還給內容（識別區＋上下 32）。 */
.creator-hero:has(.creator-hero__ident) { align-items: center; }
/* ⚠️ 只動上下內距：.creator-hero__copy 同時是 .container--wide，左右那 48px 是它跟
   下方內容切齊的依據。第一版寫成 padding:32px 0 連左右一起歸零，識別區就跟分頁列
   對不齊了（而且 hero 改成 align-items:center 後 copy 變成會縮的 flex 項，要補
   width:100% 才撐得回 container 的寬）。 */
.creator-hero:has(.creator-hero__ident) .creator-hero__copy {
  padding-block: 32px;
  width: 100%;
}
.creator-hero__copy {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-bottom: 26px;
}
.creator-hero__name {
  font-family: var(--font-cjk-display);
  font-size: 40px;
  font-weight: var(--fw-extrabold);
  line-height: 1.15;
  letter-spacing: 0.02em;
}
/* 注入版識別區的商店名回到商店 hero 的字級（0806 Dolly：放大）——
   個人頁 .profile-head__name 維持 28px，只在創作者商店 hero 內放大。 */
.creator-hero__ident .profile-head__name {
  font-size: 40px;
  font-weight: var(--fw-extrabold);
  line-height: 1.15;
  letter-spacing: 0.02em;
}
.creator-hero__tagline {
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.creator-hero__row {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: var(--space-1);
}
.creator-hero__social { display: inline-flex; gap: var(--space-2); }
.creator-hero__social a {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--glass-bg-default);
  border: 1px solid var(--glass-stroke-default);
  color: var(--neutral-white);
  font-family: var(--font-latin);
  font-size: 10px;
  font-weight: var(--fw-bold);
  text-decoration: none;
  transition: background 200ms var(--ease-out);
}
.creator-hero__social a:hover { background: var(--glass-bg-hover); }
/* Real brand marks, monochrome white (premium > original colours on a dark brand).
   fill:currentColor → the chip's white colour drives them. */
.creator-hero__social a svg { width: 18px; height: 18px; display: block; }
/* 小紅書 = the 红 glyph lifted from the real logo (recoloured white) — a single
   character, so it sits in a normal circle chip exactly like the other icons. */
.creator-hero__social a.creator-hero__social-word img { width: 18px; height: 18px; object-fit: contain; display: block; }

/* Quiet section heading inside the storefront */
.creator-sec {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-8);   /* 32px — single owner of the tabs→title gap (was 64px, which stacked with the tabs' own 24px) */
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border-subtle);
}
.creator-sec__title {
  font-family: var(--font-cjk-display);
  font-size: 16px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  margin: 0;   /* kill the UA <h2> margin (~13px each side) so rhythm comes only from the parent gap */
}
.creator-sec__aside {
  font-size: 12px;
  color: var(--text-tertiary);
}
.creator-sec__link {
  font-size: 13px;
  color: var(--yellow-500);
  text-decoration: none;
}
.creator-sec__link:hover { text-decoration: underline; text-underline-offset: 4px; }

/* Pickup slot — one featured drop, Weverse-style */
.creator-pickup {
  display: flex;
  align-items: center;
  gap: 28px;
  margin-top: 28px;
  /* 0803 Dolly：① 導覽膠囊下方那條線拿掉（膠囊本身已經是分界，再畫一條是重複）；
     ② 下方那條線是「本月精選 ↔ 商品/套組」的分界，原本 padding-bottom 之後
     子分類文字緊貼著線（實測間距 0），改由 padding-bottom 拉開。 */
  padding: 0 0 var(--space-6);
  border-bottom: 1px solid var(--border-subtle);
}
.shop-subnav { margin-top: var(--space-6); }
/* 0803 Dolly：項目分頁（全部／共創計畫／IP 授權）的卡片跟篩選列太近，且那條
   灰線不要。原本 .shop-toolbar（商城家族共用）自帶 border-bottom，卡片又直接
   貼著線起排（實測線↔卡片＝0）。
   ① 線拿掉——同 0803「導覽膠囊下方那條線拿掉」的理由：篩選列本身已經是分界。
   ② 補上 --space-6 的下間距，讓篩選文字↔卡片＝38px，跟商店分頁的
      「商品／套組↔卡片」36px 對齊，切分頁時不會跳。
   兩條都靠「後面接的是 .cocreation-grid」定位，只命中 12 個
   creator-*-projects 頁；商城家族的 .shop-toolbar 後面接 .shop-grid，不受影響。
   ⚡ :has() 取引數中最高權重，`.shop-toolbar:has(+ .cocreation-grid)` 是
   (0,2,0)，壓得過 66-shop-hub.css 的 .shop-toolbar (0,1,0)。 */
.shop-toolbar:has(+ .cocreation-grid) { border-bottom: 0; }
.shop-toolbar + .cocreation-grid { margin-top: var(--space-6); }
.creator-pickup__media {
  width: 180px;
  /* 0803 Dolly：本月精選改 2:3（跟商品卡同一個直式比例）。⚠️ ≤767px 那段
     另有一條 aspect-ratio，兩處要一起改，否則手機會被蓋回 1:1。 */
  aspect-ratio: 2 / 3;
  flex-shrink: 0;
  background: var(--bg-secondary);
  overflow: hidden;
}
.creator-pickup__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.creator-pickup__copy { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.creator-pickup__eyebrow {
  font-family: var(--font-latin);
  font-size: 11px;
  letter-spacing: 0.1em;
  color: var(--yellow-500);
}
.creator-pickup__name {
  font-family: var(--font-cjk-text);
  font-size: 17px;
  font-weight: var(--fw-bold);
  line-height: 1.4;
}
.creator-pickup__price {
  font-family: var(--font-latin);
  font-size: 14px;
  font-variant-numeric: tabular-nums;
}
.creator-pickup .btn { margin-top: 6px; align-self: flex-start; }

/* Events as quiet rows, not boxes */
.creator-events { margin-top: var(--space-3); }
.creator-event {
  display: flex;
  align-items: baseline;
  gap: var(--space-6);
  padding: var(--space-4) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.creator-event__date {
  font-family: var(--font-latin);
  font-size: 13px;
  color: var(--yellow-500);
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
  width: 150px;
}
.creator-event__title {
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-medium);
  flex: 1;
  min-width: 0;
}
.creator-event__venue {
  font-size: 12px;
  color: var(--text-tertiary);
}

/* About */
.creator-about {
  max-width: 680px;
  margin-top: var(--space-5);
  color: var(--text-secondary);
  line-height: 1.9;
  font-size: var(--fs-body-sm);
}

/* 商品圖方角（0803 Dolly）。跟 0731 影片頁「周邊商品」同一件事：商品卡的圖
   是陳列面，全站一律直角。商城家族是靠 body 的 theme-sharp 把 --rf-radius
   歸零拿到直角，創作者頁沒有那層皮膚（body 是 rf-creator），所以在這裡把同
   一個變數歸零——改卡片自己的 radius 變數，不是逐元素蓋 border-radius。
   實測：改前全站只剩創作者頁還是 14px（商城/願望清單/影片頁周邊都已 0）。
   ⚡ 必須寫成 .shop-grid[data-shop-panel]（0,3,0）：refine-shop.css 的
   `:is(body.rf-shop, body.rf-creator, [data-work-goods]) .shop-card{--rf-radius:14px}`
   是 (0,2,0)，而那支檔案在 components.css **之後**載入，同權重後者勝——只寫
   [data-shop-panel] 會被它蓋掉。同 80-work-goods.css 那條的處理。 */
.shop-grid[data-shop-panel] .shop-card { --rf-radius: 0; }

/* ─────────────────────────────────────────
   RESPONSIVE — creator storefront
   ───────────────────────────────────────── */

/* ── Tablet: 768–1023px ── */
@media (min-width: 768px) and (max-width: 1023.98px) {
  /* min-height 240 隨背景圖一起移除（0804）——高度交還給識別區內容 */
  .creator-hero__name {
    font-size: clamp(28px, 4vw, 36px);
  }

  .creator-pickup {
    gap: var(--space-5);
  }

  .creator-pickup__media {
    width: 140px;
  }
}

/* ── Phone: ≤767px ── */
@media (max-width: 767.98px) {
  /* min-height 200 隨背景圖一起移除（0804） */
  .creator-hero__name {
    font-size: clamp(24px, 7vw, 32px);
  }

  .creator-hero__copy {
    padding-bottom: var(--space-5);
    gap: var(--space-2);
  }

  .creator-hero__row {
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-2);
  }

  /* Social icons: 44px touch targets */
  .creator-hero__social a {
    width: 40px;
    height: 40px;
  }

  /* Section heading */
  .creator-sec {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-2);
    margin-top: var(--space-6);   /* 32 → 24 on phone: tighten the heading gap */
  }

  /* Pickup slot: stack image above copy */
  .creator-pickup {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
    padding: var(--space-4) 0;
  }

  .creator-pickup__media {
    width: 100%;
    max-width: 260px;
    aspect-ratio: 2 / 3;
  }

  .creator-pickup .btn {
    width: 100%;
    justify-content: center;
  }

  /* Events rows: date and title stack */
  .creator-event {
    flex-direction: column;
    gap: var(--space-1);
    padding: var(--space-4) 0;
    align-items: flex-start;
  }

  .creator-event__date {
    width: auto;
    font-size: var(--fs-caption);
  }

  .creator-event__title {
    font-size: var(--fs-body-sm);
  }
}

/* ── Small phone: ≤480px ── */
@media (max-width: 480px) {
  .creator-pickup__media {
    max-width: 100%;
  }

  .creator-hero__name {
    font-size: clamp(22px, 7vw, 28px);
  }
}

/* ─────────────────────────────────────────
   TOP FANS strip + tier system
   Tiers: INNER CIRCLE ≥80k · SUPER FANS ≥30k · SUPPORTER ≥8k · FANS
   One signifier per avatar: a 2px ring in the tier color. The top tier
   alone earns the filled treatment.

   Ported verbatim from PROTO css/components/39-top-fans-strip.css,
   eShop port batch 3. Renders the #creatorFans strip on every creator
   main page (shop-render.js's fanHtml()) — see batch3 report.
   ───────────────────────────────────────── */
.top-fans {
  display: flex;
  gap: var(--space-6);
  overflow-x: auto;
  scrollbar-width: none;
  padding: var(--space-6) 2px var(--space-2);
}
.top-fans::-webkit-scrollbar { display: none; }
.top-fan {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  flex-shrink: 0;
  width: 86px;
  text-align: center;
}
.top-fan__avatar {
  position: relative;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  padding: 2px;
  background: var(--border-default);
}
.top-fan--inner .top-fan__avatar     { background: var(--yellow-500); }
.top-fan--super .top-fan__avatar     { background: var(--success-500); }
.top-fan--supporter .top-fan__avatar { background: var(--info-500); }
.top-fan__avatar img {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  object-fit: cover;
  display: block;
  border: 2px solid var(--bg-primary);
}
.top-fan__rank {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 20px;
  height: 20px;
  border-radius: var(--radius-pill);
  background: var(--bg-primary);
  border: 1px solid var(--border-default);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-latin);
  font-size: 10px;
  font-weight: var(--fw-bold);
}
.top-fan--inner .top-fan__rank { background: var(--yellow-500); color: var(--yellow-ink); border-color: transparent; }
.top-fan__nick {
  font-size: 12px;
  color: var(--text-secondary);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.top-fan__pts {
  font-family: var(--font-latin);
  font-size: 11px;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}

/* ─────────────────────────────────────────
   RESPONSIVE — top fans strip
   ───────────────────────────────────────── */

/* ── Tablet: 768–1023px ── */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .top-fans {
    gap: var(--space-5);
  }
}

/* ── Phone: ≤767px ── */
@media (max-width: 767.98px) {
  .top-fans {
    gap: var(--space-4);
    padding: var(--space-5) var(--space-1) var(--space-2);
    /* Snap to each fan tile for easy scrolling */
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
  }

  .top-fan {
    width: 72px;
    scroll-snap-align: start;
  }

  /* Avatar: slightly smaller on phone */
  .top-fan__avatar {
    width: 52px;
    height: 52px;
  }

  .top-fan__nick {
    font-size: 11px;
  }
}

/* ── Small phone: ≤480px ── */
@media (max-width: 480px) {
  .top-fans {
    gap: var(--space-3);
  }

  .top-fan {
    width: 64px;
  }

  .top-fan__avatar {
    width: 48px;
    height: 48px;
  }

  /* Rank badge: scale to avatar */
  .top-fan__rank {
    min-width: 16px;
    height: 16px;
    font-size: 9px;
    top: -4px;
    right: -4px;
  }
}

/* ─────────────────────────────────────────
   CREATOR SUB-PAGES — 活動 timeline + 貼文 IG grid
   Each storefront section is its own page; the tab row navigates
   between real pages. Timeline: one hairline spine, upcoming events
   lit, past events dimmed below. Posts: profile head + 3-column
   square grid, likes on hover.

   PARTIAL port from PROTO css/components/42-creator-subpages.css
   (eShop port batch 3) — this is a DELTA, not the whole PROTO file.
   Two sections of the PROTO source were deliberately dropped:
     · `.reg-group` / `.reg-group__title` (PROTO lines ~290-298) — dead
       code even in PROTO itself; the 項目 (projects) page renders via
       ZTOR_SHOP_RENDER.initCreatorRegistry(), which builds `.cocreation-
       card` markup (already styled by f2e's own cocreation-card.css),
       never `.reg-group`. Cross-checked: 0 occurrences of "reg-group"
       in any of the 12 creators' 4 page types.
     · `.list-banner` + `.section--list-banner` (PROTO lines ~300-386) —
       an unrelated index.html "片單推薦" section bundled into the same
       PROTO file. f2e already imports its OWN `list-banner.css` at
       css/main.css:63 (index.html's 愛有千百種樣子 section) — re-porting
       this slice would duplicate `.list-banner__title` etc. in the
       cascade. Confirmed identical selector set, so nothing is lost.
   Only the timeline + ig-head/ig-stats/ig-grid/ig-cell rules (incl.
   their responsive blocks) survive below.
   ───────────────────────────────────────── */

/* 活動 — vertical roadmap */
.timeline {
  position: relative;
  margin-top: var(--space-8);
  padding-left: 28px;
  max-width: 860px;
}
.timeline::before {
  content: '';
  position: absolute;
  left: 5px;
  /* 往上長回分頁列底緣（負值＝抵銷 .timeline 自己的 margin-top），
     頭 40px 用漸層淡入：軸線像從分頁膠囊底下長出來，也順便避開膠囊
     圓角在這個 x 位置留下的 12px 缺口。淡完的位置＝原本的 top:8px。 */
  top: calc(var(--space-8) * -1);
  bottom: 8px;
  width: 1px;
  background: linear-gradient(to bottom, transparent 0, var(--border-default) 40px);
}
.timeline-item {
  position: relative;
  display: flex;
  gap: var(--space-6);
  padding: var(--space-6) 0;
}
.timeline-item::before {
  content: '';
  position: absolute;
  left: -28px;
  top: 34px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--bg-primary);
  border: 2px solid var(--text-tertiary);
}
.timeline-item--live::before {
  background: var(--yellow-500);
  border-color: var(--yellow-500);
}
/* 已結束的場次：內容變淡，但點要維持實心、確實蓋住軸線。
   原本整列 opacity:.5 會連點一起半透明，加上 --text-tertiary 本身就是
   半透明白（rgba 255,255,255,.45），軸線就從點的中央透出來變成空心圈
   （0806 Dolly 抓到）。改成只淡子層、點吃不透明的 --neutral-600。 */
.timeline-item--past { opacity: 1; }
.timeline-item--past > * { opacity: 0.5; }
.timeline-item--past::before {
  background: var(--neutral-600);
  border-color: var(--neutral-600);
}
.timeline-item__poster {
  width: 110px;
  aspect-ratio: 2 / 3;
  flex-shrink: 0;
  background: var(--bg-secondary);
  overflow: hidden;
}
.timeline-item__poster img { width: 100%; height: 100%; object-fit: cover; display: block; }
.timeline-item__copy {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 0;
  padding-top: 6px;
}
.timeline-item__date {
  font-family: var(--font-latin);
  font-size: 13px;
  color: var(--yellow-500);
  font-variant-numeric: tabular-nums;
}
.timeline-item--past .timeline-item__date { color: var(--text-tertiary); }
.timeline-item__title {
  font-family: var(--font-cjk-text);
  font-size: 16px;
  font-weight: var(--fw-bold);
  line-height: 1.45;
}
.timeline-item__meta {
  font-size: 12px;
  color: var(--text-tertiary);
}
.timeline-item__status {
  font-size: 12px;
  color: var(--text-secondary);
}
.timeline-item__status--onsale { color: var(--yellow-500); }
.timeline-item .btn { margin-top: var(--space-2); align-self: flex-start; }

/* 桌機 ≥1024：軸線置中、活動左右交錯（單數場在左、雙數場在右）。
   兩側都讓海報貼著中軸、文字往中軸靠，視覺重量收在中線兩旁。
   ⚠️ 刻意不做半格錯位——每張卡等高 213px、依序往下各佔一列，
   點與點的間距固定，這是「整齊」的來源（0806 Dolly 退掉咬合版）。
   點用 translateX(±50%) 壓在中線上，不用 -47.5px 這種半像素湊數。
   平板/手機不套（維持左側軸），對應下面既有的 responsive 區塊。 */
@media (min-width: 1024px) {
  .timeline {
    --tl-gap: 44px;              /* 卡片邊緣→中軸的距離 */
    max-width: 1240px;
    margin-inline: auto;
    padding-left: 0;
  }
  .timeline::before {
    left: 50%;
    transform: translateX(-50%);
  }
  .timeline-item {
    width: calc(50% - var(--tl-gap));
    box-sizing: border-box;
  }
  /* 左側場次：整列鏡像（海報在右貼中軸），文字靠右 */
  .timeline-item:nth-child(odd) {
    margin-right: auto;
    flex-direction: row-reverse;
    text-align: right;
  }
  .timeline-item:nth-child(odd) .btn { align-self: flex-end; }
  .timeline-item:nth-child(odd)::before {
    left: auto;
    right: calc(var(--tl-gap) * -1);
    transform: translateX(50%);
  }
  /* 右側場次：正常排列（海報在左貼中軸） */
  .timeline-item:nth-child(even) {
    margin-left: auto;
  }
  .timeline-item:nth-child(even)::before {
    left: calc(var(--tl-gap) * -1);
    transform: translateX(-50%);
  }
}

/* 貼文 — profile head + square grid */
.ig-head {
  display: flex;
  align-items: center;
  gap: 36px;
  margin-top: 36px;
  padding-bottom: 30px;
  border-bottom: 1px solid var(--border-subtle);
}
.ig-head__avatar {
  width: 92px;
  height: 92px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--bg-secondary);
  flex-shrink: 0;
}
.ig-head__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ig-head__info { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.ig-head__name {
  font-family: var(--font-cjk-display);
  font-size: 19px;
  font-weight: var(--fw-bold);
}
.ig-stats { display: flex; gap: 28px; }
.ig-stat { font-size: 13px; color: var(--text-tertiary); }
.ig-stat b {
  font-family: var(--font-latin);
  font-weight: var(--fw-bold);
  color: var(--neutral-white);
  margin-right: 5px;
  font-variant-numeric: tabular-nums;
}
.ig-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--space-1);
  margin-top: 28px;
}
/* 0804：貼文改成貼文卡之後，ds.js 會把 .ig-grid 換成 .profile-posts.creator-posts
   （版型改吃 12-profile-header.css），連帶把上面那條 margin-top 一起換掉，分頁列
   就直接貼著第一張卡。這裡把同一個 28 補回來——值刻意跟商店分頁的 .creator-pickup
   一致，切分頁時上緣不會跳。 */
.creator-posts { margin-top: 28px; }
.ig-cell {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--bg-secondary);
  overflow: hidden;
}
.ig-cell img { width: 100%; height: 100%; object-fit: cover; display: block; }
.ig-cell__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6px;
  padding: var(--space-4);
  text-align: center;
  background: rgba(10, 10, 10, 0.6);
  opacity: 0;
  transition: opacity 150ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .ig-cell:hover .ig-cell__overlay { opacity: 1; }
}
.ig-cell__likes {
  font-family: var(--font-latin);
  font-size: 14px;
  font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums;
}
.ig-cell__caption {
  font-size: 12px;
  color: var(--text-secondary);
  line-height: 1.6;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* ─────────────────────────────────────────
   CREATOR SUB-PAGES — RESPONSIVE
   ───────────────────────────────────────── */

/* Tablet (768–1023): minor timeline + ig-head refinements. */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .timeline {
    max-width: 100%;
  }

  .ig-head {
    gap: var(--space-6);
  }

  .list-banner {
    gap: var(--space-10);
  }

  .list-banner__title {
    font-size: clamp(36px, 4vw, var(--fs-display));
  }
}

/* Phone ≤767.98px */
@media (max-width: 767.98px) {
  /* Timeline: keep row layout but shrink poster; reduce spine gap. */
  .timeline {
    padding-left: var(--space-6);
    margin-top: var(--space-6);
  }

  /* 手機的 margin-top 是 24，軸線往上長的距離跟著改，淡入距離同步縮。 */
  .timeline::before {
    top: calc(var(--space-6) * -1);
    background: linear-gradient(to bottom, transparent 0, var(--border-default) 32px);
  }

  .timeline-item {
    flex-direction: row;
    gap: var(--space-4);
    padding: var(--space-5) 0;
  }

  .timeline-item__poster {
    width: 80px;
    /* Maintain 2:3 aspect-ratio — flex-shrink:0 already set on base. */
  }

  .timeline-item__copy {
    padding-top: var(--space-1);
    gap: var(--space-1);
  }

  .timeline-item__title {
    font-size: var(--fs-body-sm);
  }

  /* IG profile head: compress gap. */
  .ig-head {
    gap: var(--space-5);
    margin-top: var(--space-6);
    padding-bottom: var(--space-5);
  }

  .ig-head__avatar {
    width: 68px;
    height: 68px;
  }

  .ig-head__name {
    font-size: var(--fs-h5);
  }

  .ig-stats {
    gap: var(--space-5);
    flex-wrap: wrap;
  }

  /* IG grid: tighter gap — already 3-col, keep it. */
  .ig-grid {
    gap: 2px;
    margin-top: var(--space-5);
  }

  /* List banner: single-col, title clamp pulled in from existing 768 rule. */
  .list-banner {
    grid-template-columns: 1fr;
    gap: var(--space-8);
  }

  .list-banner__title {
    font-size: clamp(28px, 7vw, 36px);
    line-height: 1.3;
  }

  .list-banner__sub {
    margin-top: var(--space-3);
  }

  .list-banner__cta {
    margin-top: var(--space-6);
  }
}

/* Small phone ≤480px: further compress timeline poster + ig-head. */
@media (max-width: 480px) {
  .timeline {
    padding-left: var(--space-5);
  }

  .timeline-item__poster {
    width: 64px;
  }

  /* IG head: stack avatar above stats on very narrow screens. */
  .ig-head {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-4);
  }

  .ig-stats {
    gap: var(--space-5);
  }

  /* IG grid → 2-col on smallest phones. */
  .ig-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}


/* ── Stripe checkout mock (2026-07-29) — external-page skin: colors stay
   out of tokens by design. 0730 兩線彙整：staging 的 55-stripe-checkout.css
   與本檔內容完全一致，因 55 已被 ds-drawer 佔用，統一保留 77 刪 55。 */
/* ═══ Stripe 結帳畫面 mock（#stripeCheckout，ds.js 注入）═══
   仿 Stripe 托管結帳的「外部頁」：白底、blurple 支付鈕——刻意非站內皮膚，
   色值 hardcode 不進 tokens（同 emails/ 深色皮膚的慣例：第三方頁不是 Ztor 面）。
   進場：title/short 爆米花不足「確認付款」（付款方式區塊暫藏，只走 Stripe）。
   展示用，不實際扣款（頁尾有標示）。 */

/* 滿版白頁（手機/桌機同）：權重 0,4,0 蓋 ds.css .ds-modal[data-size=sm] 的 400 cap */
.stripe-co.ds-modal { padding: 0; }
.stripe-co.ds-modal[data-size] .ds-modal__panel {
  width: 100vw;
  height: 100vh;
  height: 100dvh;
  max-width: none;
  max-height: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  padding: 0;
  background: #ffffff;
  color: #1a1f36;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.stripe-co__bar {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: calc(12px + env(safe-area-inset-top, 0px)) 20px 12px;
  background: #ffffff;
  border-bottom: 1px solid #e6e8ee;
}
.stripe-co__back {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  margin-left: -6px;
  border: 0;
  background: none;
  color: #1a1f36;
  cursor: pointer;
}
.stripe-co__back svg { width: 20px; height: 20px; }
.stripe-co__brand {
  font-family: var(--font-latin);
  font-weight: var(--fw-bold);
  font-size: 16px;
}
.stripe-co__test {
  margin-left: auto;
  padding: 3px 8px;
  border-radius: 4px;
  background: #fcf3cf;
  color: #a06b00;
  font-size: 12px;
  font-weight: var(--fw-medium);
}
.stripe-co__body {
  max-width: 400px;
  margin: 0 auto;
  padding: 24px 20px calc(40px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.stripe-co__label { margin: 0; font-size: 14px; color: #6b7385; }
.stripe-co__amount { margin: -8px 0 8px; font-size: 28px; font-weight: var(--fw-bold); }
.stripe-co__field { display: flex; flex-direction: column; gap: 6px; }
.stripe-co__field > span { font-size: 13px; color: #4f566b; }
.stripe-co__field input {
  width: 100%;
  min-width: 0;   /* grid 1fr 會被 input min-content 撐爆（等分格老雷） */
  box-sizing: border-box;
  height: 44px;
  padding: 0 12px;
  border: 1px solid #d9dce3;
  border-radius: 8px;
  background: #ffffff;
  color: #1a1f36;
  font-size: 15px;
  box-shadow: 0 1px 2px rgba(26, 31, 54, 0.06);
}
.stripe-co__field input::placeholder { color: #a3acba; }
.stripe-co__field input:focus { outline: 2px solid #635bff33; border-color: #635bff; }
.stripe-co__row2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.stripe-co__pay {
  margin-top: 10px;
  height: 46px;
  border: 0;
  border-radius: 8px;
  background: #635bff;   /* Stripe blurple */
  color: #ffffff;
  font-size: 15px;
  font-weight: var(--fw-bold);
  cursor: pointer;
  box-shadow: 0 2px 6px rgba(99, 91, 255, 0.35);
}
.stripe-co__pay:hover { background: #5851e6; }
.stripe-co__pay:active { transform: scale(0.99); }
.stripe-co__foot {
  margin: 10px 0 0;
  text-align: center;
  font-size: 12px;
  color: #8a93a6;
}
.stripe-co__foot strong { font-weight: var(--fw-bold); color: #6b7385; }


/* ── staging 線 0729 內頁批（0730 彙整時自 56/57 改號 78/79——原號被
   eShop port 的 ds-toast/ds-success-tick 佔用，恢復一號一元件） */
/* ══════════════════════════════════════════════════════════════
   56 · 創作團隊內頁（team.html?t=<slug>）
   影片頁「創作團隊 ›」的目的頁。架構照 1.0：返回列（海報縮圖＋
   ‹ 返回 片名 主頁）、大標「創作團隊」、按職稱分組、每人方形照片＋
   名字（演員多帶「飾 X」副標）；皮膚 2.0 tokens。
   內容由 team.html 頁尾 script 依 ?t= 重建，本檔只管版型。
   本檔打包進 components.css（repo 根）→ url() 寫 assets/…。
   ══════════════════════════════════════════════════════════════ */

.teampage { padding: 40px 0 96px; }
/* container 1440 置中、內容在 container 內靠左（Dolly 0729 三調，照 1.0 超寬螢幕表現）：
   超寬螢幕整塊內容落在畫面中間；≤1440＋側欄的視窗下與貼左無異 */
.teampage__shell {
  width: 100%;               /* main 是 flex column：無此行 auto margin 會把 shell 縮成內容寬 */
  max-width: 1440px;
  margin-inline: auto;
  padding-inline: 32px;
}

/* ── 返回列：海報縮圖＋返回連結＋大標 ── */
.teampage__head {
  display: flex;
  align-items: flex-start;
  gap: 28px;
}
.teampage__posterlink {
  flex: none;
  display: block;
  border-radius: var(--radius-md);
  overflow: hidden;
}
.teampage__poster {
  display: block;
  width: 108px;                        /* 84→108（Dolly 0729 放大一階） */
  height: 152px;
  object-fit: cover;
}
.teampage__headmain { min-width: 0; padding-top: 6px; }
.teampage__backlink {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--text-primary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  text-decoration: none;
}
.teampage__backlink:hover .teampage__backname { text-decoration: underline; }
.teampage__backchev {
  flex: none;
  width: 16px;
  height: 16px;
  background-color: currentColor;
  -webkit-mask: url('assets/icons/chevron_left.svg') center / contain no-repeat;
  mask: url('assets/icons/chevron_left.svg') center / contain no-repeat;
}
.teampage__backname { color: var(--brand-500); }
.teampage__title {
  margin: 20px 0 0;                    /* 12→20：返回列與大標間距拉開（Dolly 0729） */
  font-family: var(--font-cjk-display);
  font-size: 32px;
  line-height: 1.25;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  letter-spacing: -0.01em;
}

/* ── 職稱分組（1.0 參考：演員組在最前）──
   層次（Dolly 0729）：組與組之間 hairline 分隔線（含頁首與名單之間），
   節奏改由每組的上下 padding 提供（不再用 flex gap） */
.teampage__groups {
  margin-top: 56px;
  display: flex;
  flex-direction: column;
}
.teampage__group {
  padding-block: 48px;
  border-top: 1px solid var(--border-subtle);
}
.teampage__role {
  margin: 0 0 24px;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body-lg);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
/* 同組多人（演員群）自動多欄；單人一列 */
.teampage__people {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: 32px 40px;
}
.team-person {
  display: flex;
  align-items: center;
  gap: 28px;
}
.team-person__photo {
  flex: none;
  width: 88px;
  height: 88px;
  border-radius: 50%;                  /* 圓形頭像（Dolly 0729，原 1.0 方磚改圓） */
  object-fit: cover;
}
/* 無照片佔位磚（1.0 參考）：灰底＋人形 glyph；真素材到位換回 <img> */
.team-person__photo--ph {
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--white-alpha-4);
}
.team-person__glyph {
  width: 44px;
  height: 44px;
  background-color: var(--text-tertiary);
  /* person.svg＝頭＋肩人形（自 avatar.svg 抽出、去圓底）——avatar.svg 帶實心圓底，mask 上色會變成一顆圓 */
  -webkit-mask: url('assets/icons/person.svg') center / contain no-repeat;
  mask: url('assets/icons/person.svg') center / contain no-repeat;
}
.team-person__body { min-width: 0; }
.team-person__name {
  margin: 0;
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.team-person__caption {
  margin: 4px 0 0;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}

@media (max-width: 768px) {
  /* 手機＝1.0 結構（Dolly 0729）：m-pagebar 頂欄（裸←＋置中片名+創作團隊），
     桌機返回列/大標整塊收起，內容直接從職稱分組開始 */
  .teampage { padding: 0 0 80px; }
  .teampage__shell { padding-inline: 16px; }
  .teampage__head { display: none; }
  /* 子頁無 logo header（m-pagebar 慣例＋1.0 參考）：收掉手機站台頂欄，只留 pagebar。
     手機頂欄=.m-header-rwd（support-mobile.js 生成）、在 main 外→body :has() scope */
  body:has(main.teampage) .m-header-rwd { display: none; }
  /* 若隱若現（統一照焦點頁 header，Dolly 0729）：本體透明無 hairline，
     ::before 上黑70%到全透漸層，內容從底下透出 */
  .teampage .m-pagebar {
    margin: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom: none;
  }
  /* 照首頁 header 的 soft veil（08-header.css body.has-rail 版）：
     tint 漸層＋blur(12px)、往下滲 24px 用 mask 收尾無接縫、捲動才淡入（頁首全清晰） */
  .teampage .m-pagebar::before {
    content: '';
    position: absolute;
    inset: 0 0 -24px 0;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(180deg, rgba(23, 23, 23, 0.7) 0%, rgba(23, 23, 23, 0) 100%);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    -webkit-mask-image: linear-gradient(180deg,
      rgba(0,0,0,1) 0%, rgba(0,0,0,1) 66%,
      rgba(0,0,0,0.96) 74%, rgba(0,0,0,0.82) 81%,
      rgba(0,0,0,0.58) 88%, rgba(0,0,0,0.28) 94%, rgba(0,0,0,0) 100%);
            mask-image: linear-gradient(180deg,
      rgba(0,0,0,1) 0%, rgba(0,0,0,1) 66%,
      rgba(0,0,0,0.96) 74%, rgba(0,0,0,0.82) 81%,
      rgba(0,0,0,0.58) 88%, rgba(0,0,0,0.28) 94%, rgba(0,0,0,0) 100%);
  }
  @supports (animation-timeline: scroll()) {
    /* 頁首標題也跟 veil 同節奏：剛進頁只有返回箭頭，捲動後標題才浮現 */
    .teampage .m-pagebar::before,
    .teampage .m-pagebar .m-pagebar__title {
      animation: header-veil-in linear both;
      animation-timeline: scroll(root);
      animation-range: 0 120px;
    }
  }
  /* 片名與「創作團隊」之間留一個空格寬（Dolly 0729）——用 margin 而非文字空格，
     換片 JS 改字時不用管尾空格 */
  .teampage__mtitle-name { color: var(--brand-500); margin-right: 8px; }
  .teampage__groups { margin-top: 8px; }
  .teampage__group { padding-block: 32px; }
  .teampage__group:first-child { border-top: none; }   /* 頂欄已有 hairline，首組不再疊一條 */
  .teampage__people { grid-template-columns: 1fr; gap: 16px; }
  .team-person__photo { width: 64px; height: 64px; }
}

/* ══════════════════════════════════════════════════════════════
   57 · 共創歷程內頁（cocreate-history.html）
   影片頁共創 banner「查看詳細歷程」目的頁。架構照 1.0 Figma
   （27359-308637 成功結案 retrospective），樣式語彙照花花
   cocreate-preview／2.0 tokens。衝線專屬靜態 demo。
   本檔打包進 components.css（repo 根）→ url() 寫 assets/…。
   ══════════════════════════════════════════════════════════════ */

.cchist { padding-bottom: 96px; }
.cchist__shell {
  width: 100%;                      /* main 為 flex column：width 100% 防 auto margin 縮欄 */
  max-width: var(--container-max);  /* 1280（Dolly 0729 終版：1056 太窄、1440 太寬，取標準 container token） */
  margin-inline: auto;
  padding-inline: var(--container-padding-x);   /* 48px，同全站 container */
}

/* ── Hero：糊化劇照底＋返回＋標題＋募資結果卡 ── */
.cchist__hero { position: relative; isolation: isolate; padding: 20px 0 8px; overflow-x: clip; }
.cchist__hero-bg {
  position: absolute;
  inset: 0 0 auto;
  height: 420px;
  z-index: -1;
  pointer-events: none;            /* 裝飾底圖，別攔下方分頁列的點擊 */
  background: url('assets/images/titles/f-i-am-speed/backdrop.webp') center top / cover no-repeat;
  filter: blur(10px);
  transform: scale(1.06);          /* blur 羽化不露底 */
  opacity: 0.45;
  -webkit-mask: linear-gradient(180deg, #000 0%, #000 40%, transparent 100%);
  mask: linear-gradient(180deg, #000 0%, #000 40%, transparent 100%);
}
.cchist__back { display: flex; align-items: center; height: 56px; }
.cchist__title {
  margin: 20px 0 0;
  font-family: var(--font-cjk-display);
  font-size: 32px;
  line-height: 1.25;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  letter-spacing: -0.01em;
}
.cchist__sub { margin: 8px 0 0; font-size: var(--fs-body-sm); color: var(--text-secondary); }
.cchist__initiator {
  margin: 12px 0 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
}
.cchist__initiator-avatar { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; }
.cchist__initiator-label { color: var(--text-secondary); }

/* 募資結果卡 */
/* 材質照花花 .cocreate-fund：白霧毛玻璃（white-alpha-4＋border-default＋--glass-blur） */
.cchist-card {
  margin-top: 24px;
  padding: 28px 32px;
  border-radius: var(--radius-lg);
  background: var(--white-alpha-4);
  border: 1px solid var(--border-default);
  backdrop-filter: var(--glass-blur);
  -webkit-backdrop-filter: var(--glass-blur);
}
.cchist-card__amount {
  margin: 0;
  font-family: var(--font-latin, inherit);
  font-size: 34px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.cchist-card__goal { margin: 6px 0 0; font-size: var(--fs-body-sm); color: var(--text-secondary); }
.cchist-card__barrow { display: flex; align-items: center; gap: 12px; margin-top: 18px; }
.cchist-card__bar {
  flex: 1 1 auto;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);
  overflow: hidden;
}
.cchist-card__bar-fill {
  display: block;
  width: var(--p, 100%);
  height: 100%;
  border-radius: inherit;
  background: var(--brand-500);
}
.cchist-card__pct {
  flex: none;
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--brand-500);
  color: #1a1206;
  font-size: 12px;
  font-weight: var(--fw-bold);
}
.cchist-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: 16px;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.cchist-card__meta { display: inline-flex; align-items: center; gap: 12px; }
.cchist-card__backers { color: var(--text-primary); }
.cchist-card__badge {
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-8);
  color: var(--text-secondary);
  font-size: 12px;
}

/* ── 章節通用 ── */
.cchist__sec { margin-top: 72px; }
.cchist__sec-head { display: flex; align-items: baseline; gap: 10px; }
.cchist__sec-title {
  margin: 0 0 24px;
  font-family: var(--font-cjk-display);
  font-size: 22px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.cchist__sec-head .cchist__sec-title { margin-bottom: 24px; }
.cchist__sec-count { font-size: var(--fs-body-sm); color: var(--text-tertiary); }
/* 概念圖/相關影片改用影片頁的 .detail-section__head（36-title-detail.css 單一來源）；
   本頁只補標題與內容的間距（36 桌機版沒帶 margin，靠 .detail-section 自身 gap） */
.cchist .detail-section__head { margin-bottom: 24px; }

/* ── 支持成果：三格 stat ── */
.cchist-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
}
.cchist-stat {
  padding: 28px 24px;
  border-radius: var(--radius-lg);
  background: var(--white-alpha-4);
  border: 1px solid var(--border-subtle);
  text-align: center;
}
.cchist-stat__num {
  margin: 0;
  font-size: 28px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.cchist-stat__label { margin: 8px 0 0; font-size: var(--fs-body-sm); color: var(--text-secondary); }

/* ── 內文 ── */
.cchist__prose { max-width: 920px; }
.cchist__prose p {
  margin: 0 0 16px;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  color: var(--text-secondary);
}
.cchist__facts {
  margin: 24px 0 0;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 10px 24px;
}
.cchist__facts dt { color: var(--text-primary); font-weight: var(--fw-bold); font-size: var(--fs-body-sm); }
.cchist__facts dd { margin: 0; color: var(--text-secondary); font-size: var(--fs-body-sm); }

/* ── 時程三階段（線點語彙照花花版）：done＝橘點橘線／current＝靶心點＋「目前進度」膠囊＋橘標題／
   upcoming＝空心灰點灰線；線段貫到下一顆點、末段延伸到卡右緣。
   本頁＝已結案歷程 → 三段全掛 --done（全橘）；三態留給進行中頁面重用 ── */
.cchist-timeline {
  margin-top: 28px;
  padding: 32px;
  border-radius: var(--radius-lg);
  background: var(--white-alpha-4);
  border: 1px solid var(--border-subtle);
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 32px;
}
.cchist-timeline:has(.cchist-phase__flag) { padding-top: 48px; }   /* 有「目前進度」膠囊才留頂部空間 */
.cchist-phase { position: relative; }
.cchist-phase__dotline { position: relative; display: flex; align-items: center; height: 14px; margin-bottom: 18px; }
.cchist-phase__dot {
  flex: none;
  position: relative;
  z-index: 1;                        /* 蓋過線段，靶心深色縫才吃得掉線頭 */
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--brand-500);
}
.cchist-phase--current .cchist-phase__dot {
  box-shadow: 0 0 0 3px var(--neutral-900), 0 0 0 5px var(--brand-500);   /* 橘心＋深縫＋橘外環 */
}
.cchist-phase--upcoming .cchist-phase__dot {
  background: none;
  border: 2px solid var(--white-alpha-24);
}
/* 線段照花花版：點＋線一體（線從點底下出發相連，點 z-index 蓋線頭），
   只有線尾在下一顆點前 20px 斷開（right 跨 32px grid gap 再退回缺口）；
   已完成段橘、未完成灰 */
.cchist-phase__line {
  position: absolute;
  left: 7px;
  right: calc(-32px + 20px);
  height: 3px;
  border-radius: 1.5px;
  background: var(--white-alpha-16);
}
.cchist-phase--done .cchist-phase__line { background: var(--brand-500); }
.cchist-phase:last-child .cchist-phase__line { right: 0; }
/* 「目前進度」膠囊：置中在目前階段點正上方 */
.cchist-phase__flag {
  position: absolute;
  top: -34px;
  left: 7px;
  transform: translateX(-50%);
  padding: 5px 12px;
  border-radius: 999px;
  background: var(--brand-500);
  color: var(--neutral-900);
  font-size: var(--fs-caption);
  font-weight: var(--fw-bold);
  line-height: 1.4;
  white-space: nowrap;
}
.cchist-phase__title { margin: 0; font-size: var(--fs-body-lg); font-weight: var(--fw-bold); color: var(--text-primary); }
.cchist-phase--current .cchist-phase__title { color: var(--brand-500); }
.cchist-phase__date { margin: 8px 0 0; font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--text-primary); }
.cchist-phase__desc { margin: 10px 0 0; font-size: var(--fs-body-sm); line-height: var(--lh-body-sm); color: var(--text-secondary); }
.cchist-phase__list { margin: 14px 0 0; padding: 0; list-style: none; }
.cchist-phase__list li {
  position: relative;
  padding-left: 14px;
  margin-top: 6px;
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.cchist-phase__list li::before { content: '·'; position: absolute; left: 2px; color: var(--text-tertiary); }

/* ── 預算分配 ── */
.cchist-budget__bar {
  display: flex;
  gap: 6px;
  height: 8px;
  margin-bottom: 24px;
}
.cchist-budget__seg { display: block; width: var(--w); height: 100%; border-radius: var(--radius-pill); }
.cchist-budget__seg--a { background: #6fd6a8; }
.cchist-budget__seg--b { background: #a78bfa; }
.cchist-budget__seg--c { background: #f0b429; }
.cchist-budget__seg--d { background: #f491b2; }
.cchist-budget__legend {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 24px;
}
.cchist-budget__name {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.cchist-budget__swatch { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.cchist-budget__pct { margin: 8px 0 0; font-size: 20px; font-weight: var(--fw-bold); color: var(--text-primary); }
.cchist-budget__amt { margin: 4px 0 0; font-size: var(--fs-body-sm); color: var(--text-tertiary); }

/* ── 福利三卡（已結束＝無選擇 CTA）── */
.cchist-tiers {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 20px;
}
.cchist-tier {
  position: relative;
  padding: 28px 24px;
  border-radius: var(--radius-lg);
  background: var(--white-alpha-4);
  border: 1px solid var(--border-subtle);
}
.cchist-tier--hot { border-color: rgba(255, 163, 63, 0.55); }
.cchist-tier__flag {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  padding: 3px 12px;
  border-radius: var(--radius-pill);
  background: #2b1f10;
  border: 1px solid rgba(255, 163, 63, 0.55);
  color: var(--brand-500);
  font-size: 12px;
  white-space: nowrap;
}
.cchist-tier__name { margin: 0; font-size: var(--fs-body-sm); color: var(--text-secondary); }
.cchist-tier__price { margin: 10px 0 0; font-size: 26px; font-weight: var(--fw-bold); color: var(--text-primary); }
.cchist-tier__ntd { margin: 4px 0 0; font-size: 12px; color: var(--text-tertiary); }
.cchist-tier__list { margin: 18px 0 0; padding: 0; list-style: none; }
.cchist-tier__list li {
  position: relative;
  padding-left: 14px;
  margin-top: 8px;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.cchist-tier__list li::before { content: '·'; position: absolute; left: 2px; color: var(--brand-500); }
.cchist-tier__backers {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 22px;
  padding-top: 18px;
  border-top: 1px solid var(--border-subtle);
}
.cchist-tier__backers-label { font-size: 12px; color: var(--text-tertiary); }

/* ── 相關影片 play 圈 ── */
.cchist-video { position: relative; }
.cchist-video__play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 48px;
  height: 48px;
  border-radius: 50%;
  background: rgba(10, 10, 10, 0.6) url('assets/icons/media-play.svg') center / 20px no-repeat;
  border: 1px solid rgba(255, 255, 255, 0.35);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* ── 留言＋活動公告雙欄 ── */
.cchist__duo {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 48px;
  align-items: start;
}
.cchist-commentcard {
  padding: 8px 24px;
  border-radius: var(--radius-lg);
  background: var(--white-alpha-4);
  border: 1px solid var(--border-subtle);
}
.cchist-comment { display: flex; gap: 12px; padding: 16px 0; }
.cchist-comment + .cchist-comment { border-top: 1px solid var(--border-subtle); }
.cchist-comment__avatar { flex: none; width: 36px; height: 36px; border-radius: 50%; object-fit: cover; }
.cchist-comment__meta { margin: 0; font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--text-primary); }
.cchist-comment__meta span { margin-left: 8px; font-weight: var(--fw-regular); color: var(--text-tertiary); font-size: 12px; }
.cchist-comment__text { margin: 6px 0 0; font-size: var(--fs-body-sm); line-height: var(--lh-body-sm); color: var(--text-secondary); }
.cchist-newscard {
  padding: 20px;
  border-radius: var(--radius-lg);
  background: var(--white-alpha-4);
  border: 1px solid var(--border-subtle);
}
.cchist-newscard + .cchist-newscard { margin-top: 16px; }
.cchist-newscard__meta {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
}
.cchist-newscard__meta span { color: var(--text-tertiary); font-size: 12px; }
.cchist-newscard__avatar { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; }
.cchist-newscard__title { margin: 12px 0 0; font-size: var(--fs-body); font-weight: var(--fw-bold); color: var(--text-primary); }
.cchist-newscard__img { display: block; width: 100%; border-radius: var(--radius-md); margin-top: 12px; }
.cchist-newscard__text { margin: 12px 0 0; font-size: var(--fs-body-sm); line-height: var(--lh-body-sm); color: var(--text-secondary); }

/* ── 手機三分頁（照 1.0 mobile）：桌機隱藏；底部互動列=.mtd-bar（36-title-detail.css 自帶桌機隱藏）── */
.cchist-tabs { display: none; }

/* ── RWD ── */
@media (max-width: 1024px) {
  .cchist-tiers { grid-template-columns: 1fr; max-width: 560px; }
  .cchist-timeline { grid-template-columns: 1fr; gap: 28px; padding: 32px; }
  .cchist-phase__line { display: none; }         /* 直排不畫橫向連線 */
  .cchist-phase__flag { top: -7px; left: 24px; transform: none; }   /* 直排：膠囊移到點右側 */
  .cchist-budget__legend { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cchist__duo { grid-template-columns: 1fr; }
}
@media (max-width: 768px) {
  .cchist__shell { padding-inline: 16px; }
  /* 子頁無 logo header＋pagebar（同 team.html 慣例） */
  body:has(main.cchist) .m-header-rwd { display: none; }
  body:has(main.cchist) .m-bottomnav { display: none; }   /* 本頁有自己的底部列（照 1.0），站台底導航收起 */
  /* pagebar 在本頁是 main 直下層（無 16px gutter）——泛用版型的 -16px 外距
     會把 body 撐寬 16px 造成橫向捲動，這裡歸零（main 本來就滿版） */
  /* 若隱若現（統一照焦點頁 header，Dolly 0729）：本體透明無 hairline，
     ::before 上黑70%到全透漸層，內容從底下透出 */
  .cchist .m-pagebar {
    margin: 0;
    background: none;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    border-bottom: none;
  }
  /* 照首頁 header 的 soft veil（08-header.css body.has-rail 版）：
     tint 漸層＋blur(12px)、往下滲 24px 用 mask 收尾無接縫、捲動才淡入（頁首全清晰） */
  .cchist .m-pagebar::before {
    content: '';
    position: absolute;
    inset: 0 0 -24px 0;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(180deg, rgba(23, 23, 23, 0.7) 0%, rgba(23, 23, 23, 0) 100%);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    -webkit-mask-image: linear-gradient(180deg,
      rgba(0,0,0,1) 0%, rgba(0,0,0,1) 66%,
      rgba(0,0,0,0.96) 74%, rgba(0,0,0,0.82) 81%,
      rgba(0,0,0,0.58) 88%, rgba(0,0,0,0.28) 94%, rgba(0,0,0,0) 100%);
            mask-image: linear-gradient(180deg,
      rgba(0,0,0,1) 0%, rgba(0,0,0,1) 66%,
      rgba(0,0,0,0.96) 74%, rgba(0,0,0,0.82) 81%,
      rgba(0,0,0,0.58) 88%, rgba(0,0,0,0.28) 94%, rgba(0,0,0,0) 100%);
  }
  @supports (animation-timeline: scroll()) {
    /* 頁首標題也跟 veil 同節奏：剛進頁只有返回箭頭，捲動後標題才浮現 */
    .cchist .m-pagebar::before,
    .cchist .m-pagebar .m-pagebar__title {
      animation: header-veil-in linear both;
      animation-timeline: scroll(root);
      animation-range: 0 120px;
    }
  }
  .cchist__mtitle-name { color: var(--brand-500); margin-right: 8px; }
  /* hero 圖上移滑進透明 pagebar 底下（同焦點頁內容從 header 下透出）；內容用 padding 讓回來 */
  .cchist__hero {
    margin-top: calc(-56px - env(safe-area-inset-top, 0px));
    padding-top: calc(56px + env(safe-area-inset-top, 0px));
  }
  .cchist__back { display: none; }               /* 桌機返回列收起，走 pagebar ← */
  .cchist__title { font-size: 24px; margin-top: 4px; }
  .cchist-card { padding: 20px; margin-top: 20px; }
  .cchist-card__amount { font-size: 28px; }
  .cchist__sec { margin-top: 48px; }
  .cchist__sec-title { font-size: 18px; margin-bottom: 16px; }   /* 手機字級對齊影片頁 section 標題 18 */
  .cchist-stats { gap: 10px; }
  .cchist-stat { padding: 18px 8px; }
  .cchist-stat__num { font-size: 22px; }
  .cchist { padding-bottom: calc(72px + env(safe-area-inset-bottom, 0px) + 24px); }  /* 讓出底部 .mtd-bar */

  /* 三分頁：結果卡下方、白字＋橘底線 active（同 mtd tabs 語彙） */
  .cchist-tabs {
    display: flex;
    margin-top: 28px;
    border-bottom: 1px solid var(--border-subtle);
  }
  .cchist-tabs__tab {
    flex: 1 1 0;
    border: none;
    background: none;
    padding: 12px 0 14px;
    font: inherit;
    font-size: var(--fs-body);
    color: var(--text-secondary);
    cursor: pointer;
    border-bottom: 2px solid transparent;
  }
  .cchist-tabs__tab.is-active {
    color: var(--text-primary);
    font-weight: var(--fw-bold);
    border-bottom-color: var(--brand-500);
  }
  /* 分頁切板：main[data-mtab] 只留對應區塊 */
  .cchist[data-mtab="info"] [data-cchist-mpanel]:not([data-cchist-mpanel="info"]),
  .cchist[data-mtab="stills"] [data-cchist-mpanel]:not([data-cchist-mpanel="stills"]),
  .cchist[data-mtab="videos"] [data-cchist-mpanel]:not([data-cchist-mpanel="videos"]) { display: none; }
  [data-cchist-mpanel="stills"].cchist__sec, [data-cchist-mpanel="videos"].cchist__sec { margin-top: 32px; }
  .cchist .detail-section__head { margin-bottom: var(--space-3); }   /* 手機間距規範：標題→內容 12 */

  /* 時程改直式（照 1.0 mobile）：左側縱向軌道串三節點 */
  .cchist-timeline { display: block; padding: 24px 20px; }
  .cchist-phase { position: relative; padding-left: 34px; padding-bottom: 32px; }
  .cchist-phase:last-child { padding-bottom: 0; }
  .cchist-phase__dotline { position: absolute; left: 0; top: 4px; height: auto; margin: 0; }
  .cchist-phase__line { display: none; }
  /* 縱軌同桌機語意：軌從自己的點底下出發相連（點蓋軌頭），收在下一顆點前留空 */
  .cchist-phase::before {
    content: '';
    position: absolute;
    left: 6px;
    top: 11px;
    bottom: 6px;
    width: 2px;
    border-radius: 1px;
    background: var(--white-alpha-16);           /* 未完成段灰 */
  }
  .cchist-phase--done::before { background: var(--brand-500); }
  /* 末段也畫軌（同桌機末段線延伸；Dolly 0729）：陪自己的內容走到底 */
  /* current：膠囊佔標題上方一行，點/縱軌跟著下移；前一段縱軌補長接上 */
  .cchist-phase--current { padding-top: 38px; }
  .cchist-phase--current .cchist-phase__dotline { top: 42px; }
  .cchist-phase--current::before { top: 60px; }
  .cchist-phase--current .cchist-phase__flag { top: 0; left: 34px; }
  .cchist-phase:has(+ .cchist-phase--current)::before { bottom: -32px; }

  /* 預算 legend 改橫列：名稱｜%｜金額 同行（照 1.0 mobile） */
  .cchist-budget__legend { grid-template-columns: 1fr; gap: 14px; }
  .cchist-budget__item { display: flex; align-items: baseline; gap: 12px; }
  .cchist-budget__name { flex: 1 1 auto; }
  .cchist-budget__pct { margin: 0; font-size: 18px; }
  .cchist-budget__amt { margin: 0; }

  /* 底部互動 sheet：主體樣式全部來自 36-title-detail.css 的 .mtd-bar（單一來源）。
     本頁只補：公告 panel 的顯示規則＋搬進 sheet 的欄位收頭、去卡片化（同影片頁留言） */
  .cchist .mtd-bar[data-mtd-active="news"] .mtd-bar__panel--news { display: block; }
  .cchist .mtd-bar__panel { margin-right: -16px; padding-right: 16px; }   /* 捲軸貼齊 sheet 右緣 */
  .cchist .mtd-bar__panel .cchist__sec { margin-top: 0; }
  .cchist .mtd-bar__panel .cchist__sec-head,
  .cchist .mtd-bar__panel .cchist__sec > .cchist__sec-title { display: none; }   /* sheet tab 就是標題 */
  .cchist .mtd-bar__panel .cchist-commentcard { border: 0; border-radius: 0; padding: 0; background: none; }
}


/* ── 作品專屬周邊（2026-07-31）：影片頁的「周邊商品」區塊。卡片重用商城
   .shop-card，本檔只處理格線放進 .detail-section 的差異＋手機收欄。 */
/* ════════════════════════════════════════════════════════════════
   80-work-goods.css — 作品專屬周邊（0731 Dolly）

   影片頁（title.html）的「周邊商品」區塊。創作者在 Creator Studio 上架商品
   時綁定一部作品（商品資料的 work 欄位＝作品 slug），前台就把該作品的商品
   掛回它的影片頁。

   卡片本體＝商城的 .shop-card（同一支 productCardHtml 產出，見
   assets/shop-render.js initWorkGoods），卡片 chrome（徽章／願望愛心／懸浮
   抬升）＝assets/refine-shop.css 的 :is(body.rf-shop, [data-work-goods]) 那組。
   本檔只負責「商城的卡片放進影片頁」需要的差異。

   版型（0731 Dolly 第二輪）：一排五個、可橫滑看更多、更多 → 商店頁。
   捲動與箭頭行為重用 ds.js 的 episode-rail register（同一支實作）。
   ════════════════════════════════════════════════════════════════ */

/* 沒商品 → JS 掛 hidden。.detail-section 是 display:flex，權重不夠會漏出來
   （[hidden] 被 display 蓋，全站慣犯）。 */
.detail-section[hidden] { display: none; }

/* ── 橫滑軌道 ──────────────────────────────────────────────────────
   .shop-grid 同時掛在 track 上是為了繼承商城格線的欄距語彙，但這裡要的是
   橫向流，所以把 grid 整組改寫成 flex（欄數/列距交給下面的 flex 基準）。 */
/* 商品圖方角（0731 Dolly）。商城頁靠 theme-sharp 把 --rf-radius 歸零拿到
   直角，影片頁沒有那層皮膚，所以在這裡把同一個變數歸零——改的是卡片自己
   的 radius 變數，不是逐元素蓋 border-radius，和商城走同一條路。
   ⚡ 必須寫成 .detail-section[data-work-goods]（0,3,0）：refine-shop.css 的
   `:is(body.rf-shop, [data-work-goods]) .shop-card{--rf-radius:14px}` 同為
   (0,2,0)，而那支檔案在 components.css **之後**載入，同權重後者勝——只寫
   [data-work-goods] 會被它蓋掉（實測就是 14px 沒變）。 */
.detail-section[data-work-goods] .shop-card { --rf-radius: 0; }

.goods-rail { position: relative; }
.goods-rail__track {
  display: flex;
  gap: var(--space-4);
  overflow-x: auto;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  /* overflow-x:auto 會把 overflow-y 夾成 clip，卡片 hover 的 translateY(-4px)
     連同陰影會被切頂。內距撐開再用負外距拉回，外觀間距不變。 */
  padding-block: 10px;
  margin-block: -10px;
  /* .shop-grid 自帶的 margin-top:36 在 .detail-section（flex column, gap:24）
     裡會疊成 60；歸零交給 section 的 gap 統一供給。 */
  margin-top: 0;
}
.goods-rail__track::-webkit-scrollbar { display: none; }

/* 一排五個：扣掉四道 gap 再五等分。minmax(0,…) 的 flex 版＝min-width:0，
   避免長商品名把卡片撐寬（等分格撐爆的同型雷）。 */
.goods-rail__track > .shop-card {
  flex: 0 0 calc((100% - 4 * var(--space-4)) / 5);
  min-width: 0;
  scroll-snap-align: start;
}

/* ── 箭頭 ──────────────────────────────────────────────────────────
   外觀完全沿用 .episode-rail__nav（同頁、同語彙），只在 36-title-detail.css
   的選擇器列表補上 .goods-rail__nav，不另刻一套玻璃配方。 */

/* ════════════════════════════════════════════════════════════════
   商城的作品專頁模式 — shop.html?work=<slug>
   影片頁「更多」的落點：只列該作品的周邊。形式分類 chip 照常運作，作用在
   這個子集上（作品 × 形式 兩軸並存，不是把作品塞進形式分類裡）。
   ════════════════════════════════════════════════════════════════ */
/* 「前往作品頁」用全站既有的 .section-link；這裡只要讓 [hidden] 蓋得掉它的
   display（同 .detail-section 那個慣犯）。 */
.section-link[hidden] { display: none; }

/* 工具列右側群組。.shop-toolbar 是 space-between，左邊是形式 chip；把作品
   下拉與件數收進同一個右側群組，兩者才不會被 space-between 拆開。
   ——這是版面膠水，不是新元件：下拉本體整個用我的片庫既有的 .mylib-sort。 */
.shop-toolbar__right {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex: none;
}
/* .mylib-sort 的唯一變體：它原本是「排序」所以 icon 是上下箭頭；這裡是
   「單選篩選」，換成 chevron。只換遮罩圖，其餘配方完全沿用。 */
.mylib-sort__updown--pick {
  -webkit-mask-image: url('assets/icons/chevron_down.svg');
          mask-image: url('assets/icons/chevron_down.svg');
}

/* ── 手機／窄視窗：五欄放不下就依序收 ─────────────────────────────
   商城家族 RWD 尚未做，這裡先自己收斂。 */
@media (max-width: 1280px) {
  .goods-rail__track > .shop-card { flex-basis: calc((100% - 3 * var(--space-4)) / 4); }
}
@media (max-width: 1024px) {
  .goods-rail__track > .shop-card { flex-basis: calc((100% - 2 * var(--space-4)) / 3); }
}
@media (max-width: 768px) {
  /* 手機：兩個半露出第三張的邊，暗示可以滑 */
  .goods-rail__track > .shop-card { flex-basis: calc((100% - 1.5 * var(--space-4)) / 2.3); }
  .goods-rail__nav { display: none; }   /* 觸控直接滑，不放箭頭 */
}

/* ══════════════════════════════════════════════════════════════════════
   81-auth.css — 登入／註冊（Dolly 2026-08-01）

   一份畫面、兩個容器：
     整頁  login.html 的 [data-zauth-page]（header＋側邊欄照舊，表單置中）
     彈窗  auth.js 注入的 #zauthModal（.ds-modal primitive，功能閘才出現）
   markup 單一來源＝auth.js 的 authHomeHtml()，所以這裡只描述外觀，
   兩個容器共用同一組 .zauth* 樣式，不會分岔。

   入口分流的理由：header 的登入是「瀏覽意圖」，換頁不打斷任何事；
   功能閘（寫影評／買爆米花／租借）是「做到一半被擋」，換頁會弄丟現場。
   ══════════════════════════════════════════════════════════════════════ */

.zauth {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  width: 100%;
}
.zauth__title {
  margin: 0 0 var(--space-2);
  text-align: center;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  line-height: 1.3;
  color: var(--text-primary);
}

/* ── 兩個登入方式：icon 靠左、文字視覺置中（同 .cc-login2__sso 的老配方） ── */
.zauth__ways {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}
.zauth__way {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  padding: 0 var(--space-12);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-tertiary);
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body);
  color: var(--text-primary);
  transition: border-color 150ms var(--ease-out), background-color 150ms var(--ease-out);
}
.zauth__way:hover { border-color: var(--border-default); background: var(--bg-secondary); }
.zauth__way:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.zauth__way-icon {
  position: absolute;
  left: var(--space-5);
  display: inline-flex;
  width: 22px;
  height: 22px;
  color: var(--text-secondary);
}

/* 商標兩種畫法（auth.js 依商標是不是多色選其一，見那邊的註記）：
   .zauth__ico＝單色，mask 吃 currentColor；.zauth__logo＝多色，原圖直出。 */
.zauth__ico {
  display: block;
  width: 100%;
  height: 100%;
  background: currentColor;
  -webkit-mask: var(--zauth-ico) center / contain no-repeat;
  mask: var(--zauth-ico) center / contain no-repeat;
}
.zauth__logo { display: block; width: 100%; height: 100%; object-fit: contain; }

/* ── 社群四方格：只放商標、不放文字（一列排得下四個） ── */
.zauth__socials {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3);
}
.zauth__social {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 56px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-lg);
  background: var(--bg-tertiary);
  cursor: pointer;
  color: var(--text-primary);
  transition: border-color 150ms var(--ease-out), background-color 150ms var(--ease-out);
}
.zauth__social:hover { border-color: var(--border-default); background: var(--bg-secondary); }
.zauth__social:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 2px; }
.zauth__social .zauth__ico,
.zauth__social .zauth__logo { width: 24px; height: 24px; }

.zauth__cta { width: 100%; }

.zauth__redeem {
  margin: 0;
  text-align: center;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.zauth__redeem a { color: var(--brand-500); text-decoration: none; }
.zauth__redeem a:hover { text-decoration: underline; }

.zauth__legal {
  margin: 0;
  text-align: center;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  line-height: 1.6;
  color: var(--text-tertiary);
}

/* ── 彈窗容器：.ds-modal 只需要收一下寬度；內距沿用 primitive 的 --space-8。
   ⚠️ 別寫 var(--space-7)——站上沒有這個 token，整條 padding 會被判無效而歸 0
   （標題會貼到面板頂、撞到關閉鈕）。space 階是 1-6/8/10/12/14/16/20/24/32/40。 */
.zauth-modal .ds-modal__panel {
  max-width: 440px;
  padding-top: var(--space-12);   /* 多留一點，讓置中標題不與右上關閉鈕同高 */
}
.zauth-modal__body { display: block; }

/* ── 整頁容器：置中一欄，寬度對齊彈窗，換頁不換版型 ── */
.zauth-page {
  display: flex;
  justify-content: center;
  padding: var(--space-12) var(--container-padding-x) var(--space-16);
}
.zauth-page__inner { width: min(440px, 100%); }

@media (max-width: 768px) {
  .zauth-page { padding: var(--space-8) var(--space-5) var(--space-12); }
  .zauth__way, .zauth__social { height: 52px; }
}


/* ── 尺寸指南內容層（2026-08-02）：.ref-* 由 refBodyHtml() 產出，商品頁的
   ds-drawer 與選規格面板的推入層共用同一份 markup，過去兩邊都沒有樣式。 */
/* ════════════════════════════════════════════════════════════════
   82-size-guide.css — 尺寸指南「內容層」（.ref-*）

   由 shop-detail-render.js 的 refBodyHtml() 產出，兩個掛載點共用：
     ① 商品頁 → .ds-drawer（桌機右滑出／手機底部）
     ② 選規格面板 → .pdp-sheet__ref 推入層（見 60-pdp-sheet.css）
   內容層本身**從 0729 建成起就沒有任何 CSS**（0802 實測：表格是瀏覽器
   預設、單位切換是系統白底方鈕、`is-you` 高亮完全看不出來）——兩個掛載點
   一起壞，所以樣式寫在這裡而不是任一掛載點的檔案裡。
   ════════════════════════════════════════════════════════════════ */

/* ── 控制列：尺寸標準（國際/US/EU/JP）＋單位（公分/英吋） ── */
.ref-controls {
  display: flex; flex-wrap: wrap; gap: var(--space-2);
  margin-bottom: var(--space-5);
}
.ref-units {
  display: inline-flex; gap: 2px; padding: 3px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
}
.ref-units button {
  padding: 6px 14px; border: 0; border-radius: var(--radius-pill);
  background: none; color: var(--text-tertiary);
  font-family: var(--font-cjk-text); font-size: var(--fs-caption);
  line-height: 1; cursor: pointer;
  transition: background 160ms var(--ease-out), color 160ms var(--ease-out);
}
.ref-units button:hover { color: var(--text-secondary); }
.ref-units button.is-on { background: rgba(255, 255, 255, 0.14); color: var(--text-primary); }

/* ── 尺寸表 ──
   欄位是數字對照，等寬數字才掃得動；「你的尺寸」那列是這一層的存在理由
   （買家點進來就是要找自己那一行），給它橘字＋底色，不是只加個 badge。 */
.ref-table {
  width: 100%; border-collapse: collapse;
  font-size: var(--fs-body-sm); font-variant-numeric: tabular-nums;
}
.ref-table th {
  padding: 0 var(--space-3) var(--space-2) 0; text-align: left;
  font-weight: var(--fw-medium); font-size: var(--fs-caption);
  color: var(--text-tertiary);
  border-bottom: 1px solid var(--border-subtle);
  white-space: nowrap;
}
.ref-table td {
  padding: var(--space-3) var(--space-3) var(--space-3) 0;
  color: var(--text-secondary);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.ref-table th:last-child, .ref-table td:last-child { padding-right: 0; }
.ref-table td:first-child { color: var(--text-primary); font-weight: var(--fw-medium); white-space: nowrap; }
.ref-table tr:last-child td { border-bottom: 0; }
.ref-table tr.is-you td { color: var(--yellow-500); background: rgba(255, 163, 63, 0.07); }
.ref-table tr.is-you td:first-child { color: var(--yellow-500); }
.ref-you {
  display: inline-block; margin-left: var(--space-2); padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: rgba(255, 163, 63, 0.16);
  font-size: 11px; font-weight: var(--fw-medium);
  color: var(--yellow-500); vertical-align: middle;
}

/* ── 段落標題／版型說明／註記 ── */
.ref-sect {
  margin: var(--space-6) 0 var(--space-3);
  font-family: var(--font-cjk-text); font-size: var(--fs-body-sm);
  font-weight: var(--fw-semibold); color: var(--text-primary);
}
.ref-fit {
  margin-top: var(--space-4); padding: var(--space-4);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.04);
  font-size: var(--fs-body-sm); line-height: 1.7; color: var(--text-secondary);
}
.ref-note {
  margin: var(--space-5) 0 0;
  font-size: var(--fs-caption); line-height: 1.6; color: var(--text-tertiary);
}

/* ── 如何測量：示意圖＋步驟 ──
   圖是線稿（stroke: currentColor + 寫死的橘標線），窄面板放不下並排，
   所以窄的時候疊成單欄。 */
.ref-measure { display: flex; gap: var(--space-5); align-items: flex-start; }
.ref-measure__fig { flex: 0 0 132px; color: var(--text-primary); }
.ref-measure__fig svg { width: 100%; height: auto; display: block; }
.ref-measure__steps { margin: 0; min-width: 0; }
.ref-measure__steps dt {
  font-size: var(--fs-body-sm); font-weight: var(--fw-semibold);
  color: var(--text-primary);
}
.ref-measure__steps dd {
  margin: 2px 0 var(--space-4); font-size: var(--fs-caption);
  line-height: 1.6; color: var(--text-tertiary);
}
.ref-measure__steps dd:last-child { margin-bottom: 0; }
@media (max-width: 420px) {
  .ref-measure { flex-direction: column; align-items: center; }
  .ref-measure__fig { flex: none; width: 132px; }
}


/* ── 編輯個人檔案面板＋變更相片彈窗（2026-08-04）：入口是 profile-menu，每一頁
   都有一顆，所以樣式不能再留在 profile-mobile.css（頁面層）。面板 markup 由
   ds.js 隨點隨注入，這支給它排版與外殼皮膚。 */
/* ══ 編輯個人檔案 面板 ＋ 變更相片彈窗 ═══════════════════════════════════
   0804 從 assets/rwd/profile-mobile.css [A6] 搬進元件層。
   搬的理由＝入口變成全站的：profile-menu（頭像下拉）每一頁都有一顆「編輯個人檔案」，
   面板本身由 ds.js 隨點隨注入（EDIT_PROFILE_HTML），所以樣式不能再住在只有
   profile.html 會載的頁面層檔案裡——那是 memory「page-key CSS 害新頁裸奔」的同一個病。
   ⚠️ 這支住在 css/components/ → url() 基準是 repo root（assets/…），不是 ../icons/。
      （本段目前沒有 url()，新增時記得。）

   面板骨架＝ds-drawer primitive（55-ds-drawer.css ＋ assets/ds-drawer.js）：滑入／scrim／
   Esc／捲動鎖／focus trap 都是它的，這裡只給內容排版與外殼皮膚。
   ══════════════════════════════════════════════════════════════════ */

/* 人物雙套（0803 個人頁的 data-persona 機制）。面板現在住在每一頁，但
   body[data-profile-role] 只有個人頁的情境展示面板會寫——沒有那個屬性的頁面
   剛好命中 :not()，顯示影迷那份（＝profile-menu 頭上寫的「電影雷達丹丹」）。
   規則跟 profile-mobile.css:170 那組同語意，但收斂在面板／彈窗內，不擴大成全域。 */
body[data-profile-role^="creator"] .edit-profile [data-persona="default"],
body[data-profile-role^="creator"] .avatar-pick  [data-persona="default"] { display: none; }
body:not([data-profile-role^="creator"]) .edit-profile [data-persona="creator"],
body:not([data-profile-role^="creator"]) .avatar-pick  [data-persona="creator"] { display: none; }

/* 面板開著時收掉「情境展示」膠囊（profile.html 頁面自帶的 dev 元件）。
   桌機它固定在右下角、正好壓在 440 寬的面板上；手機面板是滿版底部 sheet，
   膠囊會浮在內容上——0805 實測擋住平台清單最後一項（Tiktok）。
   做法照 profile-mobile.css 既有的 `body.menu-open .wp-roomdev{display:none}`。
   放元件層而不是頁面層：面板現在每一頁都能開，之後哪一頁擺了 dev 膠囊都自動涵蓋。 */
body:has(.edit-profile.ds-drawer.is-open) .wp-roomdev[data-profiledemo],
body:has(.edit-profile.ds-drawer.is-open) .wp-roomdev[data-shop-devbar],
body:has(.edit-profile.ds-drawer.is-open) .dev-toggle { display: none; }

/* 面板外殼對齊購物車抽屜（0803 Dolly：「側邊的線要符合購物車樣式」「上方應該沒有線」）。
   ⚠️ 那條頂緣亮線不是誰多畫的，是 primitive 的 box-shadow:var(--glass-shadow) 裡的
   `inset 0 1px 0.5px rgba(225,238,255,.30)`——玻璃材質的頂緣高光。購物車面板用的是
   純側向投影，所以整條 box-shadow 換成它那組，高光跟著消失。
   側邊框：primitive 是 --border-strong（白 22%，偏亮），購物車是 --border-default。
   ・不抄購物車的 backdrop-filter：它底色已經 98% 不透明，模糊看不出來，卻會讓面板
     變成 fixed 的 containing block（同 memory「backdrop-filter 綁架 fixed」）。 */
.edit-profile .ds-drawer__panel {
  background: rgba(14, 14, 14, 0.98);
  border-left: 1px solid var(--border-default);
  box-shadow: -28px 0 70px rgba(0, 0, 0, 0.6);
}
/* 標題規格＝.ztor-cart__title（0803 Dolly：字級要一致）；連 head 的內距也照抄，
   不然標題的左緣會跟購物車差 4px。 */
.edit-profile .ds-drawer__head { padding: var(--space-6) var(--space-6) var(--space-4); }
.edit-profile__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: 22px;
  line-height: 1.3;
  letter-spacing: -0.01em;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.edit-profile .ds-drawer__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  padding: var(--space-6);   /* 與 head 同內距，左緣才對齊 */
}
/* 頭像列＝1.0 的那張卡：頭像｜名字（吃掉剩餘寬）｜變更相片 */
.edit-profile__avatar-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-card);
}
/* 元件層的 .profile-head__avatar 是頁首那顆的尺寸；面板裡縮成 56。用同一個 class
   是為了帶著 --photo 變體那組「真人照滿版」的覆寫，不必在這裡重寫一次。 */
.edit-profile__avatar { width: 56px; height: 56px; flex: 0 0 auto; }
.edit-profile__avatar-name {
  flex: 1;
  min-width: 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body, 15px);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.edit-profile__avatar-row .btn { flex: 0 0 auto; }
/* 自介：cc-field__input 是固定 44px 高的單行框，多行要把高度交回內容 */
.edit-profile__textarea {
  height: auto;
  min-height: 120px;
  padding: var(--space-3);
  line-height: 1.6;
  resize: vertical;
}
/* 段落標題（社群連結）。上緣多給一段，讓它跟上面的自介分成兩塊而不是一路排下來。 */
.edit-profile__section {
  margin: var(--space-2) 0 0;
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body, 15px);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}

/* ── 社群連結：選了才長出來（0805 Dolly）────────────────────────────────
   0803 那版是 8 個常駐 .cc-field，面板下半部整個都在講一件很少人會全填的事。
   現在預設一列都沒有，「＋ 新增社群連結」展開平台清單、選一個才長一列。 */
.edit-profile__socials { display: flex; flex-direction: column; gap: var(--space-4); }
/* 一列都沒有時連 gap 都不要參與版面（父層 .ds-drawer__body 也有 gap） */
.edit-profile__socials:empty { display: none; }
/* 平台名｜垃圾桶：.cc-field 的第一個子節點原本是 label，這裡換成一整列 */
.edit-profile__social-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}
.edit-profile__social-del {
  flex: 0 0 auto;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 0;
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 150ms var(--ease-out), background 150ms var(--ease-out);
}
/* ⚠️ 這支住在 css/components/ → url() 基準是 repo root，不是 ../icons/ */
.edit-profile__social-del::before {
  content: '';
  display: block;
  width: 16px;
  height: 16px;
  margin: 0 auto;
  background: currentColor;
  -webkit-mask: url('assets/icons/trash.svg') center / contain no-repeat;
          mask: url('assets/icons/trash.svg') center / contain no-repeat;
}
.edit-profile__social-del:hover { color: var(--error-500); background: var(--white-alpha-8); }

.edit-profile__addwrap { display: flex; flex-direction: column; gap: var(--space-3); }
/* 空狀態那句只在一列都沒有時出現——狀態由 CSS 反應（:empty），不靠 JS 開關 display */
.edit-profile__socials-hint {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.edit-profile__socials:not(:empty) + .edit-profile__addwrap .edit-profile__socials-hint { display: none; }

/* 虛線框＝「這裡還可以再加」的通用語彙；實心鈕會跟底部的「儲存」搶主次 */
.edit-profile__add {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  width: 100%;
  padding: 13px var(--space-4);
  border: 1px dashed var(--border-default);
  border-radius: var(--radius-md);
  background: transparent;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 150ms var(--ease-out), border-color 150ms var(--ease-out), background 150ms var(--ease-out);
}
.edit-profile__add:hover {
  border-color: var(--brand-500);
  color: var(--brand-500);
  background: var(--white-alpha-4, rgba(255, 255, 255, 0.04));
}
.edit-profile__add[hidden] { display: none; }   /* 八個平台都加完就整顆收掉 */
.edit-profile__add-ic {
  flex: 0 0 auto;
  width: 14px;
  height: 14px;
  background: currentColor;
  -webkit-mask: url('assets/icons/plus.svg') center / contain no-repeat;
          mask: url('assets/icons/plus.svg') center / contain no-repeat;
}
/* 平台清單：就地展開（不絕對定位）——這裡在 .ds-drawer__body 的捲動容器裡，
   浮層會被裁掉；就地展開也讓桌機手機同一套，不必再刻一個底部 sheet。 */
.edit-profile__picker {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: var(--bg-card);
  overflow: hidden;
}
.edit-profile__picker[hidden] { display: none; }
.edit-profile__pick {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3) var(--space-4);
  border: 0;
  border-top: 1px solid var(--border-subtle);
  background: transparent;
  text-align: left;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-primary);
  cursor: pointer;
  transition: background 150ms var(--ease-out);
}
/* 平台圖示：--sicon 由 12-profile-header.css 的 .profile-social--<k> 給（ds.js 把那個
   class 一起掛上來）。這裡只負責「畫多大」——圖檔本身是單一來源，換圖改那邊就好。
   ⚠️ 圖示欄固定 24 寬置中：八個標記的實際比例不一（小紅書是 3:1 字標），不固定欄寬
      的話每一列的文字起點都會差幾 px。 */
.edit-profile__pick::before {
  content: '';
  flex: 0 0 24px;
  /* 明寫 width／height 蓋掉 12-profile-header.css 給 --xhs 的 26×20（那是頭部圓鈕
     的尺寸）。同權重、這支載入序在後 → 這裡贏；不要只靠 flex-basis 壓 width，太隱晦。 */
  width: 24px;
  height: 18px;
  background-color: currentColor;
  -webkit-mask: var(--sicon) center / contain no-repeat;
          mask: var(--sicon) center / contain no-repeat;
  opacity: 0.75;
}
.edit-profile__pick:hover::before { opacity: 1; }
.edit-profile__pick:first-child { border-top: 0; }
.edit-profile__pick:hover { background: var(--white-alpha-8); }
/* 底部動作條。primitive 的 __foot 只有 flex:0 0 auto（.ds-actionbar 那支檔沒被移植
   進 f2e），內距／分隔線／滿版鈕都要消費者自己給。 */
.edit-profile__foot {
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--border-subtle);
  background: var(--bg-secondary);
}
.edit-profile__foot .btn { width: 100%; }

/* ── 變更相片彈窗（0803 Dolly：「換大頭照也一起做」）──────────────────────
   照 1.0 那張稿：大頭照大圖預覽 ＋ 一張兩列的清單卡（上傳相片／移除目前的大頭貼照，
   後者紅字）。走通用 .ds-modal primitive（z-index 1000 > 抽屜的 300，所以會蓋在
   面板上而不是被壓在下面），掛 <body> 直屬層。 */
.avatar-pick .ds-modal__title { margin: 0 0 var(--space-5); }
/* 兩個 view 共用同一個 panel（0805）。原生 [hidden] 在這裡就夠，但明寫一條免得
   之後有人給 .avatar-pick__view 一個 display 就把它蓋掉（memory「[hidden] 被 display 蓋」）。 */
.avatar-pick__view[hidden] { display: none; }

/* 只有一張圓形大頭照預覽（0805 Dolly 退回並排的方形「商店進店圖」：不是所有用戶
   都會有商店，不該在人人都走的改頭像流程裡預設一個少數人才有的用途）。 */
.avatar-pick__preview {
  width: 200px;
  height: 200px;
  margin: 0 auto var(--space-5);
  border-radius: var(--radius-pill);
  overflow: hidden;
  background: var(--bg-tertiary);
}
.avatar-pick__preview img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* 佔位剪影（還沒設過大頭照）：元件層那顆是 54%＋opacity .5，這裡照同一個語彙 */
.avatar-pick__preview--empty img {
  width: 54%; height: 54%; margin: 23%; opacity: 0.5; object-fit: contain;
}

/* 錯誤訊息（0805 起是這裡唯一的說明文字——常駐的建議規格那行已依 Dolly 指示整條
   移除，規格改由 <input accept> 與這行「選錯了才說」承擔）。
   只在 hidden 拿掉時佔位，所以間距寫在它自己身上，不動預覽與清單的既有間距。 */
.avatar-pick__err {
  margin: 0 0 var(--space-3);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  line-height: 1.5;
  color: var(--error-500);
  text-align: center;
}
.avatar-pick__err[hidden] { display: none; }

/* ── 裁剪對位（0805）────────────────────────────────────────────────
   裁剪框本身就是「會被裁下來的方形」＝商店進店圖；上面疊一個圓形安全區指示
   大頭照切到哪。一張圖兩個去處，用一個框講完，不做兩次裁剪。 */
.avatar-crop__stage {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  max-width: 300px;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--neutral-900, #0a0a0a);
  cursor: grab;
  touch-action: none;             /* 自己吃拖曳／雙指縮放，不讓瀏覽器先捲走 */
  user-select: none;
}
.avatar-crop__stage:active { cursor: grabbing; }
.avatar-crop__img {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  max-width: none;                /* 站上圖片預設 max-width:100%，會壓掉放大 */
  -webkit-user-drag: none;
  user-select: none;
  pointer-events: none;           /* 拖曳事件一律由 stage 收，不被 img 的原生拖曳搶走 */
}
/* 圓框：外圍那圈超大 box-shadow 把方框內、圓框外的區域壓暗一階——
   壓暗代表「不在大頭照裡」，不是「會被裁掉」，所以只壓 .3 不壓死。 */
.avatar-crop__mask {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, 0.85);
  box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.32);
  pointer-events: none;
}
.avatar-crop__hint {
  margin: var(--space-4) 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  line-height: 1.6;
  color: var(--text-tertiary);
  text-align: center;
}
.avatar-crop__zoom {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-4) 0 var(--space-5);
  color: var(--text-tertiary);
}
/* − / ＋ 用偽元素畫：站上只有 plus.svg，沒有 minus，畫兩條線比多存一個檔省事 */
.avatar-crop__zoom-ic { position: relative; flex: 0 0 auto; width: 14px; height: 14px; }
.avatar-crop__zoom-ic::before {
  content: '';
  position: absolute;
  left: 1px; right: 1px; top: 50%;
  height: 1.5px; margin-top: -0.75px;
  border-radius: 1px;
  background: currentColor;
}
.avatar-crop__zoom-ic--in::after {
  content: '';
  position: absolute;
  top: 1px; bottom: 1px; left: 50%;
  width: 1.5px; margin-left: -0.75px;
  border-radius: 1px;
  background: currentColor;
}
/* 滑桿規格沿用播放器音量條（.player__vol-range），不另造一套 */
.avatar-crop__range {
  flex: 1;
  min-width: 0;
  height: 3px;
  -webkit-appearance: none;
  appearance: none;
  border-radius: var(--radius-pill);
  background: var(--border-default);
  outline: none;
  cursor: pointer;
}
.avatar-crop__range::-webkit-slider-thumb {
  -webkit-appearance: none;
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--text-primary); cursor: pointer;
}
.avatar-crop__range::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%; border: 0;
  background: var(--text-primary); cursor: pointer;
}
.avatar-crop__acts { display: flex; gap: var(--space-3); }
.avatar-crop__acts .btn { flex: 1; }
.avatar-pick__list {
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  overflow: hidden;
}
.avatar-pick__act {
  display: block;
  width: 100%;
  padding: var(--space-4) var(--space-5);
  border: 0;
  background: transparent;
  text-align: left;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body, 15px);
  color: var(--text-primary);
  cursor: pointer;
  transition: background 150ms var(--ease-out);
}
.avatar-pick__act:hover { background: var(--white-alpha-8); }
/* 分隔線掛在第二列的上緣，不用 .a + .a——這裡兩列都是固定的，但保持跟站上其他
   清單同一種寫法（相鄰選擇器遇到雙套 markup 會挑錯，見 memory）。 */
.avatar-pick__act--danger { border-top: 1px solid var(--border-subtle); color: var(--error-500); }

/* ── 貼文報告（post-report.html；0806 Dolly：1.0「績效報告」改名＋2.0 皮）──
   表格寬過視窗 → .preport-wrap 自帶 overflow-x:auto（頁面本體不橫捲）。
   欄位說明抽屜殼＝ds-drawer primitive，這裡只放內容排版。 */

/* 控制列（0805 Dolly 定案）：搜尋左；右側＝cal-picker 日期＋白玻璃匯出鈕。
   ⓘ 欄位說明住 page-head 右側 info 槽（同 cocreate「瞭解共創計畫」）——
   曾試過放日期旁（太近像同一組）與搜尋框下方（跟搜尋無關、懸空），定頁首。 */
.preport-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
}
/* ⓘ 文字鈕＝page-head__info 的外觀掛在 button 上，只重置 UA 盒模型
   （字體/顏色/hover 由 .page-head__info 供給） */
.preport-guide-link {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}
.preport-search {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 0 1 420px;
  min-width: 260px;
  height: 44px;
  padding: 0 18px;
  border-radius: 999px;
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
  color: var(--text-tertiary);
}
.preport-search svg { width: 18px; height: 18px; flex: 0 0 auto; }
.preport-search input {
  flex: 1;
  min-width: 0;
  background: none;
  border: none;
  outline: none;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-primary);
}
.preport-search input::placeholder { color: var(--text-tertiary); }
.preport-bar__right {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
/* 日期＝cal-picker（85-cal-picker.css）。trigger 在 bar 右側、貼著視窗右緣，
   popover 改右對齊 trigger 才不會捅出容器外。 */
.preport-bar__right .cal__pop { left: auto; right: 0; }

/* 表格：橫向可捲 */
.preport-wrap {
  overflow-x: auto;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
  background: var(--bg-secondary);
}
.preport {
  width: 100%;
  min-width: 1240px;   /* 10 欄的自然寬，窄視窗在 wrap 裡捲 */
  /* separate 不用 collapse（0806 首欄凍結）：collapse 的框線畫在 row 上，
     凍結欄捲動時框線不跟著 sticky 走；framework＝框線搬到 cell 上 */
  border-collapse: separate;
  border-spacing: 0;
}
.preport__th {
  padding: 14px 16px;
  background: var(--bg-tertiary);
  border-bottom: 1px solid var(--border-subtle);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  color: var(--text-tertiary);
  text-align: left;
  white-space: nowrap;
}
.preport__th--sort button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
}
.preport__th--sort.is-on button { color: var(--text-primary); }
/* 排序 icon（0805 Dolly：換 sorting-toggle.svg）——mask 吃 currentColor，
   跟表頭字色一起亮暗 */
.preport-sort-ic {
  width: 12px;
  height: 12px;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask: url('assets/icons/sorting-toggle.svg') center / contain no-repeat;
  mask: url('assets/icons/sorting-toggle.svg') center / contain no-repeat;
}
.preport__row:hover { background: rgba(255, 255, 255, 0.03); }
.preport__td {
  padding: 16px;
  /* 框線在 cell 不在 row（border-collapse:separate 之後 row 的框線不會渲染） */
  border-bottom: 1px solid var(--border-subtle);
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-secondary);
  vertical-align: middle;
  white-space: nowrap;
}
.preport__row:last-child .preport__td { border-bottom: none; }
.preport__td--title {
  min-width: 260px;
  max-width: 320px;
  white-space: normal;
  color: var(--text-primary);
  line-height: 1.55;
}
/* 兩行截斷搬進內層 span——clamp 需要 overflow:hidden，放在 td 上會把
   凍結欄的右緣陰影（掛在 td 外側）一起裁掉 */
.preport__title-clamp {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}

/* ── 首欄凍結（0806 Dolly：橫捲看更多時「貼文標題」欄固定在左緣）──
   底色必須不透明（neutral token），否則其他欄會從底下透出來 */
.preport__th:first-child,
.preport__td--title {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--bg-secondary);
}
.preport__th:first-child { z-index: 3; background: var(--bg-tertiary); }
/* 列 hover 的 3% 白疊在 row 上、會被凍結欄的不透明底蓋掉——用同值合成補回 */
.preport__row:hover .preport__td--title {
  background:
    linear-gradient(rgba(255, 255, 255, 0.03), rgba(255, 255, 255, 0.03)),
    var(--bg-secondary);
}
/* 右緣陰影只在真的捲出去時出現（.is-x-scrolled 由 post-report.js 反應 scrollLeft） */
.preport__th:first-child::after,
.preport__td--title::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: -12px;
  width: 12px;
  background: linear-gradient(90deg, rgba(0, 0, 0, 0.35), transparent);
  opacity: 0;
  transition: opacity 160ms ease;
  pointer-events: none;
}
.preport-wrap.is-x-scrolled .preport__th:first-child::after,
.preport-wrap.is-x-scrolled .preport__td--title::after { opacity: 1; }
.preport__td--film { max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
.preport__td--earn { color: var(--success-500); font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; }
/* 0805 Dolly：獲利以外的數字欄（建立日期＋五個 metric）縮一階 13px——
   Satoshi 數字 14px 視覺上仍偏大，且與中文欄拉不開層級 */
.preport__td--num { font-variant-numeric: tabular-nums; font-size: 13px; }
.preport__empty { text-align: center; color: var(--text-tertiary); padding: 40px 16px; }

/* 貼文形式 chip：圖文=中性、影片=紅（照 1.0 的紅標語意） */
.preport-form {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  background: rgba(255, 255, 255, 0.06);
  font-size: 12px;
  color: var(--text-secondary);
  white-space: nowrap;
}
.preport-form--video {
  background: rgba(239, 68, 68, 0.14);
  color: #f87171;
}
/* 形式 icon（0805 Dolly）：圖文=text.svg、影片=video-outline.svg——
   mask 吃 currentColor，跟 chip 字色走（影片自動變紅） */
.preport-form__ic {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask: url('assets/icons/text.svg') center / contain no-repeat;
  mask: url('assets/icons/text.svg') center / contain no-repeat;
}
.preport-form--video .preport-form__ic {
  -webkit-mask-image: url('assets/icons/video-outline.svg');
  mask-image: url('assets/icons/video-outline.svg');
}

/* 分頁列（視覺示意） */
.preport-pages {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 8px;
  margin-top: var(--space-6);
}
.preport-page {
  min-width: 36px;
  height: 36px;
  padding: 0 8px;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-subtle);
  background: none;
  color: var(--text-tertiary);
  font-family: var(--font-latin);
  font-size: 14px;
  cursor: pointer;
}
.preport-page:hover:not(:disabled):not(.preport-page--gap) { color: var(--text-primary); border-color: var(--border-default); }
.preport-page.is-on { background: rgba(var(--brand-rgb), 0.14); border-color: rgba(var(--brand-rgb), 0.4); color: var(--yellow-500); }
.preport-page:disabled { opacity: 0.35; cursor: default; }
.preport-page--gap { border: none; cursor: default; }

/* ── 明細展開（0806 Dolly：1.0 的提及內容/獲利展開換 2.0 皮）──
   表格內的展開箭頭：提及 >1 部才出現、獲利每列都有 */
.preport-more {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-left: 4px;
  vertical-align: middle;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text-tertiary);
  transition: color 150ms ease;
}
.preport-more::before {
  content: "";
  width: 14px;
  height: 14px;
  background: currentColor;
  -webkit-mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
  mask: url('assets/icons/chevron_right.svg') center / contain no-repeat;
}
.preport-more:hover { color: var(--text-primary); }

/* 明細彈窗（殼＝ds-modal，這裡只排內容）。
   0806 Dolly 二修：✕ 對齊標題行（primitive 預設吸在 16/16，跟 32 內距的
   標題差半格）；表頭分隔線加深＋上下間距拉開。 */
.preport-modal .ds-modal__close {
  top: var(--space-8);
  right: var(--space-8);
}
/* h2 的 UA margin 會把標題往下推 20px，跟 ✕ 錯開——四邊寫定。
   下緣 32＝標題跟內容拉開（0806 三修；跟表格/總獲利卡自帶的 20 收合取大） */
.preport-modal .ds-modal__title { margin: 0 0 var(--space-8); }
.preport-modal .prm {
  width: 100%;
  border-collapse: collapse;
  margin-top: var(--space-5);
}
.prm__th {
  padding: 10px 4px 16px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-regular);
  color: var(--text-tertiary);
  text-align: left;
  /* 0806 三修：分隔線要淡＝白 8%（介於 subtle 4% 與 default 12% 之間） */
  border-bottom: 1px solid var(--white-alpha-8);
  white-space: nowrap;
}
.prm__td {
  padding: 14px 4px;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-primary);
}
.prm tbody tr:first-child .prm__td { padding-top: 18px; }
.prm__th--num,
.prm__td--num { text-align: right; font-variant-numeric: tabular-nums; }
.prm__th--q,
.prm__td--q { text-align: center; }
.prm__td--q { color: var(--text-secondary); font-variant-numeric: tabular-nums; }
.prm__td--total { font-weight: var(--fw-bold); }
/* 畫質標籤（4K/1080P/720P）＝亮底墨字小膠囊，照 1.0 語彙 */
.prm__q {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-sm);
  background: var(--neutral-100);
  color: var(--neutral-900);
  font-family: var(--font-latin);
  font-size: 12px;
  font-weight: var(--fw-bold);
  line-height: 1.4;
}
/* 總獲利卡：label 左、綠色金額右 */
.preport-modal__total {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-top: var(--space-5);
  padding: 20px 24px;
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-subtle);
  font-family: var(--font-cjk-text);
  font-size: 15px;
  color: var(--text-primary);
}
.preport-modal__total strong {
  font-size: 20px;
  font-weight: var(--fw-bold);
  color: var(--success-500);
  font-variant-numeric: tabular-nums;
}

/* 欄位說明抽屜內容 */
.preport-guide__body { padding: 8px 24px 32px; overflow-y: auto; }
.preport-guide__item { margin-top: 24px; }
.preport-guide__item h3 {
  margin: 0 0 10px;
  font-family: var(--font-cjk-text);
  font-size: 15px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.preport-guide__item p {
  margin: 0;
  padding: 14px 16px;
  border-radius: var(--radius-lg);
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid var(--border-subtle);
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-secondary);
}


/* ── cal-picker 日期區段選擇器（2026-08-05 自共創線移植）：行為在 ds.js
   DS.register('cal-picker')，頁面只放 [data-cal] trigger。首個消費者＝貼文報告。 */
/* ════════════════════════════════════════════════════════════
   cal-picker（0805 自共創線移植；來源＝花花 feature/cocreate-waitlist
   的 components.css，樣式逐字搬、只加 .cal__chev——staging 沒有她那顆
   support-row__chevron，trigger 的下箭頭改用現成 chevron_down.svg mask）。

   Trigger button (btn--ghost btn--sm) + floating popover: preset column
   + month grid with range selection + 重置/確認. Behaviour is the
   ds.js `cal-picker` registration, which GENERATES the popover DOM
   — pages only ship the trigger button inside [data-cal].
   Demo-level: picking a range relabels the trigger; tables keep
   their static rows. Every colour is a token; the amber range
   endpoints reuse --yellow-500 with ink text.
   ════════════════════════════════════════════════════════════ */
.cal {
  position: relative;
  display: inline-flex;
}
.cal > .btn { gap: 6px; }
.cal__chev {
  width: 16px;
  height: 16px;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
  mask: url('assets/icons/chevron_down.svg') center / contain no-repeat;
  transition: transform 160ms ease;
}
[data-cal-btn][aria-expanded="true"] .cal__chev { transform: rotate(180deg); }
.cal__pop {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  z-index: 70;
  display: flex;
  gap: var(--space-5);
  width: max-content;
  max-width: min(92vw, 480px);
  padding: var(--space-5);
  background: var(--bg-secondary);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5);
}
.cal__pop[hidden] { display: none; }

/* preset column */
.cal__presets {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  min-width: 72px;
}
.cal__preset {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  cursor: pointer;
  white-space: nowrap;
}
.cal__preset:hover { color: var(--text-primary); }
.cal__preset.is-active { color: var(--yellow-500); }

/* calendar column */
.cal__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.cal__head {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-bottom: var(--space-1);
}
.cal__month {
  flex: 1;
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--yellow-500);
}
.cal__nav {
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--text-secondary);
  font-size: var(--fs-body);
  cursor: pointer;
}
.cal__nav:hover {
  background: var(--white-alpha-8);
  color: var(--text-primary);
}
.cal__dows,
.cal__grid {
  display: grid;
  grid-template-columns: repeat(7, 34px);
}
.cal__grid { row-gap: 2px; }
.cal__dow {
  height: 28px;
  display: grid;
  place-items: center;
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.cal__day {
  position: relative;
  height: 34px;
  display: grid;
  place-items: center;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--fs-body-sm);
  font-variant-numeric: tabular-nums;
  color: var(--text-primary);
  cursor: pointer;
}
/* weekend columns read amber (v1 look) — muted spill days stay muted */
.cal__day:nth-child(7n + 6):not(.is-muted):not(.is-start):not(.is-end),
.cal__day:nth-child(7n):not(.is-muted):not(.is-start):not(.is-end) { color: var(--yellow-500); }
.cal__day.is-muted { color: var(--text-tertiary); }
.cal__day:hover::after {
  content: "";
  position: absolute;
  inset: 1px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
}
/* range bar — square middle, rounded caps; endpoints are amber discs */
.cal__day.is-range { background: var(--white-alpha-8); }
.cal__day.is-range-start { border-radius: var(--radius-pill) 0 0 var(--radius-pill); }
.cal__day.is-range-end { border-radius: 0 var(--radius-pill) var(--radius-pill) 0; }
.cal__day.is-range-start.is-range-end { border-radius: var(--radius-pill); }
.cal__day.is-start,
.cal__day.is-end { color: var(--neutral-900); font-weight: var(--fw-bold); }
.cal__day.is-start::before,
.cal__day.is-end::before {
  content: "";
  position: absolute;
  inset: 1px;
  border-radius: var(--radius-pill);
  background: var(--yellow-500);
  z-index: -1;
}
.cal__day.is-start,
.cal__day.is-end { z-index: 0; }

/* footer actions — text buttons per the v1 shot */
.cal__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-6);
  margin-top: var(--space-2);
}
.cal__reset,
.cal__confirm {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  cursor: pointer;
}
.cal__reset { color: var(--error-500); }
.cal__confirm { color: var(--text-primary); }
.cal__reset:hover,
.cal__confirm:hover { text-decoration: underline; }

/* sm: the popover stacks — presets become a wrap row on top */
@media (max-width: 600px) {
  .cal__pop { flex-direction: column; gap: var(--space-4); }
  .cal__presets {
    flex-direction: row;
    flex-wrap: wrap;
    gap: var(--space-3) var(--space-4);
    min-width: 0;
  }
}


/* ── 兌換中心（2026-08-06）：1.0 架構換 2.0 皮；輸入列＋兌換成功彈窗排版。 */
/* ── 兌換中心（redeem-center.html；0806 Dolly：1.0 架構換 2.0 皮）──
   單欄表單頁：輸入列（含掃描入口）＋兌換 CTA＋來源/步驟說明。
   兌換成功彈窗殼＝ds-modal，tick＝ds-success-tick（57），這裡只給
   綠盤與置中排版。 */

/* 0806 Dolly：欄位置中（720 欄在 wide container 裡靠左會讓右半邊全空） */
.redeem { max-width: 720px; margin-inline: auto; }

.redeem__label {
  display: block;
  margin: 0 0 14px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}

/* 輸入列：pill 場＋右側掃描 icon（同站上輸入場語彙：bg-tertiary＋subtle 框） */
.redeem__field {
  display: flex;
  align-items: center;
  gap: 8px;
  height: 56px;
  padding: 0 8px 0 20px;
  border-radius: var(--radius-lg);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
  transition: border-color 150ms ease;
}
.redeem__field:focus-within { border-color: var(--border-strong); }
.redeem__input {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  outline: none;
  font-family: var(--font-cjk-text);
  font-size: 15px;
  color: var(--text-primary);
}
.redeem__input::placeholder { color: var(--text-tertiary); }
.redeem__scan {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  flex: 0 0 auto;
  background: none;
  border: 0;
  padding: 0;
  border-radius: var(--radius-md);
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 150ms ease, background 150ms ease;
}
.redeem__scan:hover { color: var(--text-primary); background: var(--white-alpha-8); }
/* 0806 Dolly：icon 用官方 scan-qrcode.svg（mask 吃 currentColor） */
.redeem__scan-ic {
  width: 22px;
  height: 22px;
  background: currentColor;
  -webkit-mask: url('assets/icons/scan-qrcode.svg') center / contain no-repeat;
  mask: url('assets/icons/scan-qrcode.svg') center / contain no-repeat;
}

.redeem__submit { width: 100%; margin-top: 16px; }

/* 說明區 */
.redeem__info { margin-top: 48px; }
.redeem__h {
  margin: 32px 0 12px;
  font-family: var(--font-cjk-display);
  font-size: 18px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.redeem__info .redeem__h:first-child { margin-top: 0; }
.redeem__p {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: 15px;
  line-height: 1.8;
  color: var(--text-secondary);
}
.redeem__steps {
  margin: 0;
  padding-left: 22px;
  font-family: var(--font-cjk-text);
  font-size: 15px;
  line-height: 2;
  color: var(--text-secondary);
}
.redeem__closing { margin-top: 24px; }

/* 兌換成功彈窗：內容置中＋實心綠盤深勾（0729 成功打勾定版） */
.redeem-ok .ds-modal__panel { text-align: center; }
.redeem-ok__disc {
  width: 56px;
  height: 56px;
  margin: 8px auto 0;
  border-radius: var(--radius-pill);
  background: var(--success-500);
  color: var(--neutral-900);   /* 勾走 currentColor＝深勾 */
  display: grid;
  place-items: center;
}
.redeem-ok__disc .ds-success-tick {
  width: 100%;
  height: 100%;
  display: grid;
  place-items: center;
}
.redeem-ok__title { margin-top: 20px; }
.redeem-ok__body strong { color: var(--text-primary); font-weight: var(--fw-medium); }


/* ── 展開貼文彈窗（2026-08-06）：點 .post-card 開詳情 popup（1.0 review 頁
   換皮）；markup/行為都在 ds.js（INJECTABLE_MODALS.postPop＋post-open）。 */
/* ── 展開貼文彈窗 postpop（0806 二版：照 Figma Desktop v4 node 1777-22633）──
   markup＝ds.js INJECTABLE_MODALS.postPop、行為＝ds.js post-open。
   版型：**固定高度單一浮卡**——面板上方留 40px、貼齊視窗底（上圓角）、
   ✕ 在面板背景右上；左=內文區自己捲（overflow 在 .postpop__main）、
   右欄 348 滿高不捲走、留言列表 fill 剩餘高度內捲、輸入列固定在底。
   ⚠️ Figma 稿是 1.0 設計系統——顏色/字體全部映射 2.0 token：
   金 #BB936B→brand 橘（追蹤鈕=btn--yellow-ghost）、#28282B 框線→border token、
   Noto/Proxima→站上字體堆疊。 */

/* 殼：panel＝唯一的彈窗背景（bg/border/陰影沿用 ds.css 面板預設） */
.ds-modal.postpop {
  z-index: 2100;               /* 搜尋 overlay（z 2000）裡也能點貼文開窗 */
  padding: 40px 48px 0;        /* 上方留 40、底貼齊視窗；側 48=✕（32＋間距 12）要住得下 */
  align-items: stretch;
  overflow: hidden;
}
.ds-modal.postpop .ds-modal__panel {
  max-width: 1264px;           /* 1200 內容＋左右各 32 內距 */
  width: 100%;
  height: 100%;
  max-height: none;
  margin: 0 auto;
  padding: 0;
  overflow: visible;           /* 捲動交給 __main／__comments；✕ 掛面板外緣不能被裁 */
  border-radius: var(--radius-xl) var(--radius-xl) 0 0;
}
/* ✕ 在彈窗外右側——頂緣與面板切齊、與面板右緣留 12（整顆都在面板外的深色區） */
.ds-modal.postpop .ds-modal__close {
  position: absolute;
  top: 0;
  right: -44px;                /* 32 鈕＋12 間距 */
  z-index: 2;
}
/* ds.css 的 .ds-modal .btn 高度鎖 50——把 sm 鈕（追蹤/查看影片資訊）還原成 36 */
.ds-modal.postpop .btn.btn--sm { height: 36px; }

.postpop__wrap {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 348px;
  gap: 32px;
  align-items: stretch;
  height: 100%;
  padding-right: 32px;         /* 右欄離面板右緣；左欄內距在 __main 裡跟著捲 */
}

/* ── 左：內文捲動區（面板已是背景，這裡不再自帶卡皮） ── */
.postpop__main {
  min-width: 0;
  min-height: 0;
  overflow-y: auto;
  padding: 40px 32px 48px;
}
.postpop__title {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: 26px;
  font-weight: var(--fw-bold);
  line-height: 1.5;
  color: var(--text-primary);
}
/* 日期左、動作組右（icon＋數字直排，Figma Icons 168 寬四件組） */
.postpop__meta {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
}
.postpop__date {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  line-height: 30px;   /* 跟 30px icon 的第一行對齊 */
}
.postpop__acts {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
.postpop__act {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  min-width: 30px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text-secondary);
  transition: color 150ms ease;
}
/* :not(.is-active)：同權重且 87 排在 54 後，裸 hover 會蓋掉 like/save 的 rose/橘 */
.postpop__act:hover:not(.is-active) { color: var(--text-primary); }
.postpop__act .post-card__stat-icon { width: 24px; height: 24px; }
.postpop__act .ds-icon.ds-icon { width: 24px; height: 24px; }
/* 讚/收藏 icon＝inline --ico mask（照影片頁掛法）——全站 like/save 特效的
   實心切換（ds.js 換 var）與彈跳（54 檔 [style*="--ico"]）都認這個 */
.postpop__act-ic {
  width: 24px;
  height: 24px;
  display: inline-block;
  flex-shrink: 0;
  background: currentColor;
  -webkit-mask: var(--ico) center / contain no-repeat;
          mask: var(--ico) center / contain no-repeat;
}
.postpop__act-ic--sm { width: 20px; height: 20px; }
.postpop__act-n {
  font-family: var(--font-latin);
  font-size: 12px;
  color: var(--text-tertiary);
}

/* 提及影片條（Figma Metion 68 高：海報 28×40／名稱＋★評分／右=價格徽章） */
.postpop__mention {
  display: flex;
  align-items: center;
  gap: 24px;
  margin-top: 16px;
  padding: 8px 12px;
  border-radius: var(--radius-md);
  background: var(--bg-tertiary);
  border: 1px solid var(--border-subtle);
  text-decoration: none;
  transition: background 150ms ease;
}
.postpop__mention:hover { background: rgba(255, 255, 255, 0.07); }
.postpop__mention-poster {
  width: 28px;
  height: 40px;
  border-radius: 2px;
  object-fit: cover;
  flex: 0 0 auto;
}
.postpop__mention-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.postpop__mention-name {
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-medium);
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.postpop__mention-rating {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-latin);
  font-size: 12px;
  font-weight: var(--fw-bold);
  color: var(--yellow-500);   /* Figma 紅星評分→2.0 品牌橘 */
}
.postpop__mention-star {
  width: 12px;
  height: 12px;
  background: currentColor;
  -webkit-mask: url('assets/icons/star.svg') center / contain no-repeat;
  mask: url('assets/icons/star.svg') center / contain no-repeat;
}
/* 右側動作：查看影片資訊（0806 Dolly：原 Figma 收費徽章不要，價格本來就是 mock） */
.postpop__mention-view { flex: 0 0 auto; }

/* 媒體＋內文 */
.postpop__media {
  margin-top: 16px;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--neutral-900);
}
.postpop__media img,
.postpop__media video {
  width: 100%;
  max-height: 56vh;
  object-fit: contain;
  display: block;
}
.postpop__cover { border: 0; border-radius: 0; }
.postpop__body {
  margin: 24px 0 0;
  font-family: var(--font-cjk-text);
  font-size: 16px;
  line-height: 1.8;
  color: var(--text-secondary);
  white-space: pre-line;
}
.postpop__translate {
  display: block;
  margin-top: 24px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.postpop__translate:hover { text-decoration: underline; }

/* 標籤（Figma Edit Tag 36 高 pills） */
.postpop__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 56px;
}
.postpop__tag {
  display: inline-flex;
  align-items: center;
  height: 36px;
  padding: 0 16px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-secondary);
}

/* 看更多（Figma More Posts：標題＋三列、1px 分隔線、右側 16:9 縮圖） */
.postpop__moreposts { margin-top: 56px; }
.postpop__moreposts-title {
  margin: 0 0 24px;
  font-family: var(--font-cjk-display);
  font-size: 20px;
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
.postpop__moreposts-name { color: var(--yellow-500); }
.postpop__mp {
  display: flex;
  align-items: flex-start;
  gap: 24px;
  width: 100%;
  padding: 24px 0;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--border-subtle);
  text-align: left;
  cursor: pointer;
}
.postpop__mp:first-child { padding-top: 0; }
.postpop__mp:last-child { border-bottom: 0; padding-bottom: 0; }
.postpop__mp-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.postpop__mp-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.postpop__mp-avatar {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-pill);
  object-fit: cover;
}
.postpop__mp-author {
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.postpop__mp-date {
  font-family: var(--font-latin);
  font-size: 12px;
  color: var(--text-tertiary);
}
.postpop__mp-title {
  font-family: var(--font-cjk-display);
  font-size: 17px;
  font-weight: var(--fw-bold);
  line-height: 1.5;
  color: var(--text-primary);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  overflow: hidden;
}
.postpop__mp-excerpt {
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 1.7;
  color: var(--text-tertiary);
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  overflow: hidden;
}
.postpop__mp-stats {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-top: 4px;
}
.postpop__mp-stat {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-latin);
  font-size: 13px;
  color: var(--text-tertiary);
}
.postpop__mp-stat .post-card__stat-icon { width: 18px; height: 18px; }
.postpop__mp-thumb {
  width: 216px;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  border-radius: var(--radius-md);
  flex: 0 0 auto;
}

/* ── 右欄：滿高固定（不隨內文捲動）——作者卡定高、留言卡 fill 剩餘 ── */
.postpop__side {
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 40px 0 24px;
}
/* 作者卡（Figma Tastemaker：框線卡 r16 p16、頭像 32＋名 18、身分縮排、追蹤=橘 ghost） */
.postpop__author {
  display: flex;
  align-items: flex-start;
  gap: 16px;
  padding: 16px;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xl);
  background: var(--bg-secondary);
}
.postpop__author-info {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.postpop__author-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.postpop__avatar {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  object-fit: cover;
  flex: 0 0 auto;
}
.postpop__author-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-cjk-display);
  font-size: 16px;   /* 0806 Dolly：18 太大 */
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.postpop__author-role {
  padding-left: 40px;   /* 32 頭像＋8 gap，跟名字左緣切齊 */
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-tertiary);
}

/* 留言卡（Figma Comment area：上=列表卡、下=輸入卡 darker 底、共用外框圓角）
   fill 右欄剩餘高度；列表內捲、輸入列固定在卡底 */
.postpop__cpanel {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xl);
  overflow: hidden;
  background: var(--bg-secondary);
}
.postpop__comments {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 24px;
  padding: 16px 16px 32px;
  overflow-y: auto;
}
.postpop__chead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.postpop__ccount {
  margin: 0;
  font-family: var(--font-cjk-display);
  font-size: 16px;
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.postpop__ccount span { font-family: var(--font-latin); }
/* 排序＝全站 .comment-sort＋sortmenu（0806 Dolly：照短劇頁）——樣式/行為都在
   36-title-detail.css＋ds.js title-comments（cpanel 掛 comment-col 自動接）；
   選單 z 已在 36 抬到 2200 蓋過本彈窗 2100 */

/* 留言列（Figma：頭像 32／名 14＋時間 12／內文 16／動作 24 icon＋查看回覆） */
.postpop__c {
  display: flex;
  gap: 16px;
}
.postpop__c-avatar {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-pill);
  object-fit: cover;
  flex: 0 0 auto;
}
.postpop__c-avatar--empty { opacity: 0.5; }
.postpop__c-body {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.postpop__c-head {
  display: flex;
  align-items: center;
  gap: 8px;
}
.postpop__c-name {
  font-family: var(--font-cjk-text);
  font-size: 14px;
  font-weight: var(--fw-medium);
  color: var(--text-primary);
}
.postpop__c-time {
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}
.postpop__c-text {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: 15px;
  line-height: 1.8;
  color: var(--text-secondary);
  overflow-wrap: anywhere;
}
.postpop__c-acts {
  display: flex;
  align-items: center;
  gap: 16px;
}
.postpop__cact {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  color: var(--text-tertiary);
  transition: color 150ms ease;
}
.postpop__cact:hover:not(.is-active) { color: var(--text-secondary); }
.postpop__cact .post-card__stat-icon { width: 20px; height: 20px; }
.postpop__cact-n {
  font-family: var(--font-latin);
  font-size: 13px;
  color: var(--text-tertiary);
}
.postpop__c-translate {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  font-weight: var(--fw-bold);
  color: var(--text-tertiary);
}
.postpop__c-translate:hover { color: var(--text-secondary); }
.postpop__c-replies {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: flex-start;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: 13px;
  color: var(--text-tertiary);
}
.postpop__c-replies-line {
  width: 30px;
  height: 1px;
  background: var(--text-tertiary);
}

/* 空狀態（沿用 1.0 文案）——滿高留言卡裡垂直置中 */
.postpop__cempty {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 12px;
  padding: 64px 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
}
.postpop__cempty-ico {
  width: 36px;
  height: 36px;
  background: currentColor;
  -webkit-mask: url('assets/icons/comment.svg') center / contain no-repeat;
  mask: url('assets/icons/comment.svg') center / contain no-repeat;
  opacity: 0.7;
}

/* 輸入卡（Figma Personal Comment：darker 底、pill 輸入含頭像、外側送出、下=使用說明） */
.postpop__cinput {
  padding: 24px 16px;
  background: var(--bg-tertiary);
  border-top: 1px solid var(--border-subtle);
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.postpop__cinput-row {
  display: flex;
  align-items: center;
  gap: 8px;
}
.postpop__cinput-pill {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  height: 40px;
  padding: 8px;
  border-radius: 20px;
  background: rgba(255, 255, 255, 0.04);
}
.postpop__cinput-avatar {
  width: 24px;
  height: 24px;
  border-radius: var(--radius-pill);
  object-fit: cover;
  flex: 0 0 auto;
}
.postpop__cinput-field {
  flex: 1;
  min-width: 0;
  background: none;
  border: 0;
  outline: none;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  color: var(--text-primary);
}
.postpop__cinput-field::placeholder { color: rgba(255, 255, 255, 0.24); }
.postpop__cinput-send {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  background: none;
  border: 0;
  padding: 0;
  border-radius: var(--radius-pill);
  color: var(--text-secondary);
  cursor: pointer;
  transition: color 150ms ease;
}
.postpop__cinput-send:hover { color: var(--text-primary); }
.postpop__cinput-send::before {
  content: "";
  width: 18px;
  height: 18px;
  background: currentColor;
  -webkit-mask: url('assets/icons/send.svg') center / contain no-repeat;
  mask: url('assets/icons/send.svg') center / contain no-repeat;
}
.postpop__chelp {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  align-self: flex-start;
  padding-left: 8px;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--font-cjk-text);
  font-size: 12px;
  color: var(--text-tertiary);
}
.postpop__chelp-ic {
  width: 16px;
  height: 16px;
  background: currentColor;
  -webkit-mask: url('assets/icons/info.svg') center / contain no-repeat;
  mask: url('assets/icons/info.svg') center / contain no-repeat;
}

/* 卡片可點的手勢提示（post-open 行為掛上 .post-card--openable） */
.post-card--openable { cursor: pointer; }

/* 手機：m-full 滿版接管——回到單欄整面板捲動（固定高度佈局只給桌機） */
@media (max-width: 768px) {
  .ds-modal.postpop { padding: 0; }
  .ds-modal.postpop .ds-modal__panel {
    max-width: none;
    margin: 0;
    border-radius: 0;
    overflow-y: auto;          /* 手機＝整面板捲 */
  }
  .ds-modal.postpop .ds-modal__close { position: fixed; top: 16px; right: 16px; }
  .postpop__wrap {
    display: block;
    height: auto;
    padding: 16px;
  }
  .postpop__main { overflow: visible; padding: 40px 4px 0; }
  .postpop__side { margin-top: 24px; padding: 0; }
  .postpop__cpanel { flex: none; }
  .postpop__comments { overflow: visible; }
  .postpop__cempty { flex: none; }
  .postpop__mp-thumb { width: 128px; }
}

/* 0807：貼文彈窗現在也吃 .community-card（首頁／個人頁的三欄貼文卡）。
   可點的手感要跟 .post-card--openable 一致。 */
.community-card--openable { cursor: pointer; }

/* ⚠️ .postpop__mention 是 display:flex，會蓋掉 [hidden] 屬性（家規：[hidden] 被
   display 蓋，要用 .class[hidden] 補回）。標題沒有《片名》時 fill() 會設
   mention.hidden＝true，少了這條就會留下一條沒有海報的空「查看影片資訊」。
   周湯豪的貼文標題大多沒有書名號，所以這個洞是 0807 才被看見的。 */
.postpop__mention[hidden] { display: none; }


/* ── 彩蛋解鎖（2026-08-06）：vault.html 庫房清單＋解鎖條件卡；企劃＝
   handover/unlock-surprises-nick-vault-plan-0806.md；解鎖動效沿用 .dsburst。 */
/* ────────────────────────────────────────────────────────────
   88-vault.css — 彩蛋解鎖（Unlock Surprises）· vault.html 專用
   企劃＝handover/unlock-surprises-nick-vault-plan-0806.md。
   左欄庫房清單（參考 media-vault 原型）＋右欄內容/解鎖條件。
   鎖定態＝模糊縮圖＋檔名式標題（檔名可見、內容不可見）；
   解鎖動效沿用 .dsburst（54-like-save.css），此檔不重造粒子。
   ──────────────────────────────────────────────────────────── */

/* ── 頁首補充 ── */
.vault-en {
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-medium);
  color: var(--text-tertiary);
  margin-left: 8px;
  letter-spacing: 0.02em;
}

/* 創作者識別列（頭像/名字/認證，資料=ZTOR_CREATOR_PROFILE） */
.vault-creator {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 4px 0 28px;
}
.vault-creator__avatar {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-pill);
  overflow: hidden;
  border: 1px solid var(--border-subtle);
  flex: 0 0 auto;
}
.vault-creator__avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vault-creator__id { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.vault-creator__name {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
}
/* 認證標章＝37-search 同配方（verify.svg 挖空勾 → 墊深色小圓盤） */
.vault-creator__verify {
  position: relative;
  display: inline-block;
  width: 18px;
  height: 18px;
  margin-left: 4px;
  vertical-align: -3px;
  flex: 0 0 auto;
}
.vault-creator__verify::before {
  content: "";
  position: absolute;
  inset: 3px;
  border-radius: var(--radius-pill);
  background: var(--bg-secondary);
}
.vault-creator__verify::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--brand-500);
  -webkit-mask: url('assets/icons/verify.svg') center / contain no-repeat;
  mask: url('assets/icons/verify.svg') center / contain no-repeat;
}
.vault-creator__role {
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
}
.vault-creator__links { margin-left: auto; display: flex; gap: 8px; flex: 0 0 auto; }

/* ── 版面：左庫房欄＋右內容欄 ── */
.vault-layout {
  display: grid;
  grid-template-columns: 348px minmax(0, 1fr);
  gap: 32px;
  align-items: start;
}

/* ── 左欄 ── */
.vault-rail {
  position: sticky;
  top: 96px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.vault-rail__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: 0 2px;
}
.vault-rail__title {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h5);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin: 0;
}
.vault-rail__count { font-size: var(--fs-caption); color: var(--text-tertiary); }

/* 鑰匙 icon（0806 Dolly 定稿＝assets/icons/key.svg）：mask 吃 currentColor，
   尺寸由各使用處指定。⚠️ 本檔併進根目錄 components.css → 路徑帶 assets/ 前綴 */
.vault-ico-key {
  display: inline-block;
  flex: 0 0 auto;
  background: currentColor;
  -webkit-mask: url('assets/icons/key.svg') center / contain no-repeat;
  mask: url('assets/icons/key.svg') center / contain no-repeat;
}
/* 解鎖 toast 訊息尾的鑰匙（vault.html 在 DSToast 後補進去的） */
.ds-toast__msg .vault-ico-key { width: 15px; height: 15px; vertical-align: -2px; margin-left: 2px; }

/* 彩蛋庫清單（視角切換收進右下角情境展示面板，頁上沒有開關） */
.vault-rail__list { display: flex; flex-direction: column; gap: 8px; }
.vault-item {
  display: flex;
  align-items: center;
  gap: 12px;
  width: 100%;
  padding: 10px;
  background: none;
  border: 1px solid transparent;
  border-radius: var(--radius-lg);
  cursor: pointer;
  text-align: left;
  transition: background 0.2s ease, border-color 0.2s ease;
}
.vault-item:hover { background: rgba(255, 255, 255, 0.04); }
.vault-item.is-active {
  background: rgba(var(--brand-rgb), 0.08);
  border-color: var(--brand-500);
}
.vault-item__cover {
  width: 56px;
  height: 56px;
  border-radius: var(--radius-md);
  overflow: hidden;
  flex: 0 0 auto;
  background: var(--bg-tertiary);
}
.vault-item__cover img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vault-item__cover--tile { display: grid; place-items: center; color: var(--text-tertiary); }
.vault-item__cover--tile svg { width: 22px; height: 22px; }
.vault-item__body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.vault-item__name {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vault-item.is-active .vault-item__name { color: var(--brand-500); }
.vault-item__meta { font-size: var(--fs-caption); color: var(--text-tertiary); }
.vault-item__keys {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  flex: 0 0 auto;
}
.vault-item__keys .vault-ico-key { width: 14px; height: 14px; }
.vault-item.is-active .vault-item__keys { color: var(--brand-500); }
/* 一般用戶視角：未動工＝鎖頭、開始解鎖＝進度（0806 Dolly：前台不擺鑰匙數） */
.vault-item__lock {
  margin-left: auto;
  display: inline-flex;
  color: var(--text-tertiary);
  flex: 0 0 auto;
}
.vault-item__lock svg { width: 16px; height: 16px; }
.vault-item__prog {
  margin-left: auto;
  font-family: var(--font-latin);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  flex: 0 0 auto;
}
.vault-item__prog b { color: var(--brand-500); font-weight: var(--fw-bold); }
.vault-item.is-active .vault-item__prog { color: var(--text-secondary); }

/* 新增庫房（創作者視角限定） */
.vault-rail__add {
  width: 100%;
  padding: 16px;
  background: none;
  border: 1px dashed var(--border-strong);
  border-radius: var(--radius-lg);
  color: var(--text-secondary);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  cursor: pointer;
  transition: color 0.2s ease, border-color 0.2s ease;
}
.vault-rail__add:hover { color: var(--text-primary); border-color: var(--text-tertiary); }

/* ── 右欄：庫房頭 ── */
.vault-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 24px;
  margin: 0 0 20px;
}
.vault-head__copy { min-width: 0; }
.vault-head__title {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-h4);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin: 0;
}
.vault-head__desc {
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
  margin: 6px 0 0;
  line-height: 1.7;
}
.vault-head__stats {
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  margin: 10px 0 0;
}
.vault-head__progress {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  flex: 0 0 auto;
  padding-top: 6px;
}
.vault-head__progress-label { font-size: var(--fs-body-sm); color: var(--text-secondary); }
.vault-head__progress-label b { color: var(--brand-500); font-weight: var(--fw-bold); }
.vault-head__bar {
  width: 220px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.08);
  overflow: hidden;
  display: block;
}
.vault-head__bar-fill {
  display: block;
  height: 100%;
  border-radius: var(--radius-pill);
  background: var(--brand-500);
  transition: width 0.4s ease;
}
/* 主人提示（創作者以用戶身分看前台）：資訊語氣、不做成按鈕——管理在 Creator Studio */
.vault-owner-note {
  margin: 0 0 16px;
  padding: 10px 14px;
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  background: rgba(255, 255, 255, 0.03);
  font-size: var(--fs-body-sm);
  color: var(--text-tertiary);
  line-height: 1.6;
}
.vault-owner-note b { color: var(--text-secondary); font-weight: var(--fw-bold); }

/* ── 內容卡 ── */
.vault-cards { display: flex; flex-direction: column; gap: 12px; }
.vault-card {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr) auto;
  gap: 20px;
  align-items: center;
  padding: 14px;
  background: var(--bg-card);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-xl);
}
.vault-card__media {
  position: relative;
  width: 168px;
  height: 105px;
  border-radius: var(--radius-md);
  overflow: hidden;
  /* ⚠️ 原本用 --bg-tertiary，它跟 --bg-card 是同一個值（#1e1e1e）——縮圖底色
     等於卡片底色，深色或模糊的縮圖就整個糊進卡片裡（0806 Dolly）。改深一階。 */
  background: var(--neutral-800);
}
/* 縮圖外框：一圈內描邊。深色縮圖、無圖的 tile 都還看得出「這裡是一張縮圖」，
   不靠圖片本身的亮度撐邊界。z-index 蓋過鎖頭/時長 chip，但只是一條 hairline。 */
.vault-card__media::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.09);
  pointer-events: none;
  z-index: 3;
}
.vault-card__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.vault-card__media > :first-child { transition: filter 0.45s ease, transform 0.45s ease; }
/* 鎖定＝模糊（檔名可見、內容不可見）；scale 蓋掉模糊的透明邊。
   brightness 從 .65 拉到 .78：太暗會跟卡片同化，模糊本身已足夠藏內容。 */
.vault-card__media.is-blurred > :first-child {
  filter: blur(14px) brightness(0.78) saturate(0.85);
  transform: scale(1.12);
}
/* 波形 tile 模糊淺一點——「看得出是一段音檔」才有試聽片段的吊胃口感 */
.vault-card__media.is-blurred > .vault-wave {
  filter: blur(6px) brightness(0.7);
  transform: scale(1.06);
}
.vault-card.is-unlocking .vault-card__media > :first-child { filter: none; transform: none; }
/* 大彩蛋：縮圖不做任何特殊處理（0806 Dolly 五改＝橘描邊、呼吸動態、光暈全拿掉）。
   「這件不一樣」完全由標題列的「大彩蛋」小標講明；資料的 grand 旗標仍會渲染
   is-grand class，之後要再掛視覺效果從這裡接。 */
/* 音檔＝波形 tile（未公開 demo 的「私人硬碟」感，不用精修圖） */
.vault-wave {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  width: 100%;
  height: 100%;
  padding: 18px 14px;
}
.vault-wave span {
  flex: 1;
  height: var(--h, 40%);
  border-radius: 2px;
  background: linear-gradient(180deg, var(--brand-400), var(--brand-600));
  opacity: 0.75;
}
/* 貼文＝檔案 tile */
.vault-doc { display: grid; place-items: center; width: 100%; height: 100%; color: var(--text-tertiary); }
.vault-doc svg { width: 26px; height: 26px; }
/* 時長 chip */
.vault-card__dur {
  position: absolute;
  right: 8px;
  bottom: 8px;
  z-index: 2;
  padding: 2px 8px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.55);
  color: var(--neutral-white);
  font-family: var(--font-latin);
  font-size: var(--fs-caption);
}
/* 鎖頭（玻璃小圓盤；dsburst 從這裡噴發） */
.vault-card__lock {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.45);
  border: 1px solid rgba(255, 255, 255, 0.16);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--neutral-white);
}
.vault-card__lock svg { width: 18px; height: 18px; transition: opacity 0.3s ease, transform 0.3s ease; }
.vault-card.is-unlocking .vault-card__lock { background: transparent; border-color: transparent; }
.vault-card.is-unlocking .vault-card__lock svg { opacity: 0; transform: scale(0.6); }

.vault-card__body { min-width: 0; }
/* 標題區：狀態標籤在標題「上方」自成一行（0806 Dolly 三改；原並排在標題前） */
.vault-card__titlerow {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 8px;
  min-width: 0;
}
.vault-card__titlerow .status-tag { flex: 0 0 auto; }
/* 標籤同一列橫排（狀態標籤＋大彩蛋小標），不是各佔一行 */
.vault-card__tags { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; }
.vault-card__title {
  font-family: var(--font-cjk-display);
  font-size: var(--fs-body);
  font-weight: var(--fw-bold);
  color: var(--text-primary);
  margin: 0;
}
/* 鎖定態＝檔名式標題（拉丁檔名走 Satoshi） */
.vault-card__title--file {
  font-family: var(--font-latin);
  letter-spacing: 0.01em;
  overflow-wrap: anywhere;
}
.vault-card__file {
  font-family: var(--font-latin);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  margin: 4px 0 0;
  overflow-wrap: anywhere;
}
/* 卡片底列＝三種不同性質的資訊，用「形」分辨而不是只靠間距（0806 Dolly 二改）：
   ① 類型＝膠囊 chip（有底有框，一眼是分類標籤）
   ② 解鎖條件＝鑰匙 icon＋文字（一眼是「怎麼拿到」）
   ③ 已解鎖人數＝推到最右的統計（灰、數字 tabular，一眼是社會證明） */
.vault-card__cond {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  column-gap: 12px;
  row-gap: 8px;
  font-size: var(--fs-caption);
  margin: 12px 0 0;
}
.vault-card__type {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  flex: 0 0 auto;
  padding: 3px 9px;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.06);
  border: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  line-height: 1.35;
}
.vault-card__type svg { width: 13px; height: 13px; opacity: 0.85; }
.vault-card__condtext {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-width: 0;
  color: var(--text-secondary);
}
.vault-card__condtext .vault-ico-key {
  width: 13px;
  height: 13px;
  flex: 0 0 auto;
  color: var(--text-tertiary);
}
/* 統計靠右：跟左邊兩項拉開距離，本身就是一段獨立資訊 */
.vault-card__keys {
  margin-left: auto;
  flex: 0 0 auto;
  color: var(--text-tertiary);
  font-variant-numeric: tabular-nums;
}
.vault-card__keys b {
  color: var(--text-secondary);
  font-weight: var(--fw-bold);
}

/* 0806 Dolly：右側改單一水平列（標籤｜鈕 並排、垂直置中）——原本直向堆疊＋
   置中，各卡組合不同落點就參差。說明文字收到列下方右對齊。 */
.vault-card__aside {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
  min-width: 150px;   /* 0806 二改：原 220 太寬，收成剛好裝得下 CTA 的一條窄帶 */
  text-align: right;
  /* 動作區＝獨立色塊（0806 Dolly）：滿高貼齊卡片右緣的深灰面板，跟左側資訊分開。
     負 margin 抵掉卡片自己的 14px padding；圓角比卡片少 1px（卡片有 1px 邊框，
     內圓角要對齊外圓角的內側，不然轉角會露出一條白邊）。 */
  align-self: stretch;
  margin: -14px -14px -14px 0;
  padding: 14px 16px;
  /* 0806 四改：--neutral-800 太深、像挖了一塊洞；改成比卡片「微亮一階」的
     薄白層（疊在 --bg-card 上約 #232323）。分界線也拿掉——色塊本身就夠分區，
     再加一條 hairline 反而變成兩層邊界。 */
  background: rgba(255, 255, 255, 0.02);
  border-radius: 0 calc(var(--radius-xl) - 1px) calc(var(--radius-xl) - 1px) 0;
}
.vault-card__note {
  flex-basis: 100%;
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  line-height: 1.55;
  margin: 0;
}
.vault-card__issued {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.vault-card__issued b { color: var(--text-primary); font-weight: var(--fw-bold); }
.vault-card__issued .vault-ico-key { width: 15px; height: 15px; }

/* ── RWD（桌機為主的 demo 頁；窄幅收成單欄可讀即可） ── */
@media (max-width: 1180px) {
  .vault-layout { grid-template-columns: 300px minmax(0, 1fr); gap: 24px; }
}
@media (max-width: 920px) {
  .vault-layout { grid-template-columns: minmax(0, 1fr); }
  .vault-rail { position: static; }
  .vault-head { flex-direction: column; }
  .vault-head__progress { align-items: flex-start; }
}
@media (max-width: 720px) {
  .vault-card { grid-template-columns: 112px minmax(0, 1fr); }
  .vault-card__media { width: 112px; height: 74px; }
  .vault-card__aside {
    grid-column: 1 / -1;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-start;
    text-align: left;
    min-width: 0;
  }
}


/* ── 頭號粉絲積分榜（2026-08-06）：自 origin/L 港回的基底（頒獎台/表格/等級卡）；
   頁＝leaderboard.html?creator=、refine 層＝assets/refine-engagement.css。 */
/* ────────────────────────────────────────────────────────────
   89-leaderboard.css — 頭號粉絲積分榜（0806 自 origin/L components.css
   8326-8736 港回；6/18 砍頁時連同舊 40-leaderboard 一起移除，eShop 港回
   shop-render.initLeaderboard 後基底樣式一直缺席，本次補回）。
   refine 層＝assets/refine-engagement.css（原本就在）。
   ──────────────────────────────────────────────────────────── */
/* ─────────────────────────────────────────
   LEADERBOARD — podium → table → sticky my-rank
   ───────────────────────────────────────── */
.lb-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-2);
}
.lb-head__title {
  font-family: var(--font-cjk-display);
  font-size: 20px;
  font-weight: var(--fw-bold);
}
.lb-head__season {
  font-size: 12px;
  color: var(--text-tertiary);
}
.lb-back {
  font-size: 13px;
  color: var(--yellow-500);
  text-decoration: none;
}
.lb-back:hover { text-decoration: underline; text-underline-offset: 4px; }

/* Tier legend — four quiet columns over a hairline */
.lb-legend {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-4);
  margin-top: var(--space-8);   /* 32px — on scale (was off-scale 28px) */
  padding-top: 18px;
  border-top: 1px solid var(--border-subtle);
}
.lb-legend__tier { display: flex; flex-direction: column; gap: 5px; }
.lb-legend__note { font-size: 12px; color: var(--text-tertiary); }

/* Tier chip: top tier filled, the rest ringed */
.lb-chip {
  display: inline-flex;
  align-items: center;
  align-self: flex-start;
  height: 20px;
  padding: 0 9px;
  border-radius: var(--radius-pill);
  font-family: var(--font-latin);
  font-size: 10px;
  font-weight: var(--fw-bold);
  letter-spacing: 0.08em;
  border: 1px solid var(--border-default);
  color: var(--text-secondary);
  white-space: nowrap;
}
.lb-chip--inner     { background: var(--yellow-500); color: var(--yellow-ink); border-color: transparent; }
.lb-chip--super     { border-color: var(--success-500); color: var(--success-500); }
.lb-chip--supporter { border-color: var(--info-500); color: var(--info-500); }

/* Podium — #1 centred and raised */
.lb-podium {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: var(--space-14);
  margin: var(--space-12) 0 var(--space-4);   /* 48 / 16 — on scale (was 56/16) */
}
.lb-podium .top-fan { width: 110px; }
.lb-podium .top-fan__avatar { width: 84px; height: 84px; }
.lb-podium .top-fan--first .top-fan__avatar { width: 108px; height: 108px; }
.lb-podium .top-fan__nick { font-size: 13px; color: var(--neutral-white); }
.lb-podium .top-fan__pts { font-size: 12px; }

/* Table */
.lb-table { margin-top: var(--space-6); }   /* 24px — on scale (was 28px) */
.lb-row {
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 13px 0;
  border-top: 1px solid var(--border-subtle);
}
.lb-row__rank {
  font-family: var(--font-latin);
  font-size: 13px;
  color: var(--text-tertiary);
  width: 36px;
  flex-shrink: 0;
  font-variant-numeric: tabular-nums;
}
.lb-row__avatar {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  padding: 2px;
  background: var(--border-default);
  flex-shrink: 0;
}
.lb-row--inner .lb-row__avatar     { background: var(--yellow-500); }
.lb-row--super .lb-row__avatar     { background: var(--success-500); }
.lb-row--supporter .lb-row__avatar { background: var(--info-500); }
.lb-row__avatar img { width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block; border: 2px solid var(--bg-primary); }
.lb-row__nick {
  flex: 1;
  min-width: 0;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.lb-row__pts {
  font-family: var(--font-latin);
  font-size: 14px;
  font-weight: var(--fw-medium);
  font-variant-numeric: tabular-nums;
}

/* My rank — pinned, auth-gated */
/* MY RANK — floating snackbar. Fixed near the bottom so the user always sees
   their rank AND the gap to the next tier (the climb incentive). Auth-gated:
   a visitor has no "my rank". */
.lb-me {
  position: fixed;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  z-index: 60;
  width: min(560px, calc(100vw - 120px));   /* narrower — content-sized, no dead space */
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) 18px;
  border-radius: var(--radius-lg);
  background: rgba(18, 18, 18, 0.88);
  border: 1px solid var(--border-default);
  backdrop-filter: blur(18px) saturate(1.2);
  -webkit-backdrop-filter: blur(18px) saturate(1.2);
}
/* recentre into the content column, past the left side-rail */
body.has-rail .lb-me { left: calc(50% + 36px); }

.lb-me__rank {
  font-family: var(--font-latin);
  font-weight: var(--fw-bold);
  font-size: 16px;
  color: var(--yellow-500);
  min-width: 30px;
  text-align: center;
  flex-shrink: 0;
}
.lb-me .lb-row__avatar,
.lb-me .lb-row__avatar img { width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0; }
.lb-me .lb-row__avatar img { object-fit: cover; display: block; }

/* Two compact rows: identity + score, then progress + next-tier target. */
.lb-me__main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 7px; }
.lb-me__line { display: flex; align-items: center; gap: var(--space-2); }
.lb-me__nick { flex: 1; min-width: 0; font-family: var(--font-cjk-text); font-size: 14px; font-weight: var(--fw-medium); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lb-me .lb-chip { flex-shrink: 0; }
.lb-me__score { flex-shrink: 0; margin-left: auto; font-family: var(--font-cjk-text); font-size: 12px; color: var(--text-tertiary); white-space: nowrap; }
.lb-me__score b { font-family: var(--font-latin); font-size: 15px; color: var(--text-primary); font-variant-numeric: tabular-nums; }

.lb-me__line--climb { gap: 10px; }
.lb-me__bar { flex: 1; min-width: 0; height: 6px; border-radius: 999px; background: rgba(255, 255, 255, 0.1); overflow: hidden; }
.lb-me__bar-fill { display: block; height: 100%; border-radius: 999px; background: linear-gradient(90deg, var(--yellow-600), var(--yellow-400)); box-shadow: 0 0 10px rgba(255, 163, 63, 0.55); }
.lb-me__next { flex-shrink: 0; max-width: 58%; font-family: var(--font-cjk-text); font-size: 11.5px; color: var(--text-secondary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.lb-me__next b { color: var(--yellow-500); font-family: var(--font-latin); }

/* clearance so the floating card never covers the last rows (logged-in only) */
body[data-auth="logged-in"].rf-leaderboard main { padding-bottom: 130px; }

@media (max-width: 767.98px) {
  /* Float ABOVE the bottom-nav pill (pill ≈ 12px + ~53px tall) so the two
     never collide — this was the overlap in the screenshot. */
  .lb-me, body.has-rail .lb-me {
    left: 50%; width: calc(100vw - 24px);
    bottom: calc(84px + env(safe-area-inset-bottom, 0px));
    gap: 10px; padding: 11px var(--space-4);
  }
  /* Extra clearance so the last rows are never hidden behind the floating card. */
  body[data-auth="logged-in"].rf-leaderboard main { padding-bottom: calc(188px + env(safe-area-inset-bottom, 0px)); }
}

/* Ways to earn — verb rows with point chips */
.lb-earn { margin-top: var(--space-4); max-width: 560px; }
.lb-earn__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--border-subtle);
}
.lb-earn__verb { font-size: 14px; }
.lb-earn__pts {
  font-family: var(--font-latin);
  font-size: 12px;
  font-weight: var(--fw-bold);
  color: var(--yellow-500);
}

/* Shop v2 mobile collapse — kept below the global MOBILE TWEAKS block
   on purpose (source-order rule from the README). */
@media (max-width: 767.98px) {
  .shop-grid, .creator-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 10px; row-gap: 30px; }
  .shop-toolbar { flex-direction: column; gap: var(--space-3); }
  .creator-hero__name { font-size: 30px; }
  .creator-pickup { flex-direction: column; align-items: flex-start; }
  .lb-legend { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .lb-podium { gap: var(--space-6); }
  .creator-event { flex-wrap: wrap; }
  .creator-event__date { width: auto; }
}

/* ─────────────────────────────────────────
   LEADERBOARD — RESPONSIVE
   ───────────────────────────────────────── */

/* Tablet (768–1023): tighten podium gap and table row padding. */
@media (min-width: 768px) and (max-width: 1023.98px) {
  .lb-podium {
    gap: var(--space-8);
    margin: var(--space-10) 0 var(--space-4);
  }

  .lb-legend {
    gap: var(--space-4);
  }
}

/* Phone ≤767.98px */
@media (max-width: 767.98px) {
  /* Head row: stack title and season on one side, keep back link. */
  .lb-head {
    flex-wrap: wrap;
    gap: var(--space-2);
  }

  .lb-head__title {
    font-size: var(--fs-h4);
  }

  /* Podium: scale avatars down, reduce gap and top margin. */
  .lb-podium {
    gap: var(--space-6);
    margin: var(--space-8) 0 var(--space-4);
  }

  .lb-podium .top-fan {
    width: 80px;
  }

  .lb-podium .top-fan__avatar {
    width: 62px;
    height: 62px;
  }

  .lb-podium .top-fan--first .top-fan__avatar {
    width: 80px;
    height: 80px;
  }

  .lb-podium .top-fan__nick {
    font-size: var(--fs-caption);
  }

  /* Legend: two columns at 768px already set; keep it consistent. */
  .lb-legend {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
    margin-top: var(--space-6);
  }

  /* Table rows: reduce gap and avatar so rank/name/points stay on one line. */
  .lb-row {
    gap: var(--space-3);
    padding: var(--space-3) 0;
  }

  .lb-row__rank {
    width: 28px;
    font-size: var(--fs-caption);
  }

  .lb-row__avatar {
    width: 32px;
    height: 32px;
  }

  .lb-row__nick {
    font-size: var(--fs-caption);
  }

  .lb-row__pts {
    font-size: var(--fs-caption);
  }

  /* Ways-to-earn rows: let the label wrap if needed. */
  .lb-earn__row {
    gap: var(--space-3);
  }

  .lb-earn__verb {
    font-size: var(--fs-caption);
  }
}

/* Small phone ≤480px: compress further; legend goes single-col. */
@media (max-width: 480px) {
  .lb-legend {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-3);
  }

  .lb-podium {
    gap: var(--space-5);
  }

  .lb-podium .top-fan {
    width: 68px;
  }

  .lb-podium .top-fan__avatar {
    width: 52px;
    height: 52px;
  }

  .lb-podium .top-fan--first .top-fan__avatar {
    width: 68px;
    height: 68px;
  }

  /* Stack climb label and points vertically when space is very tight. */
  .lb-me__climb-top {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
  }
}

/* ═══════════════════════════════════════════════════════════════════════════
   GAMIFICATION LAYER — make the leaderboard a climb-the-ranks dopamine hub.
   Real medal podium (pedestals + crown + gold/silver/bronze badges + tier
   glow) and a promotion-framed "my rank" bar. Desktop + mobile. Appended last
   so it wins the cascade. Mobbin refs: Kahoot / Duolingo / Stadium Live.
   The renderer (shop-render.js) already emits rank badges + tier classes, so
   this is pure CSS — DOM order in .lb-podium is [#2, #1(.--first), #3].
   ═══════════════════════════════════════════════════════════════════════════ */

/* Raised pedestals — #1 (centre) tallest, then #2, then #3. align-items:flex-end
   (set on .lb-podium) makes the blocks rise from one baseline = a podium. */
.lb-podium .top-fan { position: relative; }
.lb-podium .top-fan::after {
  content: "";
  display: block;
  width: 86%;
  margin-top: 14px;
  border-radius: 12px 12px 0 0;
  border: 1px solid var(--border-subtle);
  border-bottom: 0;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.10), rgba(255, 255, 255, 0.02));
}
.lb-podium .top-fan:nth-child(1)::after { height: 46px; background: linear-gradient(180deg, rgba(232, 234, 240, 0.22), rgba(232, 234, 240, 0.03)); }  /* silver (#2) */
.lb-podium .top-fan--first::after        { height: 68px; background: linear-gradient(180deg, rgba(255, 200, 90, 0.28),  rgba(255, 163, 63, 0.04)); }   /* gold (#1)   */
.lb-podium .top-fan:nth-child(3)::after  { height: 32px; background: linear-gradient(180deg, rgba(214, 150, 95, 0.24),  rgba(214, 150, 95, 0.03)); }   /* bronze (#3) */

/* Medal rank badges */
.lb-podium .top-fan__rank { width: 22px; height: 22px; font-size: 11px; border-color: transparent; }
.lb-podium .top-fan:nth-child(1) .top-fan__rank { background: linear-gradient(180deg, #eef0f5, #b9bcc6); color: #33363d; }
.lb-podium .top-fan--first .top-fan__rank        { background: linear-gradient(180deg, #ffe48f, #f4a623); color: #5a3a00; box-shadow: 0 2px 10px rgba(244, 166, 35, 0.5); }
.lb-podium .top-fan:nth-child(3) .top-fan__rank  { background: linear-gradient(180deg, #edc09a, #c8854c); color: #4a2b10; }

/* Crown over #1 */
.lb-podium .top-fan--first .top-fan__avatar::after {
  content: "";
  position: absolute; left: 50%; top: -24px; transform: translateX(-50%);
  width: 34px; height: 26px;
  background: url('assets/icons/crown.svg') center / contain no-repeat;
  filter: drop-shadow(0 2px 6px rgba(244, 166, 35, 0.5));
}

/* Tier-coloured avatar glow on the podium (subtle dopamine) */
.lb-podium .top-fan--inner .top-fan__avatar     { box-shadow: 0 8px 26px rgba(255, 163, 63, 0.38); }
.lb-podium .top-fan--super .top-fan__avatar     { box-shadow: 0 8px 24px rgba(54, 211, 153, 0.30); }
.lb-podium .top-fan--supporter .top-fan__avatar { box-shadow: 0 8px 24px rgba(90, 200, 250, 0.28); }

/* The score is the dopamine number — make it read */
.lb-podium .top-fan__pts { font-size: 13px; color: var(--text-secondary); font-weight: var(--fw-medium); }
.lb-podium .top-fan--first .top-fan__pts { color: var(--yellow-500); font-weight: var(--fw-bold); }

/* My-rank bar — frame it as a PROMOTION climb (brand glow + glowing fill) */
.lb-me { border-color: rgba(255, 163, 63, 0.32); box-shadow: 0 14px 44px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255, 163, 63, 0.14), inset 0 1px 0 rgba(255, 255, 255, 0.06); }
.lb-me__bar-fill { box-shadow: 0 0 10px rgba(255, 163, 63, 0.55); }
.lb-me__climb-label::before {
  content: "▲"; display: inline-block; margin-right: 5px;
  font-size: 9px; color: var(--yellow-500); transform: translateY(-1px);
}

/* Mobile: scale the pedestals + crown down with the smaller podium */
@media (max-width: 767.98px) {
  .lb-podium .top-fan:nth-child(1)::after { height: 34px; }
  .lb-podium .top-fan--first::after        { height: 50px; }
  .lb-podium .top-fan:nth-child(3)::after  { height: 24px; }
  .lb-podium .top-fan--first .top-fan__avatar::after { width: 28px; height: 22px; top: -20px; }
  .lb-podium .top-fan__pts { font-size: 12px; }
}

@media (prefers-reduced-motion: reduce) {
  .lb-me__bar-fill { box-shadow: none; }
}



/* ── 出價／得標付款側板（2026-08-07 競標線）。原名 89-bid-sheet，併回 endgame 時
   與排行榜撞號，改編 90。行為＝assets/ztor-bid.js（window.ZtorBid）。 */
/* BID SHEET (.bid-sheet__* + .completion-* + .pop-sheet__*). [COMPONENT] */
/* ─────────────────────────────────────────────
   BID SHEET  ·  the ztor-bid.js ds-drawer sheets (bid / claim), ported
   from the L endgame build. .completion-* is the shared completion-flow
   layout (form / pinned foot / success view); .pop-sheet__* rows are the
   fee-breakdown rows the claim sheet reuses. Consumed by ztor-bid.js;
   cc-field / cc-pay / cc-success / ds-success-tick come from the
   checkout + tick components.
   ───────────────────────────────────────────── */

.completion-form { margin-top: var(--space-4); display: flex; flex-direction: column; gap: var(--space-3); }
.completion-foot { margin-top: var(--space-5); display: flex; flex-direction: column; gap: var(--space-2); }
.ds-actionbar > .completion-foot { margin-top: 0; }

/* ⚠️ 面板圓角已於 0807 撤除（Dolly：出價／得標付款面板要跟購物車面板、通知面板
   一樣是直角）。原本這裡有一條 border-radius: var(--radius-xl) 0 0 var(--radius-xl)
   ＋ overflow: hidden，只給 ztor-bid／ztor-claim 兩個 drawer。
   現在完全不覆寫，直接吃 .ds-drawer__panel 基底（55-ds-drawer.css）＝直角，
   跟 .ztor-cart__panel 同一種長相。overflow 也不用補：捲動由 .ds-drawer__body
   的 overflow-y: auto 負責，購物車面板也沒設。
   連帶：section-themes.css 的 theme-sharp 浮層還原清單裡那兩個 id 也已移除
   （沒有圓角就不需要還原 radius token）。 */

/* .ds-actionbar 那支檔沒被移植進 f2e，foot 皮膚由消費者自己給（同 83-edit-profile
   的 __foot 作法）——只 scope 到本模組的兩個 sheet，別動其他 drawer。 */
[data-ds-drawer="ztor-bid"] .ds-drawer__foot,
[data-ds-drawer="ztor-claim"] .ds-drawer__foot {
  padding: var(--space-5) var(--space-6) max(var(--space-6), env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border-default);
  background: var(--bg-secondary);
}
[data-ds-drawer="ztor-bid"] .completion-foot .btn,
[data-ds-drawer="ztor-claim"] .completion-foot .btn { width: 100%; }
.completion-success { padding-block: var(--space-6); text-align: center; }
.completion-qr { width: fit-content; margin: var(--space-5) auto; }

.bid-sheet__status { margin-bottom: var(--space-5); padding: var(--space-4); border: 1px solid var(--border-default); border-radius: var(--radius-lg); background: var(--bg-secondary); }
.bid-sheet__row { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: var(--space-2); }
.bid-sheet__row:last-child { margin-bottom: 0; }
.bid-sheet__k { font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-tertiary); }
.bid-sheet__cur { font-family: var(--font-latin); font-size: var(--fs-h4); font-weight: var(--fw-bold); color: var(--yellow-500); font-variant-numeric: tabular-nums; }
.bid-sheet__chips { display: flex; gap: var(--space-2); margin-bottom: var(--space-4); }
.bid-sheet__chip { flex: 1; min-height: 44px; border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--white-alpha-4); color: var(--text-primary); font-family: var(--font-latin); font-size: var(--fs-body-sm); font-weight: var(--fw-semibold); cursor: pointer; transition: border-color 160ms var(--ease-out), background 160ms var(--ease-out); }
.bid-sheet__chip:hover { border-color: var(--border-strong); }
.bid-sheet__chip.is-selected { border-color: var(--yellow-500); background: rgba(255, 163, 63, 0.08); }
.bid-sheet__note { margin: var(--space-3) 0 0; font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }
/* 狀態卡下面那行「N 人競標 · N 人關注」 */
.bid-sheet__meta { margin: calc(var(--space-4) * -1) 0 var(--space-5); font-family: var(--font-cjk-text); font-size: var(--fs-caption); color: var(--text-tertiary); }

/* 出價紀錄（別人的出價，即時進來） */
.bid-act { margin-bottom: var(--space-5); }
.bid-act__title { margin: 0 0 var(--space-3); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--text-secondary); }
.bid-act__list { margin: 0; padding: 0; list-style: none; }
.bid-act__row {
  display: flex; align-items: center; gap: var(--space-3);
  padding: var(--space-3) 0;
  border-top: 1px solid var(--border-subtle);
}
.bid-act__row:first-child { border-top: 0; }
.bid-act__avatar {
  flex: 0 0 auto; width: 28px; height: 28px; border-radius: var(--radius-pill);
  display: grid; place-items: center;
  background: var(--white-alpha-8); color: var(--text-secondary);
  font-family: var(--font-latin); font-size: var(--fs-caption); font-weight: var(--fw-bold);
}
.bid-act__name { flex: 1 1 auto; min-width: 0; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bid-act__amt { flex: 0 0 auto; font-family: var(--font-latin); font-size: var(--fs-body-sm); font-weight: var(--fw-bold); color: var(--text-primary); font-variant-numeric: tabular-nums; }
/* 你自己的那筆：橘字（同站上「這是我」的語彙） */
.bid-act__row.is-me .bid-act__name,
.bid-act__row.is-me .bid-act__amt { color: var(--brand-500); }
.bid-act__row.is-me .bid-act__avatar { background: color-mix(in srgb, var(--brand-500) 22%, transparent); color: var(--brand-500); }
/* 新進來的那筆閃一下就退掉（別用 opacity 過場——會閃白，見 memory） */
.bid-act__row.is-new { animation: bidActIn 1.2s var(--ease-out); }
@keyframes bidActIn {
  0%   { background: color-mix(in srgb, var(--brand-500) 18%, transparent); }
  100% { background: transparent; }
}
@media (prefers-reduced-motion: reduce) { .bid-act__row.is-new { animation: none; } }
/* 成功頁把金額提出來（.cc-success__msg 整段是次要色，這顆要是主色粗體） */
.cc-success__amount { color: var(--text-primary); font-weight: var(--fw-bold); }

/* 訂單列「已被超越」的狀態欄：小字狀態 ＋ 橘色玻璃「提高出價」鈕（0806 Dolly）。
   鈕的外觀完全來自既有的 .btn--yellow-ghost（玻璃底＋橘內光），這裡只排版。
   ⚠️ 它是 role=button 的 <span>——狀態欄住在 .olist__main（button）內，放真按鈕
   會被 parser 拆掉整列（見 my-orders.js listRow 註解）。 */
/* 小字與鈕互相置中對齊（0806 Dolly）——鈕比小字寬，靠左切齊會看起來歪 */
.olist__statusact { display: inline-flex; flex-direction: column; align-items: center; gap: 6px; }
.olist__statusact-note {
  font-family: var(--font-cjk-text); font-size: var(--fs-caption); line-height: 1;
  color: var(--text-tertiary);
}
/* .btn 的 padding 只在尺寸 modifier 上，這裡沿用 --sm，只收窄一點讓 108px 欄裝得下 */
.olist__rebid.btn--sm { padding: 7px 14px; }

.pop-sheet__balance { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: var(--space-4); padding: var(--space-4); border: 1px solid var(--border-default); border-radius: var(--radius-lg); background: var(--bg-secondary); }
.pop-sheet__balance-v { font-family: var(--font-latin); font-size: var(--fs-h4); font-weight: var(--fw-bold); color: var(--yellow-500); font-variant-numeric: tabular-nums; }
.pop-sheet__row { display: flex; align-items: baseline; justify-content: space-between; font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--text-secondary); padding: var(--space-2) 0; }
.pop-sheet__row--total { border-top: 1px solid var(--border-subtle); margin-top: var(--space-2); padding-top: var(--space-3); color: var(--text-primary); font-weight: var(--fw-bold); }
.pop-sheet__insufficient { margin: var(--space-3) 0; padding: var(--space-3) var(--space-4); border: 1px solid var(--error-500); border-radius: var(--radius-md); font-family: var(--font-cjk-text); font-size: var(--fs-body-sm); color: var(--error-500); }
.pop-sheet__packages { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
.pop-sheet__package { display: flex; align-items: center; justify-content: space-between; min-height: 52px; padding: var(--space-3) var(--space-4); border: 1px solid var(--border-default); border-radius: var(--radius-md); background: var(--white-alpha-4); color: var(--text-primary); cursor: pointer; transition: border-color 160ms var(--ease-out), background 160ms var(--ease-out); }
.pop-sheet__package.is-selected { border-color: var(--yellow-500); background: rgba(255, 163, 63, 0.08); }
.pop-sheet__package-pop { font-family: var(--font-latin); font-weight: var(--fw-bold); }
.pop-sheet__package-price { font-family: var(--font-latin); font-size: var(--fs-body-sm); color: var(--text-secondary); }

@media (prefers-reduced-motion: reduce) {
  .bid-sheet__chip, .pop-sheet__package { transition: none; }
}


/* ── 如何競標說明面板（2026-08-07）：shop-auction.html 分類列最右的入口 ＋
   右側 ds-drawer 的內容層（.howto-*）。內容是靜態 markup，沒有行為。 */
/* AUCTION HOW-TO (.shop-howto trigger + .howto-* 面板內容層). [COMPONENT] */
/* ─────────────────────────────────────────────
   如何競標  ·  shop-auction.html 分類列最右的說明入口 → 右側 ds-drawer
   面板（桌機右滑出／手機底部，骨架＝55-ds-drawer.css＋assets/ds-drawer.js）。
   markup 是 shop-auction.html 的靜態內容（沒有 JS，純說明）。

   ⚠️ 這頁 body 帶 theme-sharp（radius token 全歸零），所以內容層刻意
      **不用卡片**——只有數字圓標（--radius-pill 不受 theme-sharp 影響）
      與髮絲線分隔。這樣就不必把本面板加進 section-themes.css 的浮層
      radius 還原清單（那檔一改就要全站 bump ?v）。
   ───────────────────────────────────────────── */

/* ── 觸發器：分類列最右的文字鈕 ──
   同排的 .shop-filter__item 是 14px／tertiary，這顆刻意同級同色，hover 才轉白：
   它是輔助說明，不該跟「全部／入場券／收藏珍品」搶那條橘色底線。 */
.shop-howto {
  flex: 0 0 auto;
  margin-left: auto;            /* .shop-count 目前 display:none，靠自己貼右緣 */
  align-self: center;           /* .shop-toolbar 是 baseline 對齊，inline-flex 的
                                   baseline 會落在 icon 上而浮高（同 60-pdp-sheet
                                   的 __guide 註解） */
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 0;
  border: 0;
  background: none;
  font-family: var(--font-cjk-text);
  font-size: 14px;
  line-height: 1.2;
  color: var(--text-tertiary);
  cursor: pointer;
  transition: color 150ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .shop-howto:hover { color: var(--neutral-white); }
}
.shop-howto:focus-visible { outline: 2px solid var(--yellow-500); outline-offset: 3px; }
.shop-howto__icon { display: inline-grid; place-items: center; }
.shop-howto__icon svg { width: 15px; height: 15px; display: block; }

/* ⚠️ 手機（≤767.98px）看不到這顆：整條 .shop-toolbar 被 67-shop-filter-sheet.css
   收掉，分類改走底部 sheet（shop-filter-sheet.js 注入）。手機要不要有「如何競標」
   入口＝待 Dolly 決定，那會是 sheet 裡的一列，不是這顆按鈕。 */

/* ── 面板內容層 ── */
.howto-lead {
  margin: 0 0 var(--space-6);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  line-height: 1.75;
  color: var(--text-secondary);
}

/* 四步驟：數字圓標＋往下的連接線，讀起來是一條流程而不是四則條列 */
.howto-steps { margin: 0; padding: 0; list-style: none; }
.howto-step {
  position: relative;
  display: flex;
  gap: var(--space-4);
  padding-bottom: var(--space-5);
}
.howto-step:last-child { padding-bottom: 0; }
.howto-step:not(:last-child)::before {
  content: '';
  position: absolute;
  left: 13px;                   /* = 圓標中線（26 / 2） */
  top: 32px;
  bottom: 4px;
  width: 1px;
  background: var(--border-default);
}
.howto-step__n {
  flex: 0 0 auto;
  width: 26px;
  height: 26px;
  border-radius: var(--radius-pill);
  display: grid;
  place-items: center;
  background: color-mix(in srgb, var(--brand-500) 16%, transparent);
  color: var(--brand-500);
  font-family: var(--font-latin);
  font-size: 13px;
  font-weight: var(--fw-bold);
  line-height: 1;
}
.howto-step__c { min-width: 0; }
.howto-step__t {
  margin: 4px 0 6px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  line-height: 1.3;
  color: var(--text-primary);
}
.howto-step__d {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  line-height: 1.8;
  color: var(--text-tertiary);
}
.howto-step__d b { font-weight: var(--fw-bold); color: var(--text-secondary); }
.howto-link {
  color: var(--brand-500);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-thickness: 1px;
}
.howto-link:hover { color: var(--yellow-400); }

/* 小節：一條髮絲線分段，不用卡片（見檔頭 theme-sharp 註記） */
.howto-sect {
  margin-top: var(--space-8);
  padding-top: var(--space-5);
  border-top: 1px solid var(--border-subtle);
}
.howto-sect__t {
  margin: 0 0 var(--space-2);
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  font-weight: var(--fw-bold);
  line-height: 1.4;
  color: var(--text-primary);
}

/* 費用：左項目右說明，跟出價面板的 .bid-sheet__row 同一種讀法 */
.howto-fees { margin: 0; }
.howto-fees__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  padding: 10px 0;
  border-bottom: 1px solid var(--border-subtle);
}
.howto-fees__row:last-child { border-bottom: 0; }
.howto-fees dt {
  font-family: var(--font-cjk-text);
  font-size: var(--fs-body-sm);
  color: var(--text-secondary);
}
.howto-fees dd {
  margin: 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  color: var(--text-tertiary);
  text-align: right;
}
.howto-note {
  margin: var(--space-3) 0 0;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  line-height: 1.7;
  color: var(--text-tertiary);
}

.howto-rules { margin: 0; padding: 0; list-style: none; }
.howto-rules li {
  position: relative;
  margin-top: var(--space-3);
  padding-left: 16px;
  font-family: var(--font-cjk-text);
  font-size: var(--fs-caption);
  line-height: 1.8;
  color: var(--text-tertiary);
}
.howto-rules li::before {
  content: '';
  position: absolute;
  left: 2px;
  top: 9px;
  width: 4px;
  height: 4px;
  border-radius: var(--radius-pill);
  background: var(--white-alpha-24);
}
.howto-rules b { font-weight: var(--fw-bold); color: var(--text-secondary); }

/* ── 底部固定操作列 ──
   .ds-actionbar 沒被移植進 f2e，皮膚由消費者自己給（同 90-bid-sheet 的作法），
   只 scope 到本面板，別動其他 drawer。 */
[data-ds-drawer="auction-howto"] .ds-drawer__foot {
  padding: var(--space-5) var(--space-6) max(var(--space-6), env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--border-default);
  background: var(--bg-secondary);
}
[data-ds-drawer="auction-howto"] .ds-drawer__foot .btn { width: 100%; }

