/* ==========================================================================
   layout.css — 背景、导航、区块骨架、页脚
   ========================================================================== */

/* --------------------------------------------------------------------------
   背景
   深色打底 + 两团极淡的缓慢流动渐变 + 一层噪点。
   全部纯 CSS / 内联 SVG，零外部资源，零加载负担。
   -------------------------------------------------------------------------- */
.site-bg {
    position: fixed;
    inset: 0;
    z-index: var(--z-bg);
    pointer-events: none;
    background: var(--bg);
    overflow: hidden;
}

.site-bg::before {
    content: "";
    position: absolute;
    inset: -30%;
    background:
        radial-gradient(
            42% 42% at 78% 12%,
            oklch(54.5% 0.203 var(--base-hue) / 0.16) 0%,
            transparent 68%
        ),
        radial-gradient(
            38% 38% at 12% 82%,
            oklch(54.5% 0.203 var(--base-hue) / 0.09) 0%,
            transparent 70%
        );
    /* 90s 一轮。慢到几乎察觉不到在动，但页面不会是死的 */
    animation: bg-drift 90s var(--ease) infinite alternate;
    will-change: transform;
}

@keyframes bg-drift {
    0% {
        transform: translate3d(0, 0, 0) scale(1);
    }
    100% {
        transform: translate3d(-4%, 3%, 0) scale(1.12);
    }
}

/* 蓝图网格 + 噪点
   三层背景叠在一起：粗网格（120px）、点阵（24px）、噪点。
   网格给纯色深底一点"工程图纸"的质感（参考 maximeheckel 的首屏），
   噪点消除大面积渐变的色带（banding）。全部压得极淡 —— 是质感，不是图案。 */
.site-bg::after {
    content: "";
    position: absolute;
    inset: 0;
    opacity: 0.5;
    background-image:
        /* 粗网格 */
        repeating-linear-gradient(
            to right,
            oklch(97.5% 0.002 var(--base-hue) / 0.045) 0 1px,
            transparent 1px 120px
        ),
        repeating-linear-gradient(
            to bottom,
            oklch(97.5% 0.002 var(--base-hue) / 0.045) 0 1px,
            transparent 1px 120px
        ),
        /* 点阵 */
        radial-gradient(
            oklch(97.5% 0.002 var(--base-hue) / 0.05) 0.5px,
            transparent 0.5px
        ),
        /* 噪点 */
        url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)' opacity='0.06'/%3E%3C/svg%3E");
    background-size: 120px 120px, 120px 120px, 24px 24px, 180px 180px;
}

/* 光标聚光灯
   跟随指针的一团柔光，把深色背景局部照亮。
   位置由 site.js 写进 --mx / --my（rAF 里做插值，所以移动是滑的而不是跳的）。 */
.site-bg__spot {
    position: absolute;
    inset: 0;
    opacity: 0;
    transition: opacity 0.6s var(--ease);
    background: radial-gradient(
        circle 420px at var(--mx, 50%) var(--my, 50%),
        oklch(54.5% 0.203 var(--base-hue) / 0.13) 0%,
        transparent 70%
    );
}

/* 只在真正有指针的设备上显示 —— 触屏没有"光标"，这层只会白占性能 */
@media (hover: hover) and (pointer: fine) {
    .site-bg__spot {
        opacity: 1;
    }
}

/* --------------------------------------------------------------------------
   容器
   -------------------------------------------------------------------------- */
.container {
    width: 100%;
    max-width: var(--container-max);
    margin-inline: auto;
    padding-inline: var(--container-padding);
}

main {
    position: relative;
    z-index: var(--z-content);
}

/* --------------------------------------------------------------------------
   揭幕时，内容从下方升上来

   加载期间主页整体下移一段；幕布开始上移的同一刻，这个位移归零，
   于是"幕布向上退出"和"页面从下方升起"是同一段动作 —— 页面像是被
   幕布带着从底下抽出来的，而不是幕布后面本来就有静态的一页。

   transform 只在 is-loading 期间存在，揭幕结束后回到 none。
   这点很关键：元素只要带 transform（哪怕是 translateY(0)）就会成为
   固定定位后代的包含块。这里 main 内部目前没有固定定位元素，
   但把这个约束去掉更稳妥，免得以后往里加东西时踩坑。
   -------------------------------------------------------------------------- */
/* 加载页上滑退场的同时，整页从下方一个视口的高度升上来。

   关键在于两者必须**同速同向**：加载页上移多少，内容就升多少，
   于是加载页的下边缘始终贴着内容的上边缘，中间不会露出背景。
   早先的写法是幕布向上掀、内容只下移 180px，方向相反 ——
   结果幕布掀开后先露出一片空黑，而且标题正好卡在幕布边缘被切成两半。

   时长与缓动取自 <head> 里内联定义的 --curtain-*，
   那边必须独立于外部 CSS 存在，否则加载页在样式表加载失败时无法退场。

   只过渡 transform，不过渡 opacity：内容要在揭幕那一刻就已经在那里，
   "升上来"的观感全部由位移产生。（给 opacity 加过渡会让幕布后面先空一片。） */
main,
.site-footer {
    transition: transform var(--curtain-duration) var(--curtain-ease);
}

html.is-loading main,
html.is-loading .site-footer {
    transform: translateY(100vh);
}

/* --------------------------------------------------------------------------
   锚点定位

   曾经在这里做过 CSS 滚动吸附（scroll-snap-type: y proximity），
   结果手感很卡，已移除。原因是吸附和 Lenis 会互相抢：Lenis 每帧主动
   写 scrollTop 来做平滑插值，浏览器同时想把滚动位置拉到最近的吸附点，
   两者来回拉锯，落点是稳的但过程中一直在抖。

   教训：验证吸附不能只看"最终停在哪"，得看滚动过程中的手感 ——
   落点 0 误差、位置稳定，都不代表过程不抖。

   scroll-padding-top 保留：它管的是锚点跳转时区块顶不要被固定导航压住，
   和吸附无关。
   -------------------------------------------------------------------------- */
html {
    scroll-padding-top: 72px;
}

section {
    padding-block: var(--section-padding);
}

/* 区块之间一条极淡的分隔线 —— 比空白更能划分层次 */
section + section {
    border-top: 1px solid var(--border);
}

/* --------------------------------------------------------------------------
   导航
   -------------------------------------------------------------------------- */
.site-nav {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: var(--z-nav);
    border-bottom: 1px solid transparent;
    transition: background-color 0.4s var(--ease), border-color 0.4s var(--ease),
        backdrop-filter 0.4s var(--ease);
}

/* 停在顶部时收起导航，首屏保持完全干净；往下滚才滑出来。
   隐藏态挂在 html.js 下 —— 没有 JS 就永远显示，不会出现"导航不见了
   又没东西能把它叫回来"的死局。
   visibility 的过渡故意延迟到位移结束，这样滑出动画看得见，
   同时隐藏期间链接也不会被 Tab 键聚焦到。 */
html.js .site-nav {
    transform: translateY(-100%);
    opacity: 0;
    visibility: hidden;
    transition: transform 0.5s var(--ease), opacity 0.5s var(--ease),
        visibility 0s linear 0.5s, background-color 0.4s var(--ease),
        border-color 0.4s var(--ease);
}

html.js .site-nav[data-scrolled] {
    transform: translateY(0);
    opacity: 1;
    visibility: visible;
    transition: transform 0.5s var(--ease), opacity 0.5s var(--ease),
        visibility 0s, background-color 0.4s var(--ease),
        border-color 0.4s var(--ease);
}

.site-nav__inner {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--gap-padding);
    min-height: 72px;
}

/* 滚动后才出现毛玻璃底 —— 首屏保持完全通透 */
.site-nav[data-scrolled] {
    background-color: oklch(18.5% 0.008 var(--base-hue) / 0.72);
    border-bottom-color: var(--border);
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
}

@supports not (backdrop-filter: blur(1px)) {
    .site-nav[data-scrolled] {
        background-color: var(--bg);
    }
}

.site-nav__wordmark {
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: -0.01em;
    white-space: nowrap;
}

.site-nav__links {
    display: flex;
    align-items: center;
    gap: clamp(1rem, 2.5vw, 2.25rem);
}

.site-nav__link {
    position: relative;
    font-size: var(--fs-small);
    color: var(--text-secondary);
    transition: color 0.3s var(--ease);
    white-space: nowrap;
}

.site-nav__link::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -4px;
    width: 100%;
    height: 1px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: right;
    transition: transform 0.4s var(--ease);
}

.site-nav__link:hover {
    color: var(--text-primary);
}

.site-nav__link:hover::after {
    transform: scaleX(1);
    transform-origin: left;
}

.site-nav__actions {
    display: flex;
    align-items: center;
    gap: 1rem;
}

.site-nav__icon {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    border-radius: var(--radius-full);
    color: var(--text-secondary);
    transition: var(--animation-fast);
}

.site-nav__icon:hover {
    color: var(--text-primary);
    background: var(--surface-hover);
}

/* 移动端菜单按钮 —— 桌面端隐藏 */
.site-nav__toggle {
    display: none;
    width: 40px;
    height: 40px;
    place-items: center;
    border-radius: var(--radius-full);
    color: var(--text-primary);
}

/* --------------------------------------------------------------------------
   Hero
   -------------------------------------------------------------------------- */
.hero {
    display: flex;
    flex-direction: column;
    justify-content: center;
    min-height: 100svh;
    padding-block: clamp(7rem, 16vh, 11rem) var(--section-padding);
    border-top: none;
}

.hero .container {
    position: relative;
}

/* 常驻动画：一个缓慢自转的线框二十面体。
   画在 canvas 上而不是上 WebGL —— 同样能做出"发光的工程结构"观感，
   但零依赖、约 5KB，也不需要为它再引一个 600KB 的 three.js。
   放在首屏右半边那片空白里，正好补上版面重心。 */
.hero__canvas {
    position: absolute;
    top: 50%;
    right: 0;
    translate: 0 -50%;
    width: clamp(300px, 36vw, 560px);
    aspect-ratio: 1;
    z-index: 0;
    pointer-events: none;
}

/* 文字压在动画之上 */
.hero__copy {
    position: relative;
    z-index: 1;
}

/* 注意：这里一律 right: 0，不要用负偏移"探出"容器。
   绝对定位的包含块是 .container 的 padding box，负的 right 会直接把
   页面撑宽 —— 实测 768px 时溢出 92px、1024px 时溢出 12px，
   documentElement.scrollWidth 会大于视口宽。想让它更靠边就调宽度，
   不要调 right。 */
@media (max-width: 1100px) {
    .hero__canvas {
        width: clamp(240px, 32vw, 380px);
        opacity: 0.55;
    }
}

/* 窄屏没有横向空间并排 —— 退到文字后面当底纹 */
@media (max-width: 860px) {
    .hero__canvas {
        top: 42%;
        width: min(78vw, 420px);
        opacity: 0.22;
    }
}

.hero__chip {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    margin-bottom: clamp(1.75rem, 5vh, 3rem);
}

.hero__avatar {
    width: 52px;
    height: 52px;
    border-radius: var(--radius-full);
    border: 1px solid var(--border-strong);
    object-fit: cover;
    flex-shrink: 0;
}

.hero__status {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.hero__status-dot {
    width: 6px;
    height: 6px;
    border-radius: var(--radius-full);
    background: oklch(78% 0.16 150);
    box-shadow: 0 0 0 3px oklch(78% 0.16 150 / 0.18);
    flex-shrink: 0;
}

.hero__name {
    font-size: var(--fs-hero);
    line-height: var(--lh-hero);
    letter-spacing: var(--ls-hero);
    font-weight: 500;
    font-variation-settings: "opsz" 32;
    margin-bottom: clamp(1.25rem, 3vh, 2rem);
}

/* 三行宣言。
   比原来的 display 字号小一档、颜色也压暗一档 —— 它的作用是托住下面那句
   大标题的重心，抢戏就本末倒置了。用 flex column 而不是写死 <br>：
   中文/英文行高不同，写死断行在别的字体下会错位。 */
.hero__statement {
    display: flex;
    flex-direction: column;
    font-size: clamp(1.25rem, 2.9vw, 2.375rem);
    line-height: 1.3;
    letter-spacing: -0.015em;
    font-weight: 400;
    color: var(--text-muted);
    margin-bottom: clamp(2rem, 5vh, 3rem);
}

/* 每行是一个独立元素，遮罩揭示才可能错位。
   注意：clip-path 会裁掉下伸部，行高留宽一点才不会切到字母尾巴。 */
.hero__statement > span {
    display: block;
    padding-bottom: 0.08em;
}

.hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.875rem 1.25rem;
    align-items: center;
}

/* 邮箱直接摆在首屏动作区里 —— 访客不用滚到最底下就能看到联系方式。
   样式上刻意比按钮弱：是一行可选的联系方式，不是第三个 CTA。 */
.hero__email {
    font-family: var(--font-mono);
    font-size: var(--fs-small);
    color: var(--text-muted);
    padding-bottom: 2px;
    border-bottom: 1px solid transparent;
    transition: var(--animation-fast);
    word-break: break-all;
}

.hero__email:hover {
    color: var(--accent);
    border-bottom-color: var(--accent);
}

/* --------------------------------------------------------------------------
   区块标题
   -------------------------------------------------------------------------- */
.section-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: clamp(2.5rem, 6vh, 4.5rem);
}

.section-head__title {
    font-size: var(--fs-h2);
    letter-spacing: var(--ls-h2);
    line-height: var(--lh-h2);
    font-weight: 500;
    margin-top: 0.75rem;
}

.section-head__count {
    font-size: var(--fs-small);
    color: var(--text-muted);
}

/* --------------------------------------------------------------------------
   关于
   -------------------------------------------------------------------------- */
.about__grid {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: clamp(2rem, 6vw, 6rem);
    align-items: start;
}

.about__lead {
    font-size: var(--fs-h3);
    line-height: 1.5;
    letter-spacing: var(--ls-h3);
    color: var(--text-secondary);
}

/* 三条自述。
   用 grid 固定第一列宽度，emoji 宽度不一才不会把后面的文字推得参差不齐。 */
.about__facts {
    display: flex;
    flex-direction: column;
    gap: 0.7rem;
    margin-top: 1.75rem;
    font-size: var(--fs-body);
    color: var(--text-secondary);
}

.about__facts li {
    display: grid;
    grid-template-columns: 1.6em 1fr;
    align-items: baseline;
    gap: 0.4rem;
}

.about__emoji {
    font-size: 1.05em;
    font-style: normal;
    /* emoji 自带的饱和度在深色底上偏刺眼，压一点让它融进画面 */
    filter: saturate(0.82);
}

.about__list {
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}

.about__list a {
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
    color: var(--text-secondary);
    font-size: var(--fs-small);
    transition: var(--animation-fast);
}

.about__list a:hover {
    color: var(--accent);
    gap: 0.875rem;
}

.about__socials {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-top: 0.25rem;
}

.about__social {
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border: 1px solid var(--border);
    border-radius: var(--radius-full);
    color: var(--text-secondary);
    transition: var(--animation-fast);
}

.about__social:hover {
    color: var(--text-primary);
    border-color: var(--border-strong);
    background: var(--surface-hover);
    transform: translateY(-2px);
}

/* --------------------------------------------------------------------------
   联系
   -------------------------------------------------------------------------- */
.contact {
    text-align: center;
}

.contact__title {
    font-size: var(--fs-display);
    letter-spacing: var(--ls-display);
    line-height: 1.1;
    font-weight: 500;
    margin-bottom: 2rem;
}

.contact__email {
    display: inline-block;
    font-family: var(--font-mono);
    font-size: clamp(0.95rem, 2.2vw, 1.5rem);
    color: var(--text-primary);
    padding-bottom: 0.25rem;
    border-bottom: 1px solid var(--border-strong);
    transition: var(--animation-fast);
    word-break: break-all;
}

.contact__email:hover {
    color: var(--accent);
    border-color: var(--accent);
}

/* --------------------------------------------------------------------------
   页脚
   -------------------------------------------------------------------------- */
.site-footer {
    position: relative;
    z-index: var(--z-content);
    border-top: 1px solid var(--border);
    padding-block: 2rem;
    font-size: var(--fs-small);
    color: var(--text-muted);
}

.site-footer__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 0.75rem 1.5rem;
}

.site-footer__row + .site-footer__row {
    margin-top: 0.75rem;
    padding-top: 0.75rem;
    border-top: 1px solid var(--border);
}

.site-footer__links {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
}

.site-footer a {
    transition: color 0.3s var(--ease);
}

.site-footer a:hover {
    color: var(--text-primary);
}

.site-footer__beian:hover {
    color: var(--accent);
}

/* --------------------------------------------------------------------------
   响应式
   -------------------------------------------------------------------------- */
@media (max-width: 860px) {
    .about__grid {
        grid-template-columns: 1fr;
    }

    .hero {
        min-height: auto;
        padding-block: 8rem 5rem;
    }
}

@media (max-width: 640px) {
    .site-nav__links {
        display: none;
    }

    .site-nav__toggle {
        display: grid;
    }

    .hero__statement {
        max-width: none;
    }
}
