/* Framed sheet · feedback under the hand. No animation holds content hidden. */
:root {
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);
  --t-press: 140ms;
  --t-panel: 280ms;
  --t-exit: 160ms;
}

/* Entrances use WAAPI after content renders. Without JS, content stays visible. */
.reveal { opacity: 1; transform: none; animation: none; }
/* Badge, chart and cursor animations retain their app.css definitions. */
/* Preserve the signature orbit, online pulse and slow cover drift from app.css. */
.pulse.off::after { display: none; }
.stream { overflow-anchor: none; scrollbar-gutter: stable; scroll-behavior: auto; }
.btn, .icon-btn, .opt, .linkish, .topics button, .nav-item, .trow, .seg button {
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.icon-btn, .opt, .linkish, .nav-item, .trow, .seg button {
  transition: color var(--t-hover) ease, background-color var(--t-hover) ease,
    opacity var(--t-hover) ease, transform var(--t-press) var(--ease-out);
}
.topics button { transition: border-color var(--t-hover) ease, background-color var(--t-hover) ease, transform var(--t-press) var(--ease-out); }
.topics button:hover, .panel-hover:hover { transform: none; }
.btn:active:not(:disabled), .icon-btn:active:not(:disabled), .opt:active:not(:disabled),
.topics button:active:not(:disabled) { transform: scale(.975); }
.trow:active { transform: scale(.995); }
.btn[aria-busy="true"] { cursor: progress; }
.composer:focus-within { border-color: var(--accent); }
.composer { transition: border-color var(--t-hover) ease; }
.seg { position: relative; }
.seg button { position: relative; }
.conversation-mark { display: grid; place-items: center; flex: none; width: 36px; height: 36px; color: var(--accent); }
.conversation-mark > svg { width: 32px; height: 32px; }
.topics button:focus-visible svg { color: var(--accent); transform: translateX(3px); }

/* Keep the original sent/received message entrances; message IDs prevent replay. */
.toast { animation: toast-enter var(--t-ui) var(--ease-out); }
@keyframes toast-enter { from { opacity: 0; transform: translate(-50%, 7px) scale(.985); } to { opacity: 1; transform: translateX(-50%); } }
.menu { animation: menu-enter var(--t-ui) var(--ease-out); transform-origin: left bottom; }
@keyframes menu-enter { from { opacity: 0; transform: translateY(5px) scale(.98); } to { opacity: 1; transform: none; } }
dialog[open], dialog.dlg[open] { animation: dialog-enter var(--t-panel) var(--ease-out); }
dialog::backdrop { animation: backdrop-enter var(--t-ui) ease; }
@keyframes dialog-enter { from { opacity: 0; transform: translateY(10px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes backdrop-enter { from { opacity: 0; } to { opacity: 1; } }

/* Native top-layer/discrete transitions keep Escape, focus restoration and
   modal semantics intact. Older browsers use the entrance above. */
@supports (transition-behavior: allow-discrete) {
  dialog, dialog.dlg {
    opacity: 0;
    transform: translateY(6px) scale(.985);
    transition: opacity var(--t-exit) ease, transform var(--t-exit) var(--ease-out),
      display var(--t-panel) allow-discrete, overlay var(--t-panel) allow-discrete;
  }
  dialog[open], dialog.dlg[open] {
    opacity: 1; transform: none; animation: none;
    transition-duration: var(--t-panel);
  }
  dialog::backdrop {
    opacity: 0; animation: none;
    transition: opacity var(--t-ui) ease, display var(--t-panel) allow-discrete, overlay var(--t-panel) allow-discrete;
  }
  dialog[open]::backdrop { opacity: 1; }
  @starting-style {
    dialog[open], dialog.dlg[open] { opacity: 0; transform: translateY(10px) scale(.98); }
    dialog[open]::backdrop { opacity: 0; }
  }
}

/* Details and mobile navigation open as layers, never by animating grid widths. */
.detail-close { display: none; }
@media (max-width: 1180px) {
  .detail-close { display: inline-grid; position: absolute; top: 10px; right: 10px; z-index: 1; }
  .details > section:first-of-type > h3 { padding-right: 36px; }
  .inbox:not(.no-details) .details { animation: details-enter var(--t-panel) var(--ease-out); }
  @keyframes details-enter { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
  @supports (transition-behavior: allow-discrete) {
    .details { transition: opacity var(--t-exit) ease, transform var(--t-exit) var(--ease-out), display var(--t-panel) allow-discrete; }
    .inbox.no-details .details { opacity: 0; transform: translateX(14px); }
    .inbox:not(.no-details) .details { opacity: 1; transform: none; animation: none; transition-duration: var(--t-panel); }
    @starting-style { .inbox:not(.no-details) .details { opacity: 0; transform: translateX(14px); } }
  }
}
@media (max-width: 860px) {
  .a-side { visibility: hidden; pointer-events: none; transition: transform var(--t-panel) var(--ease-out), visibility 0s var(--t-panel); }
  .a-side.open { visibility: visible; pointer-events: auto; transition-delay: 0s; }
  .btn, .icon-btn, .opt { min-height: 40px; }
}

@media (hover: hover) and (prefers-reduced-motion: no-preference) {
  .topics button:hover, .panel-hover:hover { transform: translateY(-2px); }
}
@media (hover: none) {
  .topics button:hover { background: var(--surface); border-color: var(--border); transform: none; }
  .topics button:hover svg { color: var(--faint); transform: none; }
  .btn.primary:hover svg { transform: none; }
}
.motion-paused *, .motion-paused *::before, .motion-paused *::after { animation-play-state: paused !important; }
dialog.dlg-fallback[open] { transform: translate(-50%, -50%); animation: fallback-dialog-enter var(--t-panel) var(--ease-out); }
@keyframes fallback-dialog-enter { from { opacity: 0; transform: translate(-50%, calc(-50% + 10px)) scale(.98); } to { opacity: 1; transform: translate(-50%, -50%); } }

/* The lake is a postcard; the existing photo drift stays on the inner image. */
.cover { transition: transform 500ms var(--ease-out); }
.cover.lake-tilt { transform: perspective(850px) rotateX(var(--lake-rx, 0deg)) rotateY(var(--lake-ry, 0deg)); }
.topics button:hover svg, .topics button:focus-visible svg { transform: translateX(4px); color: var(--accent); }

/* A line is drawn where the visitor starts writing; a packet scans the line. */
.composer::before, .composer::after { content: ''; position: absolute; z-index: 1; pointer-events: none; background: var(--accent); }
.composer::before { left: 16px; right: 16px; top: -1px; height: 2px; transform: scaleX(0); transform-origin: left; opacity: 0; transition: transform 420ms var(--ease-out), opacity 180ms ease; }
.composer::after { left: 16px; top: -2px; width: 5px; height: 4px; border-radius: 2px; opacity: 0; }
.composer:focus-within::before { transform: scaleX(1); opacity: .65; }
.composer:focus-within::after { animation: data-scan 1900ms linear infinite; }
@keyframes data-scan { 0% { transform: translateX(0); opacity: 0; } 12%, 80% { opacity: 1; } 100% { transform: translateX(var(--ink-run, 160px)); opacity: 0; } }

/* The folded-note mark draws in like a small circuit booting up. */
.conversation-mark > svg path { stroke-dasharray: 180; stroke-dashoffset: 0; animation: note-trace 1300ms var(--ease-out) both; }
.conversation-mark > svg path:nth-child(2) { animation-delay: 100ms; }
.conversation-mark > svg path:nth-child(3) { animation-delay: 200ms; }
@keyframes note-trace { from { stroke-dashoffset: 180; opacity: .3; } to { stroke-dashoffset: 0; opacity: 1; } }
.data-packet { position: fixed; z-index: 69; width: 4px; height: 4px; border-radius: 50%; background: var(--accent); pointer-events: none; }

/* Each delivery mode keeps its own gesture instead of one universal bounce. */
.opt[data-act="burn"][aria-pressed="true"] svg { animation: flame-wake 440ms var(--ease-out); transform-origin: 50% 100%; }
.opt[data-act="schedule"][aria-pressed="true"] svg { animation: clock-wind 480ms var(--ease-out); }
.opt[data-act="drift"][aria-pressed="true"] svg { animation: bottle-launch 600ms var(--ease-out); }
@keyframes flame-wake { from { transform: scaleY(.65) scaleX(.9); opacity: .45; } to { transform: none; opacity: 1; } }
@keyframes clock-wind { from { transform: rotate(-65deg); } to { transform: rotate(0); } }
@keyframes bottle-launch { 0% { transform: translateY(2px) rotate(-12deg); } 55% { transform: translateY(-2px) rotate(8deg); } 100% { transform: none; } }
[data-act="send"]:not(:disabled):hover svg { transform: translate(2px, -1px) rotate(-8deg); }

/* 页面之间切换：旧页面轻轻退远、淡掉，新页面从下面浮上来；左上角的“个人动态”留在原地不闪。
   只有支持跨页过渡的浏览器才有，其它浏览器照常直接打开。 */
@view-transition { navigation: auto; }
.h-top .brand { view-transition-name: brand; }
::view-transition-old(root) { animation: vt-out 240ms cubic-bezier(.4, 0, 1, 1) both; }
::view-transition-new(root) { animation: vt-in 560ms cubic-bezier(.2, .7, 0, 1) both; }
@keyframes vt-out { to { opacity: 0; scale: .985; } }
@keyframes vt-in { from { opacity: 0; translate: 0 22px; } }
/* Day and night spread from the visible theme control, with a plain fallback. */
:root.theme-vt::view-transition-old(root) { animation: none; }
:root.theme-vt::view-transition-new(root) { animation: theme-unfold 580ms var(--ease-out); mix-blend-mode: normal; }
/* 支持的浏览器里改成羽化的边：像天色从按钮那儿慢慢晕开，而不是一刀切的圆 */
@property --theme-r { syntax: '<length>'; inherits: false; initial-value: 0px; }
@supports (mask-image: radial-gradient(circle, #000, transparent)) {
  :root.theme-vt::view-transition-new(root) {
    animation: theme-dusk 820ms cubic-bezier(.5, 0, .2, 1) both;
    -webkit-mask-image: radial-gradient(circle at var(--theme-x, 50%) var(--theme-y, 24px), #000 calc(var(--theme-r) - 140px), transparent var(--theme-r));
    mask-image: radial-gradient(circle at var(--theme-x, 50%) var(--theme-y, 24px), #000 calc(var(--theme-r) - 140px), transparent var(--theme-r));
  }
  @keyframes theme-dusk { from { --theme-r: 0px; } to { --theme-r: calc(150vmax + 140px); } }
}

/* 页面顶上一条细细的湖蓝线，跟着往下滚的进度变长（浏览器自己算，不占脚本） */
@supports (animation-timeline: scroll()) {
  body.home::after, body.page::after, body.fp-page::after { content: ""; position: fixed; left: 0; right: 0; top: 0; z-index: 60; height: 2px; background: var(--accent); transform-origin: 0 50%; scale: 0 1; pointer-events: none; animation: read-progress linear both; animation-timeline: scroll(root); }
  @keyframes read-progress { to { scale: 1 1; } }
}

/* 按下主按钮时，从手指的位置荡开一圈墨水 */
.go, button.btn.primary { position: relative; overflow: hidden; isolation: isolate; }
.ink { position: absolute; z-index: -1; border-radius: 50%; background: currentColor; pointer-events: none; }

/* 滚动的数字：每一位是一条 0–9 的竖带，像机械计数器一样转到位 */
.odo { display: inline-flex; }
.odo-d { display: inline-block; height: 1em; overflow: hidden; line-height: 1; }
.odo-s { display: flex; flex-direction: column; transition: transform var(--odo-ms, 1600ms) cubic-bezier(.16, .84, .24, 1) var(--odo-delay, 0ms); }
.odo-s > span { height: 1em; line-height: 1; }
@keyframes theme-unfold { from { clip-path: circle(0 at var(--theme-x, 50%) var(--theme-y, 24px)); } to { clip-path: circle(150vmax at var(--theme-x, 50%) var(--theme-y, 24px)); } }

/* ================= 细节特效（全站） ================= */
/* 鼠标移到主按钮上：一道光从左往右扫过 */
.go:not(.go-cv)::after, button.btn.primary::after { content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none; background: linear-gradient(105deg, transparent 35%, rgba(255, 255, 255, .26) 50%, transparent 65%) 130% 0 / 250% 100% no-repeat; }
@media (hover: hover) { .go:not(.go-cv):hover::after, button.btn.primary:not(:disabled):hover::after { background-position: -30% 0; transition: background-position 900ms cubic-bezier(.16, .84, .24, 1); } }
/* 背景里慢慢漂的两团湖蓝光（页面加 <div class="aura">） */
.aura { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }
.aura i { position: absolute; width: 56vmax; height: 56vmax; border-radius: 50%; filter: blur(70px); opacity: .5; will-change: transform; }
.aura i:nth-child(1) { left: -14vmax; top: -18vmax; background: radial-gradient(circle, color-mix(in srgb, var(--accent) 28%, transparent), transparent 62%); animation: aura-a 26s ease-in-out infinite alternate; }
.aura i:nth-child(2) { right: -18vmax; top: 30vh; background: radial-gradient(circle, color-mix(in srgb, var(--accent) 20%, transparent), transparent 62%); animation: aura-b 32s ease-in-out infinite alternate; }
@keyframes aura-a { to { transform: translate3d(18vmax, 14vmax, 0) scale(1.15); } }
@keyframes aura-b { to { transform: translate3d(-16vmax, -10vmax, 0) scale(.9); } }
/* 发送时从按钮上迸出的小光点 */
.spark-fly { position: fixed; z-index: 76; width: 6px; height: 6px; margin: -3px 0 0 -3px; border-radius: 50%; background: var(--accent); pointer-events: none; }
/* 输入框里写了字：发送按钮外面一圈光轻轻呼吸，提示可以发了 */
.composer:has(textarea:not(:placeholder-shown)) [data-act="send"]:not(:disabled) { animation: send-ready 2.4s ease-in-out infinite; }
@keyframes send-ready { 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 0%, transparent); } 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 22%, transparent); } }
/* 正文里的链接：下划线从左往右长出来 */
.foot-contact a, .r-basic a, .say a { text-decoration: none; background: linear-gradient(currentColor, currentColor) 0 100% / 100% 1px no-repeat; transition: background-size 500ms cubic-bezier(.16, .84, .24, 1); }
@media (hover: hover) { .foot-contact a:hover, .r-basic a:hover, .say a:hover { background-size: 0 1px; background-position: 100% 100%; } }

/* ===== fx.js 的样式 ===== */
.fx-ch { display: inline-block; transition: opacity 900ms ease, translate 1200ms cubic-bezier(.16, .84, .24, 1), filter 1000ms ease; transition-delay: calc(var(--c) * 45ms); }
.fx-wait .fx-ch { opacity: 0; translate: 0 .45em; filter: blur(10px); }
.fx-spot { position: relative; isolation: isolate; }
.fx-spot::after { content: ""; position: absolute; inset: 0; z-index: -1; border-radius: inherit; pointer-events: none; opacity: 0; transition: opacity 500ms ease;
  background: radial-gradient(420px circle at var(--mx, 50%) var(--my, 50%), color-mix(in srgb, var(--accent) 13%, transparent), transparent 65%); }
.fx-spot:hover::after { opacity: 1; }
.fx-mag { transition: translate 500ms cubic-bezier(.16, .84, .24, 1), background-color var(--t-hover) ease, color var(--t-hover) ease; }
.fx-tilt { position: relative; transform: perspective(1000px) rotateX(var(--fx-rx, 0deg)) rotateY(var(--fx-ry, 0deg)); transition: transform 700ms cubic-bezier(.16, .84, .24, 1); }
.fx-tilt::after { content: ""; position: absolute; inset: 0; border-radius: inherit; pointer-events: none; opacity: 0; transition: opacity 400ms ease; background: radial-gradient(circle at var(--fx-gx, 50%) var(--fx-gy, 30%), rgba(255, 255, 255, .42), transparent 50%); }
.fx-tilt:hover::after { opacity: 1; }

/* Final rule deliberately follows every animation, including page-local CSS.
   The former .reveal opacity:0 must never survive in this mode. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; scroll-behavior: auto !important; }
  .reveal { opacity: 1 !important; transform: none !important; }
  .cover, .cover img, .topics button:hover, .btn:active, .icon-btn:active, .opt:active, .trow:active { transform: none !important; }
  .composer::before, .composer::after { display: none; }
  ::view-transition-old(root), ::view-transition-new(root), :root.theme-vt::view-transition-new(root) { animation: none; }
  .ink, .spark-fly, .aura { display: none; }
  .fx-wait .fx-ch { opacity: 1; translate: none; filter: none; } .fx-tilt { transform: none; }
  .typing i { opacity: .65; }
}
@media (prefers-reduced-motion: reduce) { @view-transition { navigation: none; } }
