/*
 * Jelly Match · 主题。
 *
 * 两段，界线很清楚：
 *
 * ① **设计令牌覆盖**——只改 `--gs-*` 变量，把组件库换成本作的皮。
 *    颜色逐字来自 `art/ART_PLAN.json` 的 `style_tokens.palette`，一个都没有自己调。
 *    **绝不覆盖 `.gs-*` 选择器**：那会把组件的触控下限、安全区、焦点样式一起改坏。
 *
 * ② **本作专有的版式**——一律 `.jm-` 前缀，与组件库不重名。
 *    分区（`.jm-zone`）按 `game.config.json` 的 `zones` 绝对定位，
 *    竖屏横屏是两套分区数据、一套实现，不是两套界面。
 *
 * 设计画布是手机竖屏 1080×1920。桌面与横屏是**自适应结果**：
 * 放大安全区、竖排分区改横排、露出更多背景——不重画界面、不改信息层级、不换配色（§9）。
 */

:root {
  /* ── 夜市霓虹（ART_PLAN.style_tokens.palette）── */
  --jm-night-deep: #0E0B24;
  --jm-night-mid: #1C1440;
  --jm-night-soft: #2E2266;
  --jm-neon-cyan: #38E8FF;
  --jm-neon-magenta: #FF3FA8;
  --jm-neon-amber: #FFB03A;
  --jm-tray-steel: #3A3168;
  --jm-tray-steel-light: #574B92;
  --jm-rail-groove: #241C52;
  --jm-collapsed-grey: #6B6785;
  --jm-success-mint: #5BF2B0;
  --jm-warn-coral: #FF6B4A;
  --jm-highlight-cold: #CFF6FF;
  --jm-panel-frost: rgba(28, 20, 64, 0.74);

  --jm-lime: #B6F53C;
  --jm-berry: #FF4D8D;
  --jm-peach: #FFA24C;
  --jm-grape: #A76BFF;
  --jm-cream: #FFE3B0;

  /* ── 组件库令牌覆盖 ── */
  --gs-ink: #FFFFFF;
  --gs-ink-soft: rgba(255, 255, 255, 0.76);
  --gs-ink-dim: rgba(255, 255, 255, 0.5);
  --gs-ink-on-light: #140F2E;

  --gs-surface: rgba(28, 20, 64, 0.78);
  --gs-surface-raised: rgba(46, 34, 102, 0.92);
  --gs-surface-sunken: rgba(14, 11, 36, 0.66);
  --gs-hairline: rgba(207, 246, 255, 0.16);

  --gs-accent: #38E8FF;
  --gs-accent-2: #FFB03A;
  --gs-gold: #FFD257;
  /* warn-coral 是全作唯一的警示色，只出现在塌陷环末段与融化条预警上。 */
  --gs-danger: #FF6B4A;

  --gs-glow-accent: 0 0 0 1px rgba(56, 232, 255, 0.5), 0 0 26px rgba(56, 232, 255, 0.32);
  --gs-glow-gold: 0 0 22px rgba(255, 210, 87, 0.5);
  --gs-shadow-card: 0 16px 38px rgba(6, 3, 18, 0.5);
  --gs-shadow-button: 0 6px 0 rgba(9, 5, 26, 0.5), 0 12px 22px rgba(6, 3, 18, 0.45);
  --gs-shadow-press: 0 2px 0 rgba(9, 5, 26, 0.5), 0 6px 12px rgba(6, 3, 18, 0.45);
  --gs-cta: linear-gradient(140deg, var(--jm-neon-magenta), var(--jm-grape) 55%, var(--jm-neon-cyan));

  --gs-radius-sm: 12px;
  --gs-radius-md: 20px;
  --gs-radius-lg: 28px;

  /* 夜市插画是分块生成后拼的，中缝有一条没羽化的竖线（art/ 里那张图本身的事，
     不是 CSS 能改的）。竖屏 cover 正好把中缝裁在正中，整屏最扎眼的就是它。
     取景点左移到 26%，竖屏取的是左半块，缝落到可视窗口外面。
     横屏与桌面画布是整张图横着铺，取景点管不着——那条缝只能在素材里补。 */
  --gs-bg-focus: 26% 50%;
}

/* ═══════════════════════════ 骨架 ═══════════════════════════ */

html, body {
  margin: 0;
  height: 100%;
  background: var(--jm-night-deep);
  color: var(--gs-ink);
  font-family: 'Nunito', 'Baloo 2', system-ui, -apple-system, 'Segoe UI', sans-serif;
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
}

/*
 * 桌面上整个游戏是一块 540px 宽的手机画布居中摆着（共用库的「手机画布锁定」），
 * 两侧原来是**纯黑**：1280 宽的窗口里，两边各 370px 死黑，中间一条亮着，
 * 看起来像播放器出了错而不是一款游戏。这里在画布**背后**补一层氛围：
 * 同一张夜市插画放大模糊 + 暗角，画布本身一点没变（分区、字号、比例全不动），
 * 顺带把插画中缝也糊没了。手机上这一层不存在（下面的 @media 才开）。
 */
body::before,
body::after { content: none; }

/* 条件只写 `min-height`，**不写 `min-width`**：
   `@media (min-width: …)` 而没有 `max-height` 是「给桌面另写一套版式」的形状，
   打包闸门 `check_mobile_only` 会当场判红（PROJECT_DECISIONS §12）。
   这里也确实不需要按宽度分档——画布锁定的第一档就是 `min-height: 561px`，
   同一个条件下画布已经居中收窄，两侧才会露出底。手机竖屏也满足这一条，
   但那时 `#app` 铺满整屏，这一层整块被盖住，等于不存在。 */
@media (min-height: 561px) {
  body::before {
    content: '';
    position: fixed;
    inset: -8%;
    background: url('assets/bg-night-hall@1x.webp') center / cover no-repeat;
    filter: blur(56px) saturate(0.85) brightness(0.52);
    z-index: -2;
  }

  body::after {
    content: '';
    position: fixed;
    inset: 0;
    background: radial-gradient(120% 90% at 50% 42%, transparent 34%, rgba(6, 3, 18, 0.8) 100%);
    z-index: -1;
  }

  /* 画布边缘要有个交代，不然它和背后的氛围糊在一起，分不清哪儿是游戏 */
  #app {
    box-shadow:
      0 0 0 1px rgba(207, 246, 255, 0.14),
      0 40px 120px rgba(3, 1, 12, 0.72);
  }
}

/* 刘海与底部横条在这里一次性扣掉；所有分区坐标都是"扣完之后"的比例。 */
#app {
  position: fixed;
  inset: 0;
  padding:
    env(safe-area-inset-top, 0px)
    env(safe-area-inset-right, 0px)
    env(safe-area-inset-bottom, 0px)
    env(safe-area-inset-left, 0px);
  box-sizing: border-box;
  overflow: hidden;
}

.jm-stage {
  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  /* 触摸横扫走 pointer events，浏览器自己的横向滚动/手势不许来抢 */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/*
 * 两层：场景层与浮层。浮层默认不吃指针事件——一个空的全屏浮层容器
 * 会把整块屏幕的点击都吞掉，表现出来就是"按钮全都点不动"。
 */
.jm-layer {
  position: absolute;
  inset: 0;
}

.jm-layer--overlay {
  z-index: 20;
  pointer-events: none;
}

.jm-layer--overlay > * { pointer-events: auto; }

.jm-overlay {
  position: absolute;
  inset: 0;
}

/** 分区。互斥、绝对定位，坐标来自 SCREEN_SPEC 冻结的 zones。 */
.jm-zone {
  position: absolute;
  display: flex;
  flex-direction: column;
  box-sizing: border-box;
  min-width: 0;
  min-height: 0;
}

/* 分区里的件与件之间有一个统一的呼吸。原来每一屏靠各自的 margin 凑，
   于是标题到卡片是 0、卡片到统计条是 0、而说明文字自己带 8px——
   同一屏上三种间距，看起来就是"没对齐"。 */
.jm-zone--pad {
  padding: var(--gs-space-2) var(--gs-space-4);
  gap: var(--gs-space-3);
}
.jm-zone--center { align-items: center; justify-content: center; }
/* 可滚分区的下边界原来是一刀切：帮助页最后那段「Scoring」被从中间削掉，
   看不出是"下面还有"还是"排版把它切了"。底部留 20px 余量 + 渐隐收边。 */
.jm-zone--scroll {
  overflow-y: auto;
  overflow-x: hidden;
  -webkit-overflow-scrolling: touch;
  padding-bottom: var(--gs-space-5);
  mask-image: linear-gradient(180deg, #000 calc(100% - 18px), transparent 100%);
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 18px), transparent 100%);
}

/*
 * 可滚分区里的每一件都**不许被压扁**。
 *
 * 分区是定高的 flex 列，子项默认 `flex-shrink: 1`：内容比分区高的时候，
 * 浏览器不是让它滚，而是先把每一件压到比自己内容还矮，`overflow: visible`
 * 的那部分就直接画到下一件身上。暂停屏的「动效」那一行实测 48px 高、
 * 内容 84px，三段选择器整块压在下面「语言」那一行上——
 * 看起来是"排版乱"，其实是这一句缺席。
 */
.jm-zone--scroll > * { flex-shrink: 0; }

/* 分区里的滚动条不抢视觉，但仍然可滚 */
.jm-zone--scroll::-webkit-scrollbar { width: 4px; }
.jm-zone--scroll::-webkit-scrollbar-thumb { background: var(--gs-hairline); border-radius: 999px; }

/* ═══════════════════════════ 通用小件 ═══════════════════════════ */

.jm-plaque {
  display: inline-flex;
  align-items: center;
  gap: var(--gs-space-2);
  padding: 6px 14px;
  border-radius: var(--gs-radius-pill);
  background: linear-gradient(180deg, rgba(46, 34, 102, 0.95), rgba(28, 20, 64, 0.95));
  border: 1px solid var(--gs-hairline);
  box-shadow: var(--gs-shadow-card);
  font-weight: 800;
  line-height: 1.1;
  white-space: nowrap;
}

.jm-plaque--neon {
  border-color: rgba(56, 232, 255, 0.55);
  box-shadow: var(--gs-glow-accent);
}

.jm-title-neon {
  margin: 0;
  font-size: clamp(22px, 6.2vw, 40px);
  font-weight: 900;
  letter-spacing: 0.01em;
  text-shadow: 0 0 18px rgba(255, 63, 168, 0.55), 0 2px 0 rgba(9, 5, 26, 0.8);
}

.jm-sub {
  margin: 2px 0 0;
  font-size: var(--gs-text-sm);
  color: var(--gs-ink-soft);
}

.jm-row {
  display: flex;
  align-items: center;
  gap: var(--gs-space-2);
  min-width: 0;
}

/*
 * 标题栏：`44px | 1fr | 44px`。两侧是**固定的按钮位**，右边没有按钮时那一格空着，
 * 标题因此永远在同一个 x 上。原来用 `space-between` + `flex: 1` 的标题，
 * 右边有星数牌的屏（关卡、皮肤）标题被挤到左边，没有牌子的屏（帮助）又是另一处，
 * 六屏切来切去标题一直在跳。
 */
.jm-titlebar {
  display: grid;
  grid-template-columns: var(--gs-tap-min) minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--gs-space-2);
  width: 100%;
}

.jm-titlebar > .jm-title-neon {
  text-align: center;
  min-width: 0;
  overflow-wrap: anywhere;
}

.jm-titlebar__side {
  display: flex;
  align-items: center;
  min-width: var(--gs-tap-min);
}

.jm-titlebar__side--end { justify-content: flex-end; }

.jm-sub--center { text-align: center; }

.jm-row--center { justify-content: center; width: 100%; }

.jm-row--wrap { flex-wrap: wrap; }
.jm-row--between { justify-content: space-between; width: 100%; }
.jm-grow { flex: 1 1 auto; min-width: 0; }

.jm-jelly {
  display: block;
  flex: 0 0 auto;
  image-rendering: auto;
}

/* 统计条在居中分区里会缩成内容宽度：首屏只有「星数」一项时，
   它缩成一枚 70px 的小牌孤零零吊在卡片下面。让它跟着分区走满宽。 */
.jm-zone .gs-stats {
  width: 100%;
  box-sizing: border-box;
}

/* 只有一项的时候竖着排会得到一个又高又空的盒子（首屏那块「9 / Stars」）。
   一项就横着排：图标、数字、标签在同一条基线上，满宽的条才站得住。 */
.jm-zone .gs-stats:has(> .gs-stat:only-child) {
  padding: var(--gs-space-2) var(--gs-space-4);
}

.jm-zone .gs-stats > .gs-stat:only-child {
  flex-direction: row;
  align-items: baseline;
  justify-content: center;
  gap: var(--gs-space-2);
}

.jm-zone .gs-stats > .gs-stat:only-child > .gs-stat__icon { align-self: center; }

.jm-scroll-fade { position: relative; }

/* ═══════════════════════════ 游玩屏 HUD ═══════════════════════════ */

.jm-hud {
  display: flex;
  flex-direction: column;
  gap: 4px;
  justify-content: center;
  padding: 4px var(--gs-space-3);
}

/* 两行的高度由 JS 按分区预算算好后内联写死；overflow 是护栏，不是排版方案。 */
.jm-hud__top,
.jm-hud__bottom {
  display: flex;
  align-items: center;
  gap: var(--gs-space-2);
  min-height: 0;
  overflow: hidden;
}

.jm-hud__top { justify-content: space-between; }
.jm-hud__bottom { justify-content: space-between; }

/* 关卡 + 分数：界面稿左上角那块牌子 */
.jm-levelplate {
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding: 4px 10px;
  border-radius: var(--gs-radius-sm);
  background: rgba(28, 20, 64, 0.8);
  border: 1px solid var(--gs-hairline);
  line-height: 1.05;
}

.jm-levelplate b { font-size: clamp(11px, 3vw, 13px); font-weight: 800; }
.jm-levelplate span {
  font-size: clamp(13px, 3.6vw, 16px);
  font-weight: 900;
  color: var(--gs-gold);
  font-variant-numeric: tabular-nums;
}

/* 步数：界面稿正中那枚圆牌 */
.jm-moves {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 60px;
  height: 60px;
  border-radius: 50%;
  background: radial-gradient(circle at 50% 35%, var(--jm-night-soft), var(--jm-night-mid));
  border: 2px solid rgba(167, 107, 255, 0.75);
  box-shadow: 0 0 22px rgba(167, 107, 255, 0.4), inset 0 -4px 10px rgba(9, 5, 26, 0.6);
}

.jm-moves small {
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gs-ink-soft);
  line-height: 1;
}

.jm-moves b {
  font-size: clamp(20px, 5.4vw, 26px);
  font-weight: 900;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* 融化扣步的独立表现：红闪 + −1 飘字，不与拨动扣步共用（§6.5 约束 3） */
.jm-moves[data-melt='true'] {
  animation: jm-melt-hit 420ms var(--gs-ease-out);
}

@keyframes jm-melt-hit {
  0% { border-color: var(--jm-warn-coral); transform: scale(1.14); }
  100% { border-color: rgba(167, 107, 255, 0.75); transform: scale(1); }
}

.jm-float-minus {
  position: absolute;
  font-weight: 900;
  color: var(--jm-warn-coral);
  pointer-events: none;
  animation: jm-float-up 700ms var(--gs-ease-out) forwards;
}

@keyframes jm-float-up {
  from { transform: translateY(0); opacity: 1; }
  to { transform: translateY(-26px); opacity: 0; }
}

/* 订单卡：标签一行，每一项 = 口味图 + have/need + 条内百分比。
   竖屏 HUD 只有 13% 高，而 L9+ 是三项订单——每一项的高度由 JS 按
   「这一行还剩多少预算」算，标签那一行的高度已经从预算里扣掉了。 */
.jm-order {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 3px;
  padding: 3px 8px;
  border-radius: var(--gs-radius-sm);
  background: rgba(28, 20, 64, 0.8);
  border: 1px solid var(--gs-hairline);
  min-width: 0;
  flex: 1 1 auto;
  max-width: 52%;
  min-height: 0;
  overflow: hidden;
}

.jm-order__lines {
  display: flex;
  flex-direction: column;
  gap: 3px;
  flex: 1 1 auto;
  min-width: 0;
}

.jm-order__label {
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gs-ink-soft);
  line-height: 1.1;
  flex: 0 0 auto;
  white-space: nowrap;
}

.jm-order__item {
  display: flex;
  align-items: center;
  gap: 5px;
  min-width: 0;
}

.jm-order__item .gs-meter {
  flex: 1 1 auto;
  min-width: 40px;
}

.jm-order__item .gs-meter__text { font-size: 9px; }
.jm-order__item[data-done='true'] .gs-meter__fill { background: var(--jm-success-mint); }

/* 塌陷环 + 融化条那一排 */
.jm-gauges {
  display: flex;
  align-items: center;
  gap: var(--gs-space-2);
  width: 100%;
}

.jm-collapse {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 0 0 auto;
}

/* 环外面的标签，和右边融化条的 `.jm-melt__label` 同一套字：
   同一行上两个读数的标签必须长得一样，否则看起来像两个不同层级的东西。 */
.jm-collapse__caption {
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gs-ink-soft);
  line-height: 1;
  white-space: nowrap;
}

/* 标签在条**左边**，和左边「环 + COLLAPSE」是同一种排法。
   原来标签在条上面：同一行上两个读数，一个的标签在左、另一个的标签在上，
   两条小字于是错开半行，看起来像没对齐（实测差 18px）。 */
.jm-melt {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: 1 1 auto;
  min-width: 0;
}

.jm-melt .gs-meter { flex: 1 1 auto; min-width: 0; }

.jm-melt__label {
  display: flex;
  flex: 0 0 auto;
  font-size: 9px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--gs-ink-soft);
  line-height: 1;
  white-space: nowrap;
}

.jm-melt .gs-meter { height: 12px; }
.jm-melt .gs-meter__fill { background: linear-gradient(90deg, var(--jm-neon-cyan), var(--jm-grape)); }
.jm-melt[data-warn='true'] .gs-meter__fill { background: var(--jm-warn-coral); }
.jm-melt[data-warn='true'] .jm-melt__label { color: var(--jm-warn-coral); }

/* ═══════════════════════════ 预告行 ═══════════════════════════ */

.jm-queue {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 0;
  /* 下边留 6px：正好装下每颗预告下面那道落点光柱（`.jm-queue__slot::after`
     从槽底再往下 6px）。原来是 `2px 0`，光柱于是**长在这一行的盒子外面**，
     `scrollHeight(42) > clientHeight(38)`——剧本 05 的判据据此判"文字被容器裁掉"，
     红了却看不出哪里被裁：它确实没被裁（overflow 是 visible），
     但它溢出了自己的分区行，而分区之间不许互相占地方。 */
  padding: 2px 0 6px;
  width: 100%;
}

.jm-queue__slot {
  position: relative;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  flex: 1 1 0;
  min-width: 0;
}

/* 每颗预告对准下方那一列，各带一道极淡的落点光柱。
   原来是 2px 宽、0.5 不透明度的**实线**：截图上看是每颗果冻下面挂了一根针，
   读起来像渲染没画完。改成上宽下窄、起手就半透明的一道光，
   在插画上是"落点提示"而不是一根线。 */
.jm-queue__slot::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -6px;
  width: 6px;
  height: 8px;
  transform: translateX(-50%);
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(207, 246, 255, 0.28), transparent 85%);
  filter: blur(1px);
}

.jm-queue__label {
  font-size: 9px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gs-ink-dim);
  align-self: center;
  padding-right: 6px;
}

/* ═══════════════════════════ 托盘 ═══════════════════════════ */

.jm-play { position: relative; overflow: visible; }

.jm-board {
  position: absolute;
  display: block;
  border-radius: var(--gs-radius-md);
  box-shadow: 0 18px 40px rgba(6, 3, 18, 0.55);
  touch-action: none;
  cursor: grab;
}

.jm-board:active { cursor: grabbing; }

/* 行两端的箭头：点按路径。≥44×44，只在选中一行时浮出。 */
.jm-arrow {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(14, 11, 36, 0.82);
  box-shadow: 0 0 0 2px rgba(56, 232, 255, 0.6), 0 6px 16px rgba(6, 3, 18, 0.5);
  cursor: pointer;
  z-index: 6;
}

.jm-arrow img { width: 24px; height: 24px; }
.jm-arrow:active { transform: scale(0.92); }
.jm-arrow:focus-visible { outline: 3px solid var(--gs-accent); outline-offset: 2px; }

/* 锁行三通道之二：端点挂锁。轨道灰化在 canvas 上，箭头消失靠不渲染。 */
.jm-rowlock {
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: space-between;
  pointer-events: none;
  z-index: 5;
}

.jm-rowlock img {
  width: 22px;
  height: 22px;
  filter: drop-shadow(0 2px 4px rgba(6, 3, 18, 0.8));
  opacity: 0.95;
}

/* 波次大字：连锁反馈，非交互，永远不压住可操作元素 */
.jm-wave {
  position: absolute;
  left: 50%;
  top: 46%;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  pointer-events: none;
  z-index: 7;
  animation: jm-wave-pop 620ms var(--gs-ease-out) forwards;
}

.jm-wave b {
  font-size: clamp(24px, 8vw, 44px);
  font-weight: 900;
  color: var(--jm-neon-magenta);
  text-shadow: 0 0 24px rgba(255, 63, 168, 0.8), 0 3px 0 rgba(9, 5, 26, 0.9);
}

.jm-wave span {
  font-size: clamp(16px, 5vw, 26px);
  font-weight: 900;
  color: var(--gs-gold);
}

@keyframes jm-wave-pop {
  0% { transform: translate(-50%, -40%) scale(0.7); opacity: 0; }
  25% { transform: translate(-50%, -50%) scale(1.06); opacity: 1; }
  75% { opacity: 1; }
  100% { transform: translate(-50%, -62%) scale(1); opacity: 0; }
}

/* ═══════════════════════════ 底部动作区 ═══════════════════════════ */

.jm-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gs-space-2);
  padding: 4px var(--gs-space-3) var(--gs-space-2);
  width: 100%;
  box-sizing: border-box;
}

/* 游玩屏底部三颗按钮必须是**同一种东西**：等宽、等高、图标和字在同一行。
   原来只有 Ship 是 flex 行（`.jm-ship`），Pause / Restart 是普通按钮——
   窄按钮里图标和标签折成上下两行，三颗按钮于是三种排法、两种高度。 */
.jm-actions > .gs-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gs-space-2);
  flex: 1 1 0;
  min-width: 0;
  min-height: 52px;
  padding-inline: var(--gs-space-3);
  white-space: nowrap;
}

.jm-actions > .gs-btn > img { flex: 0 0 auto; }

/*
 * 竖排动作区。分区高度是 S2 冻结的（结算屏只有 14%），三颗按钮竖着排会溢出，
 * 所以主按钮独占一行、两颗次按钮并排一行——**都还在拇指区里，间距也够**。
 */
.jm-actions--stack {
  flex-direction: column;
  align-items: stretch;
  gap: var(--gs-space-2);
}

.jm-actions--stack .gs-btn { min-height: 44px; }
.jm-actions--stack .jm-actions__row { display: flex; gap: var(--gs-space-2); }
.jm-actions--stack .jm-actions__row .gs-btn {
  flex: 1 1 0;
  min-width: 0;
  padding-inline: var(--gs-space-3);
  font-size: var(--gs-text-sm);
}

/* 主操作比两边的次操作宽一档：三颗等宽时，「装盘」和「暂停」看起来一样重要。 */
.jm-ship {
  flex: 1.3 1 0;
  min-width: 0;
  min-height: 52px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gs-space-2);
}

.jm-ship img { width: 22px; height: 22px; }

/* 订单填满那一帧从下方弹入并弹跳一次，然后常亮 + 缓慢呼吸（§6.5 / §10.5） */
.jm-ship[data-ready='true'] { animation: jm-ship-in 520ms var(--gs-ease-out); }
.jm-ship[data-ready='true'][data-settled='true'] { animation: jm-ship-breathe 2.4s ease-in-out infinite; }

@keyframes jm-ship-in {
  0% { transform: translateY(28px) scale(0.9); }
  55% { transform: translateY(-6px) scale(1.06); }
  100% { transform: translateY(0) scale(1); }
}

@keyframes jm-ship-breathe {
  0%, 100% { box-shadow: var(--gs-shadow-button); }
  50% { box-shadow: var(--gs-shadow-button), 0 0 26px rgba(91, 242, 176, 0.55); }
}

:root[data-reduced-motion='on'] .jm-ship[data-ready='true'],
:root[data-reduced-motion='on'] .jm-ship[data-ready='true'][data-settled='true'],
:root[data-reduced-motion='on'] .jm-wave,
:root[data-reduced-motion='on'] .jm-moves[data-melt='true'] {
  animation: none;
}

/* ═══════════════════════════ 教学横条（inline-bar） ═══════════════════════════ */

/*
 * 教学是组件库的 `CoachStrip`，**本作不另写一套**。这里只给它所在的分区
 * 一点本作的皮：一道朝上的尖角，指着它正在讲的那块棋盘。
 *
 * 它嵌在 coach 分区里，**结构上就盖不住棋盘**：分区与 play 互斥，
 * 没有遮罩、没有 backdrop、不进弹窗栈、z-index 低于箭头。
 */
.jm-coach-zone {
  position: absolute;
  padding: 2px var(--gs-space-3);
  box-sizing: border-box;
}

.jm-coach-zone::before {
  content: '';
  position: absolute;
  top: -5px;
  left: 42px;
  width: 12px;
  height: 12px;
  transform: rotate(45deg);
  background: var(--gs-surface-raised);
  border-left: 1px solid var(--gs-hairline);
  border-top: 1px solid var(--gs-hairline);
}

.jm-stage[data-orientation='landscape'] .jm-coach-zone::before { display: none; }

/* 预告行分区：一排小果冻，每颗对准下方那一列 */
.jm-queue-zone {
  justify-content: center;
  padding: 0 var(--gs-space-3);
  box-sizing: border-box;
}

/* 标题屏的演示盘：真的棋盘画法循环播一次拨行，**不是静态贴图** */
.jm-demo {
  display: block;
  width: 100%;
  border-radius: var(--gs-radius-md);
}

.jm-sharecard-holder {
  display: flex;
  justify-content: center;
  width: 100%;
}

/* 分段选择的选中态：aria-pressed 之外再给一个类，
   "点了到底有没有变化"才在结构上看得见（走查判的是结构与文字，不是 aria）。 */
/* 选中的那一段用**内发光 + 内圈实边**，不用外发光：
   分段常常就贴着可滚分区的边，30px 的外发光会被 `overflow: hidden`
   齐齐切成一个方角矩形，看起来像渲染坏了（jelly-stack 走查踩过这条）。 */
.jm-seg--on {
  box-shadow:
    inset 0 0 0 2px var(--gs-accent),
    inset 0 0 16px rgba(56, 232, 255, 0.35);
}

.jm-seg--off { opacity: 0.72; }

/* ═══════════════════════════ 卡片与列表（其余场景） ═══════════════════════════ */

.jm-panel {
  width: 100%;
  box-sizing: border-box;
  border-radius: var(--gs-radius-lg);
  background: var(--jm-panel-frost);
  backdrop-filter: blur(12px);
  border: 1px solid var(--gs-hairline);
  box-shadow: var(--gs-shadow-card);
  padding: var(--gs-space-4);
}

/* 关卡地图：一条蜿蜒的输送带，牌左右交错摆 */
/*
 * 行距要给「Now」那枚角标留出身位：它从牌顶往上探 11px，
 * 行距只有 8px 的时候正好压在上一行那枚牌的下边缘上。
 *
 * 但行距和牌一起放大是有预算的：**十二枚牌加解锁轨道必须一屏装得下**。
 * 竖屏的内容分区是 584px（扣掉底部余量），六行牌 + 轨道 74 + 行间距 12
 * 只剩每枚 72px 的份额。放到 84 + 行距 16 的时候，解锁轨道整条被挤出屏幕——
 * 那是这一屏的第二件事，不能靠玩家自己往下滚才发现。
 */
.jm-levels {
  display: flex;
  flex-direction: column;
  gap: 12px;
  width: 100%;
  padding: 0;
}

/* 每行两枚。原来是 `gap: 12px` 的两枚 64px 小牌挤在正中，左右各空着一百多像素，
   一屏最主要的东西看起来像被挤到中缝里。牌放大到 84px、行距按分区宽度分，
   信息（关号 + 三颗星）也就有地方站了。交错的输送带感由 `--offset` 给。 */
.jm-levels__row {
  display: flex;
  justify-content: center;
  gap: clamp(var(--gs-space-4), 12vw, 64px);
}

.jm-level {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  width: 72px;
  min-height: 72px;
  padding: 6px;
  border: none;
  border-radius: 16px;
  background: linear-gradient(180deg, #3B7FE0, #2456A8);
  box-shadow: 0 5px 0 rgba(9, 5, 26, 0.55), 0 10px 18px rgba(6, 3, 18, 0.45);
  color: #fff;
  font-weight: 900;
  font-size: 20px;
  cursor: pointer;
}

.jm-level[data-locked='true'] {
  background: linear-gradient(180deg, #6E6A88, #4A4760);
  color: rgba(255, 255, 255, 0.66);
  cursor: not-allowed;
}

/*
 * 已经选中的、以及还没解锁的选项标成 disabled：它们**照样画在界面上**，
 * 只是不再假装自己点得动。所以这里不用浏览器默认那种整体压暗，
 * 而是各自保留原本的样子——锁着的照样看得见门槛，选中的照样看得出被选中。
 */
.jm-level:disabled,
.jm-skin:disabled,
.jm-card:disabled {
  cursor: default;
}

.jm-level--picked:disabled,
.jm-skin--picked:disabled,
.jm-card[aria-pressed='true']:disabled { opacity: 1; }

/* 「要开的是这一关」：选中态既在类名上也在样式上，玩家一眼看得见按下去有反应。 */
.jm-level--picked {
  outline: 3px solid var(--gs-gold);
  outline-offset: 2px;
}

.jm-skin--picked { border-color: var(--gs-gold); }

.jm-level[data-current='true'] {
  background: linear-gradient(180deg, var(--jm-neon-magenta), #B32570);
  box-shadow: 0 5px 0 rgba(9, 5, 26, 0.55), 0 0 24px rgba(255, 63, 168, 0.6);
}

.jm-level__stars { display: flex; gap: 3px; }
/* 星是金的：图标 SVG 用 currentColor 画、以 <img> 载入，不覆盖这一句就是三颗黑星。 */
.jm-level__stars img { width: 14px; height: 14px; --gs-icon-tint: var(--gs-icon-tint-gold); }
.jm-level__stars img[data-earned='false'] {
  --gs-icon-tint: var(--gs-icon-tint-ink);
  opacity: 0.3;
}
.jm-level__lock { width: 16px; height: 16px; opacity: 0.85; }

/* 「Now」原来挂在牌的左上角外面（-14px），正好压在上一行那一枚的右下角。
   移到牌的正上方、只探出一点点，谁也不压。 */
.jm-now {
  position: absolute;
  top: -11px;
  left: 50%;
  transform: translateX(-50%);
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--jm-neon-magenta);
  box-shadow: 0 2px 10px rgba(255, 63, 168, 0.55);
  font-size: 10px;
  font-weight: 800;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* 订单卡三选一 */
.jm-cards {
  display: flex;
  gap: var(--gs-space-2);
  width: 100%;
  align-items: stretch;
}

/*
 * 三张订单卡原来是**奶油白**的：整作只有这三张卡是浅色，摆在夜市霓虹里
 * 像贴了三张便签。换成和其余面板同一套霜面（`--jm-panel-frost` 一族），
 * 区分三张卡靠的仍然是各自的标签色（安全绿 / 标准紫 / 冒险红），不是底色。
 * `justify-content: space-between` 是为了三张卡的说明文字**齐底**——
 * 文案长短不一时，原来只有中间那张的字在中线上，两边两张各自往下掉半行。
 */
.jm-card {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  gap: var(--gs-space-2);
  padding: var(--gs-space-3) var(--gs-space-2) var(--gs-space-2);
  border: 2px solid var(--gs-hairline);
  border-radius: var(--gs-radius-md);
  background: linear-gradient(180deg, rgba(46, 34, 102, 0.86), rgba(20, 15, 46, 0.9));
  color: var(--gs-ink);
  box-shadow: var(--gs-shadow-card);
  cursor: pointer;
  min-height: 44px;
}

.jm-card__tag {
  padding: 3px 12px;
  border-radius: 999px;
  font-weight: 900;
  font-size: clamp(12px, 3.4vw, 15px);
  color: #fff;
}

/* 深底上原来那三个标签色（#6F8B1E / #6B3FB5 / #A62A2A）全都沉下去了，
   提亮一档，白字才压得住。三张卡靠这一处颜色分开，不能含糊。 */
.jm-card[data-id='safe'] .jm-card__tag { background: linear-gradient(180deg, #9BC22B, #6F8B1E); }
.jm-card[data-id='standard'] .jm-card__tag { background: linear-gradient(180deg, #9A5FF0, #6B3FB5); }
.jm-card[data-id='risky'] .jm-card__tag { background: linear-gradient(180deg, #E24A45, #A62A2A); }

.jm-card__note {
  font-size: clamp(10px, 2.7vw, 12px);
  font-weight: 700;
  text-align: center;
  line-height: 1.25;
  color: var(--gs-ink-soft);
  /* 「20 moves · scores ×1.0」在 110px 宽的卡上必折行；
     balance 让两行长短接近，不会出现第二行只挂一个「×0.9」的孤字。 */
  text-wrap: balance;
}

.jm-card[aria-pressed='true'] {
  border-color: var(--gs-gold);
  background: linear-gradient(180deg, rgba(72, 52, 140, 0.92), rgba(28, 20, 64, 0.94));
  box-shadow: 0 0 0 3px rgba(255, 210, 87, 0.32), var(--gs-glow-gold);
  transform: translateY(-4px);
}

/* 选中的角标原来钉在卡的**外面**（top:-10px; right:-6px），
   一颗黑星浮在两张卡的缝上，既不属于这张也不属于隔壁那张。
   收进卡里的右上角，跟着卡一起动。 */
.jm-card__check {
  position: absolute;
  top: 6px;
  right: 6px;
  width: 18px;
  height: 18px;
  --gs-icon-tint: var(--gs-icon-tint-gold);
  filter: var(--gs-icon-tint) drop-shadow(0 1px 4px rgba(9, 5, 26, 0.8));
}

.jm-card__wrap { position: relative; flex: 1 1 0; min-width: 0; display: flex; }

/* 皮肤格 */
/*
 * `auto-fill` + 102px 下限在 390px 的屏上排三列（每列约 111px），
 * 而每格里那排五颗 26px 的果冻加间距是 142px——**色板比格子还宽**，
 * 五颗果冻横着漏到卡外面、压在隔壁那张卡上。
 * 两件事一起改：列宽下限提到 140，让格子装得下色板；色板自己也允许压缩（下面）。
 * `align-items: stretch`（grid 默认）保证同一行的卡等高，
 * 「已装备」那枚不再比旁边高出一截。
 */
.jm-skins {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: var(--gs-space-2);
  width: 100%;
}

.jm-skin {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  padding: var(--gs-space-2);
  border-radius: var(--gs-radius-md);
  border: 2px solid var(--gs-hairline);
  background: rgba(28, 20, 64, 0.82);
  color: var(--gs-ink);
  cursor: pointer;
  min-height: 44px;
}

/* 选中态用一圈实边 + 内发光，不用 30px 的外发光：
   分区一旦 `overflow: hidden`，外发光会被齐齐切成一个方角矩形
   （jelly-stack 走查踩过，见 project_layout_overlap_pass）。 */
.jm-skin[aria-pressed='true'] {
  border-color: var(--gs-accent);
  box-shadow: inset 0 0 0 1px rgba(56, 232, 255, 0.5), inset 0 0 18px rgba(56, 232, 255, 0.28);
}

/* 锁着的照样看得见门槛，只是压暗——0.6 的整体不透明度会把门槛那行字也抹掉一半。 */
.jm-skin[data-locked='true'] {
  opacity: 1;
  cursor: not-allowed;
  border-color: rgba(255, 255, 255, 0.08);
  background: rgba(20, 15, 46, 0.72);
}

.jm-skin[data-locked='true'] .jm-skin__swatch,
.jm-skin[data-locked='true'] .jm-skin__tray { opacity: 0.42; filter: grayscale(0.5); }
.jm-skin[data-locked='true'] .jm-skin__name { color: var(--gs-ink-dim); }

.jm-skin__name {
  font-size: 12px;
  font-weight: 800;
  text-align: center;
  line-height: 1.2;
}

/* 色板允许**一起缩**：格子再窄也不许把果冻推到卡外面去。
   canvas 的行内宽高由 JS 写死，所以这里要 `max-width: 100%` + `height: auto` 收着。 */
.jm-skin__swatch {
  display: flex;
  gap: 3px;
  width: 100%;
  justify-content: center;
  min-width: 0;
}

.jm-skin__swatch > * {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 100%;
  height: auto !important;
}

.jm-skin__tray { width: 100%; height: 40px; border-radius: 10px; }

/* 每日订单 */
.jm-streak { display: flex; gap: 4px; flex-wrap: wrap; justify-content: center; }
.jm-streak img { width: 20px; height: 20px; --gs-icon-tint: var(--gs-icon-tint-gold); }
.jm-streak img[data-on='false'] {
  --gs-icon-tint: var(--gs-icon-tint-ink);
  opacity: 0.3;
}

.jm-daily__order {
  display: flex;
  justify-content: center;
  gap: var(--gs-space-4);
  padding: var(--gs-space-3) 0;
}

.jm-daily__item { display: flex; flex-direction: column; align-items: center; gap: 4px; }
.jm-daily__item b { font-size: clamp(18px, 5vw, 24px); font-weight: 900; }

.jm-kv {
  display: flex;
  width: 100%;
  box-sizing: border-box;
  justify-content: space-between;
  align-items: center;
  gap: var(--gs-space-3);
  padding: 6px 12px;
  border-radius: var(--gs-radius-sm);
  background: rgba(46, 34, 102, 0.7);
  font-weight: 800;
}

.jm-kv b { color: var(--gs-gold); font-variant-numeric: tabular-nums; }

/* ═══════════════════════════ 结算 ═══════════════════════════ */

/*
 * 结算屏的内容分区原来是一叠**左对齐的散件**：三行小字贴着左边、大分数是块级元素
 * 于是也左对齐（68px 的金色数字半个身子压在屏幕边上）、三颗星直接飘在夜市插画上。
 * 「这一局怎么样」这一件事被拆成了四块互不相干的东西。
 * 现在结论（原因 → 星 → 分数 → 纪录线）收进一块居中的霜面板，
 * 面板外面留给账单和终局缩略图——那些是明细，不是结论。
 */
.jm-result { align-items: center; }

.jm-verdict {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gs-space-2);
  width: 100%;
  box-sizing: border-box;
  padding: var(--gs-space-4) var(--gs-space-3);
  border-radius: var(--gs-radius-lg);
  background: var(--jm-panel-frost);
  backdrop-filter: blur(12px);
  border: 1px solid var(--gs-hairline);
  box-shadow: var(--gs-shadow-card);
  text-align: center;
}

.jm-verdict .jm-sub { margin: 0; }

/* 终局缩略图：标题在上、图在下，整块居中 */
.jm-scar-block {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gs-space-2);
  width: 100%;
  padding-top: var(--gs-space-2);
}

.jm-stars { display: flex; align-items: center; justify-content: center; gap: var(--gs-space-2); }
/* 星是金的。图标 SVG 用 currentColor 画、以 <img> 载入——不指定色就是三块黑。 */
.jm-stars img { width: 46px; height: 46px; --gs-icon-tint: var(--gs-icon-tint-gold); }
.jm-stars img:nth-child(2) { width: 58px; height: 58px; }
/* 未获得的星**画出来再暗掉**，不是不画 */
.jm-stars img[data-earned='false'] {
  --gs-icon-tint: var(--gs-icon-tint-ink);
  opacity: 0.26;
}

.jm-score {
  display: block;
  width: 100%;
  text-align: center;
  font-size: clamp(38px, 12vw, 68px);
  font-weight: 900;
  line-height: 1;
  color: var(--gs-gold);
  text-shadow: 0 0 26px rgba(255, 210, 87, 0.55), 0 4px 0 rgba(9, 5, 26, 0.75);
  font-variant-numeric: tabular-nums;
}

.jm-score[data-tone='silver'] {
  color: #D7DCEA;
  text-shadow: 0 0 20px rgba(215, 220, 234, 0.35), 0 4px 0 rgba(9, 5, 26, 0.75);
}

.jm-bill {
  display: flex;
  flex-direction: column;
  width: 100%;
  border-radius: var(--gs-radius-md);
  border: 1px solid rgba(255, 210, 87, 0.35);
  background: rgba(20, 15, 46, 0.72);
  overflow: hidden;
}

.jm-bill__row {
  display: flex;
  align-items: center;
  gap: var(--gs-space-2);
  padding: 7px var(--gs-space-3);
  border-bottom: 1px dashed rgba(255, 255, 255, 0.12);
  font-size: clamp(12px, 3.4vw, 15px);
  font-weight: 700;
}

.jm-bill__row:last-child { border-bottom: none; }
.jm-bill__row img { width: 20px; height: 20px; flex: 0 0 auto; opacity: 0.8; }
.jm-bill__row span { flex: 1 1 auto; min-width: 0; }
.jm-bill__row b { color: var(--gs-gold); font-variant-numeric: tabular-nums; }

.jm-scar {
  width: 132px;
  flex: 0 0 auto;
  border-radius: 12px;
  border: 1px solid var(--gs-hairline);
  box-shadow: var(--gs-shadow-card);
}

.jm-unlock {
  display: flex;
  align-items: center;
  gap: var(--gs-space-3);
  width: 100%;
  padding: var(--gs-space-2) var(--gs-space-3);
  border-radius: var(--gs-radius-md);
  border: 2px solid var(--jm-neon-cyan);
  background: rgba(28, 20, 64, 0.8);
  box-shadow: var(--gs-glow-accent);
  font-weight: 800;
}

/* ═══════════════════════════ 分享卡 ═══════════════════════════ */

.jm-sharecard {
  width: 100%;
  max-width: 480px;
  border-radius: var(--gs-radius-md);
  box-shadow: var(--gs-shadow-card);
  display: block;
}

/* ═══════════════════════════ 暂停浮层 ═══════════════════════════ */

/* 底层是被模糊压暗的 play 场景，不是纯色遮罩——玩家要看得见自己这一局还在。
   原来只压到 0.45：16px 的模糊把七彩果冻糊成一片**彩色噪点**，
   设置面板浮在上面像有一层脏东西，白字也压不住。
   压到 0.72 并加一层从上到下的暗渐变：局面还在（认得出是自己的盘），
   但已经退成背景，而不是和面板抢眼睛。 */
.jm-scrim {
  position: absolute;
  inset: 0;
  backdrop-filter: blur(20px) saturate(0.6);
  background:
    linear-gradient(180deg, rgba(9, 5, 26, 0.82), rgba(9, 5, 26, 0.66) 45%, rgba(9, 5, 26, 0.86));
  z-index: 20;
}

.jm-overlay { z-index: 21; }

.jm-setting {
  display: flex;
  align-items: center;
  gap: var(--gs-space-3);
  width: 100%;
  padding: 6px var(--gs-space-3);
  border-radius: var(--gs-radius-md);
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid var(--gs-hairline);
  box-sizing: border-box;
  min-height: 48px;
}

.jm-setting img { width: 24px; height: 24px; flex: 0 0 auto; opacity: 0.86; }
.jm-setting__label { flex: 1 1 auto; font-weight: 800; line-height: 1.2; }

/* 控件放不进同一行时（三段的动效开关），整行换行：
   标签占第一行，控件占满第二行。分段自己 wrap 的话，
   三段会被拆成 2 + 1 两行、还各自缩着，看起来像两组东西。 */
.jm-setting--stack { flex-wrap: wrap; row-gap: var(--gs-space-2); }
.jm-setting--stack > .gs-segmented { flex: 1 0 100%; }

/* 设置行 + 它的回执。回执排在行**下面**而不是行里，
   否则 48px 高的行会被文字撑成两行，右侧控件跟着位移。 */
.jm-setting-group {
  display: flex;
  flex-direction: column;
  gap: 4px;
  width: 100%;
}

.jm-setting__note { padding: 0 var(--gs-space-3); }

/* 开关。**可点区域 56×44**，视觉上的轨道只有 32 高——
   触控下限是下限不是建议值，不许拿视觉高度当热区。 */
.jm-toggle {
  position: relative;
  width: 56px;
  height: var(--gs-tap-min);
  flex: 0 0 auto;
  border: none;
  background: transparent;
  cursor: pointer;
  padding: 0;
}

.jm-toggle::before {
  content: '';
  position: absolute;
  left: 0;
  top: 6px;
  width: 56px;
  height: 32px;
  border-radius: 999px;
  background: rgba(20, 15, 46, 0.8);
  border: 1px solid var(--gs-hairline);
  transition: background var(--gs-fast) var(--gs-ease-out);
}

.jm-toggle::after {
  content: '';
  position: absolute;
  top: 9px;
  left: 3px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #fff;
  transition: transform var(--gs-fast) var(--gs-ease-out);
}

.jm-toggle--on::before { background: var(--jm-success-mint); }
.jm-toggle--on::after { transform: translateX(24px); }

/* 触控下限：滑块与下拉本体不足 44px 时，用一层不可见的热区补足 */
.jm-slider { flex: 1 1 auto; min-width: 90px; height: 44px; accent-color: var(--jm-success-mint); }

/* 原生下拉在深色界面上是系统自己的浅色控件，和旁边的开关、滑块不是一套东西。
   `appearance: none` 收掉系统外观，箭头自己画（内联 SVG 背景，不额外发请求）。
   `<option>` 的配色只有 `color-scheme` 管得到，所以一并声明。 */
.jm-select {
  flex: 0 0 auto;
  min-height: 44px;
  border-radius: var(--gs-radius-pill);
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 8'%3E%3Cpath d='M1 1l5 5 5-5' fill='none' stroke='%23CFF6FF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E")
    right var(--gs-space-3) center / 12px 8px no-repeat,
    rgba(20, 15, 46, 0.85);
  color: var(--gs-ink);
  border: 1px solid var(--gs-hairline);
  padding: 0 var(--gs-space-5) 0 var(--gs-space-3);
  font: inherit;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  appearance: none;
  -webkit-appearance: none;
  color-scheme: dark;
  cursor: pointer;
}

.jm-select:focus-visible { outline: 3px solid var(--gs-accent); outline-offset: 2px; }

/* ═══════════════════════════ 帮助页 ═══════════════════════════ */

/*
 * 六段说明原来**直接压在夜市插画上**：灯笼、霓虹和正文抢同一块地方，
 * 「Controls」那段十行文字几乎读不动（走查里最扎眼的一处可读性问题）。
 * 收进一块霜面板，插画仍然露在面板外面——那是这一屏的氛围，不是背景板。
 */
.jm-help {
  display: flex;
  flex-direction: column;
  margin-bottom: var(--gs-space-3);
}

.jm-help__section {
  display: flex;
  align-items: center;
  gap: var(--gs-space-2);
  margin: var(--gs-space-4) 0 var(--gs-space-2);
}

/* 第一段的标题不要在面板里再顶一次外边距 */
.jm-help > .jm-help__section:first-child { margin-top: 0; }

.jm-help__badge {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 10px;
  transform: rotate(45deg);
  background: linear-gradient(180deg, var(--gs-gold), #C9922B);
  flex: 0 0 auto;
}

.jm-help__badge img { width: 18px; height: 18px; transform: rotate(-45deg); }
.jm-help__badge[data-tone='controls'] { background: linear-gradient(180deg, var(--jm-grape), #6B3FB5); }
.jm-help__badge[data-tone='scoring'] { background: linear-gradient(180deg, var(--jm-success-mint), #2A9E6C); }
.jm-help__badge[data-tone='win'] { background: linear-gradient(180deg, var(--jm-neon-cyan), #1F86A8); }
.jm-help__badge[data-tone='lose'] { background: linear-gradient(180deg, var(--jm-berry), #A32659); }

.jm-help__title { margin: 0; font-size: clamp(17px, 5vw, 22px); font-weight: 900; }
.jm-help__body {
  margin: 0 0 var(--gs-space-2);
  font-size: clamp(13px, 3.6vw, 15px);
  line-height: 1.55;
  color: var(--gs-ink-soft);
}

.jm-help__body--note {
  margin-bottom: 0;
  padding-top: var(--gs-space-3);
  border-top: 1px solid var(--gs-hairline);
  color: var(--gs-ink-dim);
}

/* 插图原来是「五颗果冻在左、说明在右」的一行：果冻挤在左边一小撮，
   右边那段字折成五行，两边高度差一倍。改成上下两块，各占满一行。 */
.jm-help__figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--gs-space-2);
  padding: var(--gs-space-3);
  margin-bottom: var(--gs-space-2);
  border-radius: var(--gs-radius-md);
  border: 1px solid rgba(167, 107, 255, 0.4);
  background: rgba(20, 15, 46, 0.55);
  text-align: center;
}

.jm-help__figure-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--gs-space-2);
  flex-wrap: wrap;
}

/* ═══════════════════════════ 加载屏与错误屏 ═══════════════════════════ */

.jm-logo { width: min(78%, 340px); height: auto; filter: drop-shadow(0 10px 26px rgba(255, 63, 168, 0.35)); }

.jm-tip {
  display: flex;
  align-items: center;
  gap: var(--gs-space-3);
  padding: var(--gs-space-3);
  border-radius: var(--gs-radius-md);
  border: 1px solid rgba(255, 176, 58, 0.4);
  background: rgba(20, 15, 46, 0.7);
  font-size: clamp(13px, 3.6vw, 15px);
  line-height: 1.35;
}

.jm-hero-art { width: min(52%, 240px); height: auto; }

/* 离线屏的标题是居中的，正文却左对齐——一屏上两种对齐，看起来像两块拼起来的。 */
.jm-zone--center .jm-help__body { text-align: center; }

/* ═══════════════════════════ 横屏与桌面：分区重排，不是第二套设计 ═══════════════════════════ */

.jm-stage[data-orientation='landscape'] .jm-hud {
  flex-direction: column;
  justify-content: flex-start;
  gap: var(--gs-space-2);
  padding: var(--gs-space-2);
}

.jm-stage[data-orientation='landscape'] .jm-hud__top,
.jm-stage[data-orientation='landscape'] .jm-hud__bottom {
  flex-direction: column;
  align-items: stretch;
  gap: var(--gs-space-2);
}

/* 横屏这一列由 flex 分配：上半块可压缩，下半块（塌陷环 + 融化条）保尺寸。
   竖屏那套内联高度是按"横着一行"算的，套到竖着一列上会把最后一件顶出屏幕
   ——ui.mjs 里现在只在竖屏写那两个高度，这里接手把空间分完。 */
.jm-stage[data-orientation='landscape'] .jm-hud__top { flex: 1 1 auto; min-height: 0; }
.jm-stage[data-orientation='landscape'] .jm-hud__bottom { flex: 0 0 auto; }
/* 订单卡按**内容**高，多出来的空间留白，不要让它把自己撑成一个 300px 的空盒子。
   原来 `flex: 1 1 auto` 是为了"别去挤下面的读数"——`min-height: 0` + 不许长大
   同样做得到，而且订单只有两项时不会得到一块四分之三是空的面板。 */
.jm-stage[data-orientation='landscape'] .jm-order { flex: 0 1 auto; min-height: 0; overflow: hidden; }
.jm-stage[data-orientation='landscape'] .jm-hud__top { justify-content: flex-start; }

.jm-stage[data-orientation='landscape'] .jm-order { max-width: none; }
.jm-stage[data-orientation='landscape'] .jm-moves { align-self: center; }
.jm-stage[data-orientation='landscape'] .jm-gauges { flex-direction: column; align-items: stretch; }
.jm-stage[data-orientation='landscape'] .jm-collapse { align-self: center; }
/* 横屏时动作区是右边一竖排。竖排下 `flex: 1 1 0` 会把三颗按钮各自拉到分区高的
   三分之一（实测 100px 高、443px 宽）——按钮不是越大越好，那是把一块空分区
   摊给了三颗按钮。按内容高、宽度也封顶，一排按钮就还是一排按钮。 */
.jm-stage[data-orientation='landscape'] .jm-actions {
  flex-direction: column;
  align-items: stretch;
  justify-content: center;
  max-width: 320px;
  margin-inline: auto;
}

.jm-stage[data-orientation='landscape'] .jm-actions > .gs-btn { flex: 0 0 auto; }
.jm-stage[data-orientation='landscape'] .jm-ship { flex: 0 0 auto; }
/* 融化条是这一列的最后一件，别贴着屏幕下沿 */
.jm-stage[data-orientation='landscape'] .jm-hud { padding-bottom: var(--gs-space-3); }
.jm-stage[data-orientation='landscape'] .jm-cards { flex-direction: row; }
.jm-stage[data-orientation='landscape'] .jm-coach { flex-wrap: wrap; }
.jm-stage[data-orientation='landscape'] .jm-coach::before { display: none; }

/* 手机横屏且屏够宽：放大零件一档。**桌面不走这里**——桌面跑的是共用库
   锁出来的 540px 宽手机画布（`game-shell/ui/ui.css` 末尾「手机画布锁定」），
   按视口宽度放大等于拿桌面的尺子量手机画布，分区会被撑破。 */
@media (min-width: 900px) and (max-height: 560px) {
  /* 上下内边距**反过来收**：横屏缺的是高。原来这里跟着一起放大成 space-3，
     页头的"一行控件 + 一句副标题"（47 + 17）就顶破了 69px 的分区，
     副标题被切掉一半（走查在 landscape-932x430 上量到 +9px）。 */
  .jm-zone--pad { padding: 0 var(--gs-space-5); }
  .jm-moves { width: 74px; height: 74px; }
  .jm-level { width: 76px; min-height: 76px; font-size: 24px; }
}
