/* drill.css — 1:1 移植自小程序 subpkg2/pages/pc-phrase/pc-phrase.wxss
   单位说明：vw/em 体系原样保留（浏览器原生支持）；rpx 按 750rpx=屏宽 换算为 vw(×0.13333)；
   ≥960px 媒体查询为 px 锁定，与小程序电脑版完全一致。 */

/* 练习时隐藏全局顶栏/页脚，沉浸全屏（与小程序独立页面一致）*/
body.drill-mode .topbar, body.drill-mode .footer { display: none !important; }
body.drill-mode .main { padding: 0 !important; max-width: none !important; }

/* 练习页（指法 #kfPage / 词组等 .kf-page）：保留顶栏，
   但解除 #main 的 1080px 宽度限制，让页面（含背景网格）铺满视口。
   注意：**不能带 body.is-home 前缀** —— 只有首页/指法页带这个 class，
   拼音练习、中文练习是 body.dark-topbar，带上前缀它们的宽度就不放开，
   网格背景左右会各空出 176px（铺不满）。 */
.main:has(#kfPage),
.main:has(.kf-page) { max-width: none; padding: 0; }

.drill-page {
  width: 100%;
  min-height: calc(100 * var(--pg-vh, 1vh));
  display: flex;
  flex-direction: column;
  /* 上下留出等量内衬：内容不够高时自然居中，够高时也不会贴死顶栏 */
  padding: 18px 0;
  box-sizing: border-box;
  position: relative;
  background: linear-gradient(180deg, #E6F4F1 0%, #FFFFFF 30%);
}
/* 练习页同时挂着 .kf-page（要靠它的 100vw 破开 #main 限宽、铺满视口），
   但它又是 flex 列容器 —— 不加这条，100vw 会被 flex 压回内容宽度，
   背景网格就铺不满、只盖住中间一块。 */
.drill-page.kf-page { flex-shrink: 0; }

/* ===== 顶栏内的练习模式区（返回 + 关卡标题），替代品牌区 =====
   同时隐藏首页那排导航（练习页是独立的一套导航，与首页不同）。 */
/* 练习页顶栏：只留「返回 + 标题」。
   首页那排导航、右侧登录按钮、左侧品牌区全部隐藏。
   注意：这些都自带 display:flex，会压过 hidden 属性，必须用 !important 隐藏。 */
body.practice-mode .topnav,
body.practice-mode #userBox,
body.practice-mode .brand { display: none !important; }
body.practice-mode .topbar-inner { gap: 0; }
/* 练习模式下：品牌区/导航/登录框都隐藏了，这个容器撑满整条顶栏，
   内部再用 margin-left:auto 把样式按钮推到最右。 */
.topbar-practice { display: flex; align-items: center; gap: 14px; min-width: 0; flex: 1; }
.tbp-back {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 6px 15px 6px 9px; border-radius: 999px;
  font-size: 13.5px; font-weight: 700;
  color: #ffffff;
  background: rgba(255, 255, 255, .13);
  border: 1.5px solid rgba(255, 255, 255, .30);
  cursor: pointer; user-select: none; white-space: nowrap;
  transition: background .18s, border-color .18s, transform .18s;
}
.tbp-back:hover {
  background: rgba(255, 255, 255, .24);
  border-color: rgba(255, 255, 255, .55);
  transform: translateX(-2px);
}
.tbp-back:active { transform: scale(.96); }
.tbp-arrow { font-size: 18px; line-height: 1; margin-top: -2px; }
.tbp-title {
  font-size: 15px; font-weight: 800; color: #ffffff;
  letter-spacing: .5px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* 顶栏样式按钮组（键盘样式 / 手部样式）：推到最右 */
.tbp-skins { display: inline-flex; gap: 8px; margin-left: auto; }

/* ===== 练习页顶栏：悬浮胶囊条（照 Aceternity 那个 NavBody）=====
   顶部导航不再是通栏黑条，而是一条居中悬浮的圆角胶囊；返回/标题在左，
   控制按钮 + 样式按钮在右，全部收在这一条里（原来控制行单独占一行，已合并）。 */
.topbar {
  position: sticky; top: 0; z-index: 50;
  background: transparent;
  border-bottom: none;
  box-shadow: none;
  /* 胶囊离页面顶端留出余量（原来 12px 太贴顶） */
  padding: 24px 20px 0;
  pointer-events: none;          /* 只让胶囊本身可点，其余区域不挡内容 */
}
.topbar[hidden] { display: none; }
.topbar-inner {
  pointer-events: auto;
  max-width: 1400px; margin: 0 auto; height: auto; min-height: 0;
  padding: 7px 12px 7px 10px; border-radius: 999px;
  display: flex; align-items: center; gap: 12px;
  /* 黑底胶囊 + 里面按钮用白底黑字（用户要求整体反色） */
  background: #111111;
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border: 1px solid #111111;
  box-shadow: 0 8px 28px rgba(3, 24, 21, .22);
}
/* 练习页：胶囊宽度对齐练习内容那一列（932px）。
   原来 max-width:1400px —— 宽屏上比练习卡片宽、又比屏幕窄，两头不靠，看着别扭。
   932 正是 .pinyin-flow / 操作行 / 键盘可见边框共用的宽度，
   对齐后顶栏左右边与下面卡片完全齐平（宽屏窄屏都居中，窄屏自动变成屏宽-40）。 */
body.practice-mode .topbar-inner { max-width: 932px; }
/* 胶囊里的按钮：**黑底白字、扁平无阴影**（用户要求去掉立体感）。
   注意 .ctl-btn 基础规则带渐变底 + 两层阴影，这里必须覆盖掉。 */
.topbar-inner .ctl-btn,
.topbar-inner .tbp-skin-btn,
.topbar-inner .tbp-back {
  height: 34px; flex: 0 0 auto; padding: 0 13px;
  flex-direction: row; gap: 7px;
  border-radius: 999px;
  background: #ffffff;
  background-image: none;
  border: 1px solid #ffffff;
  box-shadow: none;
  color: #111111;
  transform: none;
}
.topbar-inner .ctl-btn:hover,
.topbar-inner .tbp-skin-btn:hover,
.topbar-inner .tbp-back:hover {
  background: #e8efee; border-color: #e8efee; transform: none; box-shadow: none;
}
.topbar-inner .ctl-btn:active,
.topbar-inner .tbp-skin-btn:active,
.topbar-inner .tbp-back:active { transform: none; }
/* 胶囊内控制按钮的「开关」：从小圆点换成**方形勾选框**（Uiverse 那个果冻勾选）。
   JS 仍按老逻辑切 .on 类（syncCtl 里 dot.classList.toggle('on', on)），
   这里只改外观：未选=空心方框，选中=填色 + 打勾 + 果冻弹跳。 */
.topbar-inner .ctl-btn .ctl-dot {
  position: relative;
  width: 16px; height: 16px;
  border-radius: 5px;
  background: transparent;
  border: 1.5px solid #c2cdcb;
  box-shadow: none;
  transition: background .2s ease, border-color .2s ease;
}
/* 勾：右+下两条边，旋转 45° 成对勾 */
.topbar-inner .ctl-btn .ctl-dot::after {
  content: '';
  position: absolute;
  top: 1px; left: 4.5px;
  width: 4px; height: 8px;
  opacity: 0;
  transform: rotate(45deg) scale(0);
  border-right: 1.8px solid #fff;
  border-bottom: 1.8px solid #fff;
  transition: all .3s ease;
  transition-delay: .1s;
}
/* 选中态：填色 + 打勾 + 果冻弹跳 */
.topbar-inner .ctl-btn .ctl-dot.on {
  border-color: transparent;
  background: #0f9488;
  animation: ctlJelly .4s ease;
}
.topbar-inner .ctl-btn .ctl-dot.on::after {
  opacity: 1;
  transform: rotate(45deg) scale(1);
}
@keyframes ctlJelly {
  0%   { transform: scale(1, 1); }
  20%  { transform: scale(1.3, .7); }
  40%  { transform: scale(.7, 1.3); }
  60%  { transform: scale(1.1, .9); }
  80%  { transform: scale(.9, 1.1); }
  100% { transform: scale(1, 1); }
}
/* 各开关的选中色沿用原来的主题色 */
.topbar-inner .ctl-btn .ctl-dot.dot-sm.on { background: #1F2937; }
.topbar-inner .ctl-btn .ctl-dot.dot-jm.on { background: #EC4899; }
.topbar-inner .ctl-btn .ctl-dot.dot-ym.on { background: #2563EB; }
.topbar-inner .ctl-btn .ctl-dot.dot-voice.on { background: #0F766E; }
.topbar-inner .ctl-btn .ctl-dot.dot-py.on,
.topbar-inner .ctl-btn .ctl-dot.dot-grid.on,
.topbar-inner .ctl-btn .ctl-dot.dot-cn.on { background: #0F766E; }
.topbar-inner .ctl-btn .ctl-dot.dot-replay.on { background: #3B82F6; }
.topbar-inner .ctl-btn .ctl-dot.dot-hand.on { background: #0F766E; }
@media (prefers-reduced-motion: reduce) {
  .topbar-inner .ctl-btn .ctl-dot.on { animation: none; }
}
.topbar-inner .ctl-btn span,
.topbar-inner .tbp-skin-btn,
.topbar-inner .tbp-back { font-size: 12.5px; font-weight: 700; white-space: nowrap; }
.topbar-inner .tbp-ctl { display: inline-flex; align-items: center; gap: 7px; margin-left: 6px; }
.topbar-inner .tbp-skins { margin-left: auto; gap: 7px; }
.topbar-inner .tbp-skin-btn::before { box-shadow: 0 0 0 1px rgba(15,118,110,.35); }
.topbar-inner .tbp-title { color: #ffffff; }
/* 胶囊右侧「样式」按钮（键盘样式/文字样式/背景样式/手部样式） */
.topbar-inner .tbp-skin-btn { font-size: 12.5px; padding: 7px 13px; }
/* 窄屏：胶囊允许横向滚动，别把按钮挤没 */
@media (max-width: 1100px) {
  .topbar-inner { overflow-x: auto; scrollbar-width: none; }
  .topbar-inner::-webkit-scrollbar { display: none; }
  .topbar-inner .tbp-skins { margin-left: 0; }
}
/* —— 方块样式面板：分区（动态 / 边框 / 颜色）—— */
.bx-sec { padding: 9px 0 3px; border-top: 1px solid #eef5f3; }
.bx-sec:first-of-type { border-top: none; padding-top: 2px; }
.bx-sec .skin-head { font-size: 13px; font-weight: 900; color: #0b5c55; margin-bottom: 8px; }
.bx-sub { font-size: 12px; font-weight: 700; color: #7b918c; margin: 10px 0 7px; }
.bx-sub2 { font-size: 10.5px; color: #94a8a4; margin-top: 7px; line-height: 1.4; }
/* 动态样式：固定 3 列网格，每排都对齐（不再随名字长短忽多忽少） */
.bx-anims { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
.bx-anim {
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; font-weight: 700; color: #4a726b;
  background: #f4faf9; border: 1.5px solid #dcebe7;
  border-radius: 999px; padding: 5px 8px;
  cursor: pointer; user-select: none; white-space: nowrap;
  transition: background .16s, border-color .16s, color .16s;
}
.bx-anim:hover { background: #e6f4f1; border-color: #9fd8ce; }
.bx-anim.on { background: #0F766E; border-color: #0F766E; color: #fff; }
/* 文字样式面板「拼音分段变色」开关里的小色点：声母=深墨 / 介母=粉 / 韵母=蓝 */
.bx-anim .pyc-dot {
  width: 8px; height: 8px; border-radius: 50%; margin-right: 5px; flex: none;
  box-shadow: 0 0 0 1px rgba(15,42,38,.25);
}
.bx-anim.on .pyc-dot { box-shadow: 0 0 0 1px rgba(255,255,255,.8); }
.pyc-dot-sm { background: #1F2937; }
.pyc-dot-jm { background: #EC4899; }
.pyc-dot-ym { background: #2563EB; }
/* 重动效（一直转）：标签右上角一个小圆点做区分 */
/* "一直转"分组提示：带边框的提示框，放在该组按钮上方，红字醒目。
   （取消原先每个按钮上的小圆点 —— 上方已有整组提醒，点属重复。） */
.bx-tipline {
  grid-column: 1 / -1;          /* 跨满整行（在 3 列网格里） */
  font-size: 11.5px; font-weight: 700; color: #d93a30;
  line-height: 1.62; padding: 9px 12px; margin: 2px 0 5px;
  background: #fff5f4; border: 1.5px solid #f6c9c5; border-radius: 10px;
}
/* 转速滑块 */
.bx-speed {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 8px;
  margin-top: 8px; padding-top: 9px; border-top: 1px dashed #e2efec;
}
.bx-speed-label { font-size: 12px; font-weight: 800; color: #0b5c55; flex: none; }
.bx-speed-slow, .bx-speed-fast { font-size: 11px; color: #94a8a4; flex: none; }
.bx-speed-range {
  flex: 1; min-width: 0; height: 4px;
  -webkit-appearance: none; appearance: none;
  background: linear-gradient(90deg, #cde9e4, #0F766E);
  border-radius: 999px; outline: none; cursor: pointer;
}
.bx-speed-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff; border: 2.5px solid #0F766E;
  box-shadow: 0 1px 4px rgba(11,92,85,.25); cursor: pointer;
}
.bx-speed-range::-moz-range-thumb {
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff; border: 2.5px solid #0F766E;
  box-shadow: 0 1px 4px rgba(11,92,85,.25); cursor: pointer;
}

/* 一键恢复默认：放在面板最底部，整行宽按钮 */
.bx-reset-wrap {
  display: flex; flex-direction: column; gap: 8px;
  margin-top: 14px; padding-top: 14px; border-top: 1px solid #eef5f3;
}
.bx-reset {
  display: block; width: 100%;      /* 整行宽 */
  font-size: 13.5px; font-weight: 800; color: #0b5c55;
  background: #f4faf9; border: 1.5px solid #cde9e4;
  border-radius: 11px; padding: 11px 16px;
  cursor: pointer; user-select: none;
  transition: background .16s, border-color .16s, transform .16s;
}
.bx-reset:hover { background: #e6f4f1; border-color: #9fd8ce; }
.bx-reset:active { transform: scale(.985); }
.bx-reset-tip { font-size: 10.5px; color: #94a8a4; line-height: 1.5; text-align: center; }

/* 色板：一排圆点 */
.bx-colors { display: flex; flex-wrap: wrap; gap: 7px; }
.bx-color {
  width: 26px; height: 26px; border-radius: 50%;
  background: var(--c, #16302c);
  border: 2px solid #fff;
  box-shadow: 0 0 0 1.5px #dcebe7, 0 1px 3px rgba(0,0,0,.14);
  cursor: pointer; display: block;
  transition: transform .16s, box-shadow .16s;
}
.bx-color:hover { transform: scale(1.14); }
.bx-color.on { box-shadow: 0 0 0 2.5px #0F766E, 0 1px 4px rgba(0,0,0,.2); }

/* 背景样式缩略图：一小块示意底色 + 纹理 */
.skin-bg {
  height: 48px; border-radius: 8px; margin-bottom: 8px;
  border: 1px solid #e4eeeb; overflow: hidden;
}
.skin-bg-mint {
  background:
    radial-gradient(60px 40px at 20% 0%, rgba(16,185,129,.26), transparent 70%),
    linear-gradient(180deg, #effaf6, #ffffff);
}
.skin-bg-sakura {
  background:
    radial-gradient(60px 40px at 20% 0%, rgba(244,114,182,.26), transparent 70%),
    linear-gradient(180deg, #fdf3f6, #ffffff);
}
.skin-bg-lavender {
  background:
    radial-gradient(60px 40px at 20% 0%, rgba(139,92,246,.24), transparent 70%),
    linear-gradient(180deg, #f6f4fd, #ffffff);
}
.skin-bg-warm {
  background:
    radial-gradient(60px 40px at 20% 0%, rgba(245,158,11,.28), transparent 70%),
    linear-gradient(180deg, #fdf8f0, #ffffff);
}
.skin-bg-cool {
  background:
    radial-gradient(60px 40px at 20% 0%, rgba(56,189,248,.30), transparent 70%),
    linear-gradient(180deg, #f0f7fd, #ffffff);
}
.skin-bg-plain {
  background: #ffffff;
}

/* ===== 主题页（侧边栏「主题」入口）===== */
.theme-page { width: min(1120px, calc(100% - 48px)); margin: 0 auto; padding: 28px 0 72px; }
.tp-head {
  background: linear-gradient(135deg, #0b423c, #0F766E);
  border-radius: 24px; padding: 30px 40px 28px; color: #fff; margin-bottom: 26px;
}
.tp-kicker { font-size: 12px; font-weight: 800; letter-spacing: .2em; color: #7de8d6; text-transform: uppercase; margin-bottom: 10px; }
.tp-head h1 { font-size: clamp(30px, calc(3.2 * var(--pg-vw, 1vw)), 40px); font-weight: 900; letter-spacing: 2px; margin: 0 0 10px; }
.tp-head p { margin: 0; color: rgba(255,255,255,.78); font-size: 14px; }
/* 主题页返回练习按钮 */
.tp-back {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 6px 14px;
  background: rgba(255,255,255,.15); border: 1px solid rgba(255,255,255,.25);
  border-radius: 8px; cursor: pointer; font-family: inherit;
  color: #fff; font-size: 13px; font-weight: 700;
  margin-bottom: 14px;
  transition: background .2s, transform .15s;
}
.tp-back:hover { background: rgba(255,255,255,.28); }
.tp-back:active { transform: scale(.96); }
.tp-back .tbp-arrow { font-size: 16px; line-height: 1; margin-right: 2px; }
/* 主题页 tab 导航（抄 kf-tabs 胶囊风） */
.tp-tabs {
  display: flex; gap: 16px;
  margin-bottom: 22px;
  overflow-x: auto; scrollbar-width: none;
}
.tp-tabs::-webkit-scrollbar { display: none; }
.tp-tab {
  flex: 0 0 auto;
  padding: 10px 26px;
  border: 1px solid #e4eeeb;
  border-radius: 14px; cursor: pointer; font-family: inherit;
  background: #fff; color: #0d3b35;
  font-size: 15px; font-weight: 700; white-space: nowrap;
  box-shadow: 0 4px 14px rgba(7, 59, 53, 0.06);
  transition: all .2s ease;
}
.tp-tab:hover { border-color: #0e6f64; }
.tp-tab.active {
  background: linear-gradient(135deg, #0e6f64, #0b5c55);
  border-color: rgba(94, 234, 212, 0.5);
  color: #fff;
  box-shadow: 0 8px 20px rgba(11, 92, 85, 0.28);
}
.tp-tab-panel { animation: tpFade .25s ease; }
@keyframes tpFade { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
.tp-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px; }
.tp-card {
  background: #fff; border: 2px solid #e4eeeb; border-radius: 16px;
  padding: 12px 12px 14px; cursor: pointer; text-align: center;
  transition: border-color .15s, transform .15s, box-shadow .15s;
}
.tp-card:hover { transform: translateY(-2px); box-shadow: 0 8px 22px rgba(15,42,38,.08); }
.tp-card.on { border-color: #0F766E; }
.tp-card .skin-bg { height: 88px; margin-bottom: 10px; border-radius: 10px; }
.tp-name { font-size: 14px; font-weight: 800; color: #0b5c55; }
.tp-tip { font-size: 11px; color: #0F766E; font-weight: 700; margin-top: 3px; height: 15px; line-height: 15px; }

/* —— 拼音格/米字格卡片缩略：白底面板内放大展示（线条画法与颜色复用 .skin-th-pg / .skin-th-mz，--c 注色）—— */
.tp-grid-thumb {
  height: 92px; border-radius: 10px; margin-bottom: 10px;
  background: #ffffff; border: 1px solid #e4eeeb;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}
.tp-grid-thumb .skin-th-pg { width: 72%; height: 48px; }
.tp-grid-thumb .skin-th-mz { width: 58px; height: 58px; }
.tp-sec-head { margin: 30px 0 14px; }
.tp-sec-title { font-size: 17px; font-weight: 900; color: #16302c; }
.tp-sec-sub { font-size: 12px; color: #8aa39e; margin-top: 3px; }
.tp-card-hand { display: flex; flex-direction: column; align-items: center; padding: 12px 12px 14px; }
.tp-grid-hand { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }

/* —— 键盘样式卡片：1:1 抄练习页真实键盘（mpKbRows 的 .keyboard-area），固定舞台内整体等比缩小 —— */
/* 键盘区一排 3 个：卡片够宽，键盘缩小后才看得清键帽和字母（9 个皮肤正好 3×3 铺满） */
.tp-grid-kb { grid-template-columns: repeat(3, 1fr); }
.tp-kb-wrap {
  position: relative; height: 74px; margin-bottom: 10px; border-radius: 10px; overflow: hidden;
  background: linear-gradient(165deg, #f7f8f8 0%, #ebeeee 100%);
}
.tp-kbs {
  position: absolute; left: 50%; top: 50%; width: 880px;
  transform: translate(-50%, -50%) scale(0.165);
  pointer-events: none;
}
/* 舞台内键盘改固定像素（立体键帽样式与练习页大屏版完全一致，只是不再用 vw） */
.tp-kbs .keyboard-area {
  width: 880px; max-width: none; margin: 0; gap: 4px; padding: 16px 12px;
  border-radius: 18px; border-width: 2px;
  box-shadow: 0 8px 18px rgba(15,42,38,.14);
}
.tp-kbs .kb-row { gap: 4px; }
.tp-kbs .kb-key { height: 53px; border-radius: 7px; border-width: 1px; }
.tp-kbs .kb-key-main { font-size: 15px; }
.tp-kbs .kb-key-sub, .tp-kbs .kb-func-text { font-size: 9px; }
/* 每张卡各自套皮肤色（比 body.kbskin-* 的当前皮肤优先级更高，九张卡九种色） */
.tp-kbs.tp-kbs-default .keyboard-area { --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #eff7f7 100%); --kb-frame-border: #0F766E; --kb-key-bg: #FFFFFF; --kb-key-border: #0F766E; --kb-key-text: #1F2937; --kb-key-sub: #9CA3AF; --kb-func-bg: #F0FAF9; --kb-func-border: #0F766E; --kb-func-text: #0F766E; }
.tp-kbs.tp-kbs-mono    .keyboard-area { --kb-frame-bg: #ffffff; --kb-frame-border: #111111; --kb-key-bg: #FFFFFF; --kb-key-border: #111111; --kb-key-text: #111111; --kb-key-sub: #6b6b6b; --kb-func-bg: #F5F5F5; --kb-func-border: #111111; --kb-func-text: #111111; }
.tp-kbs.tp-kbs-red     .keyboard-area { --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #fff1f0 100%); --kb-frame-border: #d93a30; --kb-key-bg: #FFFFFF; --kb-key-border: #d93a30; --kb-key-text: #b3271f; --kb-key-sub: #c97a74; --kb-func-bg: #fff1f0; --kb-func-border: #d93a30; --kb-func-text: #b3271f; }
.tp-kbs.tp-kbs-pink    .keyboard-area { --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #fdf0f7 100%); --kb-frame-border: #e0559b; --kb-key-bg: #FFFFFF; --kb-key-border: #e0559b; --kb-key-text: #b83c7c; --kb-key-sub: #cf90b4; --kb-func-bg: #fdf0f7; --kb-func-border: #e0559b; --kb-func-text: #b83c7c; }
.tp-kbs.tp-kbs-blue    .keyboard-area { --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #f0f6ff 100%); --kb-frame-border: #2f6fe4; --kb-key-bg: #FFFFFF; --kb-key-border: #2f6fe4; --kb-key-text: #1f55b8; --kb-key-sub: #7f9fd6; --kb-func-bg: #f0f6ff; --kb-func-border: #2f6fe4; --kb-func-text: #1f55b8; }
.tp-kbs.tp-kbs-orange  .keyboard-area { --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #fff5eb 100%); --kb-frame-border: #e8833a; --kb-key-bg: #FFFFFF; --kb-key-border: #e8833a; --kb-key-text: #bd6420; --kb-key-sub: #d7a274; --kb-func-bg: #fff5eb; --kb-func-border: #e8833a; --kb-func-text: #bd6420; }
.tp-kbs.tp-kbs-purple  .keyboard-area { --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #f7f3ff 100%); --kb-frame-border: #7c5cd6; --kb-key-bg: #FFFFFF; --kb-key-border: #7c5cd6; --kb-key-text: #5f43ab; --kb-key-sub: #a294cc; --kb-func-bg: #f7f3ff; --kb-func-border: #7c5cd6; --kb-func-text: #5f43ab; }
.tp-kbs.tp-kbs-cyan    .keyboard-area { --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #ecfbfb 100%); --kb-frame-border: #14b8c4; --kb-key-bg: #FFFFFF; --kb-key-border: #14b8c4; --kb-key-text: #0e8a94; --kb-key-sub: #7ccbd1; --kb-func-bg: #ecfbfb; --kb-func-border: #14b8c4; --kb-func-text: #0e8a94; }
.tp-kbs.tp-kbs-dark    .keyboard-area { --kb-frame-bg: linear-gradient(158deg, #263241 0%, #1b2530 100%); --kb-frame-border: #3d4d61; --kb-key-bg: #34414f; --kb-key-border: #55677e; --kb-key-text: #e8eef5; --kb-key-sub: #93a3b7; --kb-func-bg: #2b3746; --kb-func-border: #55677e; --kb-func-text: #b9c7d6; }

/* —— 手部样式卡片：真实手图（与练习页同一张 hand_f_l.png + 同一套变色滤镜）—— */
.tp-hand-wrap {
  width: 100%; height: 140px; margin-bottom: 10px; border-radius: 10px; overflow: hidden;
  background: linear-gradient(165deg, #f7f8f8 0%, #ebeeee 100%);
  display: flex; align-items: center; justify-content: center;
}
.tp-hand-img { --hf: contrast(1.55) saturate(1.6) brightness(1.02); height: 120px; width: auto; max-width: 100%; filter: var(--hf); transform: translate(75px, -35px) scale(2.8); transform-origin: center center; }
.tp-card-hand[data-thand="pink"]   .tp-hand-img { filter: var(--hf) hue-rotate(333deg) saturate(.88) brightness(1.08); }
.tp-card-hand[data-thand="rose"]   .tp-hand-img { filter: var(--hf) hue-rotate(333deg) saturate(1.25); }
.tp-card-hand[data-thand="orange"] .tp-hand-img { filter: var(--hf) hue-rotate(38deg); }
.tp-card-hand[data-thand="amber"]  .tp-hand-img { filter: var(--hf) hue-rotate(32deg) saturate(.95); }
.tp-card-hand[data-thand="yellow"] .tp-hand-img { filter: var(--hf) hue-rotate(45deg) saturate(.9) brightness(1.1); }
.tp-card-hand[data-thand="green"]  .tp-hand-img { filter: var(--hf) hue-rotate(142deg) saturate(1.2); }
.tp-card-hand[data-thand="teal"]   .tp-hand-img { filter: var(--hf) hue-rotate(175deg) saturate(.9); }
.tp-card-hand[data-thand="cyan"]   .tp-hand-img { filter: var(--hf) hue-rotate(184deg); }
.tp-card-hand[data-thand="blue"]   .tp-hand-img { filter: var(--hf) hue-rotate(219deg); }
.tp-card-hand[data-thand="indigo"] .tp-hand-img { filter: var(--hf) hue-rotate(243deg) saturate(1.1); }
.tp-card-hand[data-thand="purple"] .tp-hand-img { filter: var(--hf) hue-rotate(258deg); }

/* —— 打字音效卡片：喇叭图标 + 对勾/叉角标；正确绿、错误红 —— */
.tp-card-sound { display: flex; flex-direction: column; align-items: center; padding: 12px 12px 14px; }
.tp-grid-sound { grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); }
.tp-sound-wrap {
  position: relative;
  width: 100%; height: 96px; margin-bottom: 10px; border-radius: 10px;
  background: linear-gradient(165deg, #f7f8f8 0%, #ebeeee 100%);
  display: flex; align-items: center; justify-content: center;
  color: #9aa7b2; transition: color .15s;
}
.tp-sound-wrap > svg { width: 40px; height: 40px; }
.tp-sound-badge {
  position: absolute; right: 10px; bottom: 8px; width: 22px; height: 22px;
  border-radius: 50%; display: flex; align-items: center; justify-content: center;
}
.tp-sound-badge svg { width: 13px; height: 13px; }
.sfx-ok .tp-sound-badge { background: #e3f3ec; color: #0f766e; }
.sfx-err .tp-sound-badge { background: #fde8e7; color: #e8453c; }
.tp-card-sound.sfx-ok:hover .tp-sound-wrap,
.tp-card-sound.sfx-ok.on .tp-sound-wrap { color: #0f766e; }
.tp-card-sound.sfx-err:hover .tp-sound-wrap,
.tp-card-sound.sfx-err.on .tp-sound-wrap { color: #e8453c; }
/* 自定义上传卡：未上传时虚线框，提示"可以放自己的音频" */
.tp-card-sound.sfx-empty .tp-sound-wrap {
  background: #f6f9f8;
  box-shadow: inset 0 0 0 1.5px #c6d7d2;
  color: #8ba39c;
}
.tp-card-sound.sfx-empty:hover .tp-sound-wrap { color: #0f766e; box-shadow: inset 0 0 0 1.5px #0f766e; }
/* 已上传自定义卡右上角：更换 / 删除 小圆钮 */
.tp-sound-acts {
  position: absolute; top: 7px; right: 7px; display: flex; gap: 4px;
  opacity: 0; transition: opacity .15s;
}
.tp-card-sound.on .tp-sound-acts,
.tp-card-sound:hover .tp-sound-acts { opacity: 1; }
.tp-sound-act {
  width: 22px; height: 22px; border-radius: 50%;
  background: rgba(255,255,255,.92); box-shadow: 0 1px 4px rgba(15,60,52,.18);
  display: flex; align-items: center; justify-content: center;
  cursor: pointer; color: #6f827c;
}
.tp-sound-act svg { width: 12px; height: 12px; }
.tp-sound-act:hover { color: #0f766e; }
.tp-sound-act[data-sfxact="delete"]:hover { color: #e8453c; }
/* ===== 主题页 · 音效音量滑杆（正确组绿 / 错误组红，各管各的）===== */
.tp-vol-row {
  display: flex; align-items: center; gap: 10px;
  margin: 2px 2px 6px; max-width: 360px;
}
.tp-vol-ico { width: 20px; height: 20px; color: #8ba39c; display: flex; flex: 0 0 auto; }
.tp-vol-ico svg { width: 20px; height: 20px; }
.tp-vol-label { font-size: 13px; color: #5f756e; flex: 0 0 auto; }
.tp-vol {
  -webkit-appearance: none; appearance: none;
  flex: 1 1 auto; height: 22px; background: transparent; cursor: pointer; margin: 0;
}
.tp-vol::-webkit-slider-runnable-track {
  height: 6px; border-radius: 3px; background: #dde8e4;
}
.tp-vol::-moz-range-track {
  height: 6px; border-radius: 3px; background: #dde8e4;
}
.tp-vol::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; border-radius: 50%;
  background: #fff; border: 2px solid #0f766e;
  margin-top: -6px; box-shadow: 0 1px 4px rgba(15,60,52,.25);
}
.tp-vol::-moz-range-thumb {
  width: 14px; height: 14px; border-radius: 50%;
  background: #fff; border: 2px solid #0f766e;
  box-shadow: 0 1px 4px rgba(15,60,52,.25);
}
.tp-vol-row.sfx-err .tp-vol::-webkit-slider-thumb { border-color: #e8453c; }
.tp-vol-row.sfx-err .tp-vol::-moz-range-thumb { border-color: #e8453c; }
.tp-vol-val { font-size: 13px; color: #5f756e; min-width: 42px; text-align: right; flex: 0 0 auto; }

/* ===== 主题页 · 文字样式：左「自动打字」预览台 + 右三行色板/分段变色 =====
   预览台结构/类名与词组练习完全一致（.pf-item.current 待输入框 + .pf-units 四线格 +
   .pf-pchar/.pf-pchar-hit/.pf-char-cursor + .pf-mtzg），样式零重写——只把练习页的
   vw 基准字号换成固定 30px，内部全 em 自动等比缩小；三色/高亮块/光标/米字格框色
   全部走练习页同一套变量（--pc-py-* / --py-line / --mtzg-*），选了拼音格/米字格样式也自动跟随。 */
.tp-text-wrap {
  display: grid; grid-template-columns: 380px 1fr; gap: 16px 26px;
  background: #fff; border: 2px solid #e4eeeb; border-radius: 16px; padding: 18px;
}
.tp-text-prev {
  grid-row: 1 / span 5; align-self: stretch;
  display: flex; flex-direction: column; gap: 12px;
}
/* 预览框撑满剩余高度，拼音+米字格在其中垂直居中 */
.tp-text-prev .pf-item {
  border-width: 2px; border-radius: 14px; padding: 14px 14px 18px;
  flex: 1; display: flex; align-items: center; justify-content: center;
}
.tp-text-prev .pf-units { font-size: 50px; gap: 1em; }
.tp-text-prev .pf-unit { gap: .42em; }
.tp-text-prev .pf-mtzg { width: 1.94em; height: 1.94em; border-radius: .08em; }
.tp-text-prev .pf-mtzg-char { font-size: 1.37em; }
/* 图例：三个小色块 + 文字说明 */
.tp-text-legend { display: flex; justify-content: center; gap: 16px; margin-top: 14px; }
.tp-text-legend span { display: inline-flex; align-items: center; font-size: 11.5px; font-weight: 700; color: #6b8a84; }
.tp-text-legend i { width: 14px; height: 14px; border-radius: 4px; margin-right: 5px; box-shadow: inset 0 0 0 1px rgba(15,42,38,.12); }
/* 右列：三行色板 + 分段变色开关 + 恢复默认 */
.tp-text-row { display: flex; flex-direction: column; gap: 7px; }
.tp-text-label { font-size: 13px; font-weight: 800; color: #16302c; }
.tp-text-wrap .bx-reset { justify-self: start; padding: 8px 22px; }
/* 文字样式区的色点：正方形（只改主题页，不动练习页面板的圆点） */
.tp-text-wrap .bx-color, .tp-cn-wrap .bx-color { border-radius: 7px; }
@media (max-width: 760px) {
  .tp-text-wrap { grid-template-columns: 1fr; }
  .tp-text-prev { grid-row: auto; }
}

/* ===== 主题页 · 中文练习样式：预览台 + 配色/动效控制 =====
   预览台 = 复制真实中文练习的外壳 + 自动循环打「请参考」。
   关键：预览容器设 container-type: inline-size，让 .flow-cn 里的 cqw 单位直接生效，
   样式零覆盖——真实页面啥样，预览台就啥样。 */
.tp-cn-wrap {
  /* 左列收窄到 460px：预览框左右不再空一大截（三个米字格 1.94em×3 + 间距 ≈ 360px 放得下），
     字不缩；省下的宽度给右列色板，色点行数更少、整块更矮 */
  display: grid; grid-template-columns: 460px 1fr; gap: 16px 26px;
  background: #fff; border: 2px solid #e4eeeb; border-radius: 16px; padding: 18px;
}
/* 预览容器本身就是 .flow-cn（div 同时挂 tp-cn-prev / flow-cn / cn-line 三个类），
   container-type 直接设在它身上 —— .flow-cn 里所有 cqw 单位按 380px 列宽缩放。
   （注意不能写成 ".tp-cn-prev .flow-cn" 后代选择器：那样匹配不到任何元素，
   container-type 不生效 → cqw 回落按视口算 → 预览台里的字大得离谱） */
.tp-cn-prev {
  /* 左列整列只放预览台（与「文字样式」完全对称）：占满右侧全部控件行，
     色板/动效一律排右列，不会掉到预览下面去 */
  grid-row: 1 / span 8; align-self: stretch;
  display: flex; flex-direction: column; gap: 12px;
  container-type: inline-size;
  container-name: tp-cn;
}
/* 预览外框：与「文字样式」预览台同款 2px 圆角框，撑满左列高度、内容上下居中 */
.tp-cn-prev .pf-item {
  flex: 1; display: flex; align-items: center; justify-content: center;
  padding: 12px 14px;
  border-width: 2px; border-radius: 14px;
}
/* 练习页为了去掉「第二层框」把 .flow-cn.cn-line .pf-item.current 的边框设为透明，
   预览台要把这圈框显示回来（选择器多加一个 .tp-cn-prev 提高优先级）：
   颜色/底色跟随「待输入框样式」 */
.tp-cn-prev.flow-cn.cn-line .pf-item.current {
  border-color: var(--box-border, #0F766E);
  background: var(--box-bg, transparent);
}
/* 预览台字号：与「文字样式」预览台完全对齐（根字号 50px、米字格 1.94em、目标字 1.37em）。
   cn 自己按容器宽算 4.9cqw（560px 列 ≈ 27px）→ 比上面那台小一大圈；
   改回固定 px：练习页满宽时本来也是 4.9cqw × 960 ≈ 47px 的观感。
   同时把米字格 2.47em 收到 1.94em，一排三个才不会顶出 560px 左列。 */
.tp-cn-prev.flow-cn .pf-units.cn-units.cn-words.cn-words-w2 { font-size: 50px; }
.tp-cn-prev.flow-cn .pf-units.cn-units .pf-mtzg { width: 1.94em; height: 1.94em; }
.tp-cn-prev.flow-cn .pf-units.cn-units .pf-mtzg-char { font-size: 1.37em; }
/* 下格打对/打错的字跟上面目标字保持同号（cn 原值 1.86em 是配 2.47em 格子的） */
.tp-cn-prev.flow-cn .pf-units.cn-units .pf-input-cell .pf-bottom-char { font-size: 1.37em; }
/* 打字中的拼音块：1:1 抄练习页 .cn-input.cn-input-inword.has-text
   —— 宽度完全按内容走（练习页 _fitCnWidth：textW + 10 的余量，打一个字长一个字）。
   关键 flex: 0 0 auto —— 这个块是输入格（固定 1.94em 宽）里的 flex 子项，
   默认 flex-shrink:1 会在内容超过格子宽时把它压回格子宽，
   于是「打进第 4 个字母就不再加宽了」。禁止收缩后才会一直往右长。 */
.tp-cn-prev .cn-sim-box {
  display: inline-flex; align-items: center; justify-content: center;
  flex: 0 0 auto; width: max-content; box-sizing: border-box;
  height: 1.35em; line-height: 1.35em;
  padding: 0 .1em 7px; border-radius: .12em;
  background: var(--pc-cn-boxbg, #0f3d36);
  color: var(--pc-cn-boxfg, #FFFFFF);
  font-size: 1em; font-weight: 800; white-space: nowrap;
}
/* 位置同练习页：窄（没占满格子）时居中在格子里；比格子宽了就贴格子左边、只往右延伸
   （safe center：装得下居中，装不下自动退回 start，否则左右都会溢出） */
.tp-cn-prev.flow-cn .pf-input-cell { justify-content: safe center; }
/* 拼音块里的白光标（同练习页 input 的 caret-color） */
.tp-cn-prev .cn-sim-caret {
  display: inline-block; flex: 0 0 auto;
  width: .045em; height: 1em; margin-left: 2px;
  background: var(--pc-cn-boxfg, #fff);
  animation: pfCursorBlink .8s steps(1) infinite;
}
.tp-cn-wrap .bx-reset { justify-self: start; padding: 8px 22px; }
@media (max-width: 760px) {
  .tp-cn-wrap { grid-template-columns: 1fr; }
  .tp-cn-prev { grid-row: auto; }
}

/* 手部样式的圆形色块 */
.skin-opt-hand { display: flex; flex-direction: column; align-items: center; padding: 12px 8px 9px; }
.skin-dot {
  width: 34px; height: 34px; border-radius: 50%;
  background: var(--c, #e8453c);
  box-shadow: inset 0 -3px 6px rgba(0,0,0,.18), 0 2px 6px rgba(0,0,0,.16);
  margin-bottom: 8px;
}

/* ===== 拼音格/米字格/待输入框 的缩略图（与键盘缩略图同构：白底 + 该色线条）=====
   颜色全部走 --c（由 JS 按选中项写入），保证「一眼能看出是什么颜色」。 */
.skin-thumb {
  height: 42px; border-radius: 8px; margin-bottom: 7px;
  background: #ffffff; border: 1px solid #e4eeeb;
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
}

/* —— 拼音格缩略：四线三格（上下两条实线 + 中间两条淡线）—— */
.skin-th-pg { position: relative; width: 90%; height: 30px; }
.skin-th-pg i { position: absolute; left: 0; right: 0; display: block; }
.skin-th-pg i:nth-child(1) { top: 0;    border-top: 1.8px solid var(--c); }
.skin-th-pg i:nth-child(2) { top: 33.3%; border-top: 1.3px solid var(--c); opacity: .5; }
.skin-th-pg i:nth-child(3) { top: 66.6%; border-top: 1.3px solid var(--c); opacity: .5; }
.skin-th-pg i:nth-child(4) { bottom: 0; border-top: 1.8px solid var(--c); }

/* —— 米字格缩略：外框 + 十字 + 两条斜线 —— */
.skin-th-mz { position: relative; width: 40px; height: 40px; border: 2px solid var(--c); border-radius: 4px; }
.skin-th-mz i { position: absolute; display: block; }
.skin-th-mz i:nth-child(1) { left: 0; right: 0; top: 50%; border-top: 1.4px solid var(--c); opacity: .55; }
.skin-th-mz i:nth-child(2) { top: 0; bottom: 0; left: 50%; border-left: 1.4px solid var(--c); opacity: .55; }
.skin-th-mz i:nth-child(3) { top: 50%; left: 50%; width: 141%; border-top: 1.4px solid var(--c); opacity: .38; transform: translate(-50%,-50%) rotate(45deg); }
.skin-th-mz i:nth-child(4) { top: 50%; left: 50%; width: 141%; border-top: 1.4px solid var(--c); opacity: .38; transform: translate(-50%,-50%) rotate(-45deg); }

/* —— 待输入框缩略：一个圆角框，边框为该色 —— */
.skin-th-box { width: 80px; height: 36px; border: 2.4px solid var(--c); border-radius: 9px; background: transparent; }

/* 拼音格/米字格/待输入框 三项：与键盘样式同为 3 列排布、同样大小 */
.skin-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.skin-grid-3 .skin-opt { padding: 9px 9px 7px; border-radius: 13px; }
.skin-grid-3 .skin-thumb { height: 50px; margin-bottom: 6px; }
.skin-grid-3 .skin-opt-name { font-size: 13px; }
.skin-grid-3 .skin-opt-tip { font-size: 10.5px; }
.tbp-skin-btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 15px; border-radius: 999px;
  font-size: 13px; font-weight: 700; color: #fff;
  background: rgba(255,255,255,.13);
  border: 1.5px solid rgba(255,255,255,.28);
  cursor: pointer; user-select: none; white-space: nowrap;
  transition: background .18s, border-color .18s;
}
.tbp-skin-btn::before {
  content: ''; width: 7px; height: 7px; border-radius: 2px;
  background: linear-gradient(135deg,#fff 50%, #0F766E 50%);
  box-shadow: 0 0 0 1px rgba(255,255,255,.5);
}
.tbp-skin-btn:hover { background: rgba(255,255,255,.24); border-color: rgba(255,255,255,.55); }

/* —— 键盘样式气泡（从按钮下方展开，带指向按钮的小箭头）—— */
.skin-pop {
  position: fixed; z-index: 300;
  transform: translateX(-50%) translateY(-8px) scale(.94);
  transform-origin: 50% 0%;
  opacity: 0;
  transition: transform .2s cubic-bezier(.2,1.3,.4,1), opacity .16s ease;
  width: 470px;
  /* 面板内容较长（键盘 + 拼音格 + 米字格 + 待输入框），限制高度并可滚动 */
  max-height: calc(calc(100 * var(--pg-vh, 1vh)) - 110px);
  overflow-y: auto;
  overscroll-behavior: contain;
  box-sizing: border-box;
  background: #ffffff; border-radius: 18px; padding: 18px 18px 14px;
  border: 1.5px solid #cde9e4;
  box-shadow: 0 20px 46px rgba(7,59,53,.28), 0 3px 10px rgba(7,59,53,.14);
}
.skin-pop.in  { transform: translateX(-50%) translateY(0) scale(1); opacity: 1; }
.skin-pop.out { transform: translateX(-50%) translateY(-6px) scale(.96); opacity: 0; }
/* 指向按钮的小箭头：left 由 JS 按按钮实际位置写入，保证箭头对准按钮 */
.skin-arrow {
  position: absolute; top: -8px; left: 50%;
  margin-left: -8px;
  width: 14px; height: 14px;
  background: #ffffff;
  border-left: 1.5px solid #cde9e4;
  border-top: 1.5px solid #cde9e4;
  transform: rotate(45deg);
  border-radius: 2px 0 0 0;
}
.skin-head {
  font-size: 14px; font-weight: 900; color: #0b5c55;
  letter-spacing: .5px; margin-bottom: 10px;
}
.skin-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 9px; }
.skin-opt {
  border: 2px solid #e4eeeb; border-radius: 12px;
  padding: 8px 8px 7px; cursor: pointer;
  transition: border-color .2s, transform .2s, box-shadow .2s, background .2s;
}
.skin-opt:hover { border-color: #9fd8ce; transform: translateY(-2px); box-shadow: 0 8px 18px rgba(11,92,85,.13); }
.skin-opt.on { border-color: #0F766E; background: #f4faf9; }

/* 缩略图：画一个迷你键盘（外框 + 三排键帽 + 空格），直观体现该皮肤配色 */
.skin-kb {
  display: flex; flex-direction: column; gap: 2px;
  padding: 4px; border-radius: 7px; margin-bottom: 7px;
}
.skin-kb-row { display: flex; gap: 2px; }
.skin-kb-row i {
  flex: 1 1 0; height: 6px; border-radius: 2px; display: block;
}
.skin-kb-space i { flex: 1 1 0; }
/* 各皮肤缩略：外框色 + 键帽描边色跟着皮肤走 */
.skin-kb-default { background: #ffffff; border: 2px solid #0F766E; }
.skin-kb-default i { background: #ffffff; border: 1px solid #0F766E; }
.skin-kb-mono { background: #ffffff; border: 2px solid #111111; }
.skin-kb-mono i { background: #ffffff; border: 1px solid #111111; }
.skin-kb-red { background: #ffffff; border: 2px solid #d93a30; }
.skin-kb-red i { background: #ffffff; border: 1px solid #d93a30; }
.skin-kb-pink { background: #ffffff; border: 2px solid #e0559b; }
.skin-kb-pink i { background: #ffffff; border: 1px solid #e0559b; }
.skin-kb-blue { background: #ffffff; border: 2px solid #2f6fe4; }
.skin-kb-blue i { background: #ffffff; border: 1px solid #2f6fe4; }
.skin-kb-orange { background: #ffffff; border: 2px solid #e8833a; }
.skin-kb-orange i { background: #ffffff; border: 1px solid #e8833a; }
.skin-kb-purple { background: #ffffff; border: 2px solid #7c5cd6; }
.skin-kb-purple i { background: #ffffff; border: 1px solid #7c5cd6; }
.skin-kb-cyan { background: #ffffff; border: 2px solid #14b8c4; }
.skin-kb-cyan i { background: #ffffff; border: 1px solid #14b8c4; }
/* 暗夜：深底浅键，缩略也要体现出来 */
.skin-kb-dark { background: #263241; border: 2px solid #3d4d61; }
.skin-kb-dark i { background: #34414f; border: 1px solid #55677e; }

.skin-opt-name { font-size: 13px; font-weight: 800; color: #0b5c55; text-align: center; }
.skin-opt-tip { font-size: 10.5px; color: #0F766E; text-align: center; margin-top: 2px; font-weight: 700; }
.skin-foot { margin-top: 14px; font-size: 11.5px; color: #94a8a4; text-align: center; }

/* ===== 练习页背景：白底 + 网格 + 柔光（指法 #kfPage / 词组 .kf-page 通用）===== */
#kfPage, .kf-page {
  background: var(--bg-base, linear-gradient(180deg, #f4faf9 0%, #ffffff 300px));
}
#kfPage::before, .kf-page::before {
  content: ''; position: absolute; left: 0; right: 0;
  /* 往上伸 62px 盖住透明顶栏那一条（否则顶部露白，见文件后的说明） */
  top: calc(-1 * var(--topbar-h, 62px)); bottom: 0;
  pointer-events: none;
  background: var(--bg-glow,
    radial-gradient(900px 560px at 12% -6%, rgba(20,179,166,.10), transparent 62%),
    radial-gradient(760px 500px at 90% -2%, rgba(56,189,248,.07), transparent 64%));
}
#kfPage::after, .kf-page::after {
  /* 背景纹理层（网格 / 点阵 / 斜纹…），由"背景样式"切换。 */
  content: ''; position: absolute; left: 0; right: 0;
  top: calc(-1 * var(--topbar-h, 62px));
  height: calc(760px + var(--topbar-h, 62px));
  pointer-events: none;
  background-image: var(--bg-tex, none);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.75) 52%, transparent 90%);
          mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.75) 52%, transparent 90%);
}
#kfPage > *, .kf-page > * { position: relative; z-index: 1; }

/* ===== 背景样式（顶栏「背景样式」按钮切换）===== */
/* ① 网格（默认）—— 已由上面的兜底值给出，这里不用重复 */
/* ② 点阵 */
/* 注：指法页容器是 #kfPage，词组等练习页容器是 .kf-page#drillPage，两边都要覆盖 */
body.bgskin-dots :is(#kfPage, .kf-page) {
  --bg-tex: radial-gradient(rgba(15,118,110,.42) 1.4px, transparent 1.5px);
  --bg-tex-size: 22px 22px;
}
/* ③ 斜纹 */
body.bgskin-diag :is(#kfPage, .kf-page) {
  --bg-tex: repeating-linear-gradient(45deg, rgba(15,118,110,.16) 0 1px, transparent 1px 12px);
  --bg-tex-size: auto;
}
/* ③b 细斜纹 */
body.bgskin-diag2 :is(#kfPage, .kf-page) {
  --bg-tex: repeating-linear-gradient(-45deg, rgba(15,118,110,.12) 0 1px, transparent 1px 7px);
  --bg-tex-size: auto;
}
/* ④ 大十字格 */
body.bgskin-cross :is(#kfPage, .kf-page) {
  --bg-tex:
    linear-gradient(rgba(15,118,110,.5) 1px, transparent 1px),
    linear-gradient(90deg, rgba(15,118,110,.5) 1px, transparent 1px);
  --bg-tex-size: 90px 90px, 90px 90px;
}
/* ④b 小方格（单层，更密） */
body.bgskin-cross2 :is(#kfPage, .kf-page) {
  --bg-tex:
    linear-gradient(rgba(15,118,110,.30) 1px, transparent 1px),
    linear-gradient(90deg, rgba(15,118,110,.30) 1px, transparent 1px);
  --bg-tex-size: 26px 26px, 26px 26px;
}
/* ⑤ 横线（练习本的感觉） */
body.bgskin-hline :is(#kfPage, .kf-page) {
  --bg-tex: repeating-linear-gradient(180deg, rgba(15,118,110,.28) 0 1px, transparent 1px 34px);
  --bg-tex-size: auto;
}
/* ⑥ 暖光（米黄底 + 暖色柔光，无纹理） */
body.bgskin-warm {
  --bg-base: linear-gradient(180deg, #fdf8f0 0%, #ffffff 320px);
  --bg-glow:
    radial-gradient(900px 560px at 12% -6%, rgba(245,158,11,.10), transparent 62%),
    radial-gradient(760px 500px at 90% -2%, rgba(232,131,58,.07), transparent 64%);
  --bg-tex: none;
}
/* ⑦ 冷光（淡蓝底 + 冷色柔光） */
body.bgskin-cool {
  --bg-base: linear-gradient(180deg, #f0f7fd 0%, #ffffff 320px);
  --bg-glow:
    radial-gradient(900px 560px at 12% -6%, rgba(56,189,248,.12), transparent 62%),
    radial-gradient(760px 500px at 90% -2%, rgba(99,102,241,.07), transparent 64%);
  --bg-tex: none;
}
/* ⑧ 纯白 */
body.bgskin-plain {
  --bg-base: #ffffff;
  --bg-glow: none;
  --bg-tex: none;
}
/* 主题背景应用到整个 body（首页 is-home 保持原样） */
body[class*='bgskin-']:not(.is-home) {
  background: var(--bg-base, linear-gradient(180deg, #e9f6f3 0%, #ffffff 320px)) no-repeat;
}
body.bgskin-mint {
  --bg-base: linear-gradient(180deg, #effaf6 0%, #ffffff 320px);
  --bg-glow:
    radial-gradient(900px 560px at 12% -6%, rgba(16,185,129,.10), transparent 62%),
    radial-gradient(760px 500px at 90% -2%, rgba(45,212,191,.08), transparent 64%);
  --bg-tex: none;
}
body.bgskin-sakura {
  --bg-base: linear-gradient(180deg, #fdf3f6 0%, #ffffff 320px);
  --bg-glow:
    radial-gradient(900px 560px at 12% -6%, rgba(244,114,182,.10), transparent 62%),
    radial-gradient(760px 500px at 90% -2%, rgba(251,146,60,.06), transparent 64%);
  --bg-tex: none;
}
body.bgskin-lavender {
  --bg-base: linear-gradient(180deg, #f6f4fd 0%, #ffffff 320px);
  --bg-glow:
    radial-gradient(900px 560px at 12% -6%, rgba(139,92,246,.10), transparent 62%),
    radial-gradient(760px 500px at 90% -2%, rgba(99,102,241,.07), transparent 64%);
  --bg-tex: none;
}

/* ===== 导航栏 ===== */
.drill-nav-bar {
  position: relative;
  width: 100%;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 99;
  flex-shrink: 0;
}
.nav-back-drill {
  position: absolute;
  left: calc(2.13 * var(--pg-vw, 1vw));
  bottom: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(0.53 * var(--pg-vw, 1vw));
  padding: 0 calc(3.2 * var(--pg-vw, 1vw)) 0 calc(2.13 * var(--pg-vw, 1vw));
  border-radius: calc(13.33 * var(--pg-vw, 1vw));
  background: linear-gradient(158deg, #ffffff 0%, #eff7f7 100%);
  border: calc(0.2 * var(--pg-vw, 1vw)) solid #0F766E;
  box-shadow: 0 calc(0.27 * var(--pg-vw, 1vw)) calc(0.8 * var(--pg-vw, 1vw)) rgba(15,118,110,.06), 0 calc(0.8 * var(--pg-vw, 1vw)) calc(1.87 * var(--pg-vw, 1vw)) rgba(15,118,110,.10);
  z-index: 100;
  cursor: pointer;
}
.nav-back-drill:active { transform: scale(0.92); }
.nav-back-drill .nbd-arrow { font-size: calc(4.8 * var(--pg-vw, 1vw)); font-weight: 700; color: #0F766E; line-height: 1; margin-top: calc(-0.53 * var(--pg-vw, 1vw)); }
.nav-back-drill .nbd-txt { font-size: calc(3.47 * var(--pg-vw, 1vw)); font-weight: 600; color: #0F766E; line-height: 1; }
.nav-title { font-size: calc(4 * var(--pg-vw, 1vw)); font-weight: 700; color: #0F766E; letter-spacing: calc(0.27 * var(--pg-vw, 1vw)); }

/* 控制栏 */
.ctl-row {
  display: flex;
  align-items: stretch;
  justify-content: space-between;
  gap: calc(1.2 * var(--pg-vw, 1vw));
  width: 100%;
  max-width: 960px;
  margin: calc(1.4 * var(--pg-vw, 1vw)) auto 0;
  padding: 0 calc(2 * var(--pg-vw, 1vw));
  box-sizing: border-box;
}
.ctl-btn {
  flex: 1;
  min-width: 0;
  height: min(58px, calc(6.1 * var(--pg-vw, 1vw)));
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: calc(0.3 * var(--pg-vw, 1vw));
  border-radius: calc(1.2 * var(--pg-vw, 1vw));
  background: linear-gradient(180deg, #FFFFFF 0%, #F0FAF9 100%);
  border: calc(0.2 * var(--pg-vw, 1vw)) solid var(--skin-main, #0F766E);   /* 跟「键盘样式」联动 */
  box-shadow: 0 2px 0 var(--skin-shadow, rgba(15,118,110,0.14)), 0 6px 16px var(--skin-shadow, rgba(15,118,110,0.10));
  color: var(--skin-main, #0F766E);
  font-size: calc(1.4 * var(--pg-vw, 1vw));
  font-weight: 700;
  box-sizing: border-box;
  cursor: pointer;
  user-select: none;
}
.ctl-btn .ctl-dot {
  width: calc(1.6 * var(--pg-vw, 1vw));
  height: calc(1.6 * var(--pg-vw, 1vw));
  border-radius: 50%;
  background: #E2E8F0;
  border: calc(0.15 * var(--pg-vw, 1vw)) solid #CBD5E1;
  box-shadow: inset 0 calc(0.15 * var(--pg-vw, 1vw)) calc(0.3 * var(--pg-vw, 1vw)) rgba(0,0,0,0.12);
}
.ctl-btn .ctl-dot.on {
  border-color: transparent;
  box-shadow: 0 0 calc(0.8 * var(--pg-vw, 1vw)) currentColor, inset 0 calc(0.1 * var(--pg-vw, 1vw)) calc(0.2 * var(--pg-vw, 1vw)) rgba(255,255,255,0.4);
}
.ctl-btn .dot-sm.on { background: #1F2937; color: #1F2937; }
.ctl-btn .dot-jm.on { background: #EC4899; color: #EC4899; }
.ctl-btn .dot-ym.on { background: #2563EB; color: #2563EB; }
.ctl-btn .dot-voice.on { background: #0F766E; color: #0F766E; }

/* ===== 拼音打字 / 看词听写 tab 切换 ===== */
.ctl-mode-tabs {
  display: flex;
  align-items: center;
  gap: calc(0.5 * var(--pg-vw, 1vw));
  padding: calc(0.3 * var(--pg-vw, 1vw));
  border-radius: calc(1.5 * var(--pg-vw, 1vw));
  background: #E6F4F2;
  border: calc(0.15 * var(--pg-vw, 1vw)) solid #C5E3DF;
  box-shadow: inset 0 1px 2px rgba(0,0,0,.04);
  flex-shrink: 0;
}
.ctl-mode-tab {
  border: none;
  background: transparent;
  padding: calc(0.4 * var(--pg-vw, 1vw)) calc(1.2 * var(--pg-vw, 1vw));
  border-radius: calc(1.1 * var(--pg-vw, 1vw));
  font-size: calc(1.3 * var(--pg-vw, 1vw));
  font-weight: 700;
  color: #0F766E;
  cursor: pointer;
  white-space: nowrap;
  transition: all .18s ease;
  user-select: none;
}
.ctl-mode-tab.active {
  background: #0F766E;
  color: #FFFFFF;
  box-shadow: 0 2px 0 rgba(15,118,110,.2), 0 4px 10px rgba(15,118,110,.25);
}

/* ===== 文字流：拼音上、汉字下 ===== */
.pinyin-flow-scroll {
  margin: calc(2 * var(--pg-vw, 1vw)) auto 0;
  max-width: 960px;
  width: 100%;
  padding: 0 calc(2 * var(--pg-vw, 1vw));
  box-sizing: border-box;
  flex-shrink: 0;
}
.pinyin-flow {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  grid-auto-rows: 1fr;
  gap: calc(2 * var(--pg-vw, 1vw));
  padding: calc(2.4 * var(--pg-vw, 1vw)) calc(2 * var(--pg-vw, 1vw));
  max-width: 960px;
  width: 100%;
  box-sizing: border-box;
  background: #FFFFFF;
  border: calc(0.2 * var(--pg-vw, 1vw)) solid var(--skin-main, #0F766E);   /* 大卡片边框跟「键盘样式」联动 */
  border-radius: calc(2.4 * var(--pg-vw, 1vw));
  box-shadow: 0 2px 10px var(--skin-shadow, rgba(15,118,110,.10)), 0 8px 24px var(--skin-shadow, rgba(15,118,110,.08));
  /* 作为单词模式的尺度容器：内容用 cqw（按本框宽度百分比，等同小程序 rpx 原理），
     框封顶(max-width:960)时字和格一起封顶，框变窄时整体一起缩，框与字永不错位 */
  container-type: inline-size;
}
.pf-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: calc(1 * var(--pg-vw, 1vw));
  padding: calc(2.4 * var(--pg-vw, 1vw)) calc(1 * var(--pg-vw, 1vw));
  box-sizing: border-box;
  flex-shrink: 0;
  border-radius: 8px;
  width: 100%;
  border: calc(0.2 * var(--pg-vw, 1vw)) solid transparent;
}
.pf-units {
  display: flex;
  gap: calc(4 * var(--pg-vw, 1vw));
  justify-content: center;
  align-items: flex-start;
  position: relative;
  font-size: calc(6.2 * var(--pg-vw, 1vw));
}
.pf-units::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1.8em;
  z-index: 2; /* 四线格线压在高亮块(1)上面、字母(3)下面 */
  pointer-events: none;
  border-top: 0.032em solid var(--py-line, rgba(15,118,110,.5));
  border-bottom: 0.032em solid var(--py-line, rgba(15,118,110,.5));
  background-image:
    linear-gradient(var(--py-line-soft, rgba(15,118,110,.28)), var(--py-line-soft, rgba(15,118,110,.28))),
    linear-gradient(var(--py-line-soft, rgba(15,118,110,.28)), var(--py-line-soft, rgba(15,118,110,.28)));
  background-size: 100% 0.022em;
  background-repeat: no-repeat;
  background-position: 0 33.33%, 0 66.67%;
}
.pf-unit {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(2.6 * var(--pg-vw, 1vw));
}
.pf-pinyin {
  display: flex;
  align-items: stretch;
  justify-content: center;
  gap: calc(0.1 * var(--pg-vw, 1vw));
  height: 1.8em;
  overflow: hidden;
  position: relative;
  /* 注意：这里不能设 z-index（会创建层叠上下文，导致内部无法按 高亮块<线<字母 分层） */
}
/* 层序：高亮块(1) < 四线格线(2) < 字母/光标(3)，保证高亮块压在拼音线下面 */
.pf-pchar {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 0;
  transition: background .15s ease;
}
.pf-pchar-text { font-size: inherit; font-weight: 600; color: var(--pc-py-pending, #94A3B8); line-height: 1.1; transition: color .15s ease; position: relative; top: -0.0998em; z-index: 3; }
.pf-err-shake {
  color: #EF4444 !important;
  animation: pfErrShake 0.25s ease;
}
@keyframes pfErrShake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(calc(-0.4 * var(--pg-vw, 1vw))); }
  50% { transform: translateX(calc(0.4 * var(--pg-vw, 1vw))); }
  75% { transform: translateX(calc(-0.4 * var(--pg-vw, 1vw))); }
}
.pf-pt-sm { color: #1F2937 !important; }
.pf-pt-jm { color: #EC4899 !important; }
.pf-pt-ym { color: #2563EB !important; }
/* 待输入/已输入/高亮块三色由「文字样式」面板控制（--pc-py-pending / --pc-py-typed / --pc-py-hit） */
/* 高亮块画在 ::before 层（z-index 0）→ 在四线格线(2)下面；
   字母 .pf-pchar-text 保持 z-index 3 → 仍然浮在四线格线上面（不能跟高亮块绑在同一层） */
.pf-item.current .pf-pchar-hit, .pf-item.done .pf-pchar-hit { background: transparent; }
.pf-item.current .pf-pchar-hit::before, .pf-item.done .pf-pchar-hit::before {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--pc-py-hit, #E6F4F1);
  z-index: 0;
}
.pf-item.current .pf-pchar-hit .pf-pchar-text { color: var(--pc-py-typed, #0C615B) !important; }
.pf-item.current .pf-pchar-text { color: var(--pc-py-pending, #94A3B8); }
.pf-item.done .pf-pchar-text { color: var(--pc-py-typed, #0C615B); }
.pf-char-cursor {
  position: absolute;
  top: calc(100% - 0.19em);
  left: 50%;
  transform: translateX(-50%);
  width: 0.5em;
  height: 0.08em;
  border-radius: 0.04em;
  background: #EF4444;
  animation: pfCursorBlink 1s step-end infinite;
  z-index: 3;
}
@keyframes pfCursorBlink {
  0%, 49% { opacity: 1; }
  50%, 100% { opacity: 0; }
}
/* 汉字米字格 */
.pf-mtzg {
  position: relative;
  width: calc(12 * var(--pg-vw, 1vw));
  height: calc(12 * var(--pg-vw, 1vw));
  border: 0.05em solid var(--mtzg-border, #e05a5a);   /* 米字格框色：随「拼音格样式」变 */
  border-radius: calc(0.2 * var(--pg-vw, 1vw));
  background: #fffdfd;
  box-sizing: border-box;
  overflow: hidden;
  flex-shrink: 0;
}
.pf-mtzg-h { position: absolute; left: 0; right: 0; top: 50%; border-top: 0.032em solid var(--mtzg-inner, #eeb2b2); }
.pf-mtzg-v { position: absolute; top: 0; bottom: 0; left: 50%; border-left: 0.032em solid var(--mtzg-inner, #eeb2b2); }
.pf-mtzg-d1 { position: absolute; top: 50%; left: 50%; width: 200%; border-top: 0.032em solid var(--mtzg-diag, #f1c2c2); transform: translate(-50%,-50%) rotate(45deg); }
.pf-mtzg-d2 { position: absolute; top: 50%; left: 50%; width: 200%; border-top: 0.032em solid var(--mtzg-diag, #f1c2c2); transform: translate(-50%,-50%) rotate(-45deg); }
.pf-mtzg-char { position: absolute; top: 50%; left: 50%; transform: translate(-50%,-50%); font-size: calc(8.5 * var(--pg-vw, 1vw)); font-weight: 800; color: #334155; line-height: 1; }
.pf-item.done .pf-mtzg-char { color: #334155; }
.pf-mtzg-char-done { color: #334155 !important; }
/* 当前正在输入的那个词外面的框：颜色/底色由「待输入框样式」决定 */
.pf-item.current { border-color: var(--box-border, #0F766E); background: var(--box-bg, transparent); }

.drill-loading { padding: calc(10.67 * var(--pg-vw, 1vw)) 0; text-align: center; color: #0F766E; font-size: calc(3.47 * var(--pg-vw, 1vw)); font-weight: 700; }

/* ===== 看词听写：米字格下方的音节类型小提示（双拼/三拼/整体认读/零声母）=====
   em 挂在 .pf-units 的字号上，sz-s/sz-m 各档会自己等比缩放，不用逐档写死 */
.pf-syl-hint {
  margin-top: calc(-1.4 * var(--pg-vw, 1vw));             /* .pf-unit gap 2.6vw，往回收一点，贴住米字格 */
  font-size: .24em;
  line-height: 1;
  font-weight: 800;
  letter-spacing: .03em;
  white-space: nowrap;
  padding: .34em .72em;
  border-radius: 999px;
}
.pf-syl-hint.hint-shuang { color: #0F766E; background: rgba(15,118,110,.10); }
.pf-syl-hint.hint-san    { color: #B45309; background: rgba(217,119,6,.13); }
.pf-syl-hint.hint-ztd    { color: #6D28D9; background: rgba(124,58,237,.10); }
.pf-syl-hint.hint-zero   { color: #475569; background: rgba(100,116,139,.13); }

/* 看词听写：整词完成的小礼花画布（铺满视口、不吃点击） */
.dict-fx-canvas {
  position: fixed; inset: 0;
  width: calc(100 * var(--pg-vw, 1vw)); height: calc(100 * var(--pg-vh, 1vh));
  pointer-events: none;
  z-index: 200;
}

/* 看词听写：整词打对，拼音+米字格同步轻弹两下（380ms 内切词前播完；不变色不加光）*/
@keyframes pfOkPop {
  0%        { transform: scale(1); }
  18%       { transform: scale(1.06); }
  36%       { transform: scale(1); }
  58%       { transform: scale(1.06); }
  80%,100%  { transform: scale(1); }
}
.pf-word-ok { animation: pfOkPop .38s ease; transform-origin: center center; }

/* ===== 键盘 ===== */
/* 基础 keyboard-wrapper / kb-main：各页面共用，不写死，不破坏指法练习 */
.keyboard-wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: calc(1 * var(--pg-vw, 1vw));
  align-self: center;
  width: 100%;
  max-width: 960px;
  margin: calc(2 * var(--pg-vw, 1vw)) auto 0;
  padding: 0 calc(2 * var(--pg-vw, 1vw));
  box-sizing: border-box;
  overflow: visible;
  position: relative;
}
.kb-main {
  width: 100%;
  max-width: 960px;
  display: flex;
  flex-direction: column;
  gap: calc(1 * var(--pg-vw, 1vw));
}

/* ===== 以下全是看词听写 (.kf-page) 独有的样式，绝不影响指法练习 ===== */

/* 看词听写：不被 flex:1 撑满，高度 shrink-to-fit 键盘！
   这样图例 top:100% 才会等于键盘底部！ */
.kf-page .keyboard-wrapper {
  flex: 0 0 auto;
  position: relative;
  width: 932px;
  max-width: 932px;
  margin-top: calc(4 * var(--pg-vw, 1vw));
  padding: 0;
}
.kf-page .kb-main {
  max-width: 932px;
}
/* 卡片与键盘之间的实线分隔（看词听写 + 指法练习共用） */
.keyboard-wrapper::before {
  content: '';
  position: absolute;
  top: calc(-2 * var(--pg-vw, 1vw));
  left: calc(4 * var(--pg-vw, 1vw));
  right: calc(4 * var(--pg-vw, 1vw));
  height: 2px;
  background: rgba(90,120,115,.5);
  -webkit-mask-image: linear-gradient(
    90deg,
    transparent 0%, rgba(0,0,0,.3) 10%, rgba(0,0,0,1) 40%, rgba(0,0,0,1) 60%, rgba(0,0,0,.3) 90%, transparent 100%
  );
  mask-image: linear-gradient(
    90deg,
    transparent 0%, rgba(0,0,0,.3) 10%, rgba(0,0,0,1) 40%, rgba(0,0,0,1) 60%, rgba(0,0,0,.3) 90%, transparent 100%
  );
  pointer-events: none;
}

/* 侧面板（absolute 定位，贴 keyboard-wrapper 右边）—— 看词听写 + 指法练习共用 */
.kf-side-panel {
  position: absolute;
  top: 0;
  left: 100%;
  margin-left: calc(1.6 * var(--pg-vw, 1vw));
  height: calc(29 * var(--pg-vw, 1vw));
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  pointer-events: none;
  min-width: calc(6.5 * var(--pg-vw, 1vw));
}
.kf-sp-item { text-align: left; box-sizing: border-box; }
.kf-sp-label {
  font-size: calc(1.05 * var(--pg-vw, 1vw));
  font-weight: 700;
  color: #7b918c;
  letter-spacing: .5px;
  line-height: 1.2;
  white-space: nowrap;
}
.kf-sp-num {
  margin-top: calc(0.2 * var(--pg-vw, 1vw));
  font-size: calc(2.2 * var(--pg-vw, 1vw));
  font-weight: 900;
  color: #16302c;
  line-height: 1.1;
}
.kf-sp-num.kf-sp-ok { color: #0F766E; }
.kf-sp-num.kf-sp-err { color: #EF4444; }
/* 练习页：hand-placeholder 裁侧边+底部
   inset(-9999px -14px 24px -14px)
   top=-9999 → 顶部放开（手覆盖键盘部分保留）
   right=-14px → 右扩14px，匹配 kw 右边界
   bottom=24px → 底部上移24px，两只手视觉底对齐
   left=-14px → 左扩14px，匹配 kw 左边界
   kw 不动 → 阴影完整！不动手位置！不动键盘！ */
.practice-mode .hand-placeholder {
  clip-path: inset(-9999px -14px 24px -14px);
}
/* ===== 键盘皮肤变量：换肤只改这些颜色值，尺寸一律不动 → 手部绝不偏位 ===== */
.keyboard-area {
  /* 默认：主题绿 */
  --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #eff7f7 100%);
  --kb-frame-border: #0F766E;
  --kb-frame-shadow: inset 0 1px 0 rgba(255,255,255,0.95), 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(15,118,110,0.05), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(15,118,110,0.10);
  --kb-key-bg: #FFFFFF;
  --kb-key-border: #0F766E;
  --kb-key-text: #1F2937;
  --kb-key-sub: #9CA3AF;
  --kb-func-bg: #F0FAF9;
  --kb-func-border: #0F766E;
  --kb-func-text: #0F766E;

  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: calc(0.4 * var(--pg-vw, 1vw));
  padding: calc(1.9 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw));
  flex-shrink: 0;
  width: 100%;
  max-width: 960px;
  margin: 0 auto;
  box-sizing: border-box;
  border-radius: calc(2.08 * var(--pg-vw, 1vw));
  background: var(--kb-frame-bg);
  border: calc(0.2 * var(--pg-vw, 1vw)) solid var(--kb-frame-border);
  box-shadow: var(--kb-frame-shadow);
}
/* ===== 方块样式：控制方块**三种状态**的底色（边框一律黑色）=====
   --bx-border 统一黑；--bx-active 待输入底色；--bx-done 已输入底色。 */
body.bxskin-default { --bx-border: #16302c; --bx-text: #0b5c55; --bx-active: #0b423c; --bx-done: #14b3a6; --bx-active-text: #fff; --bx-active-shadow: rgba(11,66,60,.55); }
body.bxskin-teal    { --bx-border: #16302c; --bx-text: #0b5c55; --bx-active: #0F766E; --bx-done: #2dd4bf; --bx-active-text: #fff; --bx-active-shadow: rgba(15,118,110,.5); }
body.bxskin-dark    { --bx-border: #16302c; --bx-text: #0b5c55; --bx-active: #2b3746; --bx-done: #4a5d70; --bx-active-text: #fff; --bx-active-shadow: rgba(0,0,0,.4); }
body.bxskin-orange  { --bx-border: #16302c; --bx-text: #0b5c55; --bx-active: #e8833a; --bx-done: #f0a76a; --bx-active-text: #fff; --bx-active-shadow: rgba(232,131,58,.5); }
body.bxskin-blue    { --bx-border: #16302c; --bx-text: #0b5c55; --bx-active: #2f6fe4; --bx-done: #6f9df0; --bx-active-text: #fff; --bx-active-shadow: rgba(47,111,228,.5); }
body.bxskin-purple  { --bx-border: #16302c; --bx-text: #0b5c55; --bx-active: #7c5cd6; --bx-done: #a68ce8; --bx-active-text: #fff; --bx-active-shadow: rgba(124,92,214,.5); }

/* ===== 各键盘皮肤对外输出的主色 =====
   只给「方块区大卡片」的边框/投影用，别的元素不读它。 */
body.kbskin-default { --skin-main: #0F766E; --skin-shadow: rgba(15,118,110,.18); }
body.kbskin-mono    { --skin-main: #111111; --skin-shadow: rgba(0,0,0,.18); }
body.kbskin-red     { --skin-main: #d93a30; --skin-shadow: rgba(217,58,48,.18); }
body.kbskin-pink    { --skin-main: #e0559b; --skin-shadow: rgba(224,85,155,.18); }
body.kbskin-blue    { --skin-main: #2f6fe4; --skin-shadow: rgba(47,111,228,.18); }
body.kbskin-orange  { --skin-main: #e8833a; --skin-shadow: rgba(232,131,58,.18); }
body.kbskin-purple  { --skin-main: #7c5cd6; --skin-shadow: rgba(124,92,214,.18); }
body.kbskin-cyan    { --skin-main: #14b8c4; --skin-shadow: rgba(20,184,196,.18); }
body.kbskin-dark    { --skin-main: #55677e; --skin-shadow: rgba(0,0,0,.28); }

/* ===== 拼音格样式（顶栏「键盘样式」面板底部选择）=====
   只管四线三格的线色（米字格已拆出，见下面「米字格样式」）*/
body.pyskin-default { --py-line: rgba(15,118,110,.5); --py-line-soft: rgba(15,118,110,.28); }
body.pyskin-mono    { --py-line: rgba(17,17,17,.55);  --py-line-soft: rgba(17,17,17,.30); }
body.pyskin-red     { --py-line: rgba(217,58,48,.55); --py-line-soft: rgba(217,58,48,.28); }
body.pyskin-pink    { --py-line: rgba(224,85,155,.55);--py-line-soft: rgba(224,85,155,.28); }
body.pyskin-blue    { --py-line: rgba(47,111,228,.55);--py-line-soft: rgba(47,111,228,.28); }
body.pyskin-orange  { --py-line: rgba(232,131,58,.55);--py-line-soft: rgba(232,131,58,.28); }
body.pyskin-purple  { --py-line: rgba(124,92,214,.55);--py-line-soft: rgba(124,92,214,.28); }
body.pyskin-cyan    { --py-line: rgba(20,184,196,.55);--py-line-soft: rgba(20,184,196,.28); }

/* ===== 米字格样式（汉字格：外框 + 十字内衬线 + 斜线）===== */
body.mzskin-default { --mtzg-border: #e05a5a; --mtzg-inner: #eeb2b2;              --mtzg-diag: #f1c2c2; }
body.mzskin-mono    { --mtzg-border: #111111; --mtzg-inner: rgba(17,17,17,.35);   --mtzg-diag: rgba(17,17,17,.28); }
body.mzskin-red     { --mtzg-border: #d93a30; --mtzg-inner: rgba(217,58,48,.35);  --mtzg-diag: rgba(217,58,48,.28); }
body.mzskin-pink    { --mtzg-border: #e0559b; --mtzg-inner: rgba(224,85,155,.35); --mtzg-diag: rgba(224,85,155,.28); }
body.mzskin-blue    { --mtzg-border: #2f6fe4; --mtzg-inner: rgba(47,111,228,.35); --mtzg-diag: rgba(47,111,228,.28); }
body.mzskin-orange  { --mtzg-border: #e8833a; --mtzg-inner: rgba(232,131,58,.35); --mtzg-diag: rgba(232,131,58,.28); }
body.mzskin-purple  { --mtzg-border: #7c5cd6; --mtzg-inner: rgba(124,92,214,.35); --mtzg-diag: rgba(124,92,214,.28); }
body.mzskin-cyan    { --mtzg-border: #14b8c4; --mtzg-inner: rgba(20,184,196,.35); --mtzg-diag: rgba(20,184,196,.28); }

/* ===== 待输入框样式（当前正在输入的那个词外面的框）=====
   只改边框颜色，框内不加任何底色（--box-bg 恒为透明）*/
body.boxskin-default { --box-border: #0F766E; --box-bg: transparent; }
body.boxskin-mono    { --box-border: #111111; --box-bg: transparent; }
body.boxskin-red     { --box-border: #d93a30; --box-bg: transparent; }
body.boxskin-pink    { --box-border: #e0559b; --box-bg: transparent; }
body.boxskin-blue    { --box-border: #2f6fe4; --box-bg: transparent; }
body.boxskin-orange  { --box-border: #e8833a; --box-bg: transparent; }
body.boxskin-purple  { --box-border: #7c5cd6; --box-bg: transparent; }
body.boxskin-cyan    { --box-border: #14b8c4; --box-bg: transparent; }

/* —— 皮肤②：纯黑白线条（黑框白键黑字）—— */
body.kbskin-mono .keyboard-area {
  --kb-frame-bg: #ffffff;
  --kb-frame-border: #111111;
  --kb-frame-shadow: inset 0 1px 0 #ffffff, 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(0,0,0,0.06), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(0,0,0,0.10);
  --kb-key-bg: #FFFFFF;
  --kb-key-border: #111111;
  --kb-key-text: #111111;
  --kb-key-sub: #6b6b6b;
  --kb-func-bg: #F5F5F5;
  --kb-func-border: #111111;
  --kb-func-text: #111111;
}
/* —— 皮肤③：红 —— */
body.kbskin-red .keyboard-area {
  --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #fff1f0 100%);
  --kb-frame-border: #d93a30;
  --kb-frame-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(217,58,48,0.06), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(217,58,48,0.12);
  --kb-key-bg: #FFFFFF;
  --kb-key-border: #d93a30;
  --kb-key-text: #b3271f;
  --kb-key-sub: #c97a74;
  --kb-func-bg: #fff1f0;
  --kb-func-border: #d93a30;
  --kb-func-text: #b3271f;
}
/* —— 皮肤④：粉 —— */
body.kbskin-pink .keyboard-area {
  --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #fdf0f7 100%);
  --kb-frame-border: #e0559b;
  --kb-frame-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(224,85,155,0.06), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(224,85,155,0.12);
  --kb-key-bg: #FFFFFF;
  --kb-key-border: #e0559b;
  --kb-key-text: #b83c7c;
  --kb-key-sub: #cf90b4;
  --kb-func-bg: #fdf0f7;
  --kb-func-border: #e0559b;
  --kb-func-text: #b83c7c;
}
/* —— 皮肤⑤：蓝 —— */
body.kbskin-blue .keyboard-area {
  --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #f0f6ff 100%);
  --kb-frame-border: #2f6fe4;
  --kb-frame-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(47,111,228,0.06), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(47,111,228,0.12);
  --kb-key-bg: #FFFFFF;
  --kb-key-border: #2f6fe4;
  --kb-key-text: #1f55b8;
  --kb-key-sub: #7f9fd6;
  --kb-func-bg: #f0f6ff;
  --kb-func-border: #2f6fe4;
  --kb-func-text: #1f55b8;
}
/* —— 皮肤⑥：橙 —— */
body.kbskin-orange .keyboard-area {
  --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #fff5eb 100%);
  --kb-frame-border: #e8833a;
  --kb-frame-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(232,131,58,0.06), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(232,131,58,0.12);
  --kb-key-bg: #FFFFFF;
  --kb-key-border: #e8833a;
  --kb-key-text: #bd6420;
  --kb-key-sub: #d7a274;
  --kb-func-bg: #fff5eb;
  --kb-func-border: #e8833a;
  --kb-func-text: #bd6420;
}
/* —— 皮肤⑦：紫 —— */
body.kbskin-purple .keyboard-area {
  --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #f7f3ff 100%);
  --kb-frame-border: #7c5cd6;
  --kb-frame-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(124,92,214,0.06), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(124,92,214,0.12);
  --kb-key-bg: #FFFFFF;
  --kb-key-border: #7c5cd6;
  --kb-key-text: #5f43ab;
  --kb-key-sub: #a294cc;
  --kb-func-bg: #f7f3ff;
  --kb-func-border: #7c5cd6;
  --kb-func-text: #5f43ab;
}
/* —— 皮肤⑧：青色（薄荷）—— */
body.kbskin-cyan .keyboard-area {
  --kb-frame-bg: linear-gradient(158deg, #ffffff 0%, #ecfbfb 100%);
  --kb-frame-border: #14b8c4;
  --kb-frame-shadow: inset 0 1px 0 rgba(255,255,255,.95), 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(20,184,196,0.06), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(20,184,196,0.12);
  --kb-key-bg: #FFFFFF;
  --kb-key-border: #14b8c4;
  --kb-key-text: #0e8a94;
  --kb-key-sub: #7ccbd1;
  --kb-func-bg: #ecfbfb;
  --kb-func-border: #14b8c4;
  --kb-func-text: #0e8a94;
}
/* —— 皮肤⑨：暗夜（深底浅键，唯一深色配色）—— */
body.kbskin-dark .keyboard-area {
  --kb-frame-bg: linear-gradient(158deg, #263241 0%, #1b2530 100%);
  --kb-frame-border: #3d4d61;
  --kb-frame-shadow: inset 0 1px 0 rgba(255,255,255,.10), 0 calc(0.5 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)) rgba(0,0,0,0.25), 0 calc(3 * var(--pg-vw, 1vw)) calc(6 * var(--pg-vw, 1vw)) rgba(0,0,0,0.35);
  --kb-key-bg: #34414f;
  --kb-key-border: #55677e;
  --kb-key-text: #e8eef5;
  --kb-key-sub: #93a3b7;
  --kb-func-bg: #2b3746;
  --kb-func-border: #55677e;
  --kb-func-text: #b9c7d6;
}
.kb-row { display: flex; gap: calc(0.4 * var(--pg-vw, 1vw)); width: 100%; padding: 0; box-sizing: border-box; }
.kb-key {
  flex: 1;
  min-width: 0;
  height: min(58px, calc(6.1 * var(--pg-vw, 1vw)));
  border-radius: calc(0.73 * var(--pg-vw, 1vw));
  background: var(--kb-key-bg);
  border: calc(0.2 * var(--pg-vw, 1vw)) solid var(--kb-key-border);
  box-shadow: none;
  transition: all 0.08s ease;
  box-sizing: border-box;
  position: relative;
}
.kb-key-inner {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
}
.kb-key-main { font-size: calc(1.9 * var(--pg-vw, 1vw)); font-weight: 800; line-height: 1; color: var(--kb-key-text); }
.kb-key-sub { font-size: calc(1 * var(--pg-vw, 1vw)); font-weight: 600; line-height: 1; color: var(--kb-key-sub); }
.kb-func { background: var(--kb-func-bg); border-color: var(--kb-func-border); box-shadow: none; }
.kb-func-text { font-size: calc(1 * var(--pg-vw, 1vw)); font-weight: 700; color: var(--kb-func-text); line-height: 1; }
.kb-key.w12 { flex: 1.25; }
.kb-key.w15 { flex: 1.5; }
.kb-key.w17 { flex: 1.75; }
.kb-key.w20 { flex: 2; }
.kb-key.w22 { flex: 2.25; }
.kb-key.w28 { flex: 2.75; }
.kb-key.w62 { flex: 6.25; }
.kb-bump-dot {
  position: absolute;
  bottom: calc(0.4 * var(--pg-vw, 1vw));
  left: 50%;
  transform: translateX(-50%);
  width: calc(1.7 * var(--pg-vw, 1vw));
  height: 2px;
  border-radius: calc(0.1 * var(--pg-vw, 1vw));
  background: rgba(15,118,110,.55);
}
/* 键盘上"当前要按的键"：深墨绿键身（与上方方块一致）+ 红色光晕 */
/* 键盘上"当前要按的键"：底色跟着"待输入颜色"走，与上方方块保持一致 */
.kb-target {
  background: var(--bx-active, #0b423c) !important;
  border-color: var(--bx-border, #16302c) !important;
  box-shadow: 0 0 12px var(--ripple-color, rgba(232, 69, 60, .5)) !important;
  z-index: 2;
}
.kb-target .kb-key-main { color: var(--bx-active-text, #FFFFFF) !important; font-size: calc(1.9 * var(--pg-vw, 1vw)); }
.kb-target .kb-key-sub { color: var(--bx-active-text, rgba(255,255,255,0.85)) !important; font-size: calc(1 * var(--pg-vw, 1vw)); }
/* 光晕涟漪：颜色跟着手部样式走（--ripple-color，默认红） */
.kb-target::before,
.kb-target::after {
  content: '';
  position: absolute; inset: -2px;
  border-radius: inherit;
  border: 2px solid var(--ripple-color, rgba(232, 69, 60, .9));
  pointer-events: none;
  animation: seqRipple 2s ease-out infinite;
}
.kb-target::after { animation-delay: 1s; }
.kb-wrong {
  background: linear-gradient(180deg, #EF4444 0%, #DC2626 100%) !important;
  border-color: #B91C1C !important;
  box-shadow: 0 0 calc(2.5 * var(--pg-vw, 1vw)) rgba(239,68,68,0.35) !important;
  animation: shakeKey 0.25s ease;
}
.kb-wrong .kb-key-main { color: #FFFFFF !important; font-size: calc(1.9 * var(--pg-vw, 1vw)); }
.kb-wrong .kb-key-sub { color: rgba(255,255,255,0.85) !important; font-size: calc(1 * var(--pg-vw, 1vw)); }
@keyframes shakeKey {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(calc(-0.5 * var(--pg-vw, 1vw))); }
  50% { transform: translateX(calc(0.5 * var(--pg-vw, 1vw))); }
  75% { transform: translateX(calc(-0.5 * var(--pg-vw, 1vw))); }
}

/* ===== 手部覆盖区（键盘手图）===== */
.hand-placeholder {
  position: relative;
  width: 100%;
  height: calc(30 * var(--pg-vw, 1vw));
  margin: calc(-1 * var(--pg-vw, 1vw)) auto 0;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  /* overflow 改成 visible：让手图往上覆盖键盘时不被裁掉
     只这一个改动，其他所有值跟你之前调好的一样 */
  overflow: visible;
  z-index: 50;
}

/* ===== 热力图图例（关闭手图教学时显示） ===== */
.heat-legend {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  margin-top: calc(0.6 * var(--pg-vw, 1vw));
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  gap: calc(2.8 * var(--pg-vw, 1vw));
  padding: calc(0.6 * var(--pg-vw, 1vw)) 0;
}
.hl-item {
  display: flex;
  align-items: center;
  gap: calc(0.7 * var(--pg-vw, 1vw));
}
.hl-dot {
  width: calc(1.8 * var(--pg-vw, 1vw));
  height: calc(1.8 * var(--pg-vw, 1vw));
  border-radius: calc(0.5 * var(--pg-vw, 1vw));
  flex-shrink: 0;
}
.hl-dot.hl-green { background: rgba(15,118,110,.75); }
.hl-dot.hl-yellow { background: rgba(234,179,8,.75); }
.hl-dot.hl-red { background: rgba(239,68,68,.75); }
.hl-text {
  font-size: calc(1.35 * var(--pg-vw, 1vw));
  font-weight: 700;
  color: #5a7069;
  letter-spacing: .3px;
}
.hand-clipper {
  position: absolute;
  left: var(--hand-left, 42%);
  bottom: var(--hand-bottom, 60px);
  transform: translateX(-50%) scale(var(--hand-scale, 1.9));
  width: 600px;
  height: 318px;
  pointer-events: none;
  z-index: 999;
}
/* 左右手不再单独 override，用 JS 注入的 CSS 变量控制 */
/* SVG 手图：保持比例填满容器 */
.hand-clipper svg.hand-image,
.hand-clipper-left,
.hand-clipper-right {
  width: 100%;
  height: 100%;
  display: block;
}
/* 手图（磨砂方案已回退，改回最简形态） */
/* 手图：
   灰线要加深，但红色指示线要保持鲜亮。
   纯 brightness/contrast 是全图生效的，会把红色一起压暗（变黑红），
   所以改用「加饱和度」——灰色饱和度为 0，saturate 对它几乎无效，
   但对本来是红色的线条会把它推得更艳更亮。 */
.hand-clipper .hand-image {
  width: 100%;
  height: 100%;
  opacity: 1;
  /* 基础滤镜：加深灰线 + 让红线更艳。
     换颜色时在这些之后追加 hue-rotate —— 灰线无饱和度，不受色相旋转影响，
     所以无论选哪个颜色，手的灰线深浅都保持一致。 */
  --hand-filter: contrast(1.55) saturate(1.6) brightness(1.02);
  filter: var(--hand-filter);
  transition: filter .2s ease;
}
/* 涟漪光晕颜色：与手部颜色联动（--ripple-color 定在主容器上） */
body                                 { --ripple-color: rgba(232, 69, 60, .9); }   /* 红 */
body.handskin-pink   { --ripple-color: rgba(255, 95, 168, .9); }
body.handskin-rose   { --ripple-color: rgba(225, 29, 116, .9); }
body.handskin-orange { --ripple-color: rgba(245, 158, 11, .9); }
body.handskin-amber  { --ripple-color: rgba(217, 119, 6, .9); }
body.handskin-yellow { --ripple-color: rgba(234, 179, 8, .9); }
body.handskin-green  { --ripple-color: rgba(22, 163, 74, .9); }
body.handskin-teal   { --ripple-color: rgba(13, 148, 136, .9); }
body.handskin-cyan   { --ripple-color: rgba(20, 184, 196, .9); }
body.handskin-blue   { --ripple-color: rgba(47, 111, 228, .9); }
body.handskin-indigo { --ripple-color: rgba(79, 70, 229, .9); }
body.handskin-purple { --ripple-color: rgba(139, 92, 246, .9); }

/* 各手部颜色：在基础滤镜后追加色相旋转（角度按纯红 → 目标色计算） */
body.handskin-pink   .hand-image { filter: var(--hand-filter) hue-rotate(333deg) saturate(.88) brightness(1.08); }
body.handskin-rose   .hand-image { filter: var(--hand-filter) hue-rotate(333deg) saturate(1.25); }
body.handskin-orange .hand-image { filter: var(--hand-filter) hue-rotate(38deg); }
body.handskin-amber  .hand-image { filter: var(--hand-filter) hue-rotate(32deg) saturate(.95); }
body.handskin-yellow .hand-image { filter: var(--hand-filter) hue-rotate(45deg) saturate(.9) brightness(1.1); }
body.handskin-green  .hand-image { filter: var(--hand-filter) hue-rotate(142deg) saturate(1.2); }
body.handskin-teal   .hand-image { filter: var(--hand-filter) hue-rotate(175deg) saturate(.9); }
body.handskin-cyan   .hand-image { filter: var(--hand-filter) hue-rotate(184deg); }
body.handskin-blue   .hand-image { filter: var(--hand-filter) hue-rotate(219deg); }
body.handskin-indigo .hand-image { filter: var(--hand-filter) hue-rotate(243deg) saturate(1.1); }
body.handskin-purple .hand-image { filter: var(--hand-filter) hue-rotate(258deg); }
.hand-clipper-right { }
.hand-clipper-left { }

/* ===== 底部操作行 ===== */
.skip-bar { display: flex; gap: calc(1.2 * var(--pg-vw, 1vw)); width: 100%; max-width: 960px; margin: calc(1.2 * var(--pg-vw, 1vw)) auto 0; padding: 0 calc(2 * var(--pg-vw, 1vw)); box-sizing: border-box; }
.sb-skip-btn {
  flex: 1;
  min-width: 0;
  height: min(58px, calc(6.1 * var(--pg-vw, 1vw)));
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: calc(1.2 * var(--pg-vw, 1vw));
  background: linear-gradient(135deg, #14B8A6, #0F766E);
  color: #FFFFFF;
  font-size: calc(1.4 * var(--pg-vw, 1vw));
  font-weight: 700;
  box-shadow: 0 2px 0 rgba(15,118,110,0.18), 0 6px 16px rgba(15,118,110,0.14);
  box-sizing: border-box;
  cursor: pointer;
  user-select: none;
}
.sb-skip-btn.sb-end { background: linear-gradient(135deg, #EF4444, #DC2626); }
.sb-skip-btn:active { transform: scale(0.97); }
.sb-stat {
  flex: 2;
  min-width: 0;
  height: min(58px, calc(6.1 * var(--pg-vw, 1vw)));
  display: flex;
  align-items: center;
  justify-content: center;
  gap: calc(1 * var(--pg-vw, 1vw));
  border-radius: calc(1.2 * var(--pg-vw, 1vw));
  background: #FFFFFF;
  border: calc(0.2 * var(--pg-vw, 1vw)) solid var(--skin-main, #0F766E);      /* 对/错计数框：跟键盘样式联动 */
  box-shadow: 0 2px 8px var(--skin-shadow, rgba(15,118,110,.12));
  box-sizing: border-box;
  font-size: calc(1.6 * var(--pg-vw, 1vw));
  font-weight: 800;
}
.sb-stat-correct { color: #0F766E; }
.sb-stat-errors { color: #EF4444; }
.sb-stat-divider { color: #CBD5E1; font-weight: 300; }

/* ===== ≥450px（PC 浏览器常态）===== */
@media (min-width: 450px) {
  .drill-nav-bar { padding-top: 10px !important; padding-bottom: 10px !important; height: auto !important; }
  .nav-back-drill { left: 10px; padding: 0 12px 0 8px; border-radius: 50px; gap: 2px; border-width: 2px; }
  .nav-back-drill .nbd-arrow { font-size: 18px; margin-top: -2px; }
  .nav-back-drill .nbd-txt { font-size: 13px; }
  .nav-title { font-size: 15px; letter-spacing: 2px; }

  .pinyin-flow-scroll { margin: calc(1.6 * var(--pg-vw, 1vw)) auto 0; padding: 0 calc(2 * var(--pg-vw, 1vw)); }
  .pinyin-flow { gap: calc(2 * var(--pg-vw, 1vw)); padding: calc(2.4 * var(--pg-vw, 1vw)) calc(2 * var(--pg-vw, 1vw)); border-radius: calc(1.8 * var(--pg-vw, 1vw)); }
  .pf-item { padding: calc(1.8 * var(--pg-vw, 1vw)) calc(1.2 * var(--pg-vw, 1vw)); gap: calc(1.4 * var(--pg-vw, 1vw)); }
  .pf-units { gap: calc(3.4 * var(--pg-vw, 1vw)); font-size: calc(5.2 * var(--pg-vw, 1vw)); }
  .pf-unit { gap: calc(2.4 * var(--pg-vw, 1vw)); }
  .pf-syl-hint { margin-top: calc(-1.1 * var(--pg-vw, 1vw)); }
  .pf-mtzg { width: calc(10 * var(--pg-vw, 1vw)); height: calc(10 * var(--pg-vw, 1vw)); }

  .keyboard-wrapper { gap: calc(1 * var(--pg-vw, 1vw)); padding: 0 calc(2 * var(--pg-vw, 1vw)); margin-top: calc(2 * var(--pg-vw, 1vw)); }
  /* 看词听写专属 */
  .kf-page .keyboard-wrapper { padding: 0; margin-top: calc(4 * var(--pg-vw, 1vw)); }
  .keyboard-wrapper.has-dash::before { top: calc(-2 * var(--pg-vw, 1vw)); left: calc(4 * var(--pg-vw, 1vw)); right: calc(4 * var(--pg-vw, 1vw)); }
  .kf-side-panel { top: 0; height: calc(29 * var(--pg-vw, 1vw)); margin-left: calc(1.6 * var(--pg-vw, 1vw)); }
  /* 指法练习专用：侧面板靠得更近 */
  .kf-side-panel.kf-sp-compact { margin-left: calc(0.6 * var(--pg-vw, 1vw)); }

  .kb-main { gap: calc(1 * var(--pg-vw, 1vw)); }
  .keyboard-area { gap: calc(0.4 * var(--pg-vw, 1vw)); padding: calc(1.9 * var(--pg-vw, 1vw)) calc(1.5 * var(--pg-vw, 1vw)); border-radius: calc(2.08 * var(--pg-vw, 1vw)); }
  .kb-row { gap: calc(0.4 * var(--pg-vw, 1vw)); }
  .kb-key { height: min(58px, calc(6.1 * var(--pg-vw, 1vw))); border-radius: calc(0.73 * var(--pg-vw, 1vw)); }
  .kb-key-main { font-size: calc(1.9 * var(--pg-vw, 1vw)); }
  .kb-key-sub { font-size: calc(1 * var(--pg-vw, 1vw)); }
  .kb-func-text { font-size: calc(1 * var(--pg-vw, 1vw)); }
  .kb-bump-dot { width: calc(1.7 * var(--pg-vw, 1vw)); bottom: calc(0.4 * var(--pg-vw, 1vw)); }
  .hand-placeholder { height: calc(30 * var(--pg-vw, 1vw)); }
  .hand-clipper { width: calc(40 * var(--pg-vw, 1vw)); height: calc(21.2 * var(--pg-vw, 1vw)); }
}

/* ===== <960px：键盘已占满宽度，隐藏右侧统计面板 ===== */
@media (max-width: 959px) {
  .kf-side-panel { display: none; }
  .heat-legend { display: none; }
}

/* ===== ≥960px：px 锁定（与小程序电脑版完全一致）===== */
@media (min-width: 960px) {
  .pinyin-flow-scroll { margin: 14px auto 0; padding: 0 14px; }
  .pinyin-flow { gap: 19px; padding: 23px 19px; border-radius: 17px; border-width: 1px; }
  .pf-item { padding: 17px 12px; gap: 13px; border-width: 1px; }
  .pf-units { gap: 34px; }
  .pf-unit { gap: 24px; }
  .pf-syl-hint { margin-top: -11px; }
  .pf-units { font-size: 50px; }
  .pf-mtzg { width: 96px; height: 96px; border-width: 2px; }
  .pf-mtzg-h { border-top-width: 1.2px; }
  .pf-mtzg-v { border-left-width: 1.2px; }
  .pf-mtzg-d1 { border-top-width: 1.2px; }
  .pf-mtzg-d2 { border-top-width: 1.2px; }
  .pf-mtzg-char { font-size: 76px; }
  .keyboard-wrapper { gap: 10px; padding: 0 14px; margin-top: 16px; }
  /* 看词听写专属 */
  .kf-page .keyboard-wrapper { padding: 0; margin-top: 48px; }
  .keyboard-wrapper.has-dash::before { top: -24px; left: 48px; right: 48px; }
  .kf-side-panel { top: 0; height: 348px; margin-left: 18px; min-width: 60px; }
  .kf-side-panel.kf-sp-compact { margin-left: 6px; }
  .kf-sp-label { font-size: 12px; white-space: nowrap; }
  .heat-legend { margin-top: 12px; padding: 6px 0; gap: 26px; }
  .hl-item { gap: 7px; }
  .hl-dot { width: 16px; height: 16px; border-radius: 5px; }
  .hl-text { font-size: 16px; }

  .kb-main { gap: 10px; }
  .kf-sp-label { font-size: 11px; }
  .kf-sp-num { margin-top: 2px; font-size: 22px; }
  .keyboard-area { gap: 4px; padding: 18px 14px; border-radius: 20px; border-width: 2px; }
  .ctl-row { gap: 10px; padding: 0 14px; }
  .ctl-btn { height: 58px; gap: 3px; border-radius: 11px; font-size: 14px; border-width: 1px; }
  .ctl-btn .ctl-dot { width: 15px; height: 15px; border-width: 2px; }
  .ctl-mode-tabs { gap: 5px; padding: 3px; border-radius: 14px; border-width: 1px; }
  .ctl-mode-tab { padding: 4px 12px; border-radius: 10px; font-size: 13px; }
  .skip-bar { gap: 10px; padding: 0 14px; }
  .sb-skip-btn { height: 58px; border-radius: 11px; font-size: 14px; }
  .sb-stat { height: 58px; gap: 10px; border-radius: 11px; border-width: 1px; font-size: 14px; }
  .kb-row { gap: 4px; }
  .kb-key { height: 58px; border-radius: 7px; border-width: 1px; }
  .kb-key-main { font-size: 18px; }
  .kb-key-sub, .kb-func-text { font-size: 10px; }
  .kb-bump-dot { width: 16px; height: 2px; border-radius: 1px; bottom: 4px; }
  .kb-target .kb-key-main, .kb-wrong .kb-key-main { font-size: 18px; }
  .kb-target .kb-key-sub, .kb-wrong .kb-key-sub { font-size: 10px; }
  .hand-placeholder { height: 288px; margin: -9.6px auto 0; }
  .hand-clipper { left: var(--hand-left, 42%); bottom: var(--hand-bottom, 60px); width: 600px; height: 318px; }
}

/* ============================================================
   单词模式（三字/成语/五~七字/短句/文章/古诗）：一屏一个整词卡片
   照搬 pc-triphase / pc-idiom / pc-sixword / pc-poem：
   flow 单列、卡片整行居中、pf-units 横排、米字格按字数分档（字越多格越小）
   所有尺寸用 cqw（=练习框宽度的 1%，原理同小程序 rpx）：
   框 .pinyin-flow 已声明 container-type，框封顶 960 时里面字/格/拼音一起封顶，
   浏览器放大比例或窗口变窄时整体按框宽一起等比缩小 —— 框与字同一尺度，绝不挤出框外。
   ============================================================ */
.pinyin-flow.flow-single {
  grid-template-columns: repeat(1, 1fr);
  grid-auto-rows: auto;
  justify-items: center;
}
.flow-single .pf-item { width: 100%; min-width: 0; padding: 2.2cqw 1cqw; }
/* 单词模式只有外层 flow 一个框：pf-item 不显示边框（对应小程序单词页 .pf-item 无 border 属性，
   .pf-item.current 的 border-color 因 border-style:none 不生效），强制 current 边框透明 */
.flow-single .pf-item.current { border-color: transparent; }
/* 中文练习同理（类名是 flow-cn cn-line，不是 flow-single，所以上面那条吃不到）：
   一整屏只有一个词时，.pf-item.current 那圈边框会套在卡片里变成"第二个框"。
   外面已经有 flow 卡片了，这里也强制透明。 */
.flow-cn.cn-line .pf-item.current { border-color: transparent; background: transparent; }

/* 格子/汉字一律 em，根字号（.pf-units）用 cqw → 整套随框宽缩放 */
.flow-single .pf-unit { flex: 0 0 auto; min-width: 0; display: flex; flex-direction: column; align-items: center; }
.flow-single .pf-mtzg { width: 2.45em; height: 2.45em; border-width: 0.055em; }
.flow-single .pf-mtzg-h, .flow-single .pf-mtzg-v,
.flow-single .pf-mtzg-d1, .flow-single .pf-mtzg-d2 { border-width: 0.032em; }
.flow-single .pf-mtzg-char { font-size: 1.85em; }

/* 三字/两字（大格，框封顶时约 178px，一排不折行）*/
.flow-single .pf-units.sz-s { font-size: 7.9cqw; gap: 0.62em; flex-wrap: nowrap; justify-content: center; }
/* 四字成语（约 142px，一排不折行）*/
.flow-single .pf-units.sz-m { font-size: 6.3cqw; gap: 0.6em; flex-wrap: nowrap; justify-content: center; }
.flow-single .pf-units.sz-m .pf-mtzg { width: 2.46em; height: 2.46em; }
.flow-single .pf-units.sz-m .pf-mtzg-char { font-size: 1.83em; }
/* 五~六字（约 105px，一排不折行，间距收紧）*/
.flow-single .pf-units.sz-l { font-size: 4.7cqw; gap: 0.55em 0.5em; flex-wrap: nowrap; justify-content: center; }
.flow-single .pf-units.sz-l .pf-mtzg { width: 2.45em; height: 2.45em; }
.flow-single .pf-units.sz-l .pf-mtzg-char { font-size: 1.82em; }
/* 七字以上（古诗/短句/文章，约 84px，允许换行居中）*/
.flow-single .pf-units.sz-xl { font-size: 3.8cqw; gap: 0.5em 0.55em; flex-wrap: wrap; justify-content: center; }
.flow-single .pf-units.sz-xl .pf-mtzg { width: 2.4em; height: 2.4em; border-width: 0.06em; }
.flow-single .pf-units.sz-xl .pf-mtzg-char { font-size: 1.82em; }

/* ============================================================
   以下：小程序「电脑/外接键盘练习」剩余全部模式样式 1:1 移植
   （中文双格/中文 flow/中文古诗/拼音古诗/拼音六字两排/听写长光标/
     指法 cube/选关页/拼音掉落队列/拼音强化），数值照搬各页 wxss。
   ============================================================ */

/* ===== 控制圆点：各模式点亮色（深绿/紫/橙）===== */
.ctl-btn .dot-py.on, .ctl-btn .dot-grid.on, .ctl-btn .dot-cn.on,
/* 关掉「键盘提示」→ 整块键盘 + 手图一起隐藏（盲打） */
.keyboard-wrapper.hint-off { display: none; }

.ctl-btn .dot-hand.on { background: #0F766E; color: #0F766E; }
/* 「再听一遍」不是开关，圆点始终点亮（蓝色，跟开关区分开） */
.ctl-btn .dot-replay, .ctl-btn .dot-replay.on { background: #3B82F6; color: #3B82F6; }
.ctl-btn .dot-upper.on { background: #7C3AED; color: #7C3AED; }
.ctl-btn .dot-hanzi.on { background: #F59E0B; color: #F59E0B; }

/* ===== 中文打字练习（整页单列，双格米字/横线）===== */
.pinyin-flow.flow-cn { grid-template-columns: 1fr; grid-auto-rows: auto; justify-items: center; gap: 0; }
.flow-cn .pf-item { width: 100%; min-width: 0; }
.flow-cn .cn-unit { gap: 0.48em; position: relative; flex: 0 0 auto; min-width: 0; }
.flow-cn .pf-pinyin { gap: 0.02em; justify-content: flex-start; flex-wrap: nowrap; }
.flow-cn .pf-pchar-text.pf-pchar-hint { color: var(--pc-cn-pinyin, #0F766E) !important; }
.flow-cn .pf-pinyin-off .pf-pchar-text { visibility: hidden !important; }

/* 中文非 flow（词组/三字/成语）：字号按字数分档，cqw 随练习框等比缩放 */
.flow-cn .pf-units.cn-units { font-size: 7.9cqw; gap: 0.68em; flex-wrap: nowrap; justify-content: center; align-items: flex-start; }
.flow-cn .pf-units.cn-units.cn-n4 { font-size: 6.2cqw; }
/* 一排 6 个字（词组练习一屏 3 组词）：字号再收一档，一排才放得下 */
.flow-cn .pf-units.cn-units.cn-n6 { font-size: 4.1cqw; gap: 0.5em; }
/* 第二排：跟第一排拉开一点间距 */
.flow-cn .pf-units.cn-units.pf-units-row2 { margin-top: 1.6em; }

/* ===== 一屏多个词：词内紧、词间大间距（别让几个词连成一句话）===== */
.flow-cn .pf-units.cn-words {
  font-size: 4.1cqw;
  gap: 2.6em;                     /* 词与词之间拉开 */
  justify-content: center;
}
/* 每排 2 个词（三字常用语 3 字/词、成语 4 字/词）：
   词比二字词宽，一排要放得下就得把字号收一档。
   字号是按「一排最宽的词」实测定出来的，保证不溢出、不换行。 */
.flow-cn .pf-units.cn-words.cn-words-w2 { font-size: 4.9cqw; gap: 1.8em; }
/* 每排 1 个词/句（六字短句 6 字）：一句独占一行，字可以给大些 */
.flow-cn .pf-units.cn-words.cn-words-w1 { font-size: 5.2cqw; gap: 0; }
/* 成语（4 字/词，每排 2 个 → 一排 8 字）：成语拼音普遍较长（chéng huáng chéng kǒng
   这种），字号得比三字的 w2 再收一档。按全词库最坏组合（最长的两个成语配一排）
   实测：3.5cqw 时最坏情况仍留有余量，再大就超容器。 */
.flow-cn .pf-units.cn-words.cn-words-w2.cn-words-w2i { font-size: 3.5cqw; gap: 1.8em; }
/* 六字短句（6 字/句，每排 2 句 → 一排 12 字）：一排字最多，字号再收。
   同样按最坏组合（最长两个六字句配一排）实测：2.5cqw 有余量，3.0 就超容器。 */
.flow-cn .pf-units.cn-words.cn-words-w2.cn-words-w6s { font-size: 2.5cqw; gap: 1.2em; }
/* 一个词：里面的字紧挨着，当成一个整体 */
.flow-cn .pf-word {
  display: flex;
  align-items: flex-start;
  gap: 0.34em;                    /* 词内的两个字之间也只留一点缝 */
  /* 输入框里的拼音超出格子时要往右画出去，所以不能被裁 */
  overflow: visible;
}
/* 两排之间 */
.flow-cn .pf-units.cn-words.pf-units-row2 { margin-top: 2.2em; }
.flow-cn .pf-units.cn-units .pf-mtzg { width: 2.47em; height: 2.47em; border-width: 0.05em; border-radius: 0; }
.flow-cn .pf-units.cn-units.cn-n4 .pf-mtzg { width: 2.45em; height: 2.45em; }
.flow-cn .pf-units.cn-units .pf-mtzg-h,
.flow-cn .pf-units.cn-units .pf-mtzg-v,
.flow-cn .pf-units.cn-units .pf-mtzg-d1,
.flow-cn .pf-units.cn-units .pf-mtzg-d2 { border-width: 0.032em; }
.flow-cn .pf-units.cn-units .pf-mtzg-char { font-size: 1.86em; }
.flow-cn .pf-units.cn-units.cn-n4 .pf-mtzg-char { font-size: 1.84em; }
/* 米字格里的目标汉字颜色（中文练习「文字样式」可调）。
   要压过 .pf-mtzg-char-done 的 !important，所以这里也得带 !important；
   限定在 .flow-cn 内，不影响拼音打字家族。 */
.flow-cn .pf-mtzg-char { color: var(--pc-cn-hanzi, #334155) !important; }

/* 双格：上格目标字 + 下格输入格（照搬 pc-phrase2）*/
.flow-cn .pf-mtzg-pair { display: flex; flex-direction: column; flex-shrink: 0; gap: 0; position: relative; z-index: 1; }
.flow-cn .pf-mtzg-pair > .pf-mtzg:first-child { border-top-left-radius: 0.15em; border-top-right-radius: 0.15em; }
.flow-cn .pf-mtzg-pair > .pf-mtzg:last-child {
  border-bottom-left-radius: 0.15em; border-bottom-right-radius: 0.15em;
  border-top: none; overflow: visible;
}
.flow-cn .pf-mtzg-bottom .pf-mtzg-d1,
.flow-cn .pf-mtzg-bottom .pf-mtzg-d2 { opacity: 0.55; }
.flow-cn .pf-input-cell { display: flex; align-items: center; justify-content: center; overflow: visible; }

/* ===== 中文练习的输入框 =====
   基本形态：填满当前那个输入格（单字布局时）。
   多词布局（词组练习）时输入框改挂在 .pf-word 上、横跨整个词一格宽，
   见下面的 .cn-input-inword。 */
.flow-cn .pf-input-cell { position: relative; }
.cn-input {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  box-sizing: border-box;
  padding: 0 0.06em;
  border: 0;
  outline: none;
  background: transparent;
  font-family: inherit;
  font-size: 1em;          /* 跟汉字一样大（父级字号就是汉字字号） */
  font-weight: 800;
  color: #0F766E;
  text-align: center;
  caret-color: #0F766E;
  z-index: 2;
  white-space: nowrap;
  /* 不要裁剪：拼音超出格子时要能**往右画出去**（父级已放开 overflow） */
  overflow: visible;
}
/* 多词布局：输入框横跨整个词，放在底部那排（目标字格下面）。
   文字先居中；撑满后再往右延伸（靠 text-align 从 center 切到 left 做不到，
   所以用 padding 撑：内容窄时左右 padding 大→居中；内容宽时 padding 收缩→贴左）。 */
/* 多词布局里的输入框：left / width 由 JS 按"当前字那一格"动态设置，
   所以这里不写 left/right，只定高度和纵向位置。
   纵向：对齐到底部那排输入格（实测 4.76em）。 */
.cn-input.cn-input-inword {
  /* 纵向 top 由 JS 实测写入（见 _mountCnInput）—— 因为 host 在不同布局下不一样
     （词格局 .pf-word / flow 布局 .pf-mtzg-pair），写死的 em 值必有一边对不上。
     这里只定高度：按**含下伸部分的字形**留够（g/p/q/y 的尾巴要放得下）。 */
  top: 0;
  height: 1.35em;
  line-height: 1.35em;
  /* 左右各留 1px 防毛边（宽度余量已经由 JS 在算宽度时留出）。
     底部垫 7px：逐像素量出字形在字体框里**偏下** ——
     字号 36.57 时，字形上留白 9px、下留白 2px（差 7px）。
     底部垫 7px 把文字整体上移 3.5px，上下就都约 5.5px，视觉居中。 */
  padding: 0 1px 7px;
  font-size: 1em;
  color: #ffffff;
  caret-color: #ffffff;
  border-radius: 0.12em;
  background: transparent;      /* 默认透明（还没打字时不显示色块） */
}
/* 有内容时才显示底色块（JS 在有字时加 .has-text）。
   底色 / 框内文字与光标色由「动效样式」面板控制（--pc-cn-boxbg / --pc-cn-boxfg）。 */
.cn-input.cn-input-inword.has-text {
  background: var(--pc-cn-boxbg, #0f3d36);
  color: var(--pc-cn-boxfg, #FFFFFF);
  caret-color: var(--pc-cn-boxfg, #FFFFFF);
}
/* 还没打字：光标用深墨绿细线画在格子中间（不要白线、不要色块） */
.cn-input.cn-input-inword:not(.has-text) { caret-color: #0F766E; }
/* 拼音超出格子时要能**往右画出去**，所以整条链路都不能裁：
   格子（.pf-input-cell / .pf-mtzg 自带 overflow:hidden）、词容器、整行。 */
.flow-cn .pf-mtzg.pf-input-cell { overflow: visible; }
.flow-cn .pf-units.cn-words { overflow: visible; }
.flow-cn .pf-item { overflow: visible; }
.flow-cn .pf-mtzg-pair { overflow: visible; }
.flow-cn .pf-mtzg-clip { position: absolute; top: 0; left: 0; right: 0; bottom: 0; overflow: hidden; pointer-events: none; }
/* 打对后填进格子的字：字号/字重跟**上面米字格里的目标字**保持一致
   （不用楷体 —— 上面是黑体，两种字体大小看着不一样）。 */
.flow-cn .pf-input-cell .pf-bottom-char {
  position: relative; z-index: 1;
  font-size: 1.86em; font-weight: 800;
  line-height: 1;
}
.flow-cn .cn-n4 .pf-input-cell .pf-bottom-char { font-size: 1.84em; }
.pf-bottom-char-done { color: var(--pc-cn-ok, #16a34a); }
.pf-bottom-char-wrong { color: #dc2626; }
.flow-cn .pf-bottom-cursor {
  width: 45%; height: 6%; flex-shrink: 0;
  background: #0F766E; border-radius: 2px;
  animation: pfCursorBlink 0.8s steps(1) infinite;
}
/* 横线模式（默认）：下格无边无线，仅一条红横线 */
.flow-cn.cn-line .pf-input-cell { border-color: transparent; background: transparent; }
.flow-cn.cn-line .pf-input-cell .pf-mtzg-clip { display: none; }
/* 线的左右端由 JS 实测算出（--wl / --wr，单位 em），写在格子内联样式上：
   一个词下面的横线要**从第一个字最左连到最后一个字最右**。
   注意不能用固定的 ±0.4em 伸展去补缝 —— 拼音长时槽位被撑宽
   （.pf-unit 宽度按拼音长度算、格子在其中居中），两格间距会远大于 0.34em，
   固定伸展补不满就断开。默认 0 = 单字布局（三字/成语）时线就贴格子两边。 */
.flow-cn.cn-line .pf-input-cell::after {
  content: ''; position: absolute; bottom: 0;
  left: var(--wl, 0em); right: var(--wr, 0em);
  height: 0.05em; background: #e05a5a;
}
/* ===== 错误提示动效（中文练习，由「动效样式」面板切换，挂 body 上的 cn-err-*）=====
   JS 打错时给那一格挂 .cn-cell-shake 触发；具体播哪个动画由 body 的类决定。
   none = 不提示（此时 JS 也不挂类）。 */
.cn-cell-shake { animation-duration: .36s; animation-timing-function: cubic-bezier(.36,.07,.19,.97); animation-fill-mode: both; }
body.cn-err-shake   .cn-cell-shake { animation-name: cnCellShake; }
body.cn-err-flashRed .cn-cell-shake { animation-name: cnCellRed; }
body.cn-err-shrink  .cn-cell-shake { animation-name: cnCellShrink; }
@keyframes cnCellShake {
  0%, 100% { transform: translateX(0); }
  15% { transform: translateX(-0.22em); }
  30% { transform: translateX(0.22em); }
  45% { transform: translateX(-0.16em); }
  60% { transform: translateX(0.16em); }
  75% { transform: translateX(-0.08em); }
  90% { transform: translateX(0.08em); }
}
@keyframes cnCellRed {
  0%, 100% { background: transparent; }
  40% { background: rgba(239,68,68,.22); }
}
@keyframes cnCellShrink {
  0%, 100% { transform: scale(1); }
  45% { transform: scale(.82); }
}

/* ===== 输入正确动效（同上，挂 body 上的 cn-ok-*）=====
   都由 JS 给那一格挂 .cn-cell-flash 触发。 */
.cn-cell-flash { animation-duration: .42s; animation-timing-function: ease; animation-fill-mode: both; }
body.cn-ok-flash .cn-cell-flash .pf-bottom-char { animation: cnCellFlash .42s ease both; }
body.cn-ok-pop   .cn-cell-flash               { animation-name: cnCellPop; }
body.cn-ok-pop   .cn-cell-flash .pf-bottom-char { animation: cnCellPopChar .42s cubic-bezier(.34,1.56,.64,1) both; }
@keyframes cnCellFlash {
  0%   { opacity: 1; }
  18%  { opacity: .12; }
  36%  { opacity: 1; }
  54%  { opacity: .12; }
  72%  { opacity: 1; }
  100% { opacity: 1; }
}
@keyframes cnCellPop {
  0%, 100% { transform: scale(1); }
  40% { transform: scale(1.16); }
}
@keyframes cnCellPopChar {
  0%, 100% { transform: scale(1); }
  45% { transform: scale(1.12); }
}
@media (prefers-reduced-motion: reduce) {
  .cn-cell-shake, .cn-cell-flash, .cn-cell-flash .pf-bottom-char { animation: none; }
}

/* ===== 中文 flow（激励短句/办公文本/六字短句）：6 字一排折行 + 3 排窗口上移 ===== */
.pf-item-flow {
  overflow: hidden; padding-top: 0; padding-bottom: 0;
  justify-content: flex-start; width: 100%;
  font-size: calc(4.6 * var(--pg-vw, 1vw));
}
.pf-units-flow {
  display: flex; flex-wrap: wrap;
  justify-content: center; align-items: flex-start;
  column-gap: 0.68em; row-gap: 0;
  transition: transform 0.45s ease;
  position: relative;
}
.pf-units-flow::before { display: none; }
.pf-row-break { flex: 0 0 100%; width: 100%; height: 0.6em; }
.flow-cn .pf-units-flow .pf-unit { flex: 0 0 auto; }
.flow-cn .pf-units-flow .pf-pinyin { justify-content: center; width: 100%; }
.flow-cn .pf-units-flow .pf-unit::before,
.flow-cn .pf-units-poem .pf-unit::before {
  content: ''; position: absolute; top: 0; left: -0.34em; right: -0.34em;
  height: 1.8em; z-index: 0; pointer-events: none;
  border-top: 0.032em solid rgba(15,118,110,.5);
  border-bottom: 0.032em solid rgba(15,118,110,.5);
  background-image:
    linear-gradient(rgba(15,118,110,.28), rgba(15,118,110,.28)),
    linear-gradient(rgba(15,118,110,.28), rgba(15,118,110,.28));
  background-size: 100% 0.022em; background-repeat: no-repeat;
  background-position: 0 33.33%, 0 66.67%;
}
.flow-cn .pf-units-flow .pf-mtzg { width: 2.3311em; height: 2.3311em; border-width: 0.05em; border-radius: 0; }
.flow-cn .pf-units-flow .pf-mtzg-h,
.flow-cn .pf-units-flow .pf-mtzg-v,
.flow-cn .pf-units-flow .pf-mtzg-d1,
.flow-cn .pf-units-flow .pf-mtzg-d2 { border-width: 0.032em; }
.flow-cn .pf-units-flow .pf-mtzg-char { font-size: 1.64em; }
.flow-cn .pf-units-flow .pf-input-cell .pf-bottom-char { font-size: 1.64em; }

/* ===== 中文古诗：5 列网格，3 排窗口，第 4 排整体上移 ===== */
.pf-item-poem {
  overflow: hidden; padding-top: 0; padding-bottom: 0;
  justify-content: flex-start; width: 100%;
  height: calc(25.64 * calc(4.6 * var(--pg-vw, 1vw)));
}
.pf-units-poem {
  display: grid; grid-template-columns: repeat(5, 2.9333em);
  gap: 0.6em 0.68em; width: 17.387em;
  justify-content: center; margin: 0 auto;
  font-size: calc(4.6 * var(--pg-vw, 1vw)); transition: transform 0.45s ease;
  position: relative;
}
.pf-units-poem.pf-lift-1 { transform: translateY(-8.7466em); }
.pf-units-poem::before { display: none; }
.flow-cn .pf-units-poem .pf-unit { width: 2.9333em; }
.flow-cn .pf-units-poem .pf-pinyin { justify-content: center; width: 100%; }
.flow-cn .pf-units-poem .pf-mtzg { width: 2.9333em; height: 2.9333em; border-width: 0.05em; border-radius: 0; }
.flow-cn .pf-units-poem .pf-mtzg-h,
.flow-cn .pf-units-poem .pf-mtzg-v,
.flow-cn .pf-units-poem .pf-mtzg-d1,
.flow-cn .pf-units-poem .pf-mtzg-d2 { border-width: 0.032em; }
.flow-cn .pf-units-poem .pf-mtzg-char { font-size: 2.06em; }
.flow-cn .pf-units-poem .pf-input-cell .pf-bottom-char { font-size: 2.06em; }

/* ===== 拼音古诗：两排裁剪窗口 + 整排上移（照搬 pc-test）===== */
.pf-poem-window {
  width: 100%; overflow: hidden;
  height: calc(2 * var(--pf-pitch) - var(--pf-rowgap));
  font-size: calc(5.2 * var(--pg-vw, 1vw));
  --pf-rowgap: calc(1 * var(--pg-vw, 1vw));
  --pf-pitch: calc(28.1 * var(--pg-vw, 1vw));
}
.pf-poem-inner {
  display: flex; flex-direction: column;
  gap: var(--pf-rowgap);
  transition: transform 0.35s ease; will-change: transform;
}
.pf-poem-row { font-size: calc(5.2 * var(--pg-vw, 1vw)); gap: 0.68em; }
.flow-single .pf-poem-row .pf-unit { gap: 0.48em; }
.flow-single .pf-poem-row .pf-mtzg { width: calc(22 * var(--pg-vw, 1vw)); height: calc(22 * var(--pg-vw, 1vw)); border-width: 0.05em; border-radius: 0; }
.flow-single .pf-poem-row .pf-mtzg-h,
.flow-single .pf-poem-row .pf-mtzg-v,
.flow-single .pf-poem-row .pf-mtzg-d1,
.flow-single .pf-poem-row .pf-mtzg-d2 { border-width: 0.032em; }
.flow-single .pf-poem-row .pf-mtzg-char { font-size: 2.0667em; }

/* ===== 拼音六字短句：上排 3 字、下排 3 字（照搬 pc-sixword）===== */
.pf-two-row { width: 100%; display: flex; flex-direction: column; align-items: center; }
.flow-single .pf-two-row .pf-units { font-size: calc(7.5 * var(--pg-vw, 1vw)); gap: 0.68em; }
.pf-units-row2 { margin-top: 0.55em; }

/* ===== 看词听写：关拼音后的动态长光标（照搬 pc-phrase2）===== */
.pf-typed-group { display: flex; gap: 0.02em; position: relative; }
.pf-pinyin-cursor-long {
  position: absolute; top: calc(100% - 0.19em);
  left: -2px; right: -2px;
  height: 0.08em; border-radius: 0.04em;
  background: #EF4444; z-index: 2;
}
.pf-pinyin-cursor-first { width: 0.58em; left: 50%; right: auto; transform: translateX(-50%); }

/* ===== 指法键位练习：3D 翻转 cube（照搬 keyfingering/practice）===== */
/* 顶部不留大空隙：紧跟头部，只留一点点呼吸 */
.seq-bar { display: flex; justify-content: center; width: 100%; margin: 10px auto 0; box-sizing: border-box; }
/* 方块区大卡片：只有它的边框/投影跟键盘样式联动（--skin-main 由 body.kbskin-* 定义）*/
.seq-track {
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  gap: calc(3.5 * var(--pg-vw, 1vw)); width: 100%; box-sizing: border-box;
  background: #ffffff;
  border: 2px solid var(--skin-main, #0F766E);
  border-radius: calc(1.875 * var(--pg-vw, 1vw));
  padding: calc(3.2 * var(--pg-vw, 1vw)) calc(3 * var(--pg-vw, 1vw));
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .9),
    0 14px 34px var(--skin-shadow, rgba(15,118,110,.18)),
    0 3px 8px var(--skin-shadow, rgba(15,118,110,.10));
}
.seq-row { display: flex; width: 100%; justify-content: space-between; align-items: center; gap: calc(1.8 * var(--pg-vw, 1vw)); box-sizing: border-box; }
.seq-key {
  width: calc(12.8 * var(--pg-vw, 1vw)); height: calc(12.8 * var(--pg-vw, 1vw)); position: relative; border-radius: 0;
  perspective: calc(calc(12.8 * var(--pg-vw, 1vw)) * 8); perspective-origin: center center;
  flex-shrink: 0;
}
/* 当前待输入的方块提到最上层：
   它有放大/涟漪动效会超出自身格子，
   ① 同行内会被右邻盖住 → 提 .seq-key
   ② 跨行会被下一行盖住 → 用 :has() 把整个 .seq-row 提起来 */
.seq-key.seq-active { z-index: 5; }
.seq-key.seq-error { z-index: 6; }
.seq-row { position: relative; }
.seq-row:has(.seq-active) { z-index: 5; }
.seq-row:has(.seq-error)  { z-index: 6; }
.cube {
  width: calc(12.8 * var(--pg-vw, 1vw)); height: calc(12.8 * var(--pg-vw, 1vw)); position: relative;
  transform-style: preserve-3d;
  transition: transform 0.5s cubic-bezier(0.4, 0, 0.2, 1);
  transform: rotateX(0deg);
  /* 立方体底色保持透明：填色会从面缝里透出来形成一条深色细线（已踩过的坑）。 */
  background: transparent; border-radius: 0;
}
.face {
  /* 每个面四边各向外撑 1.5%（约 1.8px）：
     旋转时相邻两面在棱边处充分重叠，消除亚像素细缝。
     外扩量要够 —— "一直转"会转到任意角度，缝比定点翻转时更明显。 */
  position: absolute; top: -1.5%; left: -1.5%;
  width: 103%; height: 103%;
  border-radius: 0; display: flex; align-items: center; justify-content: center;
  font-size: calc(calc(12.8 * var(--pg-vw, 1vw)) * 0.42); font-weight: 800; color: #FFFFFF;
  backface-visibility: hidden; box-sizing: border-box;
}
/* 六个面统一：白底 + 主题边框 + 深色字。
   关键：**每个面都带边框和文字**，这样无论往哪个方向翻，
   翻出来看到的都是有字有边的方块，而不是一块空白。 */
.face-front,
.face-back,
.face-top,
.face-bottom,
.face-left,
.face-right {
  background: #ffffff;
  color: var(--bx-font, var(--bx-text, #0b5c55));
  border: calc(calc(12.8 * var(--pg-vw, 1vw)) * 0.028) solid var(--bx-border, #16302c);
  box-sizing: border-box;
}
/* 正面：只做定位，不再带"白色顶高光"——
   那条 rgba(255,255,255,.9) 的内阴影是当初给白键帽画的，
   方块换成彩色后它就变成正面独有的一抹白（看着像"没贴合、漏白"）。 */
.face-front  { transform: translateZ(calc(calc(12.8 * var(--pg-vw, 1vw)) / 2)); }
.face-back   { transform: rotateY(180deg) translateZ(calc(calc(12.8 * var(--pg-vw, 1vw)) / 2)); }
.face-top    { transform: rotateX(90deg)  translateZ(calc(calc(12.8 * var(--pg-vw, 1vw)) / 2)); }
.face-bottom { transform: rotateX(-90deg) translateZ(calc(calc(12.8 * var(--pg-vw, 1vw)) / 2)); }
.face-left   { transform: rotateY(-90deg) translateZ(calc(calc(12.8 * var(--pg-vw, 1vw)) / 2)); }
.face-right  { transform: rotateY(90deg)  translateZ(calc(calc(12.8 * var(--pg-vw, 1vw)) / 2)); }
/* 当前待输入：整块实心（六个面同色，只给正面加光晕）*/
.seq-active .face-front {
  position: relative;
  box-shadow: 0 0 22px var(--bx-active-shadow, rgba(11, 66, 60, .55));
}
/* 待输入键的光晕涟漪：两圈青绿光环不断向外扩散淡出（像水波一圈圈荡开）。
   用两个 ::before / ::after 错开时间，形成连续不断的波动。 */
.seq-active .face-front::before,
.seq-active .face-front::after {
  content: '';
  position: absolute; inset: -2px;
  border-radius: inherit;
  border: 2px solid var(--ripple-color, rgba(232, 69, 60, .9));
  pointer-events: none;
  animation: seqRipple 2s ease-out infinite;
}
.seq-active .face-front::after { animation-delay: 1s; }
@keyframes seqRipple {
  0%   { transform: scale(1);     opacity: .9; }
  70%  { opacity: .25; }
  100% { transform: scale(1.55);  opacity: 0; }
}
/* 六个面全部统一 —— 不能只写 front/top，
   否则翻到其他面时文字色仍是"未输入"的深色，在深底上看不见（曾表现为"露白"）。
   字体色规则：自选的 --bx-font 只作用于「未输入」；
   待输入 / 已输入保持白色（或底色过浅时自动转深，保证可读）。 */
.seq-active .face {
  background: var(--bx-active, #0b423c);
  color: var(--bx-active-text, #ffffff);
  border-color: var(--bx-border, #16302c);
}
.seq-done .face {
  background: var(--bx-done, #14b3a6);
  color: var(--bx-done-text, #ffffff);
  border-color: var(--bx-border, #16302c);
}
/* ===== 打对后的翻转动效（动态样式）=====
   默认向下翻（rotateX -90°，露出顶面）；可切换成其他方向。 */
.seq-done .cube {
  transform: rotateX(-90deg);
  transition: transform .55s cubic-bezier(.22, 1.2, .36, 1);
}
/* 向上翻：转 +90°，露出底面 */
body.bxanim-up .seq-done .cube { transform: rotateX(90deg); }
/* 向左翻 */
body.bxanim-left .seq-done .cube { transform: rotateY(-90deg); }
/* 向右翻 */
body.bxanim-right .seq-done .cube { transform: rotateY(90deg); }
/* 斜翻 4 种：立方体绕「棱轴」转 180° 有 6 根轴可选，取其中 4 根，
   每根翻出来的方向观感都不同，且都能落到真实的面（不会翻到棱边）。 */
body.bxanim-diag1 .seq-done .cube { transform: rotate3d(1, 1, 0, 180deg); }
body.bxanim-diag2 .seq-done .cube { transform: rotate3d(1, -1, 0, 180deg); }
body.bxanim-diag3 .seq-done .cube { transform: rotate3d(1, 0, 1, 180deg); }
body.bxanim-diag4 .seq-done .cube { transform: rotate3d(0, 1, 1, 180deg); }
/* ===== 持续旋转（打对后一直转，8 个方向）=====
   用 CSS 动画交给 GPU 合成；但 18 个方块同时转仍占 GPU，低端机会掉帧。
   每个方向 = 一根旋转轴 + 一个旋转正负：
     向下/向上 = 绕 X 轴；向左/向右 = 绕 Y 轴；4 个斜向 = 绕对角轴 */
[class*="bxanim-spin"] .seq-done .cube {
  --spinaxis: 1,0,0;
  --spindir: 1;
  will-change: transform;
  animation: bxSpinAxis var(--spin-dur, 1.6s) linear infinite;   /* 时长由转速滑块控制 */
}
body.bxanim-spin-up    .seq-done .cube { --spindir: -1; }                        /* 向上 */
body.bxanim-spin-left  .seq-done .cube { --spinaxis: 0,1,0; --spindir: -1; }     /* 向左 */
body.bxanim-spin-right .seq-done .cube { --spinaxis: 0,1,0; --spindir: 1; }      /* 向右 */
body.bxanim-spin-d1    .seq-done .cube { --spinaxis: 1,1,0; }                    /* 斜向1 */
body.bxanim-spin-d2    .seq-done .cube { --spinaxis: 1,-1,0; }                   /* 斜向2 */
body.bxanim-spin-d3    .seq-done .cube { --spinaxis: 1,0,1; }                    /* 斜向3 */
body.bxanim-spin-d4    .seq-done .cube { --spinaxis: 0,1,1; }                    /* 斜向4 */
@keyframes bxSpinAxis {
  from { transform: rotate3d(var(--spinaxis, 1,0,0), 0deg); }
  to   { transform: rotate3d(var(--spinaxis, 1,0,0), calc(360deg * var(--spindir, 1))); }
}
/* 低配模式：系统开了"减少动态效果"时自动关掉持续动画（停在半途的静态姿态）*/
@media (prefers-reduced-motion: reduce) {
  [class*="bxanim-spin"] .seq-done .cube { animation: none; transform: rotate3d(1, 1, 0, 180deg); }
}
/* 弹跳翻：幅度更大带回弹 */
body.bxanim-bounce .seq-done .cube {
  transition: transform .7s cubic-bezier(.28, 1.6, .42, 1);
}
.seq-error { animation: seqShake 0.4s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }
.seq-error .face-front { background: #EF4444 !important; color: #FFFFFF !important; outline-color: #DC2626 !important; }
@keyframes seqShake {
  0%, 100% { transform: translateX(0); }
  10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
  20%, 40%, 60%, 80% { transform: translateX(5px); }
}
.seq-cursor {
  position: absolute; bottom: calc(1.1 * var(--pg-vw, 1vw)); left: 50%; transform: translateX(-50%);
  width: calc(7.1 * var(--pg-vw, 1vw)); height: calc(0.57 * var(--pg-vw, 1vw)); background: #EF4444;
  animation: seqCursorBlink 1s step-end infinite; z-index: 5;
}
@keyframes seqCursorBlink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }

/* 指法过关结果区 */
.result-area {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; width: 100%; box-sizing: border-box;
  margin: 20px auto 0; padding: 40px 20px;
  background: #FFFFFF; border: 2px solid #0F766E; border-radius: 18px;
  box-shadow: 0 6px 24px rgba(15,118,110,0.10);
}
.rr-status { text-align: center; }
.rr-passed { font-size: 22px; font-weight: 800; color: #0F766E; display: block; }
.rr-retry { font-size: 18px; font-weight: 700; color: #1E2133; display: block; }
.rr-detail { font-size: 13px; color: #64748B; text-align: center; line-height: 1.6; }
.rr-buttons { display: flex; gap: 10px; margin-top: 4px; }
.rr-btn { padding: 8px 20px; border-radius: 10px; font-size: 14px; font-weight: 700; cursor: pointer; user-select: none; }
.rr-btn-primary {
  background: linear-gradient(135deg, #14B8A6, #0F766E); color: #FFFFFF;
  box-shadow: 0 2px 7px rgba(23,181,164,0.4);
}
.rr-btn-secondary { background: rgba(15,118,110,0.07); color: #0F766E; border: 1px solid rgba(15,118,110,0.28); }

/* ===== 通关结算：五星 + 六格指标 ===== */
.rr-stars {
  display: flex; gap: 8px; justify-content: center;
  margin: 2px 0 4px;
  font-size: 40px; line-height: 1;
}
.rr-stars .rr-star { color: #DCE5E3; transition: color .2s ease; }
.rr-stars .rr-star.on { color: #F5B301; text-shadow: 0 2px 8px rgba(245,179,1,.35); }
/* 四星及以上：整排加金色光晕，强化"打得漂亮"的观感 */
.rr-stars.rr-stars-gold .rr-star.on {
  color: #FFC107;
  text-shadow: 0 2px 14px rgba(255,193,7,.6);
}
.rr-grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(96px, 1fr));
  gap: 10px;
  width: 100%; max-width: 520px;
  margin: 6px 0 4px;
}
.rr-cell {
  background: #F1FAF8;
  border-radius: 12px;
  padding: 12px 8px;
  text-align: center;
}
.rr-cell-num { font-size: 24px; font-weight: 800; color: #0F766E; line-height: 1.2; }
.rr-cell-label { font-size: 12px; color: #64748B; margin-top: 2px; }

/* ===== 全屏通关庆祝页 =====
   设计：浅色底（白到淡青），中央一张白卡浮在柔和光晕上，SVG 金星依次坠落。
   浅底 + 白卡，清爽、有"奖状"的感觉。 */
.cf-layer {
  position: fixed; inset: 0; z-index: 160;
  display: flex; align-items: center; justify-content: center;
  padding: 24px;
  overflow: hidden;
  background:
    radial-gradient(900px 620px at 50% 32%, #ffffff 0%, #f2faf8 52%, #e6f4f1 100%);
  animation: cfIn .38s cubic-bezier(.2,.7,.2,1);
}
@keyframes cfIn { from { opacity: 0; } to { opacity: 1; } }
/* 柔和的光晕：淡青 + 淡金，给白底一点层次，不至于死白 */
.cf-aura {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    radial-gradient(560px 420px at 50% 30%, rgba(20,184,166,.13), transparent 72%),
    radial-gradient(440px 340px at 14% 80%, rgba(255,193,7,.13), transparent 74%),
    radial-gradient(440px 340px at 88% 16%, rgba(94,234,212,.12), transparent 74%);
  animation: cfAura 7s ease-in-out infinite;
}
@keyframes cfAura {
  0%, 100% { opacity: .85; transform: scale(1); }
  50%      { opacity: 1;   transform: scale(1.06); }
}
/* 礼花画布铺满整层，不吃点击 */
.cf-canvas {
  position: absolute; inset: 0; z-index: 6;
  width: 100%; height: 100%;
  pointer-events: none;
}
.cf-defs { position: absolute; width: 0; height: 0; }
.cf-card {
  position: relative; z-index: 4;
  width: 100%; max-width: 680px;
  padding: 58px 54px 48px;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
  border-radius: 34px;
  background: #ffffff;
  border: 1px solid rgba(15,118,110,.10);
  box-shadow:
    0 34px 90px rgba(15, 118, 110, .18),
    0 2px 10px rgba(15, 118, 110, .06);
  animation: cfCardIn .55s cubic-bezier(.2,.9,.25,1.05) both;
}
@keyframes cfCardIn {
  from { opacity: 0; transform: translateY(34px) scale(.93); }
  to   { opacity: 1; transform: none; }
}
.cf-title {
  font-size: clamp(44px, calc(5.2 * var(--pg-vw, 1vw)), 60px); font-weight: 900; letter-spacing: 6px;
  color: #0B3B35;
  /* 900 已是字重上限，再加一圈同色描边让笔画更实、更"粗" */
  -webkit-text-stroke: 1.5px #0B3B35;
}
/* 星星：五颗全都从上方依次掉落、落地弹跳。
   掉落用多段关键帧模拟 GSAP elastic.out(1, 0.3)：落下 → 过冲 → 回弹 → 收敛
   （cubic-bezier 只能过冲一次，做不出"弹几下"）。 */
.cf-stars {
  display: flex; gap: 10px; justify-content: center;
  margin: 22px 0 6px;
}
.cf-star {
  width: clamp(38px, calc(4.2 * var(--pg-vw, 1vw)), 52px);
  height: clamp(38px, calc(4.2 * var(--pg-vw, 1vw)), 52px);
  display: block;
  opacity: 0;
  animation: cfStarDrop .95s forwards;
  animation-delay: calc(var(--slot) * 140ms + 220ms);
}
/* 点亮的星带金色光晕（filter 比 text-shadow 细腻得多） */
.cf-star.on {
  filter: drop-shadow(0 2px 10px rgba(255,193,7,.55));
}
@keyframes cfStarDrop {
  0%   { opacity: 0; transform: translateY(-210px) scale(.72); }
  6%   { opacity: 1; }
  /* 落地过冲 —— 这里开始"咚" */
  11%  { transform: translateY(52px) scale(1.08); }
  21%  { transform: translateY(26px) scale(1.03); }
  /* 第一次回弹（向上超过原位） */
  31%  { transform: translateY(-26px) scale(.99); }
  41%  { transform: translateY(7px); }
  51%  { transform: translateY(-3.5px); }
  61%  { transform: translateY(3.5px); }
  71%  { transform: translateY(-1.6px); }
  81%  { transform: translateY(1px); }
  91%  { transform: translateY(-.4px); }
  100% { opacity: 1; transform: translateY(0) scale(1); }
}
/* 4 星以上，点亮的星星落稳后持续呼吸（只动 filter，不碰 transform，避免和掉落冲突）。
   注意：animation 简写会把 delay 重置成 0，所以这里必须把错峰延迟重新写一遍，
   否则五颗星会同步掉下来（之前就是这个 bug）。 */
.cf-star.on.gold {
  animation: cfStarDrop .95s forwards,
             cfStarGlow 2.4s ease-in-out infinite;
  animation-delay: calc(var(--slot) * 140ms + 220ms),
                   calc(var(--slot) * 140ms + 1720ms);
}
@keyframes cfStarGlow {
  0%, 100% { filter: drop-shadow(0 2px 10px rgba(255,193,7,.55)); }
  50%      { filter: drop-shadow(0 0 22px rgba(255,193,7,.95)); }
}
/* 成绩六格（2 行 × 3 列）：浅青底块，压在白卡上 */
.cf-grid {
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 12px; width: 100%;
  margin: 26px 0 0;
}
.cf-cell {
  background: linear-gradient(180deg, #F4FAF9, #EAF5F2);
  border: 1px solid #DDEDE9;
  border-radius: 16px;
  padding: 16px 10px;
}
/* 有失误时这一格泛红，一眼能看出问题在哪 */
.cf-cell-warn {
  background: linear-gradient(180deg, #FFF6F5, #FEECEA);
  border-color: #FAD9D5;
}
.cf-num {
  font-size: clamp(24px, calc(2.6 * var(--pg-vw, 1vw)), 31px); font-weight: 800;
  color: #0B3B35; line-height: 1.15;
  font-variant-numeric: tabular-nums;
}
/* 主指标（准确率）用金色点出来，避免六格一样重 */
.cf-num-hero { color: #D99000; }
.cf-num-warn { color: #E8453C; }
.cf-num i { font-size: .54em; font-style: normal; font-weight: 700; opacity: .6; margin-left: 1px; }
.cf-label { font-size: 12px; color: #7A8F8B; margin-top: 5px; letter-spacing: .5px; }
/* ===== 通关卡片：热力键盘图 ===== */
.cf-heat-box {
  margin-top: 14px;
  padding: 0;
  background: transparent;
  border-radius: 0;
  border: none;
}
.cf-heat-title {
  font-size: 12px;
  font-weight: 700;
  color: #0F766E;
  margin-bottom: 2px;
  text-align: center;
  letter-spacing: .5px;
}
.cf-heat-kb-mini {
  max-width: 600px;
  margin: 0 auto;
  position: relative;
  height: 200px;
  overflow: visible;
}
.cf-heat-kb-mini .keyboard-wrapper {
  /* 跟主页面一模一样的固定宽度，确保键盘排版不塌 */
  width: 932px;
  max-width: 932px;
  flex: 0 0 auto;
  position: absolute;
  top: 0;
  left: 50%;
  margin-top: 0;
  padding: 0;
  transform-origin: top center;
  transform: translateX(-50%) scale(0.58);
}
.cf-heat-tip {
  margin-top: 6px;
  text-align: center;
  font-size: 13px;
  color: #5a7069;
}
.cf-heat-label { margin-right: 6px; }
.cf-heat-key {
  display: inline-block;
  margin: 0 3px;
  padding: 1px 7px;
  background: rgba(239,68,68,.12);
  color: #EF4444;
  border-radius: 4px;
  font-weight: 700;
  font-size: 12px;
}
.cf-heat-all {
  color: #0F766E;
  font-weight: 600;
}
/* ===== 数字滚轮（老虎机式）=====
   每一位是一列 0-9，strip 从 0 滚到目标数字后停住。
   --end 是目标格的 translateY 倍数，--dur/--dly 由 JS 按位数算（越靠右越久）。 */
.cf-roll {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  /* 行高 = 一格高度，strip 每格也走这个高度，translateY 才能精确对齐 */
  height: 1.15em;
  line-height: 1.15em;
}
.cf-roll-strip {
  display: block;
  animation: cfRoll var(--dur) cubic-bezier(.16, .8, .26, 1) var(--dly) both;
  will-change: transform;
}
.cf-roll-strip > span { display: block; height: 1.15em; line-height: 1.15em; }
/* 往下转：从上方（负位移）滑到 0 位 */
@keyframes cfRoll {
  from { transform: translateY(calc(var(--start) * 1.15em)); }
  to   { transform: translateY(0); }
}
/* 速度为空时显示的长横线 */
.cf-roll-plain { display: inline-block; }
/* 关掉动效偏好时直接显示最终数字，不滚 */
@media (prefers-reduced-motion: reduce) {
  .cf-roll-strip { animation: none; transform: none; }
}
/* 评语：数据下方、按钮上方 */
.cf-sub {
  margin-top: 24px; padding-top: 22px;
  width: 100%;
  border-top: 1px dashed #E1EDEA;
  font-size: clamp(15px, calc(1.5 * var(--pg-vw, 1vw)), 17px); line-height: 1.7;
  color: #5A716D;
  letter-spacing: .3px;
}
.cf-btns {
  display: flex; gap: 12px; justify-content: center; flex-wrap: wrap;
  margin-top: 32px;
}
.cf-btn {
  padding: 14px 34px; border-radius: 10px;
  font-size: 16px; font-weight: 700; font-family: inherit;
  letter-spacing: .5px;
  cursor: pointer;
  border: 1px solid #D5E6E2;
  background: #ffffff;
  color: #0F766E;
  transition: transform .18s ease, background .2s ease, border-color .2s ease, color .2s ease;
}
.cf-btn:hover {
  background: #F2FAF8;
  border-color: #0F766E;
  color: #0B5C55;
  transform: translateY(-1px);
}
.cf-btn-primary {
  background: linear-gradient(135deg, #14B8A6, #0F766E);
  border-color: transparent; color: #ffffff;
  font-weight: 800;
  box-shadow: 0 8px 22px rgba(20, 184, 166, .32);
}
.cf-btn-primary:hover {
  background: linear-gradient(135deg, #17C9B5, #0D6B64);
  box-shadow: 0 12px 28px rgba(20, 184, 166, .45);
  color: #ffffff;
}
/* （音效开关按钮已移除，音效本身保留。后续要在别处加开关时，可调 Sound.on 读写） */

/* ===== 让「拼音练习模式」页跟「指法练习」页视觉统一 =====
   两边是两套不同的页面骨架（.pym2-page vs .kf-page），这几条是把拼音页
   对齐到指法页：背景网格/柔光、图片区正方形、关掉图片浮动动画。
   放在文件末尾以保证优先级。 */

/* ① 内容区对齐指法页的 1080 宽（#main 已经是居中容器，这里只要取消它自己的
   限宽和内边距，让内容占满 1080 即可）。注意**不要**用 width:100vw 破开视口 ——
   #main 本身有居中偏移，破开会把内容甩到屏幕左边去。 */
.pym2-page {
  position: relative;
  max-width: none; margin: 0;
  padding: 24px 0 72px;
}
/* 网格/柔光横向铺满整个视口：pym2 页在 1080 限宽的 #main 里，
   伪元素用 100vw + translateX(-50%) 破开容器，左右不再被截。 */
.pym2-page::before,
.pym2-page::after {
  width: calc(100 * var(--pg-vw, 1vw));
  left: 50%;
  right: auto;
  transform: translateX(-50%);
}
/* #main 自带 28/24/60 的内边距，会把内容挤窄 48px；拼音页自己管间距，去掉它。
   （只在这一页生效，其它页面不受影响） */
body:has(.pym2-page) #main { padding-top: 0; padding-bottom: 0; padding-left: 0; padding-right: 0; }
.pym2-page > * {
  position: relative; z-index: 1;
  width: 100%; max-width: 1080px;
  margin-left: auto; margin-right: auto;
}
/* 页面底色（指法页在 .kf-page 上写死的同一条渐变） */
body:has(.pym2-page) {
  background:
    radial-gradient(1100px 480px at 50% -120px, rgba(15,118,110,.10), transparent 70%),
    var(--bg-base, linear-gradient(180deg, #f4faf9 0%, #ffffff 300px));
}
.pym2-page::before {
  content: ''; position: absolute; top: 0; bottom: 0; pointer-events: none; z-index: 0;
  background: var(--bg-glow,
    radial-gradient(900px 560px at 12% -6%, rgba(20,179,166,.10), transparent 62%),
    radial-gradient(760px 500px at 90% -2%, rgba(56,189,248,.07), transparent 64%));
}
/* 网格：逐字抄指法页 —— 粗线 .42 / 细线 .2，遮罩 0%→46% 淡到 .8、94% 完全透明。
   （之前细线用了 .16、遮罩只淡到 .94，所以网格比指法页浅且"一整屏都清晰"，
   看着是两种网格。） */
.pym2-page::after {
  content: ''; position: absolute; top: 0; bottom: 0;
  pointer-events: none; z-index: 0;
  background-image: var(--bg-tex, none);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.9) 62%, rgba(0,0,0,.5) 100%);
  mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.9) 62%, rgba(0,0,0,.5) 100%);
}
/* 内容压在背景之上（宽度/居中规则见上面那条，这里只补层级） */

/* ③ 页头（深绿大条）对齐指法页 Hero：盒模型 + 字号 + kicker 小标题 */
.pym2-page .mode-bar {
  padding: 30px 40px 28px;
  margin: 0 0 26px;
  border-radius: 24px;
}
.pym2-page .mode-bar-txt { gap: 0; }
/* kicker：跟指法页 .kf-hero-kicker 同一套（青绿小字 + 两侧短横） */
.pym2-page .mode-bar-kicker {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 12px; font-weight: 800; letter-spacing: 0.2em;
  text-transform: uppercase; color: #7de8d6;
  margin-bottom: 10px;
}
.pym2-page .mode-bar-kicker::before,
.pym2-page .mode-bar-kicker::after {
  content: ''; width: 28px; height: 2px; border-radius: 1px; background: #7de8d6;
}
/* 标题和副标题的边距逐字抄指法页 .kf-hero-title / .kf-hero-sub：
   标题 margin 16/10、副标题 margin-top 9、副标题行高 normal。
   少任何一条，页头就会比指法页矮一截（之前差 24px 就是这个）。 */
.pym2-page .mode-bar-txt h1 {
  font-size: clamp(32px, calc(3.4 * var(--pg-vw, 1vw)), 42px); font-weight: 900;
  letter-spacing: 2px; line-height: 1.1;
  margin: 16px 0 10px;
}
.pym2-page .mode-bar-txt p {
  margin: 0; margin-top: 9px;
  font-size: 14.5px; line-height: normal; letter-spacing: 1px;
  color: rgba(255,255,255,.72);
}

/* ② 关掉图片浮动动画 + hover 放大（别一直晃、别一碰就鼓起来）。
   注意**不要**改成正方形：拼音页用的是横版图（560×494），强裁 1:1 会把人物
   上下切掉；指法页的图是竖版（810×1080），靠 cover 裁两侧才显得是方的。 */
.pym2-page .pym2-art-img { animation: none; }
.pym2-page .pym2-card:hover .pym2-art-img { animation: none; transform: none; filter: none; }
.pym2-page .pym2-card:hover { transform: none; }

/* ④ 卡片文字：居中 + 放大（跟指法页一致） */
.pym2-page .pym2-card-in {
  align-items: center; text-align: center;
  gap: 4px;
  padding: 16px 12px 18px;
}
.pym2-page .pym2-name { font-size: 20px; font-weight: 900; color: #0f3d36; letter-spacing: .5px; }
.pym2-page .pym2-desc { font-size: 13px; color: #7a8f8b; line-height: 1.5; min-height: 0; }
/* 段落标题已删，网格自己留出与页头之间的间距 */
.pym2-page .pym2-grid { margin-top: 26px; }


/* ===== 打字模式网格里的最后一格：基础练习三张竖排小卡 =====
   这一格跟旁边一张模式卡完全同尺寸（由 .pym2-grid 的列宽决定）。
   **外面不套卡片** —— 三张小卡直接等分铺满这一格。 */
.py-base-cell { position: relative; }
.py-base-stack {
  display: grid; grid-template-rows: repeat(3, 1fr);
  gap: 12px; height: 100%;
}
.py-base-mini {
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  gap: 5px; padding: 8px 10px;
  border-radius: 14px;
  /* 墨绿卡片（与首页 Hero、指法关卡卡的深色一致） */
  background: linear-gradient(150deg, #0c443d 0%, #093832 100%);
  border: 1px solid rgba(94,234,212,.22);
  box-shadow: 0 8px 20px rgba(7,59,53,.16), inset 0 1px 0 rgba(255,255,255,.06);
  cursor: pointer;
  text-align: center;
  transition: transform .22s cubic-bezier(.2,.7,.2,1),
              border-color .2s ease, box-shadow .2s ease;
}
/* 悬停联动（照 Uiverse 那段）：鼠标放上某张，它放大、**其余两张模糊并缩小** */
.py-base-stack:hover > .py-base-mini:not(:hover) {
  filter: blur(4px);
  transform: scale(.9);
  opacity: .55;
}
.py-base-stack > .py-base-mini {
  transition: transform .35s cubic-bezier(.2,.7,.2,1),
              filter .35s ease, opacity .35s ease,
              border-color .2s ease, box-shadow .25s ease;
}
.py-base-mini:hover {
  transform: scale(1.06);
  z-index: 2;
  border-color: rgba(94,234,212,.55);
  box-shadow: 0 16px 32px rgba(7,59,53,.28), inset 0 1px 0 rgba(255,255,255,.10);
}
@media (prefers-reduced-motion: reduce) {
  .py-base-stack:hover > .py-base-mini:not(:hover) { filter: none; transform: none; opacity: 1; }
  .py-base-stack > .py-base-mini:hover { transform: none; }
}
.py-base-mini-name {
  font-size: 19px; font-weight: 900; color: #ffffff;
  letter-spacing: .8px; line-height: 1.25;
}
.py-base-mini-desc {
  font-size: 12.5px; color: rgba(255,255,255,.72); line-height: 1.35;
}

/* 图片区比例 = 图片本身的比例（560×494 ≈ 1.134:1）。
   这样图片正好铺满、不留色边也不裁切，同时所有卡片等高、整行对齐。
   （之前用 contain + 固定高度会露出左右色条；用 cover 又会把人裁掉。） */
.pym2-page .pym2-card-art {
  aspect-ratio: 560 / 494;
  height: auto;
}
.pym2-page .pym2-art-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
}

@media (max-width: 560px) {
  .cf-card { padding: 36px 22px 28px; }
  .cf-title { font-size: 30px; letter-spacing: 2px; }
  .cf-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .cf-cell { padding: 13px 8px; }
  .cf-btns { gap: 8px; }
  .cf-btn { padding: 12px 22px; font-size: 14px; }
}
/* 关掉动效偏好时不做粒子/缩放 */
@media (prefers-reduced-motion: reduce) {
  .cf-card, .cf-layer, .cf-aura { animation: none; }
  .cf-star { animation: none; opacity: 1; transform: none; }
  .cf-canvas { display: none; }
}

/* ===== 选关页（指法/拼音入口，深绿 hero + 深绿 section 条 + 白卡）===== */
/* ===== 指法/拼音关卡选择页：浅绿精致风（与原深绿条 + 描边卡区分）===== */
/* 指法选择页外壳：保留顶栏（非 drill-mode）。
   背板必须铺满整个视口宽（与首页 lp-hero 一样用 100vw 破容器），否则左右漏白边。 */
.kf-page {
  min-height: calc(calc(100 * var(--pg-vh, 1vh)) - 64px);
  padding: 24px 24px 72px;      /* 选择页用的内边距 */
  width: 100%; margin-left: 0;
}
/* 词组等练习页虽然也挂了 .kf-page（为了背景），但布局由 .drill-page 自己管，
   不要选择页那圈内边距，否则内容会被挤窄。 */
/* 上下等量留白：内容不够高时在视口里自然居中，够高时也不贴死顶栏/底部 */
/* 顶部间距：顶栏（sticky，占 ~74px）已经撑开了，这里再补一点余量，
   让练习卡片和胶囊之间透气（原来 20px 偏挤）。 */
.kf-page.drill-page { padding: 30px 0 22px; }
/* 背景网格练习页铺满视口：去掉 #main 的上下内衬，避免顶栏下方/页面底部露白 */
body:has(.kf-page) #main { padding-top: 0 !important; padding-bottom: 0 !important; }
.kf-page .pinyin-flow-scroll { max-width: 1080px; margin: 0 auto; padding: 0; }
/* 词组等练习页：练习框在 1080 容器内水平居中；
   宽度与键盘可见边框（.keyboard-area = 960 - 2×14px 内衬 = 932）对齐，不比键盘宽 */
.kf-page .pinyin-flow { margin: 0 auto; width: calc(100% - 28px); max-width: 932px; }
.kf-index { padding: 0 0 20px; }
/* 指法关卡：每关一行（文字在左、键盘在右），键位看得更清、页面更透气 */
/* 两列：键盘填满卡片宽（键约 30px，接近拼音页 Hero 的观感），页面不至于太长 */
.kf-page .kf-sub-grid { grid-template-columns: repeat(2, 1fr); gap: 18px; }
/* 每关：胶囊按钮在上、整块键盘在下，两者同宽 */
.kf-page .kf-sub-card { flex-direction: column; align-items: stretch; gap: 9px; }
/* 卡片外壳：把标题和键盘收在同一个卡片里（内边距给内容留呼吸空间） */
.kf-page .kf-sub-card {
  padding: 12px 14px 14px; border-radius: 16px;
  background: linear-gradient(160deg, #0c443d 0%, #093832 100%);
  border: 1px solid rgba(94,234,212,.18);
  box-shadow: 0 8px 20px rgba(7,59,53,.16), inset 0 1px 0 rgba(255,255,255,.06);
  gap: 10px;
  transition: transform .22s cubic-bezier(.2,.7,.2,1), border-color .24s ease, box-shadow .24s ease;
}
.kf-page .kf-sub-card:hover {
  border-color: rgba(94,234,212,.44);
  box-shadow: 0 16px 32px rgba(7,59,53,.24), inset 0 1px 0 rgba(255,255,255,.09);
}
/* 窄屏：卡片改为竖排、键盘按容器宽自适应，避免超出。
   注意：拼音页有 `.pym2-hero-kb { display:none }` 的窄屏规则，这里要覆盖回来，
   否则指法页复用了同一个类名会导致键盘整个消失。 */
.kf-page .kf-sub-card .kf-mini-kb { display: flex; }
@media (max-width: 1080px) {
  .kf-page .kf-sub-grid { grid-template-columns: 1fr; }
  .kf-page .kf-sub-card { flex-direction: column; align-items: stretch; gap: 10px; }
  .kf-page .kf-sub-card .kf-mini-kb { flex: 1 1 auto; width: auto; display: flex; }
}
/* 深墨绿 Hero：与首页 lp-hero、拼音/中文选择页统一 */
.kf-hero {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 9px; padding: 40px 28px; margin-bottom: 28px;
  border-radius: 24px; text-align: center; color: #fff;
  background:
    radial-gradient(700px 340px at 84% -14%, rgba(45,212,191,.22), transparent 62%),
    linear-gradient(158deg, #073b35 0%, #0a5148 44%, #0e6f64 100%);
  box-shadow: 0 24px 54px rgba(7, 59, 53, .28);
}
.kf-hero::after {
  content: ''; position: absolute; width: 320px; height: 320px; border-radius: 50%;
  right: -100px; top: -150px;
  background: radial-gradient(circle, rgba(94, 234, 212, .24), transparent 65%);
}
.kf-hero-title { position: relative; font-size: 31px; font-weight: 900; color: #fff; letter-spacing: 1.5px; }
.kf-hero-sub { position: relative; font-size: 14.5px; color: rgba(255, 255, 255, .72); }
.kf-section { margin-bottom: 26px; }
.kf-section-head {
  display: flex; align-items: center; gap: 14px;
  padding: 14px 20px; margin-bottom: 14px;
  border-radius: 16px; background: #ffffff;
  border: 1px solid #e4eeeb;
  box-shadow: 0 4px 14px rgba(11,92,85,.05);
}
.kf-section-num {
  width: 34px; height: 34px; border-radius: 11px;
  background: linear-gradient(135deg, #14b3a6, #0b5c55);
  box-shadow: 0 6px 14px rgba(11,92,85,.22);
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 800; color: #FFFFFF; flex-shrink: 0;
}
.kf-section-head > div { display: flex; flex-direction: column; gap: 3px; text-align: left; }
.kf-section-head .kf-section-title { font-size: 17px; font-weight: 800; color: #0b5c55; line-height: 1.1; }
.kf-section-desc { font-size: 12.5px; color: #7a8f8b; line-height: 1.3; }
/* 拼音页的独立分区标题（无 head 包裹）*/
.kf-index > .kf-section-title {
  display: block; text-align: left;
  font-size: 15px; font-weight: 800; color: #0b5c55;
  background: #ffffff; border: 1px solid #e4eeeb; border-radius: 14px;
  box-shadow: 0 4px 14px rgba(11,92,85,.05);
  padding: 13px 20px; margin: 0 0 14px;
}
.kf-index > .kf-section-title::before {
  content: ''; display: inline-block; width: 4px; height: 15px; border-radius: 2px;
  background: linear-gradient(180deg,#2DD4BF,#0b5c55); margin-right: 10px; vertical-align: -2px;
}
.kf-sub-grid { display: grid; grid-template-columns: repeat(2, 1fr); grid-auto-rows: 1fr; gap: 14px; }
/* 注意：用 .kf-page 前缀提高优先级，否则会被上面的 .kf-page .kf-sub-grid（2 列）压掉，
   导致"基础练习"3 张卡排成 2+1、第三张孤零零掉到下一行 */
.kf-page .kf-sub-grid-3 { grid-template-columns: repeat(3, 1fr); gap: 12px; }
.kf-page .kf-sub-grid-qh { grid-template-columns: repeat(4, 1fr); gap: 10px; }
/* ===== 指法关卡卡：文字与键盘分离 =====
   键盘直接复用首页 .lp-kbd / .lp-key（自带玻璃机身与键帽样式），
   这里只补：卡片是纯可点区域（无外观）、键帽尺寸适配小卡、目标键高亮。 */
.kf-sub-card {
  position: relative;
  width: 100%; min-width: 0; box-sizing: border-box;
  display: flex; flex-direction: column; align-items: stretch;
  gap: 11px; cursor: pointer;
  transition: transform .22s cubic-bezier(.2,.7,.2,1);
}
.kf-sub-card:hover { transform: translateY(-4px); }
.kf-sub-card:active { transform: translateY(-1px) scale(.99); }

/* —— 文字：贴在键盘上方，朴素两行，不加任何底 —— */
.kf-sub-meta { padding: 0 2px; }
.kf-sub-keys { display: block; font-size: 15px; font-weight: 800; color: #0f3d36; letter-spacing: .4px; line-height: 1.2; }
.kf-sub-desc { display: block; font-size: 12px; color: #7b918c; line-height: 1.4; margin-top: 3px; }
.kf-sub-all .kf-sub-keys { font-size: 14px; }

/* —— 键盘：复用拼音练习页 Hero 的键帽（.pym2-hkb-key），画成一整块标准键盘 ——
   键宽用 --kw 按真键盘比例分配（空格 6.5 倍宽、Backspace 2 倍宽…）。 */
/* 键盘：包在卡片内。沿用顶部大键盘 (.lp-kbd) 的层次规格——
   半透明玻璃底 + 顶部内高光 + 大面积外投影，做出"浮在卡片上"的厚度感。 */
.kf-sub-card .kf-mini-kb {
  padding: 9px 11px; border-radius: 12px; gap: 5px;
  flex: 1 1 auto; width: 100%;
  background: rgba(3, 32, 28, .55);
  border: 1px solid rgba(255, 255, 255, .14);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .12),
    0 10px 22px rgba(0, 0, 0, .32),
    0 2px 4px rgba(0, 0, 0, .22);
  transition: box-shadow .24s ease, border-color .24s ease;
}
.kf-sub-card:hover .kf-mini-kb {
  border-color: rgba(255, 255, 255, .22);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .14),
    0 16px 30px rgba(0, 0, 0, .38),
    0 3px 6px rgba(0, 0, 0, .26);
}
/* hover 描边圈：按要求只圈「内层键盘」，卡片边缘不圈。
   键盘是深色机身，所以环用亮青（与 Hero 键盘同色，--ring-bright 在 style.css 的 :root）。
   键盘自带 1px 边框、又没有 overflow:hidden，所以 inset:-1px 把环推到边框外沿贴齐，
   否则环会内缩 1px、外面留一条浅色细边。 */
.kf-sub-card .kf-mini-kb { position: relative; }
.kf-sub-card .kf-mini-kb::after {
  content: '';
  position: absolute; inset: -1px;
  border-radius: inherit;
  padding: 2px;                    /* 环的粗细 */
  background: conic-gradient(var(--ring-bright) 0deg var(--lp-ring), transparent var(--lp-ring) 360deg);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  z-index: 3;                      /* 压在键帽之上 */
  transition: --lp-ring 800ms ease-in-out;
}
.kf-sub-card:hover .kf-mini-kb::after { --lp-ring: 360deg; }
@media (prefers-reduced-motion: reduce) {
  .kf-sub-card .kf-mini-kb::after { transition: none; }
}
.kf-sub-card .pym2-hkb-row { gap: 4px; }
.kf-sub-card .pym2-hkb-key {
  flex: var(--kw, 1) 1 0; min-width: 0; width: auto;
  /* 键高按卡片键盘的 16 列宽算：531/16≈30px 宽，高取 30px 保持正方 */
  height: 30px;
  font-size: 11px; border-radius: 6px; letter-spacing: .2px;
  /* 键帽厚度：与顶部大键盘同款（底部实心投影 + 顶部内高光），做出立体键感 */
  box-shadow:
    0 3px 0 rgba(0, 0, 0, .38),
    inset 0 1px 0 rgba(255, 255, 255, .14);
}
/* 悬浮卡片时键帽微微抬起 */
.kf-sub-card:hover .pym2-hkb-key { box-shadow: 0 4px 0 rgba(0,0,0,.40), inset 0 1px 0 rgba(255,255,255,.18); }
/* 悬停单颗键帽：抬起更明显 */
.kf-sub-card .pym2-hkb-key:hover {
  transform: translateY(-1.5px);
  box-shadow: 0 5px 0 rgba(0,0,0,.42), 0 0 12px rgba(94,234,212,.28), inset 0 1px 0 rgba(255,255,255,.2);
}
/* 功能键（Tab/Caps/Shift/Enter/⌫/Ctrl/Alt/Space）：字更小，避免撑破键帽 */
.kf-sub-card .pym2-hkb-key.func { font-size: 7px; color: rgba(255,255,255,.5); }
.kf-sub-card .pym2-hkb-bump { bottom: 3px; width: 8px; height: 1.5px; }
/* 目标键：亮青实心 + 发光，同时保留键帽厚度投影 */
.kf-sub-card .pym2-hkb-key.on {
  background: linear-gradient(180deg, #5eead4 0%, #2dd4bf 100%);
  color: #04302b; border-color: #7de8d6;
  box-shadow:
    0 3px 0 rgba(11, 92, 85, .5),
    0 0 0 3px rgba(94, 234, 212, .18),
    0 4px 12px rgba(94, 234, 212, .42),
    inset 0 1px 0 rgba(255, 255, 255, .5);
}
/* 目标键呼吸：错峰（delay 由 JS 写入） */
.kf-sub-card .pym2-hkb-key.on { animation: kfKeyPulse 2.6s ease-in-out infinite; }
@keyframes kfKeyPulse { 0%,100% { transform: translateY(0); } 50% { transform: translateY(-1.5px); } }
/* 鼠标划过单键：轻微抬起（沿用拼音页 near 观感） */
.kf-sub-card .pym2-hkb-key:hover {
  background: rgba(94,234,212,.18); color: #cffaf4;
  border-color: rgba(94,234,212,.5); transform: translateY(-1.5px);
}

/* ===== 指法页背景：白底 + 网格 + 柔光（只改这里，卡片/键盘/Hero 保持原样）=====
   ① 底色   极浅的青白，比纯白柔和
   ② 网格   青灰细网格，向下淡出，不抢内容
   ③ 柔光   左上青绿 + 右上淡蓝，给平面一点体积感 */
.kf-page {
  position: relative;
  /* overflow 必须 visible：背景伪元素要往上伸出 62px 盖住透明顶栏那一条
     （见下面的 ::before/::after）。overflow:hidden 会把伸出去的部分裁掉 → 顶部露白。 */
  overflow: visible;
  min-height: calc(calc(100 * var(--pg-vh, 1vh)) - 64px);
  /* 铺满视口（破开 #main 的限宽），左右不留白 */
  width: 100%; margin-left: 0;
  background: var(--bg-base, linear-gradient(180deg, #f4faf9 0%, #ffffff 300px));
}
/* ③ 柔光 */
/* 顶栏区那 62px 也要有背景：
   顶栏本体是透明的（底色由内部悬浮胶囊承担），而 .kf-page 从顶栏下方才开始，
   伪元素 top:0 是相对 .kf-page 定位 → 上面 62px 会露白。
   这里把两层背景都向上延伸 62px（top 负值 + 高度补回来），盖住顶栏那一条。
   62px 用变量 --topbar-h 表达，改了顶栏高度这里跟着变。 */
.kf-page::before {
  content: ''; position: absolute; left: 0; right: 0; top: calc(-1 * var(--topbar-h, 62px)); bottom: 0;
  pointer-events: none;
  background: var(--bg-glow,
    radial-gradient(900px 560px at 12% -6%, rgba(20,179,166,.10), transparent 62%),
    radial-gradient(760px 500px at 90% -2%, rgba(56,189,248,.07), transparent 64%));
}
/* ② 网格：青绿细线（双层：40px 细网格 + 160px 粗网格），铺满整页、往下略淡 */
.kf-page::after {
  content: ''; position: absolute; left: 0; right: 0;
  top: calc(-1 * var(--topbar-h, 62px));
  bottom: 0; height: auto;
  pointer-events: none;
  /* 必须读变量（默认才是写死的网格），否则会盖掉「背景样式」选出来的纹理 */
  background-image: var(--bg-tex,
    /* 粗网格（每 4 格一条，颜色更深，做骨架） */
    linear-gradient(rgba(15,118,110,.42) 1px, transparent 1px),
    linear-gradient(90deg, rgba(15,118,110,.42) 1px, transparent 1px),
    /* 细网格（填充） */
    linear-gradient(rgba(15,118,110,.20) 1px, transparent 1px),
    linear-gradient(90deg, rgba(15,118,110,.20) 1px, transparent 1px));
  background-size: var(--bg-tex-size, 160px 160px, 160px 160px, 40px 40px, 40px 40px);
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.9) 62%, rgba(0,0,0,.5) 100%);
          mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.9) 62%, rgba(0,0,0,.5) 100%);
}
/* 拼音练习页：网格铺满整页（原规则高度写死 900px，页面更高时下面就空了）。
   **overflow 必须放开** —— 这页会被 fitToViewport 整体缩放，
   缩放后元素的渲染框变小，overflow:hidden 会把 100vw 宽的网格裁掉一圈
   （表现就是左右两侧没有网格）。只作用于 #pyPage，不动其他页面。 */
#pyPage.kf-page { overflow: visible; }
#pyPage.kf-page::after {
  bottom: 0; height: auto;
  -webkit-mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.9) 62%, rgba(0,0,0,.5) 100%);
          mask-image: linear-gradient(180deg, #000 0%, rgba(0,0,0,.9) 62%, rgba(0,0,0,.5) 100%);
}
/* 指法总览页（非 drill-mode，在 1080 限宽容器里）：网格/柔光同样通栏，
   练习页（drill-mode 时 #main 已放全宽）不受影响。 */
body:not(.drill-mode) .kf-page::before,
body:not(.drill-mode) .kf-page::after {
  width: calc(100 * var(--pg-vw, 1vw));
  left: 50%; right: auto;
  transform: translateX(-50%);
}
.kf-page > * { position: relative; z-index: 1; }
/* —— Hero：保持原来的深色外观（不透明，避免白底上发灰）—— */
.kf-page .kf-hero {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center;
  gap: 26px;
  padding: 40px 44px 44px; margin-bottom: 30px;
  border-radius: 24px;
  background: linear-gradient(160deg, #0c443d 0%, #093832 100%);
  border: 1px solid rgba(94,234,212,.16);
  box-shadow: inset 0 1px 0 rgba(255,255,255,.08), 0 20px 46px rgba(0,0,0,.22);
}
.kf-page .kf-hero-txt { text-align: center; }
.kf-page .kf-hero::after { display: none; }
.kf-hero-spot { position: absolute; inset: 0; pointer-events: none; transition: background .16s ease-out; }
.kf-hero-txt { position: relative; z-index: 2; flex: none; min-width: 0; }
.kf-hero-kicker {
  display: inline-flex; align-items: center; gap: 10px;
  font-size: 12px; letter-spacing: 3px; font-weight: 700;
  color: #7de8d6; text-transform: uppercase;
}
.kf-hero-kicker::before { content: ''; width: 28px; height: 2px; border-radius: 2px; background: #5eead4; }
.kf-page .kf-hero-title {
  font-size: 40px; font-weight: 900; color: #fff;
  letter-spacing: 3px; line-height: 1.15;
  margin: 16px 0 10px;
  text-shadow: 0 0 34px rgba(94,234,212,.30);
}
.kf-page .kf-hero-sub { font-size: 14.5px; color: rgba(255,255,255,.66); letter-spacing: 1px; }
/* Hero 大键盘：深色键盘浮在浅色 Hero 上，对比强烈 */
.kf-hero-kb { position: relative; z-index: 2; width: 100%; max-width: 760px; }
.kf-hero-kb .lp-kbd { width: 100%; }
.kf-hero-kb .lp-key { height: clamp(24px, calc(2.9 * var(--pg-vw, 1vw)), 40px); }
/* 鼠标掠过时被 JS 标记为 .hot 的那颗键 */
.kf-hero-kb .lp-key.hot {
  background: #5eead4 !important;
  color: #07413b !important;
  border-color: #5eead4 !important;
  transform: translateY(3px);
  box-shadow: 0 0 22px rgba(94,234,212,.6) !important;
}

/* 窄屏：Hero 装饰键盘收起（与拼音页 Hero 同样处理），标题占满 */
@media (max-width: 1080px) {
  .kf-page .kf-hero { padding: 30px 26px; gap: 0; }
  .kf-hero-kb { display: none; }
  .kf-page .kf-hero-title { font-size: 31px; letter-spacing: 2px; }
}
@media (max-width: 760px) {
  .kf-page .kf-hero { padding: 26px 20px; }
  .kf-page .kf-hero-title { font-size: 26px; }
  .kf-section-num { font-size: 24px; }
  .kf-page .kf-section-head { padding: 13px 16px; gap: 13px; }
  .kf-page .kf-section-head .kf-section-title { font-size: 16px; }
  .kf-section-count { padding: 4px 10px; font-size: 11px; }
}

/* —— 大关标题：大号描边序号 + 标题 + 关卡数标签（恢复深色玻璃条）—— */
.kf-page .kf-section-head {
  display: flex; align-items: center; gap: 18px;
  padding: 16px 22px; margin-bottom: 14px;
  border-radius: 16px;
  /* 不透明深墨绿（白底上不发灰） */
  background: linear-gradient(120deg, #0e4a43 0%, #0a3d37 100%);
  border: 1px solid rgba(94,234,212,.18);
  box-shadow: 0 8px 20px rgba(7,59,53,.18), inset 0 1px 0 rgba(255,255,255,.08);
}
.kf-section-num {
  flex: none;
  font-size: 30px; font-weight: 900; line-height: 1;
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(94,234,212,.65);
  font-variant-numeric: tabular-nums;
}
.kf-section-txt { flex: 1; min-width: 0; }
.kf-page .kf-section-head .kf-section-title {
  font-size: 18px; font-weight: 800; color: #fff; letter-spacing: .8px; line-height: 1.2;
}
.kf-page .kf-section-desc { font-size: 12.5px; color: rgba(255,255,255,.55); line-height: 1.3; margin-top: 2px; }
.kf-section-count {
  flex: none; font-size: 12px; font-weight: 700;
  color: #7de8d6; background: rgba(94,234,212,.12);
  border: 1px solid rgba(94,234,212,.28);
  border-radius: 999px; padding: 5px 13px;
}

/* —— 卡片标题：做成一个胶囊按钮（主标题 + 副说明）—— */
/* 胶囊按钮：与下方键盘同宽（整行），包住标题与说明文字。
   用不透明深墨绿底 + 亮青边，保证深底上文字清晰可读。 */
/* 标题区：去掉胶囊边框，改为「标题 + 一条两端渐隐的分隔线」 */
.kf-page .kf-sub-card .kf-sub-meta {
  width: 100%; flex: none;
  display: flex; flex-direction: column; align-items: center;
  gap: 2px;
  padding: 2px 0 10px;
  background: none; border: none; box-shadow: none;
  position: relative;
}
/* 分隔线：中间实、两端渐隐（用渐变做，不是实线） */
.kf-page .kf-sub-card .kf-sub-meta::after {
  content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
  background: linear-gradient(90deg,
    rgba(255,255,255,0) 0%,
    rgba(255,255,255,.55) 22%,
    rgba(255,255,255,.85) 50%,
    rgba(255,255,255,.55) 78%,
    rgba(255,255,255,0) 100%);
}
/* 悬浮卡片时，分隔线整体提亮 */
.kf-page .kf-sub-card:hover .kf-sub-meta::after {
  background: linear-gradient(90deg,
    rgba(94,234,212,0) 0%,
    rgba(94,234,212,.7) 22%,
    rgba(94,234,212,1) 50%,
    rgba(94,234,212,.7) 78%,
    rgba(94,234,212,0) 100%);
}
.kf-page .kf-sub-keys { color: #ffffff; font-size: 15px; font-weight: 900; letter-spacing: .6px; line-height: 1.25; }
.kf-page .kf-sub-desc { color: rgba(255, 255, 255, .72); font-size: 11px; line-height: 1.25; }
/* 全部基准键那张名字长，稍收一号 */
.kf-page .kf-sub-all .kf-sub-keys { font-size: 13.5px; }

/* —— 基础练习：3 张大卡（声母/韵母/整体认读）——
   比强化卡更有份量：更大的字、左侧一道主题色竖条、悬停时上浮发光 */
/* 基础练习的 3 张卡：加深底色 + 左侧竖条 + 悬停发光，
   与右侧的强化小卡拉开层级（这是"主入口"，该更重） */
.kf-page .kf-sub-grid-3 .kf-sub-card {
  position: relative; overflow: hidden;
  padding: 20px 20px 18px;
  border-radius: 16px;
  gap: 4px;
  background: linear-gradient(150deg, #0c443d 0%, #093832 100%);
  border: 1px solid rgba(94,234,212,.22);
}
/* 左侧主题色竖条 */
.kf-page .kf-sub-grid-3 .kf-sub-card::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 4px;
  background: linear-gradient(180deg, #5eead4, #0F766E);
  transition: width .22s ease;
}
.kf-page .kf-sub-grid-3 .kf-sub-card:hover::before { width: 6px; }
.kf-page .kf-sub-grid-3 .kf-sub-card:hover {
  border-color: rgba(94,234,212,.55);
  transform: translateY(-3px);
  box-shadow: 0 18px 38px rgba(7,59,53,.30), inset 0 1px 0 rgba(255,255,255,.10);
}
.kf-page .kf-sub-grid-3 .kf-sub-keys { font-size: 18px; letter-spacing: 1px; line-height: 1.2; }
.kf-page .kf-sub-grid-3 .kf-sub-desc { font-size: 12px; margin-top: 6px; }

/* —— 声母强化：4 列小卡 ——
   23 关若都用深绿大块会很压抑。改成：深绿只集中在左侧的声母徽章上，
   右侧两行文字（X 强化 / 拼音串）。扫一眼就能定位到要找的声母 */
/* 注意：这里是**浅色页面底**（白/浅绿），不是深色 Hero。
   所以卡片用白底、文字用深色 —— 之前照搬深色卡的白色文字，白字白底完全看不见 */
.kf-page .kf-sub-grid-qh .kf-sub-card.kf-qh-card {
  display: flex; flex-direction: row; align-items: center; gap: 11px;
  padding: 11px 13px;
  border-radius: 13px;
  background: #ffffff;
  border: 1px solid #dfeeea;
  box-shadow: 0 2px 6px rgba(15,118,110,.05);
  cursor: pointer;
}
.kf-page .kf-sub-grid-qh .kf-sub-card.kf-qh-card:hover {
  background: #f4fbfa;
  border-color: rgba(15,118,110,.45);
  transform: translateY(-2px);
  box-shadow: 0 12px 24px rgba(15,118,110,.16);
}
/* 声母徽章：卡片里唯一的深色重块 */
.kf-page .kf-qh-badge {
  flex: none;
  min-width: 36px; height: 36px; padding: 0 6px;
  border-radius: 10px;
  display: flex; align-items: center; justify-content: center;
  font-size: 15px; font-weight: 900; letter-spacing: .3px;
  color: #eafffb;
  background: linear-gradient(150deg, #12857c 0%, #0a4a43 100%);
  box-shadow: 0 3px 8px rgba(0,0,0,.24), inset 0 1px 0 rgba(255,255,255,.14);
}
.kf-page .kf-qh-txt { flex: 1; min-width: 0; }
.kf-page .kf-qh-name {
  font-size: 13px; font-weight: 800; color: #0f3d36;
  letter-spacing: .3px; line-height: 1.2;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kf-page .kf-qh-desc {
  margin-top: 3px;
  font-size: 10.5px; line-height: 1.3;
  color: #8aa19d;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ===== 拼音基础练习：音节掉落队列（照搬 pinyin/practice-base）===== */
.drill-header { text-align: center; margin: calc(1.6 * var(--pg-vw, 1vw)) auto calc(0.4 * var(--pg-vw, 1vw)); max-width: 960px; width: 100%; padding: 0 calc(2 * var(--pg-vw, 1vw)); box-sizing: border-box; }
.game-hint {
  display: inline-block; font-size: calc(2.7 * var(--pg-vw, 1vw)); color: #FFFFFF; font-weight: 700;
  padding: calc(0.9 * var(--pg-vw, 1vw)) calc(3.4 * var(--pg-vw, 1vw)); border-radius: calc(13.33 * var(--pg-vw, 1vw));
  background: linear-gradient(135deg, #14B8A6 0%, #0F766E 60%, #0C615B 100%);
  box-shadow: 0 4px 14px rgba(15,118,110,0.35);
}
.drill-stack {
  position: relative; overflow: hidden; box-sizing: border-box;
  height: calc(5 * calc(13.6 * var(--pg-vw, 1vw)) - calc(2 * var(--pg-vw, 1vw)) + 2 * calc(1.4 * var(--pg-vw, 1vw)));
  margin: calc(1.4 * var(--pg-vw, 1vw)) auto 0; padding: calc(1.4 * var(--pg-vw, 1vw)) calc(2.4 * var(--pg-vw, 1vw));
  background: #FFFFFF; border: calc(0.2 * var(--pg-vw, 1vw)) solid #0F766E; border-radius: 12px;
  box-shadow: 0 2px 10px rgba(15,118,110,0.10);
}

/* 练习区白底大卡片：宽度对齐**按钮和键盘的可见区**（932），
   不是它们的容器（960）。容器左右各有 2vw 内边距，卡片是实体框，
   所以要直接取 932 才跟按钮两端齐平。 */
.py-stage-card {
  padding: calc(1.6 * var(--pg-vw, 1vw)) calc(2 * var(--pg-vw, 1vw));
  width: 100%;
  max-width: 932px;
  box-sizing: border-box;
  margin: 0 auto;
  background: #FFFFFF;
  border: 1px solid var(--skin-main, #0F766E);
  border-radius: 14px;
  box-shadow: 0 2px 10px var(--skin-shadow, rgba(15,118,110,.10));
  container-type: inline-size;
}

/* ===== 声母/韵母/整体认读：照搬词组练习的 .pf-* 结构 =====
   DOM：.pf-item > .pf-units > .pf-unit > (.pf-pinyin + .pf-mtzg)。
   尺寸一律用 **cqw**（相对卡片宽度），不用 vw —— 这样卡片缩的时候
   字和格一起缩，永远不错位、不撑变形。 */
.pf-pyonly { display: block; font-size: 5cqw; height: auto; }
/* .pf-item 在这里是**整行**（包 6 个单元），不是词组练习里的"一个词"，
   所以要把「当前输入框」那圈绿边框和底色去掉（否则会框住整行）。 */
.pf-pyonly .pf-item,
.pf-pyonly .pf-item.current { width: 100%; padding: 0.6cqw 0; box-sizing: border-box;
  border: 0 !important; background: none !important;
  /* 关键：让 .pf-units 撑满整行。默认 align-items:center 会把子元素按内容收缩，
     结果 units 只有 322px 而卡片内宽 401px → 两边空一大块、单元全挤在中间。 */
  align-items: stretch; }
/* 每排 5 个，均匀铺满整行。
   高度受约束：整页一旦超过视口，fitToViewport 会把整页缩到 0.45，
   连背景网格一起缩小（那才是"背景被截掉"的真因）。所以间距要节制。 */
.pf-pyonly .pf-units {
  gap: 2cqw;
  font-size: 7cqw;
  justify-content: space-between;
  padding: 0 2.4cqw;
}
/* 单元内：拼音与米字格的间距 */
.pf-pyonly .pf-unit { gap: 3cqw; flex: 0 0 auto; }
.pf-pyonly .pf-mtzg { width: 11cqw; height: 11cqw; }
.pf-pyonly .pf-mtzg-char { font-size: 7.4cqw; }
/* 两排之间：够分开即可，不要撑高 */
.pf-pyonly .pf-item { padding: 1.2cqw 0; }

/* 单元状态：把词组练习那套 .pf-item.done / .pf-item.current 的规则
   **原样搬**到 .pf-unit（我们的 .pf-item 是整行，状态挂在单元上）。
   注意高亮块（.pf-pchar-hit::before，浅青底）必须保留 —— 打过的字母下面那块。 */
.pf-pyonly .pf-unit.done .pf-pchar-hit,
.pf-pyonly .pf-unit.current .pf-pchar-hit { background: transparent; }
.pf-pyonly .pf-unit.done .pf-pchar-hit::before,
.pf-pyonly .pf-unit.current .pf-pchar-hit::before {
  content: '';
  position: absolute; inset: 0;
  background: var(--pc-py-hit, #E6F4F1);
  z-index: 0;
}
.pf-pyonly .pf-unit.done .pf-pchar-text,
.pf-pyonly .pf-unit.current .pf-pchar-hit .pf-pchar-text { color: var(--pc-py-typed, #0C615B) !important; }
.pf-pyonly .pf-unit.current .pf-pchar-text,
.pf-pyonly .pf-unit.upcoming .pf-pchar-text { color: var(--pc-py-pending, #94A3B8); }
.pf-pyonly .pf-unit.done .pf-mtzg-char { color: #334155; }
.drill-row {
  position: absolute; left: calc(1 * var(--pg-vw, 1vw)); right: calc(1 * var(--pg-vw, 1vw));
  top: calc(calc(1.4 * var(--pg-vw, 1vw)) + var(--slot, 0) * calc(13.6 * var(--pg-vw, 1vw)));
  height: calc(11.6 * var(--pg-vw, 1vw));
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; justify-items: center;
  padding: 0 calc(2.4 * var(--pg-vw, 1vw)); box-sizing: border-box;
  border-radius: calc(2.13 * var(--pg-vw, 1vw));
  background: #0F766E; border: calc(0.2 * var(--pg-vw, 1vw)) solid #0C615B;
  transition: top 0.32s ease;
  animation: drillDrop 0.3s ease backwards;
  animation-delay: calc(var(--slot, 0) * 45ms);
}
.drill-row.upcoming { background: #FFFFFF; border-color: #0F766E; box-shadow: 0 2px 6px rgba(15,118,110,0.14); }
.drill-row.no-anim { animation: none; }
.drill-row.leaving { animation: drillVanish 0.16s ease-in forwards; }
@keyframes drillVanish {
  0% { transform: scale(1); opacity: 1; }
  100% { transform: scale(0.84) translateY(calc(1.6 * var(--pg-vw, 1vw))); opacity: 0; }
}
@keyframes drillDrop {
  0% { transform: translateY(calc(-22 * var(--pg-vw, 1vw))); opacity: 0; }
  40% { opacity: 1; }
  100% { transform: translateY(0); opacity: 1; }
}
.drill-syllable { position: relative; display: flex; align-items: center; justify-content: center; gap: calc(0.4 * var(--pg-vw, 1vw)); }
.drill-syl-char { position: relative; display: flex; align-items: center; justify-content: center; }
.drill-syl-text { font-size: calc(4.4 * var(--pg-vw, 1vw)); font-weight: 800; color: #FFFFFF; line-height: 1; }
.drill-row.upcoming .drill-syl-text { color: #0F766E; }
.drill-syl-text.is-hit { color: #5EEAD4; }
.drill-syl-cursor {
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: calc(-1.4 * var(--pg-vw, 1vw)); width: calc(3.4 * var(--pg-vw, 1vw)); height: calc(0.55 * var(--pg-vw, 1vw));
  background: #EF4444; border-radius: calc(0.2 * var(--pg-vw, 1vw));
  animation: pfCursorBlink 1s step-end infinite;
}
.drill-row-ex { font-size: calc(4.8 * var(--pg-vw, 1vw)); font-weight: 800; color: #FFFFFF; line-height: 1; }
.drill-row.upcoming .drill-row-ex { color: #0F766E; }
.drill-row-upper {
  font-size: calc(4.4 * var(--pg-vw, 1vw)); font-weight: 800; color: #FFFFFF; line-height: 1;
  width: calc(5.6 * var(--pg-vw, 1vw)); display: flex; align-items: center; justify-content: center;
  white-space: nowrap; overflow: hidden;
}
.drill-row.upcoming .drill-row-upper { color: #7C3AED; }
.drill-hidden { visibility: hidden; }

/* ===== 拼音声母强化：3×4 词格（照搬 pinyin/practice）===== */
.qh-flow {
  display: flex; flex-direction: column; gap: calc(1.4 * var(--pg-vw, 1vw));
  margin: calc(1.4 * var(--pg-vw, 1vw)) auto 0; padding: calc(1.8 * var(--pg-vw, 1vw)) calc(1.6 * var(--pg-vw, 1vw)); box-sizing: border-box;
  background: #FFFFFF; border: calc(0.2 * var(--pg-vw, 1vw)) solid #0F766E; border-radius: calc(2.4 * var(--pg-vw, 1vw));
  box-shadow: 0 2px 10px rgba(15,118,110,0.10);
}
.pf-row { display: flex; gap: calc(1.4 * var(--pg-vw, 1vw)); width: 100%; flex-wrap: nowrap; }
.qh-flow .pf-item {
  flex: 1 1 0; min-width: 0;
  flex-direction: column; align-items: center; justify-content: center;
  gap: calc(1.1 * var(--pg-vw, 1vw)); padding: calc(0.9 * var(--pg-vw, 1vw)) calc(1.2 * var(--pg-vw, 1vw));
  border: calc(0.2 * var(--pg-vw, 1vw)) solid transparent; border-radius: calc(1 * var(--pg-vw, 1vw));
  background: #FFFFFF;
}
.qh-flow .pf-item.current { border-color: #0F766E; }
.pf-chars {
  display: flex; align-items: flex-start; justify-content: center;
  gap: calc(0.2 * var(--pg-vw, 1vw)); height: calc(9.4 * var(--pg-vw, 1vw)); overflow: hidden; width: 100%;
}
.pf-char { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; flex: 0 0 auto; }
.pf-char-text { font-size: calc(5.5 * var(--pg-vw, 1vw)); font-weight: 600; color: #94A3B8; line-height: 1.1; }
.pf-char.pf-char-hit { background: #E6F4F1; border-radius: calc(0.5 * var(--pg-vw, 1vw)); }
.pf-char-hit .pf-char-text { color: #0C615B; }
.qh-flow .pf-item.done .pf-char-text { color: #0C615B; }
.qh-flow .pf-char-cursor { top: calc(6.6 * var(--pg-vw, 1vw)); width: calc(2.8 * var(--pg-vw, 1vw)); height: calc(0.5 * var(--pg-vw, 1vw)); }
.pf-ex { font-size: calc(6.6 * var(--pg-vw, 1vw)); font-weight: 800; color: #334155; line-height: 1; }

/* ===== 450px 断点 ===== */
@media (min-width: 450px) {
  /* 中文 flow / 中文古诗标尺 */
  .pf-item-flow { font-size: calc(4.4 * var(--pg-vw, 1vw)); }
  .pf-item-poem { height: calc(25.64 * calc(4.4 * var(--pg-vw, 1vw))); }
  .pf-units-poem { font-size: calc(4.4 * var(--pg-vw, 1vw)); }

  /* 拼音古诗标尺与窗口变量 */
  .pf-poem-window { font-size: calc(5.6 * var(--pg-vw, 1vw)); --pf-rowgap: calc(2.4 * var(--pg-vw, 1vw)); --pf-pitch: calc(30.4 * var(--pg-vw, 1vw)); }
  .pf-poem-row { font-size: calc(5.6 * var(--pg-vw, 1vw)); }
  .flow-single .pf-poem-row .pf-mtzg { width: 2.7206em; height: 2.7206em; border-width: calc(0.4 * var(--pg-vw, 1vw)); }
  .flow-single .pf-poem-row .pf-mtzg-h,
  .flow-single .pf-poem-row .pf-mtzg-v,
  .flow-single .pf-poem-row .pf-mtzg-d1,
  .flow-single .pf-poem-row .pf-mtzg-d2 { border-width: calc(0.24 * var(--pg-vw, 1vw)); }
  .flow-single .pf-poem-row .pf-mtzg-char { font-size: 2.0588em; }

  /* 拼音六字两排标尺 */
  .flow-single .pf-two-row .pf-units { font-size: calc(6.8 * var(--pg-vw, 1vw)); }

  /* 指法 cube 450 档（保持 vw 比例，数值同基准档）*/
  .seq-bar { margin: 10px auto 0; }
  .seq-track { gap: calc(3.5 * var(--pg-vw, 1vw)); padding: calc(3.2 * var(--pg-vw, 1vw)) calc(3 * var(--pg-vw, 1vw)); border-radius: calc(1.875 * var(--pg-vw, 1vw)); }
  .seq-row { gap: calc(1.8 * var(--pg-vw, 1vw)); }

  /* 选关页 */
  .kf-hero { padding: 26px 20px; margin-bottom: 22px; }
  .kf-hero-title { font-size: 25px; }
  .kf-hero-sub { font-size: 14px; }
  .kf-section { margin-bottom: 22px; }
  .kf-section-head { gap: 12px; padding: 13px 16px; margin-bottom: 12px; border-radius: 14px; }
  .kf-section-num { width: 30px; height: 30px; font-size: 14px; border-radius: 10px; }
  .kf-section-head .kf-section-title { font-size: 16px; }
  .kf-section-desc { font-size: 12px; }
  .kf-sub-grid-qh { grid-template-columns: repeat(6, 1fr); gap: 8px; }

  /* 拼音掉落队列 450 档 */
  .drill-header { margin: calc(1.4 * var(--pg-vw, 1vw)) calc(1 * var(--pg-vw, 1vw)) calc(0.9 * var(--pg-vw, 1vw)); }
  .game-hint { font-size: calc(1.4 * var(--pg-vw, 1vw)); margin-top: calc(0.5 * var(--pg-vw, 1vw)); }
  .drill-stack { height: calc(5 * calc(7.8 * var(--pg-vw, 1vw)) - calc(1.2 * var(--pg-vw, 1vw)) + 2 * calc(1.2 * var(--pg-vw, 1vw))); padding: calc(1.2 * var(--pg-vw, 1vw)) calc(2 * var(--pg-vw, 1vw)); }
  .drill-row {
    left: calc(0.8 * var(--pg-vw, 1vw)); right: calc(0.8 * var(--pg-vw, 1vw));
    top: calc(calc(1.2 * var(--pg-vw, 1vw)) + var(--slot, 0) * calc(7.8 * var(--pg-vw, 1vw)));
    height: calc(6.6 * var(--pg-vw, 1vw)); padding: 0 calc(2.2 * var(--pg-vw, 1vw)); border-radius: 11px;
  }
  .drill-syl-text { font-size: calc(3.2 * var(--pg-vw, 1vw)); }
  .drill-syl-cursor { bottom: calc(-0.7 * var(--pg-vw, 1vw)); width: calc(2 * var(--pg-vw, 1vw)); height: calc(0.3 * var(--pg-vw, 1vw)); border-radius: calc(0.15 * var(--pg-vw, 1vw)); }
  @keyframes drillDrop {
    0% { transform: translateY(calc(-14 * var(--pg-vw, 1vw))); opacity: 0; }
    40% { opacity: 1; }
    100% { transform: translateY(0); opacity: 1; }
  }
  .drill-row-ex { font-size: calc(2.8 * var(--pg-vw, 1vw)); }
  .drill-row-upper { font-size: calc(2.4 * var(--pg-vw, 1vw)); width: calc(3.2 * var(--pg-vw, 1vw)); }

  /* 拼音强化 450 档 */
  .pf-chars { height: calc(5 * var(--pg-vw, 1vw)); }
  .pf-char-text { font-size: calc(2.9 * var(--pg-vw, 1vw)); }
  .qh-flow .pf-char-cursor { top: calc(3.45 * var(--pg-vw, 1vw)); width: calc(1.5 * var(--pg-vw, 1vw)); height: calc(0.26 * var(--pg-vw, 1vw)); }
  .pf-ex { font-size: calc(3.3 * var(--pg-vw, 1vw)); }
}

/* ===== 960px 断点：px 锁定（与小程序电脑版一致）===== */
@media (min-width: 960px) {
  /* 中文 flow / 中文古诗 */
  .pf-item-flow { font-size: 44px; padding-top: 0; padding-bottom: 0; }
  .pf-item-poem { height: 1128px; padding-top: 0; padding-bottom: 0; }
  .pf-units-poem { font-size: 44px; }

  /* 拼音古诗 */
  .pf-poem-window { font-size: 44px; --pf-rowgap: 23px; --pf-pitch: 231.32px; }
  .pf-poem-row { font-size: 44px; gap: 1.25em; }
  .flow-single .pf-poem-row .pf-mtzg { width: 108px; height: 108px; border-width: 2px; }
  .flow-single .pf-poem-row .pf-mtzg-h,
  .flow-single .pf-poem-row .pf-mtzg-v,
  .flow-single .pf-poem-row .pf-mtzg-d1,
  .flow-single .pf-poem-row .pf-mtzg-d2 { border-width: 1.3px; }
  .flow-single .pf-poem-row .pf-mtzg-char { font-size: 82px; }

  /* 拼音六字两排 */
  .flow-single .pf-two-row .pf-units { font-size: 44px; gap: 1.25em; }

  /* 指法 cube 锁定 125px */
  .seq-bar { margin: 12px auto 0; }
  .seq-track { gap: 32px; padding: 28px; border-width: 2px; border-radius: 18px; }
  .seq-row { gap: 18px; }
  .seq-key { width: 125px; height: 125px; perspective: 1000px; }
  .cube { width: 125px; height: 125px; }
  /* 同样外扩 2px，消掉旋转接缝 */
  .face { top: -1px; left: -1px; width: 127px; height: 127px; font-size: 52px; }
  .face-front { border-width: 3px; transform: translateZ(62.5px); }
  .face-back { transform: rotateY(180deg) translateZ(62.5px); }
  .face-top { border-width: 3px; transform: rotateX(90deg) translateZ(62.5px); }
  .face-bottom { transform: rotateX(-90deg) translateZ(62.5px); }
  .face-left { transform: rotateY(-90deg) translateZ(62.5px); }
  .face-right { transform: rotateY(90deg) translateZ(62.5px); }
  .seq-cursor { bottom: 11px; width: 62px; height: 6px; }

  /* 选关页 */
  .kf-index { padding: 0 0 20px; }
  .kf-hero { padding: 30px 24px; margin-bottom: 24px; }
  .kf-section { margin-bottom: 24px; }
  .kf-section-head { padding: 14px 18px; margin-bottom: 12px; }
  .kf-sub-grid { gap: 14px; }
  .kf-sub-card { border-width: 1px; border-radius: 14px; }
  .kf-sub-card:active { transform: none; }
  .kf-sub-grid-qh { gap: 8px; }

  /* 拼音掉落队列 px 锁定 */
  .drill-header { margin: 14px auto 9px; }
  .game-hint { font-size: 14px; padding: 8px 30px; border-radius: 50px; }
  .drill-stack { height: calc(5 * 72px - 12px + 2 * 14px); padding: 14px 28px; border-width: 2px; border-radius: 16px; }
  .drill-row {
    left: 10px; right: 10px;
    top: calc(14px + var(--slot, 0) * 72px);
    height: 60px; padding: 0 20px; border-radius: 11px; border-width: 1px;
  }
  .drill-syl-text { font-size: 26px; }
  .drill-syl-cursor { bottom: -6px; width: 16px; height: 3px; border-radius: 1.5px; }
  @keyframes drillDrop {
    0% { transform: translateY(-140px); opacity: 0; }
    40% { opacity: 1; }
    100% { transform: translateY(0); opacity: 1; }
  }
  .drill-row-ex { font-size: 22px; }
  .drill-row-upper { font-size: 24px; width: 32px; }

  /* 拼音强化 px 锁定 */
  .qh-flow { gap: 13px; padding: 17px 19px; border-width: 1px; border-radius: 17px; }
  .pf-row { gap: 13px; }
  .qh-flow .pf-item { gap: 11px; padding: 9px 12px; border-width: 1px; border-radius: 10px; }
  .pf-chars { height: 46px; }
  .pf-char-text { font-size: 27px; }
  .qh-flow .pf-char-cursor { top: 32px; width: 14px; height: 3px; }
  .pf-ex { font-size: 31px; }
}



/* ============================================================
   指法练习选择页 · 全新设计
   ============================================================ */
.kf-page .kf-index { padding: 6px 0 60px; }

/* —— Hero：深墨绿横幅（只留标题，无大键盘/统计）—— */
.kf-page .kf-hero {
  position: relative; overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 30px 40px 28px; margin-bottom: 26px;
  border-radius: 24px; text-align: center; color: #fff;
  background:
    radial-gradient(760px 360px at 84% -14%, rgba(45, 212, 191, 0.22), transparent 62%),
    linear-gradient(158deg, #073b35 0%, #0a5148 44%, #0e6f64 100%);
  box-shadow: 0 24px 54px rgba(7, 59, 53, 0.28);
}
.kf-page .kf-hero-spot { display: block; }
.kf-page .kf-hero-txt { position: relative; display: flex; flex-direction: column; align-items: center; }
.kf-page .kf-hero-kicker {
  font-size: 12px; font-weight: 800; letter-spacing: 0.2em;
  text-transform: uppercase; color: #7de8d6; margin-bottom: 10px;
}
.kf-page .kf-hero-title {
  font-size: clamp(32px, calc(3.4 * var(--pg-vw, 1vw)), 42px); font-weight: 900;
  color: #fff; letter-spacing: 2px; line-height: 1.1;
}
.kf-page .kf-hero-sub { margin-top: 9px; font-size: 14.5px; color: rgba(255, 255, 255, 0.72); }

/* —— 大关按钮：独立白卡（间距拉开、阴影各自独立、悬停有上浮空间）—— */
.kf-tabs {
  display: flex; gap: 20px;
  margin: -12px -12px 26px;
  padding: 12px;
  overflow-x: auto; scrollbar-width: none;
}
.kf-tabs::-webkit-scrollbar { display: none; }
.kf-tab {
  position: relative;
  flex: 1 1 0; min-width: 210px;
  display: flex; align-items: center; justify-content: center;
  height: 86px;
  border: 1px solid #e4eeeb; cursor: pointer; font-family: inherit;
  border-radius: 16px;
  background: #ffffff; color: #0d3b35;
  font-size: 20px; font-weight: 800; letter-spacing: .02em; white-space: nowrap;
  box-shadow: 0 12px 28px rgba(7, 59, 53, 0.10);
  transition: transform .22s cubic-bezier(.2,.7,.2,1), box-shadow .25s ease, border-color .22s ease, background .25s ease, color .25s ease;
}
.kf-tab.active {
  background: linear-gradient(135deg, #0e6f64, #0b5c55);
  border-color: rgba(94, 234, 212, 0.5);
  color: #ffffff;
  box-shadow: 0 20px 40px rgba(11, 92, 85, 0.32);
}

/* —— 边缘光环 + 鼠标跟随光斑（与下方关卡卡片同款）—— */
.kf-tab::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(var(--teal) 0deg var(--lp-ring), transparent var(--lp-ring) 360deg);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  z-index: 4;
  opacity: 0;
  transition: --lp-ring 800ms ease-in-out, opacity .3s ease;
}
.kf-tab:hover::after { --lp-ring: 360deg; opacity: 1; }
.kf-tab-spot {
  position: absolute; inset: 0; z-index: 2; pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(220px circle at var(--mx, 50%) var(--my, 50%), rgba(94, 234, 212, 0.16), transparent 65%);
  opacity: 0; transition: opacity .3s ease;
}
.kf-tab:hover .kf-tab-spot { opacity: 1; }
.kf-tab-name { position: relative; z-index: 3; }

/* —— 大关面板 —— */
.kf-page .kf-panel { display: none; }
.kf-page .kf-panel.active { display: block; animation: kfPanelIn .28s ease; }
@keyframes kfPanelIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.kf-page .kf-panel-desc { margin: 0 2px 16px; font-size: 13px; color: #7a8f8b; }
.kf-page .kf-panel .kf-sub-grid { margin-top: 0; }

/* —— 关卡网格：沿用拼音练习「声母强化」小卡，3 列 —— */
.kf-page .kf-sub-grid { grid-template-columns: repeat(3, 1fr); gap: 14px; align-items: start; margin-top: 18px; }
@media (max-width: 1080px) { .kf-page .kf-sub-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 760px) { .kf-page .kf-sub-grid { grid-template-columns: 1fr; } }

/* 指法关卡卡：插画区改为真正的正方形（1:1），方图铺满不裁切；
   图片 object-fit: cover 铺满，竖图会被左右裁掉、方图完整显示；
   去掉浮动动效，不上下跳、不露底 */
.kf-page .kf-panel .pym2-card-art { aspect-ratio: 1 / 1; }
.kf-page .kf-panel .pym2-art-img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; object-position: center;
  animation: none;
}
.kf-page .kf-panel .pym2-card:hover .pym2-art-img { animation: none; transform: none; }
/* 卡片底部文字：轮换文本（键名 ⇄ 手指说明），居中 + 放大 + 加粗 + 逐字切换 */
.kf-page .kf-panel .pym2-card-in { align-items: center; text-align: center; padding: 16px 12px 18px; }
.kf-page .kf-panel .kf-rot {
  position: relative;
  display: block; width: 100%;
  height: 38px; overflow: hidden;
  font-size: 26px; font-weight: 900; letter-spacing: .5px; color: #0f3d36;
  line-height: 38px;
}
.kf-page .kf-panel .kf-rot-line {
  position: absolute; left: 0; right: 0; top: 0;
  display: flex; align-items: center; justify-content: center;
  height: 38px; white-space: nowrap;
}
.kf-page .kf-panel .kf-rot-line span {
  display: inline-block;
  transition: transform .5s cubic-bezier(.25, 1.25, .4, 1), opacity .45s ease;
  transition-delay: calc(var(--i) * 0.03s);
}
.kf-page .kf-panel .kf-rot-a span { transform: translateY(0); opacity: 1; }
.kf-page .kf-panel .kf-rot-b span { transform: translateY(130%); opacity: 0; }
.kf-page .kf-panel .kf-rot.alt .kf-rot-a span { transform: translateY(-130%); opacity: 0; }
.kf-page .kf-panel .kf-rot.alt .kf-rot-b span { transform: translateY(0); opacity: 1; }
.kf-page .kf-art-ph {
  position: absolute; inset: 0;
  display: grid; place-items: center;
  font-size: 30px; font-weight: 900; letter-spacing: 3px;
  color: #0e6f64;
  background: repeating-linear-gradient(135deg, #f1faf8 0 12px, #e8f5f2 12px 24px);
}

/* ===== 自定义练习面板（第 4 个标签）=====
   注意：本文件靠 .kf-page .kf-panel 前缀提权，新样式必须放在文件末尾，
   否则会被前面的规则压掉。 */
/* 自定义练习面板：内层元素统一跟键盘同宽（1000px）并居中，
   否则头/尾会撑到 1030px、比键盘宽出一截，看起来没对齐。 */
.kf-page .kf-cust {
  display: flex; flex-direction: column; gap: 20px;
  padding: 26px 24px 28px;
  background: #ffffff;
  border: 1px solid #e4eeeb;
  border-radius: 20px;
  box-shadow: 0 12px 28px rgba(7, 59, 53, 0.10);
}
.kf-page .kf-cust > * { width: 100%; max-width: 1000px; margin-left: auto; margin-right: auto; }
.kf-page .kf-cust-head { display: flex; flex-direction: column; gap: 8px; }
.kf-page .kf-cust-title { font-size: 20px; font-weight: 900; color: #0f3d36; letter-spacing: .5px; }
.kf-page .kf-cust-sub { font-size: 14px; color: #7a8f8b; line-height: 1.6; }
.kf-page .kf-cust-sub b { color: #0F766E; font-weight: 800; }

/* 练习模式：四选一。四个按钮**等分撑满一整行**（与下方键盘同宽），
   文字居中；鼠标移到按钮上有跟首页按钮同款的"跑光圈"（--lp-ring，见 style.css）。 */
.kf-page .kf-cust-modes {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 12px;
}
.kf-page .kf-cust-mode {
  position: relative;
  display: flex; flex-direction: column; gap: 4px;
  align-items: center; justify-content: center;
  padding: 14px 12px;
  border: 1px solid #dbe8e5; border-radius: 14px;
  background: #ffffff; color: #24413C;
  text-align: center; cursor: pointer; font-family: inherit;
  overflow: hidden;
  box-shadow: 0 4px 12px rgba(15,118,110,.06);
  transition: transform .2s ease, border-color .2s ease,
              background .2s ease, box-shadow .2s ease;
}
.kf-page .kf-cust-mode b { font-size: 18px; font-weight: 800; line-height: 1.35; }
.kf-page .kf-cust-mode span { font-size: 13.5px; color: #8aa39e; line-height: 1.4; }
.kf-page .kf-cust-mode:hover {
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(15,118,110,.14);
}
/* hover 跑光圈：与 .kf-tab::after 同一套机制（conic-gradient + mask 挖空） */
.kf-page .kf-cust-mode::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(var(--teal) 0deg var(--lp-ring), transparent var(--lp-ring) 360deg);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  z-index: 2;
  opacity: 0;
  transition: --lp-ring 800ms ease-in-out, opacity .3s ease;
}
.kf-page .kf-cust-mode:hover::after { --lp-ring: 360deg; opacity: 1; }
/* hover 鼠标跟随光斑（--mx / --my 由 JS 写在按钮上） */
.kf-page .kf-cust-mode-spot {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  border-radius: inherit;
  background: radial-gradient(150px circle at var(--mx, 50%) var(--my, 50%), rgba(94,234,212,.18), transparent 65%);
  opacity: 0; transition: opacity .3s ease;
}
.kf-page .kf-cust-mode:hover .kf-cust-mode-spot { opacity: 1; }
.kf-page .kf-cust-mode.on {
  background: linear-gradient(135deg, #14B8A6, #0F766E);
  border-color: #0B5C55; color: #ffffff;
  box-shadow: 0 8px 20px rgba(15,118,110,.26);
}
.kf-page .kf-cust-mode.on span { color: rgba(255,255,255,.78); }
@media (max-width: 900px) {
  .kf-page .kf-cust-modes { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .kf-page .kf-cust-mode { padding: 12px 10px; }
  .kf-page .kf-cust-mode b { font-size: 16px; }
  .kf-page .kf-cust-mode span { font-size: 12px; }
}
@media (prefers-reduced-motion: reduce) {
  .kf-page .kf-cust-mode::after,
  .kf-page .kf-ck::after { transition: none; }
  .kf-page .kf-cust-mode:hover::after,
  .kf-page .kf-ck:hover::after { --lp-ring: 360deg; opacity: 1; }
}

/* 自定义练习的可点选键盘：一整块 5 行标准键盘 */
.kf-page .kf-cust-kb { position: relative; padding: 4px 0; }
/* 限制最大宽度：太宽会把键帽拉成扁条，比例失真。
   1000px 时 15 个单位 ≈ 每单位 62px，键帽接近正方形。 */
.kf-page .kf-ck {
  position: relative;
  display: flex; flex-direction: column; gap: 8px;
  width: 100%; max-width: 1000px; margin: 0 auto;
  padding: 18px 16px;
  background: linear-gradient(180deg, #eef5f3, #e4efec);
  border: 1px solid #d3e3df;
  border-radius: 16px;
  box-shadow: inset 0 -2px 6px rgba(15,118,110,.06);
}
/* 键盘整体的跑光圈：跟模式按钮/首页按钮同一套（悬停时绕边框转一圈） */
.kf-page .kf-ck::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: inherit;
  padding: 2px;
  background: conic-gradient(var(--teal) 0deg var(--lp-ring), transparent var(--lp-ring) 360deg);
  -webkit-mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  mask: linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  pointer-events: none;
  z-index: 3;
  opacity: 0;
  transition: --lp-ring 800ms ease-in-out, opacity .3s ease;
}
.kf-page .kf-ck:hover::after { --lp-ring: 360deg; opacity: 1; }
.kf-page .kf-ck-row { display: flex; gap: 8px; position: relative; z-index: 1; }
/* --w 是宽度档（1 = 标准键）。用 flex-grow/shrink/basis 三个都设成 --w，
   整排严格按宽度档分配、不会被内容挤歪（只写 flex: w 1 0 会让宽键抢走比例）。 */
.kf-page .kf-ck-key {
  position: relative;
  flex-grow: var(--w);
  flex-shrink: var(--w);
  flex-basis: 0;
  min-width: 0;
  height: 62px;
  display: flex; flex-direction: column;
  align-items: center; justify-content: center;
  border-radius: 8px;
  background: linear-gradient(180deg, #ffffff, #f6faf9);
  border: 1px solid #dbe8e5;
  /* 立体感：底部一条厚阴影当"键身"，按下时收掉 */
  box-shadow: 0 3px 0 #d3e2de, 0 4px 8px rgba(15,118,110,.07);
  color: #24413C;
  font-size: 14px; font-weight: 800;
  user-select: none;
  transition: transform .09s ease, box-shadow .09s ease,
              background .18s ease, border-color .18s ease, color .18s ease;
}
.kf-page .kf-ck-main { line-height: 1.1; }
.kf-page .kf-ck-sub {
  font-size: 9px; font-weight: 700; color: #9db4b0;
  line-height: 1.1; margin-top: 1px;
}
/* 可点选的键：手指光标 + hover 微亮，暗示"能点" */
.kf-page .kf-ck-pick { cursor: pointer; }
.kf-page .kf-ck-pick:hover {
  background: linear-gradient(180deg, #ffffff, #eef8f6);
  border-color: #14B8A6;
  color: #0F766E;
}
/* 按下：键帽下压，底部厚阴影收掉 —— 有真的按下去的手感 */
.kf-page .kf-ck-pick:active {
  transform: translateY(3px);
  box-shadow: 0 0 0 #d3e2de, 0 1px 3px rgba(15,118,110,.10);
}
/* 选中态：青绿实心 + 金色描边，跟旁边的星星/按钮同一套语言 */
.kf-page .kf-ck-key.on {
  background: linear-gradient(180deg, #14B8A6, #0F766E);
  border-color: #0B5C55;
  color: #ffffff;
  box-shadow: 0 3px 0 #0B5C55, 0 4px 12px rgba(15,118,110,.28);
}
.kf-page .kf-ck-key.on .kf-ck-sub { color: rgba(255,255,255,.72); }
.kf-page .kf-ck-key.on:hover { color: #ffffff; }
/* 选中键的序号角标：练到这个键的顺序（1、2、3…） */
.kf-page .kf-ck-order {
  position: absolute; top: -5px; right: -5px;
  min-width: 18px; height: 18px; padding: 0 4px;
  display: grid; place-items: center;
  border-radius: 9px;
  background: #FFC107; color: #4A2E00;
  font-size: 11px; font-weight: 900; line-height: 1;
  box-shadow: 0 2px 6px rgba(0,0,0,.18);
  pointer-events: none;
}
/* 功能键：压暗、不可点 */
.kf-page .kf-ck-func {
  background: linear-gradient(180deg, #f4f8f7, #eaf1ef);
  border-color: #dfeae7;
  color: #9db4b0;
  font-size: 10.5px; font-weight: 700;
  box-shadow: 0 3px 0 #dde8e5;
  cursor: default;
}
.kf-page .kf-ck-space { display: block; width: 64px; height: 3px; border-radius: 2px; background: #cfdeda; }
/* 窄屏：键帽矮一点、字号小一点，否则一排塞不下 */
@media (max-width: 900px) {
  .kf-page .kf-ck { gap: 5px; padding: 12px 10px; }
  .kf-page .kf-ck-row { gap: 5px; }
  .kf-page .kf-ck-key { height: 46px; font-size: 12px; border-radius: 6px; }
  .kf-page .kf-ck-sub { display: none; }
  .kf-page .kf-ck-func { font-size: 9px; }
}

/* 已选键位：胶囊（照抄 style.css 的 .rank-tab 写法） */
.kf-page .kf-cust-picked {
  display: flex; flex-wrap: wrap; gap: 8px;
  min-height: 40px; align-items: center;
}
.kf-page .kf-cust-chip {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 40px; padding: 8px 16px;
  border-radius: 22px;
  background: #0F766E; color: #fff;
  border: 1.5px solid #0F766E;
  font-size: 16px; font-weight: 800;
}
.kf-page .kf-cust-empty { font-size: 14px; color: #9db4b0; }

.kf-page .kf-cust-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.kf-page .kf-cust-count { font-size: 15px; font-weight: 700; color: #0f3d36; }
.kf-page .kf-cust-actions { display: flex; gap: 10px; }
.kf-page .kf-cust-btn {
  padding: 10px 26px; border-radius: 22px;
  font-size: 15px; font-weight: 800; cursor: pointer;
  background: #fff; color: #0f3d36;
  border: 1.5px solid #cfe3df;
  transition: background .2s ease, color .2s ease, border-color .2s ease, opacity .2s ease;
}
.kf-page .kf-cust-btn:hover { border-color: #0F766E; }
/* 开始练习：青绿渐变胶囊（沿用 .rr-btn-primary 那套配色） */
.kf-page .kf-cust-btn.kf-cust-start {
  background: linear-gradient(135deg, #14B8A6, #0F766E);
  color: #fff; border-color: transparent;
}
.kf-page .kf-cust-btn.kf-cust-start.is-disabled {
  opacity: .45; cursor: not-allowed;
  background: #cfe3df; color: #7a8f8b; background-image: none;
}
/* 键盘上的选中态（键盘组件接进来后，给键位元素加 .on 即生效） */
.kf-page .kf-cust-kb .on {
  background: #0F766E !important; color: #fff !important;
  border-color: #0F766E !important;
}


/* ===== 练习页间距 =====
   控制按钮已并进顶部悬浮胶囊（不再单独占一行），所以这里只需要：
   卡片贴住胶囊下方、操作行跟着卡片。 */
.pinyin-flow-scroll { margin-top: 0; }
.skip-bar { margin-top: calc(1.4 * var(--pg-vw, 1vw)); }
@media (max-width: 980px) {
  .skip-bar { margin-top: 10px; }
}
@media (max-width: 760px) {
  .skip-bar { margin-top: 8px; }
}

/* ===== 练习页（#kfPage / .kf-page 家族）：页面底色统一由 body 承担 =====
   顶栏是透明的（底色由内部那条悬浮胶囊承担），而页面容器从顶栏**下方**才开始，
   它自己的底色画不到顶栏那一条上 → 顶部那一条会露出 body 的底色。
   练习页原来把 body 设成纯白（#fff），于是顶栏那条明显是白的（用户反馈的"顶部露白"），
   而页面本体是浅青绿渐变，一眼就能看出两条不一样。
   改为：body 铺同一份页面底色（连顶栏那一条一起盖住），容器自己不再铺底色 ——
   只有一层渐变，既不会露白，也不会出现"两层渐变错位"的分界线。 */
body:has(.kf-page), body:has(#kfPage) {
  /* 只铺一次、不重复（否则每屏高一截就会重来一条渐变）；渐变收白后由 #fff 兜底 */
  background: #fff var(--bg-base, linear-gradient(180deg, #f4faf9 0%, #ffffff 300px)) no-repeat !important;
}
body:has(.kf-page) :is(#kfPage, .kf-page),
body:has(#kfPage) :is(#kfPage, .kf-page) { background: transparent; }
/* 顶栏实际高度：胶囊上留白 24 + 胶囊本体 ~50 ≈ 74px，取 80 兜住。
   页面背景的柔光层/纹理层要向上延伸这么多，才盖得住整个顶栏区。 */
:root { --topbar-h: 80px; }
