/* ==========================================================================
   贡献名单页 · 视觉统一层
   目标：与首页（一刻千金Ki）保持一致 —— 同背景点阵、同主题变量、同卡片语言。
   加载位置：必须在 contributors.php 内联 <style> 之后，才能覆盖页面自带样式。
   变量（--bg / --card / --ink / --ink-soft / --line / --gold-* / --dot /
   --shadow-* / --serif / --sans）来自 assets/css/style.css。
   ========================================================================== */

/* ---------- 1. 后台配色接管 ----------
    contributors.php 已把后台「外观设置」的四个颜色写进 --brand* / --text-color /
    --bg-pattern-color / --bg-pattern-size，这里只做深浅色适配：
    · 浅色：正文用后台文字色，点缀用 --brand（未改配置时即主题默认金）
    · 深色：正文强制回到 --ink（用户选的浅色文字色在深底下不可读）      */
:root { --text-color: var(--ink); }
[data-theme="dark"] { --text-color: var(--ink); }

/* 点缀色统一走 --brand*，后台改主色调即刻生效 */
:root {
    --gold-text:   var(--brand);
    --gold-bright: var(--brand-bright);
    --gold-core:   var(--brand-bright);
    --gold-deep:   var(--brand-deep);
    --gold-soft:   var(--brand-soft);
    --gold-line:   var(--brand-line);
}

/* 深色下的品牌色：浅色品牌是「深金」(#a9700f)，直接搬到深底上会发闷
   （22px 的贡献价值、11px 的价值胶囊都只有 3:1 出头）。这里沿着同一主色
   的浅阶（--brand-bright）往白里提一档，色相不变、明度适配深底。
   「品牌软底」则改为「基色兑透明度」：后台下发的是浅底用的近白值
   (#f6f0e7)，直接用在深色卡片上会变成刺眼的白药丸（价值标签、排序
   选中态、主页钮 hover 都是它）。color-mix 不支持的旧内核会丢弃整条
   声明、退回原值，不影响功能 */
[data-theme="dark"] {
    --brand: color-mix(in srgb, var(--brand-bright) 85%, #ffffff);
    --brand-soft: color-mix(in srgb, var(--brand) 16%, transparent);
    --brand-line: color-mix(in srgb, var(--brand) 36%, transparent);
}

/* ---------- 2. 背景：与首页同一套点阵 ---------- */
body {
    background-color: var(--bg) !important;
    background-image: radial-gradient(var(--bg-pattern-color) 1px, transparent 1px) !important;
    background-size: var(--bg-pattern-size) var(--bg-pattern-size) !important;
    color: var(--ink);
    font-family: var(--sans);
    /* padding-top 会随公告条开关变化（见第 8 节），带上过渡，关掉时整页是滑上去而不是跳一下 */
    transition: background-color .45s ease, color .45s ease, padding-top .3s ease;
}
[data-theme="dark"] body {
    background-image: radial-gradient(var(--bg-pattern-color) 1px, transparent 1px) !important;
}

/* 深色底下，后台选的浅色点阵会糊掉，统一提到 22% 白 */
[data-theme="dark"] body {
    background-image: radial-gradient(rgba(255, 255, 255, .16) 1px, transparent 1px) !important;
}

/* ---------- 3. 页头 ---------- */
.gradient-header {
    background: transparent;
    padding: 72px 24px 44px;
}

/* 眉标：与首页 .eyebrow 同款（两侧短线 + 大字距），块级独占一行 */
.page-eyebrow {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin: 0 0 16px;
    font-family: var(--sans);
    font-size: 12px;
    letter-spacing: .32em;
    color: var(--ink-soft);
}
.page-eyebrow::before,
.page-eyebrow::after {
    content: "";
    width: 26px;
    height: 1px;
    background: var(--line-strong);
}

/* 品牌名：用首页同款鎏金扫光（.gold-flow），Ki 单独小斜体 */
.logo {
    font-family: var(--serif);
    font-weight: 900;
    font-size: clamp(44px, 7vw, 78px);
    line-height: 1.12;
    letter-spacing: .02em;
    color: var(--ink);
    text-shadow: none;
    display: inline-block;
}
.logo .ki {
    font-size: .4em;
    font-style: italic;
    margin-left: 8px;
    vertical-align: 6px;
}
.logo::before { display: none; } /* data-text 阴影副本在扫光下会穿帮成灰重影，直接去掉 */

.page-title {
    font-family: var(--serif);
    font-size: clamp(20px, 3vw, 28px);
    font-weight: 700;
    color: var(--ink);
    letter-spacing: .08em;
    margin-bottom: 16px;
}
/* 分隔线由原来的黑灰渐变换成首页同款金色细线 */
.page-title::after {
    width: 88px;
    height: 2px;
    background: linear-gradient(90deg, transparent 0%, var(--brand-bright) 50%, transparent 100%);
    box-shadow: none;
    opacity: .7;
}

/* 副标题：去掉内联的黑→灰渐变（深色下几乎不可见），改用首页墨色 */
.subtitle {
    display: block;
    max-width: 660px;
    margin: 0 auto 30px;
    font-family: var(--serif);
    font-size: 16px;
    line-height: 1.75;
    letter-spacing: .05em;
    color: var(--ink-soft) !important;
    -webkit-text-fill-color: var(--ink-soft) !important;
    background: none !important;
    animation: none !important;
    text-shadow: none;
}
.subtitle::before { display: none; }

/* 返回首页按钮已删除：站名本身就做成首页入口（页头左上那颗胶囊与居中的品牌区
   是两套重心，去掉之后页头只剩一条竖轴）。链接化的外观写在 contributors.php 内联层，
   因为它要覆盖的是页面自带的 .logo 声明 */

/* ---------- 4. 顶部统计徽章 ---------- */
.stat-badge {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 999px;
    padding: 8px 16px;
    color: var(--ink-soft);
    box-shadow: var(--shadow-sm);
    font-size: 13px;
}
.stat-badge .count-animate { color: var(--ink); font-weight: 700; font-family: var(--num); font-variant-numeric: tabular-nums; }
.stat-badge i, .stat-badge .users-icon, .stat-badge .code-branch-icon,
.stat-badge .star-icon, .stat-badge .eye-icon, .stat-badge .date-icon { color: var(--brand); }

/* ---------- 5. 搜索与排序 ---------- */
main {
    max-width: 1080px;
    margin: 0 auto;
    padding: 0 24px;
}
.controls-container {
    margin: 0 auto 26px;
}
.search-box {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 12px;
    box-shadow: var(--shadow-sm);
    transition: border-color .3s ease, box-shadow .3s ease;
}
.search-box:focus-within {
    border-color: var(--brand-bright);
    box-shadow: 0 0 0 3px var(--brand-soft);
}
.search-box input {
    background: transparent;
    color: var(--ink);
}
.search-box input::placeholder { color: var(--ink-soft); }
.search-box i { color: var(--ink-soft); }

/* 排序按钮：未选中是幽灵文字（减少视觉噪音），选中用品牌色软底 pill */
.sort-options { gap: 4px; flex-wrap: wrap; justify-content: center; }
.sort-button {
    background: none;
    border: 1px solid transparent;
    color: var(--ink-soft);
    border-radius: 999px;
    padding: 7px 15px;
    font-size: 13px;
    font-weight: 600;
    box-shadow: none;
    transition: transform .25s ease, color .25s ease, border-color .25s ease, background .25s ease;
}
.sort-button:hover {
    color: var(--ink);
    background: var(--bg-soft);
}
.sort-button.active {
    background: var(--brand-soft);
    color: var(--brand);
    border-color: var(--brand-line);
}

/* ---------- 6. 贡献者卡片 ----------
   卡片的完整样式已下沉到 contributors.php 内联 <style>（走 --card / --line /
   --ink / --brand* 主题变量，深浅自动适配）。这里只保留两件内联样式管不了的事：
   ① 用户自选的记录底色是浅色块，深色模式下整体压暗，避免刺眼；
   ② 深色下记录轨道竖线/节点描边提亮一档，避免糊进背景。 */
.contributor-header .username { letter-spacing: .01em; }

[data-theme="dark"] .detail-body[style*="background-color"] {
    background-color: rgba(255, 255, 255, .06) !important;
}
[data-theme="dark"] .detail-item::before { background: var(--line-strong); }

/* ---------- 7. 加载 / 空状态 / 错误 ---------- */
#loadingIndicator > div,
#errorMessage > div {
    display: inline-block;
    padding: 22px 28px;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: 14px;
    color: var(--ink);
    box-shadow: var(--shadow-sm);
    text-align: center;
}
#loadingIndicator,
#noDataMessage,
#errorMessage { text-align: center; }
#noDataMessage { color: var(--ink-soft); }
#noDataMessage h3 { color: var(--ink); font-size: 18px; }
.state-title { font-weight: 700; color: var(--ink); }
.state-sub { font-size: 12px; color: var(--ink-soft); margin-top: 2px; }
#errorMessage h3 { color: var(--warn); font-size: 18px; }
#errorMessage p { color: var(--ink-soft); }
#errorMessage .state-card-danger > i { color: var(--warn); }
.state-btn {
    background: var(--ink);
    color: var(--on-accent);
    border: none;
    padding: 9px 18px;
    border-radius: 999px;
    cursor: pointer;
    font-size: 13px;
    font-weight: 600;
    transition: transform .25s ease, box-shadow .25s ease;
}
.state-btn:hover { transform: translateY(-1px); box-shadow: var(--shadow); }
.state-card-danger .state-btn { background: var(--warn); color: var(--on-accent); }

/* 加载圈：页面原来引用了不存在的 @keyframes spin，转圈根本没生效，这里补上 */
.loading-spinner {
    width: 24px;
    height: 24px;
    border: 3px solid var(--line);
    border-top-color: var(--brand-bright);
    border-radius: 50%;
    animation: spin 1s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
    .loading-spinner { animation-duration: 3s; }
}

/* 浮层提示（JS 生成） */
.notification-toast {
    background: var(--card) !important;
    color: var(--ink) !important;
    border: 1px solid var(--line) !important;
    box-shadow: var(--shadow) !important;
}

/* ---------- 8. 声明（站外顶部横幅 · 跑马灯）/ 页脚 / 备案 ---------- */
/* 位置与形态：页头内的注脚卡 → 站外整宽条 + 单行走字。
   与第一版的整宽静态灰带不是一回事：
   · 静态灰带横在品牌区上方，切断了居中页头，读起来像广告位 / 浏览器提示条；
   · 走字屏本身就是「公告条」的语言，且高度恒定一行，不会把页头往下推。
   结构分两层：.site-notice 只管出现动画与层叠，.site-notice-inner 管内容——
   内容限宽居中，超宽屏上两端不顶格。
   底色不是一条通长的实色带：由 ::before 铺一层 --bg-soft，左右两端渐变淡出，
   页面底色从缝里透出来，边缘就没有硬切线。不挂「公告」籤——只有一个走字区和一个
   关闭键，第一句自己会说明来意。 */
:root {
    --notice-h: 36px;      /* 条子净高（另加 3px 给进度条），主题钮让位也用它算 */
    --notice-fade: 240px;  /* 底色两端虚化宽度：比文字那层更长，字散完底还有一层薄雾 */
    --notice-veil: 150px;  /* 文字进雾的距离 */
}
.site-notice {
    /* 就待在文档流最前面，随页面一起滚走——用户要的是「只在顶部出现」，
       不是贴顶常驻。上一版用 fixed 是为了常驻，才需要 body 补 padding-top；
       现在它自己就占那一行高度，那套让位逻辑全部拆掉。
       position 仍需非 static：::before/::after 要挂在这层上 */
    position: relative;
    /* 相对定位 + z-index 才成为堆叠上下文，::before 的负层级才不会掉到页面背景后面 */
    z-index: 190;
    /* 收起时高度归零，页面平滑上移（不用 display:none 硬切） */
    overflow: hidden;
    transition: height .32s cubic-bezier(.22, .9, .26, 1), padding-top .32s cubic-bezier(.22, .9, .26, 1), opacity .22s ease;
    /* 出现动画：从顶边滑下来 + 淡入，位移量就是自身高度 */
    animation: siteNoticeIn .5s cubic-bezier(.22, .9, .26, 1) both;
    /* 高度拆成两段：3px 让给顶部滚动进度条（它仍是 fixed，会盖住条子最上面），
       金色进度线正好压在留白里，不切字 */
    height: calc(var(--notice-h) + 3px);
    padding-top: 3px;
    /* 底色交给 ::before，条子本身不铺色，两端才淡得出去 */
    background: none;
    font-family: var(--sans);
}
@keyframes siteNoticeIn {
    from { transform: translate3d(0, -100%, 0); opacity: 0; }
    /* 收尾用 none 而不是 translate3d(0,0,0)：留下 transform 会一直挂着一层合成层，
       条子里的 12px 小字在部分显卡上会糊；none 让它落位后回归普通绘制 */
    to   { transform: none; opacity: 1; }
}

/* 底色层：纯色 + 两端渐变淡出（两重保险——mask 生效时按 mask 裁，
   mask 不被支持时还有这层渐变兜底，任何情况下都不会留一条硬边的灰带）。
   同时挂 backdrop-filter：淡出的那截会趋于透明，页面正文从条子底下滚过时
   被糊成一片颜色，而不是透出清晰的半行字。
   注意 mask 只能裁在 ::before 上——裁条子本身会把关闭键一起裁淡。 */
.site-notice::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: linear-gradient(90deg,
        transparent 0,
        var(--bg-soft) var(--notice-fade),
        var(--bg-soft) calc(100% - var(--notice-fade)),
        transparent 100%);
    -webkit-backdrop-filter: blur(14px);
    backdrop-filter: blur(14px);
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 var(--notice-fade), #000 calc(100% - var(--notice-fade)), transparent 100%);
    mask-image: linear-gradient(90deg, transparent 0, #000 var(--notice-fade), #000 calc(100% - var(--notice-fade)), transparent 100%);
}

/* 底边发丝线同样两端淡出：实色线走到虚化区里会突然断头，比不画还难看。
   有了这条线就不必再给条子加投影，投影的矩形边界同样会露出硬边 */
.site-notice::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    pointer-events: none;
    background: linear-gradient(90deg,
        transparent 0,
        var(--line) var(--notice-fade),
        var(--line) calc(100% - var(--notice-fade)),
        transparent 100%);
}

.site-notice-inner {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 12px;
    /* 限宽居中：1920/2560 的屏上两端留得住呼吸，不会像贴边跑马灯 */
    width: 100%;
    max-width: 1280px;
    height: 100%;
    margin: 0 auto;
    padding: 0 26px;
    text-align: left;
}
/* 收起：高度与内边距一起归零，页面平滑上移。
   animation 的 fill:both 会把 opacity 钉在 1，必须先关掉动画，
   否则下面那条 opacity 过渡永远跑不起来（这是踩过的坑） */
.site-notice.is-hidden {
    animation: none;
    height: 0;
    padding-top: 0;
    opacity: 0;
    pointer-events: none;
}

/* 走字视口：两端走进雾里，字从右缘慢慢凝出来、往左缘一点点散掉。
   渐隐不是一条直线的斜坡（直线能看出「一刀切」的边），
   而是按比例分四段收：先掉到一成，再半成，最后才归零，接近雾的衰减曲线。
   距离取 --notice-veil，比底色那层（--notice-fade）短：
   字在雾里散完时身后仍有一层薄底托着，不会出现「字已透明、底下却突然全实」的断层，
   也不会反过来让字在实底上发虚。 */
.site-notice-viewport {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg,
        transparent 0,
        rgba(0, 0, 0, .10) calc(var(--notice-veil) * .30),
        rgba(0, 0, 0, .38) calc(var(--notice-veil) * .55),
        rgba(0, 0, 0, .74) calc(var(--notice-veil) * .80),
        #000 var(--notice-veil),
        #000 calc(100% - var(--notice-veil)),
        rgba(0, 0, 0, .74) calc(100% - var(--notice-veil) * .80),
        rgba(0, 0, 0, .38) calc(100% - var(--notice-veil) * .55),
        rgba(0, 0, 0, .10) calc(100% - var(--notice-veil) * .30),
        transparent 100%);
    mask-image: linear-gradient(90deg,
        transparent 0,
        rgba(0, 0, 0, .10) calc(var(--notice-veil) * .30),
        rgba(0, 0, 0, .38) calc(var(--notice-veil) * .55),
        rgba(0, 0, 0, .74) calc(var(--notice-veil) * .80),
        #000 var(--notice-veil),
        #000 calc(100% - var(--notice-veil)),
        rgba(0, 0, 0, .74) calc(100% - var(--notice-veil) * .80),
        rgba(0, 0, 0, .38) calc(100% - var(--notice-veil) * .55),
        rgba(0, 0, 0, .10) calc(100% - var(--notice-veil) * .30),
        transparent 100%);
}
/* 动画写在 CSS、位移量与时长由 JS 写进自定义属性：
   位移 = 一份内容的宽度（不是 -50%），份数随屏宽增减都不用改这里 */
.site-notice-scroll {
    display: flex;
    width: max-content;
    animation: siteNoticeRun var(--sn-dur, 40s) linear infinite;
    will-change: transform;
}
.site-notice-group {
    display: flex;
    flex: none;
    align-items: center;
    /* 末尾这段留白 = 循环到头时的段间距：两份文案接上时只有这里呼吸，
       同时也是 JS 量位移量的落点（位移恒等于一份的宽度） */
    padding-right: 56px;
}
@keyframes siteNoticeRun {
    from { transform: translate3d(0, 0, 0); }
    to   { transform: translate3d(var(--sn-shift, -50%), 0, 0); }
}
/* 悬停 / 键盘聚焦时停走：想读完整句就停下来，移开继续 */
.site-notice:hover .site-notice-scroll,
.site-notice:focus-within .site-notice-scroll { animation-play-state: paused; }

/* 一整段免责声明：不分句、不加分隔点，读起来是一段话而不是一排标签。
   不换行是硬要求——两行文字的走字屏会上下行互相追，而且一条 39px 的条子塞不下两行 */
.sn-text {
    flex: none;
    font-size: 12.5px;
    line-height: 1;
    color: var(--ink-soft);
    white-space: nowrap;
    letter-spacing: .005em;
}
/* 重点句：加粗 + 正文墨色。
   不用品牌金：浅底上金句发飘、深底上又太刺眼，连排起来像广告；
   墨色 800 在 --ink-soft 的灰底文里已经是最高对比，深色模式下自动变近白 */
.sn-text b {
    font-weight: 800;
    color: var(--ink);
    letter-spacing: .01em;
}

.site-notice-close {
    flex: none;
    display: inline-grid;
    place-items: center;
    width: 24px;
    height: 24px;
    border: 0;
    border-radius: 50%;
    background: none;
    font-family: inherit;
    color: var(--ink-soft);
    font-size: 11px;
    cursor: pointer;
    opacity: .6;
    /* 关闭键落在两端最淡的那一段上，只靠图标容易「飘」在页头里。
       用一圈同色柔光把雾推开一点——实底区里它与底色同色，看不出来；
       透明区里就是一小片磨砂，按钮始终有落脚点。 */
    box-shadow: 0 0 14px 9px var(--bg-soft);
    transition: background .2s ease, opacity .2s ease, color .2s ease;
}
.site-notice-close:hover { background: var(--line); color: var(--ink); opacity: 1; }
.site-notice-close:focus-visible { outline: 2px solid var(--brand-bright); outline-offset: 2px; }

/* 条子不再贴顶常驻，主题钮不用再让位，上一版为「跟着条子上下移」补的那份
   transition 覆盖也一并撤掉，回到 style.css 的默认过渡 */

/* ---------- 页脚 ---------- */
/* 底色、上下留白由 style.css 的 footer 统一给（与宠物挂件同一块 --stage 地台），
   这里只管名单页自己的排版：一行版权、一行备案胶囊，两行居中对齐、间距固定，
   不再靠内联 style 堆 margin */
.site-footer-inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
}
.site-footer-copy {
    margin: 0;
    font-family: var(--serif);
    font-size: 15px;
    letter-spacing: .03em;
    color: var(--ink-soft);
    line-height: 1.4;
}
/* 年份比站名淡一档：页脚的主角是那条扫光，年份跟着一起亮会显得整行在闪 */
.site-footer-year {
    font-size: 12.5px;
    letter-spacing: .1em;
    opacity: .72;
    margin-right: 6px;
}
.site-footer-icp {
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--ink-soft);
}
.site-footer-icp > i {
    font-size: 11px;
    opacity: .5;
}
.site-footer-icp a { text-decoration: none; color: inherit; }
/* 胶囊底单独一层（.icp-pill），里面的 .beian-icp 才能安心底做 background-clip:text；
   底色/描边走主题变量，深色模式下才不会是一块半透明白斑。
   内边距与首页 .icp 对齐，两页页脚才是同一套规格 */
.icp-pill {
    display: inline-block;
    padding: 6px 14px;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--card);
    box-shadow: var(--shadow-sm);
    transition: border-color .25s ease, transform .25s ease, background-color .45s ease;
}
.site-footer-icp a:hover .icp-pill {
    border-color: var(--line-strong);
    transform: translateY(-1px);
}
.site-footer-icp a:focus-visible .icp-pill {
    outline: 2px solid var(--brand);
    outline-offset: 2px;
}
/* 页脚两处鎏金：与页头同一套 goldSweep，但错开相位——
   页头、页脚站名、备案号三条同时起扫会像整页在闪，错开半拍才是「先后掠过」。
   这里写完整 animation 而不是只写 delay：避免被 .gold-flow 的简写按顺序重置 */
.footer-brand { animation: goldSweep 3s linear .7s infinite; }
.beian-icp { animation: goldSweep 3s linear 1.5s infinite; }

/* ---------- 9. 移动端 ---------- */
@media (max-width: 620px) {
    .gradient-header { padding: 60px 16px 32px; }
    main { padding: 0 16px; }
    .logo { font-size: 46px; }
    .logo .ki { margin-left: 5px; }
    .page-eyebrow { font-size: 10px; letter-spacing: .24em; gap: 8px; margin-bottom: 12px; }
    .page-eyebrow::before,
    .page-eyebrow::after { width: 16px; }
    .page-title { font-size: 20px; }
    .subtitle { font-size: 14px; }
    .contributors-grid { gap: 12px; }
    /* 移动端排序按钮给回底色：幽灵样式在触摸屏上可点区域不明显 */
    .sort-button { background: var(--bg-soft); border-color: var(--line); }
    .sort-button.active { background: var(--brand-soft); border-color: var(--brand-line); }
    /* 声明条：窄屏压矮、字号与句间距各收一档，一行仍是完整一段话。
       雾的半径也跟着收——手机上整屏才 390，168px 的虚化会吃掉半句话 */
    :root { --notice-h: 32px; --notice-fade: 46px; --notice-veil: 48px; }
    .site-notice-inner { gap: 8px; padding: 0 14px; }
    .sn-text { font-size: 11.5px; }
    .site-notice-group { padding-right: 40px; }
    /* 页脚：手机上动物图只有 47px 高，砖地 14px，上下留白跟着收，
       否则石头到「© 2026」之间会空出一大块 */
    .site-footer { padding: 26px 16px 32px; }
    .site-footer-inner { gap: 12px; }
    .site-footer-copy { font-size: 14px; }
    .site-footer-year { font-size: 11.5px; margin-right: 4px; }
    .site-footer-icp { font-size: 12px; gap: 6px; }
    .icp-pill { padding: 5px 12px; }
}
