/* =========================================================
   screens.css —— 滚动分屏（背景固定，仅内容交接）
   1. 滚动容器与吸附（含隐藏滚动条）
   2. 共用变量
   3. 单屏 .screen
   4. 主页屏：打字机 + 闪动光标
   5. 关于屏：暗角 + 排版 + 入场迟滞
   6. 滚动提示 .hint
   7. 响应式与无障碍
   ========================================================= */

/* ---------- 1. 滚动容器与吸附 ---------- */
html {
  /* 整页按屏吸附：一次滚动交接一屏 */
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
  background: #02030a;

  /*
   * 隐藏浏览器自带滚动条，但保留滚动能力。
   * scrollbar-width 给 Firefox，-ms-overflow-style 给旧版 Edge，
   * ::-webkit-scrollbar 给 Chrome / Safari / 新版 Edge。
   */
  scrollbar-width: none;
  -ms-overflow-style: none;
}

html::-webkit-scrollbar,
body::-webkit-scrollbar {
  width: 0;
  height: 0;
  display: none;
}

/*
 * 隐藏滚动条会同时抹掉「这一页还能往下滚」的视觉线索，
 * 所以第一屏的 .hint（向下滚动提示）必须保留，别顺手删了。
 */

/*
 * 内容层必须显式高于固定的 .space。
 * .space 是 position: fixed（会生成层叠上下文，z-index 为 auto），
 * 而普通流内容的绘制层级低于定位元素，不给 z-index 就会被背景盖住。
 */
.screens {
  position: relative;
  z-index: 1;
}

/* ---------- 2. 共用变量 ---------- */
:root {
  /*
   * 展示用字体。默认走「细体无衬线 + 大字号 + 微正字距」，
   * 这是最稳的高级感路线（思源宋体等衬线在很多 Windows 上会回退到
   * 点阵感很强的 SimSun，反而掉档）。
   * 想换衬线：整行替换为
   *   "Songti SC", "Noto Serif SC", "Source Han Serif SC", Georgia, serif;
   */
  --display-font: "PingFang SC", "HarmonyOS Sans SC", "Microsoft YaHei UI",
                  "Helvetica Neue", system-ui, sans-serif;

  /*
   * 动效令牌（motion tokens）。
   * 取自 transitions.dev 的 text reveal / 全局令牌表 —— 那套值是按"用途"调过的，
   * 直接引用比自己拍脑袋靠谱：
   *   --duration-very-slow  → 强调时刻、徽标出现、文字浮现
   *   --ease-smooth-out     → 位移类变化的默认缓动
   *   --distance-medium     → 文字浮现的距离
   *   --blur-medium         → 文字浮现的模糊量（注意只有 3px）
   */
  --duration-very-slow: 500ms;
  --duration-stagger: 40ms;
  --ease-smooth-out: cubic-bezier(.22, 1, .36, 1);
  --distance-medium: 12px;
  --blur-medium: 3px;
}

/* ---------- 3. 单屏 ---------- */
.screen {
  position: relative;

  display: grid;
  place-items: center;

  /* 100svh 兼容移动端地址栏收缩；不支持的浏览器回退到 100vh */
  min-height: 100vh;
  min-height: 100svh;

  padding: calc(var(--nav-h) + 24px) 20px 40px;

  scroll-snap-align: start;
  scroll-snap-stop: always;
}

/* ---------- 4. 主页屏：打字机 + 闪动光标 ---------- */
/*
 * 标题锚定在左下角，而不是浮在正中：
 * 上方留出大片星空，与偏右的星云团形成对角构图；
 * 底部居中的 .hint 在横向不与它冲突（标题靠左，提示居中）。
 * align-items 覆盖 .screen 的 center，改成贴底。
 */
.screen--home {
  align-items: end;
  justify-items: start;

  padding-left: clamp(28px, 12vw, 200px);
  /* 离底部留足距离，避开 .hint */
  padding-bottom: clamp(96px, 17vh, 190px);
}

.home-hero {
  /*
   * 变量统一放在共同父级，.typing / .typing__char / 发丝线 靠继承拿到，
   * 改一处整段动画就保持同步。
   */

  /* ---- 单字"浮现"的四个参数：直接引用上面的令牌 ---- */
  --stagger-dur: var(--duration-very-slow);   /* 500ms */
  --stagger-distance: var(--distance-medium); /* 12px  */
  --stagger-blur: var(--blur-medium);         /* 3px   */
  --stagger-ease: var(--ease-smooth-out);

  /*
   * ---- 打字机时序 ----
   * 两个可调旋钮：
   *   --typing-delay  首字启动前的停顿（越小，第一个字出现得越早）
   *   --typing-step   两字之间的间隔（越小，字蹦得越快）
   *
   * 总时长由间隔推导，所以调间隔会自动同步裁切动画、光标停闪和发丝线，
   * 不用去别处改常数。
   */
  --typing-delay: .12s;
  --typing-step: .33s;
  --typing-duration: calc(var(--typing-step) * 2);

  /*
   * 末字浮现完成的时刻 = 起手 + 打字 + 单字浮现
   * 光标的停闪、发丝线的展开都对齐到这里。
   */
  --reveal-end: calc(var(--typing-delay) + var(--typing-duration) + var(--stagger-dur));

  display: flex;
  flex-direction: column;
  align-items: flex-start;
}

/* 发丝线：全段文字落定后从左展开，给标题一个视觉落点 */
.home-hero__rule {
  display: block;
  width: 88px;
  height: 1px;
  margin-top: 24px;

  background: linear-gradient(
    90deg,
    rgba(124, 196, 255, .95) 0%,
    rgba(177, 140, 255, .45) 55%,
    rgba(177, 140, 255, 0) 100%
  );

  /*
   * both：延迟期间应用 from（width: 0），结束后停在 to。
   * 基线 width 写成终值，即使动画完全不生效，线也是完整的 88px。
   */
  /* 与单字浮现共用同一套时长/缓动，整段动画节奏才统一 */
  animation: rule-grow var(--stagger-dur) var(--stagger-ease) var(--reveal-end) both;
}

@keyframes rule-grow {
  from { width: 0; }
  to   { width: 88px; }
}

.typing {
  display: flex;
  align-items: center;

  font-family: var(--display-font);
  font-size: clamp(34px, 6vw, 64px);
  font-weight: 200;
  line-height: 1.2;
  letter-spacing: .08em;
  color: #fff;
  text-shadow: 0 2px 30px rgba(0, 0, 0, .55);
}

/*
 * 打字机：靠 width + overflow: hidden 逐字露出（光标靠这个宽度跟随）。
 * steps(2) —— 两个字分两步。
 *
 * 基线 width 直接写成终值，配合 fill-mode: both：
 * 延迟期间用 keyframe 的 from（width: 0）把它压住，
 * 万一动画不生效，文字也是完整可读的，不会变成一片空白。
 *
 * 2.16em = 2 个字 × (1em 字宽 + .08em 字距)。
 * CJK 全角字在绝大多数中文字体里正好 1em，改了 letter-spacing 要同步改。
 */
.typing__text {
  flex: none;
  display: inline-block;
  overflow: hidden;
  white-space: nowrap;

  width: 2.16em;

  /*
   * 上下各留 .45em：给字符的模糊外溢留出空间，
   * 否则 overflow: hidden 会把模糊的上下边缘齐齐切掉。
   * 再用等量负 margin 抵消，布局高度不变（box-sizing 是 border-box，
   * 纵向 padding 不影响上面 width 的横向计算）。
   */
  padding: .45em 0;
  margin: -.45em 0;

  animation: typing var(--typing-duration) steps(2, end) var(--typing-delay) both;
}

@keyframes typing {
  from { width: 0; }
  to   { width: 2.16em; }
}

/*
 * 单字浮现。
 *
 * 手法照搬 transitions.dev 的 text reveal：
 *   opacity 0 → 1
 *   translateY(12px) → 0
 *   blur(3px) → 0
 *   500ms / cubic-bezier(.22, 1, .36, 1)
 * 只有这三样，没有任何额外的发光、缩放、旋转 ——
 * 成熟配方之所以克制，是因为堆装饰就是廉价感的来源。
 *
 * 起点正好是这个字被裁切边放出来的时刻（delay + step × 序号），
 * 所以看到的是：光标扫过 → 字从下方 12px 处带着轻微虚化升起、对焦。
 * 不提前起跑，避免半透明的字被硬切边切出一道生硬的竖直边。
 *
 * 用 keyframes + both（而不是在基类写 opacity: 0）：
 * 动画整体失效时文字仍然可见，不会白屏。
 */
.typing__char {
  display: inline-block;

  animation: char-in var(--stagger-dur) var(--stagger-ease) both;
  will-change: transform, opacity, filter;
}

.typing__char:nth-child(1) {
  animation-delay: calc(var(--typing-delay) + var(--typing-step));
}

.typing__char:nth-child(2) {
  animation-delay: calc(var(--typing-delay) + var(--typing-step) * 2);
}

@keyframes char-in {
  from {
    opacity: 0;
    transform: translateY(var(--stagger-distance));
    filter: blur(var(--stagger-blur));
  }
  to {
    opacity: 1;
    transform: translateY(0);
    filter: blur(0);
  }
}

/*
 * 输入光标：一条竖线，方波闪烁（不是渐变淡入淡出）。
 *
 * 只在文字浮现期间闪，落定后停住：
 *   周期 = --reveal-end / 2.5，总时长 = 周期 × 2.5 = --reveal-end，
 *   正好在末字清晰的那一刻收尾。
 *   2.5 是「两圈半」，收尾落在周期的 50% 处 ——
 *   而 0% / 50% 这一帧正是 opacity: 1，
 *   配合 forwards 把这一帧 hold 住，光标就停在「常亮」而不是「熄灭」。
 *   ⚠️ 改成整数圈（如 3 圈）会停在 opacity: 0，光标会直接消失。
 */
.typing__caret {
  flex: none;
  width: 2px;
  height: 1em;
  margin-left: .14em;

  background: rgba(170, 214, 255, .95);
  box-shadow: 0 0 8px rgba(124, 196, 255, .85);

  animation: caret-blink calc(var(--reveal-end) / 2.5) step-end 2.5 forwards;
}

@keyframes caret-blink {
  0%, 50%   { opacity: 1; }
  51%, 100% { opacity: 0; }
}

/* ---------- 5. 关于屏：暗角 + 排版 + 入场迟滞 ---------- */

/*
 * 不用卡片。改用一层柔和径向暗角（vignette）压住星云亮度，
 * 保证文字可读，又不出现「一个盒子浮在背景上」的廉价感。
 */
.screen--about::before {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;

  background: radial-gradient(
    ellipse 62% 58% at 50% 50%,
    rgba(2, 4, 12, .84) 0%,
    rgba(2, 4, 12, .60) 45%,
    rgba(2, 4, 12, .22) 68%,
    rgba(2, 4, 12, 0) 82%
  );
}

.about {
  position: relative;
  z-index: 1;

  width: min(680px, 100%);
  color: #fff;
}

/* 眉标：小字号 + 超宽字距 + 全大写，负责「精致」的一半 */
.about__eyebrow {
  margin: 0 0 20px;

  font-family: var(--display-font);
  font-size: 11px;
  font-weight: 500;
  line-height: 1;
  letter-spacing: .34em;
  text-transform: uppercase;

  color: rgba(140, 200, 255, .82);
}

/* 主标题：大字号 + 细字重 + 紧行高，靠字号对比而不是靠装饰 */
.about__title {
  margin: 0 0 20px;

  font-family: var(--display-font);
  font-size: clamp(30px, 5.2vw, 48px);
  font-weight: 200;
  line-height: 1.22;
  letter-spacing: .04em;

  color: #fff;
  text-shadow: 0 2px 28px rgba(0, 0, 0, .55);

  text-wrap: balance;
}

/* 发丝分隔线：从主题色渐隐到透明，比实心短线更轻 */
.about__rule {
  display: block;
  width: 72px;
  height: 1px;
  margin: 0 0 32px;

  background: linear-gradient(
    90deg,
    rgba(124, 196, 255, .95) 0%,
    rgba(177, 140, 255, .45) 55%,
    rgba(177, 140, 255, 0) 100%
  );
}

/* 导语：限宽到约 34em 保证易读，行高放大到 2 */
.about__lead {
  max-width: 34em;
  margin: 0 0 22px;

  font-size: 15px;
  line-height: 2;
  color: rgba(255, 255, 255, .72);

  text-shadow: 0 1px 16px rgba(0, 0, 0, .5);
  text-wrap: pretty;
}

/*
 * 主题段落（意见 / 合作 邀请）。
 * 与导语共用同一套排版，只把字色提亮一档 —— 它是这一屏真正想说的话。
 */
.about__invite {
  max-width: 34em;
  margin: 0 0 44px;

  font-size: 15px;
  line-height: 2;
  color: rgba(255, 255, 255, .80);

  text-shadow: 0 1px 16px rgba(0, 0, 0, .5);
  text-wrap: pretty;
}

/* ----- 联系方式 -----
 * 用一条发丝线与列表分开；链接沿用"细下划线"这一套视觉语言，
 * 和排版其余部分保持同一组词汇。
 */
.about__contact {
  max-width: 34em;
  margin-top: 40px;
  padding-top: 26px;
  border-top: 1px solid rgba(255, 255, 255, .09);
}

.about__contact-text {
  margin: 0 0 14px;

  font-size: 14.5px;
  line-height: 1.95;
  color: rgba(255, 255, 255, .72);

  text-shadow: 0 1px 14px rgba(0, 0, 0, .45);
}

/* 联系方式占位符的样式：换文字即可，不用改 CSS */
.about__contact-link {
  display: inline-block;
  padding-bottom: 2px;

  font-size: 15px;
  letter-spacing: .04em;
  color: rgba(198, 222, 255, .95);

  border-bottom: 1px solid rgba(124, 196, 255, .32);
  transition: color .2s ease, border-bottom-color .2s ease;
}

.about__contact-link:hover,
.about__contact-link:focus-visible {
  color: #fff;
  border-bottom-color: rgba(124, 196, 255, .85);
}

/* ----- 入场迟滞 -----
 * 整块内容不是"啪"地出现，而是进入视口后逐级上浮淡入，
 * 用长时长 + easeOutExpo 曲线做出"拖"的感觉。
 *
 * 全部规则都挂在 .js 下：JS 没跑（被禁用/加载失败）时内容保持可见，
 * 不会出现"文字永远 opacity: 0"的白屏事故。
 */
.js .screen--about .about > * {
  opacity: 0;
  transform: translateY(26px);

  transition:
    opacity 1s cubic-bezier(.16, 1, .3, 1),
    transform 1.2s cubic-bezier(.16, 1, .3, 1);
}

.js .screen--about.is-revealed .about > * {
  opacity: 1;
  transform: none;
}

/*
 * 延迟只写在「显示态」。
 * transition-delay 取自目标状态的规则，所以往回滚时（目标态是没有延迟的
 * 基础态）会立刻隐藏，不会拖泥带水地慢慢淡出。
 *
 * ⚠️ 新增 .about 的直接子元素时，必须在这里补一条延迟，
 *    否则它没有 delay、会抢在眉标之前第一个冒出来。
 */
.js .screen--about.is-revealed .about__eyebrow { transition-delay: .06s; }
.js .screen--about.is-revealed .about__title   { transition-delay: .18s; }
.js .screen--about.is-revealed .about__rule    { transition-delay: .30s; }
.js .screen--about.is-revealed .about__lead    { transition-delay: .42s; }
.js .screen--about.is-revealed .about__invite  { transition-delay: .54s; }
.js .screen--about.is-revealed .about__contact { transition-delay: .66s; }

/* ---------- 6. 滚动提示 ---------- */
.hint {
  position: absolute;
  left: 50%;
  bottom: 28px;
  transform: translateX(-50%);

  color: rgba(255, 255, 255, .35);
  font: 12px/1.5 var(--nav-font);
  letter-spacing: .12em;
  user-select: none;

  animation: hint-float 2.4s ease-in-out infinite;
}

/* 上下浮动，提示可以继续下滑 */
@keyframes hint-float {
  0%, 100% { transform: translate(-50%, 0); }
  50%      { transform: translate(-50%, 6px); }
}

/* ---------- 7. 响应式与无障碍 ---------- */
@media (max-width: 600px) {
  .screen {
    /* 小屏内容可能超过一屏，留出上下呼吸空间 */
    padding: calc(var(--nav-h) + 16px) 18px 40px;
  }

  .screen--home {
    padding-left: 24px;
    /* .screen 的 padding 简写会把 padding-bottom 重置成 40px，这里要补回来 */
    padding-bottom: 96px;
  }

  .about__lead {
    margin-bottom: 18px;
  }

  .about__invite {
    margin-bottom: 34px;
  }
}

/*
 * 减少动态效果：
 * 关掉平滑滚动、打字机、光标闪烁与入场迟滞。
 * 注意吸附本身保留 —— 它是布局行为，不是装饰动画。
 */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .hint {
    animation: none;
  }

  /* 直接显示完整文字，光标常亮 */
  .typing__text {
    width: 2.16em;
    animation: none;
  }

  /* 不做模糊显影，字直接清晰 */
  .typing__char {
    animation: none;
  }

  .typing__caret {
    animation: none;
  }

  /* 发丝线直接到位 */
  .home-hero__rule {
    width: 88px;
    animation: none;
  }

  /* 内容直接就位，不做上浮淡入 */
  .js .screen--about .about > * {
    opacity: 1;
    transform: none;
    transition: none;
  }
}
