/* ============================================================================
 * glass.css —— 液态玻璃 (Liquid Glass) 可复用样式库
 * ----------------------------------------------------------------------------
 * 设计目标
 *   1. 零依赖：没有图片、没有外部字体、没有 CDN，离线双击即可工作。
 *   2. 全部参数集中在 :root 的自定义属性里，改变量即改外观。
 *   3. 类名语义化：.glass / .glass-panel / .glass-modal / .glass-btn / .glass-input
 *   4. 场景与主题解耦：
 *        · .scene-*   只画背景（天气天空）
 *        · [data-weather] + [data-time]  只改「玻璃与文字」的配色 token
 *      JS 只负责在 <html> 上写 data-weather / data-time / data-sim，其余全交给 CSS。
 *   5. 光有两种来源：
 *        · 固定光：::before 的左上柔光 + ::after 的折射边缘
 *        · 运动光：鼠标当光源，独立成 .glass__light 一层，
 *          JS 只写 --lx / --ly 两个"位移量"（.glass[data-light]）
 *
 * 快速上手
 *   <html lang="zh-CN" data-weather="clear" data-time="day">
 *   <link rel="stylesheet" href="glass.css">
 *   <div class="glass glass-panel glass--thin">…</div>
 *
 * 想只要玻璃、不要天气？
 *   单独引入本文件也能用：.glass 系列不依赖任何 data-* 属性，
 *   缺省走 :root 里的中性 token。
 *
 * 目录
 *   §1  参数（设计 token）
 *   §2  基础重置与页面骨架
 *   §3  玻璃本体与厚度档位（含鼠标光源）
 *   §4  组件：面板 / 按钮 / 输入 / 徽标 / 分段控件 / 列表 / 对话框
 *   §5  SVG 折射滤镜嵌入层
 *   §6  天气场景（背景天空）
 *   §7  玻璃上的降水（驻留水珠 / 上下表面积雪 / 雷暴白闪）
 *   §8  主题 token（昼夜 × 天气）
 *   §9  景深对焦（弹窗打开时的「背景失焦 → 玻璃清晰」）
 *   §10 可用性、降级与减弱动效
 * ==========================================================================*/

/* ==========================================================================
   §1  参数（设计 token）—— 所有可调项都在这里
   ========================================================================== */
:root {
  /* --- 模糊 / 色彩 --- */
  --glass-blur-base: 12px;          /* 毛玻璃基础模糊半径（面板） */
  --glass-thickness-blur: 0px;      /* 由厚度档位叠加的模糊增量（薄=负/厚=正） */
  --glass-blur: calc(var(--glass-blur-base) + var(--glass-thickness-blur));
  --glass-saturate: 1.6;            /* 饱和度提升 */
  --glass-brightness: 1.04;         /* 亮度微调 */
  --glass-contrast: 1;              /* 对比度（雾天会调低） */
  --glass-alpha: 0.10;              /* 玻璃底色不透明度（越低越透明） */
  --glass-tint: 255 255 255;        /* 玻璃底色（RGB，空格分隔，便于套 alpha） */
  --glass-text: #ffffff;            /* 玻璃上的主文字色 */

  /* --- 鼠标即光源：光标照到玻璃上的高光会跟着走 --- */
  /* 实现要点：光源是"独立的一层，只做 transform 位移"，因此指针移动时
     只动合成器，不触发重绘 —— 这是从卡顿变顺滑的关键。 */
  --lx: 0px;                        /* 光斑偏移（px，由 JS 写入） */
  --ly: 0px;
  --glass-light: 0.5;               /* 光源强度 0~1，0 = 关闭 */
  --glass-light-radius: 38%;        /* 镜面高光范围（相对那颗光透镜自身） */
  --light-transition: 420ms;        /* 光源跟随的阻尼 */

  /* --- 高光 / 折射边缘 --- */
  --glass-highlight: 0.5;           /* 内部柔光强度 0~1 */
  --glass-rim: 0.85;                /* 1px 高光描边强度 0~1 */
  --glass-rim-color: 255 255 255;
  --glass-rim-contrast: 0.35;       /* 描边下方的暗色反差线，制造「厚度折射」 */
  --glass-inner-shadow: 0.3;        /* 内阴影强度 */
  --glass-inner-shadow-color: 0 0 0; /* 内阴影颜色 */
  --glass-edge-inset: 1.2px;        /* 内阴影偏移：越大越像厚玻璃 */

  /* --- 外阴影（多层柔和，避免生硬黑影） --- */
  --glass-shadow-y: 18px;           /* 阴影整体纵深 */
  --glass-shadow-color: 8 22 48;    /* 阴影基色，带一点冷调 */
  --glass-shadow-alpha: 0.30;       /* 阴影总强度 */
  --glass-shadow-1: 0 1px 2px rgb(var(--glass-shadow-color) / calc(var(--glass-shadow-alpha) * 0.45));
  --glass-shadow-2: 0 6px 16px -8px rgb(var(--glass-shadow-color) / calc(var(--glass-shadow-alpha) * 0.9));
  --glass-shadow-3:
    0 calc(var(--glass-shadow-y) * 1.0) calc(var(--glass-shadow-y) * 2.2) calc(var(--glass-shadow-y) * -1.1)
      rgb(var(--glass-shadow-color) / var(--glass-shadow-alpha)),
    0 calc(var(--glass-shadow-y) * 0.35) calc(var(--glass-shadow-y) * 0.8) calc(var(--glass-shadow-y) * -0.4)
      rgb(var(--glass-shadow-color) / calc(var(--glass-shadow-alpha) * 0.55));

  /* --- 圆角 / 尺寸 --- */
  --glass-radius-base: 22px;
  --glass-radius: var(--glass-radius-base);
  --glass-radius-sm: calc(var(--glass-radius) * 0.55);
  --glass-radius-lg: calc(var(--glass-radius) * 1.5);
  --glass-radius-pill: 999px;
  --glass-pad: 1rem;
  --glass-gap: 0.75rem;

  /* --- 玻璃厚度（thin / regular / thick 只需改 thickness-blur） --- */
  --glass-thickness-blur: 0px;
  --glass-thickness-scale: 1;       /* 厚度视觉系数：影响内阴影与描边 */

  /* --- 折射扭曲（feTurbulence + feDisplacementMap） ---
     默认 0（关闭）：位移滤镜会在「玻璃背后的画面变化」时重算，
     而背景光斑/场景过渡一直在动 → 开着它是很贵的一项。
     想要的话把 demo 里的滑杆拉起来即可。 */
  --glass-refraction: 0;
  --glass-refraction-freq: 0.03;    /* 湍流频率：越大颗粒越细 */
  --glass-refraction-octaves: 2;    /* 湍流细节层数 */
  --glass-refraction-softness: 0.45;/* 噪声柔化（px） */
  --glass-refraction-opacity: 0.9;
  --glass-refraction-uri: none;     /* 由 weather.js 写入内联 SVG 滤镜的 data URI */

  /* --- 景深对焦（弹窗打开时的背景失焦） --- */
  --focus-blur: 0px;                /* 背景模糊量，仅由 .is-backdrop-blurred 驱动 */
  --focus-blur-amount: 10px;        /* 打开弹窗时背景应达到的模糊量（雾天会更高） */
  --focus-dim: 0;                   /* 背景变暗量 0~1 */
  --focus-dim-amount: 0.30;
  --focus-desat: 0;                 /* 背景降饱和量 0~1 */
  --focus-desat-amount: 0.45;
  --focus-scale-amount: 1.015;      /* 背景轻微推近，模拟镜头对焦行程 */
  --focus-duration: 380ms;
  --focus-ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --glass-sweep-duration: 150ms;    /* 弹窗落地时的玻璃反光扫过 */

  /* --- 雨：驻留水珠（像隔着窗玻璃看雨，不是雨丝） --- */
  --rain-count: 72;                 /* 建议值；实际数量由 JS 生成 */
  --rain-opacity: 0.7;              /* 水珠整体不透明度 */
  --rain-speed: 1;                  /* 呼吸/流动速度倍率（越大越活跃） */
  --rain-width: 3px;                /* 水珠基准直径 */
  --rain-length: 18px;              /* 泪痕基准长度 */
  --rain-blur: 0.4px;               /* 水珠柔化，营造凸透镜感 */
  --rain-sheen: 0.55;               /* 水珠高光强度 */
  --rain-fade: 8%;                  /* 顶部/底部淡出高度（很浅，水珠要贴到边） */

  /* --- 雪：上下表面积雪 --- */
  --snow-opacity: 0.85;
  --snow-speed: 1;                  /* 飘雪速度倍率 */
  --snow-accum: 1;                  /* 积雪厚度总倍率（0 = 不积雪） */
  --snow-height-top: 18px;          /* 上沿积雪高度 */
  --snow-height-bottom: 7px;        /* 下沿积雪高度 */
  --snow-soft: 0.6px;               /* 积雪边缘柔化 */

  --thunder-flash-max: 0.10;        /* 雷暴白闪峰值透明度（保持很淡） */
  --thunder-flash-duration: 2.6s;   /* 两次白闪之间的间隔 */

  /* --- 动效 --- */
  --glass-ease: cubic-bezier(0.2, 0.75, 0.3, 1);
  --glass-pop: cubic-bezier(0.34, 1.56, 0.64, 1);
  --glass-dur: 260ms;
  --dur-theme: 900ms;               /* 天气/昼夜切换过渡时长 */

  /* --- 层级 --- */
  --z-bg: 0;
  --z-orbs: 1;
  --z-glass-layer: 0;               /* .glass 内部图层的层级基准 */
  --z-content: 3;
  --z-rain: 40;
  --z-flash: 60;
  --z-overlay: 1000;
  --z-badge: 900;

  /* --- 字体 --- */
  --font-ui: system-ui, -apple-system, "Segoe UI", "PingFang SC", "Hiragino Sans GB",
    "Microsoft YaHei", "Noto Sans SC", Roboto, sans-serif;
  --font-num: ui-monospace, "SF Mono", "Cascadia Mono", Menlo, Consolas, monospace;
  --fs-body: 15px;
}

/* --------------------------------------------------------------------------
   厚度档位：只在 .glass 作用域内覆写厚度系数
   -------------------------------------------------------------------------- */
.glass--thin {
  --glass-thickness-blur: -4px;
  --glass-thickness-scale: 0.6;
  --glass-alpha: 0.06;
  --glass-inner-shadow: 0.18;
  --glass-rim: 0.62;
  --glass-shadow-y: 10px;
  --glass-shadow-alpha: 0.20;
}

.glass--regular {
  --glass-thickness-blur: 0px;
  --glass-thickness-scale: 1;
}

.glass--thick {
  --glass-thickness-blur: 9px;
  --glass-thickness-scale: 1.5;
  --glass-alpha: 0.15;
  --glass-inner-shadow: 0.45;
  --glass-rim: 1;
  --glass-shadow-y: 26px;
  --glass-shadow-alpha: 0.34;
}

/* 卡片式的小玻璃（日历格、事件条）用这一档 */
.glass--micro {
  --glass-blur: 8px;
  --glass-saturate: 1.35;
  --glass-alpha: 0.07;
  --glass-radius: 12px;
  --glass-inner-shadow: 0.22;
  --glass-shadow-y: 6px;
  --glass-shadow-alpha: 0.16;
  --glass-light: 0.34;              /* 小元素上的光斑收敛一些，避免糊成一片 */
  --glass-light-radius: 78%;
}

/* ==========================================================================
   §2  基础重置与页面骨架
   ========================================================================== */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  min-height: 100%;
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: 1.55;
  color: var(--ui-text, #fff);
  background: var(--bg, linear-gradient(#0f172a, #020617));
  transition: background var(--dur-theme) var(--glass-ease), color var(--dur-theme) var(--glass-ease);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
  overscroll-behavior: none;   /* 弹窗打开锁定滚动时，避免 iOS 回弹穿透 */
}

/* 页面滚动容器（弹窗打开时对它加 filter 实现背景失焦） */
.page {
  position: relative;
  z-index: 1;
  min-height: 100vh;
  min-height: 100dvh;
  padding: clamp(16px, 4vw, 40px);
  display: flex;
  flex-direction: column;
  gap: clamp(16px, 2.4vw, 28px);
  transform-origin: 50% 40%;
}

/* 固定在天幕上的背景层，不参与滚动，也不参与失焦（模糊纯色渐变没有意义） */
.bg-stack {
  position: fixed;
  inset: 0;
  z-index: var(--z-bg);
  overflow: hidden;
  pointer-events: none;
}

/* 背景上的氛围光斑：让玻璃有「可以折射的东西」，雨天不明显、晴天很通透 */
.bg-orbs {
  position: fixed;
  inset: -10%;
  z-index: var(--z-orbs);
  pointer-events: none;
  display: block;
  /* 相机对焦时背景轻微推近，弹窗打开时会跟着 .page 一起动 */
  transform-origin: 50% 40%;
  transition: transform var(--focus-duration) var(--focus-ease);
}

.orb {
  position: absolute;
  border-radius: 50%;
  /* 大半径模糊很贵，所以只在"不动"的时候才划算：
     静态光斑只光栅化一次，之后只是合成。 */
  filter: blur(42px);
  opacity: var(--orb-opacity, 0.55);
}

/* 漂移是可选增强：一旦光斑在动，它背后的每一个 backdrop-filter
   都要每帧重算 —— 这就是"滚动/移动时特别卡"的根因。
   需要氛围感时给 .bg-orbs 加上 --animated 即可（默认不加）。 */
.bg-orbs--animated .orb {
  animation: orb-drift var(--orb-duration, 26s) ease-in-out infinite alternate;
}

.orb--sun {
  width: max(38vw, 240px);
  aspect-ratio: 1;
  left: var(--orb-x, 68%);
  top: var(--orb-y, 8%);
  background: radial-gradient(
    circle at 50% 50%,
    rgb(255 236 180 / 0.95),
    rgb(255 205 110 / 0.45) 45%,
    transparent 72%
  );
}

.orb--moon {
  width: max(30vw, 180px);
  aspect-ratio: 1;
  left: var(--orb-x, 70%);
  top: var(--orb-y, 6%);
  background: radial-gradient(
    circle at 50% 50%,
    rgb(226 238 255 / 0.9),
    rgb(150 180 255 / 0.32) 46%,
    transparent 74%
  );
  --orb-opacity: 0.5;
}

.orb--haze {
  width: max(60vw, 320px);
  aspect-ratio: 1;
  left: var(--orb-x, 12%);
  top: var(--orb-y, 55%);
  background: radial-gradient(
    circle at 50% 50%,
    rgb(190 210 235 / 0.5),
    transparent 70%
  );
  --orb-opacity: 0.35;
  --orb-duration: 34s;
}

/* 玻璃上的细颗粒：抵消大面积渐变可能出现的色带，同时增加「玻璃质地」。
   注意：它带 mix-blend-mode，一旦页面里有动画元素，整屏混合就要重算，
   所以默认不插入这个元素（less 用在静态页面里，或者接受这点开销）。 */
.grain {
  position: fixed;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: 0.05;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* 无 SVG 滤镜能力的浏览器（或性能较弱的设备）直接隐藏颗粒 */
html[data-sim="low"] .grain {
  display: none;
}

/* ==========================================================================
   §3  玻璃本体
   .glass 负责玻璃质感；.glass-panel / .glass-modal 等负责尺寸与布局。
   内部图层顺序（全部 pointer-events:none，不影响内容交互）：
     0  折射层（可选，SVG 滤镜只作用于背景，内容保持锐利）
     .field--drops  降水层（雨滴 / 雪痕）
     .field--sheen  高光扫过
     ::before       内部柔光（左上 → 右下）
     ::after        折射边缘（上/左亮线，下/右暗线）
   ========================================================================== */
.glass {
  position: relative;
  isolation: isolate;             /* 把内部层级关在玻璃里，避免污染外部堆叠 */
  overflow: hidden;               /* 裁掉溢出的高光与雨滴，让玻璃像一整块 */
  border-radius: var(--glass-radius);
  border: 0;
  color: var(--glass-text);
  background:
    linear-gradient(
      158deg,
      rgb(var(--glass-tint) / calc(var(--glass-alpha) * 1.15)) 0%,
      rgb(var(--glass-tint) / calc(var(--glass-alpha) * 0.6)) 42%,
      rgb(var(--glass-tint) / calc(var(--glass-alpha) * 0.88)) 100%
    );
  box-shadow:
    inset 0 0 0 1px rgb(var(--glass-rim-color) / var(--glass-rim)),
    inset 0 var(--glass-edge-inset) calc(var(--glass-edge-inset) * 1.6)
      calc(var(--glass-edge-inset) * -0.5)
      rgb(var(--glass-rim-color) / calc(var(--glass-rim) * 0.5)),
    inset 0 calc(var(--glass-edge-inset) * -1.1) calc(var(--glass-edge-inset) * 2.4)
      calc(var(--glass-edge-inset) * -1.6)
      rgb(var(--glass-inner-shadow-color) / calc(var(--glass-inner-shadow) * var(--glass-thickness-scale))),
    var(--glass-shadow-1),
    var(--glass-shadow-2),
    var(--glass-shadow-3);
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate))
    brightness(var(--glass-brightness)) contrast(var(--glass-contrast));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate))
    brightness(var(--glass-brightness)) contrast(var(--glass-contrast));
  transition:
    background var(--dur-theme) var(--glass-ease),
    box-shadow var(--dur-theme) var(--glass-ease),
    backdrop-filter var(--dur-theme) var(--glass-ease),
    -webkit-backdrop-filter var(--dur-theme) var(--glass-ease),
    border-radius var(--glass-dur) var(--glass-ease),
    color var(--dur-theme) var(--glass-ease),
    transform var(--glass-dur) var(--glass-ease);
}

/* 内部柔光：一层很淡的白色渐变（左上 → 右下）。
   这里是"静态光"；跟着鼠标走的"动态光"由 .glass__light 那一层负责
   （原因见该层的注释：动态光必须是独立合成层，不能改这里的背景）。 */
.glass::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  border-radius: inherit;
  background:
    radial-gradient(
      130% 110% at 8% 0%,
      rgb(255 255 255 / calc(var(--glass-highlight) * 0.5)) 0%,
      rgb(255 255 255 / calc(var(--glass-highlight) * 0.12)) 32%,
      transparent 62%
    ),
    linear-gradient(
      152deg,
      rgb(255 255 255 / calc(var(--glass-highlight) * 0.20)) 0%,
      rgb(255 255 255 / 0) 44%,
      rgb(255 255 255 / calc(var(--glass-highlight) * 0.07)) 100%
    );
  opacity: 1;
  transition: opacity var(--dur-theme) var(--glass-ease);
}

/* 折射边缘：上/左亮、下/右暗的仿真折射线，
   其中"迎光那一段"会随鼠标光源加强 —— 光打在玻璃棱上会亮起来。 */
.glass::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  border-radius: inherit;
  padding: var(--glass-edge-inset);
  /* 静态折射边缘：上/左亮、下/右暗。不随光源移动 —— 见 .glass__light。 */
  background: linear-gradient(
    152deg,
    rgb(var(--glass-rim-color) / calc(var(--glass-rim) * 0.95)) 0%,
    rgb(var(--glass-rim-color) / calc(var(--glass-rim) * 0.18)) 26%,
    rgb(var(--glass-rim-contrast) / 0) 52%,
    rgb(var(--glass-rim-color) / calc(var(--glass-rim) * 0.14)) 78%,
    rgb(var(--glass-rim-color) / calc(var(--glass-rim) * 0.75)) 100%
  );
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
  transition: opacity var(--dur-theme) var(--glass-ease);
}

/* --------------------------------------------------------------------------
   跟随鼠标的光源层（.glass__light，由 weather.js 只注入给大面板）
   --------------------------------------------------------------------------
   为什么单独一层？
     之前它写在 ::before 的背景里（background-position 随指针变），
     那属于"重绘"：指针每动一帧，整块玻璃的渐变 + box-shadow 都要重画。
     现在改成「静态渐变 + transform 位移」，位移走合成器，指针移动不再重绘。
   层比玻璃大一圈，光斑可以在玻璃内部自由游走，多出来的部分被 overflow 裁掉。

   选择器为什么写成 `.glass > .glass__light`（而不是单个类名）？
     这一层是 JS 用 `class="glass__layer glass__light"` 注入的，所以它同时吃到
     `.glass__layer { inset: 0; z-index: 0 }` 那条基础规则。两条选择器优先级
     相同，而 `.glass__layer` 在文件里更靠后 —— 结果就是 inset/z-index 被它盖掉，
     光源层退化成"和玻璃一样大"，光斑一偏移就滑出玻璃被裁掉，看上去像是光源失灵。
     这里用两段选择器把优先级抬上去，`inset: -52%` 才真正生效。
   -------------------------------------------------------------------------- */
.glass > .glass__light {
  z-index: 1;
  /* 一颗跟着指针走的"光透镜"——不是浮在玻璃上发光的球，
     而是玻璃被光打到的那一点：透镜把身后的画面微微折射提亮
     （backdrop-filter），上面叠一颗锐利的镜面高光。看上去是
     "光照到玻璃、玻璃在那点上反光并折射"，而不是玻璃自己在发光。
     选择器写成 .glass > .glass__light 是为了压过 .glass__layer 的
     inset:0/z-index:0（两者同优先级、.glass__layer 更靠后）——
     单类名会被它盖回去。 */
  width: 120px;
  height: 120px;
  left: 50%;
  top: 50%;
  margin-left: -60px;
  margin-top: -60px;
  border-radius: 50%;
  opacity: var(--glass-light, 0.5);
  -webkit-backdrop-filter: blur(1.4px) brightness(1.12) saturate(1.25);
  backdrop-filter: blur(1.4px) brightness(1.12) saturate(1.25);
  background:
    /* 锐利的镜面高光（偏左上，像光打在曲面上的亮点） */
    radial-gradient(
      circle at 38% 30%,
      rgb(255 255 255 / 0.95) 0%,
      rgb(255 255 255 / 0.35) 18%,
      transparent var(--glass-light-radius, 38%)
    ),
    /* 透镜边缘一圈淡淡的折射晕 */
    radial-gradient(
      circle at 50% 50%,
      rgb(255 255 255 / 0) 56%,
      rgb(220 235 255 / 0.22) 78%,
      rgb(255 255 255 / 0) 100%
    );
  transform: translate3d(var(--lx, 0px), var(--ly, 0px), 0);
  transition: transform var(--light-transition) var(--glass-ease),
    opacity var(--light-transition) var(--glass-ease);
  will-change: transform;
}

/* 没有鼠标（触屏）或用户关了灯：整层收掉，别留一块假光斑 */
.glass[data-light="off"] .glass__light {
  display: none;
}

/* 玻璃表面的光源：没有鼠标（触屏）或用户关了灯时，把跟随光斑关掉，
   只保留固定的内部柔光，免得画面上留一块假的死光斑。 */
.glass[data-light="off"] {
  --glass-light: 0;
}

/* 让内容始终压在玻璃图层之上 */
.glass > *:not(.glass__layer) {
  position: relative;
  z-index: var(--z-content);
}

.glass__layer {
  position: absolute;
  inset: 0;
  z-index: var(--z-glass-layer);
  pointer-events: none;
  border-radius: inherit;
}

/* --------------------------------------------------------------------------
   §5  SVG 折射扭曲层
   只有 html[data-refraction="on"] 时才由 weather.js 自动注入（低配 / 减弱动效
   / 小屏都不注入）。它作用的是 backdrop-filter 的结果，所以文字与交互元素
   保持绝对锐利，只有玻璃边缘被"折射"。

   两个要点：
     1. 用 backdrop-filter: blur(0.001px) 把身后的画面"取"到本层，再用
        filter: url(#...) 对这份采样做 feDisplacementMap。
     2. 用环形遮罩（padding + mask-composite: exclude）把扭曲限制在边缘一圈，
        玻璃中心不参与扭曲 —— 这就是真实玻璃的折射分布，也最省性能。
   -------------------------------------------------------------------------- */
.glass__refraction {
  z-index: var(--z-glass-layer);
  padding: var(--glass-edge-inset);
  opacity: var(--glass-refraction-opacity);
  -webkit-backdrop-filter: blur(0.001px);
  backdrop-filter: blur(0.001px);
  filter: var(--glass-refraction-uri, none);
  -webkit-mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  mask:
    linear-gradient(#000 0 0) content-box,
    linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask-composite: exclude;
}

.glass[data-refraction="off"] .glass__refraction {
  display: none;
}

/* 有驻留水珠时，折射要覆盖整片玻璃（水珠把身后的景色整体扭一下），
   而不是只作用在边缘一圈 —— 边缘环形的成本更低，但对水珠不好看。 */
.glass--beads .glass__refraction {
  padding: 0;
  -webkit-mask: linear-gradient(#000 0 0);
  mask: linear-gradient(#000 0 0);
  -webkit-mask-composite: source-over;
  mask-composite: add;
}

/* ==========================================================================
   §4  组件
   ========================================================================== */

/* --- 面板：页面级别的玻璃卡片 --- */
.glass-panel {
  padding: clamp(16px, 2.4vw, 26px);
  border-radius: var(--glass-radius-lg);
}

.glass-panel--flush {
  padding: 0;
}

/* --- 按钮 --- */
.glass-btn {
  --glass-radius: var(--glass-radius-pill);
  --glass-alpha: 0.16;
  --glass-highlight: 0.62;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  min-height: 44px;                /* 移动端可点区域 */
  padding: 0.62em 1.15em;
  font: inherit;
  font-weight: 600;
  font-size: 0.94rem;
  letter-spacing: 0.01em;
  line-height: 1.2;
  color: var(--glass-text);
  cursor: pointer;
  -webkit-user-select: none;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition:
    transform var(--glass-dur) var(--glass-pop),
    box-shadow var(--glass-dur) var(--glass-ease),
    background var(--dur-theme) var(--glass-ease),
    opacity 200ms linear;
}

.glass-btn:hover {
  transform: translateY(-1px);
  --glass-alpha: 0.22;
  --glass-highlight: 0.8;
}

.glass-btn:active {
  transform: translateY(0) scale(0.972);
  --glass-alpha: 0.26;
}

.glass-btn:focus-visible,
.glass-input:focus-visible,
.glass-dialog :focus-visible {
  outline: 2px solid rgb(var(--glass-rim-color) / 0.85);
  outline-offset: 2px;
}

.glass-btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
}

/* 主按钮：用一点主题强调色，其余仍保持玻璃质感 */
.glass-btn--accent {
  --glass-alpha: 0.26;
  background:
    linear-gradient(
      158deg,
      rgb(var(--accent, 120 190 255) / 0.34) 0%,
      rgb(var(--accent, 120 190 255) / 0.16) 60%,
      rgb(var(--glass-tint) / calc(var(--glass-alpha) * 0.8)) 100%
    );
}

.glass-btn--ghost {
  --glass-alpha: 0.05;
  --glass-rim: 0.35;
}

.glass-btn--icon {
  min-width: 44px;
  padding: 0.5em;
  border-radius: var(--glass-radius-pill);
}

/* --- 输入 --- */
.glass-input {
  --glass-radius: 14px;
  --glass-alpha: 0.12;
  --glass-blur: 8px;
  width: 100%;
  min-height: 44px;
  padding: 0.6em 0.85em;
  font: inherit;
  font-size: 0.95rem;
  color: var(--glass-text);
  background: rgb(var(--glass-tint) / calc(var(--glass-alpha) * 0.9));
  border: 1px solid rgb(var(--glass-rim-color) / calc(var(--glass-rim) * 0.45));
  border-radius: var(--glass-radius);
  box-shadow:
    inset 0 1px 0 rgb(var(--glass-rim-color) / 0.14),
    inset 0 calc(var(--glass-edge-inset) * -1) calc(var(--glass-edge-inset) * 3)
      rgb(var(--glass-inner-shadow-color) / calc(var(--glass-inner-shadow) * 0.6));
  -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
  transition:
    border-color var(--glass-dur) var(--glass-ease),
    background var(--dur-theme) var(--glass-ease),
    box-shadow var(--glass-dur) var(--glass-ease);
}

.glass-input::placeholder {
  color: rgb(var(--glass-text-rgb, 255 255 255) / 0.55);
}

.glass-input:hover {
  border-color: rgb(var(--glass-rim-color) / calc(var(--glass-rim) * 0.65));
}

.glass-input:focus {
  border-color: rgb(var(--glass-rim-color) / 0.9);
  box-shadow:
    inset 0 1px 0 rgb(var(--glass-rim-color) / 0.2),
    0 0 0 3px rgb(var(--accent, 140 200 255) / 0.22);
}

/* 让输入框内的日期/时间选择器图标在深色场景下也看得清 */
.glass-input::-webkit-calendar-picker-indicator {
  filter: invert(var(--picker-invert, 0));
  opacity: 0.75;
}

/* --- 徽标 / 标签 --- */
.glass-badge {
  --glass-radius: var(--glass-radius-pill);
  --glass-alpha: 0.14;
  --glass-blur: 10px;
  --glass-shadow-alpha: 0.18;
  display: inline-flex;
  align-items: center;
  gap: 0.45em;
  padding: 0.34em 0.78em;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  border-radius: var(--glass-radius-pill);
  white-space: nowrap;
}

.glass-badge--sm {
  padding: 0.2em 0.55em;
  font-size: 0.7rem;
}

.glass-dot {
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  background: var(--accent, #8fd0ff);
  box-shadow: 0 0 8px rgb(var(--accent-rgb, 143 208 255) / 0.9);
  flex: 0 0 auto;
}

/* --- 分段控件（月份/视图切换） --- */
.glass-seg {
  --glass-radius: var(--glass-radius-pill);
  --glass-alpha: 0.10;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 3px;
  border-radius: var(--glass-radius-pill);
}

.glass-seg > .glass-btn {
  min-height: 38px;
  padding: 0.45em 0.9em;
  --glass-rim: 0.25;
  --glass-shadow-1: none;
  --glass-shadow-2: none;
  --glass-shadow-3: none;
}

.glass-seg > .glass-btn[aria-pressed="true"] {
  --glass-alpha: 0.3;
  --glass-rim: 0.8;
}

/* --- 列表 / 分隔线 --- */
.glass-hr {
  height: 1px;
  border: 0;
  margin: 0;
  background: linear-gradient(
    90deg,
    rgb(var(--glass-rim-color) / 0) 0%,
    rgb(var(--glass-rim-color) / 0.4) 30%,
    rgb(var(--glass-rim-color) / 0.4) 70%,
    rgb(var(--glass-rim-color) / 0) 100%
  );
}

.glass-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.glass-list > li + li {
  border-top: 1px solid rgb(var(--glass-rim-color) / 0.12);
}

/* 雨滴层 / 雪痕层 / 高光扫过层：都放进玻璃内部 */
.field {
  position: absolute;
  inset: 0;
  z-index: var(--z-rain);
  pointer-events: none;
  border-radius: inherit;
  overflow: hidden;
  opacity: 0;
  transition: opacity 600ms var(--glass-ease);
}

html[data-weather="rain"] .field--drops,
html[data-weather="thunder"] .field--drops,
html[data-weather="snow"] .field--drops {
  opacity: 1;
}

/* 降水/积雪层必须压在内容之下：z-index 0 < 人造光(::before=1 / ::after=2) < 文字(3)。
   这条放在 .field 之后（.field 默认 z-index 40），保证能覆盖掉基类的值，
   否则水珠/积雪会盖在日历文字、按钮上面。 */
.field--drops {
  z-index: 0;
}

/* 玻璃反光扫过：弹窗落地后 150ms 的一次性高光 */
.field--sheen {
  z-index: 4;
  opacity: 0;
  background: linear-gradient(
    104deg,
    transparent 38%,
    rgb(255 255 255 / 0.42) 47%,
    rgb(255 255 255 / 0.12) 52%,
    transparent 62%
  );
  transform: translateX(-60%);
}

.glass.is-sheening .field--sheen {
  animation: sheen-sweep var(--glass-sweep-duration) cubic-bezier(0.3, 0.7, 0.4, 1) both;
}

/* --- 对话框本体 --- */
.glass-modal {
  --glass-radius: var(--glass-radius-lg);
  --glass-alpha: 0.2;
  --glass-blur-base: 26px;          /* 弹窗永远锐利：模糊的是它身后的世界 */
  --glass-saturate: 1.9;
  --glass-highlight: 0.75;
  --glass-shadow-y: 34px;
  --glass-shadow-alpha: 0.42;
  width: min(92vw, 460px);
  padding: clamp(18px, 3vw, 26px);
  outline: none;
}

/* ==========================================================================
   §6  天气场景（背景天空）—— 只画背景，不参与玻璃配色
   ========================================================================== */
.scene {
  position: absolute;
  inset: 0;
  background-repeat: no-repeat;
  background-size: cover;
}

.scene--clear-day {
  background-image:
    radial-gradient(120% 80% at 78% 6%, rgb(255 244 214 / 0.95) 0%, rgb(255 224 160 / 0.35) 26%, transparent 58%),
    radial-gradient(140% 120% at 50% 120%, rgb(255 190 120 / 0.35) 0%, transparent 60%),
    linear-gradient(178deg, #4fa6f5 0%, #7cc0f8 42%, #b9e2ff 74%, #e7f4ff 100%);
}

.scene--clear-night {
  background-image:
    radial-gradient(80% 60% at 76% 10%, rgb(220 234 255 / 0.42) 0%, rgb(140 170 255 / 0.16) 28%, transparent 60%),
    radial-gradient(1.6px 1.6px at 12% 22%, rgb(255 255 255 / 0.9) 50%, transparent 51%),
    radial-gradient(1.4px 1.4px at 32% 12%, rgb(255 255 255 / 0.7) 50%, transparent 51%),
    radial-gradient(1.8px 1.8px at 58% 30%, rgb(255 255 255 / 0.8) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 84% 18%, rgb(255 255 255 / 0.65) 50%, transparent 51%),
    radial-gradient(1.5px 1.5px at 68% 52%, rgb(255 255 255 / 0.55) 50%, transparent 51%),
    linear-gradient(180deg, #050b23 0%, #0a1538 38%, #101f4d 66%, #16295f 100%);
}

.scene--cloudy {
  background-image:
    radial-gradient(120% 90% at 22% 18%, rgb(255 255 255 / 0.42) 0%, transparent 55%),
    radial-gradient(130% 100% at 78% 30%, rgb(226 234 245 / 0.38) 0%, transparent 58%),
    linear-gradient(178deg, #93a5bd 0%, #a9b8cc 34%, #c6d2e0 70%, #dde5ee 100%);
}

.scene--cloudy-night {
  background-image:
    radial-gradient(120% 90% at 26% 16%, rgb(150 170 200 / 0.28) 0%, transparent 60%),
    linear-gradient(180deg, #10151f 0%, #1b2330 40%, #2a3444 74%, #3a4658 100%);
}

.scene--fog {
  background-image:
    radial-gradient(90% 70% at 50% 40%, rgb(255 255 255 / 0.55) 0%, transparent 70%),
    linear-gradient(178deg, #b6bcc4 0%, #c8ced6 38%, #d9dee5 72%, #e8ebef 100%);
}

.scene--fog-night {
  background-image:
    radial-gradient(90% 70% at 50% 40%, rgb(160 178 200 / 0.3) 0%, transparent 72%),
    linear-gradient(180deg, #1a1f27 0%, #262c36 40%, #39404c 74%, #4a5260 100%);
}

.scene--rain {
  background-image:
    radial-gradient(120% 90% at 30% 10%, rgb(120 150 180 / 0.5) 0%, transparent 60%),
    radial-gradient(140% 120% at 50% 120%, rgb(30 50 75 / 0.5) 0%, transparent 65%),
    linear-gradient(178deg, #3b4c60 0%, #4a5d73 32%, #5c7086 66%, #6e8296 100%);
}

.scene--rain-night {
  background-image:
    radial-gradient(120% 90% at 30% 8%, rgb(60 90 130 / 0.45) 0%, transparent 62%),
    linear-gradient(180deg, #080d18 0%, #0d1626 40%, #152034 72%, #1d2b42 100%);
}

.scene--snow {
  background-image:
    radial-gradient(120% 90% at 24% 12%, rgb(255 255 255 / 0.7) 0%, transparent 58%),
    radial-gradient(140% 120% at 50% 120%, rgb(220 235 255 / 0.6) 0%, transparent 62%),
    linear-gradient(178deg, #9fb6cf 0%, #b9cbe0 34%, #d3e0ec 70%, #eef5fb 100%);
}

.scene--snow-night {
  background-image:
    radial-gradient(120% 90% at 24% 12%, rgb(180 205 240 / 0.3) 0%, transparent 60%),
    linear-gradient(180deg, #0b1220 0%, #131d31 40%, #1e2b44 72%, #2a3a56 100%);
}

.scene--thunder {
  background-image:
    radial-gradient(120% 90% at 62% 8%, rgb(190 205 230 / 0.34) 0%, transparent 55%),
    radial-gradient(150% 130% at 50% 130%, rgb(10 18 34 / 0.65) 0%, transparent 68%),
    linear-gradient(178deg, #1e2735 0%, #2b3648 30%, #3a4658 64%, #4b586b 100%);
}

.scene--thunder-night {
  background-image:
    radial-gradient(120% 90% at 62% 8%, rgb(120 150 220 / 0.3) 0%, transparent 58%),
    linear-gradient(180deg, #04070f 0%, #090f1c 38%, #101a2c 70%, #172439 100%);
}

/* 雨/雪/雷暴：叠加一层冷色与压暗，让玻璃更有「厚度」 */
html[data-weather="rain"] .scene::after,
html[data-weather="snow"] .scene::after,
html[data-weather="thunder"] .scene::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    rgb(20 40 70 / 0.18) 0%,
    rgb(10 25 50 / 0.32) 60%,
    rgb(6 18 40 / 0.44) 100%
  );
}

html[data-weather="fog"] .scene::after {
  content: "";
  position: absolute;
  inset: 0;
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  background: rgb(255 255 255 / 0.08);
}

/* 场景切换的平滑过渡：两层背景交替淡入，避免任何突兀跳变 */
.bg-stack > .scene {
  transition: opacity var(--dur-theme) var(--glass-ease), filter var(--dur-theme) var(--glass-ease);
}

.bg-stack > .scene[data-state="out"] {
  opacity: 0;
}

@keyframes orb-drift {
  from {
    transform: translate3d(0, 0, 0) scale(1);
  }
  50% {
    transform: translate3d(2.5%, 3.5%, 0) scale(1.06);
  }
  to {
    transform: translate3d(-2%, 1.5%, 0) scale(0.97);
  }
}

/* ==========================================================================
   §7  玻璃上的降水
   --------------------------------------------------------------------------
   两种完全不同的物理表现：

   雨 = 「地平线式」驻留水珠
     不是从上往下落的雨丝，而是像隔着窗玻璃看雨：无数颗大小不一的凸透镜
     水珠吸附在玻璃上，每颗都折射身后的景色、顶上有强高光、下方拖着一条
     被重力缓缓拉长的泪痕。它们只做极轻微的"呼吸"，不整体下落。

   雪 = 「上下表面」积雪
     玻璃上沿堆起一层厚雪、下沿积成一条雪线，各带自己的受光高光与内阴影；
     配合细密霜花与仍在飘的雪花，形成"雪积在玻璃上"的层次。
   ========================================================================== */

/* --- 雨：驻留水珠 --- */
.rain-layer {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: var(--rain-opacity);
  /* 上下各留一点淡出，水珠不会硬切在边框上 */
  -webkit-mask-image: linear-gradient(
    180deg,
    transparent 0%,
    #000 var(--rain-fade),
    #000 calc(100% - var(--rain-fade)),
    transparent 100%
  );
  mask-image: linear-gradient(
    180deg,
    transparent 0%,
    #000 var(--rain-fade),
    #000 calc(100% - var(--rain-fade)),
    transparent 100%
  );
}

/* 每一颗水珠：圆头 + 向下的泪痕（同一个元素，用背景渐变画出来） */
.drop {
  position: absolute;
  left: var(--x, 50%);
  /* 顶端贴在随机高度上，泪痕从顶端往下长 */
  top: var(--y, 40%);
  width: var(--w, var(--rain-width));
  /* 高度 = 宽度（圆头）+ 泪痕长度 */
  height: calc(var(--w, var(--rain-width)) + var(--h, var(--rain-length)));
  margin-left: calc(var(--w, var(--rain-width)) / -2);
  border-radius: 50% 50% 48% 48% / 34% 34% 62% 62%;
  background:
    /* 顶部高光：模拟凸透镜的镜面反射 */
    radial-gradient(
      circle at 34% 18%,
      rgb(255 255 255 / var(--rain-sheen)) 0%,
      rgb(255 255 255 / calc(var(--rain-sheen) * 0.35)) 26%,
      transparent 52%
    ),
    /* 珠体本身：上亮下暗，像一滴水 */
    radial-gradient(
      62% 46% at 50% 16%,
      rgb(236 248 255 / 0.5) 0%,
      rgb(168 206 240 / 0.22) 62%,
      rgb(120 165 215 / 0.16) 100%
    ),
    /* 泪痕：从珠体下缘往下渐隐的一条细线 */
    linear-gradient(
      180deg,
      rgb(224 242 255 / 0.34) 0%,
      rgb(198 226 252 / 0.18) 42%,
      rgb(180 210 245 / 0.05) 78%,
      transparent 100%
    );
  box-shadow:
    inset 0 1px 1px rgb(255 255 255 / calc(var(--rain-sheen) * 0.85)),
    inset 0 -1px 2px rgb(92 142 200 / 0.28),
    0 0 5px rgb(212 236 255 / calc(var(--rain-sheen) * 0.3));
  /* 凸透镜感全靠「顶部镜面高光 + 上亮下暗的珠体」这两层渐变。
     这里刻意不写 backdrop-filter 也不写 filter: blur() ——
     两者都会给每颗水珠单独开一块渲染表面，几十颗就是几十块，
     是上一版卡顿的最大来源。 */
  transform: translate3d(0, 0, 0) rotate(var(--tilt, 0deg));
  /* 水珠受重力缓慢下滑：滑到底部淡出，再回原位"重新凝结"（bead-slide）。
     只动 transform + opacity，两者都在合成器上完成，不触发重绘。
     也不写 will-change：动画本身已经会让浏览器把它提升为合成层，
     显式声明几十个只会白白多占显存。 */
  animation: bead-slide var(--dur, 7s) cubic-bezier(0.25, 0.1, 0.35, 1) var(--delay, 0s) infinite;
}

/* 大颗水珠：更重，滑得更快，下坠时被拉得更长 */
.drop--big {
  border-radius: 50% 50% 46% 46% / 30% 30% 66% 66%;
  box-shadow:
    inset 0 1.5px 2px rgb(255 255 255 / calc(var(--rain-sheen) * 0.95)),
    inset 0 -2px 3px rgb(86 138 198 / 0.34),
    0 1px 6px rgb(206 232 255 / calc(var(--rain-sheen) * 0.38));
  animation-duration: calc(var(--dur, 7s) * 0.62);
}

/* 大颗折射水珠（仅弹窗 modal 用）：像 Horizon 那种贴在窗玻璃上的大水滴，
   用 backdrop-filter 真的折射身后的画面——提亮 + 加饱和，像一枚透镜。
   只生成在 modal 上（首页面板仍用上面的便宜假水珠），数量也少，开销可控。 */
.drop--lens {
  width: var(--w, 28px);
  height: calc(var(--w, 28px) * 1.18);
  margin-left: calc(var(--w, 28px) / -2);
  border-radius: 50% 50% 46% 46% / 38% 38% 62% 62%;
  -webkit-backdrop-filter: blur(1.6px) brightness(1.14) saturate(1.32);
  backdrop-filter: blur(1.6px) brightness(1.14) saturate(1.32);
  background:
    /* 顶部镜面高光 */
    radial-gradient(
      circle at 36% 24%,
      rgb(255 255 255 / 0.9) 0%,
      rgb(255 255 255 / 0.25) 22%,
      transparent 50%
    ),
    /* 底部一点暗影，让透镜有体积 */
    radial-gradient(
      circle at 62% 78%,
      rgb(180 210 240 / 0.3) 0%,
      transparent 60%
    );
  box-shadow:
    inset 0 2px 3px rgb(255 255 255 / 0.6),
    inset 0 -3px 5px rgb(60 100 150 / 0.35),
    0 2px 5px rgb(40 60 90 / 0.4);
  animation: lens-slide var(--dur, 6s) cubic-bezier(0.3, 0.2, 0.4, 1) var(--delay, 0s) infinite;
}

/* 折射水珠贴着窗面被重力带下去：滑到底淡出再回顶重凝 */
@keyframes lens-slide {
  0%   { transform: translate3d(0, 0, 0); opacity: 0; }
  10%  { opacity: 1; }
  78%  { opacity: 1; }
  100% { transform: translate3d(0, var(--fall, 60px), 0); opacity: 0; }
}

/* 极小水雾点：没有泪痕，只是一点反光，几乎不动 */
.drop--mist {
  height: var(--w, 3px);
  border-radius: 50%;
  background:
    radial-gradient(
      circle at 38% 32%,
      rgb(255 255 255 / calc(var(--rain-sheen) * 0.9)) 0%,
      rgb(214 236 255 / 0.3) 52%,
      transparent 74%
    );
  box-shadow: none;
  filter: blur(0.5px);
  animation: bead-breathe var(--dur, 18s) ease-in-out var(--delay, 0s) infinite;
}

/* 静止水珠：表面张力大、暂时粘住，只做极慢的呼吸，给"会动的水珠"做对比 */
.drop--still {
  animation: bead-breathe var(--dur, 16s) ease-in-out var(--delay, 0s) infinite;
}

/* 已经流过的旧泪痕：静态、很淡，让玻璃看起来"湿了很久" */
.drops-static {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: calc(var(--rain-opacity) * 0.65);
  background-image:
    linear-gradient(180deg, rgb(214 238 255 / 0.16) 0%, transparent 68%),
    linear-gradient(180deg, rgb(206 232 255 / 0.13) 0%, transparent 74%),
    linear-gradient(180deg, rgb(222 242 255 / 0.1) 0%, transparent 80%);
  background-repeat: no-repeat;
  background-size: 1.5px 46%, 2px 62%, 1px 34%;
  background-position: 23% 6%, 61% 2%, 84% 12%;
}

/* 水珠下滑：贴着玻璃被重力往下带，到接近底部时淡出，循环回原位重凝。
   duration 由 --dur（4~10s）控制、相位由负的 --delay 错开，
   所以画面里永远同时有"正在滑 / 正在消失 / 刚凝结"的水珠，不会同步跳动。 */
@keyframes bead-slide {
  0% {
    transform: translate3d(0, 0, 0) rotate(var(--tilt, 0deg)) scale(1);
    opacity: 1;
  }
  82% {
    opacity: 1;
  }
  100% {
    transform: translate3d(0, var(--fall, 40px), 0) rotate(var(--tilt, 0deg)) scale(1, 1.08);
    opacity: 0;
  }
}

@keyframes bead-breathe {
  0%,
  100% {
    transform: translate3d(0, 0, 0) rotate(var(--tilt, 0deg)) scale(1);
  }
  50% {
    /* 毫米级的滑动：像水珠在重力下勉强挪了一下 */
    transform: translate3d(0, var(--slide, 1.5px), 0) rotate(var(--tilt, 0deg)) scale(1, 1.03);
  }
}

/* --- 雪 / 霜花 --- */
.snow-layer {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: var(--snow-opacity);
}

/* 细密霜花：用多重 radial-gradient 拼出结晶感，静态且极省性能。
   不用 mix-blend-mode：它和 .field--drops 里会动的水珠在同一层里，
   带混合就会让整块玻璃每帧重新混合。 */
.frost {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background-image:
    radial-gradient(1.6px 1.6px at 14% 18%, rgb(255 255 255 / 0.7) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 28% 62%, rgb(255 255 255 / 0.55) 50%, transparent 51%),
    radial-gradient(2px 2px at 44% 26%, rgb(255 255 255 / 0.65) 50%, transparent 51%),
    radial-gradient(1.4px 1.4px at 62% 74%, rgb(255 255 255 / 0.5) 50%, transparent 51%),
    radial-gradient(1.8px 1.8px at 78% 34%, rgb(255 255 255 / 0.6) 50%, transparent 51%),
    radial-gradient(1.2px 1.2px at 90% 58%, rgb(255 255 255 / 0.45) 50%, transparent 51%),
    radial-gradient(150% 80% at 50% 0%, rgb(255 255 255 / 0.2) 0%, transparent 58%),
    radial-gradient(130% 70% at 50% 100%, rgb(232 244 255 / 0.16) 0%, transparent 56%);
  -webkit-mask-image: radial-gradient(130% 120% at 50% 50%, #000 46%, rgb(0 0 0 / 0.3) 100%);
  mask-image: radial-gradient(130% 120% at 50% 50%, #000 46%, rgb(0 0 0 / 0.3) 100%);
}

/* 上沿积雪：厚、边缘起伏、下缘被自己的阴影压暗 */
.snow-accum {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: calc(var(--snow-opacity) * var(--snow-accum, 1));
  transition: opacity 900ms var(--glass-ease);
}

.snow-accum--top {
  /* 雪盖就贴在玻璃上沿，不再往下/往外溢：
     .glass 是 overflow:hidden，往外画的部分会被直接裁掉，只会白白让
     "雪有多厚"变得不可控。这里让元素从顶边起算，厚度完全由变量决定。 */
  top: 0;
  height: calc(var(--snow-height-top, 18px) * 1.55);
  background:
    /* 雪堆本体：上沿近乎实心白，往下带一点冷调的蓝灰（雪自己的阴影） */
    linear-gradient(
      180deg,
      rgb(255 255 255 / 0.99) 0%,
      rgb(255 255 255 / 0.95) 34%,
      rgb(233 244 255 / 0.74) 66%,
      rgb(203 226 250 / 0.3) 88%,
      rgb(203 226 250 / 0) 100%
    ),
    /* 起伏的雪面：几个宽椭圆叠出堆雪的波浪边 */
    radial-gradient(38% 130% at 12% 0%, rgb(255 255 255 / 0.85) 0%, transparent 70%),
    radial-gradient(30% 150% at 38% 6%, rgb(255 255 255 / 0.75) 0%, transparent 72%),
    radial-gradient(42% 120% at 68% 0%, rgb(255 255 255 / 0.8) 0%, transparent 70%),
    radial-gradient(28% 140% at 92% 8%, rgb(255 255 255 / 0.7) 0%, transparent 72%);
  /* 遮罩用「线性 + 若干雪丘」叠加（遮罩层默认是 add 求并集）：
     线性那条保证上半段是实心雪，几团椭圆再往下多铺一点，
     下缘于是变成起伏的波浪，而不是一条笔直的淡出线。 */
  -webkit-mask-image:
    linear-gradient(180deg, #000 0%, #000 56%, rgb(0 0 0 / 0.4) 84%, transparent 100%),
    radial-gradient(22% 52% at 8% 40%, #000 0 72%, transparent 74%),
    radial-gradient(17% 44% at 27% 34%, #000 0 72%, transparent 74%),
    radial-gradient(25% 56% at 50% 42%, #000 0 72%, transparent 74%),
    radial-gradient(16% 42% at 72% 32%, #000 0 72%, transparent 74%),
    radial-gradient(21% 50% at 92% 40%, #000 0 72%, transparent 74%);
  mask-image:
    linear-gradient(180deg, #000 0%, #000 56%, rgb(0 0 0 / 0.4) 84%, transparent 100%),
    radial-gradient(22% 52% at 8% 40%, #000 0 72%, transparent 74%),
    radial-gradient(17% 44% at 27% 34%, #000 0 72%, transparent 74%),
    radial-gradient(25% 56% at 50% 42%, #000 0 72%, transparent 74%),
    radial-gradient(16% 42% at 72% 32%, #000 0 72%, transparent 74%),
    radial-gradient(21% 50% at 92% 40%, #000 0 72%, transparent 74%);
  box-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.95),
    inset 0 calc(var(--snow-height-top, 18px) * -0.35) calc(var(--snow-height-top, 18px) * 0.5)
      rgb(150 185 225 / 0.35);
  filter: blur(var(--snow-soft, 0.6px));
}

.snow-accum--bottom {
  top: auto;
  bottom: 0;
  height: calc(var(--snow-height-bottom, 7px) * 1.9);
  background:
    linear-gradient(
      0deg,
      rgb(255 255 255 / 0.97) 0%,
      rgb(255 255 255 / 0.88) 38%,
      rgb(228 241 255 / 0.52) 72%,
      transparent 100%
    ),
    radial-gradient(34% 130% at 18% 100%, rgb(255 255 255 / 0.7) 0%, transparent 74%),
    radial-gradient(44% 120% at 52% 100%, rgb(255 255 255 / 0.62) 0%, transparent 74%),
    radial-gradient(30% 140% at 84% 100%, rgb(255 255 255 / 0.66) 0%, transparent 74%);
  /* 同 --top：线性遮罩保证贴着下沿是实心，几团雪丘把上缘顶成波浪 */
  -webkit-mask-image:
    linear-gradient(0deg, #000 0%, #000 54%, rgb(0 0 0 / 0.4) 84%, transparent 100%),
    radial-gradient(20% 46% at 12% 60%, #000 0 72%, transparent 74%),
    radial-gradient(24% 54% at 38% 54%, #000 0 72%, transparent 74%),
    radial-gradient(17% 44% at 62% 62%, #000 0 72%, transparent 74%),
    radial-gradient(23% 50% at 88% 56%, #000 0 72%, transparent 74%);
  mask-image:
    linear-gradient(0deg, #000 0%, #000 54%, rgb(0 0 0 / 0.4) 84%, transparent 100%),
    radial-gradient(20% 46% at 12% 60%, #000 0 72%, transparent 74%),
    radial-gradient(24% 54% at 38% 54%, #000 0 72%, transparent 74%),
    radial-gradient(17% 44% at 62% 62%, #000 0 72%, transparent 74%),
    radial-gradient(23% 50% at 88% 56%, #000 0 72%, transparent 74%);
  box-shadow: inset 0 1px 0 rgb(255 255 255 / 0.6);
  filter: blur(var(--snow-soft, 0.6px));
}

/* 带积雪时，玻璃自身的内描边也跟着变白，避免"雪浮在玻璃外面" */
html[data-weather="snow"] .glass {
  box-shadow:
    inset 0 0 0 1px rgb(255 255 255 / calc(var(--glass-rim) * 1.1)),
    inset 0 calc(var(--glass-edge-inset) * 2.2) calc(var(--glass-edge-inset) * 4)
      calc(var(--glass-edge-inset) * -1)
      rgb(255 255 255 / 0.35),
    inset 0 calc(var(--glass-edge-inset) * -1.1) calc(var(--glass-edge-inset) * 2.4)
      calc(var(--glass-edge-inset) * -1.6)
      rgb(var(--glass-inner-shadow-color) / calc(var(--glass-inner-shadow) * var(--glass-thickness-scale))),
    var(--glass-shadow-1),
    var(--glass-shadow-2),
    var(--glass-shadow-3);
}

/* 雪粒满天飞舞：从窗顶出发，一边落一边左右摆，落出窗底就淡出重来 */
.flake {
  position: absolute;
  left: var(--x, 50%);
  top: 0;                          /* 从窗顶出发，往下落 */
  width: var(--w, 7px);
  height: var(--w, 7px);
  border-radius: 50%;
  background: radial-gradient(
    circle at 38% 34%,
    rgb(255 255 255 / 0.95) 0%,
    rgb(235 246 255 / 0.5) 52%,
    rgb(200 226 255 / 0.08) 100%
  );
  box-shadow: 0 0 6px rgb(255 255 255 / 0.3);
  /* 柔化交给径向渐变自己，不用 filter：filter 会给每颗雪粒单独开渲染表面。
     满天飞舞靠 flake-fall，只动 transform + opacity，走合成器不重绘；
     --fall/--drift/--op 由 JS 给 px。 */
  animation: flake-fall var(--dur, 14s) linear var(--delay, 0s) infinite;
}

/* 雪粒不再分"静止/飘动"两种：所有雪粒都满天飞舞（见 flake-fall） */

@keyframes flake-fall {
  0%   { transform: translate3d(0, -24px, 0); opacity: 0; }
  10%  { opacity: var(--op, 1); }
  25%  { transform: translate3d(calc(var(--drift, 20px) * 0.8), calc(var(--fall, 120px) * 0.25), 0); }
  50%  { transform: translate3d(calc(var(--drift, 20px) * -0.6), calc(var(--fall, 120px) * 0.5), 0); }
  75%  { transform: translate3d(calc(var(--drift, 20px) * 0.5), calc(var(--fall, 120px) * 0.75), 0); }
  90%  { opacity: var(--op, 1); }
  100% { transform: translate3d(var(--drift, 20px), var(--fall, 120px), 0); opacity: 0; }
}

/* --- 雷暴白闪：极低频率、极低透明度 --- */
.flash {
  position: fixed;
  inset: 0;
  z-index: var(--z-flash);
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(
    120% 90% at 50% 0%,
    rgb(255 255 255 / 1) 0%,
    rgb(220 235 255 / 0.55) 45%,
    rgb(180 205 255 / 0.15) 100%
  );
  /* 不用 mix-blend-mode：整屏混合 + 动画 = 每帧全屏重混合，很贵 */
}

html[data-weather="thunder"] .flash {
  animation: thunder-flash var(--thunder-flash-duration) steps(1, end) infinite;
}

@keyframes thunder-flash {
  0%,
  92%,
  100% {
    opacity: 0;
  }
  93% {
    opacity: var(--thunder-flash-max);
  }
  94.2% {
    opacity: calc(var(--thunder-flash-max) * 0.25);
  }
  95% {
    opacity: calc(var(--thunder-flash-max) * 0.85);
  }
  96.4% {
    opacity: calc(var(--thunder-flash-max) * 0.1);
  }
  97.2% {
    opacity: 0;
  }
}

@keyframes sheen-sweep {
  0% {
    opacity: 0;
    transform: translateX(-60%);
  }
  18% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translateX(60%);
  }
}

/* ==========================================================================
   §7.5 二级卡片：一扇真正的窗子（data-lod="window"）
   --------------------------------------------------------------------------
   日历格这类「二级卡片」如果每格都塞几十个水珠 DOM，会同时踩两个坑：

     · 卡：几十个元素各自 animation + will-change + filter，合成层爆炸；
           再叠加每格的 backdrop-filter，背景一有动画就每帧全部重算。
     · 丢：日历每换一次月就 innerHTML 重建，JS 注入进去的图层全被抹掉，
           于是"二级卡片永远没有雨滴、也没有积雪"。

   这里换成「纯 CSS 窗面」：只用元素与生俱来的 ::before / ::after，
   纹理是静态背景（光栅化一次就有），会动的那一层只做 transform 位移
   （走合成器，不重绘）。零 DOM、零重渲染风险、零每帧重算。

     ::before = 窗面上的东西（密集水珠 / 霜花）+ 内部高光 + 1px 玻璃边
     ::after  = 会动的那一层（雨痕下滑 / 上下积雪）

   纹理用可平铺的 radial-gradient 定义（--wet-tex / --frost-tex）：
     · 大面板：background-size 设成平铺周期（--wet-tile），铺满整面
     · 小卡片：background-size: 100% 100%，一层纹理正好铺满一格
   同一份纹理两种用法，改一处就全都变。
   ========================================================================== */
:root {
  --wet-tile: 118px;      /* 面板上的水珠平铺周期 */
  --frost-tile: 86px;     /* 面板上的霜花平铺周期 */

  /* 密集水珠：每颗都是「顶部高光 + 珠体」的小椭圆（凸透镜的观感） */
  --wet-tex:
    radial-gradient(3.4px 3.8px at 12% 16%, rgb(255 255 255 / 0.62), rgb(198 230 255 / 0.16) 58%, transparent 100%),
    radial-gradient(2.2px 2.6px at 31% 9%, rgb(255 255 255 / 0.5), rgb(198 230 255 / 0.13) 60%, transparent 100%),
    radial-gradient(2.8px 3.4px at 56% 22%, rgb(255 255 255 / 0.55), rgb(198 230 255 / 0.15) 58%, transparent 100%),
    radial-gradient(1.8px 2.2px at 78% 12%, rgb(255 255 255 / 0.45), rgb(198 230 255 / 0.11) 62%, transparent 100%),
    radial-gradient(3.8px 4.6px at 22% 41%, rgb(255 255 255 / 0.6), rgb(198 230 255 / 0.17) 56%, transparent 100%),
    radial-gradient(2.4px 2.8px at 47% 52%, rgb(255 255 255 / 0.5), rgb(198 230 255 / 0.14) 60%, transparent 100%),
    radial-gradient(3.2px 3.8px at 71% 46%, rgb(255 255 255 / 0.56), rgb(198 230 255 / 0.15) 58%, transparent 100%),
    radial-gradient(2px 2.4px at 90% 38%, rgb(255 255 255 / 0.44), rgb(198 230 255 / 0.11) 62%, transparent 100%),
    radial-gradient(3.6px 4.4px at 16% 72%, rgb(255 255 255 / 0.62), rgb(198 230 255 / 0.17) 56%, transparent 100%),
    radial-gradient(2.6px 3px at 41% 84%, rgb(255 255 255 / 0.5), rgb(198 230 255 / 0.13) 60%, transparent 100%),
    radial-gradient(3px 3.6px at 66% 76%, rgb(255 255 255 / 0.56), rgb(198 230 255 / 0.15) 58%, transparent 100%),
    radial-gradient(2.2px 2.6px at 87% 91%, rgb(255 255 255 / 0.46), rgb(198 230 255 / 0.12) 60%, transparent 100%);

  /* 霜花：更白、更碎，带一点结晶拉丝 */
  --frost-tex:
    radial-gradient(1.8px 1.8px at 11% 14%, rgb(255 255 255 / 0.8) 46%, transparent 100%),
    radial-gradient(1.3px 1.3px at 29% 33%, rgb(255 255 255 / 0.62) 46%, transparent 100%),
    radial-gradient(2.1px 2.1px at 47% 12%, rgb(255 255 255 / 0.74) 46%, transparent 100%),
    radial-gradient(1.5px 1.5px at 68% 27%, rgb(255 255 255 / 0.58) 46%, transparent 100%),
    radial-gradient(1.9px 1.9px at 86% 16%, rgb(255 255 255 / 0.7) 46%, transparent 100%),
    radial-gradient(1.4px 1.4px at 18% 58%, rgb(255 255 255 / 0.6) 46%, transparent 100%),
    radial-gradient(2.2px 2.2px at 38% 71%, rgb(255 255 255 / 0.76) 46%, transparent 100%),
    radial-gradient(1.6px 1.6px at 59% 52%, rgb(255 255 255 / 0.56) 46%, transparent 100%),
    radial-gradient(1.9px 1.9px at 79% 68%, rgb(255 255 255 / 0.68) 46%, transparent 100%),
    radial-gradient(1.3px 1.3px at 93% 47%, rgb(255 255 255 / 0.54) 46%, transparent 100%),
    radial-gradient(1.7px 1.7px at 24% 88%, rgb(255 255 255 / 0.66) 46%, transparent 100%),
    radial-gradient(2px 2px at 63% 91%, rgb(255 255 255 / 0.72) 46%, transparent 100%),
    radial-gradient(1.4px 1.4px at 88% 85%, rgb(255 255 255 / 0.58) 46%, transparent 100%);
}

/* window 级不需要 backdrop-filter：省掉一格的背景采样，观感靠底色 + 窗面纹理撑住 */
.glass[data-lod="window"] {
  --glass-blur: 0px;
  --glass-alpha: 0.16;
  -webkit-backdrop-filter: none;
  backdrop-filter: none;
}

/* 比 window 还小的元素（按钮、图标、输入框）：什么降水都不加 */
.glass[data-lod="none"] > .field--drops {
  display: none;
}

/* --- 雨 / 雷暴：窗面上挂满水珠，三道雨痕往下淌 --- */
html[data-weather="rain"] .glass[data-lod="window"]::before,
html[data-weather="thunder"] .glass[data-lod="window"]::before {
  background-image:
    /* 内部高光（原来在 .glass::before 上的那层，这里一并保留） */
    linear-gradient(
      152deg,
      rgb(255 255 255 / calc(var(--glass-highlight) * 0.22)) 0%,
      rgb(255 255 255 / 0) 46%,
      rgb(255 255 255 / calc(var(--glass-highlight) * 0.08)) 100%
    ),
    var(--wet-tex);
  /* 单一值会作用到所有层：一层纹理正好铺满这一格 */
  background-size: 100% 100%;
  background-repeat: no-repeat;
  opacity: calc(var(--rain-opacity) / 0.7);
  box-shadow:
    inset 0 0 0 1px rgb(var(--glass-rim-color) / calc(var(--glass-rim) * 0.55)),
    inset 0 1px 0 rgb(255 255 255 / 0.28);
}

html[data-weather="rain"] .glass[data-lod="window"]::after,
html[data-weather="thunder"] .glass[data-lod="window"]::after {
  --win-period: 46px;
  --win-dur: calc(4.6s / var(--rain-speed, 1));
  inset: auto 0 auto 0;
  /* 上下各多留一个周期，往上一格周期后依然铺满 → 循环无缝 */
  top: calc(-1 * var(--win-period));
  height: calc(100% + 2 * var(--win-period));
  padding: 0;
  border-radius: 0;
  -webkit-mask: none;
  mask: none;
  opacity: 0.9;
  /* 每一格平铺里：0deg 让"最亮的一段"落在格子底部 —— 那是往下淌的水头，
     越往上越淡，是它身后拖出的湿痕。配合 win-flow 的向下位移才是
     "水滴领着尾巴往下走"，而不是反过来的倒放。 */
  background-image:
    linear-gradient(0deg, rgb(255 255 255 / 0.52) 0%, rgb(214 238 255 / 0.17) 40%, transparent 100%),
    linear-gradient(0deg, rgb(255 255 255 / 0.4) 0%, rgb(206 232 255 / 0.12) 46%, transparent 100%),
    linear-gradient(0deg, rgb(255 255 255 / 0.46) 0%, rgb(210 236 255 / 0.15) 42%, transparent 100%);
  background-repeat: repeat-y;
  background-size: 3px var(--win-period), 2px var(--win-period), 2.5px var(--win-period);
  background-position: 21% 0, 58% 12px, 83% 26px;
  animation: win-flow var(--win-dur) linear infinite;
}

/* 雨痕要"往下淌"。位移量刚好是一个平铺周期（--win-period），
   所以从 -1 个周期滑回 0 的过程里，循环点上画面完全一致 —— 既无缝，
   方向又是向下的（早先写成 0 → -1 个周期，看上去就是雨在往上飘）。
   transform 走合成器，不触发重绘。 */
@keyframes win-flow {
  from {
    transform: translate3d(0, calc(-1 * var(--win-period)), 0);
  }
  to {
    transform: translate3d(0, 0, 0);
  }
}

/* --- 雪：窗面结霜，上下沿各积一层雪 --- */
html[data-weather="snow"] .glass[data-lod="window"]::before {
  background-image:
    linear-gradient(
      152deg,
      rgb(255 255 255 / calc(var(--glass-highlight) * 0.24)) 0%,
      rgb(255 255 255 / 0) 46%,
      rgb(255 255 255 / calc(var(--glass-highlight) * 0.1)) 100%
    ),
    var(--frost-tex);
  background-size: 100% 100%;
  background-repeat: no-repeat;
  opacity: calc(var(--snow-opacity) / 0.85);
  box-shadow:
    inset 0 0 0 1px rgb(255 255 255 / calc(var(--glass-rim) * 0.7)),
    inset 0 1px 0 rgb(255 255 255 / 0.4);
}

html[data-weather="snow"] .glass[data-lod="window"]::after {
  inset: 0;
  padding: 0;
  border-radius: 0;
  -webkit-mask: none;
  mask: none;
  opacity: calc(var(--snow-opacity) * var(--snow-accum, 1));
  background-repeat: no-repeat;
  background-position: bottom, bottom, bottom, bottom, bottom;
  background-size: 100% 22%, 100% 16%, 100% 20%, 100% 17%, 100% 19%;
  /* 只在窗台（下沿）积雪：实心雪线 + 几团高低不一的雪丘顶成波浪上缘。
     雪是从天上飞着落下来的，落到底沿就堆住——所以只积在下面，
     上沿不再堆（垂直窗面上沿堆不住雪）。 */
  background-image:
    linear-gradient(0deg, rgb(255 255 255 / 0.97) 0%, rgb(240 249 255 / 0.62) 46%, transparent 100%),
    radial-gradient(34% 130% at 18% 100%, rgb(255 255 255 / 0.95), transparent 72%),
    radial-gradient(44% 120% at 52% 100%, rgb(255 255 255 / 0.9), transparent 74%),
    radial-gradient(30% 140% at 84% 100%, rgb(255 255 255 / 0.92), transparent 74%),
    radial-gradient(24% 96% at 68% 96%, rgb(255 255 255 / 0.88), transparent 78%);
}

/* 让每一格的雨痕周期/速度错开，避免整片日历整齐划一地流动 */
.day:nth-child(3n) {
  --win-period: 54px;
  --win-dur: calc(5.6s / var(--rain-speed, 1));
}

.day:nth-child(4n) {
  --win-period: 40px;
  --win-dur: calc(3.9s / var(--rain-speed, 1));
}

.day:nth-child(5n) {
  --win-period: 62px;
  --win-dur: calc(6.4s / var(--rain-speed, 1));
}

/* 面板上的「湿润底色」：.field--drops 自身的静态纹理。
   它和水珠 DOM 是两个层次 —— 纹理负责"整面都湿了"，DOM 水珠负责"个别大颗的"。 */
html[data-weather="rain"] .glass[data-lod="full"] > .field--drops,
html[data-weather="thunder"] .glass[data-lod="full"] > .field--drops {
  background-image: var(--wet-tex);
  background-size: var(--wet-tile) var(--wet-tile);
  background-repeat: repeat;
}

html[data-weather="snow"] .glass[data-lod="full"] > .field--drops {
  background-image: var(--frost-tex);
  background-size: var(--frost-tile) var(--frost-tile);
  background-repeat: repeat;
}

/* ==========================================================================
   §8  主题 token（昼夜 × 天气）
   只改「玻璃与文字」的配色；背景由 §6 的场景负责。
   ========================================================================== */
:root,
html[data-time="day"] {
  --bg: linear-gradient(178deg, #4fa6f5 0%, #7cc0f8 42%, #b9e2ff 74%, #e7f4ff 100%);
  --ui-text: #0d1a2b;
  --ui-text-dim: rgb(13 26 43 / 0.66);
  --ui-line: rgb(13 26 43 / 0.12);
  --glass-tint: 255 255 255;
  --glass-text: #0d1a2b;
  --glass-text-rgb: 13 26 43;
  --glass-shadow-color: 24 46 80;
  --glass-rim-color: 255 255 255;
  --glass-rim-contrast: 12 28 50;
  --glass-inner-shadow-color: 18 38 70;
  --accent: 46 128 210;
  --accent-rgb: 46 128 210;
  --picker-invert: 0;
  --orb-opacity: 0.5;
  --glass-shadow-alpha: 0.26;
}

html[data-time="night"] {
  --bg: linear-gradient(180deg, #050b23 0%, #0a1538 38%, #101f4d 66%, #16295f 100%);
  --ui-text: #eaf2ff;
  --ui-text-dim: rgb(234 242 255 / 0.68);
  --ui-line: rgb(234 242 255 / 0.14);
  --glass-tint: 226 238 255;
  --glass-text: #f2f7ff;
  --glass-text-rgb: 242 247 255;
  --glass-shadow-color: 0 4 14;
  --glass-rim-color: 235 244 255;
  --glass-rim-contrast: 0 0 0;
  --glass-inner-shadow-color: 0 0 0;
  --accent: 138 190 255;
  --accent-rgb: 138 190 255;
  --picker-invert: 1;
  --orb-opacity: 0.42;
  --glass-shadow-alpha: 0.45;
  --glass-brightness: 1.08;
}

/* 雨：冷色、压暗，玻璃更「湿」 */
html[data-weather="rain"],
html[data-weather="thunder"] {
  --glass-tint: 206 228 255;
  --glass-saturate: 1.35;
  --glass-brightness: 0.96;
  --glass-rim: 0.7;
  --glass-highlight: 0.6;
  --accent: 96 176 240;
  --accent-rgb: 96 176 240;
}

html[data-weather="rain"][data-time="night"],
html[data-weather="thunder"][data-time="night"] {
  --glass-tint: 190 216 255;
  --glass-brightness: 0.9;
  --accent: 120 186 255;
  --accent-rgb: 120 186 255;
}

/* 雪：高亮白、冷调、对比略低，像一层霜 */
html[data-weather="snow"] {
  --glass-tint: 245 250 255;
  --glass-saturate: 1.15;
  --glass-brightness: 1.06;
  --glass-rim: 1;
  --glass-highlight: 0.7;
  --accent: 120 176 235;
  --accent-rgb: 120 176 235;
}

/* 雾：降低对比度与饱和度，背景模糊加强 */
html[data-weather="fog"] {
  --glass-contrast: 0.94;
  --glass-saturate: 1.12;
  --glass-highlight: 0.55;
  --glass-rim: 0.6;
  --focus-blur-amount: 16px;
  --accent: 140 165 195;
  --accent-rgb: 140 165 195;
}

/* 阴：中性灰调，玻璃厚度感最强 */
html[data-weather="cloudy"] {
  --glass-saturate: 1.2;
  --glass-brightness: 1.02;
  --glass-tint: 236 242 250;
  --glass-thickness-blur: 4px;
  --glass-alpha: 0.18;
  --glass-shadow-alpha: 0.34;
  --accent: 120 150 190;
  --accent-rgb: 120 150 190;
}

/* 晴：明亮通透，高光更强 */
html[data-weather="clear"] {
  --glass-saturate: 1.75;
  --glass-brightness: 1.06;
  --glass-highlight: 0.72;
  --glass-rim: 0.95;
}

html[data-weather="clear"][data-time="day"] {
  --glass-tint: 255 250 240;
  --accent: 232 154 62;
  --accent-rgb: 232 154 62;
  --orb-opacity: 0.72;
}

/* 晴 + 夜：深蓝 + 冷高光 */
html[data-weather="clear"][data-time="night"] {
  --glass-tint: 214 230 255;
  --glass-highlight: 0.66;
  --glass-rim: 0.9;
  --accent: 150 186 255;
  --accent-rgb: 150 186 255;
  --orb-opacity: 0.5;
}

/* ==========================================================================
   §9  景深对焦：背景失焦 → 玻璃清晰
   用法：打开弹窗时给 <body>（或任意容器）加 .is-backdrop-blurred
   ========================================================================== */
.page {
  filter: blur(var(--focus-blur))
    brightness(calc(1 - var(--focus-dim) * 0.55))
    saturate(calc(1 - var(--focus-desat)));
  transition: filter var(--focus-duration) var(--focus-ease), transform var(--focus-duration) var(--focus-ease);
}

/* will-change 只在真的要动的时候挂：常驻会让整页永久占一块大合成层 */
.is-backdrop-blurred .page {
  --focus-blur: var(--focus-blur-amount);
  --focus-dim: var(--focus-dim-amount);
  --focus-desat: var(--focus-desat-amount);
  transform: scale(var(--focus-scale-amount));
  will-change: filter, transform;
}

/* 背景稍微推远一点，弹窗就近了 */
.is-backdrop-blurred .bg-orbs {
  transform: scale(calc(var(--focus-scale-amount) + 0.01));
}

/* 遮罩：本身几乎透明，只负责居中、接管点击、并把背景再压一层暗 */
.modal-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  padding: max(12px, env(safe-area-inset-top)) 12px max(12px, env(safe-area-inset-bottom));
  background: radial-gradient(
    120% 100% at 50% 50%,
    rgb(4 10 22 / 0.10) 0%,
    rgb(4 10 22 / 0.34) 100%
  );
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  opacity: 1;
  transition: opacity var(--focus-duration) var(--focus-ease);
}

.modal-overlay[hidden] {
  display: none;
}

/* 打开前的「对焦行程」：遮罩先到，玻璃随后落到焦点 */
.modal-overlay:not(.is-open) {
  opacity: 0;
  pointer-events: none;
}

/* 弹窗本体：入场 scale(0.92) → scale(1)，透明度 0 → 1 */
.modal-overlay .glass-modal {
  --enter-scale: 0.92;
  animation: modal-in var(--focus-duration) var(--glass-pop) both;
}

@keyframes modal-in {
  0% {
    opacity: 0;
    transform: scale(var(--enter-scale)) translateY(6px);
    filter: blur(6px);
  }
  55% {
    filter: blur(0);
  }
  100% {
    opacity: 1;
    transform: scale(1) translateY(0);
    filter: blur(0);
  }
}

/* 关闭：反向播放，背景重新对焦 */
.modal-overlay.is-closing {
  opacity: 0;
}

.modal-overlay.is-closing .glass-modal {
  animation: modal-out 220ms var(--glass-ease) both;
}

@keyframes modal-out {
  0% {
    opacity: 1;
    transform: scale(1) translateY(0);
    filter: blur(0);
  }
  100% {
    opacity: 0;
    transform: scale(0.94) translateY(8px);
    filter: blur(4px);
  }
}

/* 弹窗内的高光扫过：落地瞬间的一次玻璃反光（150ms） */
.glass-modal.is-sheening .field--sheen {
  animation: sheen-sweep var(--glass-sweep-duration) cubic-bezier(0.3, 0.7, 0.4, 1) both;
}

/* 对话框结构（配合 <dialog class="glass glass-modal" popover>） */
.glass-dialog {
  margin: auto;
  border: 0;
  background: transparent;
  padding: 0;
  max-width: none;
  max-height: none;
  color: inherit;
}

.glass-dialog::backdrop {
  background: transparent;
}

.dialog-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1rem;
}

.dialog-title {
  margin: 0;
  font-size: 1.06rem;
  font-weight: 700;
  letter-spacing: 0.01em;
}

.dialog-sub {
  margin: 0.15rem 0 0;
  font-size: 0.8rem;
  color: var(--ui-text-dim);
}

.field-row {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  margin-bottom: 0.8rem;
}

.field-row > label {
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  color: var(--ui-text-dim);
  text-transform: uppercase;
}

.field-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.6rem;
}

.dialog-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
  margin-top: 1.15rem;
}

/* ==========================================================================
   §10 可用性、降级与减弱动效
   ========================================================================== */

/* 不支持 backdrop-filter：退化为「半透明 + 更强阴影」，仍然可用 */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .glass {
    background:
      linear-gradient(
        158deg,
        rgb(var(--glass-tint) / calc(var(--glass-alpha) * 2.1)) 0%,
        rgb(var(--glass-tint) / calc(var(--glass-alpha) * 1.5)) 100%
      );
    box-shadow:
      inset 0 0 0 1px rgb(var(--glass-rim-color) / var(--glass-rim)),
      var(--glass-shadow-2),
      var(--glass-shadow-3);
  }

  .glass__refraction {
    display: none;
  }
}

/* 减弱动效：所有位移动画/过渡关掉，但水珠、积雪、霜花这些"静态的雨雪"全部保留
   —— 天气本身是画面内容，不该因为动效偏好而消失。 */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  /* 背景光斑和雪花的漂移也停掉 */
  .orb,
  .flake {
    animation: none !important;
  }

  /* 跟随鼠标的光源无需过渡（本来也不该有动效） */
  :root {
    --light-transition: 0s;
  }

  html[data-weather="thunder"] .flash {
    animation: none !important;
    opacity: 0 !important;
  }

  .is-backdrop-blurred .page {
    transform: none;
  }
}

/* 移动端：降低模糊与阴影开销，保证滚动流畅 */
@media (max-width: 720px) {
  :root {
    --glass-blur-base: 10px;
    --glass-shadow-y: 14px;
    --glass-radius-base: 18px;
    --rain-opacity: 0.62;
  }

  .glass-modal {
    --glass-blur-base: 18px;
    --glass-saturate: 1.6;
  }

  .page {
    padding: 14px;
  }

  /* 颗粒在小屏上收益低、代价高（整屏 overlay 混合），直接不显示 */
  .grain {
    display: none;
  }
}

/* 低性能设备（JS 检测后写入 data-sim="low"）：关掉最贵的效果 */
html[data-sim="low"] {
  --glass-refraction: 0;
  --glass-blur-base: 10px;
}

html[data-sim="low"] .glass__refraction,
html[data-sim="low"] .bg-orbs {
  display: none;
}

/* 低性能设备：水珠动画降到极简（静态雨痕感，省掉逐帧合成） */
html[data-sim="low"] .drop {
  animation-duration: 0s !important;
  animation-iteration-count: 1 !important;
  box-shadow: inset 0 1px 1px rgb(255 255 255 / 0.7);
}

/* 低配设备：窗面雨痕也不再滚动，只留静态水珠/霜花 */
html[data-sim="low"] .glass[data-lod="window"]::after {
  animation: none !important;
}

/* 低配设备：连跟随光源都省掉（保留静态高光即可） */
html[data-sim="low"] .glass__light {
  display: none;
}

/* 触摸设备：玻璃层不抢手势 */
@media (hover: none) {
  .glass__layer,
  .field {
    pointer-events: none;
  }
}

/* 打印：玻璃变白纸 */
@media print {
  .bg-stack,
  .bg-orbs,
  .grain,
  .field,
  .flash,
  .modal-overlay {
    display: none !important;
  }

  .glass {
    box-shadow: none;
    border: 1px solid #ccc;
    background: #fff;
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}
