/* 万物可期主页面样式。
 *
 * 原先内联在 index.html 的 <style> 里（约 1900 行，占该文件一半以上），
 * 拆出为独立文件便于维护，同时纳入 scripts/stamp-assets.mjs 的哈希版本管理，
 * 内容变更后 ?v= 会自动更新，不会出现回访用户拿到旧样式的情况。
 *
 * 仅 index.html 使用；八个子专题页用的是 topic-page.css。
 */

:root {
  --bg: #070b12;
  --panel: #101723;
  --panel-2: #151f2c;
  --card: rgba(255, 255, 255, .92);
  --ink: #111827;
  --muted: #687385;
  --line: rgba(255, 255, 255, .14);
  --soft-line: #dce4ee;
  --brand: #ef553f;
  --brand-dark: #c63a2f;
  --cyan: #49c6ff;
  --gold: #f1b85b;
  --green: #1c9b78;
  --blue: #3568f2;
  --shadow: 0 22px 60px rgba(0, 0, 0, .28);
  --soft-shadow: 0 14px 36px rgba(12, 20, 31, .12);
}

* { box-sizing: border-box; }

/* 顶栏是 sticky 常驻的，锚点跳转若不留出等高的外边距，
   目标区块的顶部会被顶栏盖住。8px 是额外的呼吸空间。
   新增锚点目标时记得一并加上这个类。 */
#topStage,
#serviceSection,
#commoditySection {
  scroll-margin-top: calc(58px + env(safe-area-inset-top) + 8px);
}

html {
  scroll-behavior: smooth;
  /* 禁用双击放大与捏合缩放；单指滚动不受影响。
     配合 viewport meta 与 generated-assets/no-zoom.js 一起生效。 */
  touch-action: pan-x pan-y;
}

body {
  margin: 0;
  min-height: 100vh;
  background:
    radial-gradient(circle at 50% -8%, rgba(73, 198, 255, .18), transparent 26%),
    linear-gradient(180deg, #070b12 0%, #101723 48%, #edf2f7 48%, #edf2f7 100%);
  color: var(--ink);
  font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Microsoft YaHei", Arial, sans-serif;
}

button, a {
  font-family: inherit;
  -webkit-tap-highlight-color: transparent;
}

button {
  border: 0;
  cursor: pointer;
}

.phone {
  width: min(100%, 430px);
  min-height: 100vh;
  margin: 0 auto;
  position: relative;
  /* 必须用 overflow: clip，两个写法都不能换：
     · 不能用 overflow: hidden —— 它会把 .phone 变成滚动容器，
       内部 .topbar 的 position: sticky 会改为相对 .phone 定位而失效，顶栏跟着页面滚走。
     · 不能只写 overflow-x: clip —— 纵向不裁剪时，子元素的直角会从
       .phone 的圆角外露出来（子页面宽屏下有 border-radius: 20px）。
     clip 两个方向都裁剪且不创建滚动容器，同时满足圆角裁剪与 sticky。 */
  overflow: clip;
  background: #edf2f7;
  box-shadow: 0 0 0 1px rgba(255,255,255,.08), 0 34px 90px rgba(5, 9, 14, .28);
  /* 为底部 CTA 与「返回置顶」按钮让位。距视口底部（另加安全区）：
     CTA 0~54，按钮 70~108，风险提示末行 75+20(.risk 下内边距)=95 起、至 112。
     当前值下末行与按钮纵向重叠 13px，但末行较短、未延伸到按钮所在的右侧，
     视觉上不显。若风险提示文案变长或换行位置改变，需增至 88px（刚好不重叠）
     或 100px（留 12px 间隙）。改这里时 .jump-filter 的 bottom 要同步考虑。 */
  padding-bottom: calc(75px + env(safe-area-inset-bottom));
}

.topbar {
  position: sticky;
  top: 0;
  z-index: 30;
  height: calc(58px + env(safe-area-inset-top));
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: env(safe-area-inset-top) 16px 0;
  color: #fff;
  background: rgba(7, 11, 18, .74);
  border-bottom: 1px solid rgba(255,255,255,.10);
  backdrop-filter: blur(18px);
}

.topbar-actions {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.brand {
  display: flex;
  align-items: center;
  flex: 0 1 auto;
  min-width: 0;
  line-height: 0;
}

.brand-logo {
  display: block;
  width: auto;
  height: 32px;
  max-width: 168px;
  object-fit: contain;
}

.top-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 32px;
  padding: 0 10px;
  border-radius: 999px;
  color: #ffddc8;
  background: rgba(239, 85, 63, .14);
  border: 1px solid rgba(239, 85, 63, .36);
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
}

/* 分享按钮位于 .topbar-actions 内，随 sticky 顶栏常驻。
   不要改成 position: fixed —— .phone 是 430px 居中，固定定位会相对浏览器视口，
   宽屏下按钮会飘到窗口边缘，并盖住同在右上角的「投教&资讯」标签。 */
.share-btn {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  min-height: 32px;
  padding: 0 11px;
  border-radius: 999px;
  color: #33465c;
  background: #fff;
  border: 1px solid rgba(16, 32, 51, .14);
  font-size: 12px;
  font-weight: 800;
  white-space: nowrap;
  -webkit-tap-highlight-color: transparent;
  transition: background .16s ease, border-color .16s ease;
}

.share-btn:active {
  background: #f2f5f9;
  border-color: rgba(16, 32, 51, .22);
}

.share-btn svg {
  width: 14px;
  height: 14px;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* 窄屏顶栏要同时容纳 logo、投教标签和分享按钮，
   空间不足时只保留图标，aria-label 已提供无障碍名称。 */
@media (max-width: 360px) {
  .share-btn {
    padding: 0 9px;
    gap: 0;
  }

  .share-btn-text {
    display: none;
  }
}

.share-toast {
  /* 顶栏高度 58px + 安全区，正好落在分享按钮正下方、右对齐顶栏内边距 */
  position: fixed;
  top: calc(58px + env(safe-area-inset-top) + 10px);
  right: 16px;
  z-index: 100;
  display: flex;
  align-items: center;
  gap: 8px;
  max-width: calc(100% - 32px);
  padding: 11px 15px;
  border-radius: 12px;
  color: #fff;
  background: rgba(14, 19, 27, .92);
  box-shadow: 0 12px 30px rgba(0, 0, 0, .24);
  font-size: 15px;
  font-weight: 700;
  opacity: 0;
  pointer-events: none;
  /* 从右侧滑入：先整体移出视口右缘，再归位 */
  transform: translateX(calc(100% + 16px));
  transition: opacity .26s ease, transform .3s cubic-bezier(.2, .8, .3, 1);
}

.share-toast-icon {
  flex: 0 0 auto;
  width: 17px;
  height: 17px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.share-toast-text {
  min-width: 0;
}

.share-toast.is-visible {
  opacity: 1;
  transform: translateX(0);
}

/* 微信内的分享引导蒙层。
   微信 WebView 不允许用代码唤起分享面板（防诱导传播），JS-SDK 也只能预设
   分享卡片的标题与缩略图、无法主动打开菜单，因此这里只能引导用户点右上角 ···。
   z-index 要高于 .share-toast(100)、.jump-filter(32) 与顶栏。 */
.wx-guide {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: flex;
  align-items: flex-start;
  justify-content: flex-end;
  padding: calc(12px + env(safe-area-inset-top)) 18px 0;
  background: rgba(8, 13, 20, .74);
  opacity: 0;
  transition: opacity .2s ease;
  -webkit-tap-highlight-color: transparent;
}

.wx-guide.is-visible { opacity: 1; }
.wx-guide[hidden] { display: none; }

.wx-guide-inner {
  max-width: 262px;
  text-align: right;
  color: #fff;
}

.wx-guide-arrow {
  display: block;
  width: 56px;
  height: 56px;
  margin-left: auto;
  margin-right: 6px;
  color: #6fe3a0;
  fill: none;
  stroke: currentColor;
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.wx-guide-title {
  margin: 8px 0 0;
  font-size: 17px;
  font-weight: 900;
}

.wx-guide-sub {
  margin: 7px 0 0;
  color: rgba(255, 255, 255, .72);
  font-size: 13px;
  line-height: 1.6;
}

.wx-guide-actions {
  margin-top: 15px;
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}

.wx-guide-btn {
  min-height: 36px;
  padding: 0 15px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 900;
  -webkit-tap-highlight-color: transparent;
}

.wx-guide-btn.primary {
  color: #0e131b;
  background: #fff;
  border: 0;
}

.wx-guide-btn.ghost {
  color: #fff;
  background: transparent;
  border: 1px solid rgba(255, 255, 255, .42);
}

.stage {
  min-height: 622px;
  padding: 18px 14px 20px;
  color: #fff;
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(circle at 82% 13%, rgba(239, 85, 63, .26), transparent 22%),
    radial-gradient(circle at 20% 30%, rgba(73, 198, 255, .22), transparent 34%),
    linear-gradient(180deg, #07101b 0%, #0b111c 54%, #111a27 100%);
}

.stage::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(rgba(255,255,255,.055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255,255,255,.055) 1px, transparent 1px);
  background-size: 34px 34px;
  mask-image: linear-gradient(180deg, rgba(0,0,0,.78), transparent 72%);
  pointer-events: none;
}

.stage::after {
  content: "";
  position: absolute;
  left: -90px;
  right: -90px;
  bottom: -150px;
  height: 280px;
  border-radius: 50%;
  border-top: 1px solid rgba(73, 198, 255, .28);
  background: radial-gradient(ellipse at center, rgba(73, 198, 255, .12), transparent 58%);
  pointer-events: none;
}

.hero-copy {
  position: relative;
  z-index: 2;
  padding: 10px 4px 8px;
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 10px;
  border-radius: 999px;
  color: #cbeeff;
  background: rgba(73, 198, 255, .10);
  border: 1px solid rgba(73, 198, 255, .22);
  font-size: 12px;
  font-weight: 900;
}

.eyebrow::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--cyan);
  box-shadow: 0 0 0 5px rgba(73, 198, 255, .14), 0 0 20px rgba(73, 198, 255, .82);
}

h1 {
  margin: 15px 0 10px;
  font-size: 38px;
  line-height: 1.05;
  letter-spacing: 0;
}

.hero-copy p {
  margin: 0;
  max-width: 350px;
  color: rgba(232, 242, 255, .78);
  font-size: 14px;
  line-height: 1.7;
}

.hero-video {
  position: relative;
  z-index: 2;
  margin-top: 16px;
  border-radius: 18px;
  overflow: hidden;
  background: #0f172a;
  border: 1px solid rgba(255,255,255,.14);
  box-shadow: var(--shadow);
}

/* 静音自动播放后，供用户主动开启声音的浮层按钮。
   控制栏自带的喇叭按钮 3 秒无操作就随控制栏隐藏，这颗常驻按钮是稳定的取消静音入口。
   z-index 取 100：要盖过播放器内部 UI（xgplayer 内部最高到 91），但低于全屏时
   播放器根节点的 9999，这样进入全屏后本按钮会自然退到播放器之下，不会浮在画面上。 */
.unmute-btn {
  position: absolute;
  top: 10px;
  right: 10px;
  z-index: 100;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 7px 11px;
  color: #fff;
  background: rgba(12, 20, 32, .62);
  border: 1px solid rgba(255, 255, 255, .26);
  border-radius: 999px;
  font-size: 11px;
  font-weight: 800;
  line-height: 1;
  backdrop-filter: blur(8px);
  -webkit-tap-highlight-color: transparent;
  transition: opacity .2s ease;
}

.unmute-btn svg {
  width: 14px;
  height: 14px;
  fill: currentColor;
}

.unmute-btn svg .x {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
}

.unmute-btn[hidden] {
  display: none;
}

/* 全屏时把整个 .hero-video 抬到最高层。
   它自身的 z-index: 2 会创建层叠上下文，把播放器全屏的 z-index: 9999 困在里面，
   导致地图（z-index 2、DOM 更靠后）、顶栏（30）、底部 CTA（200）压在全屏画面之上。
   类由 video-player.js 的 bindFullscreenLift 观察播放器 class 后切换。
   同时解除 overflow 与圆角，避免全屏画面被容器裁切。 */
.hero-video.is-player-fullscreen {
  z-index: 10000;
  overflow: visible;
  border-radius: 0;
}

/* 全屏时 contain 会露出上下留白。容器底色与 xgplayer 自带的 .xgplayer{background:#000}
   落在同一元素、同优先级，本站样式后加载会盖掉它，留白就成了浅色——这里显式改回黑色。 */
.hero-video-player.xgplayer-is-cssfullscreen,
.hero-video-player.xgplayer-is-fullscreen,
.hero-video-player.xgplayer-rotate-fullscreen {
  background: #000;
}

/* 播放器容器。xgplayer 以 fluid 模式按 16:9 内联撑高，这里只负责底色与裁切方式 */
.hero-video-player {
  width: 100%;
  background: #dce7f0;
}

.hero-video-player video {
  object-fit: cover;
}

/* 全屏时容器变成竖屏（如 390x844），而视频是 16:9 横向。此时若仍用 cover，
   会按高度放大约 1.76 倍再裁掉左右，只剩画面中间约四分之一——必须改回 contain。
   非全屏时容器本身就是 16:9，cover 与 contain 等效，故只在全屏态覆盖。 */
.hero-video-player.xgplayer-rotate-fullscreen video,
.hero-video-player.xgplayer-is-cssfullscreen video,
.hero-video-player.xgplayer-is-fullscreen video {
  object-fit: contain;
}

/* 覆盖 xgplayer 默认的字节红进度条，统一到本站主色。
   注意 .xgplayer 类是加在容器自身上的（与 .hero-video-player 同元素），不是子元素，
   因此这里必须写成连写的 .hero-video-player.xgplayer，写成后代选择器不会生效。 */
.hero-video-player.xgplayer .xgplayer-progress .xgplayer-progress-played {
  background: var(--brand);
}

.video-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 12px;
  padding: 42px 14px 13px;
  color: #fff;
  background: linear-gradient(180deg, transparent, rgba(5, 9, 14, .76));
  pointer-events: none;
}

.video-caption b {
  display: block;
  font-size: 15px;
  line-height: 1.35;
}

.video-caption span {
  display: block;
  margin-top: 4px;
  color: rgba(255,255,255,.72);
  font-size: 11px;
  font-weight: 800;
}

.video-pill {
  flex: 0 0 auto;
  padding: 6px 9px;
  border-radius: 999px;
  background: rgba(239, 85, 63, .92);
  color: #fff;
  font-size: 11px;
  font-weight: 900;
  white-space: nowrap;
}

.map-stage {
  position: relative;
  z-index: 2;
  height: 318px;
  margin-top: 18px;
  border-radius: 18px;
  overflow: hidden;
  background:
    radial-gradient(circle at 58% 48%, rgba(73, 198, 255, .18), transparent 30%),
    linear-gradient(145deg, rgba(15, 31, 49, .86), rgba(5, 9, 14, .92));
  border: 1px solid rgba(255,255,255,.14);
  box-shadow: var(--shadow), inset 0 0 0 1px rgba(255,255,255,.06);
}

.map-stage.has-selection {
  background:
    radial-gradient(circle at var(--focus-x) var(--focus-y), rgba(239, 85, 63, .26), transparent 18%),
    radial-gradient(circle at 58% 48%, rgba(73, 198, 255, .16), transparent 34%),
    linear-gradient(145deg, rgba(15, 31, 49, .86), rgba(5, 9, 14, .92));
}

.map-label {
  position: absolute;
  left: 14px;
  top: 13px;
  z-index: 4;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 7px 10px;
  border-radius: 999px;
  color: rgba(235, 247, 255, .86);
  background: rgba(7, 11, 18, .45);
  border: 1px solid rgba(255,255,255,.12);
  font-size: 11px;
  font-weight: 900;
  backdrop-filter: blur(10px);
}

.map-label::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: #22d3ee;
  box-shadow: 0 0 15px rgba(34, 211, 238, .85);
}

.china-map {
  position: absolute;
  inset: 34px 14px 56px;
  width: calc(100% - 28px);
  height: calc(100% - 90px);
  filter: drop-shadow(0 18px 26px rgba(0,0,0,.34));
  transition: transform .42s ease;
}

.map-stage.has-selection .china-map {
  transform: scale(1.035);
}

.china-map path {
  fill: rgba(63, 94, 121, .66);
  stroke: rgba(116, 202, 255, .42);
  stroke-width: 2;
}

.route-line {
  position: absolute;
  left: 18%;
  right: 15%;
  top: 56%;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(73,198,255,.48), rgba(239,85,63,.42), transparent);
  transform: rotate(-8deg);
  opacity: .72;
}

.map-marker {
  position: absolute;
  z-index: 5;
  width: 38px;
  height: 38px;
  padding: 0;
  border-radius: 50%;
  transform: translate(-50%, -50%);
  background: rgba(73, 198, 255, .10);
  border: 1px solid rgba(73, 198, 255, .46);
  box-shadow: 0 0 0 8px rgba(73, 198, 255, .08), 0 0 26px rgba(73, 198, 255, .28);
  color: #fff;
  transition: transform .24s ease, border-color .24s ease, box-shadow .24s ease;
}

.map-marker::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--cyan);
  transform: translate(-50%, -50%);
  box-shadow: 0 0 18px rgba(73, 198, 255, .88);
}

.map-marker::after {
  content: attr(data-label);
  position: absolute;
  left: 50%;
  top: 35px;
  transform: translateX(-50%);
  padding: 4px 8px;
  border-radius: 999px;
  color: rgba(255,255,255,.88);
  background: rgba(7, 11, 18, .58);
  border: 1px solid rgba(255,255,255,.14);
  font-size: 10px;
  font-weight: 900;
  white-space: nowrap;
  backdrop-filter: blur(10px);
}

.map-marker:not(.pending) {
  animation: pulse 2.8s ease-in-out infinite;
}

.map-marker.active,
.map-marker:hover {
  transform: translate(-50%, -50%) scale(1.14);
  border-color: rgba(239, 85, 63, .76);
  box-shadow: 0 0 0 10px rgba(239, 85, 63, .12), 0 0 34px rgba(239, 85, 63, .46);
}

.map-marker.active::before {
  background: var(--brand);
  box-shadow: 0 0 24px rgba(239, 85, 63, .95);
}

.map-marker.pending {
  background: rgba(255,255,255,.06);
  border-color: rgba(255,255,255,.18);
  box-shadow: none;
  opacity: .68;
}

.map-marker.pending::before {
  background: rgba(255,255,255,.48);
  box-shadow: none;
}

@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 6px rgba(73, 198, 255, .08), 0 0 24px rgba(73, 198, 255, .24); }
  50% { box-shadow: 0 0 0 12px rgba(73, 198, 255, .02), 0 0 32px rgba(73, 198, 255, .40); }
}

.map-drawer {
  position: relative;
  z-index: 6;
  margin: -66px 10px 0;
  padding: 14px;
  border-radius: 18px;
  color: #0f172a;
  background: rgba(255,255,255,.92);
  border: 1px solid rgba(255,255,255,.70);
  box-shadow: var(--shadow);
  backdrop-filter: blur(18px);
  transition: transform .22s ease, opacity .22s ease;
}

.drawer-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 10px;
}

.drawer-kicker {
  display: inline-flex;
  width: fit-content;
  padding: 5px 8px;
  border-radius: 999px;
  color: var(--brand-dark);
  background: #fff1ed;
  font-size: 11px;
  font-weight: 900;
}

.map-drawer h2 {
  margin: 8px 0 6px;
  font-size: 22px;
  line-height: 1.2;
  letter-spacing: 0;
}

.map-drawer p {
  margin: 0;
  color: var(--muted);
  font-size: 13px;
  line-height: 1.55;
}

/* 卡片右上角的品类药丸。此位置原是「实时行情」状态药丸，
   状态已并入价格标签行的备注文案，这里改放品类。
   配色取中性灰蓝：品类是分类信息，不宜沿用原来表示「实时」的蓝色。 */
.drawer-industry {
  flex: 0 0 auto;
  padding: 6px 9px;
  border-radius: 999px;
  color: #5a6b7d;
  background: #eef2f7;
  font-size: 11px;
  font-weight: 900;
  white-space: nowrap;
}

.btn {
  display: grid;
  place-items: center;
  min-height: 36px;
  border-radius: 12px;
  font-size: 13px;
  font-weight: 900;
  text-decoration: none;
}

.btn.primary {
  color: #fff;
  background: var(--brand);
  box-shadow: 0 12px 26px rgba(239, 85, 63, .24);
}

.btn.secondary {
  color: #17202a;
  background: #fff;
  border: 1px solid #dbe4ef;
}

.section {
  padding: 18px 14px 0;
}

.section-title {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 12px;
  padding: 0 2px 10px;
}

h2 {
  margin: 0;
  font-size: 20px;
  line-height: 1.25;
  letter-spacing: 0;
}

.more {
  color: #7b8796;
  font-size: 12px;
  font-weight: 900;
  white-space: nowrap;
}

.card {
  border-radius: 18px;
  background: #fff;
  border: 1px solid #dfe7f0;
  box-shadow: var(--soft-shadow);
}

.service-card {
  padding: 16px;
  position: relative;
  overflow: hidden;
  color: #fff;
  background:
    radial-gradient(circle at 88% 16%, rgba(239, 85, 63, .36), transparent 30%),
    linear-gradient(135deg, #151f2c 0%, #0e1622 100%);
  border-color: rgba(255,255,255,.10);
}

.service-card::after {
  content: "";
  position: absolute;
  right: -42px;
  bottom: -56px;
  width: 180px;
  height: 180px;
  border-radius: 50%;
  border: 22px solid rgba(255,255,255,.055);
}

.service-card small {
  display: inline-flex;
  padding: 6px 9px;
  border-radius: 999px;
  color: #ffdfd6;
  background: rgba(239, 85, 63, .16);
  border: 1px solid rgba(239, 85, 63, .28);
  font-size: 11px;
  font-weight: 900;
}

.service-card h2 {
  margin-top: 10px;
  max-width: 300px;
}

.service-card p {
  max-width: 300px;
  margin: 9px 0 14px;
  color: rgba(239, 246, 255, .72);
  font-size: 13px;
  line-height: 1.6;
}

.service-grid {
  position: relative;
  z-index: 1;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 9px;
}

.service-item {
  min-height: 78px;
  padding: 11px;
  border-radius: 14px;
  background: rgba(255,255,255,.09);
  border: 1px solid rgba(255,255,255,.12);
}

.service-item b {
  display: block;
  font-size: 13px;
  margin-bottom: 5px;
}

.service-item span {
  display: block;
  color: rgba(239,246,255,.64);
  font-size: 11px;
  line-height: 1.38;
}

.service-banner-link {
  display: block;
  border-radius: 18px;
  -webkit-tap-highlight-color: transparent;
}

.service-banner-link:active .service-banner {
  opacity: .85;
}

.service-banner {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 18px;
  background: #fff;
  border: 1px solid #dfe7f0;
  box-shadow: var(--soft-shadow);
}

/* 从「了解服务」跳转过来时闪烁两下，帮助用户定位到刚跳到的位置。
   单次动画 0.6s，重复 2 次共 1.2s，JS 中的移除时机需与之匹配。 */
.service-banner.is-highlighted {
  animation: service-flash .6s ease-in-out 2;
}

@keyframes service-flash {
  0%, 100% {
    box-shadow: 0 0 0 0 rgba(239, 85, 63, 0), 0 0 0 0 rgba(239, 85, 63, 0), var(--soft-shadow);
  }
  50% {
    box-shadow: 0 0 0 3px var(--brand), 0 0 0 9px rgba(239, 85, 63, .22), var(--soft-shadow);
  }
}

@media (prefers-reduced-motion: reduce) {
  .service-banner.is-highlighted {
    animation: none;
    box-shadow: 0 0 0 3px var(--brand), var(--soft-shadow);
  }

  .wx-guide {
    transition: none;
  }
}

.tabs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  padding: 0 2px 11px;
  scrollbar-width: none;
}

.tab {
  flex: 0 0 auto;
  min-height: 36px;
  padding: 0 14px;
  border-radius: 999px;
  color: #475569;
  background: #fff;
  border: 1px solid #dfe7f0;
  font-size: 13px;
  font-weight: 900;
}

.tab.active {
  color: #fff;
  background: #151f2c;
  border-color: #151f2c;
}

.episode-list {
  display: grid;
  /* 不用 gap：间距要参与展开/收起的高度动画，而 gap 不可动画。
     改为每张卡片带 margin-bottom，容器用等量负 margin 抵消最后一张，
     整体高度与原来的 gap: 10px 完全一致。 */
  margin-bottom: -10px;
}

.episode {
  display: grid;
  grid-template-columns: 112px 1fr;
  gap: 12px;
  margin-bottom: 10px;
  padding: 11px;
  /* 左列只有缩略图，右列是标签行 + 标题 + 行情行，缩略图垂直居中。
     底部原有的「产地 / 去观看」整行已取消，产地做成药丸并入标签行右端。 */
  align-items: center;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: transform .18s ease, border-color .18s ease, box-shadow .18s ease;
}

.episode:hover,
.episode.spotlight {
  transform: translateY(-1px);
  border-color: rgba(239, 85, 63, .38);
  box-shadow: 0 16px 40px rgba(239, 85, 63, .13);
}

.episode[hidden],
.episode.collapsed-extra {
  display: none;
}

/* 期数 / 最新 / 产地 同处一行：前两个靠左，产地药丸靠右。
   .tag 自带 margin-bottom: 6px，此处由容器统一控制间距，故清零。 */
/* grid 子项默认 min-width: auto（= 最小内容宽度）。右列里产地药丸是 nowrap 的，
   窄屏上它的最小内容宽度会超过 1fr 分到的空间，于是整张卡片被顶宽、
   再被 .phone 的 overflow: clip 裁掉——表现就是产地药丸和行情行在小屏上被切掉。
   min-width: 0 允许该列真正收缩到分配宽度，内部元素再各自省略或换行。 */
.episode-body {
  min-width: 0;
}

.episode-tags {
  display: flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 6px;
}

/* 两个药丸字号不同（.tag 11px、.episode-loc 10px），仅靠 padding 会差 1.2px 行高。
   统一 min-height 让盒高一致，字号差异保留——.tag 是主标签，本就该大一档。 */
.episode-tags .tag,
.episode-tags .episode-loc {
  min-height: 22px;
  display: inline-flex;
  align-items: center;
  margin-top: 0;
  margin-bottom: 0;
  vertical-align: baseline;
}

/* 产地｜品类药丸，推到行尾。
   必须可收缩（flex: 0 1 auto + min-width: 0）：此前是 0 0 auto 且 nowrap，
   窄屏（如 320px）上它宽度不肯让步，整条被挤出卡片，再被 .episode 的
   overflow: hidden 裁掉，表现为产地药丸在小屏上整个消失。现在改为宽度不够时
   自身省略号截断，始终留在卡片内。 */
.episode-loc {
  margin: 0 0 0 auto;
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  padding: 4px 8px;
  border-radius: 999px;
  color: #5a6b7d;
  background: #f1f5f9;
  font-size: 10px;
  font-weight: 800;
  white-space: nowrap;
}

.thumb {
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: 8px;
  object-fit: cover;
  object-position: center;
  background: #e7edf3;
}

.tag {
  display: inline-flex;
  width: fit-content;
  padding: 4px 8px;
  border-radius: 999px;
  color: var(--brand-dark);
  background: #fff1ed;
  font-size: 11px;
  font-weight: 900;
  margin-bottom: 6px;
}

/* 「最新」角标。原先是标签行里的第三颗药丸，窄屏上会把期数、产地一起挤到换行，
   现移到卡片左上角，样式沿用子页面行情卡的 .market-ribbon（同一套渐变与圆角语言）。
   直接挂在 .episode 上绝对定位：.episode 已有 position: relative 和 overflow: hidden，
   角标的直角会被卡片 18px 圆角裁出与行情卡一致的形状，并压住海报左上角一小块。 */
.episode-ribbon {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  /* min-width 把角标撑宽；inline-flex 居中让「最新」落在加宽后的正中，
     否则多出来的宽度会全部留在文字右侧（行情卡 .market-ribbon 就是这个表现）。 */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 56px;
  box-sizing: border-box;
  padding: 4px 11px;
  color: #fff;
  background: linear-gradient(112deg, #ff8734, #ef4057);
  border-radius: 0 0 12px 0;
  font-size: 10px;
  font-weight: 900;
  line-height: 1.4;
  letter-spacing: .3px;
}

/* 标题固定占两行高度。卡片高度由右列文字撑开、缩略图垂直居中，
   单行标题（如第一期「透视熔炉，读懂铜的K线」）会让卡片变矮、海报上下贴边，与其他期不齐；
   窄屏上长标题折成三行又会把卡片撑高。超出两行的部分直接裁掉。 */
.episode h3 {
  margin: 0;
  padding-right: 8px;
  height: calc(1.35em * 2);
  overflow: hidden;
  font-size: 15px;
  line-height: 1.35;
  letter-spacing: 0;
}

/* 标题确实被裁掉时（由 index.html 中 watchEpisodeTitleClamp 检测并加类），
   第二行末尾渐隐，提示后面还有字。两层遮罩：第一行全显，第二行右端 4em 渐变到透明。
   不用 line-clamp 省略号，是为了和内容方要的「逐渐淡去」一致。 */
.episode h3.is-clamped {
  -webkit-mask-image: linear-gradient(#000, #000), linear-gradient(to right, #000 calc(100% - 4em), transparent);
  mask-image: linear-gradient(#000, #000), linear-gradient(to right, #000 calc(100% - 4em), transparent);
  -webkit-mask-size: 100% 1.35em, 100% 1.35em;
  mask-size: 100% 1.35em, 100% 1.35em;
  -webkit-mask-position: 0 0, 0 1.35em;
  mask-position: 0 0, 0 1.35em;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* 「产业链 已上线」固定一行。原先 flex-wrap: wrap，窄屏上「碳酸锂（盐湖）产业链」
   会把「已上线」挤到第二行、卡片变高。现在不换行，超出部分裁掉，末尾渐隐（同标题）。 */
.quote-line {
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  overflow: hidden;
  white-space: nowrap;
  color: #475569;
  font-size: 12px;
  font-weight: 900;
}

.quote-line > span {
  flex-shrink: 0;
}

.quote-line.is-clamped {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 3em), transparent);
  mask-image: linear-gradient(to right, #000 calc(100% - 3em), transparent);
}

.up { color: var(--brand); }
.down { color: var(--green); }
.flat { color: #64748b; }

.list-toggle {
  width: 100%;
  min-height: 44px;
  margin-top: 12px;
  border-radius: 14px;
  color: #17202a;
  background: #fff;
  border: 1px solid #dfe7f0;
  box-shadow: var(--soft-shadow);
  font-size: 13px;
  font-weight: 900;
}

/* 上下内边距必须对称：卡片内边距会与 .news-row 的 13px 叠加，
   写成 4px / 16px 会让首条上方 17px、末条下方 29px，视觉上明显上紧下松。 */
/* 上下不留内边距：行自身已有 13px/14px，卡片再叠 4px 会让首行标题上方变成 18px、
   末行下方变成 19px，按分隔线看是 73/69/73，首行明显更松。留白交给行来控制。 */
.news {
  padding: 0 14px;
}

.news-row {
  padding: 13px 0;
  border-bottom: 1px solid #e9eef5;
}

/* 末行没有分隔线，会比前两行矮 1px（69/69/68）。
   这里把这 1px 补进 padding-bottom，三行等高。 */
.news-row:last-child {
  padding-bottom: 14px;
  border-bottom: 0;
}

.news-row b {
  display: block;
  font-size: 14px;
  line-height: 1.4;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.news-row span {
  display: block;
  margin-top: 6px;
  color: var(--muted);
  font-size: 11px;
}

a.news-row {
  display: block;
  color: inherit;
  text-decoration: none;
}

a.news-row:active b { opacity: .6; }

/* 资讯加载中的骨架屏 */
.news-row.is-loading b,
.news-row.is-loading span {
  border-radius: 4px;
  color: transparent;
  background: linear-gradient(90deg, #eef2f7 25%, #e3e9f1 37%, #eef2f7 63%);
  background-size: 400% 100%;
  animation: news-shimmer 1.4s ease-in-out infinite;
}

.news-row.is-loading b {
  width: 92%;
  height: 15px;
}

.news-row.is-loading span {
  width: 38%;
  height: 11px;
  margin-top: 8px;
}

.news-row.is-loading:nth-child(2) b { width: 78%; }
.news-row.is-loading:nth-child(3) b { width: 85%; }

@keyframes news-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

@media (prefers-reduced-motion: reduce) {
  .news-row.is-loading b,
  .news-row.is-loading span {
    animation: none;
  }
}

.news-error {
  padding: 16px 0 6px;
  color: var(--muted);
  font-size: 11px;
  text-align: center;
}

.risk {
  padding: 6px 18px 20px;
  color: #7b8796;
  font-size: 11px;
  line-height: 1.55;
}

.fixed-cta {
  position: fixed;
  left: 50%;
  bottom: 0;
  z-index: 30;
  transform: translateX(-50%);
  width: min(100%, 430px);
  min-height: 54px;
  padding: 7px 14px calc(7px + env(safe-area-inset-bottom));
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  background: rgba(255, 255, 255, .92);
  border-top: 1px solid #dfe7f0;
  backdrop-filter: blur(18px);
}

.jump-filter {
  position: fixed;
  right: max(16px, calc((100vw - 430px) / 2 + 16px));
  /* 贴着 .fixed-cta（高 54px + 安全区）上沿，留 16px 间隙；按钮自身高 38px。
     必须带 env(safe-area-inset-bottom)：CTA 会随安全区变高，
     若这里写死像素，刘海屏上按钮会被 CTA 顶住甚至重叠。 */
  bottom: calc(70px + env(safe-area-inset-bottom));
  z-index: 32;
  min-height: 38px;
  padding: 0 13px;
  border-radius: 999px;
  color: #fff;
  background: rgba(14, 22, 34, .9);
  box-shadow: 0 12px 28px rgba(12, 20, 31, .22);
  backdrop-filter: blur(12px);
  font-size: 12px;
  font-weight: 900;
  opacity: 0;
  pointer-events: none;
  transform: translateY(8px);
  transition: opacity .18s ease, transform .18s ease;
}

.jump-filter.show {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

@media (max-width: 360px) {
  h1 { font-size: 34px; }
  .stage { min-height: 646px; padding-left: 12px; padding-right: 12px; }
  .episode { grid-template-columns: 96px 1fr; }
}

/* Light premium theme */
:root {
  --bg: #eef3f8;
  --panel: #ffffff;
  --panel-2: #f6f9fc;
  --card: rgba(255, 255, 255, .94);
  --ink: #102033;
  --muted: #647386;
  --line: rgba(63, 98, 132, .13);
  --soft-line: #d9e3ee;
  --cyan: #238bd6;
  --shadow: 0 24px 64px rgba(52, 76, 103, .16);
  --soft-shadow: 0 14px 34px rgba(52, 76, 103, .10);
}

body {
  background:
    radial-gradient(circle at 18% 4%, rgba(35, 139, 214, .14), transparent 28%),
    radial-gradient(circle at 84% 10%, rgba(239, 85, 63, .11), transparent 26%),
    linear-gradient(180deg, #f8fbff 0%, #eef3f8 52%, #e9eff6 100%);
}

.phone {
  background: #eef3f8;
  box-shadow: 0 0 0 1px rgba(63, 98, 132, .08), 0 30px 86px rgba(52, 76, 103, .18);
}

.topbar {
  color: #102033;
  background: rgba(255, 255, 255, .86);
  border-bottom: 1px solid rgba(217, 227, 238, .86);
}

.top-chip {
  color: #b8352c;
  background: #fff3ef;
  border-color: #ffd6ca;
}

.stage {
  color: #102033;
  background:
    radial-gradient(circle at 78% 10%, rgba(239, 85, 63, .13), transparent 24%),
    radial-gradient(circle at 20% 26%, rgba(35, 139, 214, .16), transparent 34%),
    linear-gradient(180deg, #ffffff 0%, #f6faff 56%, #eef4fa 100%);
  border-bottom: 1px solid rgba(217, 227, 238, .88);
}

.stage::before {
  background-image:
    linear-gradient(rgba(35, 82, 124, .075) 1px, transparent 1px),
    linear-gradient(90deg, rgba(35, 82, 124, .075) 1px, transparent 1px);
  mask-image: linear-gradient(180deg, rgba(0,0,0,.72), transparent 76%);
}

.stage::after {
  border-top-color: rgba(35, 139, 214, .22);
  background: radial-gradient(ellipse at center, rgba(35, 139, 214, .10), transparent 60%);
}

.eyebrow {
  color: #136aa8;
  background: rgba(35, 139, 214, .09);
  border-color: rgba(35, 139, 214, .20);
}

.eyebrow::before {
  background: #238bd6;
  box-shadow: 0 0 0 5px rgba(35, 139, 214, .13), 0 0 18px rgba(35, 139, 214, .52);
}

.hero-copy p {
  color: #5d6e82;
}

.hero-video {
  background: #ffffff;
  border-color: rgba(205, 219, 232, .95);
  box-shadow: 0 22px 58px rgba(52, 76, 103, .18);
}

.hero-video .video-caption {
  position: static;
  /* 覆盖浮层版的 align-items: end——白色横条里药丸按整条高度上下居中。 */
  align-items: center;
  padding: 12px 14px;
  color: #102033;
  background: #fff;
  border-top: 1px solid #e3ebf3;
  pointer-events: auto;
}

.hero-video .video-caption span {
  color: #66778b;
}

/* 白底横条上不再用品牌橙填充，改为白色描边药丸，与横条同色系。 */
.hero-video .video-pill {
  color: #102033;
  background: #fff;
  border: 1px solid #dbe5ee;
}

.map-stage {
  background:
    radial-gradient(circle at 58% 48%, rgba(35, 139, 214, .14), transparent 31%),
    linear-gradient(145deg, rgba(255,255,255,.90), rgba(234, 242, 250, .92));
  border: 1px solid rgba(205, 219, 232, .95);
  box-shadow: 0 22px 58px rgba(52, 76, 103, .18), inset 0 0 0 1px rgba(255,255,255,.72);
}

.map-stage.has-selection {
  background:
    radial-gradient(circle at var(--focus-x) var(--focus-y), rgba(239, 85, 63, .18), transparent 18%),
    radial-gradient(circle at 58% 48%, rgba(35, 139, 214, .12), transparent 34%),
    linear-gradient(145deg, rgba(255,255,255,.90), rgba(234, 242, 250, .92));
}

.map-label {
  color: #365269;
  background: rgba(255, 255, 255, .76);
  border-color: rgba(205, 219, 232, .88);
  box-shadow: 0 10px 24px rgba(52, 76, 103, .08);
}

.map-label::before {
  background: #238bd6;
  box-shadow: 0 0 15px rgba(35, 139, 214, .58);
}

.china-map {
  filter: drop-shadow(0 18px 24px rgba(52, 76, 103, .18));
}

.china-map path {
  fill: rgba(191, 213, 231, .72);
  stroke: rgba(35, 139, 214, .36);
}

.route-line {
  background: linear-gradient(90deg, transparent, rgba(35,139,214,.42), rgba(239,85,63,.32), transparent);
  opacity: .86;
}

.map-marker {
  background: rgba(255, 255, 255, .78);
  border-color: rgba(35, 139, 214, .48);
  box-shadow: 0 0 0 8px rgba(35, 139, 214, .09), 0 10px 26px rgba(35, 139, 214, .18);
  color: #102033;
}

.map-marker::before {
  background: #238bd6;
  box-shadow: 0 0 18px rgba(35, 139, 214, .62);
}

.map-marker::after {
  color: #365269;
  background: rgba(255, 255, 255, .86);
  border-color: rgba(205, 219, 232, .92);
  box-shadow: 0 8px 20px rgba(52, 76, 103, .10);
}

.map-marker.active,
.map-marker:hover {
  border-color: rgba(239, 85, 63, .66);
  box-shadow: 0 0 0 10px rgba(239, 85, 63, .10), 0 12px 30px rgba(239, 85, 63, .22);
}

.map-marker.active::before {
  background: var(--brand);
  box-shadow: 0 0 20px rgba(239, 85, 63, .72);
}

.map-marker.pending {
  background: rgba(255,255,255,.66);
  border-color: rgba(120, 139, 158, .24);
  opacity: .74;
}

.map-marker.pending::before {
  background: #98a9ba;
}

@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 6px rgba(35, 139, 214, .08), 0 10px 24px rgba(35, 139, 214, .16); }
  50% { box-shadow: 0 0 0 12px rgba(35, 139, 214, .025), 0 12px 30px rgba(35, 139, 214, .22); }
}

.map-drawer {
  background: rgba(255,255,255,.94);
  border-color: rgba(255,255,255,.86);
  box-shadow: 0 24px 64px rgba(52, 76, 103, .18);
}

.service-card {
  color: #102033;
  background:
    radial-gradient(circle at 88% 16%, rgba(239, 85, 63, .13), transparent 30%),
    linear-gradient(135deg, #ffffff 0%, #f3f7fb 100%);
  border-color: #dfe7f0;
}

.service-card::after {
  border-color: rgba(35, 139, 214, .075);
}

.service-card small {
  color: #b8352c;
  background: #fff3ef;
  border-color: #ffd6ca;
}

.service-card p {
  color: #637386;
}

.service-item {
  background: rgba(246, 249, 252, .86);
  border-color: #dfe7f0;
}

.service-item span {
  color: #6d7d90;
}

.tab.active {
  color: #fff;
  background: #238bd6;
  border-color: #238bd6;
}

/* Refined shooting-footprint map */
.map-stage {
  height: 330px;
  isolation: isolate;
  background:
    radial-gradient(circle at 76% 20%, rgba(35, 139, 214, .13), transparent 25%),
    radial-gradient(circle at 18% 76%, rgba(66, 184, 131, .09), transparent 28%),
    linear-gradient(155deg, #fafdff 0%, #edf5fb 58%, #e5eef7 100%);
  box-shadow: 0 22px 58px rgba(52, 76, 103, .16), inset 0 0 0 1px rgba(255,255,255,.82);
}

.map-stage::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image:
    linear-gradient(rgba(52, 101, 142, .055) 1px, transparent 1px),
    linear-gradient(90deg, rgba(52, 101, 142, .055) 1px, transparent 1px);
  background-size: 24px 24px;
  mask-image: linear-gradient(180deg, rgba(0,0,0,.78), rgba(0,0,0,.30) 78%, transparent);
  pointer-events: none;
}

.map-stage::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  background: linear-gradient(180deg, rgba(255,255,255,.10), transparent 42%, rgba(119, 151, 178, .08));
  pointer-events: none;
}

.map-stage.has-selection {
  background:
    radial-gradient(circle at var(--focus-x) var(--focus-y), rgba(239, 85, 63, .18), transparent 14%),
    radial-gradient(circle at 76% 20%, rgba(35, 139, 214, .12), transparent 25%),
    radial-gradient(circle at 18% 76%, rgba(66, 184, 131, .08), transparent 28%),
    linear-gradient(155deg, #fafdff 0%, #edf5fb 58%, #e5eef7 100%);
}

.map-label,
.map-count {
  position: absolute;
  top: 13px;
  z-index: 10;
  min-height: 30px;
  display: inline-flex;
  align-items: center;
  border-radius: 999px;
  background: rgba(255,255,255,.84);
  border: 1px solid rgba(194, 212, 228, .90);
  box-shadow: 0 9px 22px rgba(52, 76, 103, .08);
  backdrop-filter: blur(12px);
}

.map-label {
  left: 13px;
  padding: 0 10px;
  color: #26465f;
  font-size: 10px;
}

.map-label::before {
  width: 6px;
  height: 6px;
  background: #238bd6;
  box-shadow: 0 0 0 4px rgba(35, 139, 214, .11);
}

.map-count {
  right: 13px;
  padding: 0 10px;
  color: #6a7b8d;
  font-size: 10px;
  font-weight: 800;
}

/* 「点击卡片查看专题」里的「卡片」二字标红。
   原先这里是数量数字，故带 margin-right 与放大的字号；
   现在是句中词，去掉外边距、字号与句子保持一致，只保留红色。 */
.map-count b {
  color: #d74635;
}

.china-map {
  z-index: 2;
  inset: 43px 20px 66px;
  width: calc(100% - 40px);
  height: calc(100% - 109px);
  filter: drop-shadow(0 14px 22px rgba(52, 76, 103, .16));
}

.china-map .land-mass,
.china-map .island {
  fill: rgba(189, 212, 230, .72);
  stroke: rgba(35, 126, 188, .48);
  stroke-width: 2;
}

.china-map .province-lines path {
  fill: none;
  stroke: rgba(82, 139, 181, .30);
  stroke-width: 1.1;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.route-network {
  position: absolute;
  inset: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

.route-network path {
  fill: none;
  vector-effect: non-scaling-stroke;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.route-shadow {
  stroke: rgba(255,255,255,.90);
  stroke-width: 4;
}

.route-main {
  stroke: rgba(48, 137, 199, .48);
  stroke-width: 1.5;
  stroke-dasharray: 3 5;
}

.map-marker {
  --marker-color: #238bd6;
  --marker-border: rgba(35, 139, 214, .52);
  --marker-ring: rgba(35, 139, 214, .12);
  --marker-glow: rgba(35, 139, 214, .62);
  z-index: 6;
  width: 42px;
  height: 42px;
  background: rgba(255,255,255,.88);
  border: 1px solid var(--marker-border);
  box-shadow: 0 8px 20px rgba(52, 76, 103, .13);
  animation: none;
}

.map-marker[data-category="agri"] { --marker-color: #3a9d69; --marker-border: rgba(58,157,105,.56); --marker-ring: rgba(58,157,105,.13); --marker-glow: rgba(58,157,105,.62); }
.map-marker[data-category="metal"] { --marker-color: #c5892f; --marker-border: rgba(197,137,47,.58); --marker-ring: rgba(197,137,47,.13); --marker-glow: rgba(197,137,47,.62); }
.map-marker[data-category="black"] { --marker-color: #586779; --marker-border: rgba(88,103,121,.56); --marker-ring: rgba(88,103,121,.13); --marker-glow: rgba(88,103,121,.58); }
.map-marker[data-category="chemical"] { --marker-color: #397be8; --marker-border: rgba(57,123,232,.56); --marker-ring: rgba(57,123,232,.13); --marker-glow: rgba(57,123,232,.62); }

.map-marker::before {
  width: 11px;
  height: 11px;
  background: var(--marker-color);
  box-shadow: 0 0 0 5px var(--marker-ring);
}

.map-marker::after {
  top: 41px;
  padding: 5px 8px;
  color: #27455f;
  background: rgba(255,255,255,.94);
  border-color: rgba(192, 210, 225, .94);
  box-shadow: 0 8px 18px rgba(52, 76, 103, .12);
  opacity: 0;
  transform: translate(-50%, -3px) scale(.94);
  transition: opacity .18s ease, transform .18s ease;
  pointer-events: none;
}

.map-marker:hover,
.map-marker:focus-visible,
.map-marker.active {
  z-index: 9;
  transform: translate(-50%, -50%) scale(1.12);
  border-color: var(--marker-color);
  box-shadow: 0 0 0 7px var(--marker-ring), 0 12px 28px rgba(52, 76, 103, .18);
}

.map-marker:focus-visible {
  outline: 2px solid #102f49;
  outline-offset: 3px;
}

.map-marker:hover::after,
.map-marker:focus-visible::after,
.map-marker.active::after {
  opacity: 1;
  transform: translate(-50%, 0) scale(1);
}

.map-marker.active {
  animation: selected-marker-pulse 2.5s ease-in-out infinite;
}

.map-marker.active::before {
  background: var(--marker-color);
  box-shadow: 0 0 0 5px var(--marker-ring), 0 0 19px var(--marker-glow);
}

.map-marker[data-commodity="gold"]::after {
  top: auto;
  bottom: 41px;
}

@keyframes selected-marker-pulse {
  0%, 100% { box-shadow: 0 0 0 6px var(--marker-ring), 0 11px 26px rgba(52, 76, 103, .17); }
  50% { box-shadow: 0 0 0 11px rgba(255,255,255,.04), 0 14px 31px rgba(52, 76, 103, .21); }
}

.map-legend {
  position: absolute;
  left: 13px;
  right: 13px;
  bottom: 73px;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  min-height: 26px;
  padding: 0 9px;
  border-radius: 999px;
  color: #657789;
  background: rgba(255,255,255,.68);
  border: 1px solid rgba(200, 216, 230, .72);
  font-size: 9px;
  font-weight: 800;
  backdrop-filter: blur(10px);
}

.map-legend span {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  white-space: nowrap;
}

.legend-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: #238bd6;
}

.legend-dot.agri { background: #3a9d69; }
.legend-dot.metal { background: #c5892f; }
.legend-dot.black { background: #586779; }
.legend-dot.chemical { background: #397be8; }

.map-drawer {
  padding: 11px 13px;
  border-radius: 18px;
  border-color: rgba(205, 219, 232, .92);
  box-shadow: 0 22px 52px rgba(52, 76, 103, .16);
}

.drawer-kicker {
  color: #b83d31;
  background: #fff2ee;
}

.map-quote-card .drawer-kicker {
  padding: 4px 7px;
  font-size: 10px;
}

.drawer-tags {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

/* 「最新」比期数弱一档：描边而非填充，避免两个实心胶囊并排抢视线 */
.drawer-latest {
  display: inline-flex;
  align-items: center;
  padding: 3px 7px;
  border-radius: 999px;
  color: #b83d31;
  background: transparent;
  border: 1px solid rgba(184, 61, 49, .34);
  font-size: 10px;
  font-weight: 900;
  white-space: nowrap;
}

.drawer-latest[hidden] {
  display: none;
}

/* 上下内边距必须与 .drawer-kicker 一致（4px），三个药丸才等高。
   本规则原是「实时行情」状态药丸留下的（5px），那时它独占右侧、
   没有对齐参照所以看不出来；现在与期数、最新并排，2px 差值很明显。
   .drawer-latest 用 3px padding + 1px 边框，合计同样是 8px，三者齐平。 */
.map-quote-card .drawer-industry {
  padding: 4px 8px;
  font-size: 10px;
}

.map-drawer h2 {
  /* 类别已并入同一行，标题下方不再需要为它留间距 */
  margin: 7px 0 0;
  font-size: 23px;
}

.map-quote-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
}

/* 品种名与类别同一行：类别靠右，沿用 .map-drawer p 的小字号。
   原先类别单独占一行，卡片因此多出一行高度。 */
/* 品类已移到右上角药丸，标题独占一行 */
.map-quote-title h2 {
  min-width: 0;
}

/* 「<合约名>最新价格」与「行情数据仅供参考」分置两端。
   这两项原先分散在价格上方与卡片底栏，合成一行后卡片少一行、也少一条分隔线。 */
.map-quote-labels {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  margin-top: 8px;
  padding-top: 7px;
  border-top: 1px solid #e3ebf2;
}

.map-quote-label {
  color: #7b8998;
  font-size: 10px;
  font-weight: 800;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* 沿用原底栏「行情数据仅供参考」的字号与颜色 */
.map-quote-note {
  flex: 0 0 auto;
  color: #7c8998;
  font-size: 9px;
}

.map-quote-card h2 {
  margin: 6px 0 2px;
  font-size: 20px;
}

.map-quote-card p {
  font-size: 11px;
  line-height: 1.35;
}

/* 分隔线已上移到 .map-quote-labels，这里不再重复画线，只留与标签行的间距 */
.map-quote-main {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  margin-top: 2px;
}

/* 价格与单位原先包在一层内嵌 div 里（外层还有「最新价」标签）。
   标签已移入 .map-quote-labels，这层 div 随之取消，其排布并到本身。 */
.map-quote-price {
  display: flex;
  align-items: baseline;
  gap: 5px;
  min-width: 0;
}

.map-quote-price strong {
  color: #142033;
  font-size: 29px;
  font-weight: 900;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.map-quote-price small {
  color: #78889a;
  font-size: 10px;
  font-weight: 700;
}

/* 涨跌块仍居于价格右侧，但内部的涨跌额与涨跌幅改为上下两行。
   原先两者横排，最新价位数多时（如 19,155）会把这一块挤到边缘甚至换行；
   竖排后该块宽度只取决于较长的那个数字，横向占用明显收窄。
   右对齐是为了让两行的个位数对齐，配合 tabular-nums 视觉更整齐。 */
.map-quote-change {
  display: inline-flex;
  flex-direction: column;
  align-items: flex-end;
  flex: 0 0 auto;
  gap: 3px;
  margin-bottom: 2px;
  font-size: 12px;
  font-weight: 900;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 两个 i 由父级的 column flex 各占一行，行距由 gap 控制，勿改回空格拼接 */
.map-quote-change i {
  font-style: normal;
}

.map-quote-change.up { color: #de4f3d; }
.map-quote-change.down { color: #138764; }
.map-quote-change.flat { color: #69798b; }

@media (max-width: 360px) {
  .map-stage { height: 322px; }
  .map-label { max-width: 164px; }
  .map-count { padding: 0 8px; font-size: 9px; }
  .map-legend { gap: 8px; padding: 0 7px; }
  .map-marker { width: 39px; height: 39px; }
  .map-marker::after { top: 38px; }
}

@media (prefers-reduced-motion: reduce) {
  .map-marker.active { animation: none; }
  .china-map, .map-marker, .map-marker::after { transition: none; }
}

.map-stage.echarts-map {
  /* 上边缘收窄 21px 后，下边界同步上移 21px，模块整体由 424px 变矮到 403px。
     画布的下内缩相应调到 50px，使地图尺寸基本不变（陆地高 285 -> 288px），
     只是整体上移，不会因模块变矮而缩小。 */
  height: 403px;
  background:
    radial-gradient(circle at 76% 18%, rgba(35, 139, 214, .13), transparent 28%),
    radial-gradient(circle at 18% 78%, rgba(66, 184, 131, .08), transparent 28%),
    linear-gradient(155deg, #fbfdff 0%, #edf5fb 58%, #e5eef7 100%);
}

.china-map-chart {
  position: absolute;
  /* 四个值依次是 上 / 左右 / 下 的内缩。
     上内缩曾是 45px——刚好让画布起点落在顶部两个胶囊（占 13~43px）下方，
     导致地图上方白掉一大片。胶囊有不透明底且层级在画布之上，地图从它们
     背后穿过并无妨，故收到 24px，把这段空白让给地图。
     下内缩 50px：舞台已由 424px 减到 403px，这里若也跟着减 21px，画布反而
     净增 21px、陆地南端会伸到卡片下面被压住。取 50px 是实测的平衡点——
     陆地高度 288px（原 285px，基本不变），南端距卡片顶部 12px。 */
  inset: 24px 8px 50px;
  z-index: 2;
  overflow: hidden;
  border-radius: 16px;
}

/* 贴地图右下角、压在 GeoJSON.CN 署名上方。
   原先在 top: 54px 的右上角，正好盖住东三省，把辽宁一带的点位挡掉了。
   下移后按钮列占据右侧 80px~178px（距底部），东三省在 50px~130px（距顶部）
   一带，两者错开。左边的图例 right: 58px 收边，与按钮列的 right: 12px + 38px
   宽度之间还留 8px 间隙，不会相撞。 */
.map-scale-controls {
  position: absolute;
  right: 12px;
  bottom: 80px;
  z-index: 12;
  display: grid;
  overflow: hidden;
  border-radius: 10px;
  background: rgba(255,255,255,.90);
  border: 1px solid rgba(194, 212, 228, .94);
  box-shadow: 0 10px 24px rgba(52, 76, 103, .13);
  backdrop-filter: blur(12px);
}

.map-control {
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  padding: 0;
  color: #29485f;
  background: transparent;
  border-bottom: 1px solid #dce7f0;
  font-size: 17px;
  font-weight: 700;
  line-height: 1;
}

.map-control:last-child { border-bottom: 0; }

.map-control:hover,
.map-control:focus-visible {
  color: #fff;
  background: #238bd6;
  outline: 0;
}

.map-control.reset {
  color: #b84135;
  font-size: 18px;
}

.map-control.reset:hover,
.map-control.reset:focus-visible {
  color: #fff;
  background: #d94f3e;
}

.echarts-map .map-legend {
  bottom: 60px;
  left: 12px;
  right: 58px;
  z-index: 8;
  justify-content: flex-start;
  width: fit-content;
  max-width: calc(100% - 72px);
  background: rgba(255,255,255,.84);
}

/* GeoJSON.CN 为地图数据方要求的署名，来自数据文件 meta.copyrightShort，
   可以弱化但请勿删除。 */
.map-attribution {
  position: absolute;
  right: 13px;
  bottom: 64px;
  z-index: 8;
  color: #b4c0cb;
  font-size: 8px;
  line-height: 1;
  opacity: .55;
}

.map-error {
  height: 100%;
  display: grid;
  place-items: center;
  margin: 0;
  padding: 24px;
  color: #64778a;
  font-size: 12px;
  text-align: center;
}

@media (max-width: 360px) {
  .map-stage.echarts-map { height: 404px; }
  .china-map-chart { left: 3px; right: 3px; }
  .map-scale-controls { right: 9px; }
  .map-control { width: 30px; height: 30px; font-size: 16px; }
  .echarts-map .map-legend { gap: 7px; right: 52px; max-width: calc(100% - 64px); }
}


/* ============ 行情卡堆 ============
   八张卡片绝对定位叠放，由 JS 的 layoutDeck() 用 transform 摆位。
   容器负责原先 .map-drawer 承担的负外边距（压在地图下沿）与左右留白。 */
/* ┌── 卡片与地图之间的间距，就调下面这一行的第一个值 ─────────────────┐
   │ margin: -30px 8px 0;                                          │
   │          ↑ 负得越多，卡片越往上、离地图越近                      │
   └───────────────────────────────────────────────────────────────┘

   地图舞台高 424px，底部这些元素都是从地图「底边」往上定位的：

     缩放按钮 .map-scale-controls   距底 80~178px
     图例     .map-legend          距底 60~86px
     署名     .map-attribution     距底 64~72px
     地图画布 .china-map-chart      下沿留 54px

   卡片顶部距地图底边 = 这里的负边距绝对值。
   肉眼看到的「空隙」= 图例底边(60px) − 卡片顶部，所以：

     -18px → 空隙 42px
     -30px → 空隙 30px
     -55px → 空隙 5px    ← 当前值，卡片几乎贴住图例

   卡片阴影是 0 22px 52px，会向上溢出约 30px。当前 -55px 下阴影上沿到
   距底 85px，已经盖过图例(60~86px)，图例是压在这层柔和阴影里的——
   这是刻意选择的观感，不是疏漏。若觉得图例发灰，把这个值调回 -30px 以内，
   或连带下移地图底部那几个元素（改它们各自的 bottom 值）。 */
.quote-deck {
  position: relative;
  z-index: 6;
  margin: -55px 8px 0;
}

.quote-deck-stack {
  position: relative;
  /* 高度由 JS 按最高的一张卡片 + 堆叠露出高度设定。
     transform 也由 JS 写入：滑到首尾后继续拖动时，整堆做橡皮筋位移。
     这里不加 transition，回弹由 rAF 动画逐帧驱动。 */
  will-change: transform;
}

.quote-deck-card {
  /* 卡片本身是超链接（点击进入该品种专题页），清掉 <a> 的默认外观。
     文字颜色由 .map-drawer 的 color 决定，这里继承即可。 */
  display: block;
  color: inherit;
  text-decoration: none;
  -webkit-tap-highlight-color: transparent;

  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  /* .map-drawer 基础规则仍带 margin: -66px 10px 0（那是单卡时代压地图用的）。
     卡片沿用了 .map-drawer 的外观样式，就会连这个外边距一起继承：
     纵向再被上拉 66px、横向又各缩进 10px——位置偏高与卡片变窄都出自这里。
     位置改由容器 .quote-deck 统一负责，这里必须显式清零。 */
  margin: 0;
  /* 变换原点必须在底边：原点若在顶部，缩放会把后卡的底边往上拉，
     抵消掉大部分下移量——DECK_PEEK 设 7px 时实际只露出 2~5px，看着几乎没有堆叠。
     原点放到底边后，缩放不动底边，露出高度恰好等于 DECK_PEEK。 */
  transform-origin: 50% 100%;
  will-change: transform, opacity;
  backface-visibility: hidden;
}

/* 圆点覆盖在卡片右侧，卡片内容右内边距相应加大避让。
   原先靠容器 padding-right 让位，那会把卡片整体压窄 20px。 */
.quote-deck-card .map-quote-head,
.quote-deck-card .map-quote-title,
.quote-deck-card .map-quote-labels,
.quote-deck-card .map-quote-main {
  padding-right: 14px;
}

/* 翻页圆点：竖排在卡堆右侧，与卡片垂直居中 */
/* 圆点要对齐「最前那张卡」的中心，不是整个卡堆的中心。
   卡堆比单卡高出 DECK_MAX_BEHIND × DECK_PEEK（露出的边缘），
   若按容器居中，圆点会整体偏低半个堆叠高度。
   这里让圆点列只占据前卡的高度区间，高度由 JS 在 renderDeck() 里写入。 */
.quote-deck-dots {
  position: absolute;
  right: 7px;
  top: 0;
  z-index: 120;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 5px;
}

.quote-dot {
  width: 6px;
  height: 6px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(52, 76, 103, .26);
  transition: background .2s ease, height .2s ease, opacity .2s ease, transform .2s ease;
  -webkit-tap-highlight-color: transparent;
}

/* 当前页拉长成胶囊，比单纯变色更容易在小尺寸下辨认 */
.quote-dot.is-active {
  height: 14px;
  border-radius: 3px;
  background: var(--brand, #de4f3d);
}

/* 八期全列会显得杂乱，只让当前页附近的 5 个保持常态，
   窗口外缩小淡化；滑到首尾时窗口贴边，被遮住的那端自然显现。 */
.quote-dot.is-far {
  opacity: .45;
  transform: scale(.66);
}

.quote-dot.is-hidden {
  opacity: 0;
  transform: scale(.4);
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .quote-dot {
    transition: none;
  }
}

/* ---- 方案 B：手写手势 ----
   纵向手势由 JS 接管，touch-action 在 initDeckInput() 里设为 pan-x，
   写在 JS 而非此处，是为了让「哪一层接管手势」和手势代码放在一起、便于对照。 */
.quote-deck-stack {
  cursor: grab;
  -webkit-tap-highlight-color: transparent;
  /* 拖动时避免选中卡片里的文字 */
  user-select: none;
  -webkit-user-select: none;
}

.quote-deck-stack:active {
  cursor: grabbing;
}
