/* ==========================================================================
   基础：重置、排版、通用原子
   ========================================================================== */

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

html {
    -webkit-text-size-adjust: 100%;
    scroll-behavior: smooth;
    /* 滚动条永远占着位置。
       弹框会 body{overflow:hidden} 把滚动条去掉，页面一变宽内容就整体横移一下；
       页面从短变长（比如刚打开帖子、切消息中心的页签）也一样会抖。
       让滚动槽常驻，这类瞬移就全没了。 */
    scrollbar-gutter: stable;
    /* 注意：这里不要给 html / body 加 overflow-x。
       hidden 会把根变成滚动容器，纵向滚动直接失效（交接单里踩过的坑）；
       clip 在桌面 Edge 的移动模拟里也会把布局搅出常驻的 22px 偏移（实测过）。
       换页滑动的横向溢出改在 #nz-main 上按次处理，见 .nz-view-fwd 那条。 */
}

body {
    margin: 0;
    min-height: 100vh;
    font-family: var(--nz-font);
    font-size: calc(var(--nz-fs-base) * var(--nz-font-scale));
    line-height: var(--nz-lh);
    color: var(--nz-text);
    background: var(--nz-bg);
    position: relative;
    -webkit-font-smoothing: antialiased;
    /* 注意：这里不要写 overflow-x: hidden。
       它会让 body 变成滚动容器，纵向滚动会莫名其妙失效（已经踩过一次）。
       页面本来就没有横向溢出，防横滑用 overscroll-behavior 就够了。 */
    overscroll-behavior: none;
}

body::before {
    content: '';
    position: fixed;
    inset: 0;
    /* 平涂背景：角落光斑和斜纹贴图都撤了，页面干净了滚动合成也省一层 */
    background: var(--nz-bg);
    z-index: -2;
}

body.is-locked { overflow: hidden; }

h1, h2, h3, h4, h5, h6 {
    margin: 0;
    color: var(--nz-text-strong);
    font-weight: 700;
    line-height: var(--nz-lh-tight);
}

p { margin: 0; }
a { color: var(--nz-accent-deep); text-decoration: none; }
a:hover { color: var(--nz-accent); }
img { max-width: 100%; border: 0; }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { margin: 0; padding: 0; list-style: none; }
hr { border: 0; border-top: 1px solid var(--nz-border); margin: 16px 0; }

:focus-visible {
    outline: 2px solid var(--nz-accent);
    outline-offset: 2px;
    border-radius: 6px;
}

::selection { background: color-mix(in srgb, var(--nz-accent) 32%, transparent); color: var(--nz-text-strong); }

/* 滚动条 */
* { scrollbar-width: thin; scrollbar-color: var(--nz-border-strong) transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-track { background: transparent; }
*::-webkit-scrollbar-thumb {
    background: var(--nz-border-strong);
    border-radius: var(--nz-radius-pill);
    border: 2px solid transparent;
    background-clip: padding-box;
}
*::-webkit-scrollbar-thumb:hover { background: var(--nz-accent); background-clip: padding-box; }

/* ── 布局原子 ─────────────────────────────────── */
.nz-row { display: flex; align-items: center; gap: 8px; }
.nz-row-between { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.nz-col { display: flex; flex-direction: column; gap: 8px; }
.nz-wrap { flex-wrap: wrap; }
.nz-grow { flex: 1 1 auto; min-width: 0; }
.nz-center { display: flex; align-items: center; justify-content: center; }
.nz-hidden { display: none !important; }
.nz-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nz-clamp-1, .nz-clamp-2, .nz-clamp-3 { display: -webkit-box; -webkit-box-orient: vertical; overflow: hidden; }
.nz-clamp-1 { -webkit-line-clamp: 1; line-clamp: 1; }
.nz-clamp-2 { -webkit-line-clamp: 2; line-clamp: 2; }
.nz-clamp-3 { -webkit-line-clamp: 3; line-clamp: 3; }

.nz-muted { color: var(--nz-text-sub); }
.nz-faint { color: var(--nz-text-faint); }
.nz-strong { color: var(--nz-text-strong); font-weight: 700; }
.nz-small { font-size: var(--nz-fs-sm); }
.nz-tiny { font-size: var(--nz-fs-xs); }
.nz-danger-text { color: var(--nz-danger); }
.nz-nowrap { white-space: nowrap; }
.nz-mono { font-family: var(--nz-font-mono); }

/* ── 图标 ─────────────────────────────────────── */
.nz-icon {
    width: 1.15em;
    height: 1.15em;
    flex: 0 0 auto;
    display: inline-block;
    vertical-align: -.18em;
    fill: none;              /* 描边图标默认不填色，选中态改成 fill: currentColor 就变实心 */
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.nz-icon-lg { width: 1.5em; height: 1.5em; }
.nz-icon-xl { width: 2em; height: 2em; }

#nz-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

/* ── 超长文字：横向滑动 + 边缘渐隐 ───────────────
   昵称 / 用户名是用户自己填的，能长到没边。省略号对"又长又没规律"的那种 id 等于看不见，
   外层还是 flex 容器的时候省略号压根不生效（黑名单和个人主页就是这么被顶出屏幕的）。
   这里让容器自己横滑，两边按滚动位置渐隐：右边没到头就渐隐，划到最右就不渐隐，
   往回划左边才渐隐。--nz-fade-l / --nz-fade-r 和 .is-scrollable 由 core/scrollX.js 维护。 */
.nz-scroll-x {
    --nz-fade-l: 0px;
    --nz-fade-r: 0px;
    max-width: 100%;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    white-space: nowrap;
    scrollbar-width: none;
    -ms-overflow-style: none;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
}
.nz-scroll-x::-webkit-scrollbar { display: none; }
.nz-scroll-x.is-scrollable {
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--nz-fade-l), #000 calc(100% - var(--nz-fade-r)), transparent 100%);
    mask-image: linear-gradient(to right, transparent 0, #000 var(--nz-fade-l), #000 calc(100% - var(--nz-fade-r)), transparent 100%);
}

/* ── 骨架屏 ───────────────────────────────────── */
.nz-skeleton {
    position: relative;
    overflow: hidden;
    background: var(--nz-surface-3);
    border-radius: var(--nz-radius-xs);
}
.nz-skeleton::after {
    content: '';
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.55), transparent);
    animation: nz-shimmer 1.4s infinite;
}
[data-color-mode="dark"] .nz-skeleton::after {
    background: linear-gradient(90deg, transparent, rgba(255,255,255,.06), transparent);
}
@keyframes nz-shimmer { 100% { transform: translateX(100%); } }

/* ── 动画 ─────────────────────────────────────── */
@keyframes nz-fade-up {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: none; }
}
@keyframes nz-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes nz-pop {
    from { opacity: 0; transform: translateY(10px) scale(.97); }
    to { opacity: 1; transform: none; }
}
@keyframes nz-slide-up {
    from { transform: translateY(100%); }
    to { transform: none; }
}
@keyframes nz-spin { to { transform: rotate(360deg); } }
@keyframes nz-pulse { 0%,100% { opacity: 1; } 50% { opacity: .45; } }

.nz-anim-in { animation: nz-fade-up var(--nz-dur-slow) var(--nz-ease-out) both; }
.nz-spin { animation: nz-spin .9s linear infinite; }

/* 页面切换的入场。只淡，不带位移：
   位移会把整页内容推一下，手机上跟滚动惯性叠在一起特别晕。
   时长比 nz-anim-in 短，因为它是每次换页都要放一遍的 */
@keyframes nz-view-in {
    from { opacity: 0; }
    to { opacity: 1; }
}
.nz-view-in { animation: nz-view-in var(--nz-dur) var(--nz-ease-out) both; will-change: opacity; }

/* 方向性切页：侧栏 / 底栏换页时内容顺着导航方向滑进来（深入从右、返回从左）。
   shell.js 在每次路由开头定方向，render() 放完一次就清掉，
   所以软刷新和局部换内容不会重放这一下。 */
@keyframes nz-view-fwd {
    from { opacity: 0; transform: translateX(32px); }
    to { opacity: 1; transform: none; }
}
@keyframes nz-view-back {
    from { opacity: 0; transform: translateX(-32px); }
    to { opacity: 1; transform: none; }
}
.nz-view-fwd,
.nz-view-back { will-change: transform, opacity; }
.nz-view-fwd { animation: nz-view-fwd .28s var(--nz-ease-out) both; }
.nz-view-back { animation: nz-view-back .28s var(--nz-ease-out) both; }
/* overflow: clip 只在动画的这 280ms 里存在（shell.js 放完就摘类）。
   关键：这一刀必须挂在父级 .nz-body 上 —— overflow 裁的是子孙，裁不到
   自己；main 自己 translateX 出的界，得让 body 来裁。不出这一刀的话
   布局视口会跟着膨胀（实测 32px 横向溢出 => 视口高 +70px），fixed 的
   底栏被顶到可视屏幕下面，动画放完才复位；桌面端则是横向滚动条闪一下。 */
.nz-body:has(> .nz-view-fwd),
.nz-body:has(> .nz-view-back) { overflow: clip; }

/* 列表项的进场：从上方落定（下沉式），排队间隔由 motion.js 内联写 animation-delay。
   fill-mode 用 backwards：等待排队的那段时间保持第一帧（透明），
   动画放完彻底交还样式，不压住卡片自己的 :active 反馈 */
@keyframes nz-sink-in {
    from { opacity: 0; transform: translateY(-14px) scale(.99); }
    to { opacity: 1; transform: none; }
}
.nz-sink { animation: nz-sink-in .34s var(--nz-ease-out) backwards; will-change: transform, opacity; }

/* ── 卡片 → 详情主卡的接力过渡（逻辑在 core/motion.js）──────────
   View Transitions 可用时走 .nz-vt-handoff：名字配对之后，
   浏览器自己把旧卡快照变形到新页主卡的位置（合成器驱动，MS Store 那种连贯感），
   其余内容交叉淡入。旧卡不用手动渐隐，交叉淡入会处理。 */
.nz-vt-handoff { view-transition-name: nz-handoff; }

::view-transition-group(nz-handoff) {
    animation-duration: .34s;
    animation-timing-function: var(--nz-ease-out);
}
/* 旧卡的内容快点让位，新内容在落位的过程中浮现 —— 容器比内容活得久 */
::view-transition-old(nz-handoff) { animation-duration: .16s; }
::view-transition-new(nz-handoff) { animation-duration: .3s; animation-delay: .06s; animation-fill-mode: backwards; }
/* 其余部分的交叉淡入跟容器一个节奏 */
::view-transition-group(root) {
    animation-duration: .28s;
    animation-timing-function: var(--nz-ease-out);
}

/* WAAPI 兜底路径（不支持 View Transitions 的老内核）：来源卡片内容先淡掉，
   放大落位的是一块底色；落位的是骨架主卡，数据到了真内容原地渐显。 */
.nz-handoff-out > * {
    opacity: 0;
    transition: opacity .16s var(--nz-ease);
}
/* 新来的内容在骨架位置上渐显（详情主卡、个人主页头部） */
.nz-reveal {
    animation: nz-fade .24s var(--nz-ease-out) both;
    will-change: opacity;
}
/* 聊天里新消息的弹入（收 / 发都走它）：从下方浮上来，比列表的"下沉"更轻 */
.nz-msg-in {
    animation: nz-pop var(--nz-dur) var(--nz-ease-out) both;
}

/* 切明暗 / 换皮肤的时候 theme.js 会临时挂上这个类（260ms 后摘掉），
   让页面颜色淡过去，而不是啪一下全换。沙箱里那份（post.css）用的是同一个类名和时长，
   两边看起来就是一起变的。
   !important 必须有：卡片、按钮自己都写了 transition，不加就被各自的覆盖掉，
   结果只有一部分元素在动，比不做还难看。只挂 260ms，不影响平时的悬停过渡。 */
html.nz-theme-switching,
html.nz-theme-switching *,
html.nz-theme-switching *::before,
html.nz-theme-switching *::after {
    transition: background-color .22s var(--nz-ease), background-image .22s var(--nz-ease),
        color .22s var(--nz-ease), border-color .22s var(--nz-ease),
        box-shadow .22s var(--nz-ease), fill .22s var(--nz-ease), stroke .22s var(--nz-ease) !important;
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    /* 上面这条被主题切换那条的优先级压住了，这里再按同样的选择器写一遍压回来 */
    html.nz-theme-switching,
    html.nz-theme-switching *,
    html.nz-theme-switching *::before,
    html.nz-theme-switching *::after {
        transition-duration: .001ms !important;
    }
}
