/* server-dsh · 登录页样式（居中台灯 → 拉灯上移 → 表单浮入）
 *
 * ⚠️ 颜色规则（T09 约定，有测试守住）：
 *    本文件**不得写死任何色值**，一律 var(--color-*) / var(--lamp-*) /
 *    var(--light-*) / var(--glass-*)。运行时色相由 theme.js 写入 --lamp-*。
 *
 * ══════════════════════════════════════════════════════════
 * 布局模型（T10c 统一，不再桌面/移动两套逻辑）
 * ══════════════════════════════════════════════════════════
 *   .lamp-stage 是 flex column 且**垂直居中**于视口：
 *     灯灭 → 台灯 + 提示 居中，这是页面的主体
 *     灯亮 → 台灯靠 transform 上移 --lamp-shift，空出的位置给表单
 *   两态之间只有一次位移，观感连续；移动端只是数值变小，逻辑相同。
 *
 *   用 transform 而非 margin/position 做位移：不触发布局重算，动画更顺滑。
 *
 * 提示文案的可见性：
 *   灯灭 → 显示（明确告知下一步做什么）
 *   灯亮 → 淡出（表单出现后即可自解释）
 *   移动端 → 字号不小于 13px（12px 在手机上偏小，用户反馈过）
 */

/* ══════════════════════════════════════════════════════════
   容器与背景
   ══════════════════════════════════════════════════════════ */
.lamp-app {
  position: relative;
  min-height: 100dvh;
  display: flex;
  align-items: center;        /* 垂直居中：灯是页面主体 */
  justify-content: center;
  padding: var(--space-5) var(--space-4)
           calc(var(--space-5) + env(safe-area-inset-bottom));
  overflow-x: hidden;
}

/* 台灯上移距离：灯亮时用（同一变量在移动端被覆盖为更小值）
 *
 * 取值依据（两轮用户反馈）：
 *   1. 「拉灯交互后上移动的位移减少 1/4」
 *        旧值 148 / 118 / 92 / 74   →   ×0.75
 *   2. 「位移控制到登录弹窗在屏幕中间符合人类的习惯」
 *        把 1 的结果再取一半，让**卡片**中心落到视口中线附近，
 *        而不是让整个"灯 + 表单"列的几何中心对齐中线。
 *
 * 为什么再除以 2 就够（推导，勿凭感觉改）：
 *   灯灭态整列只有台灯，被 .lamp-app 的 align-items:center 垂直居中，
 *   故台灯中心落在视口中线。灯亮后：
 *     卡片中心 = 台灯中心 + 位移 + (台灯半高 + 间距 + 卡片半高)
 *   要让卡片中心回到中线，需要
 *     位移 = -(台灯半高 + 间距 + 卡片半高) ≈ -(台灯半高 + 卡片半高)
 *   而 1 的结果（旧值）≈ 台灯高 - 卡片半高，两者相减即 ≈ 旧值 / 2。
 *   1080 高桌面实测：卡片中心 838 → 541，视口中线 540。✅
 */
.lamp-app {
  --lamp-shift: 55.5px;
}

.lamp-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-base);
  pointer-events: none;
  background:
    /* 灯光主晕：色相由 --lamp-glow 驱动（theme.js 写入） */
    radial-gradient(
      ellipse 46% 38% at 50% 34%,
      var(--lamp-glow, transparent) 0%,
      transparent 68%
    ),
    /* 顶部略亮的空气感，避免上方一片死黑 */
    linear-gradient(180deg, var(--light-air-top) 0%, transparent 42%),
    var(--color-bg);
  transition: background var(--dur-slow) var(--ease-standard);
}

/* ── 背景视频（T12）─────────────────────────────────────
 *
 * 由 media.js 按需注入。这些规则只在视频真的被创建后才生效，
 * 所以"没加载视频"的场景下本文件的其它部分完全不受影响。
 *
 * 三条硬要求：
 *   1. **不能盖住内容**：z-index 为 0（在 .lamp-stage 之下），
 *      且不吃指针事件（否则表单点不动）。
 *   2. **不能影响可读性**：表单上的文字必须仍是第一眼看到的东西。
 *   3. **不能毁掉素材本身**：这是一张**亮色调**（奶油白底）的插画，
 *      不是暗色照片。压得太狠会把它变成一块脏灰布——
 *      素材就白准备了。故采用"中等压暗 + 一层静态暗角遮罩"，
 *      而不是单纯把亮度砍到底。
 *
 * 压暗量的实测依据：视频不透明度 0.55 × 亮度 0.62 ≈ 等效 0.34，
 * 与 T10 那套纯渐变背景的暗度基本一致 —— 也就是"看得见画面，
 * 但不会抢表单"的那个程度。
 */
.lamp-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  filter: brightness(0.62) saturate(0.92);
  transition: opacity var(--dur-slow) var(--ease-standard);
}

/* 解码出画之后才淡入，避免"黑一下再出画面" */
.lamp-backdrop.is-ready .lamp-video { opacity: 0.55; }

/* 静态暗角遮罩：**中间浅、四周深**。
 *
 * 为什么需要它：单纯整幅压暗会让画面变成均匀的灰，
 * 既不好看也没有视觉重心。而"灯光从中间打下来"这个场景本身
 * 就要求中间亮、四周沉下去——用一层径向渐变同时解决
 * 观感与可读性（四周压暗也让表单区域的对比度更高）。
 *
 * ⚠️ 它是一层独立元素而不是给 .lamp-video 加 box-shadow：
 *    滤镜/阴影会作用在整幅画面上，做不到"中间浅四周深"。
 */
.lamp-backdrop::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(
    ellipse 62% 58% at 50% 40%,
    transparent 30%,
    var(--light-vignette) 100%
  );
  transition: opacity var(--dur-slow) var(--ease-standard);
}

.lamp-backdrop.is-ready::after { opacity: 1; }

/* 不加载视频的三种情形（减少动效 / 省流量 / 加载失败）：
   退化成 T10 的纯渐变观感，功能不受任何影响。 */
.lamp-backdrop.is-poster-only .lamp-video { display: none; }
.lamp-backdrop.is-poster-only::after { opacity: 0; }

/* 窄屏的额外压暗量放在文件末尾的响应式区（本文件约定：所有 @media
   集中在末尾，便于一处查阅）。此处不留 @media。 */

/* 纹理噪点：打散大面积纯色的色带（banding）。
   强度刻意很低（0.05）——过强会显得脏。内联 data URI，零网络请求。 */
.lamp-grain {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  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'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* 暗角：四角压暗，把视觉重心收拢到中央光源 */
.lamp-vignette {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  pointer-events: none;
  background: radial-gradient(
    ellipse 78% 72% at 50% 42%,
    transparent 42%,
    var(--light-vignette) 100%
  );
}

/* ══════════════════════════════════════════════════════════
   舞台
   ══════════════════════════════════════════════════════════ */
.lamp-stage {
  position: relative;
  z-index: var(--z-sticky);
  width: 100%;
  max-width: 400px;
  display: flex;
  flex-direction: column;
  align-items: center;
  /*
   * gap: 0 而不是 --space-4（用户要求「登录弹窗与灯的距离只到拉绳多 5px」）。
   *
   * 关键：这里必须是 0。间距改由 .login-panel 的 margin-top 承担，
   * 因为灯灭时面板是 height:0，但 **flex gap 照样会被算一次**——
   * 若把间距留在 gap 上，灯灭态就会白多出一段空隙，把台灯挤出居中位置。
   * 间距挂在面板自己身上，面板不占位时它也不占位。
   */
  gap: 0;
}

/* ── 台灯 ─────────────────────────────────────────────── */
.lamp-svg {
  width: 190px;
  height: 285px;
  flex: none;
  overflow: visible;
  /* 灯绳与提示都在 SVG 内部，故只有 SVG 需要禁用指针事件，
     再由 .lamp-cord 单独打开（否则 SVG 会挡住下方表单的点击）。 */
  pointer-events: none;
  /* 灯亮时整体上移，把居中位置让给表单 */
  transform: translateY(0);
  transition: transform var(--dur-slow) var(--ease-standard);
}

/* 灯绳的绝对定位基准是 .lamp-stage，而 .lamp-stage 顶到台灯最底部的
   高度 = 台灯高度 + 面板间距。写成一个变量，让面板可以直接引用，
   不必重复一个魔数（重复的魔数迟早会对不上）。
   ⚠️ 间距与内边距写在下面**唯一的** .login-panel 规则里，
      不要再单独开一条 .login-panel 规则：本文件的测试按
      "第一条 .login-panel 规则块"取值，拆成两条会让断言取错。 */
.lamp-app { --lamp-block-h: 285px; --panel-gap: 5px; }

.lamp-app.is-lit .lamp-svg {
  transform: translateY(calc(var(--lamp-shift) * -1));
}

.lamp-wire {
  stroke: var(--color-border-strong);
  stroke-width: 1.5;
}

/* 灯罩：填充色由 --lamp-color 驱动（theme.js 写入的暖色） */
.lamp-shade {
  fill: var(--lamp-color, var(--color-text-faint));
  transition: fill var(--dur-slow) var(--ease-standard);
}

/* 体积渐变叠层：与本体同路径，叠在上面做高光/暗部 */
.lamp-shade-volume {
  opacity: 0.92;
  transition: opacity var(--dur-slow) var(--ease-standard);
}

.lamp-halo {
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-standard);
}

.lamp-bulb {
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard);
}

/* ── 小脸（灯罩正中间，常驻显示）─────────────────────────
 *
 * 设计取舍（用户反馈「表情在灯桶中间固定好」）：
 *   位置由模板里的 SVG 坐标决定（眼睛 y=85，灯罩区间 48–126 的正中），
 *   CSS 只负责**显隐**，不参与定位——定位一旦分到两处，改动就会互相打架。
 *
 * 灯灭态也必须看得见：闭眼 + 小嘴。若灯灭时整块隐藏，
 * 「固定好」就无从谈起（上一版就是灯灭全空白）。
 * 睁眼（scaleY 0.12 → 1）是唯一的"灯亮了"信号，语义清晰。
 */
.lamp-eyes {
  opacity: 1;
}

.lamp-eye {
  fill: var(--color-text);
  transform: scaleY(0.12);
  transform-origin: center;
  transition: transform var(--dur-base) var(--ease-out-back);
}

.lamp-pupil {
  fill: var(--color-bg);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-standard);
}

/* 嘴：灯灭一条小弧（嘴角向下），灯亮展开成微笑 */
.lamp-mouth {
  fill: none;
  stroke: var(--color-text-muted);
  stroke-width: 1.8;
  stroke-linecap: round;
  opacity: 1;
  transition: opacity var(--dur-base) var(--ease-standard),
              stroke var(--dur-slow) var(--ease-standard);
}

.lamp-beam,
.lamp-beam-core {
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-standard);
}

/* ── 地面光池 ─────────────────────────────────────────── */
/* 透视压缩椭圆 + 强模糊：光"落在地上"，而不是悬空的一条梯形。 */
.lamp-pool {
  position: absolute;
  left: 50%;
  top: 268px;
  transform: translateX(-50%) scaleY(0.30);
  width: 300px;
  height: 300px;
  border-radius: var(--radius-pill);
  pointer-events: none;
  opacity: 0;
  filter: blur(26px);
  background: radial-gradient(
    circle,
    var(--lamp-glow, transparent) 0%,
    transparent 62%
  );
  transition: opacity var(--dur-slow) var(--ease-standard);
}

/* ══════════════════════════════════════════════════════════
   灯亮 / 灯灭状态
   ══════════════════════════════════════════════════════════
   顺序要求：`.lamp-app:not(.is-lit)` 必须写在 `.is-lit` **前面**。
   两者特异性相同时后者胜，靠顺序保证灯亮态的规则生效。
*/
.lamp-app:not(.is-lit) .lamp-eye {
  transform: scaleY(0.12);      /* 灯灭 = 闭眼 */
}

.lamp-app.is-lit .lamp-eye     { transform: scaleY(1); }
.lamp-app.is-lit .lamp-pupil   { opacity: 1; }
.lamp-app.is-lit .lamp-mouth   { opacity: 1; stroke: var(--color-text); }
.lamp-app.is-lit .lamp-halo    { opacity: 0.9; }
.lamp-app.is-lit .lamp-bulb    { opacity: 0.95; }
.lamp-app.is-lit .lamp-beam    { opacity: 0.75; }
.lamp-app.is-lit .lamp-beam-core { opacity: 0.5; }
.lamp-app.is-lit .lamp-pool    { opacity: 0.6; }

/* 灯泡与罩口亮光：灯灭时完全不可见（否则暗态会有一团白色雾斑） */
.lamp-light { opacity: 0; }
.lamp-app.is-lit .lamp-light { opacity: 1; }

/* 灯灭时去掉体积立体感：灰调下叠渐变会像块脏斑 */
.lamp-app.is-dim .lamp-shade-volume { opacity: 0.35; }

/* ══════════════════════════════════════════════════════════
   灯绳
   ══════════════════════════════════════════════════════════
 * 灯绳是 SVG 内部的 <g role="button">，不是 HTML <button>。
 * 原因（用户反馈「拉绳后绳也要上移动」）：
 *   台灯的上移是 .lamp-svg 上的 translateY，只有 SVG **内部**的元素
 *   才跟它共用坐标系。之前绳是 .lamp-stage 的绝对定位子元素，
 *   灯走了绳不动，灯亮后绳就悬在灯与表单之间的半空里。
 *
 * 缩放说明：SVG 的 viewBox 是 200×300，各断点按 148/190/214 宽渲染，
 *   缩放系数 0.74–1.07，1 用户单位 ≈ 1px。数值可直接按"设计像素"思考。
 */
.lamp-cord {
  pointer-events: all;      /* SVG 整体 pointer-events:none，这里单独打开 */
  cursor: grab;
  touch-action: none;       /* 拖拽时不让浏览器抢走手势 */
  user-select: none;
}

.lamp-cord:active,
.lamp-cord.is-dragging { cursor: grabbing; }

/* ⚠️ 命中层是**两块完全透明的矩形，只做命中，不画任何东西**。
 *
 * 曾经写法：`<line stroke="transparent" stroke-width="11">`。
 * 后果：在用户浏览器里渲染成一条**白色粗边框**，灯绳变成一个白框，
 * 用户连续两轮截图指出"这个轮廓"。
 * `transparent` 描边的渲染行为在各引擎不一致，不能用。
 *
 * 现在：矩形只设 `fill: transparent`，绝不碰 stroke 属性——
 * 没有描边，就没有"描边被画出来"的可能。
 */
.lamp-cord-hit {
  fill: transparent;
}

/* 绳身：**fill 的矩形，不是 stroke 的线**。
   这样它的粗细是几何宽度，与描边渲染无关。 */
.lamp-cord-rope {
  fill: var(--color-border-strong);
}

.lamp-cord-knob {
  fill: var(--color-text-muted);
  stroke: none;
  transition: fill var(--dur-fast) var(--ease-standard);
}

/* 悬停/聚焦：绳结染上主题色并放大（transform-box 保证绕自身中心缩放，
   否则 SVG 元素会绕用户坐标原点缩放而跑飞）。 */
.lamp-cord:hover .lamp-cord-knob,
.lamp-cord:focus-visible .lamp-cord-knob {
  fill: var(--lamp-color, var(--color-accent));
  transform-box: fill-box;
  transform-origin: center;
  transform: scale(1.3);
}

/* 键盘聚焦时把绳身也染色，SVG 元素没有默认 outline 样式 */
.lamp-cord:focus-visible { outline: none; }
.lamp-cord:focus-visible .lamp-cord-rope { fill: var(--color-accent); }

/* ══════════════════════════════════════════════════════════
   提示文案
   ══════════════════════════════════════════════════════════
 * 明确告知下一步动作（用户反馈"提示要合理"）。
 * 字号 13px 起：12px 在手机上偏小，读起来吃力。
 *
 * ⚠️ 它也是 SVG <text>（与灯绳同理，必须随灯一起上移）。
 *   SVG 会被 CSS 放大（190/200、214/200 …），故 font-size 要按
 *   200/实际宽度 反向补偿，渲染出来才是 13–15px：
 *     手机 148px → 200/148 ≈ 1.35 → 13 × 1.35 ≈ 17.6
 *     基础 190px → 200/190 ≈ 1.05 → 14 × 1.05 ≈ 14.8
 *     桌面 214px → 200/214 ≈ 0.93 → 15 × 0.93 ≈ 14.0
 *   居中靠 <text text-anchor="middle" x="100">，不需要 CSS 参与。
 */
.lamp-hint {
  fill: var(--color-text-muted);
  font-size: 17.6px;
  letter-spacing: 0.02em;
  white-space: nowrap;
  transition: opacity var(--dur-base) var(--ease-standard);
}

.lamp-app.is-lit .lamp-hint {
  opacity: 0;
}

/* 灯亮后提示隐藏。
   ⚠️ 只用 opacity，不能用 visibility:hidden 或 display:none：
      它现在是 SVG <text>，在 SVG 里 visibility 的继承与表现与 HTML 不同，
      而且提示本来就不参与布局（在 SVG 内部），没有"占位"问题需要处理。 */
.lamp-app.is-lit .lamp-hint {
  opacity: 0;
}

/* ══════════════════════════════════════════════════════════
   登录面板
   ══════════════════════════════════════════════════════════
 * 与台灯的间距（用户要求：「登录弹窗与灯的距离只到拉绳多 5px」）
 *
 *   间距刻意只有 5px：视觉上"表单就贴在灯下"。
 *   面板自带 6px 上内边距，让卡片不至于贴着灯显得局促。
 *
 *   ⚠️ 间距必须由 margin-top 承担，不能挂在 .lamp-stage 的 flex gap 上：
 *      灯灭时面板 height:0，但 flex gap 仍会被计算一次，
 *      灯灭态就会凭空多出一段空隙，把台灯挤出居中位置。
 *   ⚠️ padding-top 在两个状态下都要在。灯灭时面板 height:0 + overflow:hidden，
 *      内边距被裁掉，不影响居中。
 *
 * 浮入（优化 2）：固定小幅位移 + 淡入，曲线用 --ease-out-back（轻回弹）。
 * 位移只有 16px：参考项目用大回弹，小屏会溢出。
 *
 * visibility + pointer-events 让灯灭时的表单既不显示、也不可点、
 * 也不进入 Tab 序列（见 Q_H_server.md Q-035）。
 */
.login-panel {
  width: 100%;
  margin-top: var(--panel-gap);
  padding-top: 6px;
  opacity: 0;
  transform: translateY(16px) scale(0.98);
  visibility: hidden;
  pointer-events: none;
  /* 灯灭时不出现在布局流里，避免把提示文字挤偏 */
  height: 0;
  overflow: hidden;
  transition: opacity var(--dur-slow) var(--ease-standard),
              transform var(--dur-slow) var(--ease-out-back),
              visibility 0s linear var(--dur-slow);
}

.lamp-app.is-lit .login-panel {
  height: auto;
  overflow: visible;
  opacity: 1;
  transform: none;
  visibility: visible;
  pointer-events: auto;
  transition: opacity var(--dur-base) var(--ease-standard) 160ms,
              transform var(--dur-base) var(--ease-out-back) 160ms,
              visibility 0s 160ms;
}

/* 减少动效：直接切换，不做位移与过渡 */
.lamp-app.no-motion .lamp-svg,
.lamp-app.no-motion .login-panel,
.lamp-app.no-motion .lamp-eye,
.lamp-app.no-motion .lamp-beam,
.lamp-app.no-motion .lamp-beam-core,
.lamp-app.no-motion .lamp-halo,
.lamp-app.no-motion .lamp-pool,
.lamp-app.no-motion .lamp-cord-line,
.lamp-app.no-motion .lamp-hint {
  transition: none;
}

.lamp-app.no-motion .login-panel { transform: none; }

/* ── 卡片 ─────────────────────────────────────────────── */
/* 半透明 + 毛玻璃：让灯光与纹理透过来，与场景融合而非"压一块板子" */
.login-card {
  padding: var(--space-6) var(--space-5);
  background: var(--glass-bg);
  border: 1px solid var(--glass-border);
  border-radius: var(--radius-card);
  backdrop-filter: blur(var(--backdrop-blur));
  -webkit-backdrop-filter: blur(var(--backdrop-blur));
  box-shadow: 0 16px 48px var(--light-shadow);
}

/* 失败抖动（优化 3）：幅度小、时间短，只做横向位移 */
.login-card.is-shaking {
  animation: dsh-shake 420ms var(--ease-standard);
}

@keyframes dsh-shake {
  0%, 100% { transform: translateX(0); }
  15%      { transform: translateX(-7px); }
  30%      { transform: translateX(6px); }
  45%      { transform: translateX(-4px); }
  60%      { transform: translateX(3px); }
  80%      { transform: translateX(-2px); }
}

.login-title {
  margin-bottom: var(--space-1);
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-medium);
}

.login-subtitle {
  margin-bottom: var(--space-5);
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
}

/* ── 消息区 ───────────────────────────────────────────── */
.login-message:empty { display: none; }

.login-error {
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  background: var(--color-danger-dim);
  border: 1px solid var(--color-danger);
  border-radius: var(--radius-control);
  color: var(--color-text);
  font-size: var(--font-size-sm);
}

.lock-countdown:empty { display: none; }

.lock-countdown {
  margin-bottom: var(--space-4);
  color: var(--color-warning);
  font-size: var(--font-size-sm);
  font-variant-numeric: tabular-nums;
}

/* 非错误类提示（例如"口令已更新，请重新登录"）：
   与 .login-error 同一位置，但用中性/成功色，避免"成功也报红"。 */
.login-note {
  margin-bottom: var(--space-4);
  padding: var(--space-3);
  background: var(--color-bg-elevated);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-control);
  color: var(--color-text);
  font-size: var(--font-size-sm);
}

/* ── 表单 ─────────────────────────────────────────────── */
.field { margin-bottom: var(--space-4); }

/* 卡片半透明，输入框需更实的底以保证可读性 */
.login-card input[type="text"],
.login-card input[type="password"] {
  background: var(--input-bg-solid);
}

/* ── 提交按钮：含 spinner（优化 3）────────────────────── */
#login-submit {
  position: relative;
  margin-top: var(--space-2);
}

.btn-spinner {
  display: none;
  width: 14px;
  height: 14px;
  border: 2px solid currentColor;
  border-top-color: transparent;
  border-radius: var(--radius-pill);
  animation: dsh-spin 700ms linear infinite;
}

#login-submit.is-loading .btn-spinner { display: inline-block; }

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

.login-hint {
  margin-top: var(--space-4);
  color: var(--color-text-faint);
  font-size: var(--font-size-xs);
  line-height: var(--line-height-base);
}

/* ══════════════════════════════════════════════════════════
   响应式
   ══════════════════════════════════════════════════════════
 * ⚠️ 改动任何断点的 --lamp-shift，都要同步改 --lamp-block-h 与
 *   .lamp-svg 的 height（测试会核对三者一致），
 *   否则"面板离灯 5px"这条约束会在某些屏幕上失效。
 */

/* ── 平板（481–1024px）────────────────────────────────── */
@media (min-width: 481px) and (max-width: 1024px) {
  .lamp-stage { max-width: 380px; }
  .lamp-app { --lamp-shift: 44px; }
}

/* ── 移动端（≤480px）──────────────────────────────────────
   目标机型：华为 Mate 60 Pro 428×926、Redmi K90 Pro Max 412×893。
   **只改数值，不改布局模型** —— 与桌面同一套"居中→上移"逻辑，
   避免维护两套布局（这是 T10b 的教训，见 Q_H_server.md Q-038）。
*/
@media (max-width: 480px) {
  .lamp-app {
    --lamp-shift: 34.5px;
    /* 台灯变矮 → 面板间距基准跟着变矮 */
    --lamp-block-h: 222px;
    padding: var(--space-4) var(--space-4)
             calc(var(--space-4) + env(safe-area-inset-bottom));
  }

  .lamp-stage {
    max-width: 100%;
  }

  .lamp-svg {
    width: 148px;
    height: 222px;
  }

  /* 光池同步缩小，避免在窄屏溢出 */
  .lamp-pool {
    top: 210px;
    width: 210px;
    height: 210px;
    filter: blur(20px);
  }

  /* 提示：字号不低于 13px（12px 在手机上偏小）。
     SVG <text> 会被 148/200 缩小，故 font-size 按反比放大：
     13 ÷ (148/200) ≈ 17.6 用户单位，渲染后仍是 13px。 */
  .lamp-hint {
    font-size: 17.6px;
  }

  .login-card {
    padding: var(--space-5) var(--space-4);
    /* 移动端 GPU 较弱：毛玻璃会导致滚动卡顿，改用更实的底 */
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    background: var(--glass-bg-solid);
  }

  .login-title { font-size: var(--font-size-lg); }

  /* 小屏上噪点更显眼，再降一档 */
  .lamp-grain { opacity: 0.035; }

  /* 窄屏的视频再压暗一点：手机屏幕小、亮度感知更强，
     且窄屏上表单占的比例更大，需要更多对比度。
     ⚠️ 这两条放在**本响应式区**而不是视频样式旁边：
        本文件的约定是所有 @media 集中在末尾，
        散落在中间会让人以为"视频那段就是全部规则"。 */
  .lamp-video { filter: brightness(0.52) saturate(0.9); }
  .lamp-backdrop.is-ready .lamp-video { opacity: 0.45; }
}

/* ── 矮屏（横屏手机 / 小笔记本）──────────────────────────
   纵向空间不足时进一步压缩，并缩小上移距离，
   否则表单底部会被挤出视口。
*/
@media (max-height: 640px) {
  .lamp-app {
    --lamp-shift: 27.75px;    /* = 旧值 74 × 0.75 × 0.5 */
    --lamp-block-h: 186px;
  }

  .lamp-svg { width: 124px; height: 186px; }

  .lamp-pool { display: none; }   /* 矮屏没有地面空间给光池 */

  /* 13 ÷ (124/200) ≈ 21 用户单位 → 渲染后仍是 13px */
  .lamp-hint { font-size: 21px; }

  .login-card { padding: var(--space-4); }
}

/* ── 桌面：更大更舒展 ─────────────────────────────────── */
@media (min-width: 1025px) {
  .lamp-app {
    --lamp-shift: 55.5px;
    --lamp-block-h: 321px;
  }

  .lamp-svg { width: 214px; height: 321px; }

  .lamp-pool { top: 302px; width: 340px; height: 340px; }

  /* 15 ÷ (214/200) ≈ 14 用户单位 → 渲染后 15px */
  .lamp-hint { font-size: 14px; }
}
