/* .btn — M3 button skeleton */
.btn {
  display: inline-block;
  padding: 12px 24px;
  font-family: var(--font-utility);
  font-size: var(--fs-utility);
  font-weight: 500;
  letter-spacing: 0.96px;
  text-transform: uppercase;
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  transition:
    color var(--dur-micro) var(--ease),
    background-color var(--dur-micro) var(--ease),
    transform var(--dur-micro) var(--ease);
}
.btn:hover { color: var(--fg); }
.btn:focus { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn:active { transform: translateY(-1px); }
.btn:disabled { color: rgba(224, 209, 182, 0.4); cursor: not-allowed; }

.btn--primary { background: var(--fg); color: var(--bg); }
.btn--primary:hover { background: var(--bg); color: var(--fg); }

.btn--ghost { background: transparent; color: var(--fg); }
.btn--ghost:hover { color: var(--accent); }

/* .card — image-first editorial card */
.card {
  background: var(--color-surface-variant);
  border: none;
  border-radius: var(--shape-medium);
  padding: var(--fs-body);
  transition: transform var(--dur-micro) var(--ease);
}
.card:hover { transform: translateY(-2px); }

/* .input — text input */
.input {
  display: block;
  width: 100%;
  height: 40px;
  padding: 0 12px;
  font-family: var(--font-body);
  font-size: var(--fs-body);
  color: var(--fg);
  background: transparent;
  border: 1px solid var(--color-on-surface-variant);
  border-radius: var(--shape-small);
  transition: border-color var(--dur-micro) var(--ease), outline var(--dur-micro) var(--ease);
}
.input:focus {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-color: var(--color-primary);
}
.input[aria-invalid="true"] { border-color: var(--accent-7); }

/* .nav — sticky top nav (2026-10-16 升级: 方案 A · 极薄线 · Linear 风)
 * v2 调整: logo 4px wine 竖条 → 1/4 圆弧 1px stroke (细, 顶部左上角);
 *          nav 底边 70% 白太扎眼 → 顶部 transparent 占位, scrolled 后
 *          fade in 8% cream (var(--color-surface-variant), bg-relative subtle) */
.nav {
  position: sticky;
  top: 0;
  height: 60px;
  display: flex;
  align-items: center;
  gap: 24px;
  padding: 0 40px;
  background: transparent;
  z-index: 100;
  border-bottom: 1px solid transparent;            /* 占位, 避免 scroll 跳高度 */
  transition: background-color var(--dur-micro) var(--ease),
              border-bottom-color var(--dur-micro) var(--ease);
}
.nav.scrolled,
body.nav--scrolled .nav {                              /* 跟 me.js / me-edit.js 的 body class 模式统一, JS 加在 body */
  background-color: var(--bg);
  border-bottom-color: var(--color-surface-variant);  /* 8% cream, 跟 bg 自身的 5-8% lighter */
}

.nav__logo {
  position: relative;
  font-family: var(--font-display);
  font-size: 24px;
  letter-spacing: -0.6px;
  color: var(--fg);
}
.nav__logo::before {
  content: '';
  position: absolute;
  top: -2px;                           /* 弧线在 "资" 字左上方略突出, 不卡字顶 */
  left: -14px;
  width: 12px;                         /* v1 = 8x8 → stroke 0.6px 在 forest 上接近隐形;
                                          12x12 + viewBox 12 + stroke 1.5 → 实际 stroke 1.5px 清晰 */
  height: 12px;
  background-color: var(--accent-wine);  /* mask 控形状, bg 控色, 不硬编码 hex */
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M 2 10 A 8 8 0 0 1 10 2'/%3E%3C/svg%3E") no-repeat center / contain;
          mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M 2 10 A 8 8 0 0 1 10 2'/%3E%3C/svg%3E") no-repeat center / contain;
}

.nav__links { display: flex; gap: 24px; margin: 0 auto; }
.nav__links a {
  position: relative;
  display: flex;
  align-items: center;
  height: 100%;                   /* = nav 60px, 让 active underline 跟底边对齐 */
  font-family: var(--font-utility);
  font-size: var(--fs-utility);
  font-weight: 500;
  color: var(--fg);
  /* 默认 transition (回弹/leave): opacity 慢 400ms;
   * hover 状态 transition (进) 临时覆盖为 200ms, 形成"快进慢出"微回弹感 */
  transition: color var(--dur-micro) var(--ease),
              opacity 400ms var(--ease);
}
.nav__links a:hover {
  color: var(--accent);
  opacity: 0.8;
  transition: color var(--dur-micro) var(--ease),
              opacity 200ms var(--ease);
}

/* active: 当前页 link 1px accent underline, 0.3s 从左 grow。
 * 用 data-nav-current attribute + .nav.is-ready class 触发,避免首次渲染时
 * scaleX(1) 跟默认 scaleX(0) 之间的差值太小而不触发动画。 */
.nav__links a[data-nav-current] { color: var(--accent); }
.nav__links a[data-nav-current]::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -1px;                    /* 紧贴 nav 底边 1px, 视觉重叠 */
  height: 1px;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 300ms var(--ease);
}
.nav.is-ready .nav__links a[data-nav-current]::after {
  transform: scaleX(1);
}
.nav__user {
  display: flex;
  gap: 16px;
  align-items: center;
  /* 2026-10-02 — 消除 nav FOUC:
   * nav.js 同步快速路径填 USER_SKELETON / GUEST_TEMPLATE 后异步升级 USER_TEMPLATE,
   * 期间 slot 内容会替换(name placeholder → 真名 + 可能 ADMIN link)。
   * min-width / min-height hold 住布局,JS 替换内容时 nav bar 不"跳"。
   * 没 token 时 slot 内是登录/注册按钮(~140px 宽),有 token 时是 avatar + 登出 (~140px),
   * 240px 容纳 ADMIN link 追加。 */
  min-width: 240px;
  min-height: 32px;
  justify-content: flex-end;
}

/* Nav auth buttons — explicit colors so they're visible regardless of theme
 * `.btn--primary` + `.btn--ghost` are theme-driven, but the .nav slot sits
 * above the page on `theme-forest` background and must stay readable
 * (cream text on forest for ghost, forest text on cream for primary). */
.nav .nav__login {
  color: var(--fg) !important;
}
.nav .nav__login:hover { color: var(--accent) !important; }
.nav .nav__register {
  background: var(--accent) !important;
  color: var(--bg) !important;
  border: 1px solid var(--accent) !important;
}
.nav .nav__register:hover {
  background: var(--fg) !important;
  border-color: var(--fg) !important;
}
.nav .nav__logout {
  color: var(--fg) !important;
}
.nav .nav__logout:hover { color: var(--accent) !important; }
/* .nav__admin (JS 渲染) — 跟 .nav__links a 视觉对齐
 * 全局 `a { color: var(--accent) }` 默认是 beige(用户视觉上"红"),
 * .nav__links a 强制 var(--fg) cream。统一到这里 — 都是 cream。
 * 2026-10-09 修: 名册/我的 页面"管理"看起来是红色, 改为奶白。 */
.nav__admin {
  color: var(--fg);
  transition: color var(--dur-micro) var(--ease);
}
.nav__admin:hover {
  color: var(--accent);
}
.nav__avatar {
  font-family: var(--font-utility);
  font-size: var(--fs-utility);
  text-transform: uppercase;
  color: var(--fg);
  transition: color var(--dur-micro) var(--ease);
}
.nav__avatar:hover {
  color: var(--accent);
}
.nav__avatar--loading {
  /* 骨架态:低透明度 + 提示加载中,fetch /me 完成后被替换成完整名字 */
  opacity: 0.4;
  font-style: italic;
}

/* ===== Mobile hamburger (2026-10-17 mobile pass) =====
 * Desktop: 隐藏 toggle, nav__links 保持原 inline 布局。
 * ≤768px: toggle 显示, nav__links 折叠为抽屉, 点 toggle 切 is-open。
 * 抽屉 max-height 0 → 70vh 用 transition 0.3s, 无 JS 依赖展开状态。
 */
.nav__menu-toggle {
  display: none;
  width: 40px;
  height: 40px;
  background: transparent;
  border: 1px solid var(--color-surface-variant);
  cursor: pointer;
  position: relative;
  margin-left: auto;
  padding: 0;
  transition: border-color var(--dur-micro) var(--ease);
}
.nav__menu-toggle:hover { border-color: var(--accent); }
.nav__menu-toggle::before,
.nav__menu-toggle::after {
  content: '';
  position: absolute;
  left: 10px;
  width: 18px;
  height: 1.5px;
  background: var(--fg);
  transition: transform 0.3s var(--ease), top 0.3s var(--ease);
}
.nav__menu-toggle::before { top: 16px; box-shadow: 0 5px 0 var(--fg); }
.nav__menu-toggle::after { top: 22px; }
.nav.is-open .nav__menu-toggle::before {
  top: 19.5px;
  transform: rotate(45deg);
  box-shadow: none;
}
.nav.is-open .nav__menu-toggle::after {
  top: 19.5px;
  transform: rotate(-45deg);
}

@media (max-width: 768px) {
  .nav {
    height: 56px;
    padding: 0 16px;
    gap: 12px;
  }
  .nav__logo {
    font-size: 18px;
  }
  .nav__logo::before {
    left: -10px;
    width: 8px;
    height: 8px;
  }
  .nav__links {
    position: fixed;
    top: 56px;
    left: 0;
    right: 0;
    flex-direction: column;
    gap: 0;
    margin: 0;
    background: var(--bg);
    border-bottom: 1px solid var(--color-surface-variant);
    padding: 0;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s var(--ease);
    z-index: 99;
  }
  .nav.is-open .nav__links {
    max-height: 70vh;
    overflow-y: auto;
  }
  .nav__links a {
    height: 48px;
    padding: 0 24px;
    border-bottom: 1px solid var(--color-surface-variant);
    text-align: left;
  }
  .nav__user {
    min-width: 0;
    gap: 8px;
  }
  .nav__user .nav__avatar,
  .nav__user .nav__login,
  .nav__user .nav__register,
  .nav__user .nav__logout,
  .nav__user .nav__admin {
    font-size: 13px;
  }
  .nav__menu-toggle { display: block; }
}

/* .section-header — eyebrow + h2 */
.section-header { padding: 80px 0 40px; }
.section-header__eyebrow {
  font-family: var(--font-utility);
  font-size: var(--fs-utility);
  font-weight: 500;
  letter-spacing: 0.96px;
  text-transform: uppercase;
  color: var(--color-on-surface-variant);
  margin: 0 0 16px;
}

/* .footer */
.footer {
  padding: 40px;
  text-align: center;
  font-size: var(--fs-small);
  color: var(--color-on-surface-variant);
}
.footer a { color: var(--fg); margin: 0 8px; }

/* List states */
[data-loading] {
  position: relative;
  min-height: 80px;
}
[data-loading]::before {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--color-surface-variant) 50%,
    transparent 100%
  );
  background-size: 200% 100%;
  animation: none;
  opacity: 0.6;
  transition: opacity var(--dur-micro) var(--ease);
}
.list-empty {
  padding: 40px 0;
  text-align: center;
  color: var(--color-on-surface-variant);
  font-size: var(--fs-small);
}
.list-error {
  padding: 16px;
  border-radius: var(--shape-small);
  color: var(--fg);
  font-size: var(--fs-small);
}

/* Banner (notification) */
.banner {
  display: block;
  padding: 12px 24px;
  text-align: center;
  font-family: var(--font-utility);
  font-size: var(--fs-utility);
  text-transform: uppercase;
  letter-spacing: 0.96px;
  transition: transform var(--dur-micro) var(--ease);
}
.banner:hover { transform: translateY(-1px); }

/* Toast stack */
.toast-stack {
  position: fixed;
  right: 24px;
  bottom: 24px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  z-index: 1000;
}
.toast {
  padding: 12px 20px;
  border-radius: var(--shape-small);
  font-size: var(--fs-small);
  display: flex;
  gap: 8px;
  align-items: center;
  min-width: 240px;
  transition: opacity var(--dur-micro) var(--ease);
}
.toast__icon { font-weight: 700; }

/* Error page */
.error-page {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 80px 40px;
}
.error-page__inner {
  padding: 80px 60px;
  text-align: center;
  max-width: 600px;
}
.error-page__code {
  font-family: var(--font-display);
  font-size: 96px;
  font-weight: 400;
  letter-spacing: -2.4px;
  margin: 0 0 24px;
}
.error-page__msg {
  font-size: var(--fs-h3);
  color: var(--fg);
  margin: 0 0 32px;
}

/* ===== CX2 — media-wash (sleep-well editorial constant 10% black overlay) =====
 * Apply .media-wash to any position: relative wrapper holding an <img>/<video>/map.
 * The ::after pseudo paints a constant 10% black wash — keeps overlay text legible
 * without obscuring the image. Static (no fade) per sleep-well DNA.
 * C13: border-radius: 0; C11: no @keyframes.
 */
.media-wash {
  position: relative;
  isolation: isolate;
}
.media-wash::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.1);  /* 10% black — sleep-well constant */
  pointer-events: none;
  z-index: 1;
}
.media-wash > * {
  position: relative;
  z-index: 0;
}
.media-wash .media-wash__caption {
  position: absolute;
  z-index: 2;
}

/* ===== CX4 — dot-flash (slow cosine alpha pulse for status indicators) =====
 * 2s period, cosine ease — pure opacity pulse from 1 → 0.35 → 1.
 * Driven by JS (CX4 init) via requestAnimationFrame — C5/C11 forbids @keyframes.
 * Use as inline span: <span class="dot-flash">●</span>
 * Color inherits from currentColor; size inherits from font-size.
 */
.dot-flash {
  display: inline-block;
  font-size: 0.7em;
  line-height: 1;
  color: var(--accent-wine);
  transform-origin: center;
  will-change: opacity;
}
@media (prefers-reduced-motion: reduce) {
  .dot-flash { opacity: 1 !important; }
}

/* ===== CX3 — dot-loader (4 staggered dots replacing spinner) =====
 * Initial state: opacity 1 (motion.js pulses via rAF cosine).
 * Wrap loader content with: <span class="dot-loader"><span class="dot-loader__dot"></span>×4</span>
 * Used inline next to text — e.g. loading states, "评论加载中…" hovers.
 */
.dot-loader {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-family: var(--font-utility);
  font-size: var(--fs-utility);
  letter-spacing: 0.96px;
  text-transform: uppercase;
  color: var(--color-on-surface-variant);
}
.dot-loader__dot {
  display: inline-block;
  width: 4px;
  height: 4px;
  background: currentColor;
  border-radius: 0;       /* C13 */
  will-change: opacity;
}
@media (prefers-reduced-motion: reduce) {
  .dot-loader__dot { opacity: 1 !important; }
}

/* ===== CX8 — text-with-dot (trailing dot decoration) =====
 * Appends a subtle decorative dot after the text content.
 * Sleep-well uses these dots as a typographic period, often in taglines.
 * Uses ::after — content inheritable from CSS only.
 */
.text-with-dot {
  position: relative;
  display: inline-block;
  padding-right: 14px;
}
.text-with-dot::after {
  content: "";
  position: absolute;
  right: 0;
  top: 50%;
  width: 4px;
  height: 4px;
  background: currentColor;
  border-radius: 0;       /* C13 */
  transform: translateY(-50%);
  opacity: 0.8;
}

/* ===== SX3 — btn-circular (audio/play C13 semantic exception) =====
 * 50% radius is the C13 carve-out for round audio/play affordances.
 * Use ONLY for play/pause/audio semantic (e.g., "听这家店" play button).
 * NEVER apply to social/like buttons, card icons, navigation — those stay square.
 * Default size 56px (touch-friendly).
 */
.btn-circular {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  padding: 0;
  background: transparent;
  color: var(--fg);
  border: 1px solid var(--color-on-surface-variant);
  border-radius: 50%;   /* C13 exception — round audio/play affordance */
  cursor: pointer;
  font-family: var(--font-utility);
  font-size: var(--fs-body);
  transition:
    color var(--dur-micro) var(--ease),
    border-color var(--dur-micro) var(--ease),
    background-color var(--dur-micro) var(--ease),
    transform var(--dur-micro) var(--ease);
}
.btn-circular:hover {
  color: var(--accent-wine);
  border-color: var(--accent-wine);
  transform: translateY(-1px);
}
.btn-circular:focus-visible {
  outline: 2px solid var(--accent-wine);
  outline-offset: 2px;
}
.btn-circular__icon {
  font-size: 20px;
  line-height: 1;
}

/* ===== SX1 + CX1 + SX7 — chapter sidebar (sticky serif digits) =====
 * Sleep-well pattern: left-edge sticky vertical column with "01" "02" "03"
 * rendered as large serif digits. SX7: scroll-driven active marker.
 * CX1: lockstep color flip — active marker takes cream (--accent), inactive stays muted.
 * Use:
 *   <aside class="chapter-sidebar">
 *     <ol class="chapter-sidebar__list">
 *       <li class="chapter-sidebar__item is-active">01</li>
 *       <li class="chapter-sidebar__item">02</li>
 *       <li class="chapter-sidebar__item">03</li>
 *     </ol>
 *   </aside>
 * Wrap each "chapter" section with [data-chapter] and motion.js handles SX7.
 * C13: border-radius: 0. No @keyframes.
 */
.chapter-sidebar {
  position: fixed;
  top: 50%;
  left: 24px;
  transform: translateY(-50%);
  padding: 16px 0;
  pointer-events: none;
  z-index: 5;
}
.chapter-sidebar__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 12px;
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 14px;
  line-height: 1;
  letter-spacing: -0.02em;
}
.chapter-sidebar__item {
  display: block;
  color: var(--color-on-surface-variant);
  opacity: 0.4;
  transition:
    color var(--dur-micro) var(--ease),
    opacity var(--dur-micro) var(--ease),
    transform var(--dur-micro) var(--ease);
  pointer-events: auto;
}
.chapter-sidebar__item.is-active {
  color: var(--accent);     /* CX1 lockstep — active takes cream */
  opacity: 1;
  transform: scale(1.15);
  transform-origin: left center;
}
@media (prefers-reduced-motion: reduce) {
  .chapter-sidebar__item { transition: none; }
  .chapter-sidebar__item.is-active { transform: none; }
}
