/* ============================================================
   作品集网站 · 视觉规范
   阶段一：纯文字 + 灰阶骨架（不加主题色、不加图）
   模块主题色留在 --c-* 变量里，等素材定了直接替换
   ============================================================ */

:root {
  /* 底色 / 文字 */
  --bg:        #FBFAF7;   /* 暖白，不用纯白 */
  --ink:       #2A2724;   /* 深灰偏暖，不用纯黑 */
  --muted:     #8A8580;   /* 灰态文字 */
  --faint:     #B4AFA8;   /* 更弱的辅助文字 */
  --hair:      #E5E1DA;   /* 分隔线 */
  --panel:     #F3F0EA;   /* 图片卡片底 */

  --ink-dim:   #7C766F;   /* 关键词灰态 */
  /* 抽屉里的文字分三档，括号里是在 #FBFAF7 上的对比度：
     正文 --ink 14.23 / 辅助 --ink-2 9.09 / 占位 --ink-3 6.04。
     --muted / --faint 保留给首屏灰态和指标面板，别拿来写抽屉正文。 */
  --ink-2:     #4A453F;   /* 来源行、图注、数字说明、表单标签 */
  --ink-3:     #655F58;   /* TODO 占位、输入框 placeholder */

  /* ---- 热力色阶 ----
     五个关键词共用同一套，颜色由热度决定。
     低值端淡到几乎融进米色底，高值端烧到深赭。
     改这几个值就换掉全站配色。 */
  --heat-4:    #EDE8DE;   /* 冷 · 暖灰米，比底色深一点点，几乎不可见 */
  --heat-3:    #E8D5B8;   /* 浅麦 */
  --heat-2:    #E5A860;   /* 暖橙 */
  --heat-1:    #C4622D;   /* 热 · 砖红 */
  --heat-peak: #A8481C;   /* 最热 · 深赭，只给当前最热的那一个 */

  /* 字体 */
  --serif: "Noto Serif SC", "Songti SC", "Source Han Serif SC", serif;
  --sans:  "Noto Sans SC", "PingFang SC", "Source Han Sans SC", system-ui, sans-serif;
  --mono:  "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --hand:  "Zhi Mang Xing", "Xingkai SC", "STXingkai", cursive;   /* 只给批注用，正文不许用 */
  --ochre: #9C6024;       /* 手绘那支笔的实测墨色，流程图和批注都用它 */

  /* 度量 */
  --measure: 720px;       /* 正文最大宽度 */
  --block:   120px;       /* 区块间距下限 */
  --ease:    cubic-bezier(.22,.61,.36,1);
  --dur:     620ms;

  /* 首屏进场节奏：改 --line-gap 一个数就能调快慢，
     下划线和呼吸的时机都是从它算出来的，不用跟着手动改 */
  --line-lead: 100ms;                 /* 第一行等多久开始 */
  --line-gap:  500ms;                 /* 行与行的间隔 */
  --line-dur:  800ms;                 /* 每行淡入时长 */
  /* 系数 = 行数 - 1。首屏加减行数时这里要跟着改。
     只等最后一行「开始」淡入，不等它淡完 —— 下划线 0.9s 的浮现过程
     正好和最后一行重叠，观感上仍是最后出现，但整体早 800ms */
  --underline-delay: calc(var(--line-lead) + 5 * var(--line-gap) + 200ms);
}

@media (prefers-reduced-motion: reduce) {
  :root { --dur: 1ms; }

  /* 进场动画和呼吸全部取消，直接给终态 */
  .hero-line { opacity: 1; animation: none; }
  .kw::after,
  .kw.is-untouched::after { opacity: 1; animation: none; }
  .kw.is-untouched .kw-text { animation: none; }
}

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

/* [hidden] 是 UA 样式表里的 display:none，类选择器会盖掉它。
   .modal / .panel-row 都设了 display，必须在这里把 hidden 抢回来，
   否则「隐藏」的全屏遮罩会一直吞掉页面上的点击。 */
[hidden] { display: none !important; }

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  /* 别调回 300：Noto Sans SC 的 Light 在 12–16px 下中文笔画细到发虚，
     再高的对比度也救不回来 */
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

::selection { background: rgba(42,39,36,.10); }

/* ============================================================
   左上角标记
   不是导航栏 —— 只有一个名字，没有任何跳转项
   ============================================================ */

.mark {
  position: fixed;
  top: 28px;
  left: 32px;
  /* 低于移动端抽屉（40）—— 抽屉全屏推上来时应该盖住它，
     否则会和抽屉左上角的关闭按钮撞在一起 */
  z-index: 30;
  opacity: 1;
  animation: lineIn var(--line-dur) var(--ease) var(--line-lead) both;
}

.mark-name {
  font-family: var(--serif);
  font-size: 20px;
  font-weight: 500;
  letter-spacing: .04em;
  line-height: 1.2;
  color: var(--heat-1);          /* 和 favicon 同一个赭色 */
}

@media (max-width: 768px) {
  .mark { top: 20px; left: 22px; }
  .mark-name { font-size: 17px; }
}

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

/* ============================================================
   一、首屏 + 舞台
   ============================================================ */

.stage {
  display: flex;
  align-items: stretch;
  /* 必须是定高：min-height 会让抽屉被内容撑到几千 px，反而滚不动 */
  height: 100vh;
  height: 100svh;
  width: 100%;
  overflow: hidden;
}

.hero-pane {
  flex: 1 1 100%;
  min-width: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(32px, 6vw, 96px);
  transition: flex-basis var(--dur) var(--ease),
              padding var(--dur) var(--ease);
}

.hero {
  margin: 0;
  max-width: 30em;
  font-family: var(--serif);
  font-weight: 400;
  font-size: var(--hero-size, clamp(26px, 3.4vw, 44px));
  line-height: 2;
  letter-spacing: .02em;
  color: var(--muted);                 /* 整段初始是灰的 */
  transition: font-size var(--dur) var(--ease),
              color 420ms var(--ease);
}

/* ② 进场：五行依次淡入，间隔 150ms，虚线下划线最后浮现 */
.hero-line {
  display: block;
  opacity: 1;                       /* 基础态可见：动画没跑起来也不会丢内容 */
  animation: lineIn var(--line-dur) var(--ease) both;
}

.hero-line:nth-child(1) { animation-delay: calc(var(--line-lead) + 0 * var(--line-gap)); }
.hero-line:nth-child(2) { animation-delay: calc(var(--line-lead) + 1 * var(--line-gap)); }
.hero-line:nth-child(3) { animation-delay: calc(var(--line-lead) + 2 * var(--line-gap)); }
.hero-line:nth-child(4) { animation-delay: calc(var(--line-lead) + 3 * var(--line-gap)); }
.hero-line:nth-child(5) { animation-delay: calc(var(--line-lead) + 4 * var(--line-gap)); }
.hero-line:nth-child(6) { animation-delay: calc(var(--line-lead) + 5 * var(--line-gap)); }

@keyframes lineIn { from { opacity: 0; } to { opacity: 1; } }

/* —— 关键词 —— */

.kw {
  position: relative;
  display: inline-block;
  cursor: pointer;
  /* 棕色 —— 在整段灰字里是唯一有颜色的，一眼看出可点。
     用手绘那支笔的实测色，比 --heat-1 沉一些，不刺眼 */
  color: var(--ochre);
  padding: 0 .06em .04em;
  transition: color 600ms ease, opacity 420ms var(--ease);
  -webkit-tap-highlight-color: transparent;
}

/* 可点击提示。做成 ::after 而不是 border-bottom，
   这样进场时机和明暗呼吸都能单独控制，不受 .kw 本身的样式牵连 */
.kw::after {
  content: "";
  position: absolute;
  z-index: 2;                       /* 压在热力色块之上 */
  left: 0;
  right: 0;
  bottom: 0;
  border-bottom: 1.5px dashed rgba(156, 96, 36, .75);
  opacity: 1;                       /* 同上，基础态可见 */
  animation: underlineIn 900ms var(--ease) var(--underline-delay) both;
  pointer-events: none;
}

/* ③ 还没被点开的关键词，虚线极缓慢地明暗一次又一次。
   慢到几乎察觉不到，但眼睛会注意到「那里有动静」。
   五个词各自错开，不然会变成整页一起闪。 */
.kw.is-untouched::after {
  animation:
    underlineIn 900ms var(--ease) var(--underline-delay) both,
    lineBreathe 3.2s ease-in-out
      calc(var(--underline-delay) + 900ms + var(--breathe-offset, 0ms)) infinite;
}

@keyframes underlineIn { from { opacity: 0; } to { opacity: 1; } }

@keyframes lineBreathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: .4; }
}

/* 五个词共用同一套色阶，热度决定颜色 */
.kw { --c1: var(--heat-1); --c2: var(--heat-2); --c3: var(--heat-3); --c4: var(--heat-4); }

/* 当前最热的那一个，中心再深一档 */
.kw.is-hottest { --c1: var(--heat-peak); }

/* 热度高时文字本身也收回正文色，压住底下的浓色 */
/* 越热越深，不跳色系 */
.kw[data-heat-level="3"] .kw-text,
.kw[data-heat-level="4"] .kw-text { color: #7A4318; }

.kw:focus-visible {
  outline: 1px solid var(--ink);
  outline-offset: 4px;
  border-radius: 2px;
}

.kw-text { position: relative; z-index: 2; transition: color 600ms ease; }

/* 呼吸做在文字上 —— 下划线太细，明暗变化几乎看不出来。
   不能直接动 .kw 的 opacity：抽屉打开时要靠它把非焦点的词压到 30%，
   动画优先级高于普通声明，会把那个压制盖掉 */
.kw.is-untouched .kw-text {
  animation: textBreathe 3.2s ease-in-out
    calc(var(--underline-delay) + 900ms + var(--breathe-offset, 0ms)) infinite;
}

@keyframes textBreathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: .4; }
}

/* 抽屉开着的时候别在旁边一闪一闪 */
.stage.is-open .kw-text { animation: none; }

/* ============================================================
   三、热力沉淀
   多层径向渐变 + 分级色阶：热度低时只有最外圈的淡色，
   热度升高才逐层显现内圈的浓色，像真实热力图那样从边缘烧向中心
   ============================================================ */

.kw-heat {
  position: absolute;
  left: -.35em;
  right: -.35em;
  top: -.3em;
  bottom: -.3em;
  z-index: 1;                 /* 永远在文字下面（.kw-text 是 2） */
  pointer-events: none;
}

.heat-layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 900ms cubic-bezier(.4, 0, .2, 1);
  filter: blur(3px);          /* 只要一点点，让色阶衔接柔和 */
  will-change: opacity;
}

/* 每层用几个偏移的椭圆叠加，出来的是不规则的有机团块，不是圆也不是矩形 */

/* 最外圈：范围最大、颜色最淡、最先出现 */
.layer-4 {
  background:
    radial-gradient(ellipse 62% 85% at 28% 48%, var(--c4) 0%, transparent 72%),
    radial-gradient(ellipse 74% 66% at 66% 44%, var(--c4) 0%, transparent 72%),
    radial-gradient(ellipse 54% 92% at 48% 62%, var(--c4) 0%, transparent 70%);
}

.layer-3 {
  background:
    radial-gradient(ellipse 52% 72% at 32% 50%, var(--c3) 0%, transparent 70%),
    radial-gradient(ellipse 60% 56% at 62% 46%, var(--c3) 0%, transparent 70%);
}

.layer-2 {
  background:
    radial-gradient(ellipse 40% 58% at 38% 52%, var(--c2) 0%, transparent 68%),
    radial-gradient(ellipse 46% 44% at 60% 48%, var(--c2) 0%, transparent 68%);
}

/* 最内圈：范围最小、颜色最浓、最后出现 */
.layer-1 {
  background:
    radial-gradient(ellipse 26% 40% at 44% 52%, var(--c1) 0%, transparent 66%),
    radial-gradient(ellipse 30% 30% at 58% 48%, var(--c1) 0%, transparent 66%);
}

/* ============================================================
   抽屉打开时的左侧压缩状态
   ============================================================ */

.stage.is-open .hero-pane {
  flex-basis: 22%;
  /* 上内边距要能容下左上角那块标记，不然压缩后的文字会顶到它身上 */
  padding: 96px clamp(20px, 2.4vw, 40px) clamp(28px, 3vw, 56px);
  align-items: flex-start;
}

.stage.is-open .hero {
  /* 压缩态字号跟着视口走：窄屏笔记本上左栏只有 170~180px 可用，
     字号不降下去，句尾标点会被挤到下一行 */
  --hero-size: clamp(11px, 1.15vw, 17px);
  line-height: 2.2;
  max-width: none;
}

/* 除被点击的词以外，其余文字降到 30% 透明度 */
.stage.is-open .hero { color: rgba(138,133,128,.30); }
.stage.is-open .kw   { opacity: .30; }
.stage.is-open .kw.is-active { opacity: 1; color: var(--ochre); }

/* ============================================================
   四、抽屉
   ============================================================ */

.drawer {
  flex: 0 0 0%;
  min-width: 0;
  position: relative;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
  border-left: 1px solid transparent;
  opacity: 0;
  visibility: hidden;
  overscroll-behavior: contain;
  transition: flex-basis var(--dur) var(--ease),
              opacity 380ms var(--ease),
              visibility 0s linear var(--dur);
}

.stage.is-open .drawer {
  flex-basis: 78%;
  opacity: 1;
  visibility: visible;
  border-left-color: var(--hair);
  transition: flex-basis var(--dur) var(--ease),
              opacity 380ms 160ms var(--ease),
              visibility 0s;
}

.drawer-close {
  position: sticky;
  top: 0;
  align-self: flex-start;          /* 左上角 */
  flex: 0 0 auto;
  z-index: 5;
  margin: clamp(18px, 2vw, 32px) 0 0 clamp(18px, 2vw, 32px);
  width: 40px;
  height: 40px;
  display: grid;
  place-items: center;
  font-size: 22px;
  line-height: 1;
  color: var(--muted);
  background: var(--bg);
  border: 1px solid var(--hair);
  border-radius: 50%;
  cursor: pointer;
  transition: color 240ms, border-color 240ms;
}

.drawer-close:hover { color: var(--ink); border-color: var(--faint); }

/* 看板是块不透明的卡片，展开时会压住抽屉正文（桌面端约 130px）。
   给抽屉留出让位空间；看板收起了就把空间还回去。 */
@media (min-width: 769px) {
  html:not(.panel-collapsed) .stage.is-open .drawer {
    padding-right: 300px;
    transition: padding-right 320ms var(--ease);
  }
}

.drawer-body {
  width: 100%;
  max-width: var(--measure);
  margin: 0 auto;
  padding: clamp(40px, 5vw, 72px) clamp(24px, 5vw, 64px) clamp(96px, 14vh, 200px);
}

/* ============================================================
   五、抽屉内的统一结构
   ============================================================ */

.mod-head { margin-bottom: var(--block); }

.mod-title {
  margin: 0 0 .5em;
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(28px, 3vw, 38px);
  letter-spacing: .02em;
  color: var(--ink);
}

.mod-meta {
  margin: 0;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--ink-2);
}

.sec { padding-top: var(--block); }
.sec:first-of-type { padding-top: 0; }

.sec-title {
  margin: 0 0 .6em;
  font-family: var(--serif);
  font-weight: 500;
  font-size: clamp(20px, 2vw, 24px);
  letter-spacing: .02em;
  color: var(--ink);
}

.sec-meta {
  margin: 0 0 28px;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .06em;
  color: var(--ink-2);
}

.rule {
  height: 1px;
  border: 0;
  margin: 0 0 28px;
  background: var(--hair);
}

.lede {
  margin: 0;
  font-size: 17px;
  line-height: 2;
  letter-spacing: .01em;
  color: var(--ink);
}

/* —— 核心数字 —— */

.stat {
  margin: clamp(72px, 9vw, 120px) 0;
  text-align: center;
}

.stat-value {
  font-family: var(--mono);
  font-weight: 300;
  font-size: clamp(44px, 6.4vw, 76px);
  line-height: 1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.stat-value .unit {
  font-size: .42em;
  letter-spacing: .04em;
  margin-left: .18em;
  color: var(--muted);
}

.stat-rule {
  width: 120px;
  height: 1px;
  border: 0;
  margin: 28px auto;
  background: var(--hair);
}

.stat-label {
  margin: 0;
  font-size: 13px;
  line-height: 1.9;
  letter-spacing: .04em;
  color: var(--ink-2);
}

/* —— 正文 —— */

.body p {
  margin: 0 0 1.9em;
  font-size: 15.5px;
  line-height: 2.15;
  letter-spacing: .01em;
  color: var(--ink);
}

.body p:last-child { margin-bottom: 0; }

/* —— 图 —— */

.fig { margin: 64px 0 0; }

/* ---- 多张手机截图横向排，一行两个 ----
   两列也正好是「改版前 / 改版后」的天然排布，配对不会被拆开 */
.fig-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 44px 24px;
  align-items: start;
  margin: 64px 0 0;
}

/* 网格里的间距交给 gap，figure 自己不再带上边距 */
.fig-row .fig { margin: 0; }

/* 窄屏一列一张。手机截图的价值在于看清界面，
   挤成两列会小到读不了字，那就失去意义了 */
@media (max-width: 560px) {
  .fig-row { grid-template-columns: 1fr; gap: 40px; }
}

/* 后台 / 网页截图：浅色卡片 + 圆角 + 极淡阴影 */
.fig-frame.is-card {
  padding: 8px;
  background: var(--panel);
  border: 1px solid var(--hair);
  border-radius: 10px;
  box-shadow: 0 1px 2px rgba(42, 39, 36, .03);
}

/* 手机截图：统一套手机外框 */
.fig-frame.is-phone {
  max-width: 288px;
  margin: 0 auto;
  padding: 11px;
  background: var(--panel);
  border: 1px solid var(--hair);
  border-radius: 32px;
  box-shadow: 0 1px 2px rgba(42, 39, 36, .03);
  position: relative;
}

/* 听筒那道细缝，很淡，只为让它读起来是台手机 */
.fig-frame.is-phone::before {
  content: "";
  position: absolute;
  top: 5px;
  left: 50%;
  width: 46px;
  height: 3px;
  margin-left: -23px;
  border-radius: 2px;
  background: var(--hair);
}

.fig-screen {
  overflow: hidden;
  background: var(--bg);
  display: grid;
  place-items: center;
}

.fig-frame.is-card  .fig-screen { border-radius: 6px;  aspect-ratio: 16 / 10; }
.fig-frame.is-phone .fig-screen { border-radius: 22px; aspect-ratio: 9 / 18;  }

/* 放了真图就让图自己决定高度，不要硬套比例把图裁了 */
.fig-frame.has-img .fig-screen { aspect-ratio: auto; }

.fig-screen img {
  display: block;
  max-width: 100%;
  height: auto;
  margin: 0 auto;
}

/* 手机外框里的图撑满屏幕区 */
.fig-frame.is-phone .fig-screen img { width: 100%; }

/* 卡片里的图按原始尺寸走，但不放大、也不允许长得离谱。
   竖长的网页截图（比如侧边栏面板）撑满正文宽会变成一千多 px 高，
   限个高让它自己收窄居中 */
.fig-frame.is-card .fig-screen img {
  width: auto;
  max-height: 74vh;
}

.fig-placeholder {
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .08em;
  color: var(--ink-3);
}

/* 图下小字说明 —— 加了的话样式必须统一 */
.fig-caption {
  margin: 14px 0 0;
  font-size: 12.5px;
  line-height: 1.8;
  letter-spacing: .02em;
  color: var(--ink-2);
  text-align: center;
}

/* 待填标记，交付前应清空 */
/* 占位标记：靠等宽字体区分就够了，别再缩字号 ——
   .86em 套在 12px 的来源行上会掉到 10.3px，小到看不见 */
.todo {
  font-family: var(--mono);
  font-size: .95em;
  letter-spacing: .02em;
  color: var(--ink-3);
}

/* ============================================================
   十一、Footer
   ============================================================ */

.footer {
  border-top: 1px solid var(--hair);
  padding: clamp(72px, 10vw, 140px) clamp(24px, 5vw, 64px) clamp(48px, 6vw, 80px);
}

.footer-inner { max-width: var(--measure); margin: 0 auto; }

.footer-links {
  list-style: none;
  margin: 0 0 56px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
}

.footer-links a,
.footer-links button {
  font: inherit;
  font-family: var(--sans);
  background: none;
  padding: 0;
  cursor: pointer;
  font-size: 14px;
  letter-spacing: .03em;
  color: var(--ink-2);
  text-decoration: none;
  /* border 必须先归零：<button> 自带 2px outset 的默认边框，
     只写 border-bottom 的话上/左/右三条会留下来，变成一个方框 */
  border: 0;
  border-bottom: 1px solid var(--hair);
  padding-bottom: 3px;
  transition: color 240ms, border-color 240ms;
}

.footer-links a:hover,
.footer-links button:hover { color: var(--ink); border-color: var(--faint); }

/* 复制成功后的提示态，用等宽字把它和普通链接区分开 */
.footer-links button.is-done {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--ochre);
  border-bottom-color: transparent;
  cursor: default;
}

/* ---- 小程序浮窗 ---- */

.modal--mini .modal-card {
  width: auto;
  max-width: min(300px, 90vw);
  padding: 28px 28px 24px;
  text-align: center;
}

.mini-close {
  position: absolute;
  top: 8px;
  right: 10px;
  width: 28px;
  height: 28px;
  display: grid;
  place-items: center;
  font-size: 18px;
  line-height: 1;
  color: var(--ink-3);
  background: none;
  border: 0;
  cursor: pointer;
  transition: color 240ms;
}

.mini-close:hover { color: var(--ink); }

.mini-qr {
  width: 200px;
  height: 200px;
  display: block;
  margin: 0 auto 16px;
  /* 二维码不能被缩放算法糊掉，糊了就扫不出来 */
  image-rendering: -webkit-optimize-contrast;
}

.mini-hint {
  margin: 0 0 14px;
  font-size: 13px;
  letter-spacing: .03em;
  color: var(--ink-2);
}

.mini-copy {
  font: inherit;
  font-family: var(--sans);
  font-size: 12px;
  letter-spacing: .02em;
  color: var(--ink-3);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hair);
  padding: 0 0 2px;
  cursor: pointer;
  transition: color 240ms, border-color 240ms;
}

.mini-copy:hover { color: var(--ink); border-color: var(--faint); }

.mini-copy.is-done {
  font-family: var(--mono);
  color: var(--ochre);
  border-bottom-color: transparent;
  cursor: default;
}

@media (max-width: 480px) {
  .mini-qr { width: 176px; height: 176px; }
}

.footer-note {
  margin: 0;
  font-size: 11px;
  line-height: 1.8;
  letter-spacing: .03em;
  color: var(--ink-3);
}

/* ============================================================
   移动端：抽屉变成底部推上来的全屏页
   ============================================================ */

@media (max-width: 768px) {

  .stage { display: block; height: auto; }

  .hero-pane {
    min-height: 100svh;
    padding: clamp(22px, 6vw, 40px);
    transition: none;
  }

  /* 窄屏下字号收一档，保证长句不折行。
     最长的一句是 16 个字（含标点），字号要保证 16 字 + letter-spacing
     放得进「视口宽 − 两侧内边距」。5vw / 下限 15px 覆盖到 320px 的机型 */
  .hero { --hero-size: clamp(15px, 5vw, 26px); line-height: 2.1; }

  .stage.is-open .hero-pane { padding: clamp(22px, 6vw, 40px); align-items: center; }
  .stage.is-open .hero { --hero-size: clamp(15px, 5vw, 26px); }

  .drawer {
    position: fixed;
    inset: 0;
    z-index: 40;
    background: var(--bg);
    border-left: 0;
    opacity: 1;
    visibility: hidden;
    transform: translateY(100%);
    transition: transform var(--dur) var(--ease),
                visibility 0s linear var(--dur);
  }

  .stage.is-open .drawer {
    visibility: visible;
    transform: translateY(0);
    border-left: 0;
    transition: transform var(--dur) var(--ease), visibility 0s;
  }

  /* 拖拽期间关掉过渡，让它跟着手指走；松手后恢复过渡做回弹 */
  .drawer.is-dragging { transition: none; }

  /* 不能用 position: fixed —— 抽屉自带 transform，会把内部 fixed 降级成
     相对抽屉定位，按钮跟着内容一起滚走。手机端没有 ESC、也没有空白区可点，
     关闭按钮是唯一出口，滚没了就只能滑回顶部。
     sticky 在滚动容器里不受 transform 影响，保持基础规则里的 sticky 即可 */
  .drawer-close {
    top: max(12px, env(safe-area-inset-top));
    margin: 16px 0 0 16px;
  }

  .drawer-body {
    /* 按钮现在占正常流的位置，上内边距不用再给它留空 */
    padding: 24px 24px calc(120px + env(safe-area-inset-bottom));
  }

  .fig-frame.is-phone { max-width: 232px; }
}

/* 桌面端才有 hover 着色（阶段二） */
@media (hover: hover) and (pointer: fine) {
  .kw:hover { color: var(--ink); }
}

/* ============================================================
   七、运营指标面板
   角落里一块安静的小看板：不闪不跳、没有阴影、没有强调色
   ============================================================ */

.panel {
  position: fixed;
  right: 32px;
  bottom: 32px;
  z-index: 50;
  width: 240px;                       /* 定宽：值变长也不会把看板撑得跳一下 */
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  font-family: var(--mono);
  opacity: 0;
  transition: opacity 900ms linear;   /* 只淡入一次，之后不再动 */
  pointer-events: none;
  user-select: none;
}

.panel.is-ready { opacity: 1; }

.panel-card {
  width: 100%;
  padding: 18px;
  background: #F5F3EE;                /* 比页面底色略深一点点 */
  border: 1px solid var(--hair);
  border-radius: 6px;
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding-bottom: 12px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--hair);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .08em;
  color: #9A948C;
}

/* ---- 大数字 + 小标签，上下堆叠 ---- */

.metric { min-width: 0; }

.metric-num {
  font-size: 19px;
  line-height: 1.2;
  letter-spacing: -.01em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}

.metric-lab {
  margin-top: 3px;
  font-size: 10.5px;
  line-height: 1.5;
  letter-spacing: .04em;
  color: #8A8580;
  overflow-wrap: anywhere;
}

/* 前两个横向并排 */
.panel-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 12px;
  margin-bottom: 16px;
}

/* 其余纵向排列 */
.panel-list { display: grid; gap: 13px; }

/* ---- 复制信号 ---- */

.panel-signal {
  margin-top: 14px;
  font-size: 10.5px;
  line-height: 1.6;
  letter-spacing: .04em;
  color: #8A8580;
  opacity: 0;
  transition: opacity 1200ms linear;
}

.panel-signal.is-on { opacity: 1; }

/* ---- 软挽留 ---- */
/* 关抽屉后静止 20 秒长出来的一行。不打断、不遮挡，不理它就当没发生。 */

.panel-invite {
  display: block;
  width: 100%;
  margin-top: 14px;
  padding: 12px 0 0;
  border: 0;
  border-top: 1px solid var(--hair);
  font: inherit;
  font-size: 11.5px;
  line-height: 1.6;
  letter-spacing: .04em;
  text-align: left;
  color: var(--ink);
  background: none;
  cursor: pointer;
  pointer-events: auto;               /* .panel 整体是 none，控件要单独开 */
  opacity: 0;
  transition: opacity 1400ms linear, color 240ms;   /* 慢慢浮出来，不闪不跳 */
}

.panel-invite.is-on { opacity: 1; }
.panel-invite:hover { color: var(--muted); }

/* ---- 会话信息 ---- */

.panel-foot {
  margin-top: 16px;
  padding-top: 12px;
  border-top: 1px solid var(--hair);
  font-size: 10px;
  line-height: 1.5;
  letter-spacing: .04em;
  color: #A9A39C;
}

/* ---- 收起 / 展开 ---- */

.panel-toggle {
  display: none;                      /* 移动端用底部胶囊条的箭头，不用它 */
  padding: 2px 2px 2px 8px;
  font: inherit;
  font-size: 12px;
  line-height: 1;
  color: #A9A39C;
  background: none;
  border: 0;
  cursor: pointer;
  pointer-events: auto;               /* .panel 整体是 none，只有控件可点 */
  transition: color 240ms, transform 320ms var(--ease);
}

.panel-toggle:hover { color: var(--ink); }

.panel-capsule { display: none; }

@media (min-width: 769px) {

  .panel-toggle { display: block; }

  /* 收起态：只剩一行小字 */
  .panel.is-collapsed .panel-card { display: none; }

  .panel.is-collapsed .panel-capsule {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 7px 10px;
    font-family: var(--mono);
    font-size: 11px;
    letter-spacing: .04em;
    font-variant-numeric: tabular-nums;
    color: #8A8580;
    background: none;
    border: 0;
    border-radius: 4px;
    cursor: pointer;
    pointer-events: auto;
    transition: color 240ms;
  }

  .panel.is-collapsed .panel-capsule:hover { color: var(--ink); }

  .panel-capsule-main { display: flex; align-items: center; gap: 7px; }

  .panel-caret { font-size: 12px; line-height: 1; transform: rotate(180deg); }
}

/* ---------- 移动端：底部胶囊条 ---------- */

@media (max-width: 768px) {

  .panel {
    right: 0;
    left: 0;
    bottom: 0;
    width: auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    pointer-events: auto;
    transform: translateY(0);
    transition: opacity 900ms linear, transform 320ms var(--ease);
  }

  /* 滚动时自动收起，停止滚动时浮现 */
  .panel.is-tucked:not(.is-expanded) { transform: translateY(105%); }

  .panel-capsule {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    width: 100%;
    height: 38px;
    margin: 0;
    padding: 0 18px calc(0px + env(safe-area-inset-bottom) / 2);
    font-family: var(--mono);
    font-size: 11.5px;
    letter-spacing: .04em;
    color: #57524D;
    background: rgba(251, 250, 247, .82);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    border: 0;
    border-top: 1px solid var(--hair);
    cursor: pointer;
    order: 2;                          /* 胶囊在下，看板在上 */
  }

  .panel-capsule-main {
    display: flex;
    align-items: center;
    gap: 8px;
    font-variant-numeric: tabular-nums;
  }

  .panel-dot { color: #A9A39C; }

  /* 移动端底部那条不用再标一遍，标签和它后面那个间隔点一起去掉 */
  .panel-capsule-tag,
  .panel-capsule-tag + .panel-dot { display: none; }

  .panel-caret {
    font-size: 13px;
    line-height: 1;
    transform: none;
    transition: transform 320ms var(--ease);
  }

  .panel.is-expanded .panel-caret { transform: rotate(180deg); }

  .panel-card {
    order: 1;
    max-height: 0;
    padding-top: 0;
    padding-bottom: 0;
    overflow: hidden;
    border: 0;
    border-top: 1px solid var(--hair);
    border-radius: 0;
    background: rgba(245, 243, 238, .92);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    transition: max-height 380ms var(--ease), padding 380ms var(--ease);
  }

  .panel.is-expanded .panel-card {
    max-height: 60vh;
    padding-top: 18px;
    padding-bottom: 18px;
  }
}

/* ============================================================
   八 / 九、退出挽留弹窗 + 联系表单
   ============================================================ */

.modal {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: grid;
  place-items: center;
  padding: 24px;
}

.modal-scrim {
  position: absolute;
  inset: 0;
  background: rgba(42, 39, 36, .18);
  -webkit-backdrop-filter: blur(2px);
  backdrop-filter: blur(2px);
  opacity: 0;
  transition: opacity 320ms var(--ease);
}

.modal-card {
  position: relative;
  width: min(440px, 100%);
  padding: clamp(32px, 5vw, 48px);
  background: var(--bg);
  border: 1px solid var(--hair);
  border-radius: 4px;
  box-shadow: 0 24px 60px rgba(42, 39, 36, .10);
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 320ms var(--ease), transform 320ms var(--ease);
}

.modal.is-on .modal-scrim { opacity: 1; }
.modal.is-on .modal-card  { opacity: 1; transform: none; }

/* 「好啊」之后表单滑出来 */
@keyframes slideIn {
  from { opacity: 0; transform: translateX(18px); }
  to   { opacity: 1; transform: none; }
}
.modal-stage.slide-in { animation: slideIn 420ms var(--ease) both; }

.modal-title {
  margin: 0 0 24px;
  font-family: var(--serif);
  font-weight: 500;
  font-size: 24px;
  letter-spacing: .04em;
  color: var(--ink);
}

.modal-line {
  margin: 0 0 20px;
  font-size: 14.5px;
  line-height: 2;
  color: var(--ink);
}

.modal-ask {
  margin: 0 0 32px;
  font-size: 14.5px;
  line-height: 2;
  color: var(--ink);
}

.modal-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;   /* 「再逛逛」在左，「好啊」在右 */
  gap: 16px;
}

.modal-actions--single { justify-content: flex-end; }

/* 「再逛逛」弱化，但不藏 —— 正常字号、可点区域够大 */
.btn-ghost {
  padding: 10px 4px;
  font: inherit;
  font-size: 14px;
  color: var(--ink-2);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hair);
  cursor: pointer;
  transition: color 240ms, border-color 240ms;
}

.btn-ghost:hover { color: var(--ink); border-color: var(--faint); }

.btn-primary {
  padding: 12px 32px;
  font: inherit;
  font-size: 14px;
  letter-spacing: .04em;
  color: var(--bg);
  background: var(--ink);
  border: 1px solid var(--ink);
  border-radius: 2px;
  cursor: pointer;
  transition: opacity 240ms;
}

.btn-primary:hover    { opacity: .86; }
.btn-primary:disabled { opacity: .45; cursor: default; }

.modal-rule {
  height: 1px;
  border: 0;
  margin: 32px 0 16px;
  background: var(--hair);
}

/* 全站唯一保留的注脚 */
.modal-note {
  margin: 0;
  font-size: 12px;
  line-height: 1.9;
  letter-spacing: .02em;
  color: var(--ink-2);
}

/* ---------- 表单 ---------- */

.form-row {
  display: grid;
  grid-template-columns: 6.5em 1fr;
  align-items: center;
  column-gap: 14px;
  margin-bottom: 18px;
}

.form-label {
  font-size: 13px;
  letter-spacing: .04em;
  color: var(--ink-2);
}

.form-row input,
.form-row textarea {
  font: inherit;
  font-size: 14px;
  color: var(--ink);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--hair);
  padding: 8px 2px;
  border-radius: 0;
  transition: border-color 240ms;
}

.form-row textarea { resize: vertical; line-height: 1.9; align-self: start; }

.form-row input:focus,
.form-row textarea:focus { outline: none; border-bottom-color: var(--faint); }

.form-row input::placeholder,
.form-row textarea::placeholder { color: var(--ink-3); }

.form-row input.is-missing { border-bottom-color: var(--muted); }

.form-warn {
  margin: 4px 0 18px;
  font-size: 11.5px;
  line-height: 1.8;
  color: var(--ink-3);
}

.form-warn code { font-family: var(--mono); font-size: .95em; }

.form .modal-actions { margin-top: 28px; }

@media (max-width: 480px) {
  .form-row { grid-template-columns: 1fr; row-gap: 6px; }
}

/* ============================================================
   十 · ② 沉默唤醒 —— 缓慢的 opacity 变化，不抖动
   ============================================================ */

@keyframes breathe {
  0%, 100% { opacity: 1; }
  50%      { opacity: .38; }
}

.kw.is-breathing { animation: breathe 3.4s ease-in-out 2; }

/* 回访者的联系入口 —— 克制，不喧宾夺主 */
.revisit { margin: 0 0 40px; }

.revisit-link {
  padding: 0 0 4px;
  font: inherit;
  font-family: var(--sans);
  font-size: 14px;
  letter-spacing: .04em;
  color: var(--ink);
  background: none;
  border: 0;
  border-bottom: 1px solid var(--faint);
  cursor: pointer;
  transition: opacity 240ms;
}

.revisit-link:hover { opacity: .7; }


/* ============================================================
   分支流程图
   页面底色、极淡线条，不用卡片不用阴影
   ============================================================ */

.diagram {
  position: relative;
  margin: 56px 0 0;
  padding-bottom: 8px;
  --dg-row: 2.9em;                    /* 每个分支占的高度，竖线收尾要用它算 */
  --dg-stem: 26px;                    /* 竖线到文字的横向距离 */
  font-size: 15.5px;
  line-height: 1.6;
  letter-spacing: .01em;
}

.dg-root {
  color: var(--ochre);
  padding-bottom: .5em;
}

/* 整块是一个 grid，五行共用同一组列 —— 这样原因列取五个里最宽的，
   箭头和措施列自然全部对齐。拆成五个 grid 就对不齐了。 */
.dg-branches {
  position: relative;
  padding-left: var(--dg-stem);
  display: grid;
  grid-template-columns: max-content max-content auto;
  column-gap: 20px;
  align-items: center;
}

/* 主干竖线：从根节点垂下来，停在最后一个分叉的位置，不多伸一截 */
.dg-branches::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0;
  bottom: calc(var(--dg-row) / 2);
  border-left: 1px solid var(--hair);
}

.dg-branches > span {
  display: flex;
  align-items: center;
  min-height: var(--dg-row);
}

.dg-reason {
  position: relative;
  color: var(--ochre);
}

/* 每个分支的横杠，和竖线接成 ├─ */
.dg-reason::before {
  content: "";
  position: absolute;
  left: calc(var(--dg-stem) * -1);
  top: 50%;
  width: calc(var(--dg-stem) - 8px);
  border-top: 1px solid var(--hair);
}

.dg-arrow  { color: rgba(156, 96, 36, .40); }
.dg-action { color: var(--ochre); }

/* 「暂时不需要」没有对应措施 —— 这是刻意的，不是漏了。
   措施那一列压到同色系的更淡一档（不是换成灰，那样会像另一套东西），
   让「这里本来就空着」看得出来。 */
.dg-action.is-none { color: rgba(156, 96, 36, .45); }

/* 线性流程链 */
.dg-chain {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px 14px;
  color: var(--ochre);
}

.dg-step { position: relative; }

/* 标了 mark 的那一步：底下画一道，等于在图上把它圈出来 */
.dg-step.is-mark::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -5px;
  border-bottom: 1px solid rgba(156, 96, 36, .5);
}

/* 批注：像是画完之后在旁边补的一笔 */
.dg-note {
  margin: 22px 0 0;
  padding-left: calc(var(--dg-stem) + 38%);
  font-family: var(--hand);
  font-size: 17px;                    /* 手写体字面小，视觉上比正文小一档 */
  line-height: 1.5;
  letter-spacing: .02em;
  color: var(--ochre);
  transform: rotate(-1.6deg);
  transform-origin: 0 50%;
}

@media (max-width: 768px) {
  .diagram { font-size: 14.5px; --dg-stem: 20px; }
  .dg-branches { column-gap: 14px; }
  .dg-note { padding-left: var(--dg-stem); font-size: 16px; }
}

/* 窄到放不下就允许横向滚动，绝不换行把两栏对齐搞乱 */
@media (max-width: 420px) {
  .diagram { overflow-x: auto; }
  .dg-branches { min-width: max-content; }
}
