@font-face {
  font-family: 'AppSans';
  src: url('fonts/Inter.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'AppSerif';
  src: url('fonts/PlayfairDisplay-700.woff2') format('woff2');
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

:root {
  --bg: #0b0f17;
  --bg-2: #101623;
  --card: rgba(21, 29, 44, 0.86);
  --line: rgba(255, 255, 255, 0.1);
  --text: #eaf0fa;
  --muted: #8b9ab1;
  --accent: #4f8dfd;
  --accent-2: #22d3ee;
  --sat: env(safe-area-inset-top);
  --sab: env(safe-area-inset-bottom);
  --kb: 0px;                 /* 键盘高度；界面无输入框后恒为 0，保留给 toast 定位 */
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: 'AppSans', -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Hiragino Sans GB', 'Microsoft YaHei', 'Noto Sans SC', sans-serif;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}
body { position: fixed; inset: 0; overflow: hidden; touch-action: manipulation; }
[hidden] { display: none !important; }

/* ---------------- 舞台：整屏只有海报 ---------------- */
/* 海报固定 9:16：宽度优先充满（左右贴边），高度等比后在竖直方向居中，
   上下多出来的区域露出 body 底色当色块。所以这里不留左右 / 下内边距 ——
   留了就会让海报缩成"带边距的小卡片"，也就和另存的成品不是一回事了。
   安全区不参与：9:16 居中后，上下色块（各约 80pt）本身就比状态栏 / Home 条更高。

   `--stage-pt` 是**独立全屏形态下的居中补正**（默认 0，浏览器里恒为 0）：
   那种形态下布局视口比物理屏矮一个状态栏且锚在顶部，不补就会整体偏上半个状态栏。
   因为全局是 border-box，加内边距不会改变舞台自身框高，所以「量框高 → 写内边距」
   是幂等的。写这段的原因与实测数据见 app.js 的 syncStageCenter()。 */
#stage {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--stage-pt, 0px) 0 0;
}
.canvas-wrap {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 0;
}
#poster {
  /* 等比 contain：宽度够就宽满；窗口比 9:16 更扁时按高度适配，不裁不扭、永不拉伸 */
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  /* 圆角（2026-09-22 起）：**半径不在 CSS 里写死，由 JS 的 syncPosterRadius() 写内联样式** ——
     它必须等于「设计值 32 × 显示比例」，而显示比例只有运行时才知道；写成百分比会变成椭圆角
     （百分比圆角在宽 / 高上分别解析）。
     ⚠ 铁律仍在：**圆角是画进 canvas 的**（render() 里 roundRectPath + clip，四角透明），
     CSS 这边只是让屏幕与成品对得上 —— 不许出现「只在 CSS 加圆角、成品还是直角」的写法。
     阴影仍然不加。 */
  touch-action: manipulation;
}
/* 长按是「保存」，双击是「隐藏」，上下拖动是「缩放字号」——
   所以既要压住系统菜单与文本选择，也要压住浏览器自己的滚动 / 橡皮筋 */
#stage, .canvas-wrap, #poster {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  user-select: none;
}
body:not(.raw) #stage { touch-action: none; }

/* 自适应画布（`?fit=device`，默认关）：位图就是设备分辨率、海报要真正贴边，
   安全区画进画布内部 —— 所以这里同样不留边距（与默认规则一致，留着是为了
   开关打开时语义明确，不依赖默认值）。
   注意：这条的选择器权重更高，会**盖掉 `--stage-pt` 补正** —— 那是对的：
   自适应模式下画布比例就等于框的比例、海报本来就填满框，没有可补的居中偏差；
   真补上去反而会把画布压小、弄出左右黑边。 */
body.adaptive:not(.raw) #stage { padding: 0; }

.overlay {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  background: rgba(6, 10, 18, 0.72);
  font-size: 13px; color: var(--muted);
  transition: opacity 0.25s;
}
.overlay.hide { opacity: 0; pointer-events: none; }
.spinner {
  width: 26px; height: 26px; border-radius: 50%;
  border: 2.5px solid rgba(255, 255, 255, 0.15);
  border-top-color: var(--accent);
  animation: spin 0.8s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ---------------- 换图调整层（2026-09-22 重做） ----------------
   换图后弹出**不透明的整屏层**：原海报完全不动、也不出现在层里，层里只有居中的换图区
   与这张图 —— 窗口内清晰，窗口外是同一张图半透明压暗后的其余部分（拖动 / 缩放的引导）。
   窗口的位置、圆角与压暗全由 JS 画在 #editCvs 上（亮暗交界即边界），所以这里
   **没有任何 border / outline / 虚线，也没有 DOM 遮罩**。
   z-index 25 = 高于海报、低于 #hint(30)（那行操作说明要一直读得清）；
   pointer-events:none：不抢事件，拖动 / 双指 / 点击照旧落到海报上由手势层处理。 */
#editLayer {
  position: fixed;
  inset: 0;
  z-index: 25;
  background: var(--bg);
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s ease-out;   /* 只淡入；退出硬切，避免与底层海报切换打架 */
}
#editLayer.on { opacity: 1; }
/* 画布按「层尺寸 × dpr」定缓冲（见 drawEditPreview），CSS 尺寸就是铺满整层 */
#editCvs { display: block; width: 100%; height: 100%; }

/* ---------------- 语音波形层（播放期独占，2026-09-22） ----------------
   波形就是「停止按钮」：播放期间只有它可点，点它就停。位置与宽度由 JS 按 3 区（英文句 en）
   与正文列宽换算成屏幕坐标写入，这里只管外观。
   三条刻意的约束（用户明确要求「不可太突兀」「透过波形看得到 3 区的英文句子」）：
     ① **只有竖条，没有任何底板**（2026-09-22 用户要求去掉原来的 34% 深色底与描边）——
        所以这里没有 background / box-shadow / border-radius，也**绝不用 backdrop-filter**
        （一模糊就会把底下的英文句糊掉）；
     ② 竖条要细、间距要大、振幅只到 45%–85%、整组只占 58% 高度 —— 不把字形横穿成一片；
     ③ 竖条直接压在文字与照片上，所以给**竖条本身**加一圈极淡的暗投影保证亮背景（雾天照片）上
        也看得见 —— 只作用于竖条，不引入任何「框」。 */
#wave {
  position: fixed;
  z-index: 40;                 /* 高于海报与 #hint(30)，低于 #toast(60) / #saveSheet(70) */
  pointer-events: none;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transform: translateY(14px);  /* 入场：从下往上「升起」 */
  transition: opacity 0.24s ease-out, transform 0.28s cubic-bezier(0.22, 0.61, 0.36, 1);
}
#wave.show { opacity: 1; transform: translateY(0); }
/* 竖条尺寸与间距由 JS 按显示比例写 --wave-w / --wave-gap，保证随海报等比缩放 */
.wave-bars {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--wave-gap, 12px);
  width: 100%;
  height: 58%;
}
.wave-bars i {
  display: block;
  width: var(--wave-w, 3px);
  height: 100%;
  border-radius: 999px;
  background: linear-gradient(180deg, rgba(34, 211, 238, 0.62), rgba(79, 141, 253, 0.40));
  /* 极淡投影：让裸竖条在亮背景（雾天照片）上也不糊掉。半径小、几乎不扩散，观感上仍是「细线」 */
  filter: drop-shadow(0 0 3px rgba(6, 10, 18, 0.55));
  transform: scaleY(0.3);
  animation: waveBars var(--t, 1100ms) ease-in-out infinite alternate;
  animation-delay: var(--d, 0ms);
}
@keyframes waveBars {
  from { transform: scaleY(var(--h0, 0.18)); }
  to { transform: scaleY(var(--h1, 0.7)); }
}
/* 减少动态偏好：竖条仍在（只是停掉循环动画），静态高低错落仍看得出是波形 */
@media (prefers-reduced-motion: reduce) {
  .wave-bars i { animation: none; transform: scaleY(var(--h1, 0.7)); }
  #wave { transform: none; transition: opacity 0.2s; }
}

/* ---------------- 一次性手势提示（非常驻：3 秒后 / 任意操作即消失） ---------------- */
.hint {
  position: fixed;
  left: 50%;
  bottom: calc(var(--sab) + 26px);
  transform: translateX(-50%);
  z-index: 30;
  margin: 0;
  padding: 9px 16px;
  border-radius: 999px;
  background: rgba(21, 29, 44, 0.82);
  border: 1px solid var(--line);
  color: var(--text);
  font-size: 12.5px;
  white-space: nowrap;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  pointer-events: none;
  transition: opacity 0.4s;
  max-width: 92vw; overflow: hidden; text-overflow: ellipsis;
}
.hint.hide { opacity: 0; }
/* 调整模式的操作说明更长（还要说明「点图片换一张」），一行放不下 → 允许折行。
   普通提示仍是一行 nowrap，不受影响。 */
.hint.wrap {
  /* pre-line：认文本里的 \n（断行位置由 JS 定，免得浏览器把「换一张」拦腰断）；
     left:50% 会让收缩后的可用宽度只剩半屏，用 max-content 撑开 */
  white-space: pre-line;
  text-align: center;
  line-height: 1.55;
  width: max-content;
  max-width: 86vw;
  border-radius: 16px;
}

/* ---------------- 提示 / 保存反馈 ---------------- */
.toast {
  position: fixed; left: 50%;
  bottom: calc(var(--sab) + var(--kb) + 26px);
  transform: translate(-50%, 20px);
  background: rgba(28, 38, 56, 0.96);
  border: 1px solid var(--line);
  color: var(--text);
  padding: 11px 18px; border-radius: 999px;
  font-size: 13px; white-space: nowrap;
  opacity: 0; pointer-events: none; transition: opacity 0.22s, transform 0.22s;
  z-index: 60; max-width: 90vw; overflow: hidden; text-overflow: ellipsis;
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }

/* ---------------- 保存浮层（iOS：长按图片 → 存储到照片） ---------------- */
#saveSheet {
  position: fixed; inset: 0; z-index: 70;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 20px; padding: 24px;
  background: rgba(4, 8, 16, 0.94);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
}
#saveSheet[hidden] { display: none; }
#saveImg {
  width: min(76vw, 400px); max-width: 100%; height: auto; max-height: 70vh;
  border-radius: 14px; box-shadow: 0 20px 60px rgba(0, 0, 0, 0.65);
  /* 关键：把 #stage 上那条全局的 touch-callout:none / user-select:none 覆盖回来，
     否则 iOS 长按出不来「存储到照片」菜单 */
  -webkit-touch-callout: default;
  -webkit-user-select: auto;
  user-select: auto;
}
#saveTip { margin: 0; font-size: 15px; line-height: 1.6; color: #dbe4f5; text-align: center; }

/* ---------------- 只显示海报本身（导出 / 调试）：?raw=1 ---------------- */
body.raw { position: static; overflow: auto; height: auto; background: #000; }
body.raw #stage { position: static; padding: 0; display: block; }
body.raw .canvas-wrap { display: block; }
body.raw #poster {
  max-width: 100%;
  max-height: none;
  width: 100%;
  height: auto;
  border-radius: 0;
  box-shadow: none;
}
body.raw .hint,
body.raw .overlay,
body.raw #saveSheet,
body.raw #wave,
body.raw #editLayer,
body.raw .toast { display: none !important; }
