/* karin小手机 — 纯白主屏 */

:root {
  --white: rgba(255, 255, 255, 0.98);
  --glass-bg: rgba(255, 255, 255, 0.96);
  /* 页面基础底色：html / body / .phone / .phone__wall 共用。
     多层冗余铺底，避免「单层没铺满 → 透出系统桌面」的白屏/透明问题。
     设置自定义壁纸时由 .phone__wall 的背景图覆盖，无需改这里。 */
  --karin-base-bg: #ffffff;
  /* 主屏 / 程序坞图标底板的白度。集中成一个变量，便于整体微调。 */
  --karin-icon-plate-a: 0.55;
  --glass-line: rgba(230, 230, 230, 0.85);
  --glass-shadow: 0 10px 40px rgba(0, 0, 0, 0.04), inset 0 1px 0 rgba(255, 255, 255, 0.98);
  --ink: rgba(55, 58, 62, 0.88);
  --ink-soft: rgba(95, 100, 108, 0.72);
  --ink-faint: rgba(130, 136, 145, 0.55);
  --font: "Jost", "Noto Sans SC", "PingFang SC", sans-serif;
  --karin-font: var(--font);
}

html.karin-custom-font-active {
  --font: var(--karin-font);
  --mc-font: var(--karin-font);
  --mc-display: var(--karin-font);
  --mc-script: var(--karin-font);
  --mc-call-font: var(--karin-font);
  --qq-font: var(--karin-font);
  --soft-font: var(--karin-font);
  --soft-serif: var(--karin-font);
  --soft-script: var(--karin-font);
  --vf-ui: var(--karin-font);
  --vf-display: var(--karin-font);
  --vf-editorial: var(--karin-font);
  --vf-script: var(--karin-font);
  --fr-ui: var(--karin-font);
  --fr-display: var(--karin-font);
  --fr-serif: var(--karin-font);
  --fr-script: var(--karin-font);
  --fr-caps: var(--karin-font);
  --fr-kr: var(--karin-font);
  --tw-ui: var(--karin-font);
  --tw-display: var(--karin-font);
  --tw-serif: var(--karin-font);
  --tw-script: var(--karin-font);
  --tw-read-font: var(--karin-font);
  --ncm-font: var(--karin-font);
  --sp-round: var(--karin-font);
  --sp-display: var(--karin-font);
  --ins-ui: var(--karin-font);
  --ins-display: var(--karin-font);
  --mi-font: var(--karin-font);
  --mi-display: var(--karin-font);
  --mm-ui: var(--karin-font);
  --mm-display: var(--karin-font);
  --it-body: var(--karin-font);
  --it-display: var(--karin-font);
  --it-script: var(--karin-font);
  --it-editorial: var(--karin-font);
  --it-mono: var(--karin-font);
  --mdl-font-ui: var(--karin-font);
  --mdl-font-display: var(--karin-font);
  --cs-body: var(--karin-font);
  --cs-display: var(--karin-font);
  --cs-mono: var(--karin-font);
  --cs-script: var(--karin-font);
  --xw-sans: var(--karin-font);
  --xw-serif: var(--karin-font);
  --xw-display: var(--karin-font);
  --xw-script: var(--karin-font);
  --lt-sans: var(--karin-font);
  --lt-serif: var(--karin-font);
  --mn-ui: var(--karin-font);
  --mn-display: var(--karin-font);
}

/* 聊天美化主题在 .karin-chat-app 上重定义 --soft-font / --mc-display，需压过主题局部变量 */
html.karin-custom-font-active .karin-chat-app,
html.karin-custom-font-active .karin-chat-app.theme-soft,
html.karin-custom-font-active .karin-chat-app[class*="chat-bf-theme-"] {
  --mc-font: var(--karin-font);
  --mc-display: var(--karin-font);
  --mc-script: var(--karin-font);
  --mc-call-font: var(--karin-font);
  --qq-font: var(--karin-font);
  --soft-font: var(--karin-font);
  --soft-serif: var(--karin-font);
  --soft-script: var(--karin-font);
}

html.karin-custom-font-active body :where(
  :not(svg):not(svg *):not([class*="icon"]):not([class*="Icon"]):not(.material-icons)
) {
  font-family: var(--karin-font) !important;
}

/* 美化 · 各软件字号（主屏幕 desk 除外，按容器 data-karin-fs 缩放） */
:root {
  --karin-font-size-scale: 1;
}

[data-karin-fs] :where(
  p, span, div, li, a, button, label, h1, h2, h3, h4, h5, h6,
  td, th, dt, dd, blockquote, figcaption, small, time, em, strong, b, i,
  input, textarea, select, option, pre, code, cite, q, mark, sub, sup, legend
):not(.qq-room__msg-time):not(svg *):not([class*="icon"]):not([class*="Icon"]):not(.material-icons):not(.ic):not(.ic *):not([data-i]):not(.foot__dock-btn):not(.ins-icon-pick__glyph) {
  font-size: calc(1em * var(--karin-font-size-scale, 1)) !important;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html, body {
  width: 100%;
  /* 【sw-161】★ 整个白块 saga 的真正病根在这里 ★
   *
   * 原来是 height: var(--app-height, 100vh) —— 把整棵渲染树的**根高度**
   * 交给了一个 JS 可能写错的变量。
   *
   * ── 探针实测（probe-white-band.py）──
   *   把 --app-height 写成 280（真实视口 700）：
   *     白带精确出现在 y=280..699（下方 60% 全白）
   *     白带的元素归属是 **HTML 自身**
   *   写成 180：
   *     白带精确出现在 y=180..699（下方 74% 全白）
   *
   *   → **白带的起点 = --app-height 的值**，铁证。
   *
   * ── 为什么之前十几版都修不掉 ──
   *   `max-height: 100%` 只是「算大时的刹车」。而用户的现场是「算小」：
   *   html 高 280 → .phone 高 280 → y>280 处**没有任何元素覆盖** →
   *   露出最底层 body 的 background-color → 白。
   *
   *   我先前一直在改 .phone__wall（壁纸层）。但壁纸层高 280 **正是因为它
   *   在 html 里面**——把壁纸层自己改成 100vh 并不能救它，因为
   *   **父级 html 只有 280，超出部分一样露的是 html 外面的白**。
   *   这就是用户反馈「完全没变」的原因：白块不在 .phone__wall 里，
   *   在 .phone（乃至整棵树）的**外面**。
   *
   * ── 正解 ──
   *   根高度必须来自**纯视口单位**：
   *     height: 100vh   ——与 JS 变量、与父级、与任何计算彻底无关。
   *   --app-height 仍然有用（供 .phone 内部做「可视高度」参考、
   *   键盘避让等），但它**不再是渲染树的根高度**，因此它写错也不会
   *   让页面塌缩出白。
   *
   *   为什么 100vh 不怕算大：html 之外的区域本来就不绘制，
   *   算大没有任何副作用；算小才会露白。所以宁可偏大。
   *
   *   同时保留 min-height 与 height 双写，兼容老内核（部分内核只认其一）。 */
  height: 100vh;
  min-height: 100vh;
  overflow: hidden;
  background: var(--karin-base-bg, #ffffff);
  font-family: var(--font);
  -webkit-font-smoothing: antialiased;
  -webkit-tap-highlight-color: transparent;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

/* 【sw-150】最后一道防线：把壁纸图直接铺在 html 与 body 上。
 *
 * 前面的层级依赖 .phone__wall 的正确定位；若某个旧内核（Via）
 * 连绝对定位都处理异常，壁纸仍可能铺不满。
 *
 * 关键点：光给 body 加背景**没用**。之前 .phone__wall 与 .phone 都是
 * `background:#ffffff` 的不透明层，会把兜底整个盖住（实测亮度仍是 254）。
 * 所以这里同时做两件事：
 *   ① 把 --karin-base-bg 置为 transparent —— 所有引用它的层
 *      （html / body / .phone / .phone__wall）一起变透明；
 *   ② 在 html/body 上铺兜底壁纸。
 * 这样即使 .phone__wall 彻底失效，看到的仍是壁纸。
 *
 * 由 js2 的 applyWallToPhone() 写入变量并加 has-page-wall。
 * 无自定义壁纸时该类不存在，各层保持原本的白色底，观感不变。 */
html.has-page-wall,
body.has-page-wall {
  background-color: transparent;
  background-image: var(--karin-wall-image, none);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
}

html.has-page-wall {
  /* 让所有继承该变量的层一起透明 */
  --karin-base-bg: transparent;
}

.glass {
  background: var(--glass-bg);
  backdrop-filter: blur(32px) saturate(1.15);
  -webkit-backdrop-filter: blur(32px) saturate(1.15);
  border: 1px solid var(--glass-line);
  box-shadow: var(--glass-shadow);
}

/* 【sw-149】.phone 由 fixed 改为 absolute，与 .phone__wall 同理。
 *
 * 原先 .phone 是 fixed + height:var(--app-height)。两个问题：
 *   ① fixed 层的包含块是视觉视口，小窗 ROM 常不重排 → 卡在旧尺寸；
 *   ② 它同时又靠 --app-height 定高，两条来源都可能失准。
 *
 * 改为 absolute 后，包含块变成 html（initial containing block 的
 * 尺寸由 html 自身决定，而 html 宽度 100% / 高度用 --app-height），
 * 且 html/body/.phone/.phone__wall 四层共用同一个变量与同一个
 * 祖先链 —— 任何一层尺寸变化都会带着其他层一起重排，
 * 不再存在「某个 fixed 层单独卡住」的可能。
 *
 * 视觉等价性：html/body 已被约束为满屏且 overflow:hidden，
 * .phone 作为其子级 absolute + inset:0，与 fixed 满屏完全一致。 */
/* 【sw-150】.phone 也用 longhand，理由同 .phone__wall：
 * inset 简写在旧内核里可能整条失效。 */
.phone {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  /* 【sw-161】height 由 var(--app-height, 100vh) 改为 100vh。
   *
   * 原因同 html：.phone 是承载整个桌面（壁纸 + 图标 + dock）的那一层，
   *   它的高度若取自 JS 变量，变量一写小，`.phone` 就塌，
   *   塌掉的区域下方没有任何东西覆盖 → 直接露出白。
   *
   *   探针实测把 --app-height 写成 280 时，白带恰好从 y=280 开始，
   *   白带的归属正是 html —— 也就是 .phone 之外。
   *
   * 改为 100vh：.phone 永远铺满真实视口。
   *   · 变量正确时：视觉与原来完全一致；
   *   · 变量写小时：.phone 不再跟着塌，白块无从产生；
   *   · 变量写大时：100vh 本来就不受它影响，也不会溢出。
   *
   * 注意 .phone 的四条边（top/left/right/bottom: 0）+ position:absolute
   * 依然保留，它们负责定位；height 现在由视口单位给出一条确定性来源。
   *
   * --app-height 变量本身保留：它仍然被键盘避让等逻辑使用，
   * 只是不再充当「决定页面高度」的角色。 */
  height: 100vh;
  display: flex;
  flex-direction: column;
  color: var(--ink);
  overflow: hidden;
  background: var(--karin-base-bg, #ffffff);
}

/* ── 壁纸 ──
 * 【sw-149】由 fixed 改为 absolute，这是小窗白块的正解。
 *
 * 症状：小窗「放大 → 缩回」后，壁纸只铺了约 1/3 的高度，下方全白；
 *       而「全屏 → 小窗」正常，且切到全屏能恢复。
 *
 * 排除法：如果 --app-height 被写错，全屏也会一起白 —— 但全屏能恢复，
 *   说明高度变量是对的，问题在 .phone__wall 这个 fixed 层自身。
 *
 * 真凶：fixed 元素的包含块是「视觉视口」。安卓自由窗口（小窗）里系统
 *   对视觉视口的判定与普通浏览器不同，inset:0 算完后系统不认为需要
 *   重排，该层便一直停在旧尺寸；只有全屏切换才触发布局重建。
 *   叠加下面 karin-thermal.css 的「contain: strict」（含 contain:size，
 *   会让元素完全忽略自身尺寸计算），一旦算错就再无自我纠正的机会。
 *
 * 对策：不再让壁纸层自己量视口，改为依赖父容器 .phone。
 *   .phone 是 fixed + height:var(--app-height)，其高度就是权威值；
 *   壁纸层 absolute + inset:0 撑满它即可，不存在独立算错的可能。
 *   .phone 本身就是满屏的定位上下文（position:fixed），视觉等价。
 */
.phone__wall {
  position: absolute;
  /* 【sw-150】inset 简写是较新的语法（Chrome 87+）。Via 等套壳 WebView
   * 的旧内核若不支持，整条声明失效 → top/left/right/bottom 全为 auto
   * → 绝对定位元素塌缩到内容尺寸 → 壁纸只剩一小块，下方全白。
   * 这里改用 top/left/right/bottom 四条 longhand，所有内核都认。
   * （实测：把 inset 整条去掉会立刻露白，亮度 46 → 254。） */
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 0;
  /* 【sw-150】不写死白色。
   * 原来是 background:#ffffff，即「不透明白」。一旦这一层在小窗里
   * 定位异常（塌缩、被 contain 锁死、旧内核不认 inset），露出的就是
   * 这块白 —— 这正是用户反复看到的「三分之二屏幕白块」。
   * 改为 transparent + 由 --karin-base-bg 变量给色：
   * 有壁纸时变量为 transparent（让下面 html/body 的兜底壁纸透上来），
   * 无壁纸时为白色（保持原观感）。 */
  background: var(--karin-base-bg, #ffffff);
}

.phone__snow {
  display: none;
}

.phone__curtain {
  display: none;
}

.phone__curtain--l { left: 0; }
.phone__curtain--r {
  right: 0;
  transform: scaleX(-1);
}

.phone__haze {
  display: none;
}

/* ── 桌面主区 ── */
.desk-shell .desk--p1 {
  position: relative;
  z-index: 2;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: calc(env(safe-area-inset-top, 0px) + 10px) 20px calc(96px + env(safe-area-inset-bottom, 0px));
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  grid-auto-rows: min-content;
  gap: 10px 8px;
  align-content: start;
}

/* 大时钟 */
.hero-time {
  width: 100%;
  text-align: center;
  padding: 4px 0 6px;
}

.hero-time__brand {
  font-size: 10px;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 2px;
}

.hero-time__num {
  display: block;
  font-size: clamp(52px, 14vw, 64px);
  font-weight: 200;
  letter-spacing: 0.04em;
  line-height: 1;
  color: rgba(50, 54, 58, 0.82);
  font-variant-numeric: tabular-nums;
  text-shadow: 0 1px 24px rgba(255,255,255,0.8);
}

.hero-time__meta {
  margin-top: 8px;
  font-size: 12px;
  font-weight: 300;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
}

/* MEMO · 左侧双人风格 — dock 内布局见 karin-desk-p1-weekcal.css */
.wg-memo {
  background: transparent;
  padding: 8px 0 4px;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.wg-memo__pair {
  display: flex;
  justify-content: space-around;
  align-items: flex-end;
  gap: 6px;
  width: 100%;
  max-width: 182px;
}

.wg-memo__person {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  flex: 1;
  min-width: 0;
}

.wg-memo__label {
  font-size: 8px;
  opacity: 0.65;
  letter-spacing: 0.02em;
  margin-right: 2px;
}

.wg-memo__bubble {
  position: relative;
  margin: 0;
  background: rgba(255, 255, 255, 0.82);
  border: 1.5px solid rgba(200, 206, 214, 0.65);
  border-radius: 12px;
  padding: 3px 7px;
  font-size: 8px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  white-space: nowrap;
  box-shadow: 0 2px 6px rgba(120, 130, 145, 0.08);
}

.wg-memo__bubble::after {
  content: "";
  position: absolute;
  bottom: -5px;
  left: 50%;
  transform: translateX(-50%);
  border-left: 4px solid transparent;
  border-right: 4px solid transparent;
  border-top: 5px solid #fff;
}

.wg-memo__bubble::before {
  content: "";
  position: absolute;
  bottom: -7px;
  left: 50%;
  transform: translateX(-50%);
  border-left: 5px solid transparent;
  border-right: 5px solid transparent;
  border-top: 6px solid rgba(200, 206, 214, 0.65);
}

.wg-memo__ava {
  width: 58px;
  height: 58px;
  border-radius: 50%;
  flex-shrink: 0;
  border: 2px solid #b8bec8;
  background: #c4cad2;
  box-shadow: inset 0 2px 6px rgba(255, 255, 255, 0.45);
}

.wg-memo__ava.has-custom-ava {
  background-color: transparent;
  border-color: transparent;
  box-shadow: none;
}

.wg-memo__days {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  margin-top: 8px;
  width: 100%;
  max-width: 182px;
}

.wg-memo__wing {
  display: block;
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  border: 1.5px solid rgba(180, 188, 198, 0.45);
  border-right: none;
  border-bottom: none;
  border-radius: 6px 0 0 0;
  transform: rotate(-6deg);
  opacity: 0.7;
}

.wg-memo__wing--r {
  transform: scaleX(-1) rotate(-6deg);
}

.wg-memo__pill {
  margin: 0;
  background: rgba(255, 255, 255, 0.55);
  border: 1.5px solid rgba(200, 206, 214, 0.55);
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 9px;
  font-weight: 400;
  letter-spacing: 0.02em;
  color: var(--ink-soft);
  white-space: nowrap;
}


.desk--p1 > .ic[data-app="memory"] { grid-column: 3; grid-row: 6; }
.desk--p1 > .ic[data-app="chat"] { grid-column: 4; grid-row: 6; }
.desk--p1 > .ic[data-app="beauty"] { grid-column: 3; grid-row: 7; }
.desk--p1 > .ic[data-app="store"] { grid-column: 4; grid-row: 7; }

/* 音乐播放器 · INS 灰白韩系 */
.wg-player {
  grid-column: 1 / -1;
  grid-row: 4 / 6;
  position: relative;
  border-radius: 20px;
  overflow: hidden;
  padding: 10px 14px 8px;
  background: #ffffff;
  border: 1px solid rgba(230, 230, 230, 0.9);
  box-shadow:
    0 8px 28px rgba(0, 0, 0, 0.04),
    inset 0 1px 0 rgba(255, 255, 255, 0.98);
  backdrop-filter: blur(24px) saturate(1.1);
  -webkit-backdrop-filter: blur(24px) saturate(1.1);
}

.wg-player__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: inherit;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  pointer-events: none;
}

.wg-player.has-custom-player-bg {
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

.wg-player.has-custom-player-bg .wg-player__grain {
  opacity: 0.22;
}

.wg-player__grain {
  position: absolute;
  inset: 0;
  opacity: 0.35;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.08'/%3E%3C/svg%3E");
  mix-blend-mode: soft-light;
}

.wg-player__orb {
  position: absolute;
  border-radius: 50%;
  filter: blur(28px);
  pointer-events: none;
  animation: player-orb 8s ease-in-out infinite;
  animation-play-state: paused;
}

.wg-player__orb--1 {
  width: 90px;
  height: 90px;
  top: -20px;
  right: 10%;
  background: rgba(210,218,228,0.55);
}

.wg-player__orb--2 {
  width: 70px;
  height: 70px;
  bottom: 20%;
  left: -10px;
  background: rgba(255,255,255,0.7);
  animation-delay: -3s;
}

.wg-player__scan {
  position: absolute;
  inset: 0;
  background: linear-gradient(105deg, transparent 40%, rgba(255,255,255,0.45) 50%, transparent 60%);
  background-size: 200% 100%;
  animation: player-scan 6s ease-in-out infinite;
  animation-play-state: paused;
  pointer-events: none;
  opacity: 0.5;
}

.wg-player__top {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 6px;
}

.wg-player__tag {
  font-size: 8px;
  font-weight: 400;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.wg-player__live {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 8px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.wg-player__dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: #9aa3ae;
  animation: player-pulse 2s ease-in-out infinite;
  animation-play-state: paused;
}

.wg-player.is-playing .wg-player__orb,
.wg-player.is-playing .wg-player__scan,
.wg-player.is-playing .wg-player__dot {
  animation-play-state: running;
}

.wg-player__main {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 6px;
}

.wg-player__disc-area {
  position: relative;
  flex-shrink: 0;
  width: 52px;
  height: 52px;
}

.wg-player__ring {
  position: absolute;
  inset: -6px;
  border-radius: 50%;
  border: 1px solid rgba(180,188,198,0.35);
  animation: player-ring 4s ease-in-out infinite;
}

.wg-player__ring--2 {
  inset: -12px;
  border-color: rgba(180,188,198,0.18);
  animation-delay: -1.5s;
}

.wg-player.is-playing .wg-player__ring { animation-play-state: running; }
.wg-player:not(.is-playing) .wg-player__ring { animation-play-state: paused; opacity: 0.4; }

.wg-player__disc {
  position: relative;
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: linear-gradient(145deg, #d8dde4, #b8c0ca);
  box-shadow:
    0 4px 16px rgba(100,110,125,0.15),
    inset 0 1px 0 rgba(255,255,255,0.6);
  animation: player-spin 12s linear infinite;
  animation-play-state: paused;
}

.wg-player.is-playing .wg-player__disc { animation-play-state: running; }

.wg-player__cover {
  position: absolute;
  inset: 3px;
  border-radius: 50%;
  background:
    linear-gradient(160deg, rgba(255,255,255,0.15), transparent 50%),
    linear-gradient(145deg, #c4cad2, #a8b0ba);
}

.wg-player__cover.has-custom-cover {
  background: transparent;
}

.wg-player__disc:has(.has-custom-cover) {
  background: transparent;
  box-shadow: none;
}

.wg-player__hole {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 10px;
  height: 10px;
  margin: -5px 0 0 -5px;
  border-radius: 50%;
  background: rgba(240,244,248,0.95);
  border: 1px solid rgba(180,188,198,0.4);
  box-shadow: inset 0 1px 3px rgba(80,90,100,0.15);
}

.wg-player__meta {
  flex: 1;
  min-width: 0;
}

.wg-player__title {
  margin: 0 0 1px;
  font-size: 14px;
  font-weight: 300;
  letter-spacing: 0.05em;
  color: rgba(50,54,58,0.88);
  line-height: 1.2;
}

.wg-player__artist {
  margin: 0 0 4px;
  font-size: 9px;
  font-weight: 300;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
  text-transform: lowercase;
}

.wg-player__eq {
  display: flex;
  align-items: flex-end;
  gap: 2px;
  height: 12px;
  margin-bottom: 3px;
}

.wg-player__eq span {
  display: block;
  width: 2px;
  height: 4px;
  border-radius: 1px;
  background: rgba(130,136,145,0.55);
  animation: player-eq 0.9s ease-in-out infinite;
  animation-play-state: paused;
}

.wg-player.is-playing .wg-player__eq span { animation-play-state: running; }

.wg-player__eq span:nth-child(1) { animation-delay: 0s; }
.wg-player__eq span:nth-child(2) { animation-delay: 0.12s; }
.wg-player__eq span:nth-child(3) { animation-delay: 0.24s; }
.wg-player__eq span:nth-child(4) { animation-delay: 0.08s; }
.wg-player__eq span:nth-child(5) { animation-delay: 0.18s; }
.wg-player__eq span:nth-child(6) { animation-delay: 0.04s; }
.wg-player__eq span:nth-child(7) { animation-delay: 0.22s; }

.wg-player__lyric {
  margin: 0;
  font-size: 9px;
  font-weight: 300;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  opacity: 0.85;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  transition: opacity 0.6s ease;
}

.wg-player__lyric.is-fade { opacity: 0; }

.wg-player__progress {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
}

.wg-player__time {
  font-size: 9px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
  flex-shrink: 0;
  min-width: 28px;
}

.wg-player__bar {
  position: relative;
  flex: 1;
  height: 3px;
}

.wg-player__bar-track {
  position: absolute;
  inset: 0;
  border-radius: 999px;
  background: rgba(180,188,198,0.35);
}

.wg-player__bar-fill {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: 38%;
  border-radius: 999px;
  background: linear-gradient(90deg, rgba(130,136,145,0.5), rgba(80,86,94,0.75));
  transition: width 0.4s linear;
}

.wg-player__bar-head {
  position: absolute;
  top: 50%;
  left: 38%;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  background: #fff;
  border: 1px solid rgba(130,136,145,0.5);
  box-shadow: 0 1px 4px rgba(100,110,125,0.2);
  transition: left 0.4s linear;
}

.wg-player__ctrl {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 22px;
  margin-bottom: 6px;
}

.wg-player__btn {
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: none;
  padding: 0;
  cursor: pointer;
  color: var(--ink-soft);
  opacity: 0.65;
  transition: opacity 0.2s, transform 0.15s;
}

.wg-player__btn svg {
  width: 15px;
  height: 15px;
}

.wg-player__btn:active { transform: scale(0.88); opacity: 1; }

.wg-player__btn--play {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: rgba(255,255,255,0.85);
  border: 1px solid rgba(255,255,255,0.95);
  box-shadow: 0 3px 12px rgba(120,130,145,0.1), inset 0 1px 0 rgba(255,255,255,1);
  opacity: 1;
  color: rgba(70,74,80,0.85);
}

.wg-player__btn--play svg {
  width: 13px;
  height: 13px;
}

.wg-player__icon-play { display: none; }
.wg-player:not(.is-playing) .wg-player__icon-pause { display: none; }
.wg-player:not(.is-playing) .wg-player__icon-play { display: block; margin-left: 2px; }

.wg-player__foot {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 8px;
  padding-top: 5px;
  border-top: 1px solid rgba(200,208,218,0.3);
}

.wg-player__fmt {
  flex-shrink: 0;
  font-size: 8px;
  letter-spacing: 0.12em;
  color: var(--ink-faint);
}

.wg-player__marquee {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}

.wg-player__marquee span {
  display: inline-block;
  font-size: 8px;
  letter-spacing: 0.18em;
  color: var(--ink-faint);
  white-space: nowrap;
  animation: player-marquee 14s linear infinite;
  animation-play-state: paused;
}

.wg-player.is-playing .wg-player__marquee span { animation-play-state: running; }

@keyframes player-spin {
  to { transform: rotate(360deg); }
}

@keyframes player-eq {
  0%, 100% { height: 3px; opacity: 0.45; }
  50% { height: 10px; opacity: 0.9; }
}

@keyframes player-wave {
  0%, 100% { height: 4px; opacity: 0.35; }
  50% { height: 14px; opacity: 0.75; }
}

@keyframes player-pulse {
  0%, 100% { opacity: 0.4; transform: scale(0.85); }
  50% { opacity: 1; transform: scale(1.15); }
}

@keyframes player-orb {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(8px, -6px) scale(1.08); }
}

@keyframes player-scan {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

@keyframes player-ring {
  0%, 100% { transform: scale(1); opacity: 0.5; }
  50% { transform: scale(1.06); opacity: 0.85; }
}

@keyframes player-marquee {
  0% { transform: translateX(0); }
  100% { transform: translateX(-50%); }
}

@media (prefers-reduced-motion: reduce) {
  .wg-player__disc,
  .wg-player__eq span,
  .wg-player__orb,
  .wg-player__scan,
  .wg-player__ring,
  .wg-player__marquee span,
  .wg-player__dot {
    animation: none !important;
  }
}

.desk--p1 > .wg-polaroid {
  grid-column: 1 / 3;
  grid-row: 6 / 8;
  align-self: center;
  justify-self: center;
  width: 100%;
  max-width: 148px;
  padding: 4px 0;
}

.wg-polaroid__stack {
  position: relative;
  width: 100%;
  height: 132px;
}

.wg-polaroid__card {
  position: absolute;
  left: 50%;
  width: 108px;
  padding: 7px 7px 18px;
  background: #fafafa;
  border-radius: 2px;
  box-shadow:
    0 2px 8px rgba(100, 110, 125, 0.12),
    0 8px 24px rgba(100, 110, 125, 0.1),
    inset 0 0 0 1px rgba(255, 255, 255, 0.9);
  transform-origin: center center;
}

.wg-polaroid__card--3 {
  top: 6px;
  margin-left: -58px;
  transform: rotate(-14deg) translateX(-6px);
  z-index: 1;
  opacity: 0.72;
}

.wg-polaroid__card--2 {
  top: 2px;
  margin-left: -50px;
  transform: rotate(8deg) translateX(8px);
  z-index: 2;
  opacity: 0.88;
}

.wg-polaroid__card--1 {
  top: 0;
  margin-left: -54px;
  transform: rotate(-3deg);
  z-index: 3;
  animation: pol-float 5s ease-in-out infinite;
}

.wg-polaroid__photo {
  width: 100%;
  aspect-ratio: 1 / 0.92;
  border-radius: 1px;
  background:
    linear-gradient(160deg, rgba(255,255,255,0.15) 0%, transparent 40%),
    linear-gradient(145deg, #c4cad2, #a8b0ba);
  box-shadow: inset 0 1px 3px rgba(80, 90, 100, 0.08);
}

.wg-polaroid__photo--b {
  background:
    linear-gradient(200deg, rgba(255,255,255,0.12) 0%, transparent 45%),
    linear-gradient(145deg, #b8c0c8, #9ea8b2);
}

.wg-polaroid__photo--c {
  background:
    linear-gradient(120deg, rgba(255,255,255,0.2) 0%, transparent 50%),
    linear-gradient(145deg, #ccd2da, #b0b8c2);
}

.wg-polaroid__photo.has-custom-photo {
  background-color: transparent;
  box-shadow: none;
}

.wg-polaroid__cap {
  margin: 6px 0 0;
  font-size: 8px;
  font-weight: 300;
  letter-spacing: 0.06em;
  color: var(--ink-soft);
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.wg-polaroid__date {
  position: absolute;
  bottom: 6px;
  right: 10px;
  font-size: 7px;
  letter-spacing: 0.1em;
  color: var(--ink-faint);
}

.wg-polaroid__tape {
  position: absolute;
  top: -5px;
  left: 50%;
  width: 32px;
  height: 12px;
  margin-left: -16px;
  background: rgba(255, 255, 255, 0.55);
  border: 1px solid rgba(200, 206, 214, 0.45);
  box-shadow: 0 1px 3px rgba(100, 110, 125, 0.08);
  transform: rotate(-2deg);
  opacity: 0.85;
}

@keyframes pol-float {
  0%, 100% { transform: rotate(-3deg) translateY(0); }
  50% { transform: rotate(-2deg) translateY(-3px); }
}

@media (prefers-reduced-motion: reduce) {
  .wg-polaroid__card--1 { animation: none; }
}

.ic {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  border: none;
  background: none;
  cursor: pointer;
  padding: 2px 0;
}

.ic:active .ic__box { transform: scale(0.9); opacity: 0.85; }

.ic__box {
  width: 52px;
  height: 52px;
  border-radius: 16px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.15s ease;
}

.ic__box svg {
  width: 18px;
  height: 18px;
}

html.karin-alt-app-icons .ic__box,
html.karin-alt-app-icons .foot__dock-btn,
html.karin-alt-app-icons .ins-icon-pick__glyph {
  color: rgba(72, 76, 82, 0.78);
}

.ic__lbl {
  font-size: 10px;
  font-weight: 400;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}


/* ── 底部 ── */
.foot {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 5;
  padding: 36px 24px calc(8px + env(safe-area-inset-bottom, 0px));
  display: flex;
  flex-direction: column;
  align-items: center;
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(255, 255, 255, 0.2) 28%,
    rgba(255, 255, 255, 0.55) 62%,
    rgba(255, 255, 255, 0.82) 100%
  );
  pointer-events: none;
}

.foot__dock {
  pointer-events: auto;
  position: relative;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  gap: 28px;
  width: min(280px, 88vw);
  padding: 11px 24px 8px;
  border-radius: 26px;
  transition:
    opacity 0.28s ease,
    transform 0.32s cubic-bezier(0.32, 0.72, 0, 1),
    visibility 0.28s ease;
}

.foot__dock-dismiss {
  position: absolute;
  inset: 0;
  z-index: 0;
  margin: 0;
  padding: 0;
  border: none;
  border-radius: inherit;
  background: transparent;
  cursor: default;
}

.foot.is-dock-hidden {
  pointer-events: auto;
}

.phone.is-dock-hidden::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 120px;
  z-index: 4;
  pointer-events: auto;
}

.foot.is-dock-hidden .foot__dock {
  opacity: 0;
  visibility: hidden;
  transform: translateY(14px) scale(0.94);
  pointer-events: none;
}

.foot__dock-item {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  border: none;
  background: none;
  cursor: pointer;
  padding: 0;
}

.foot__dock-btn {
  width: 46px;
  height: 46px;
  background: rgba(var(--karin-icon-plate-c, 255, 255, 255), var(--karin-icon-plate-a));
  border-radius: 14px;
  border: 1px solid rgba(230, 230, 230, 0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
  transition: transform 0.15s ease;
}

.foot__dock-item:active .foot__dock-btn { transform: scale(0.88); }

.foot__dock-btn svg {
  width: 22px;
  height: 22px;
}

.foot__dock-lbl {
  font-size: 10px;
  font-weight: 400;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
  white-space: nowrap;
}

/* ── 弹层 ── */
.modal {
  position: fixed;
  inset: 0;
  z-index: 99;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(180, 190, 200, 0.2);
  backdrop-filter: blur(8px);
  padding: 24px;
}

.modal[hidden] { display: none; }

.modal__card {
  width: min(300px, 90vw);
  border-radius: 26px;
  padding: 32px 24px;
  text-align: center;
  position: relative;
}

.modal__close {
  position: absolute;
  top: 12px;
  right: 14px;
  width: 28px;
  height: 28px;
  border: none;
  border-radius: 50%;
  background: rgba(255,255,255,0.6);
  font-size: 18px;
  color: var(--ink-faint);
  cursor: pointer;
}

.modal__card svg {
  width: 36px;
  height: 36px;
  margin-bottom: 10px;
}

.modal__card h2 {
  font-size: 18px;
  font-weight: 400;
  letter-spacing: 0.06em;
  margin-bottom: 6px;
}

.modal__card p {
  font-size: 12px;
  color: var(--ink-faint);
}

@media (max-height: 700px) {
  .wg-memo__ava { width: 50px; height: 50px; }
  .desk--p1 { gap: 8px 6px; }
}

@media (hover: none) and (pointer: coarse) {
  .foot {
    padding: 28px 20px env(safe-area-inset-bottom, 6px);
  }
}

@media all and (display-mode: standalone) {
  /* 【sw-161】iOS 独立模式（添加到主屏幕）需要 --app-height：因为 iOS 的
   *   100vh 是「最大视口高」（地址栏收起时的高度），直接用 100vh 会比
   *   实际可视区更高。所以这条保留变量 —— 但要加一道 floor:
   *   min-height: 100vh，保证变量即使被写小，也绝不低于真实视口。
   *
   *   注意：min-height 优先级高于 height，所以这里 min-height 会成为
   *   生效值（例如 --app-height=660 而视口 700 时，取 700）。
   *   这正是我们要的方向 —— iOS 上「宁可偏大不可偏小」。
   *   桌面移动端（Android）不走这条，见 karin-thermal.css。 */
  html.is-ios .phone {
    height: var(--app-height, 100vh);
    min-height: 100vh;
  }
}

/* ─── 开屏遮罩（必须放在首屏同步加载的样式表里） ───
   桌面（#karin-phone-layer / .desk-custom-track）的内容由 JS 异步渲染，
   首帧时会先露出空桌面。这里在 body.karin-booting 期间用整屏遮罩盖住，
   等 js1/app.js 的 finishPhoneBoot() 完成后淡出并移除。
   注意：必须放在首屏同步加载的样式表里，懒加载样式表首帧根本还没到。 */
.karin-bootcover {
  position: fixed;
  inset: 0;
  z-index: 9500;
  display: flex;
  align-items: center;
  justify-content: center;
  background: #F8F9FB;
  opacity: 1;
  transition: opacity 0.2s ease;
}

.karin-bootcover.is-done {
  opacity: 0;
  pointer-events: none;
}

.karin-bootcover__mark {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 14px;
}

.karin-bootcover__name {
  font-family: 'Cormorant Garamond', 'Noto Serif SC', serif;
  font-size: 20px;
  letter-spacing: 0.28em;
  color: #4B8BF5;
  text-indent: 0.28em;
  animation: karin-bootcover-fadein 0.45s ease both;
}

/* 双爱心：两颗异步呼吸，比单点脉冲柔和 */
.karin-bootcover__hearts {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  margin-top: -2px;
}

.karin-bootcover__hearts svg {
  display: block;
  width: 9px;
  height: 9px;
  animation: karin-bootcover-breathe 1.5s ease-in-out infinite;
}

.karin-bootcover__hearts svg path {
  fill: #4B8BF5;
}

.karin-bootcover__hearts svg:nth-child(2) {
  animation-delay: 0.22s;
}

/* 构建号水印（sw-142 加）：更新排障用 —— 冷启动开屏约 2 秒，
   直接读出当前浏览器实际跑的是哪个版本（值由 head 里的
   karin-sw-build meta 经内联脚本注入，与页面构建天然同源） */
.karin-bootcover__build {
  font-family: 'Cormorant Garamond', 'Noto Serif SC', serif;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-indent: 0.22em;
  color: #B9C4D6;
  animation: karin-bootcover-fadein 0.45s ease both;
  animation-delay: 0.15s;
}

@keyframes karin-bootcover-fadein {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes karin-bootcover-breathe {
  0%, 100% { opacity: 0.22; transform: scale(0.8); }
  45%      { opacity: 1;    transform: scale(1); }
}

/* 无 JS 兜底：脚本未执行时不要永久遮住界面 */
html.no-js .karin-bootcover { display: none; }

@media (prefers-reduced-motion: reduce) {
  .karin-bootcover__dot { animation: none; }
}

/* ── 统一图标按钮（世界书 / ST 预设等） ── */
.mi-ico-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin: 8px 0 12px;
}
.mi-ico-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid rgba(0, 0, 0, 0.12);
  border-radius: 12px;
  background: #fff;
  color: #1a1a1a;
  cursor: pointer;
  flex-shrink: 0;
  transition: background 0.15s ease, transform 0.12s ease, border-color 0.15s ease;
}
.mi-ico-btn img {
  width: 18px;
  height: 18px;
  display: block;
  pointer-events: none;
  opacity: 0.88;
}
.mi-ico-btn:active {
  transform: scale(0.94);
}
.mi-ico-btn:hover {
  border-color: rgba(0, 0, 0, 0.22);
  background: #f7f7f5;
}
.mi-ico-btn--solid {
  background: #111;
  border-color: #111;
  color: #fff;
}
.mi-ico-btn--solid img {
  filter: invert(1);
  opacity: 1;
}
.mi-ico-btn--danger {
  color: #c0392b;
  border-color: rgba(192, 57, 43, 0.28);
}
.mi-ico-btn--danger:hover {
  background: rgba(192, 57, 43, 0.06);
}

/* ST 预设工具栏：图标按钮与文字按钮兼容 */
.stp-toolbar .mi-ico-btn.stp-btn {
  width: 34px;
  height: 34px;
  padding: 0;
  border-radius: 10px;
  font-size: 0;
}
.stp-toolbar .mi-ico-btn.stp-btn--primary {
  background: #111;
  border-color: #111;
}
.stp-toolbar .mi-ico-btn.stp-btn--primary img {
  filter: invert(1);
}

/* 世界书分组操作图标 */
.karin-worldbook-app .ins-wb-group-op.mi-ico-btn {
  width: 30px;
  height: 30px;
  padding: 0;
  border-radius: 10px;
}
.karin-worldbook-app .ins-wb-group-op.mi-ico-btn img {
  width: 15px;
  height: 15px;
}

/* 图标包替换后的 img 图标 */
.mi-ico-img {
  display: inline-block;
  vertical-align: middle;
  flex-shrink: 0;
  object-fit: contain;
  pointer-events: none;
}
button .mi-ico-img,
a .mi-ico-img,
.st-card-icon .mi-ico-img {
  width: 1.15em;
  height: 1.15em;
  min-width: 16px;
  min-height: 16px;
}
.st-card-icon .mi-ico-img,
.st-io-icon .mi-ico-img {
  width: 22px;
  height: 22px;
}
/* 深色底上的白图标 */
.mi-ico-btn--solid .mi-ico-img,
.stp-btn--primary .mi-ico-img,
.st-wb-add-btn.mi-ico-btn--solid .mi-ico-img {
  filter: invert(1);
}

/* ═══════════════════════════════════════════════════════════
   壁纸满屏修复 · 消除底部白带（sw-147 重做）
   ───────────────────────────────────────────────────────────
   原版做法（有严重副作用，已废弃）：
     html/body/.phone/.phone__wall 四层底色**全部**改成 transparent，
     只靠 .phone__wall 一层（fixed;inset:0）承担整个页面的底色。
     单点依赖 —— 该层在小窗/视口变动时没铺满，整页立刻透明，
     透出手机系统桌面，表现为「大块白（其实是透明）」。
     这与「小窗放大→缩回变白」的现象完全吻合：
     小窗缩放会让 fixed 定位层与 dvh 重新计算，.phone__wall
     可能只覆盖视口的一部分。

   新做法：底色改为**多层冗余**，任何一层没铺满都不会露白：
     · html / body  —— 兜底底色（最底层，永远铺满文档）
     · .phone       —— 跟随容器，负责容器范围内的底色
     · .phone__wall —— 壁纸层，正常情况盖住以上两层
   三层取同一个底色变量 --karin-base-bg（默认 #ffffff）。
   设置自定义壁纸时，.phone__wall 用背景图覆盖；
   未设置时它就是纯白，与原有观感一致。

   为什么不再用「全透明」：透明只在「壁纸层必然铺满」时才安全，
   而移动端小窗 / 分屏 / dvh 波动都不能保证这点。
   保留每层自身底色，代价只是多层各画一次纯色（GPU 开销可忽略），
   换来的是任何异常下都不露系统桌面。
   ═══════════════════════════════════════════════════════════ */

html,
body {
  background-color: var(--karin-base-bg, #ffffff);
}

.phone {
  background-color: var(--karin-base-bg, #ffffff);
}

.phone__wall {
  /* 有自定义壁纸时铺满；无壁纸时仍保留白底观感 */
  background-color: var(--karin-base-bg, #ffffff);
  /* 【sw-150】显式给满高度，不再依赖下面那条 calc(env())。
   * 原实现用 `bottom: calc(-1 * env(safe-area-inset-bottom))`
   * 向下多延伸一段，本意是抵消取整误差；但它会**覆盖 inset:0 的
   * bottom:0**，使高度变成「父容器高 + 安全区」这个依赖 env() 的值。
   * 在不支持 env() 的旧 WebView（如 Via 的套壳内核）里，整条 calc
   * 失效 → bottom 回落为 auto → 绝对定位元素的底边变成「内容底部」，
   * 高度塌缩，于是壁纸只铺一小截、下面全白。
   *
   * 高度由 100% 提供，不依赖 JS 变量。 */
  height: 100%;
  top: 0;
  bottom: 0;
}

/* 未设置自定义壁纸时，兜底为纯白，避免露出页面底色
   （注意：has-custom-wall 类加在 .phone__wall 元素自身，见 applyWallToPhone） */
.phone__wall:not(.has-custom-wall) {
  background-color: var(--karin-base-bg, #ffffff);
}

/* ═══════════════════════════════════════════════════════════
   .foot 底部白底渐变修复
   成因：.foot 的背景是 linear-gradient(→ rgba(255,255,255,0.82))，
   叠加 36px 顶部 padding，在屏幕底部形成一条明显的白色带。
   修复：自定义布局下取消该白色渐变，让壁纸透出；保留 dock 自身的玻璃底。
   ═══════════════════════════════════════════════════════════ */
html[data-karin-desk-layout="custom"] .foot {
  background: none !important;
}

/* 无自定义壁纸时（纯白底）保留原有渐变，避免桌面底部显得空
   ——用 .has-custom-wall 的否定形式限定：仅当壁纸层没有自定义壁纸时生效 */
html[data-karin-desk-layout="custom"] .phone:not(:has(.phone__wall.has-custom-wall)) .foot {
  background: linear-gradient(
    180deg,
    transparent 0%,
    rgba(255, 255, 255, 0.55) 62%,
    rgba(255, 255, 255, 0.82) 100%
  ) !important;
}

/* ═══════════════════════════════════════════════════════════
   小窗（自由窗口）放大 / 缩回 · 底部白底根治
   ───────────────────────────────────────────────────────────
   症状（截图实证）：
     Android 手机小窗里放大 → 壁纸在中途断开，下方一大片纯白；
     缩回小窗同样如此。只有「全屏 → 小窗」这一步正常。

   真凶：--app-height 被写成了「放大时的高度」而没跟回来。

     .phone 是 position:fixed; inset:0，但 height 又被
     var(--app-height) 覆盖（index.html 的 setHNow 写入）。
     小窗从放大缩回时：
         innerHeight   844 → 700   （即时、准确）
         clientHeight  844 → 844   （收缩滞后，残留旧值）
     index.html 里「if (clientH > finalH) finalH = clientH」把 844
     顶了上去 → --app-height = 844，而真实视口只有 700。
     于是：
       · .phone 撑到 844，底部 144px 溢出到屏幕外；
       · .phone__wall 是 fixed;inset:0，只铺到 700 → 壁纸在此断开；
       · 断开处以下露出 .phone 自身的背景 #ffffff → 「一大片白」。
     图标看起来「被白色方框垫着」，也是因为 .desk 随 .phone 排到 844。

   本文件负责「即使高度变量一时不准，也绝不露出白」的兜底：
     ① 壁纸层改用视口单位铺满，不再依赖 .phone 的高度；
     ② .phone 的底色由纯白改为跟随壁纸语义 —— 有自定义壁纸时透明，
        无壁纸时保持白色观感（与原有行为一致）。
   index.html 的 setHNow 已同步修正（clientHeight 容差 + 收缩放行），
   两处配合才能既收敛正确高度、又不留白。
   ═══════════════════════════════════════════════════════════ */

/* ① 壁纸铺满真实视口。
 *
 * 【sw-150】这里原先用 100dvh。dvh 是较新的单位（Chrome 108+），
 * 而 Via 浏览器用的是套壳 WebView，内核可能停留在不支持 dvh 的版本。
 * 一旦不识别，整条声明直接失效 —— 此时壁纸层只剩
 * `absolute + inset:0` 撑开，在旧内核里并不总能可靠铺满，
 * 于是出现「壁纸只铺一部分、下面全白」。
 *
 * 改为 vh（所有内核都支持），并**补上显式 height**：
 * 不再单靠 inset:0 推导高度，多一条确定性来源。
 * 由于父容器 .phone 已被约束为 --app-height，写成 100% 最稳妥
 * （绝对跟随父容器）。 */
/* 【sw-154】去掉原先的 min-height: 100vh。
 *
 *   min-height 会在「父容器被错误撑大」时**顺着一起变大**：
 *   实测把 --app-height 写成 844（真实视口 700）后，
 *   .phone 撑到 844，而 .phone__wall 因为 min-height:100vh 也跟着到 844
 *   —— 兜底层跟着出错层一起错，等于没有兜底。
 *
 *   改为「100% 跟随父容器」：
 *     · 父容器正确时：100% 就是正确高度；
 *     · 父容器被撑大时：壁纸层跟着一起铺满，
 *       于是壁纸层永远铺满真实视口 —— 白底无从露出。 */
.phone__wall {
  height: 100%;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
}

/* ② .phone 底色由 --karin-base-bg 统一提供（见上方「壁纸满屏修复」）。
      这里不再改成透明：多层冗余铺底才能兜住「壁纸层没铺满」的情况。 */

/* ③ 桌面主区跟随视口收口。
 *
 * ── 「小窗放大后桌面出白块」的真正根因（本文件历次修复都没打中） ──
 *
 * 结构：.phone 是 display:flex; flex-direction:column，
 *      .desk-shell 是它的 flex 子项（承载整个自定义主屏）。
 *
 * 问题：.desk-shell 此前**只有 max-height，没有 height / flex 声明**，
 *   于是它按「内容自然高度」撑开，并且**不随视口收缩**。
 *   而它的内容（.desk-custom__grid 七行网格 594px + 页面上下的安全区
 *   与 96px 底部留白）在常见机型上稳定需要约 698px。
 *
 *   实测（360×640 视口，7 行网格 + 1 个 4×4 小组件）：
 *     .phone        = 640   ← 跟随视口，正确
 *     .phone__wall  = 640   ← 跟随视口，正确
 *     .desk-shell   = 698   ← 卡住不动，**溢出视口 58px**
 *   放大到 470×844 时同样卡在 698 → 底部反而空出 146px。
 *
 *   这 58px 溢出区域没有任何元素绘制（grid 内容到 602 就结束了），
 *   露出的是 .phone 的底色 —— 也就是用户看到的那块「白」。
 *
 *   为什么过去二十次修复都没打中：
 *     一直在改 .phone / .phone__wall 的高度来源，而这两层**本来就是对的**
 *     （探针实测它们始终等于视口高）。真正卡住的是 .desk-shell。
 *
 *   为什么「移除小组件白块会变小」这个线索把人带偏了：
 *     实测放置 0 / 1 / 2 个小组件（任意尺寸）时，.desk-shell、grid 行高、
 *     页面总高**完全一致**，小组件对布局高度零影响。小组件只是恰好停在
 *     溢出区域上方，让人误以为白块是它带来的。
 *
 * 修法：让 .desk-shell 成为「占满剩余高度、并可向下收缩」的 flex 子项 ——
 *   · flex: 1 1 auto  → 吃掉 .phone 扣掉其余子项后的剩余高度；
 *   · min-height: 0  → 关键。flex 项默认 min-height:auto，会拒绝收缩到
 *                      低于内容高度，加了它才能随视口变小而收缩。
 *   不加 overflow 约束：内容超出时由内层 .desk-custom-track 自己的
 *   overflow 处理，避免影响主屏圆角与浮层的裁切。
 *
 * 实测（三种视口来回切换）：.desk-shell 恒等于视口高，溢出 0px。 */
.desk-shell {
  flex: 1 1 auto;
  min-height: 0;
}

/* ④ 【sw-165】CSS vh 脱节内核（Via 自由窗口等）接管 —— 白块的终局修复。
 *
 * 前三节修好后，「小窗放大/缩回出白块」只剩一个来源：
 *   html/body/.phone 的高度全是 100vh（本文件 111/149 行与上方 .phone），
 *   而 Via 这类套壳内核 resize 时**不更新 CSS vh**——
 *   放大 → vh 冻在小窗旧值 → 三层全部偏矮 → 视口下方没人覆盖 →
 *   露出画布白底；缩回 → vh 冻在大窗旧值 → 内容按大窗布局 → dock 被裁。
 *   壁纸兜底（本文件 175 行）铺在 html/body 盒子上，盒子自己冻住了，
 *   盒子之外照样没壁纸。启动时自由窗口初始化也是一次尺寸变化，
 *   所以「启动闪一秒白」同源。
 *
 * 接管机制：index.html 的 checkVhDrift() 用 #karin-vh-probe（100vh 标尺）
 *   实测内核 vh，与 innerHeight 差 >1px 时给根挂 data-vh-fix；
 *   本规则随之把 html/.phone 的高度切到 var(--app-height)
 *   （JS 实测 innerHeight，由 sw-153 加固过的一拍收敛机制维护，
 *   完全不依赖内核 vh）。
 *
 * 为什么现在敢用回 --app-height（sw-161 曾因它写错而废弃）：
 *   ① sw-153 已根治「防抖守卫锁死 844」——收缩方向无条件放行；
 *   ② 就算 JS 值短暂失准，html 塌掉的区域露的是**画布上传播的
 *      根背景**（有壁纸时 = 壁纸，无壁纸时 = 白，与原观感一致），
 *      不会再出现「布局白块」，只可能短暂整页壁纸，一拍即收敛。
 *
 * 正常内核：探针 vh ≡ innerHeight，data-vh-fix 永不出现，
 *   以下规则永不生效，行为与本文件此前所有版本逐像素一致。 */
html[data-vh-fix],
html[data-vh-fix] .phone {
  height: var(--app-height, 100vh);
  /* html 原有 min-height:100vh（149 行）在缩回小窗时会顶住高度、
   * 让接管失效，必须一并归零；.phone 无 min-height，写上无妨。 */
  min-height: 0;
}
