PageSourceSearch

https://github.nb-channel.top/Beta/index-Beta.html

html nb-channel.top collected 2026-10-06 13:15:40 UTC 147,314 bytes, 2,742 lines download raw bytes

1<!DOCTYPE html>
2<html lang="zh-CN">
3<head>
4    <meta charset="UTF-8">
5    
5<script>
6    /* ===== 跨子域名登录态同步(github. / cloudflare. / pythonanywhere. 之间互通)=====
7       localStorage 是按 origin 隔离的,两个子域名各存各的,所以要借 Cookie 中转。
8       Cookie 设 Domain=.nb-channel.top,所有子域名都能读到。
9
10       这里不改页面原本的任何代码 —— 而是劫持 localStorage 的读写:
11         · 页面登录时 setItem('nb_session', ...)  → 顺手写一份到 Cookie
12         · 页面登出时 removeItem('nb_session')    → 顺手把 Cookie 清掉
13         · 页面加载时 Cookie 有、本地没有          → 自动注入进 localStorage
14       所以那 57 处登录/登出代码一行都不用动。
15
16       ⚠️ 这意味着 *.nb-channel.top 下任何一个子域名都能读到这个令牌,
17          包括 product-storage 那个 Workers。将来新增子域名前先想清楚这件事。 */
18    (function () {
19        try {
20            var KEY_S = 'nb_session', KEY_U = 'nb_user';
21            var DOMAIN = '.nb-channel.top';
22            var MAXAGE = 30 * 86400;
23
24            // 只在 .nb-channel.top 域下才启用(本地开文件、或别的域名访问时保持原样)
25            if (location.hostname.indexOf('nb-channel.top') < 0) return;
26
27            function readCookie(n) {
28                var m = document.cookie.match(new RegExp('(?:^|;\\s*)' + n + '=([^;]*)'));
29                return m ? decodeURIComponent(m[1]) : null;
30            }
31            function writeCookie(n, v) {
32                document.cookie = n + '=' + encodeURIComponent(v) + ';path=/;domain=' + DOMAIN +
33                                  ';max-age=' + MAXAGE + ';SameSite=Lax' +
34                                  (location.protocol === 'https:' ? ';Secure' : '');
35            }
36            function dropCookie(n) {
37                document.cookie = n + '=;path=/;domain=' + DOMAIN + ';max-age=0;SameSite=Lax' +
38                                  (location.protocol === 'https:' ? ';Secure' : '');
39            }
40
41            // ---- ① 页面加载时双向对齐 ----
42            [KEY_S, KEY_U].forEach(function (k) {
43                var c = readCookie(k), l = null;
44                try { l = localStorage.getItem(k); } catch (e) {}
45                if (l) {
46                    if (l !== c) writeCookie(k, l);            // 本地有 → 同步出去
47                } else if (c) {
48                    try { localStorage.setItem(k, c); } catch (e) {}   // 本地没有 → 采用 Cookie
49                }
50            });
51
52            // ---- ② 劫持写入:登录时同步到 Cookie ----
53            var _set = localStorage.setItem;
54            localStorage.setItem = function (k, v) {
55                try { if ((k === KEY_S || k === KEY_U) && v) writeCookie(k, v); } catch (e) {}
56                return _set.call(localStorage, k, v);
57            };
58
59            // ---- ③ 劫持删除:登出时同步清 Cookie ----
60            var _rm = localStorage.removeItem;
61            localStorage.removeItem = function (k) {
62                try { if (k === KEY_S || k === KEY_U) dropCookie(k); } catch (e) {}
63                return _rm.call(localStorage, k);
64            };
65
66            // ---- ④ 万一有人用 clear() ----
67            var _cl = localStorage.clear;
68            localStorage.clear = function () {
69                try { dropCookie(KEY_S); dropCookie(KEY_U); } catch (e) {}
70                return _cl.call(localStorage);
71            };
72        } catch (e) {}
73    })();
74    </script>
74
75    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
76    <!-- 不发送 referer:B站图床(hdslb)防盗链,带外部站 referer 会 403 导致视频封面不显示 -->
77    <meta name="referrer" content="no-referrer">
78    <meta name="description" content="NB频道(NoBook频道)——热爱理科(和作死)的UP主NB搞事局建立的虚拟公司官网。化学物理实验、NB币虚拟经济(股票/银行/商店)、评论区与友商生态。">
79    <meta property="og:title" content="NB频道官网">
80    <meta property="og:description" content="热爱理科(和作死)的虚拟公司官网——NB币经济、化学物理、友商生态。">
81    <meta property="og:type" content="website">
82    <meta name="theme-color" content="#0f172a">
83    <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Cdefs%3E%3ClinearGradient id='g' x1='0' y1='0' x2='1' y2='1'%3E%3Cstop offset='0' stop-color='%233b82f6'/%3E%3Cstop offset='1' stop-color='%238b5cf6'/%3E%3C/linearGradient%3E%3C/defs%3E%3Crect width='64' height='64' rx='14' fill='url(%23g)'/%3E%3Ctext x='32' y='43' font-family='Arial,sans-serif' font-size='26' font-weight='800' fill='%23fff' text-anchor='middle'%3ENB%3C/text%3E%3C/svg%3E">
84    <title>NB频道官网 - Beta 新版首页</title>
85    
85<script>
86        // 夜间模式:第一帧前生效(无显式选择时跟随系统)
87        (function () {
88            try {
89                var t = localStorage.getItem('theme');
90                if (t === 'dark' || (!t && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
91                    document.documentElement.classList.add('dark');
92                }
93            } catch (e) {}
94        })();
95    </script>
95
96    <style>
97        /* ============ 基础 ============ */
98        * { margin: 0; padding: 0; box-sizing: border-box; }
99        :root {
100            --bg: #f7f8fa;
101            --ink: #1a1d23;
102            --ink-soft: #5a6270;
103            --ink-faint: #8b93a3;
104            --line: #e8eaee;
105            --brand: #3b82f6;
106            --brand-deep: #2563eb;
107            --card: #ffffff;
108            --radius-lg: 20px;
109            --radius-md: 14px;
110            --radius-sm: 8px;
111            --shadow-1: 0 1px 2px rgba(16,24,40,.04), 0 4px 12px rgba(16,24,40,.06);
112            --shadow-2: 0 2px 4px rgba(16,24,40,.05), 0 24px 48px -20px rgba(16,24,40,.18);
113        }
114        /* ============ 夜间模式(html.dark 变量覆盖) ============ */
115        html.dark { color-scheme: dark; }
116        html.dark {
117            --bg: #0a101f;
118            --ink: #e5eaf3;
119            --ink-soft: #9aa7bd;
120            --ink-faint: #64748b;
121            --line: rgba(148,163,184,.16);
122            --card: #101a2e;
123            --shadow-1: 0 1px 2px rgba(0,0,0,.3), 0 6px 18px rgba(0,0,0,.28);
124            --shadow-2: 0 2px 6px rgba(0,0,0,.35), 0 28px 60px -24px rgba(0,0,0,.6);
125        }
126        html.dark body {
127            background:
128                radial-gradient(900px 520px at 85% -10%, rgba(59,130,246,.06), transparent 60%),
129                var(--bg);
130            /* 不用 background-attachment:fixed:与吸顶导航 backdrop-filter 叠加时刷新首帧合成卡顿 */
131        }
132        html.dark .nav { background: rgba(10,16,31,.72); border-bottom-color: rgba(148,163,184,.12); }
133        html.dark .nav.scrolled { background: rgba(10,16,31,.9); }
134        html.dark .nav-auth { background: rgba(255,255,255,.05); color: var(--ink); border-color: rgba(148,163,184,.25); }
135        html.dark .nav-auth:hover { background: rgba(255,255,255,.1); border-color: rgba(59,130,246,.5); }
136        html.dark .nav-burger { background: rgba(255,255,255,.06); border-color: rgba(148,163,184,.25); color: var(--ink); }
137        html.dark .nav-scan { background: rgba(255,255,255,.06); border-color: rgba(148,163,184,.25); color: var(--ink); }
138        html.dark .theme-btn { background: rgba(255,255,255,.06); border-color: rgba(148,163,184,.25); color: var(--ink); }
139        html.dark .beta-announce { background: var(--card); }
140        html.dark .beta-announce .ba-icon { background: rgba(59,130,246,.15); }
141        html.dark .beta-announce .ba-item { color: var(--ink-soft); }
142        html.dark .friends-section { background: transparent; }
143        html.dark .vcard .thumb, html.dark .card .thumb { background: #0d1526; }
144        html.dark .drawer { background: #0f1a30; }
145        html.dark .drawer .d-close { background: rgba(255,255,255,.06); color: var(--ink); }
146        html.dark .drawer a.d-item { color: var(--ink-soft); }
147        html.dark .drawer a.d-item:hover, html.dark .drawer a.d-item.active { background: rgba(59,130,246,.14); color: #fff; }
148        html.dark .drawer .d-brand { color: var(--ink); }
149        html.dark .backtop { background: rgba(15,23,42,.85); color: #cbd5e1; border-color: rgba(148,163,184,.25); }
150        html.dark .backtop:hover { background: var(--brand); color: #fff; }
151        html.dark .hero-scroll { color: rgba(255,255,255,.35); }
152        html.dark .about-tags .tag { background: var(--card); }
153        html.dark .friend { background: rgba(255,255,255,.03); }
154        html.dark .friend:hover { background: rgba(255,255,255,.06); }
155        html.dark .footer-bottom { border-color: rgba(255,255,255,.06); }
156
157        html { scroll-behavior: smooth; }
158        body {
159            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;
160            background:
161                radial-gradient(1100px 620px at 82% -14%, rgba(59,130,246,.05), transparent 62%),
162                var(--bg);
163            /* 同上:不使用 background-attachment:fixed,避免首帧/滚动重采样开销 */
164            color: var(--ink);
165            line-height: 1.6;
166            -webkit-font-smoothing: antialiased;
167        }
168        a { text-decoration: none; color: inherit; }
169
170        /* ============ 导航 ============ */
171        .nav {
172            position: sticky; top: 0; z-index: 100;
173            display: flex; align-items: center; justify-content: space-between;
174            padding: 0 48px; height: 64px;
175            background: rgba(255,255,255,.72);
176            -webkit-backdrop-filter: blur(20px) saturate(1.5);
177            backdrop-filter: blur(20px) saturate(1.5);
178            border-bottom: 1px solid var(--line);
179        }
180        .nav-logo {
181            display: flex; align-items: center; gap: 10px;
182            font-size: 1.15rem; font-weight: 800; letter-spacing: .3px;
183            margin-left: 12px;
184        }
185        /* LOGO 图片 */
186        .logo-img { display: block; height: 32px; width: auto; }
187        .nav-logo .logo-img { height: 32px; }
188        .footer .brand .logo-img { height: 38px; margin-bottom: 4px; }
189        .drawer .d-brand .logo-img { height: 34px; }
190        html.dark .logo-img { filter: brightness(1.4) saturate(1.15); }
191        .nav-logo .dot {
192            width: 10px; height: 10px; border-radius: 3px;
193            background: linear-gradient(135deg, var(--brand), #8b5cf6);
194        }
195        .nav-menu { display: flex; gap: 2px; }
196        .nav-menu a {
197            position: relative; padding: 10px 14px;
198            font-size: .92rem; font-weight: 500; letter-spacing: .4px;
199            color: var(--ink-soft); white-space: nowrap;
200            transition: color .3s ease;
201        }
202        .nav-menu a::after {
203            content: ''; position: absolute; left: 14px; right: 14px; bottom: 2px;
204            height: 1px; background: var(--brand);
205            transform: scaleX(0); transform-origin: left;
206            transition: transform .35s cubic-bezier(.16,1,.3,1);
207        }
208        .nav-menu a:hover { color: var(--ink); }
209        .nav-menu a:hover::after { transform: scaleX(1); }
210        .nav-menu a.active { color: var(--brand); font-weight: 600; }
211        .nav-menu a.active::after { transform: scaleX(1); }
212        /* API 入口 */
213        .nav-menu a i { font-style: normal; font-size: .66em; margin-left: 2px; vertical-align: 5px; letter-spacing: 0; opacity: .75; }
214        .nav-menu a.nav-api { color: var(--brand); font-weight: 600; font-size: .86rem; }
215        .nav-menu a.nav-api:hover { color: var(--brand-deep); }
216        .nav-menu a.nav-api::after { background: var(--brand-deep); }
217        .d-item i { font-style: normal; }
218        .nav-cta {
219            padding: 9px 20px; border-radius: 999px;
220            background: var(--brand); color: #fff; font-size: .9rem; font-weight: 600;
221            box-shadow: 0 8px 20px -8px rgba(59,130,246,.5);
222            transition: all .25s ease;
223        }
224        .nav-cta:hover { background: var(--brand-deep); transform: translateY(-1px); box-shadow: 0 12px 24px -8px rgba(59,130,246,.6); }
225
226        /* ============ Hero(极简排版:细眉行 + 粗细字重对比 + 左文右画) ============ */
227        .hero {
228            position: relative; overflow: hidden;
229            padding: 140px 48px 170px;
230            background:
231                radial-gradient(860px 460px at 16% 6%, rgba(59,130,246,.15), transparent 62%),
232                radial-gradient(700px 460px at 92% 30%, rgba(139,92,246,.10), transparent 60%),
233                linear-gradient(168deg, #0b1220 0%, #101c33 58%, #1c1745 100%);
234        }
235        .hero::before {
236            content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .6;
237            background: radial-gradient(600px 300px at 15% 10%, rgba(96,165,250,.20), transparent 60%);
238            animation: heroDrift 26s ease-in-out infinite alternate;
239        }
240        .hero::after {
241            content: ''; position: absolute; inset: 0; pointer-events: none; opacity: .5;
242            background-image: radial-gradient(rgba(255,255,255,.05) 1px, transparent 1px);
243            background-size: 36px 36px;
244            mask-image: linear-gradient(to bottom, rgba(0,0,0,.55), transparent 75%);
245            -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,.55), transparent 75%);
246        }
247        @keyframes heroDrift {
248            from { transform: translate3d(-18px, -10px, 0) scale(1); }
249            to { transform: translate3d(24px, 16px, 0) scale(1.06); }
250        }
251        /* Hero → 背景色的柔和过渡 */
252        .hero-fade {
253            position: absolute; left: 0; right: 0; bottom: 0; height: 120px; z-index: 1;
254            background: linear-gradient(to bottom, transparent, var(--bg));
255            pointer-events: none;
256        }
257        .hero-inner {
258            position: relative; z-index: 2; max-width: 1180px; margin: 0 auto;
259            display: grid; grid-template-columns: 1.05fr .95fr; gap: 80px; align-items: center;
260        }
261        .hero-copy { text-align: left; min-width: 0; }
262        .hero-eyebrow {
263            display: block; font-size: 11px; font-weight: 400;
264            letter-spacing: 8px; color: rgba(255,255,255,.42);
265            margin-bottom: 44px;
266        }
267        .hero h1 {
268            font-size: clamp(2.5rem, 4.9vw, 4.4rem);
269            font-weight: 800; letter-spacing: 2px;
270            color: #fff; line-height: 1.16; text-wrap: balance;
271        }
272        .hero h1 .light {
273            font-weight: 200; letter-spacing: 6px; color: #9cc0ff;
274        }
275        .hero-sub {
276            margin: 36px 0 0; max-width: 440px;
277            font-size: .98rem; font-weight: 300; line-height: 2.15;
278            letter-spacing: .6px; color: rgba(255,255,255,.6);
279        }
280        .hero-actions { margin-top: 52px; display: flex; gap: 44px; align-items: center; flex-wrap: wrap; }
281        .hero-link {
282            font-size: .9rem; letter-spacing: 3px; font-weight: 600; color: #fff;
283            padding: 12px 2px; border-bottom: 1px solid rgba(255,255,255,.32);
284            transition: border-color .35s ease, color .35s ease;
285        }
286        .hero-link:hover { border-color: #7cb4ff; color: #7cb4ff; }
287        .hero-media { min-width: 0; }
288        /* LOGO 视频:无边框,单靠圆角与深影(去 chrome) */
289        .hero-video {
290            border-radius: 22px; overflow: hidden;
291            box-shadow: 0 70px 140px -60px rgba(0,0,0,.9), 0 0 0 1px rgba(255,255,255,.06);
292            background: #0b1220;
293        }
294        .hero-video video { display: block; width: 100%; opacity: 0; transition: opacity .8s ease .1s; }
295        .hero-video video.ready { opacity: 1; }
296        /* 微小探索指引(竖线脉冲) */
297        .hero-scroll {
298            position: absolute; left: 50%; bottom: 24px; transform: translateX(-50%);
299            z-index: 2;
299 color: rgba(255,255,255,.34);
300            font-size: 9px; letter-spacing: 4px;
301        }
302        .hero-scroll::after {
303            content: ''; display: block; width: 1px; height: 36px; margin: 10px auto 0;
304            background: linear-gradient(180deg, rgba(255,255,255,.55), transparent);
305            animation: scrollPulse 2.8s cubic-bezier(.16,1,.3,1) infinite;
306        }
307        @keyframes scrollPulse {
308            0% { transform: scaleY(0); transform-origin: top; }
309            45% { transform: scaleY(1); transform-origin: top; }
310            55% { transform: scaleY(1); transform-origin: bottom; }
311            100% { transform: scaleY(0); transform-origin: bottom; }
312        }
313        /* 首屏入场编排:依次淡入上移(物理缓动) */
314        @keyframes heroIn {
315            from { opacity: 0; transform: translateY(28px); }
316            to { opacity: 1; transform: none; }
317        }
318        @keyframes heroFade {
319            from { opacity: 0; }
320            to { opacity: 1; }
321        }
322        .hero-eyebrow, .hero h1, .hero-sub, .hero-actions, .hero-media {
323            animation: heroIn 1s cubic-bezier(.16,1,.3,1) both;
324        }
325        .hero-eyebrow { animation-delay: .06s; }
326        .hero h1 { animation-delay: .2s; }
327        .hero-sub { animation-delay: .36s; }
328        .hero-actions { animation-delay: .52s; }
329        .hero-media { animation-delay: .68s; }
330        .hero-scroll { animation: heroFade 1.6s ease 1.1s both; }
331
332        /* ============ 数据条 ============ */
333        .stats {
334            display: flex; justify-content: center; gap: 20px;
335            padding: 0 24px; margin-top: -58px; position: relative; z-index: 3;
336            flex-wrap: wrap;
337        }
338        .stat {
339            background: var(--card);
340            border-radius: var(--radius-md); padding: 18px 34px;
341            text-align: center; min-width: 140px;
342            box-shadow: var(--shadow-1);
343            transition: transform .3s ease, box-shadow .3s ease;
344        }
345        .stat:hover { transform: translateY(-3px); box-shadow: var(--shadow-2); }
346        .stat .num { font-size: 1.7rem; font-weight: 800; color: var(--ink); letter-spacing: .5px; }
347        .stat .num small { font-size: 1rem; font-weight: 700; color: var(--brand); }
348        /* 粉丝列:精确实时数字(6位数+千分位),字号略收以与邻卡协调 */
349        .stat-fans .num { font-size: 1.28rem; letter-spacing: .2px; }
350        /* 预设 7 字符宽度:入场 0→112,363 全程卡片不再因位数增长而跳宽重排 */
351        .stat-fans .cn { display: inline-block; min-width: 7.2ch; text-align: center; }
352        .stat .label .live {
353            display: inline-block; width: 6px; height: 6px; border-radius: 50%;
354            background: var(--brand); margin-left: 7px; vertical-align: 1px;
355            box-shadow: 0 0 0 0 rgba(59,130,246,.45);
356            animation: livePulse 1.8s ease-out infinite;
357        }
358        @keyframes livePulse {
359            0% { box-shadow: 0 0 0 0 rgba(59,130,246,.45); }
360            70% { box-shadow: 0 0 0 7px rgba(59,130,246,0); }
361            100% { box-shadow: 0 0 0 0 rgba(59,130,246,0); }
362        }
363        .stat .label { font-size: .82rem; color: var(--ink-faint); margin-top: 2px; }
364
365        /* ============ 区块通用 ============ */
366        .section { max-width: 1080px; margin: 0 auto; padding: 100px 24px; }
367        .section-head { text-align: center; margin-bottom: 52px; }
368        .section-eyebrow {
369            display: inline-flex; align-items: center; gap: 12px;
370            font-size: .78rem; font-weight: 700; letter-spacing: 3px;
371            color: var(--brand); text-transform: uppercase; margin-bottom: 14px;
372        }
373        .section-head .section-eyebrow::before, .section-head .section-eyebrow::after {
374            content: ''; width: 26px; height: 1px;
375            background: linear-gradient(90deg, transparent, var(--brand));
376            opacity: .55;
377        }
378        .section-head .section-eyebrow::after { background: linear-gradient(90deg, var(--brand), transparent); }
379        .section-head h2 { font-size: 2rem; font-weight: 800; letter-spacing: .3px; }
380        .section-head p { color: var(--ink-soft); margin-top: 12px; font-size: 1rem; }
381
382        /* ============ 核心功能 ============ */
383        .grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 20px; }
384        .card {
385            background: var(--card); border-radius: var(--radius-lg);
386            padding: 30px 26px;
387            box-shadow: var(--shadow-1);
388            transition: transform .35s cubic-bezier(.16,1,.3,1), box-shadow .35s ease;
389        }
390        .card:hover {
391            transform: translateY(-5px);
392            box-shadow: var(--shadow-2);
393        }
394        .card .ico {
395            width: 48px; height: 48px; border-radius: 14px;
396            display: flex; align-items: center; justify-content: center;
397            font-size: 1.5rem; margin-bottom: 18px;
398        }
399        /* 图标底:统一单一品牌色系(低饱和) */
400        .card .ico { background: linear-gradient(135deg, rgba(59,130,246,.14), rgba(59,130,246,.04)); }
401        .card h3 { font-size: 1.05rem; font-weight: 700; margin-bottom: 8px; }
402        .card p { font-size: .88rem; color: var(--ink-soft); line-height: 1.7; }
403        .card .go {
404            display: inline-block; margin-top: 16px; font-size: .85rem; font-weight: 600;
405            color: var(--brand); transition: color .25s ease;
406        }
407        .card:hover .go { color: var(--brand-deep); }
408
409        /* ============ 视频区 ============ */
410        .video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 20px; }
411        .vcard {
412            background: var(--card); border-radius: var(--radius-lg); overflow: hidden;
413            box-shadow: var(--shadow-1);
414            transition: transform .35s cubic-bezier(.16,1,.3,1), box-shadow .35s ease;
415        }
416        .vcard:hover { transform: translateY(-5px); box-shadow: var(--shadow-2); }
417        .vcard .thumb {
418            position: relative; height: 140px;
419            display: flex; align-items: center; justify-content: center;
420            font-size: 2.6rem; background: #eef1f6;
421        }
422        .vcard .thumb img {
423            position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
424            transition: transform .9s cubic-bezier(.16,1,.3,1);
425        }
426        .vcard:hover .thumb img { transform: scale(1.06); }
427        .vcard .thumb .dur {
428            position: absolute; right: 8px; bottom: 8px; z-index: 1;
429            background: rgba(0,0,0,.72); color: #fff;
430            font-size: .72rem; padding: 2px 8px; border-radius: 999px;
431        }
432        .vcard:nth-child(1) .thumb { background: linear-gradient(135deg, rgba(59,130,246,.14), rgba(139,92,246,.14)); }
433        .vcard:nth-child(2) .thumb { background: linear-gradient(135deg, rgba(245,158,11,.14), rgba(239,68,68,.14)); }
434        .vcard:nth-child(3) .thumb { background: linear-gradient(135deg, rgba(16,185,129,.14), rgba(59,130,246,.14)); }
435        .vcard:nth-child(4) .thumb { background: linear-gradient(135deg, rgba(139,92,246,.14), rgba(236,72,153,.14)); }
436        /* 播放按钮 */
437        .vcard .thumb .play {
438            position: absolute; width: 44px; height: 44px; border-radius: 50%;
439            background: rgba(255,255,255,.9); color: var(--brand);
440            display: flex; align-items: center; justify-content: center;
441            font-size: .95rem; box-shadow: 0 8px 20px -6px rgba(0,0,0,.25);
442            opacity: 0; transform: scale(.8);
443            transition: all .3s ease;
444        }
445        .vcard:hover .thumb .play { opacity: 1; transform: scale(1); }
446        .vcard .body { padding: 18px; }
447        .vcard h4 { font-size: .98rem; font-weight: 700; margin-bottom: 6px; line-height: 1.4; }
448        .vcard .meta { font-size: .8rem; color: var(--ink-faint); }
449        /* 查看更多按钮 */
450        .more-wrap { text-align: center; margin-top: 40px; }
451        .more-btn {
452            display: inline-block; padding: 12px 36px;
453            border: 1px solid var(--line); border-radius: 999px;
454            background: var(--card); color: var(--ink-soft);
455            font-size: .9rem; font-weight: 600; letter-spacing: 2px;
456            box-shadow: var(--shadow-1);
457            transition: all .35s ease;
458        }
459        .more-btn:hover {
460            border-color: var(--brand); color: var(--brand);
461            transform: translateY(-2px); box-shadow: var(--shadow-2);
462        }
463
464        /* ============ 公司理念 ============ */
465        .about-grid {
466            display: grid; grid-template-columns: 1.05fr 1fr; gap: 56px; align-items: start;
467        }
468        .about-text .section-eyebrow { margin-bottom: 12px; }
469        .about-text h2 { font-size: 1.9rem; font-weight: 800; line-height: 1.35; margin-bottom: 18px; }
470        .about-text h2 .grad {
471            color: var(--brand);
472            -webkit-background-clip: initial; background-clip: initial;
473        }
474        .about-text p { color: var(--ink-soft); font-size: .95rem; line-height: 1.9; margin-bottom: 14px; }
475        .about-text p b { color: var(--ink); font-weight: 700; }
476        .about-sign { font-size: .95rem; font-weight: 700; color: var(--ink); margin-top: 20px; }
477        .about-sign span { color: var(--ink-faint); font-weight: 500; font-size: .82rem; margin-left: 8px; }
478        .about-tags { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 24px; }
479        .about-tags .tag {
480            font-size: .8rem; color: var(--ink-soft);
481            background: var(--card);
482            padding: 7px 16px; border-radius: 999px;
483        }
484        .about-tags .tag b { color: var(--brand); font-weight: 700; }
485        .about-cards { display: flex; flex-direction: column; gap: 16px; }
486        .about-card {
487            display: flex; gap: 18px; align-items: flex-start;
488            background: var(--card);
489            border-radius: var(--radius-md); padding: 22px 24px;
490            box-shadow: var(--shadow-1);
491            transition: transform .35s cubic-bezier(.16,1,.3,1), box-shadow .35s ease;
492        }
493        .about-card:hover { transform: translateY(-3px); box-shadow: var(--shadow-2); }
494        .about-card .ico {
495            width: 44px; height: 44px; border-radius: 12px; flex-shrink: 0;
496            display: flex; align-items: center; justify-content: center;
497            font-size: .85rem; font-weight: 800; letter-spacing: 1px;
498            color: var(--brand);
499            background: linear-gradient(135deg, rgba(59,130,246,.14), rgba(59,130,246,.04));
500        }
501        .about-card .ico { background: linear-gradient(135deg, rgba(59,130,246,.14), rgba(59,130,246,.04)); }
502        .about-card h4 { font-size: 1rem; font-weight: 700; margin-bottom: 6px; }
503        .about-card h4 em { font-style: normal;
503 color: var(--ink-faint); font-weight: 500; font-size: .85rem; margin-left: 8px; }
504        .about-card p { font-size: .85rem; color: var(--ink-soft); line-height: 1.75; }
505
506        /* ============ 友商(去边框,白底靠阴影悬浮) ============ */
507        .friends-section { background: transparent; }
508        .friends-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 16px; }
509        .friend {
510            display: flex; align-items: center; gap: 14px;
511            background: var(--card);
512            border-radius: var(--radius-md); padding: 16px 18px;
513            box-shadow: var(--shadow-1);
514            transition: all .35s ease;
515        }
516        .friend:hover {
517            transform: translateY(-3px);
518            background: var(--card);
519            box-shadow: var(--shadow-2);
520        }
521        .friend .logo {
522            width: 44px; height: 44px; border-radius: 12px; flex-shrink: 0;
523            display: flex; align-items: center; justify-content: center;
524            font-size: 1.15rem; font-weight: 800; color: #fff;
525        }
526        .friend:nth-child(1) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
527        .friend:nth-child(2) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
528        .friend:nth-child(3) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
529        .friend:nth-child(4) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
530        .friend:nth-child(5) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
531        .friend:nth-child(6) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
532        .friend:nth-child(7) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
533        .friend:nth-child(8) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
534        .friend:nth-child(9) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
535        .friend:nth-child(10) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
536        .friend:nth-child(11) .logo { background: linear-gradient(160deg, #33415e, #232c42); }
537        .friend .info { flex: 1; min-width: 0; }
538        .friend .name { font-size: .98rem; font-weight: 700; display: flex; align-items: center; gap: 6px; }
539        .friend .name .arrow { color: var(--ink-faint); font-size: .8rem; transition: all .25s ease; }
540        .friend:hover .name .arrow { color: var(--brand); transform: translateX(3px); }
541        .friend .desc {
542            font-size: .8rem; color: var(--ink-faint);
543            white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-top: 2px;
544        }
545
546        /* ============ CTA(极简收束:大字 + 文本链接 + 大留白) ============ */
547        .cta { text-align: center; padding: 120px 24px; background: linear-gradient(170deg, #101c33 0%, #1c1745 100%); color: #fff; }
548        .cta h2 { font-size: clamp(1.9rem, 3.6vw, 2.8rem); font-weight: 800; letter-spacing: 2px; margin-bottom: 18px; }
549        .cta p { color: rgba(255,255,255,.62); margin-bottom: 44px; font-weight: 300; letter-spacing: .6px; }
550        .cta-actions { display: flex; gap: 48px; justify-content: center; flex-wrap: wrap; }
551        .cta .hero-link { border-bottom-color: rgba(255,255,255,.3); }
552
553        /* ============ 页脚 ============ */
554        .footer {
555            position: relative;
556            background: linear-gradient(180deg, #0f172a 0%, #0a0f1e 100%);
557            color: #94a3b8; padding: 64px 48px 32px;
558        }
559        .footer::before {
560            content: ''; position: absolute; top: 0; left: 0; right: 0; height: 1px;
561            background: linear-gradient(90deg, transparent, rgba(59,130,246,.55), rgba(139,92,246,.4), transparent);
562        }
563        .footer h5 { color: #e2e8f0; font-size: .88rem; font-weight: 700; margin-bottom: 18px; letter-spacing: 2px; }
564        .footer-grid {
565            display: grid; grid-template-columns: 2fr 1fr 1fr 1fr; gap: 40px;
566            max-width: 1080px; margin: 0 auto;
567        }
568        .footer a { display: block; color: #8fa0b8; font-size: .86rem; margin-bottom: 12px; transition: color .3s ease; }
569        .footer a:hover { color: #fff; }
570        .footer .brand { font-size: 1.1rem; font-weight: 800; color: #fff; margin-bottom: 14px; letter-spacing: .5px; }
571        .footer .brand .dot {
572            width: 10px; height: 10px; border-radius: 3px; display: inline-block;
573            background: linear-gradient(135deg, var(--brand), #8b5cf6); margin-right: 8px;
574        }
575        .footer .desc { font-size: .85rem; font-weight: 300; line-height: 2; margin-bottom: 20px; color: #7c8ba5; }
576        .footer-bottom {
577            max-width: 1080px; margin: 48px auto 0; padding-top: 26px;
578            border-top: 1px solid rgba(255,255,255,.07);
579            font-size: .76rem; letter-spacing: 1px;
579 color: #5b6a85; text-align: center;
580        }
581
582        /* ============ 动效 ============ */
583        .reveal { opacity: 0; transform: translateY(30px); transition: opacity .9s cubic-bezier(.16,1,.3,1), transform .9s cubic-bezier(.16,1,.3,1); }
584        .reveal.in { opacity: 1; transform: none; }
585        /* 悬浮上浮(置于 .reveal.in 之后,覆盖其 transform:none,保证渐显完成后悬停仍生效) */
586        .stat:hover, .card:hover, .vcard:hover, .friend:hover, .about-card:hover {
587            transform: translateY(-4px);
588            transition: transform .3s cubic-bezier(.16,1,.3,1), box-shadow .3s ease, border-color .3s ease, background .3s ease;
589        }
590
591        /* ============ 导航登录区 ============ */
592        .nav-auth-group { display: flex; gap: 8px; align-items: center; }
593        /* 北京时间时钟(上行时间 / 下行日期+标签) */
594        .nav-clock {
595            display: flex; flex-direction: column; align-items: flex-end;
596            margin-right: 8px; flex-shrink: 0; user-select: none; line-height: 1.35;
597        }
598        .nav-clock .nc-time {
599            font-size: 15.5px; font-weight: 800; letter-spacing: 1.5px;
600            color: var(--ink);
601            font-variant-numeric: tabular-nums;
602        }
603        .nav-clock .nc-row {
604            display: flex; align-items: baseline; gap: 6px;
605            font-style: normal;
606        }
607        .nav-clock .nc-date {
608            font-size: 10.5px; font-weight: 600; letter-spacing: .4px;
609            color: var(--ink-soft);
610            font-variant-numeric: tabular-nums;
611        }
612        .nav-clock .nc-label {
613            font-size: 8.5px; font-weight: 600; letter-spacing: 2px;
614            color: var(--ink-faint);
615        }
616        .nav-auth {
617            padding: 8px 18px; border-radius: 999px;
618            background: rgba(255,255,255,.6); color: var(--ink);
619            border: 1px solid var(--line);
620            font-size: .88rem; font-weight: 600; cursor: pointer;
621            transition: all .25s ease; white-space: nowrap;
622        }
623        .nav-auth:hover { background: #fff; border-color: rgba(59,130,246,.4); }
624        .nav-auth.primary {
625            background: linear-gradient(135deg, var(--brand), #6366f1);
626            border-color: transparent; color: #fff;
627            box-shadow: 0 8px 20px -8px rgba(59,130,246,.5);
628        }
629        .nav-auth.primary:hover { background: linear-gradient(135deg, #2563eb, #4f46e5); color: #fff; }
630        /* 主题切换按钮 */
631        .nav-right-group { display: flex; align-items: center; gap: 10px; }
632        .theme-btn {
633            display: flex; width: 42px; height: 42px; border-radius: 12px; cursor: pointer;
634            background: rgba(255,255,255,.7); border: 1px solid var(--line);
635            font-size: 1.1rem; color: var(--ink); align-items: center; justify-content: center;
636            transition: all .25s ease; flex-shrink: 0;
637        }
638        .theme-btn:hover { background: #fff; border-color: rgba(59,130,246,.4); }
639
640        /* ============ 公告区(去 chrome:无边框白底 + 蓝色accent条) ============ */
641        .beta-announce {
642            display: flex; align-items: center; gap: 14px;
643            max-width: 1080px; margin: 30px auto 0; padding: 15px 24px;
644            background: var(--card);
645            border-radius: var(--radius-md);
646            box-shadow: var(--shadow-1);
647            position: relative;
648        }
649        .beta-announce::before {
650            content: ''; position: absolute; left: 0; top: 18px; bottom: 18px; width: 3px;
651            border-radius: 3px; background: var(--brand);
652        }
653        .beta-announce .ba-icon {
654            width: 40px; height: 40px; border-radius: 12px; flex-shrink: 0;
655            display: flex; align-items: center; justify-content: center; font-size: 1.2rem;
656            background: linear-gradient(135deg, #dbeafe, #eff6ff);
657        }
658        .beta-announce .ba-title {
659            flex-shrink: 0; font-size: .82rem; font-weight: 700; color: var(--brand);
660            background: rgba(59,130,246,.08); border-radius: 999px; padding: 4px 12px;
661        }
662        .beta-announce .ba-track-wrap { flex: 1; overflow: hidden; min-width: 0; }
663        .beta-announce .ba-track { display: inline-block; white-space: nowrap; will-change: transform; }
664        .beta-announce .ba-item {
665            display: inline-block; white-space: nowrap; padding-right: 80px;
666            font-size: .92rem; color: var(--ink-soft);
667        }
668        .beta-announce a { color: var(--brand); text-decoration: underline; }
669        .beta-announce a:hover { color: var(--brand-deep); }
670        @media (max-width: 760px) { .beta-announce { align-items: flex-start; flex-wrap: wrap; } }
671
672        /* ============ 鼠标特效 ============ */
673        .ui-fx-glow {
674            position: fixed; left: -999px; top: -999px; width: 240px; height: 240px;
675            border-radius: 50%; pointer-events: none; z-index: 9997;
676            transform: translate(-50%, -50%);
677            background: radial-gradient(circle, rgba(59,130,246,.18), rgba(139,92,246,.10) 45%, transparent 65%);
678            opacity: 0;
679        }
680
681        /* ============ 细节卫生 ============ */
682        ::selection { background: rgba(59,130,246,.2); }
683        ::-webkit-scrollbar { width: 9px; }
684        ::-webkit-scrollbar-thumb { background: rgba(59,130,246,.3); border-radius: 9px; }
685        ::-webkit-scrollbar-thumb:hover { background: rgba(59,130,246,.5); }
686        ::-webkit-scrollbar-track { background: transparent; }
687        a:focus-visible { outline: 2px solid var(--brand); outline-offset: 3px; border-radius: 6px; }
688        .num, .cn, #siteDays { font-variant-numeric: tabular-nums; }
689        @media (prefers-reduced-motion: reduce) {
690            *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
691            html { scroll-behavior: auto; }
692        }
693
694        /* ============ 导航滚动态 + 汉堡/抽屉 ============ */
695        .nav { transition: background .3s ease, box-shadow .3s ease; }
696        .nav.scrolled { background: rgba(255,255,255,.94); box-shadow: 0 10px 30px -20px rgba(15,23,42,.3); }
697        .nav-burger {
698            display: none; width: 42px; height: 42px; border-radius: 12px; cursor: pointer;
699            background: rgba(255,255,255,.7); border: 1px solid var(--line);
700            font-size: 1.2rem; color: var(--ink); align-items: center; justify-content: center;
701        }
702        /* 扫码:手机端右上角,位置在汉堡按钮右边。
703           默认隐藏 —— ① 桌面端本来就该隐藏 ② 网页端调不动摄像头,只有 APP 里才显示
704           (JS 按 UA 判断,不是 APP 就设成 display:none)。 */
705        .nav-scan {
706            display: none; width: 42px; height: 42px; border-radius: 12px; cursor: pointer;
707            background: rgba(255,255,255,.7); border: 1px solid var(--line);
708            color: var(--ink); align-items: center; justify-content: center;
709            text-decoration: none; -webkit-tap-highlight-color: transparent;
710        }
711        .nav-scan:hover { border-color: rgba(59,130,246,.5); color: var(--brand); }
712        .nav-scan:active { transform: scale(.94); }
713        .nav-scan svg { width: 21px; height: 21px; display: block; }
714        .drawer-mask { display: none; position: fixed; inset: 0; z-index: 9000; background: rgba(15,23,42,.4); -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px); }
715        .drawer {
716            position: fixed; top: 0; right: -320px; width: 300px; max-width: 85vw; height: 100vh;
717            z-index: 9001; background: #fff; box-shadow: -20px 0 60px -30px rgba(0,0,0,.4);
718            transition: right .35s cubic-bezier(.16,1,.3,1);
719            padding: 22px 20px; overflow-y: auto;
720            display: flex; flex-direction: column; gap: 4px;
721        }
722        .drawer.open { right: 0; }
723        .drawer .d-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 14px; }
724        .drawer .d-brand { font-weight: 800; font-size: 1.05rem; display: flex; align-items: center; gap: 8px; color: var(--ink); }
725        .drawer .d-brand .dot { width: 10px; height: 10px; border-radius: 3px; display: inline-block; background: linear-gradient(135deg, var(--brand), #8b5cf6); }
726        .drawer .d-close { width: 36px; height: 36px; border-radius: 10px; border: none; background: var(--bg); font-size: .95rem; cursor: pointer; color: var(--ink); }
727        .drawer .d-close:hover { background: rgba(59,130,246,.1); }
728        .drawer a.d-item { padding: 12px 16px; border-radius: 12px; color: var(--ink-soft); font-size: .98rem; font-weight: 600; }
729        .drawer a.d-item:hover, .drawer a.d-item.active { background: rgba(59,130,246,.1); color: var(--brand); }
730        .drawer .d-auth { margin-top: 14px; padding-top: 16px; border-top: 1px solid var(--line); display: flex; gap: 10px; }
731        .drawer .d-auth a { flex: 1; text-align: center; padding: 11px; border-radius: 999px; font-size: .9rem; font-weight: 600; }
732        .drawer .d-auth .ga { background: var(--bg); color: var(--ink); border: 1px solid var(--line); }
733        .drawer .d-auth .gab { background: linear-gradient(135deg, var(--brand), #6366f1); color: #fff; }
734        @media (max-width: 900px) {
735            .nav-menu { display: none; }
736            .nav-auth-group { display: none; }
737            .nav-clock { display: none; }
738            .nav-burger { display: flex; }
739            .nav-scan { display: flex; }
740        }
741
742        /* ===== Hero 视频质感壳 ===== */
743        .hero-video { position: relative; }
744        .hero-video .hv-tag {
745            position: absolute; top: 20px; left: 50%; transform: translateX(-50%); z-index: 3;
746            display: inline-flex; align-items: center; gap: 6px;
747            padding: 6px 16px; border-radius: 999px; white-space: nowrap;
748            background: rgba(15,23,42,.55); color: rgba(255,255,255,.92);
749            -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
750            border: 1px solid rgba(255,255,255,.16); font-size: .78rem; letter-spacing: .5px;
751        }
752
753        /* ===== 大事记:sticky 之字旅程(参考 nanfu.global 国际版:滚动叙事固定舞台 + 之字年份 + 能量核心行驶) ===== */
754        .jny-section { padding-bottom: 0; }
755        .jny-spacer { position: relative; height: 200vh; } /* JS 会按需覆盖为 run+stageH,保证 sticky 全程固定 */
756        .jny-stage {
757            position: sticky; top: 70px;
758            height: calc(100vh - 130px);
759            min-height: 540px; max-height: 720px;
760            border-radius: 26px; overflow: hidden;
761            background:
762                radial-gradient(760px 420px at 18% 16%, rgba(59,130,246,.16), transparent 60%),
763                radial-gradient(700px 460px at 90% 85%, rgba(139,92,246,.12), transparent 60%),
764                linear-gradient(168deg, #0b1220 0%, #101c33 60%, #161340 100%);
765            color: #fff;
766            box-shadow: 0 60px 120px -50px rgba(8,12,28,.7);
767        }
768        .jny-bg {
769            position: absolute; inset: -20%;
770            background: radial-gradient(320px 320px at 50% 50%, rgba(96,165,250,.16), transparent 65%);
771            opacity: .7; will-change: transform;
772        }
773        /* 年份节点(之字两排) */
774        .jny-node {
775            position: absolute; text-align: center; transform: translate(-50%, -50%);
776            color: rgba(255,255,255,.85); white-space: nowrap; z-index: 3;
777            transition: color .6s ease, opacity .6s ease, text-shadow .6s ease;
778        }
779        .jny-node .jy { display: block; font-size: 1.55rem; font-weight: 800; letter-spacing: 1px; line-height: 1.05; font-variant-numeric: tabular-nums; }
780        .jny-node .jm { display: block; font-size: .62rem; font-weight: 600; letter-spacing: 2.5px; opacity: .85; margin-top: 6px; text-transform: uppercase; }
781        .jny-node.dim { opacity: .3; }
782        .jny-node.lit { color: #fff; text-shadow: 0 0 26px rgba(96,165,250,.65); }
783        .jny-node.lit .jy { color: #9cc0ff; }
784        /* 轨迹 SVG */
785        .jny-svg { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
786        .jny-svg .base { fill: none; stroke: rgba(255,255,255,.10); stroke-width: 1.5; }
787        .jny-svg .go {
788            fill: none; stroke: rgba(96,165,250,.9); stroke-width: 2;
789            filter: drop-shadow(0 0 6px rgba(96,165,250,.65));
790        }
791        /* 能量核心(可拖拽) */
792        .jny-batt {
793            position: absolute; left: 0; top: 0; width: 30px; height: 30px; margin: -15px 0 0 -15px;
794            z-index: 5; pointer-events: none;
795            will-change: transform;
796        }
797        .jny-batt i, .jny-batt b { position: absolute; border-radius: 50%; }
798        .jny-batt i { inset: 0; background: #fff; box-shadow: 0 0 22px rgba(96,165,250,.9), 0 0 46px rgba(59,130,246,.5); }
799        .jny-batt b {
800            inset: -9px; border: 1px solid rgba(96,165,250,.55);
801            animation: jnyPulse 2.4s cubic-bezier(.16,1,.3,1) infinite;
802        }
803        /* 命中热区(隐形放大,便于抓取) */
804        .jny-batt .hit {
805            position: absolute; inset: -34px; border-radius: 50%;
806            pointer-events: auto; cursor: grab;
807            touch-action: none;
808        }
809        .jny-batt:hover .hit { cursor: grab; }
810        .jny-batt.dragging .hit { cursor: grabbing; }
811        .jny-batt.dragging i {
812            box-shadow: 0 0 30px rgba(96,165,250,1), 0 0 70px rgba(59,130,246,.75);
813        }
814        .jny-batt.dragging b { animation-duration: 1s; }
815        @keyframes jnyPulse {
816            0% { transform: scale(.55); opacity: .85; }
817            70% { transform: scale(1.25); opacity: 0; }
818            100% { opacity: 0; }
819        }
820        /* 说明卡 */
821        .jny-cardwrap {
822            position: absolute; left: 50%; bottom: 3%; transform: translateX(-50%);
823            width: min(620px, 84%); height: 176px; z-index: 4;
824        }
825        .jny-card {
826            position: absolute; inset: 0; display: flex; flex-direction: column;
827            justify-content: center; align-items: center; text-align: center;
828            padding: 18px 32px; border-radius: 20px;
829            background: rgba(15,23,42,.55);
830            -webkit-backdrop-filter: blur(14px); backdrop-filter: blur(14px);
831            border: 1px solid rgba(255,255,255,.1);
832            opacity: 0; transform: translateY(18px); pointer-events: none;
833            transition: opacity .6s cubic-bezier(.16,1,.3,1), transform .6s cubic-bezier(.16,1,.3,1);
834        }
835        .jny-card.on { opacity: 1; transform: none; pointer-events: auto; }
836        .jny-tag { font-size: 11px; font-weight: 700; letter-spacing: 3px; color: #9cc0ff; margin-bottom: 8px; }
837        .jny-h { font-size: 1.32rem; font-weight: 800; letter-spacing: .6px; margin-bottom: 8px; color: #fff; }
838        .jny-d { font-size: .9rem; font-weight: 300; line-height: 1.8; color: rgba(255,255,255,.74); max-width: 540px; }
839        /* 进度提示(右上角) */
840        .jny-progress {
841            position: absolute;
841 right: 26px; top: 22px; z-index: 6;
842            display: flex; align-items: center; gap: 10px;
843            font-size: 10px; letter-spacing: 3px; color: rgba(255,255,255,.5);
844        }
845        .jny-progress .bar { width: 120px; height: 2px; background: rgba(255,255,255,.14); border-radius: 2px; overflow: hidden; }
846        .jny-progress .bar i { display: block; height: 100%; width: 0; background: linear-gradient(90deg, #60a5fa, #a78bfa); border-radius: 2px; }
847        /* SVG 轨迹 */
848        .jny-svg .base { fill: none; stroke: rgba(255,255,255,.09); stroke-width: 1.5; }
849        .jny-svg .go {
850            fill: none; stroke: rgba(110,170,255,.95); stroke-width: 2.4; stroke-linecap: round;
851            filter: drop-shadow(0 0 6px rgba(96,165,250,.7));
852        }
853        /* 节点(紧凑适配 11 个) */
854        .jny-node { transform: translate(-50%, -50%); }
855        .jny-node .jy { font-size: 1.35rem; }
856        .jny-node .jm { font-size: .58rem; letter-spacing: 2px; }
857        @media (max-width: 760px) {
858            .jny-spacer { height: 180vh; }
859            .jny-stage { border-radius: 20px; }
860            .jny-cardwrap { bottom: 2%; height: 190px; width: 90%; }
861            .jny-card { padding: 12px 14px; }
862            .jny-h { font-size: 1.02rem; }
863            .jny-d { font-size: .78rem; line-height: 1.65; }
864            .jny-progress { display: none; }
865            /* ⚡ 手机上最要命的一条:原来每张卡片都挂 backdrop-filter: blur(14px),
866               11 张叠在一起,GPU 每帧要合成 11 个模糊层 —— 掉帧的主因。
867               换成不透明度更高的纯色背景,观感几乎没差,开销天差地别。 */
868            .jny-card {
869                -webkit-backdrop-filter: none; backdrop-filter: none;
870                background: rgba(15,23,42,.9);
871            }
872            /* 没轮到的卡片用 visibility 隐藏:opacity:0 仍会被合成,visibility 不会 */
873            .jny-card { visibility: hidden; transition: opacity .35s ease, transform .35s ease, visibility 0s linear .35s; }
874            .jny-card.on { visibility: visible; transition: opacity .35s ease, transform .35s ease, visibility 0s; }
875            /* 轨迹上的发光滤镜在手机上也很贵,去掉 */
876            .jny-svg .go { filter: none; }
877            /* 窄屏三排之字:节点整体缩小,年份压紧,避免"2025"们叠在一起 */
878            .jny-node .jy { font-size: 1.08rem; letter-spacing: .5px; }
879            .jny-node .jm { font-size: .5rem; letter-spacing: 1.4px; margin-top: 4px; }
880            .jny-node.lit { text-shadow: 0 0 14px rgba(96,165,250,.6); }
881        }
882        /* ===== 回到顶部 ===== */
883        .backtop {
884            position: fixed; right: 26px; bottom: 26px; z-index: 5000;
885            width: 46px; height: 46px; border-radius: 50%;
886            background: rgba(255,255,255,.92); color: var(--brand);
887            border: 1px solid var(--line); box-shadow: 0 12px 30px -12px rgba(15,23,42,.4);
888            font-size: 1.15rem; cursor: pointer;
889            opacity: 0; transform: translateY(12px); transition: all .3s ease;
890        }
891        .backtop.show { opacity: 1; transform: none; }
892        .backtop:hover { background: var(--brand); color: #fff; }
893
894        /* ============ 响应式 ============ */
895        @media (max-width: 760px) {
896            .nav { padding: 0 20px; }
897            .nav-menu { display: none; }
898            .hero { padding: 96px 24px 130px; }
899            .hero h1 { font-size: 2.3rem; }
900            .hero-inner { grid-template-columns: 1fr; gap: 44px; }
901            .hero-eyebrow { margin-bottom: 30px; letter-spacing: 5px; }
902            .hero-sub { max-width: none; }
903            .hero-video { max-width: 380px; margin: 0 auto; }
904            .stats { gap: 12px; }
905            .stat { min-width: 120px; padding: 14px 18px; }
906            .section { padding: 84px 20px; }
907            .section-head { margin-bottom: 40px; }
908            .section-head h2 { font-size: 1.72rem; }
909            .about-grid { grid-template-columns: 1fr; gap: 36px; }
910            .friends-grid { grid-template-columns: 1fr; }
911            .footer { padding: 44px 20px 24px; }
912            .footer-grid { grid-template-columns: 1fr; gap: 28px; }
913            .backtop { right: 18px; bottom: 18px; width: 42px; height: 42px; }
914        }
915            [id] { scroll-margin-top: 86px; }
916
917
918/* ===== 「更多功能」下拉(动画与原菜单项完全一致) ===== */
919.nav-more { position: relative; display: flex; align-items: center; }
920.nav-more-btn {
921    position: relative; padding: 10px 14px;
922    font-size: .92rem; font-weight: 500; letter-spacing: .4px;
923    color: var(--ink-soft); background: none; border: none; cursor: pointer;
924    font-family: inherit; white-space: nowrap;
925    transition: color .3s ease;
926}
927.nav-more-btn::after {
928    content: ''; position: absolute; left: 14px; right: 14px; bottom: 2px;
929    height: 1px; background: var(--brand);
930    transform: scaleX(0); transform-origin: left;
931    transition: transform .35s cubic-bezier(.16,1,.3,1);
932}
933.nav-more-btn:hover { color: var(--ink); }
934.nav-more-btn:hover::after { transform: scaleX(1); }
935.nav-more.open .nav-more-btn { color: var(--ink); }
936.nav-more.open .nav-more-btn::after { transform: scaleX(1); }
937.nav-more-btn.active { color: var(--brand); font-weight: 600; }
938.nav-more-btn.active::after { transform: scaleX(1); }
939.nav-more-panel {
940    display: none; position: absolute; top: calc(100% + 12px); right: 0;
941    min-width: 178px; padding: 8px;
942    background: var(--card); border: 1px solid var(--line);
943    border-radius: 14px; box-shadow: 0 18px 44px -20px rgba(15,23,42,.42);
944    z-index: 300;
945}
946.nav-more.open .nav-more-panel { display: block; }
947.nav-more-panel a {
948    display: block; padding: 9px 12px; border-radius: 9px;
949    font-size: .88rem; color: var(--ink-soft); text-decoration: none; white-space: nowrap;
950    transition: background .18s ease, color .18s ease;
951}
952.nav-more-panel a:hover { background: rgba(59,130,246,.1); color: var(--brand); }
953html.dark .nav-more-panel { background: #0f1a30; border-color: rgba(148,163,184,.22); }
954html.dark .nav-more-panel a:hover { background: rgba(59,130,246,.2); color: #fff; }
955
956/* ===== 移动端:也显示这条菜单栏(左右滑动看全部),不再只靠汉堡 ===== */
957@media (max-width: 900px) {
958    .nav { padding: 0 12px; gap: 6px; }
959    .nav-logo { flex: 0 0 auto; margin-left: 0; }
960    .nav-menu {
961        display: flex;
962        flex: 1 1 auto;
963        min-width: 0;
964        justify-content: flex-start;
965        gap: 0;
966        overflow-x: auto;
967        overflow-y: hidden;
968        scrollbar-width: none;
969        -ms-overflow-style: none;
970        -webkit-overflow-scrolling: touch;
971        /* 右侧渐隐,提示还能往右滑 */
972        -webkit-mask-image: linear-gradient(90deg, #000 0, #000 88%, transparent 100%);
973        mask-image: linear-gradient(90deg, #000 0, #000 88%, transparent 100%);
974    }
975    .nav-menu::-webkit-scrollbar { display: none; }
976    .nav-menu > a, .nav-menu .nav-more-btn { padding: 9px 11px; font-size: .86rem; letter-spacing: .2px; }
977    .nav-menu > a::after, .nav-menu .nav-more-btn::after { left: 11px; right: 11px; }
978    .nav-right-group { flex: 0 0 auto; gap: 6px; }
979    .theme-btn { width: 38px; height: 38px; border-radius: 11px; font-size: 1rem; }
980    .nav-burger { flex: 0 0 auto; }
981    /* 下拉面板在手机上居中一点,避免贴边被裁 */
982    .nav-more-panel { right: auto; left: 0; }
983}
984@media (max-width: 420px) {
985    .nav-menu > a, .nav-menu .nav-more-btn { padding: 9px 9px; font-size: .82rem; }
986    .nav-logo .logo-img { height: 28px; }
987}
988
989/* ===== 移动端 / APP 适配增强 ===== */
990/* 1) 安全区:APP 全屏运行时,刘海屏/底部横条不再压住内容 */
991body {
992    padding-left: env(safe-area-inset-left, 0);
993    padding-right: env(safe-area-inset-right, 0);
994}
995.nav { padding-top: env(safe-area-inset-top, 0); }
996.footer { padding-bottom: calc(24px + env(safe-area-inset-bottom, 0px)); }
997.backtop { bottom: calc(18px + env(safe-area-inset-bottom, 0px)); }
998.drawer { padding-top: calc(18px + env(safe-area-inset-top, 0px)); padding-bottom: calc(18px + env(safe-area-inset-bottom, 0px)); }
999.nav-more-panel { max-height: 70vh; overflow-y: auto; }
1000.d-modal, .modal-content, .cam-modal > div, .custom-modal > div {
1001    max-height: 86vh; overflow-y: auto;
1002}
1003
1004/* 2) 触摸体验 */
1005a, button, [onclick], .sort-btn, .d-item {
1006    -webkit-tap-highlight-color: transparent;
1007    touch-action: manipulation;
1008}
1009html { -webkit-text-size-adjust: 100%; }
1010
1011/* 3) 手机上输入框字号统一 16px:
1012      小于 16px 时 iOS/部分安卓 WebView 聚焦会自动放大整页(最常见的不适配感来源) */
1013@media (max-width: 900px) {
1014    input:not([type=checkbox]):not([type=radio]), textarea, select {
1015        font-size: 16px !important;
1016    }
1017    /* 关键操作按钮的最小可点区域(不影响小图标按钮) */
1018    button:not(.d-close):not(.nav-burger):not(.nav-scan):not(.theme-btn):not(.backtop):not(.react-btn):not(.reply-btn):not(.edit-btn):not(.delete-btn):not(.report-btn):not(.sort-btn),
1019    .btn, .hero-btn, .vhero-actions a {
1020        min-height: 38px;
1021    }
1022    /* 表格:已有横向滚动容器的保持,没有的自动兜底 */
1023    .table-wrap, .scroll-x, .stock-table-wrap { -webkit-overflow-scrolling: touch; }
1024    /* 长文本防溢出 */
1025    .comment-content, .bn-reason, .d-item, .pd-tip { word-break: break-word;
1025 overflow-wrap: anywhere; }
1026}
1027
1028/* 4) APP(独立窗口)下:去掉选中高亮与长按菜单的干扰,正文更接近原生 */
1029@media (display-mode: standalone) {
1030    a, button { -webkit-user-select: none; user-select: none; }
1031    .comment-content { -webkit-user-select: text; user-select: text; }
1032    .backtop { display: none; }   /* APP 里用系统返回键更自然 */
1033}
1034
1035/* ===== 手机上「更多功能」下拉的处理 =====
1036   原因:窄屏导航菜单是横向滚动的,而「更多功能」排在菜单最右端,
1037        初始位置在屏幕外;且菜单的 overflow 会把弹出面板裁掉。
1038   处理:手机上隐藏这个下拉(核心功能在 ☰ 抽屉里都有),平板/桌面保持可用。 */
1039@media (max-width: 900px) {
1040    .nav-more { display: none !important; }
1041    .nav-more-panel { display: none !important; }
1042}
1043/* 平板/桌面:面板靠右弹出时留出边距,避免贴边 */
1044@media (min-width: 901px) {
1045    .nav-more-panel { max-width: calc(100vw - 24px); }
1046}
1047
1048
1049/* ===== 评论富文本:粗体 / 斜体 / 行内代码(不动链接颜色) ===== */
1050.comment-content b, .comment-content strong { font-weight: 800; }
1051.comment-content i, .comment-content em { font-style: italic; opacity: .95; }
1052.comment-content code {
1053    padding: 1px 6px; border-radius: 6px;
1054    background: rgba(120,120,120,.14);
1055    font-family: ui-monospace, Consolas, monospace; font-size: .92em;
1056}
1057.md-tip { font-size: .74rem; color: var(--ink-soft, #5a6270); opacity: .7; margin-top: 6px; }
1058.md-tip code { padding: 1px 4px; border-radius: 4px; background: rgba(120,120,120,.14); font-size: .95em; }
1059</style>
1060<link rel="manifest" href="/manifest.json">
1061<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32.png">
1062<link rel="apple-touch-icon" sizes="180x180" href="/images/app-icon-180.png">
1063<meta name="apple-mobile-web-app-capable" content="yes">
1064<meta name="mobile-web-app-capable" content="yes">
1065<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
1066<meta name="apple-mobile-web-app-title" content="NB频道">
1067<meta name="application-name" content="NB频道">
1068<!-- nb-theme: 主题包(国庆 / 科技)—— 必须在页面样式之后加载 -->
1069<link rel="stylesheet" href="../css/themes.css?v=35">
1070<script src="../js/theme.js?v=9"></script>
vendor: 1 bytes, line 1070
1070
1071<script src="../js/turtle-flag.js?v=31" defer></script>
vendor: 1 bytes, line 1071
1071
1072<script src="../js/fourier.js?v=34" defer></script>
vendor: 1 bytes, line 1072
1072
1073<script src="../js/tech-showcase.js?v=29" defer></script>
vendor: 1 bytes, line 1073
1073
1074<script src="../js/theme-extra.js?v=22" defer></script>
1074
1075</head>
1076<body>
1077<!-- NB-LOAN-ALERT -->
1078<div id="nbLoanAlert" style="display:none"></div>
1079<script>
1080(function () {
1081  'use strict';
1082
1083  var BANK_URL = 'bank-Beta.html';
1084
1085  function getSupa() {
1086    if (window.supabaseClient && typeof window.supabaseClient.rpc === 'function')
1087      return window.supabaseClient;
1088    if (window.sb && typeof window.sb.rpc === 'function') return window.sb;
1089    if (window.supabase && typeof window.supabase.rpc === 'function') return window.supabase;
1090    for (var k in window) {
1091      try {
1092        var v = window[k];
1093        if (v && typeof v.rpc === 'function' && typeof v.from === 'function') return v;
1094      } catch (e) {}
1095    }
1096    return null;
1097  }
1098
1099  function myId() {
1100    try {
1101      var u = JSON.parse(localStorage.getItem('nb_user') || 'null');
1102      return (u && (u.id || u.user_id)) || null;
1103    } catch (e) { return null; }
1104  }
1105
1106  function esc(s) {
1107    return String(s == null ? '' : s)
1108      .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
1109      .replace(/"/g, '&quot;');
1110  }
1111
1112  function fmt(n) {
1113    n = Number(n) || 0;
1114    if (n >= 1e8) return (n / 1e8).toFixed(2) + ' 亿';
1115    if (n >= 1e4) return (n / 1e4).toFixed(2) + ' 万';
1116    return String(Math.round(n));
1117  }
1118
1119  /* 三种状态:即将到期(蓝) / 宽限期内(橙) / 已超期(红) */
1120  var STYLE = {
1121    upcoming: { bg: 'linear-gradient(90deg,#1d4ed8,#2563eb)', icon: '\uD83D\uDCE2',          // 📢
1122                title: '贷款即将到期' },
1123    grace:    { bg: 'linear-gradient(90deg,#b45309,#d97706)', icon: '\u26A0\uFE0F',          // ⚠️
1124                title: '贷款逾期提醒' },
1125    overdue:  { bg: 'linear-gradient(90deg,#b91c1c,#dc2626)', icon: '\U0001F6A8',             // 🚨
1126                title: '贷款逾期提醒' }
1127  };
1128
1129  function render(items) {
1130    var box = document.getElementById('nbLoanAlert');
1131    if (!box || !items || !items.length) return;
1132
1133    /* 取最严重的那一条决定整体配色 */
1134    var worst = 'upcoming';
1135    items.forEach(function (it) {
1136      var s = it.status || (Number(it.days_left) <= 0 ? 'overdue' : 'grace');
1137      if (s === 'overdue') worst = 'overdue';
1138      else if (s === 'grace' && worst !== 'overdue') worst = 'grace';
1139    });
1140    var st = STYLE[worst] || STYLE.grace;
1141
1142    var rows = items.map(function (it) {
1143      var status = it.status || (Number(it.days_left) <= 0 ? 'overdue' : 'grace');
1144      var tail;
1145      if (status === 'upcoming') {
1146        tail = '到期余额不足会自动从<b>活期存款</b>扣除,存款不够才会扣余额。';
1147      } else if (status === 'grace') {
1148        tail = '还有 <b>' + (it.days_left != null ? it.days_left : 0) + '</b> 天宽限,'
1149             + '逾期满 3 天将自动从<b>活期存款</b>和<b>余额</b>中扣除(定期存款不动)。';
1150      } else {
1151        tail = '<b>宽限期已过,随时会被强制扣款。</b>';
1152      }
1153      var head = status === 'upcoming'
1154        ? '还有 <b>' + esc(it.due_in_days != null ? it.due_in_days : 0) + '</b> 天到期'
1155        : '已逾期 <b>' + esc(it.overdue_days) + '</b> 天';
1156
1157      return '<div style="margin-top:4px">'
1158           + '<b>' + esc(it.kind) + '</b> ' + head + ','
1159           + '应还 <b>' + fmt(it.owed) + '</b> NB币(本金 ' + fmt(it.principal)
1160           + ' + 利息 ' + fmt(it.interest) + ')。'
1161           + tail
1162           + '</div>';
1163    }).join('');
1164
1165    box.setAttribute('style',
1166      'position:relative;z-index:9999;padding:13px 46px 13px 20px;'
1167      + 'background:' + st.bg + ';color:#fff;font-size:.9rem;line-height:1.75;'
1168      + 'box-shadow:0 3px 14px rgba(0,0,0,.22);'
1169      + 'font-family:system-ui,-apple-system,"Microsoft YaHei",sans-serif;');
1170    box.innerHTML =
1171        '<div style="font-weight:800;font-size:.98rem;letter-spacing:1px">'
1172      + st.icon + ' ' + st.title
1173      + '</div>'
1174      + rows
1175      + '<a href="' + BANK_URL + '" style="display:inline-block;margin-top:9px;'
1176      + 'padding:5px 16px;border-radius:999px;background:rgba(255,255,255,.22);'
1177      + 'color:#fff;text-decoration:none;font-size:.84rem;font-weight:700;'
1178      + 'border:1px solid rgba(255,255,255,.4)">去银行还款 \u2192</a>'
1179      + '<span id="nbLoanAlertX" style="position:absolute;top:9px;right:14px;'
1180      + 'cursor:pointer;font-size:1.1rem;opacity:.75;line-height:1">\u00D7</span>';
1181
1182    var x = document.getElementById('nbLoanAlertX');
1183    if (x) x.onclick = function () { box.style.display = 'none'; };
1184  }
1185
1186  function check() {
1187    var sb = getSupa();
1188    var uid = myId();
1189    if (!sb || !uid) return;
1190    sb.rpc('get_my_loan_alert', { p_user_id: uid }).then(function (r) {
1191      var d = r && r.data;
1192      if (d && d.has_alert && d.items && d.items.length) render(d.items);
1193    }).catch(function (e) {
1194      console.warn('[loan-alert] 查询失败', e);
1195    });
1196  }
1197
1198  if (document.readyState === 'loading') {
1199    document.addEventListener('DOMContentLoaded', function () { setTimeout(check, 900); });
1200  } else {
1201    setTimeout(check, 900);
1202  }
1203})();
1204</script>
1204
1205
1206
1207
1208<!-- 导航 -->
1209<nav class="nav">
1210    <a class="nav-logo" href="index-Beta.html"><img class="logo-img" src="/images/NB频道LOGO.png" alt="NB频道"></a>
1211    <div class="nav-menu">
1212        <a href="index-Beta.html" class="active">首页</a>
1213        <a href="videos-Beta.html">视频</a>
1214        <a href="about-Beta.html">关于</a>
1215        <a href="rules-Beta.html">用户守则</a>
1216        <a href="changelog-Beta.html">更新日志</a>
1217        <a href="product-Beta.html">我的产品</a>
1218        <a href="APP-Beta.html">软件/APP</a>
1219        <a href="#friends">友商</a>
1220        <a class="nav-api" href="/api-sdk/" target="_blank" rel="noopener noreferrer" title="NB频道 API 文档">API<i>↗</i></a>
1221        <div class="nav-more">
1222            <button class="nav-more-btn" type="button">更多功能 ▾</button>
1223            <div class="nav-more-panel">
1224                <a href="stock-Beta.html">📈 虚拟股票</a>
1225                <a href="messages-Beta.html">🔔 消息中心</a>
1226                <a href="chat-Beta.html">👥 好友 / 私信</a>
1227                <a href="bank-Beta.html">🏦 NB银行</a>
1228                <a href="shop-Beta.html">🛍️ NB商店</a>
1229                <a href="backpack-Beta.html">🎒 我的背包</a>
1230                <a href="titles-Beta.html">🏅 称号大全</a>
1231                <a href="achievements-Beta.html">🏆 成就墙</a>
1232                <a href="lottery-Beta.html">📅 签到 · 转盘</a>
1233                <a href="lottery_records-Beta.html">🎰 抽奖记录</a>
1234                <a href="tools-Beta.html">🔧 工具</a>
1235                <a href="product_share-Beta.html">📦 作品分享</a>
1236                <a href="comments-Beta.html">💬 评论区</a>
1237                <a href="ban-Beta.html">🚫 小黑屋</a>
1238                <a href="feedback-Beta.html">🐞 漏洞/建议反馈</a>
1239                <a href="vote-Beta.html">🗳️ 投票中心</a>
1240            </div>
1241        </div>
1242    </div>
1243    <div class="nav-right-group">
1244        <div class="nav-clock" id="navClock" title="北京时间(UTC+8)">
1245            <span class="nc-time" id="bjTime">--:--:--</span>
1246            <span class="nc-row"><i class="nc-date" id="bjDate">----.--.--</i><em class="nc-label">北京时间</em></span>
1247        </div>
1248        <button class="theme-btn" id="themeBtn" aria-label="切换夜间模式">🌙</button>
1249        <div class="nav-auth-group" id="betaAuthGroup">
1250            <a class="nav-auth" id="betaProfileBtn" href="#">👤 个人中心</a>
1251            <a class="nav-auth primary" id="betaAuthBtn" href="#">登录 / 注册</a>
1252        </div>
1253    </div>
1254    <button class="nav-burger" id="navBurger" aria-label="打开菜单">☰</button>
1255    <!-- 扫码:手机端右上角、汉堡按钮的右边。只在 APP 里显示(网页端没有扫码能力)。
1256         图标 = 四个 L 形角 + 中间一条扫描线,跟微信「扫一扫」一个路子。 -->
1257    <a class="nav-scan" id="navScanBtn" href="scan-Beta.html" aria-label="扫一扫" title="扫一扫">
1258        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
1259             stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1260            <path d="M3 8.5V6a3 3 0 0 1 3-3h2.5"/>
1261            <path d="M15.5 3H18a3 3 0 0 1 3 3v2.5"/>
1262            <path d="M21 15.5V18a3 3 0 0 1-3 3h-2.5"/>
1263            <path d="M8.5 21H6a3 3 0 0 1-3-3v-2.5"/>
1264            <path d="M3.2 12h17.6"/>
1265        </svg>
1266    </a>
1267</nav>
1268<script>
1269// ===== 导航「更多功能」下拉 =====
1270(function () {
1271    var box = document.querySelector('.nav-more');
1272    if (!box) return;
1273    var btn = box.querySelector('.nav-more-btn');
1274    btn.addEventListener('click', function (e) { e.stopPropagation(); box.classList.toggle('open'); });
1275    document.addEventListener('click', function () { box.classList.remove('open'); });
1276    document.addEventListener('keydown', function (e) { if (e.key === 'Escape') box.classList.remove('open'); });
1277    var here = (location.pathname.split('/').pop() || '').toLowerCase();
1278    if (here && box.querySelector('.nav-more-panel a[href="' + here + '"]')) btn.classList.add('active');
1279})();
1280
1281// ===== 「扫一扫」入口只在 APP 里显示 =====
1282// 位置:导航栏右上角、汉堡按钮(☰)的右边那个图标。
1283// 网页端(手机浏览器)调不动摄像头 —— 安卓/iOS 都没给网页开放稳定的
1284// 扫码接口,所以这个功能只能靠 APP 里的原生实现(window.NBQr)。
1285//
1286// ⚠️ 判断"在不在 APP 里"要用【UA】,不能用 window.NBQr:
1287//    旧版 APP 里没有这个桥,用 NBQr 判断会把旧版 APP 用户误判成浏览器,
1288//    于是入口被藏起来 —— 他就永远不知道有这个功能,也就不会去更新 APP。
1289//    所以:UA 里有 NBChannelApp 就显示(哪怕他的是旧版),点进去会提示更新。
1290(function () {
1291    var isApp = false;
1292    try { isApp = /NBChannelApp/i.test(navigator.userAgent || ''); } catch (e) {}
1293    if (isApp) return;   // 在 APP 里 —— 右上角的扫码图标保持显示
1294
1295    // 网页端/桌面端:藏掉。(桌面端本来就被 CSS 的媒体查询藏了,这里主要管手机浏览器)
1296    var scanBtn = document.getElementById('navScanBtn');
1297    if (scanBtn) scanBtn.style.display = 'none';
1298})();
1299
1300// ===== 在 APP 里隐藏导航菜单的 emoji =====
1301// 站长要求:APP 的「更多功能」下拉菜单不要 emoji,网页端保留原样。
1302//
1303// APP 是 Capacitor 套壳,加载的就是本站 —— 改 HTML 会两边一起改,
1304// 所以只能按 UA 区分(APP 在 UA 里注入了 NBChannelApp)。
1305// 判断写法与上面「扫一扫」那段保持一致。
1306(function () {
1307    var isApp = false;
1308    try { isApp = /NBChannelApp/i.test(navigator.userAgent || ''); } catch (e) {}
1309    if (!isApp) return;              // 浏览器里 —— emoji 保持原样
1310
1311    // 代理对(U+1F000 以上)+ BMP 符号区 + 变体选择符 + 零宽连接符,连同后面的空格一起剥掉
1312    var LEAD = /^(?:[\u2190-\u21FF\u2300-\u23FF\u2600-\u27BF\u2B00-\u2BFF\uFE0F\u200D]|[\uD83C-\uDBFF][\uDC00-\uDFFF])+\s*/;
1313
1314    function strip() {
1315        try {
1316            var as = document.querySelectorAll('.nav-more-panel a, .drawer a');
1317            // ⚠️ 桌面下拉(.nav-more-panel)在手机上被 CSS 藏了,手机走的是
1318            //    汉堡抽屉(.drawer)。两个都要处理,否则 APP 里会漏。
1319            for (var i = 0; i < as.length; i++) {
1320                var t = as[i].textContent || '';
1321                var n = t.replace(LEAD, '');
1322                if (n !== t) as[i].textContent = n;
1323            }
1324        } catch (e) {}
1325    }
1326    strip();
1327    // 导航如果是后渲染的,补两次
1328    setTimeout(strip, 400);
1329    setTimeout(strip, 1500);
1330})();
1331
1332</script>
1332
1333
1334
1335<!-- Hero -->
1336<header class="hero">
1337    <div class="hero-inner">
1338        <div class="hero-copy">
1339            <span class="hero-eyebrow">NOBOOK CHANNEL · EST. 2026</span>
1340            <h1>热爱理科,<br>与<span class="light">作死</span>同行</h1>
1341            <p class="hero-sub">一个由 UP主「NB搞事局」建立的虚拟公司。化学与物理实验、日常作死、NB币虚拟经济与友商生态——都在这里发生</p>
1342            <div class="hero-actions">
1343                <a class="hero-link" href="#about">开始探索 ↓</a>
1344                <a class="hero-link" href="https://space.bilibili.com/3493259582114264" target="_blank" rel="noopener noreferrer">在 B站关注 ↗</a>
1345            </div>
1346        </div>
1347        <div class="hero-media">
1348            <div class="hero-video">
1349                <!-- LOGO 动画:自托管在仓库 videos/ 下,不再依赖外部图床 -->
1350                <video autoplay muted loop playsinline disablePictureInPicture preload="auto" poster="../videos/nb-logo-poster.jpg"><source src="../videos/nb-logo.mp4" type="video/mp4"></video>
1351            </div>
1352        </div>
1353    </div>
1354    <div class="hero-scroll">SCROLL</div>
1355    <div class="hero-fade"></div>
1356</header>
1357
1358<!-- 数据条 -->
1359<div class="stats">
1360    <div class="stat stat-fans"><div class="num"><span class="cn" id="biliFans" data-to="112363">0</span></div><div class="label">B站粉丝<i class="live" title="实时同步,每30秒自动更新"></i></div></div>
1361    <div class="stat"><div class="num"><span class="cn" data-to="11">0</span></div><div class="label">友商官网</div></div>
1362    <div class="stat"><div class="num"><span class="cn" data-to="0" id="siteDays">0</span></div><div class="label">建站天数</div></div>
1363    <div class="stat"><div class="num"><span class="cn" data-to="11">0</span></div><div class="label">核心功能</div></div>
1364</div>
1365
1366<!-- 公告区(数据从 admin_config 拉取,有公告才显示) -->
1367<div class="beta-announce" id="betaAnnounce" style="display:none;">
1368    <div class="ba-title">公告</div>
1369    <div class="ba-track-wrap"><div class="ba-track" id="baTrack"></div></div>
1370</div>
1371
1372<!-- 公司理念 -->
1373<section class="section" id="about">
1374    <div class="about-grid">
1375        <div class="about-text reveal">
1376            <span class="section-eyebrow">About Us</span>
1377            <h2>一家认真搞笑的<br><span class="grad">虚拟公司</span></h2>
1378            <p>NB频道,全名 <b>NoBook 频道</b>,是 UP主「NB搞事局」创立的虚拟公司。我们一边做化学物理实验、一边经营着属于所有人的数字世界——有股票、有银行、有商店,还有数不清的快乐</p>
1379            <p>我们把「搞事」当作一门严肃的事业:每一个实验都认真讲原理,每一笔虚拟交易都认真清算,每一位用户,都是公司的股东与伙伴</p>
1380            <div class="about-sign">—— NB搞事局<span>创始人 & 唯一的正式员工</span></div>
1381            <div class="about-tags">
1382                <span class="tag">成立 <b>2026.02</b></span>
1383                <span class="tag">创始人 <b>NB搞事局</b></span>
1384                <span class="tag">性质 <b>虚拟公司 · 粉丝全资持股</b></span>
1385                <span class="tag">总部 <b>地球 · 网络</b></span>
1386            </div>
1387        </div>
1388        <div class="about-cards">
1389            <div class="about-card reveal">
1390                <div class="ico">01</div>
1391                <div>
1392                    <h4>使命<em>Mission</em></h4>
1393                    <p>让理科变得好玩——把枯燥的公式变成有趣的实验,把危险的操作变成有分寸的科普</p>
1394                </div>
1395            </div>
1396            <div class="about-card reveal">
1397                <div class="ico">02</div>
1398                <div>
1399                    <h4>愿景<em>Vision</em></h4>
1400                    <p>成为中文互联网上最快乐的理科社区——虚拟但认真,真实且有趣,把「作死」玩成科学与艺术</p>
1401                </div>
1402            </div>
1403            <div class="about-card reveal">
1404                <div class="ico">03</div>
1405                <div>
1406                    <h4>价值观<em>Values</em></h4>
1407                    <p>好奇 · 安全 · 认真 · 好玩——好奇心驱动探索,安全永远是底线,虚拟经济绝不含糊,快乐是最终目的</p>
1408                </div>
1409            </div>
1410        </div>
1411    </div>
1412</section>
1413
1414<!-- 核心功能 -->
1415<section class="section">
1416    <div class="section-head reveal">
1417        <span class="section-eyebrow">Features</span>
1418        <h2>核心功能</h2>
1419        <p>从虚拟炒股到化学工具,NB频道的数字世界应有尽有</p>
1420    </div>
1421    <div class="grid">
1422        <a class="card reveal" href="videos-Beta.html">
1423            <div class="ico">🎬</div><h3>视频</h3>
1424            <p>化学实验、物理趣味与日常作死,最新视频都在这里</p><span class="go">去看看 →</span>
1425        </a>
1426        <a class="card reveal" href="comments-Beta.html">
1427            <div class="ico">💬</div><h3>评论区</h3>
1428            <p>和友商交流,支持图片、@提及与称号展示</p><span class="go">去逛逛 →</span>
1429        </a>
1430        <a class="card reveal" href="stock-Beta.html">
1431            <div class="ico">📈</div><h3>NB虚拟股票</h3>
1432            <p>NB币炒股,市场 8:00-20:00 自主运转</p><span class="go">去炒股 →</span>
1433        </a>
1434        <a class="card reveal" href="chat-Beta.html">
1435            <div class="ico">👥</div><h3>好友私信</h3>
1436            <p>实时聊天,支持发送图片与红包转账</p><span class="go">去聊天 →</span>
1437        </a>
1438        <a class="card reveal" href="product_share-Beta.html">
1439            <div class="ico">🎨</div><h3>作品分享</h3>
1440            <p>上传你的作品,或购买他人的优秀作品</p><span class="go">去看看 →</span>
1441        </a>
1442        <a class="card reveal" href="messages-Beta.html">
1443            <div class="ico">🔔</div><h3>消息中心</h3>
1444            <p>评论回复、点赞与私信通知,不遗漏任何互动</p><span class="go">去看看 →</span>
1445        </a>
1446        <a class="card reveal" href="titles-Beta.html">
1447            <div class="ico">🏅</div><h3>称号</h3>
1448            <p>12 种称号,达成条件自动解锁升级,最高 5 星</p><span class="go">去看看 →</span>
1449        </a>
1450        <a class="card reveal" href="tools-Beta.html"><div class="ico">🔧</div><h3>工具</h3><p>元素周期表、方程式配平、函数图像与加解密</p><span class="go">去使用 →</span></a>
1451        <a class="card reveal" href="bank-Beta.html">
1452            <div class="ico">🏦</div><h3>NB银行</h3>
1453            <p>存钱有利息,贷款可救急,信誉分定额度</p><span class="go">去存钱 →</span>
1454        </a>
1455        <a class="card reveal" href="shop-Beta.html">
1456            <div class="ico">🛍️</div><h3>NB商店</h3>
1457            <p>用 NB币买道具:评论颜色、特效、称号位</p><span class="go">去逛逛 →</span>
1458        </a>
1459        <a class="card reveal" href="backpack-Beta.html">
1460            <div class="ico">🎒</div><h3>我的背包</h3>
1461            <p>管理道具:使用、改色、出售、续期</p><span class="go">去看看 →</span>
1462        </a>
1463        <a class="card reveal" href="achievements-Beta.html">
1464            <div class="ico">🏆</div><h3>我的成就</h3>
1465            <p>完成挑战解锁徽章,奖励 NB币,只发一次</p><span class="go">去看看 →</span>
1466        </a>
1467        <a class="card reveal" href="lottery-Beta.html">
1468            <div class="ico">🎰</div><h3>签到抽奖</h3>
1469            <p>每日签到领 NB币,幸运转盘最高赢 2000</p><span class="go">去试试 →</span>
1470        </a>
1471    </div>
1472</section>
1473
1474<!-- 大事记:sticky 滚动叙事旅程(11 节点全历程,能量核心沿贝塞尔 S 曲线行驶) -->
1475<section class="section jny-section" id="milestone">
1476    <div class="section-head reveal">
1477        <span class="section-eyebrow">Milestones</span>
1478        <h2>大事记</h2>
1479        <p>滚动页面,或直接拖动能量核心——沿这条 S 形轨迹,穿越 NB 频道的成长史</p>
1480    </div>
1481    <div class="jny-spacer" id="jnySpacer">
1482        <div class="jny-stage" id="jnyStage">
1483            <div class="jny-bg" id="jnyBg"></div>
1484            <svg class="jny-svg" id="jnySvg" aria-hidden="true"></svg>
1485            <div class="jny-batt" id="jnyBatt"><i></i><b></b><span class="hit" title="拖动我"></span></div>
1486
1487                        <div class="jny-node" style="left:4%;top:24%;"><span class="jy">2025</span><span class="jm">02.07 · 首发</span></div>
1488            <div class="jny-node" style="left:11.7%;
1488top:58%;"><span class="jy">2025</span><span class="jm">06 · 萌芽</span></div>
1489            <div class="jny-node" style="left:19.3%;top:24%;"><span class="jy">2025</span><span class="jm">09 · 更名</span></div>
1490            <div class="jny-node" style="left:27%;top:58%;"><span class="jy">2025</span><span class="jm">12 · 入圈</span></div>
1491            <div class="jny-node" style="left:34.7%;top:24%;"><span class="jy">2026</span><span class="jm">02 · 建站</span></div>
1492            <div class="jny-node" style="left:42.3%;top:58%;"><span class="jy">2026</span><span class="jm">03 · 成立</span></div>
1493            <div class="jny-node" style="left:50%;top:24%;"><span class="jy">2026</span><span class="jm">03.13 · 十万</span></div>
1494            <div class="jny-node" style="left:57.7%;top:58%;"><span class="jy">2026</span><span class="jm">03.23 · 改名</span></div>
1495            <div class="jny-node" style="left:65.3%;top:24%;"><span class="jy">2026</span><span class="jm">03.23 · 域名</span></div>
1496            <div class="jny-node" style="left:73%;top:58%;"><span class="jy">2026</span><span class="jm">05 · 社区</span></div>
1497            <div class="jny-node" style="left:80.7%;top:24%;"><span class="jy">2026</span><span class="jm">07 · 开市</span></div>
1498            <div class="jny-node" style="left:88.3%;top:58%;"><span class="jy">2026</span><span class="jm">08 · 生态</span></div>
1499            <div class="jny-node" style="left:96%;top:24%;"><span class="jy">2026</span><span class="jm">08.27 · 焕新</span></div>
1500
1501            <div class="jny-cardwrap">
1502                <div class="jny-card">
1503                    <div class="jny-tag">2025.02.07 · 首发</div>
1504                    <h3 class="jny-h">第一条视频点亮频道</h3>
1505                    <p class="jny-d">2025年2月7日,第一条视频发布——没有剪辑技巧,没有漂亮封面,只有一盏酒精灯和一颗作死的心。后来我们才知道,那天点的火,再也没灭过</p>
1506                </div>
1507                <div class="jny-card">
1508                    <div class="jny-tag">2025.06 · 萌芽</div>
1509                    <h3 class="jny-h">第一支试管点燃了频道</h3>
1510                    <p class="jny-d">「NB实验室-作死」发布了第一期化学实验。没有剪辑技巧,没有漂亮封面,只有一支试管、一盏酒精灯和一颗作死的心——但评论区,有人认真看完了</p>
1511                </div>
1512                <div class="jny-card">
1513                    <div class="jny-tag">2025.09 · 更名</div>
1514                    <h3 class="jny-h">「NB搞事局」正式挂牌</h3>
1515                    <p class="jny-d">频道更名为 NB搞事局,正式确立"化学 · 物理 · 作死"三条内容线。第一期化学工具视频发布,粉丝突破一千——我们开始像一家公司那样想问题</p>
1516                </div>
1517                <div class="jny-card">
1518                    <div class="jny-tag">2025.12 · 入圈</div>
1519                    <h3 class="jny-h">NB 圈成形,友商登场</h3>
1520                    <p class="jny-d">陆续结识 AWM、Utw 等第一批友商。第一次联动视频发出,评论区第一次破百——原来"友商"不是商业词,是朋友</p>
1521                </div>
1522                <div class="jny-card">
1523                    <div class="jny-tag">2026.02 · 建站</div>
1524                    <h3 class="jny-h">官网 v1 上线</h3>
1525                    <p class="jny-d">2026.02.17,官网正式建站。虚拟公司终于有了自己的"注册地址"——页面很朴素,更新很勤快;建站天数,从这一天开始计算</p>
1526                </div>
1527                <div class="jny-card">
1528                    <div class="jny-tag">2026.03 · 成立</div>
1529                    <h3 class="jny-h">虚拟公司宣布成立</h3>
1530                    <p class="jny-d">NB搞事局宣布成立虚拟公司,并郑重宣布:全体粉丝为荣誉股东。不发行股份,只发行快乐;不设董事会,只设评论区</p>
1531                </div>
1532                <div class="jny-card">
1533                    <div class="jny-tag">2026.03.13 · 十万</div>
1534                    <h3 class="jny-h">十万粉丝达成</h3>
1535                    <p class="jny-d">2026年3月13日,粉丝突破十万——作死的尽头是科学,科学的尽头是 NB。被十万人看见之后,这句话更像真的了</p>
1536                </div>
1537                <div class="jny-card">
1538                    <div class="jny-tag">2026.03.23 · 改名</div>
1539                    <h3 class="jny-h">频道改名</h3>
1540                    <p class="jny-d">2026年3月23日,频道完成改名——新的名字,新的开始</p>
1541                </div>
1542                <div class="jny-card">
1543                    <div class="jny-tag">2026.03.23 · 域名</div>
1544                    <h3 class="jny-h">拥有 nb-channel.top</h3>
1545                    <p class="jny-d">同一天,虚拟公司拿到属于自己的"门牌号"——nb-channel.top。域名很小,梦想不小</p>
1546                </div>
1547                <div class="jny-card">
1548                    <div class="jny-tag">2026.05 · 社区</div>
1549                    <h3 class="jny-h">评论区与好友系统打通</h3>
1550                    <p class="jny-d">评论区支持图片与 @ 提及,好友私信上线,NB币第一次流通。从"看视频"到"认识人",社区活了起来</p>
1551                </div>
1552                <div class="jny-card">
1553                    <div class="jny-tag">2026.07 · 开市</div>
1554                    <h3 class="jny-h">NB币经济正式开市</h3>
1555                    <p class="jny-d">NB虚拟股票开市、NB银行开业:存钱、贷款、炒股、转账、红包。每天 8:00-20:00 市场自主运转——虚拟,但绝不儿戏</p>
1556                </div>
1557                <div class="jny-card">
1558                    <div class="jny-tag">2026.08 · 生态</div>
1559                    <h3 class="jny-h">十一大功能各就各位</h3>
1560                    <p class="jny-d">NB商店、背包、称号系统、用户主页、成就系统相继上线。从看视频到经营公司,从评论区到用户主页——NB频道成了一个小世界</p>
1561                </div>
1562                <div class="jny-card">
1563                    <div class="jny-tag">2026.08.27 · 焕新</div>
1564                    <h3 class="jny-h">官网 Beta 全新上线</h3>
1565                    <p class="jny-d">今天,我们把"官网"做得更像官网:留白、克制、大标题、慢动画、一条会发光的成长轨迹——这个故事,从这一页重新讲起</p>
1566                </div>
1567            </div>
1568</section>
1569<!-- 热门视频(按播放量 Top6) -->
1570<section class="section" style="padding-top:0;">
1571    <div class="section-head reveal">
1572        <span class="section-eyebrow">Videos</span>
1573        <h2>热门视频</h2>
1574        <p>播放量最高的化学实验、物理趣味与日常作死</p>
1575    </div>
1576    <div class="video-grid">
1577        <a class="vcard reveal" href="https://www.bilibili.com/video/BV1BbRYYeEVG" target="_blank" rel="noopener noreferrer">
1578            <div class="thumb"><img src="https://i0.hdslb.com/bfs/archive/fed19300b4e6f86953319ec59cbd6365b8bba08e.jpg" alt="《 加 钠 · 魔 铜 降 世 》"><span class="dur">2:26</span><span class="play">▶</span></div>
1579            <div class="body"><h4>《 加 钠 · 魔 铜 降 世 》</h4><div class="meta">《逝验室·化学》 · 58.4万播放</div></div>
1580        </a>
1581        <a class="vcard reveal" href="https://www.bilibili.com/video/BV11gNbe4ExW" target="_blank" rel="noopener noreferrer">
1582            <div class="thumb"><img src="https://i2.hdslb.com/bfs/archive/7504c36d5c933726a7ecdcfbb0d6f98085a8dc2f.jpg" alt="《 不 要 让 化 学 老 师 看 到 》"><span class="dur">6:01</span><span class="play">▶</span></div>
1583            <div class="body"><h4>《 不 要 让 化 学 老 师 看 到 》</h4><div class="meta">《逝验室·化学》 · 54.8万播放</div></div>
1584        </a>
1585        <a class="vcard reveal" href="https://www.bilibili.com/video/BV1E9AgetEYe" target="_blank" rel="noopener noreferrer">
1586            <div class="thumb"><img src="https://i1.hdslb.com/bfs/archive/dc83cea0f23b847785bf0caa2306cd86be333bf2.jpg" alt="《 救 人 3 . 0 》"><span class="dur">3:46</span><span class="play">▶</span></div>
1587            <div class="body"><h4>《 救 人 3 . 0 》</h4><div class="meta">《“救”人》 · 44.9万播放</div></div>
1588        </a>
1589        <a class="vcard reveal" href="https://www.bilibili.com/video/BV17fNoetELN" target="_blank" rel="noopener noreferrer">
1590            <div class="thumb"><img src="https://i2.hdslb.com/bfs/archive/b80837f9fdb33f8210616ebe996ebb94bf5bc5de.jpg" alt="《 救 人 》"><span class="dur">3:18</span><span class="play">▶</span></div>
1591            <div class="body"><h4>《 救 人 》</h4><div class="meta">《“救”人》 · 33.2万播放</div></div>
1592        </a>
1593        <a class="vcard reveal" href="https://www.bilibili.com/video/BV1D5zXBcECp" target="_blank" rel="noopener noreferrer">
1594            <div class="thumb"><img src="https://i0.hdslb.com/bfs/archive/2e0b55ba1f902deed1b1706f2ef7cd00490d6354.jpg" alt="《 3 0 0 V 电 池 》"><span class="dur">7:04</span><span class="play">▶</span></div>
1595            <div class="body"><h4>《 3 0 0 V 电 池 》</h4><div class="meta">《逝验室·物理》 · 15.9万播放</div></div>
1596        </a>
1597        <a class="vcard reveal" href="https://www.bilibili.com/video/BV1e6XpYbEzm" target="_blank" rel="noopener noreferrer">
1598            <div class="thumb"><img src="https://i1.hdslb.com/bfs/archive/f7e73d7e98698b82ad7d1e587371088275e02b42.jpg" alt="《 加 钠 小 逝 验 》"><span class="dur">3:34</span><span class="play">▶</span></div>
1599            <div class="body"><h4>《 加 钠 小 逝 验 》</h4><div class="meta">《逝验室·化学》 · 11.6万播放</div></div>
1600        </a>
1601    </div>
1602    <div class="more-wrap reveal">
1603        <a class="more-btn" href="videos-Beta.html">查看全部视频 →</a>
1604    </div>
1605</section>
1606
1607<!-- 友商(单独区块) -->
1608<section class="section friends-section" id="friends">
1609    <div class="section-head reveal">
1610        <span class="section-eyebrow">Partners</span>
1611        <h2>友商</h2>
1612        <p>一起搞事的伙伴们——点击卡片访问他们的官网</p>
1613    </div>
1614    <div class="friends-grid">
1615        <a class="friend reveal" href="https://orgent.pythonanywhere.com/" target="_blank" rel="noopener noreferrer">
1616            <div class="logo">O</div>
1617            <div class="info">
1618                <div class="name">Orgent <span class="arrow">↗</span></div>
1619                <div class="desc">B站已注销 · 仅留念</div>
1620            </div>
1621        </a>
1622        <a class="friend reveal" href="https://45d.cn/AWM/" target="_blank" rel="noopener noreferrer">
1623            <div class="logo">A</div>
1624            <div class="info">
1625                <div class="name">AWM <span class="arrow">↗</span></div>
1626                <div class="desc">友商官网</div>
1627            </div>
1628        </a>
1629        <a class="friend reveal" href="https://fafat.uuk.pp.ua/" target="_blank" rel="noopener noreferrer">
1630            <div class="logo">F</div>
1631            <div class="info">
1632                <div class="name">Fafat <span class="arrow">↗</span></div>
1633                <div class="desc">友商官网</div>
1634            </div>
1635        </a>
1636        <a class="friend reveal" href="https://nb-qx.top/" target="_blank" rel="noopener noreferrer">
1637            <div class="logo">L</div>
1638            <div class="info">
1639                <div class="name">Lemon(齐喜) <span class="arrow">↗</span></div>
1640                <div class="desc">友商官网</div>
1641            </div>
1642        </a>
1643        <a class="friend reveal" href="https://gaohantu.cn/" target="_blank" rel="noopener noreferrer">
1644            <div class="logo">G</div>
1645            <div class="info">
1646                <div class="name">GaoHanTu <span class="arrow">↗</span></div>
1647                <div class="desc">友商官网</div>
1648            </div>
1649        </a>
1650        <a class="friend reveal" href="https://utw.pages.dev" target="_blank" rel="noopener noreferrer">
1651            <div class="logo">U</div>
1652            <div class="info">
1653                <div class="name">Utw <span class="arrow">↗</span></div>
1654                <div class="desc">友商官网</div>
1655            </div>
1656        </a>
1657        <a class="friend reveal" href="https://pipetrainingcamp.github.io/" target="_blank" rel="noopener noreferrer">
1658            <div class="logo">P</div>
1659            <div class="info">
1660                <div class="name">PTC(GoodPTC) <span class="arrow">↗</span></div>
1661                <div class="desc">友商官网</div>
1662            </div>
1663        </a>
1664        <a class="friend reveal" href="https://yoyo-user-awa.github.io/UVS/" target="_blank" rel="noopener noreferrer">
1665            <div class="logo">V</div>
1666            <div class="info">
1667                <div class="name">UVS <span class="arrow">↗</span></div>
1668                <div class="desc">友商官网</div>
1669            </div>
1670        </a>
1671        <a class="friend reveal" href="https://3f7ceb4e.pinit.eth.limo/" target="_blank" rel="noopener noreferrer">
1672            <div class="logo">
1672W</div>
1673            <div class="info">
1674                <div class="name">UwLS <span class="arrow">↗</span></div>
1675                <div class="desc">友商官网</div>
1676            </div>
1677        </a>
1678        <a class="friend reveal" href="https://45d.cn/nb-og.top/" target="_blank" rel="noopener noreferrer">
1679            <div class="logo">Og</div>
1680            <div class="info">
1681                <div class="name">Oganesson <span class="arrow">↗</span></div>
1682                <div class="desc">友商官网</div>
1683            </div>
1684        </a>
1685        <a class="friend reveal" href="https://78439049.pinit.eth.limo/" target="_blank" rel="noopener noreferrer">
1686            <div class="logo">S</div>
1687            <div class="info">
1688                <div class="name">SOT <span class="arrow">↗</span></div>
1689                <div class="desc">友商官网</div>
1690            </div>
1691        </a>
1692    </div>
1693</section>
1694
1695<!-- CTA -->
1696<div class="cta">
1697    <h2>加入 NB 频道的数字世界</h2>
1698    <p>关注 B站、参与讨论、投资公司、购买道具——总有一款适合你</p>
1699    <div class="cta-actions">
1700        <a class="hero-link" href="https://space.bilibili.com/3493259582114264" target="_blank" rel="noopener noreferrer">在 B站关注 ↗</a>
1701        <a class="hero-link" href="comments-Beta.html">进入评论区 ↓</a>
1702    </div>
1703</div>
1704<!-- 页脚 -->
1705<footer class="footer">
1706    <div class="footer-grid">
1707        <div>
1708            <div class="brand"><img class="logo-img" src="/images/NB频道LOGO.png" alt="NB频道"><span class="logo-text">NB频道</span></div>
1709            <p class="desc">NB频道(NoBook频道)——热爱理科(和作死)的 UP主建立的虚拟公司。化学物理实验、日常作死、虚拟经济,一个有趣的数字世界</p>
1710        </div>
1711        <div>
1712            <h5>功能</h5>
1713            <a href="stock-Beta.html">虚拟股票</a><a href="bank-Beta.html">NB银行</a><a href="comments-Beta.html">评论区</a><a href="shop-Beta.html">NB商店</a><a href="tools-Beta.html">工具</a><a href="titles-Beta.html">称号系统</a><a href="feedback-Beta.html">漏洞/建议反馈</a>
1714        </div>
1715        <div>
1716            <h5>友商</h5>
1717            <a href="https://orgent.pythonanywhere.com/" target="_blank" rel="noopener noreferrer">Orgent(已注销)</a>
1718            <a href="https://45d.cn/AWM/" target="_blank" rel="noopener noreferrer">AWM</a>
1719            <a href="https://fafat.uuk.pp.ua/" target="_blank" rel="noopener noreferrer">Fafat</a>
1720            <a href="https://nb-qx.top/" target="_blank" rel="noopener noreferrer">Lemon(齐喜)</a>
1721            <a href="https://gaohantu.cn/" target="_blank" rel="noopener noreferrer">GaoHanTu</a>
1722            <a href="https://utw.pages.dev" target="_blank" rel="noopener noreferrer">Utw</a>
1723            <a href="https://pipetrainingcamp.github.io/" target="_blank" rel="noopener noreferrer">PTC(GoodPTC)</a>
1724            <a href="https://yoyo-user-awa.github.io/UVS/" target="_blank" rel="noopener noreferrer">UVS</a>
1725            <a href="https://3f7ceb4e.pinit.eth.limo/" target="_blank" rel="noopener noreferrer">UwLS</a>
1726            <a href="https://45d.cn/nb-og.top/" target="_blank" rel="noopener noreferrer">Oganesson</a>
1727            <a href="https://78439049.pinit.eth.limo/" target="_blank" rel="noopener noreferrer">SOT</a>
1728        </div>
1729        <div>
1730            <h5>联系</h5>
1731            <a href="/cdn-cgi/l/email-protection#b2dcd0d1dad3dcdcd7def28384819cd1dddf"><span class="__cf_email__" data-cfemail="c1afa3a2a9a0afafa4ad81f0f7f2efa2aeac">[email&#160;protected]</span></a><a href="https://space.bilibili.com/3493259582114264" target="_blank" rel="noopener noreferrer">B站:NB搞事局</a><a href="https://nb-channel.top" target="_blank" rel="noopener noreferrer">nb-channel.top</a>
1732        </div>
1733    </div>
1734    <div class="footer-bottom">© 2026 NB频道 · 虚拟公司 · 制作:NB搞事局 · 由 GitHub Pages / Cloudflare / PythonAnywhere 提供支持</div><div style="margin-top:8px;font-size:0.78rem;opacity:.72;line-height:1.9;">
1734本站所有内容(页面代码 · 图文 · 数据)版权归 <b>NB频道</b> 所有,未经书面授权禁止复制、镜像或部署到其他域名</div>
1735</footer>
1736
1737<!-- 移动端抽屉导航 -->
1738<div class="drawer-mask" id="drawerMask"></div>
1739<aside class="drawer" id="drawer">
1740    <div class="d-head">
1741        <span class="d-brand"><img class="logo-img" src="/images/NB频道LOGO.png" alt="NB频道"><span class="logo-text">NB频道</span></span>
1742        <button class="d-close" id="drawerClose" aria-label="关闭菜单">✕</button>
1743    </div>
1744        <a class="d-item" href="vote-Beta.html">投票中心</a>
1745        <a class="d-item" href="stock-Beta.html">虚拟股票</a>
1746        <a class="d-item" href="messages-Beta.html">消息中心</a>
1747        <a class="d-item" href="chat-Beta.html">好友 / 私信</a>
1748        <a class="d-item" href="bank-Beta.html">NB银行</a>
1749        <a class="d-item" href="shop-Beta.html">NB商店</a>
1750        <a class="d-item" href="backpack-Beta.html">我的背包</a>
1751        <a class="d-item" href="titles-Beta.html">称号大全</a>
1752        <a class="d-item" href="achievements-Beta.html">成就墙</a>
1753        <a class="d-item" href="lottery-Beta.html">签到 · 转盘</a>
1754        <a class="d-item" href="lottery_records-Beta.html">抽奖记录</a>
1755        <a class="d-item" href="tools-Beta.html">工具</a>
1756        <a class="d-item" href="product_share-Beta.html">作品分享</a>
1757        <a class="d-item" href="comments-Beta.html">评论区</a>
1758        <a class="d-item" href="ban-Beta.html">小黑屋</a>
1759        <a class="d-item" href="feedback-Beta.html">漏洞/建议反馈</a>
1760    <div class="d-auth">
1761        <a class="ga" id="dProfileBtn" href="#">个人中心</a>
1762        <a class="gab" id="dAuthBtn" href="#">登录 / 注册</a>
1763    </div>
1764</aside>
1765
1766<!-- 回到顶部 -->
1767<button class="backtop" id="backTop" aria-label="回到顶部">↑</button>
vendor: 110 bytes, lines 1767-1769
1767
1768
1769<script data-cfasync="false" src="/cdn-cgi/scripts/5c5dd728/cloudflare-static/email-decode.min.js"></script>
1769<script>
1770    // 建站天数:从 2026-02-17(本地时区)起算日历日差,写入 data-to 由 count-up 动画填充
1771    (function () {
1772        var start = new Date(2026, 1, 17);
1773        var now = new Date();
1774        start.setHours(0, 0, 0, 0);
1775        now.setHours(0, 0, 0, 0);
1776        var days = Math.round((now - start) / 86400000);
1777        var el = document.getElementById('siteDays');
1778        if (el && days > 0) el.setAttribute('data-to', days);
1779    })();
1780    // 滚动渐显(带轻微错开,更有层次)
1781    var io = new IntersectionObserver(function (es) {
1782        es.forEach(function (e) {
1783            if (e.isIntersecting) {
1784                var i = Array.prototype.indexOf.call(e.target.parentNode.children, e.target);
1785                e.target.style.transitionDelay = Math.min(i * 50, 250) + 'ms';
1786                e.target.classList.add('in');
1787                io.unobserve(e.target);
1788            }
1789        });
1790    }, { threshold: 0.1 });
1791    document.querySelectorAll('.reveal').forEach(function (el) { io.observe(el); });
1792
1793    // ===== 登录区绑定(导航 + 移动端抽屉两处共用) =====
1794    function bindAuthPair(pbEl, abEl) {
1795        if (!pbEl && !abEl) return;
1796        var storedU = null;
1797        try { storedU = JSON.parse(localStorage.getItem('nb_user')); } catch (e) {}
1798        if (storedU && storedU.id) {
1799            if (abEl) {
1800                abEl.textContent = '登出账号';
1801                abEl.onclick = function () { localStorage.removeItem('nb_user'); localStorage.removeItem('nb_session'); location.reload(); };
1802            }
1803            if (pbEl) {
1804                pbEl.textContent = '👤 ' + (storedU.username || storedU.name || '个人中心');
1805                pbEl.href = 'profile-Beta.html';
1806            }
1807        } else {
1808            var loginHref = 'login-Beta.html?redirect=' + encodeURIComponent(location.href);
1809            if (abEl) { abEl.href = loginHref; abEl.textContent = '登录 / 注册'; }
1810            if (pbEl) { pbEl.href = loginHref; pbEl.textContent = '👤 个人中心'; }
1811        }
1812    }
1813    bindAuthPair(document.getElementById('betaProfileBtn'), document.getElementById('betaAuthBtn'));
1814    bindAuthPair(document.getElementById('dProfileBtn'), document.getElementById('dAuthBtn'));
1815
1816    // ===== 公告区:从 admin_config 拉取,单行无缝滚动,链接可点击,无公告隐藏 =====
1817    // 注册为回调:supabase-js 由本段末尾异步加载,就绪后才执行——绝不阻塞首屏渲æ
1817Ÿ“
1818    function initAnnounce() {
1819        var bar = document.getElementById('betaAnnounce');
1820        if (!bar || !window.supabase) return;
1821        function esc(s) {
1822            return String(s).replace(/[&<>"']/g, function (m) {
1823                return { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[m];
1824            });
1825        }
1826        function linkify(s) {
1827            return s.replace(/(https?:\/\/[^\s<>"']+)/g, '<a href="$1" target="_blank" rel="noopener noreferrer" style="color:var(--brand); text-decoration:underline;">$1</a>');
1828        }
1829        var client = window.__nbSupabase || supabase.createClient('https://pbaafgjkwdbwcmsikcmg.supabase.co', 'sb_publishable_tv7YVJEisnvs3hvU8ImYUw_b0p6bmRg');
1830        window.__nbSupabase = client;
1831        client.from('admin_config').select('value').eq('key', 'announcement').maybeSingle()
1832            .then(function (res) {
1833                var raw = (res && res.data && res.data.value) ? String(res.data.value).trim() : '';
1834                if (!raw) return;
1835                var track = document.getElementById('baTrack');
1836                if (!track) return;
1837                bar.style.display = 'flex';   // 先显示再测量(隐藏时 offsetWidth 恒为 0)
1838                var one = '<span class="ba-item">' + linkify(esc(raw)) + '</span>';
1839                track.innerHTML = one;
1840                var item = track.querySelector('.ba-item');
1841                var itemW = item ? item.offsetWidth : 0;
1842                if (itemW > 0) {
1843                    var wrapEl = bar.querySelector('.ba-track-wrap');
1844                    var barW = wrapEl ? wrapEl.clientWidth : 0;
1845                    var copies = Math.max(2, Math.ceil(barW / itemW) + 1);
1846                    var all = '';
1847                    for (var ci = 0; ci < copies; ci++) all += '<span class="ba-item">' + linkify(esc(raw)) + '</span>';
1848                    track.innerHTML = all;
1849                    var dur = Math.max(8, itemW / 45);
1850                    if (track.animate) {
1851                        track.animate(
1852                            [{ transform: 'translateX(0)' }, { transform: 'translateX(-' + itemW + 'px)' }],
1853                            { duration: dur * 1000, iterations: Infinity, easing: 'linear' }
1854                        );
1855                    }
1856                }
1857            })
1858            .catch(function () {});
1859    }
1860    (window.__supaCallbacks = window.__supaCallbacks || []).push(initAnnounce);
1861
1862    // ===== supabase-js 异步加载器:双源容错(jsDelivr → unpkg),全失败则静默放弃 =====
1863    (function () {
1864        var cbs = window.__supaCallbacks || [];
1865        if (!cbs.length) return;
1866        var srcs = [
1867            'https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2',
1868            'https://unpkg.com/@supabase/supabase-js@2'
1869        ];
1870        var i = 0;
1871        function fire() {
1872            cbs.forEach(function (f) { try { f(); } catch (e) {} });
1873        }
1874        function load() {
1875            if (i >= srcs.length) return;
1876            var s = document.createElement('script');
1877            s.async = true;
1878            s.src = srcs[i++];
1879            s.onload = fire;
1880            s.onerror = load;      // 当前源失败/被墙 → 自动换下一个
1881            document.head.appendChild(s);
1882        }
1883        load();
1884    })();
1885
1886    // ===== 鼠标/触摸特效:跟随光晕 + 点击波纹与彩色粒子(鼠标 + 触屏都启用) =====
1887    (function () {
1888        var mqFine = window.matchMedia ? window.matchMedia('(pointer: fine)') : null;
1889        var isFine = !!(mqFine && mqFine.matches);
1890        var canTouch = ('ontouchstart' in window) ||
1891            !!(window.matchMedia && window.matchMedia('(pointer: coarse)').matches);
1892        var glow = document.createElement('div');
1893        glow.className = 'ui-fx-glow';
1894        document.body.appendChild(glow);
1895        var gx = -999, gy = -999, tx = -999, ty = -999;
1896        var colors = ['#3b82f6', '#8b5cf6', '#6366f1', '#06b6d4', '#f59e0b'];
1897        var lastTouch = 0, glowTimer = null;
1898
1899        // 光晕跟手:有精确指针(鼠标)就跟鼠标,否则跟手指
1900        if (isFine) {
1901            document.addEventListener('mousemove', function (e) { tx = e.clientX; ty = e.clientY; });
1902            document.addEventListener('mouseenter', function () { glow.style.opacity = '1'; });
1903            document.addEventListener('mouseleave', function () { glow.style.opacity = '0'; });
1904        }
1905        if (canTouch) {
1906            document.addEventListener('touchstart', function (e) {
1907                var t = e.touches[0]; if (!t) return;
1908                tx = t.clientX; ty = t.clientY;
1909                if (gx < -100) { gx = tx; gy = ty; }   // 首次别从屏幕角落滑过来
1910                glow.style.opacity = '1';
1911                if (glowTimer) { clearTimeout(glowTimer); glowTimer = null; }
1912                lastTouch = Date.now();
1913                burst(t.clientX, t.clientY, 6, 0.75);
1914            }, { passive: true });
1915            document.addEventListener('touchmove', function (e) {
1916                var t = e.touches[0]; if (!t) return;
1917                tx = t.clientX; ty = t.clientY;
1918                glow.style.opacity = '1';
1919            }, { passive: true });
1920            document.addEventListener('touchend', function () {
1921                if (glowTimer) clearTimeout(glowTimer);
1922                glowTimer = setTimeout(function () { glow.style.opacity = '0'; }, 380);
1923            }, { passive: true });
1924            document.addEventListener('touchcancel', function () { glow.style.opacity = '0'; }, { passive: true });
1925        }
1926
1927        var lx = -1e9, ly = -1e9;
1928        (function loop() {
1929            var k = isFine ? 0.14 : 0.32;   // 手指跟得紧一些
1930            gx += (tx - gx) * k;
1931            gy += (ty - gy) * k;
1932            // 静止时跳过写入:避免每帧重复改 style 白白触发样式计算
1933            if (Math.abs(gx - lx) > 0.2 || Math.abs(gy - ly) > 0.2) {
1934                glow.style.left = gx + 'px';
1935                glow.style.top = gy + 'px';
1936                lx = gx; ly = gy;
1937            }
1938            requestAnimationFrame(loop);
1939        })();
1940
1941        // ===== 点击波纹 + 彩色粒子 =====
1942        function burst(x, y, count, scale) {
1943            var ring = document.createElement('div');
1944            ring.style.cssText = 'position:fixed; left:' + x + 'px; top:' + y + 'px; width:12px; height:12px; border:2px solid rgba(59,130,246,.75); border-radius:50%; pointer-events:none; z-index:9999; transform:translate(-50%,-50%);';
1945            document.body.appendChild(ring);
1946            setTimeout(function () { if (ring.parentNode) ring.remove(); }, 900);
1947            try {
1948                ring.animate([
1949                    { transform: 'translate(-50%,-50%) scale(1)', opacity: 0.9 },
1950                    { transform: 'translate(-50%,-50%) scale(' + (5.5 * scale) + ')', opacity: 0 }
1951                ], { duration: 600, easing: 'ease-out', fill: 'forwards' });
1952            } catch (err) { ring.remove(); }
1953            for (var i = 0; i < count; i++) {
1954                var p = document.createElement('div');
1955                var size = 4 + Math.random() * 6;
1956                var ang = Math.random() * Math.PI * 2;
1957                var dist = (28 + Math.random() * 52) * scale;
1958                p.style.cssText = 'position:fixed; left:' + x + 'px; top:' + y + 'px; width:' + size + 'px; height:' + size + 'px; border-radius:50%; background:' + colors[i % colors.length] + '; pointer-events:none; z-index:9999;';
1959                document.body.appendChild(p);
1960                var dx = Math.cos(ang) * dist, dy = Math.sin(ang) * dist;
1961                var dur = 500 + Math.random() * 250;
1962                setTimeout(function (el) {
1963                    return function () { if (el.parentNode) el.remove(); };
1964                }(p), dur + 250);
1965                try {
1966                    p.animate([
1967                        { transform: 'translate(0,0) scale(1)', opacity: 1 },
1968                        { transform: 'translate(' + dx + 'px,' + dy + 'px) scale(0.1)', opacity: 0 }
1969                    ], { duration: dur, easing: 'cubic-bezier(0.15,0.8,0.25,1)', fill: 'forwards' });
1970                } catch (err) { p.remove(); }
1971            }
1972        }
1973        // 鼠标点击
1974        document.addEventListener('click', function (e) {
1975            if (Date.now() - lastTouch < 800) return;   // 触摸后浏览器会补发 click,去重
1976            burst(e.clientX, e.clientY, 8, 1);
1977        });
1978    })();
1979
1980    // ===== 大事记:sticky 旅程驱动 v3(贝塞尔 S 曲线 + 能量核心可拖拽:拖动反向驱动节点/卡片/光带/滚动条) =====
1981    (function () {
1982        var stage = document.getElementById('jnyStage');
1983        var spacer = document.getElementById('jnySpacer');
1984        var batt = document.getElementById('jnyBatt');
1985        var svg = document.getElementById('jnySvg');
1986        if (!stage || !spacer || !batt || !svg) return;
1987        var nodes = Array.prototype.slice.call(stage.querySelectorAll('.jny-node'));
1988        var cards = Array.prototype.slice.call(stage.querySelectorAll('.jny-card'));
1989        var bgEl = document.getElementById('jnyBg');
1990        var n = nodes.length;
1991        var pts = [];
1992        var basePath = null, goPathEl = null, totalLen = 1;
1993        var vh = 0, spacerTopAbs = 0, runLen = 1;
1994        var ticking = false, dragging = false, suppressScroll = false;
1995        var stageRect = null;
1996        var NS = 'http://www.w3.org/2000/svg';
1997
1998        function smoothD(p) {
1999            var d = 'M' + p[0].x.toFixed(1) + ',' + p[0].y.toFixed(1);
2000            for (var i = 1; i < p.length; i++) {
2001                var a = p[i - 1], b = p[i];
2002                var pm = p[i - 2] || a, pn = p[i + 1] || b;
2003                var c1x = a.x + (b.x - pm.x) / 6, c1y = a.y + (b.y - pm.y) / 6;
2004                var c2x = b.x - (pn.x - a.x) / 6, c2y = b.y - (pn.y - a.y) / 6;
2005                d += ' C ' + c1x.toFixed(1) + ',' + c1y.toFixed(1) + ' ' + c2x.toFixed(1) + ',' + c2y.toFixed(1) + ' ' + b.x.toFixed(1) + ',' + b.y.toFixed(1);
2006            }
2007            return d;
2008        }
2009        // ⚡ 性能关键:路径点一次性采样成数组,之后一律查表插值。
2010        //    原来每帧都调 basePath.getPointAtLength() —— 那是要二分查找算弧长的重活,
2011        //    手机上每滚动一帧调一次,直接把帧率拖垮。
2012        //    采样是构建时做一次的固定开销,之后每帧只剩几次数组取值和乘法。
2013        var SAMPLE_N = 200;
2014        var sx = null, sy = null;
2015        function buildSamples() {
2016            sx = new Float32Array(SAMPLE_N + 1);
2017            sy = new Float32Array(SAMPLE_N + 1);
2018            for (var i = 0; i <= SAMPLE_N; i++) {
2019                var q = basePath.getPointAtLength(totalLen * i / SAMPLE_N);
2020                sx[i] = q.x;
2021                sy[i] = q.y;
2022            }
2023        }
2024        // 按弧长取点(线性插值,误差远小于一个像素,肉眼无感)
2025        function pointAt(dist) {
2026            if (!sx) return { x: 0, y: 0 };
2027            var t = dist / totalLen * SAMPLE_N;
2028            if (!(t > 0)) return { x: sx[0], y: sy[0] };
2029            if (t >= SAMPLE_N) return { x: sx[SAMPLE_N], y: sy[SAMPLE_N] };
2030            var i = t | 0, fr = t - i;
2031            return { x: sx[i] + (sx[i + 1] - sx[i]) * fr,
2032                     y: sy[i] + (sy[i + 1] - sy[i]) * fr };
2033        }
2034
2035        var nodesLen = [];   // 各节点在曲线上的弧长位置(用于"到弯道顶点才切换")
2036        function nearestLen(pt) {
2037            if (!sx) return 0;
2038            var best = 0, bestD2 = 1e18;
2039            for (var i = 0; i <= SAMPLE_N; i++) {
2040                var dx = sx[i] - pt.x, dy = sy[i] - pt.y;
2041                var d2 = dx * dx + dy * dy;
2042                if (d2 < bestD2) { bestD2 = d2; best = i / SAMPLE_N; }
2043            }
2044            return best * totalLen;
2045        }
2046        function buildSvg() {
2047            var r = stage.getBoundingClientRect();
2048            var W = r.width, H = r.height;
2049            svg.setAttribute('viewBox', '0 0 ' + W + ' ' + H);
2050            svg.setAttribute('preserveAspectRatio', 'none');
2051            pts = nodes.map(function (nd) {
2052                var nr = nd.getBoundingClientRect();
2053                return { x: nr.left - r.left + nr.width / 2, y: nr.top - r.top + nr.height / 2 };
2054            });
2055            var d = smoothD(pts);
2056            svg.innerHTML = '';
2057            basePath = document.createElementNS(NS, 'path');
2058            basePath.setAttribute('class', 'base');
2059            basePath.setAttribute('d', d);
2060            svg.appendChild(basePath);
2061            goPathEl = document.createElementNS(NS, 'path');
2062            goPathEl.setAttribute('class', 'go');
2063            goPathEl.setAttribute('d', d);
2064            svg.appendChild(goPathEl);
2065            totalLen = basePath.getTotalLength ? basePath.getTotalLength() : 1;
2066            if (goPathEl) {
2067                goPathEl.style.strokeDasharray = totalLen;
2068                goPathEl.style.strokeDashoffset = totalLen;
2069            }
2070            // 记录每个节点在曲线上的弧长位置(核心驶过该位置即"到顶点")
2071            buildSamples();      // ⚡ 必须排在 nodesLen 之前 —— nearestLen 要用采样表
2072            nodesLen = pts.map(function (pt) { return nearestLen(pt); });
2073            nodesLen[0] = 0;
2074            nodesLen[nodesLen.length - 1] = totalLen;
2075        }
2076        // 渲染一个滚动进度 p(滚动与拖拽共用)
2077        var progressBar = stage.querySelector('.jny-progress .bar i');   // 只查一次,别每帧查
2078        var lastActive = -1;
2078   // 节点/卡片 class 只在"驶过节点"那一帧更新,日常滚动不再反复切换
2079        function renderP(p) {
2080            var dist = totalLen * p;
2081            // 核心真正驶过的节点数(精确到曲线弧长 → 到 S 弯顶点瞬间切换)
2082            var k = 0;
2083            while (k < n && nodesLen[k] <= dist) k++;
2084            var active = Math.min(k - 1, n - 1);
2085            if (active < 0) active = 0;
2086            if (sx && totalLen > 1) {
2087                var pt = pointAt(Math.min(dist, totalLen));
2088                // 用 transform 移动(合成层),不用 left/top(避免每帧布局)——移动端流畅的关键
2089                batt.style.transform = 'translate(' + pt.x + 'px,' + pt.y + 'px)';
2090                goPathEl.style.strokeDashoffset = totalLen - Math.min(dist, totalLen);
2091            }
2092            if (active !== lastActive) {
2093                lastActive = active;
2094                nodes.forEach(function (nd, kk) {
2095                    nd.classList.toggle('lit', kk <= active);
2096                    nd.classList.toggle('dim', kk > active);
2097                });
2098                cards.forEach(function (c, kk) { c.classList.toggle('on', kk === active); });
2099                var ap = pts[Math.min(active, pts.length - 1)];
2100                if (bgEl && ap) bgEl.style.transform = 'translate(' + (ap.x - 400) * -0.22 + 'px,' + (ap.y - 220) * -0.22 + 'px)';
2101            }
2102            if (progressBar) progressBar.style.width = (p * 100).toFixed(1) + '%';
2103        }
2104        function update() {
2105            var sy = window.scrollY || document.documentElement.scrollTop;
2106            var p = (sy - (spacerTopAbs - 70)) / runLen;
2107            p = p < 0 ? 0 : (p > 1 ? 1 : p);
2108            renderP(p);
2109        }
2110        function onScroll() {
2111            if (suppressScroll) { suppressScroll = false; return; }
2112            if (dragging) return;
2113            if (!ticking) { ticking = true; requestAnimationFrame(function () { ticking = false; update(); }); }
2114        }
2115        // ---- 拖拽:抓住能量核心沿曲线滑动,反向驱动旅程 ----
2116        function nearestDist(px, py) {
2117            if (!sx) return -1;
2118            var best = -1, bestD2 = 1e12;
2119            for (var i = 0; i <= SAMPLE_N; i++) {
2120                var dx = sx[i] - px, dy = sy[i] - py;
2121                var d2 = dx * dx + dy * dy;
2122                if (d2 < bestD2) { bestD2 = d2; best = i / SAMPLE_N; }
2123            }
2124            return best;
2125        }
2126        batt.addEventListener('pointerdown', function (e) {
2127            if (e.button !== undefined && e.button !== 0 && e.pointerType === 'mouse') return;
2128            if (!stageRect) return;
2129            dragging = true;
2130            try { batt.setPointerCapture(e.pointerId); } catch (err) {}
2131            batt.classList.add('dragging');
2132            e.preventDefault();
2133        });
2134        var dragLastX = -1e9, dragLastY = -1e9;
2135        batt.addEventListener('pointermove', function (e) {
2136            if (!dragging || !stageRect) return;
2137            var px = e.clientX - stageRect.left;
2138            var py = e.clientY - stageRect.top;
2139            // 位移过小则忽略:拖动采样(260 点全曲线搜索)较贵,别让每个像素都算一遍
2140            if (Math.abs(px - dragLastX) + Math.abs(py - dragLastY) < 4) return;
2141            dragLastX = px; dragLastY = py;
2142            var k = nearestDist(px, py);
2143            if (k < 0) return;
2144            renderP(k);
2145            // 滚动条跟随小球(拖拽期间不触发 scroll 渲染)
2146            suppressScroll = true;
2147            var target = spacerTopAbs - 70 + k * runLen;
2148            window.scrollTo(0, target);
2149        });
2150        function endDrag() {
2151            if (!dragging) return;
2152            dragging = false;
2153            batt.classList.remove('dragging');
2154        }
2155        batt.addEventListener('pointerup', endDrag);
2156        batt.addEventListener('pointercancel', endDrag);
2157        // ---- 窄屏三排之字布局:13 节点两排(4%~96%)在窄屏必然重叠,改为 5/4/4 三排 ----
2158        // 第2排从右往左排布,S 曲线连续转折;第1、3排从左往右。
2159        // 横向边界取 9%~91%:节点带 -50% 偏移,要留足半个节点宽,避免被 stage 裁边。
2160        nodes.forEach(function (nd) {
2161            nd._ol = nd.style.left; nd._ot = nd.style.top;   // 记住桌面原位置,宽屏时还原
2162        });
2163        var ROW_TOPS = ['12.5%', '33.5%', '54.5%'];
2164        var ROW_IDX = [[0, 1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]];
2165        function layoutNodes() {
2166            if (stage.clientWidth > 700) {
2167                nodes.forEach(function (nd) { nd.style.left = nd._ol; nd.style.top = nd._ot; });
2168                return;
2169            }
2170            ROW_IDX.forEach(function (row, ri) {
2171                var span = row.length - 1;
2172                var step = 82 / span;
2173                row.forEach(function (idx, j) {
2174                    var x = ri === 1 ? 91 - j * step : 9 + j * step;
2175                    nodes[idx].style.left = x.toFixed(2) + '%';
2176                    nodes[idx].style.top = ROW_TOPS[ri];
2177                });
2178            });
2179        }
2180        function measure() {
2181            layoutNodes();
2182            vh = window.innerHeight || document.documentElement.clientHeight;
2183            var run = Math.max(vh * 2.1, 1500);
2184            var stageH = stage.offsetHeight || Math.min(vh * 0.72, 720);
2185            spacer.style.height = Math.ceil(run + stageH + 60) + 'px';
2186            var sr = spacer.getBoundingClientRect();
2187            spacerTopAbs = sr.top + window.scrollY;
2188            runLen = run;
2189            buildSvg();
2190            update();
2191        }
2192        var progEl = document.createElement('div');
2193        progEl.className = 'jny-progress';
2194        progEl.innerHTML = 'JOURNEY <span class="bar"><i></i></span>';
2195        stage.appendChild(progEl);
2196        // 拖拽时基于 stage 当前视口位置换算
2197        function refreshStageRect() { stageRect = stage.getBoundingClientRect(); }
2198        window.addEventListener('scroll', function () { refreshStageRect(); }, { passive: true });
2199        window.addEventListener('scroll', onScroll, { passive: true });
2200        // resize 防抖:手机地址栏收起/旋转会连发 resize,每次都重建 SVG 是移动端卡顿源之一
2201        var rt = null;
2202        function onResize() {
2203            if (rt) clearTimeout(rt);
2204            rt = setTimeout(function () { rt = null; measure(); refreshStageRect(); }, 160);
2205        }
2206        window.addEventListener('resize', onResize);
2207        if (window.matchMedia) {
2208            try {
2209                var mq = window.matchMedia('(max-width: 700px)');
2210                if (mq.addEventListener) mq.addEventListener('change', onResize);
2211                else if (mq.addListener) mq.addListener(onResize);
2212            } catch (e) {}
2213        }
2214        refreshStageRect();
2215        measure();
2216        window.addEventListener('load', function () { measure(); refreshStageRect(); });
2217    })();
2218
2219    // ===== 数据条 count-up + B站粉丝实时同步 =====
2220    // 入场:0 → data-to(4.2s easeInOutQuart 平滑滚动);
2221    // 之后每 30s 静默轮询后端 /api/bili-fans(PythonAnywhere 进程内 30s 缓存 + 直连 B站官方接口,
2222    // 平均新鲜度约 30 秒),粉丝数有变化时数字就地平滑滚动到新值,无需刷新;
2223    // 后端失败则静默保留当前值(首屏另有 localStorage 缓存兜底)。
2224    (function () {
2225        var boxes = document.querySelectorAll('.stats .cn');
2226        if (!boxes.length) return;
2227        var fansEl = document.getElementById('biliFans');
2228        function fmt(v, comma) {
2229            return comma ? String(v).replace(/\B(?=(\d{3})+(?!\d))/g, ',') : String(v);
2230        }
2231        function anim(el, from, to, dur, done) {
2232            var comma = el.id === 'biliFans';
2233            var t0 = null, last = 0, skip = false;
2234            function step(ts) {
2235                if (!t0) t0 = ts;
2236                skip = !skip;
2237                if (skip) { requestAnimationFrame(step); return; }   // 隔帧渲染≈30fps:肉眼足够顺滑,主线程开销减半
2238                var p = Math.min((ts - t0) / dur, 1);
2239                // easeInOutQuart:起步收尾都缓、中段匀速,数字从容"跑"到目标
2240                var e = p < 0.5 ? 8 * p * p * p * p : 1 - Math.pow(-2 * p + 2, 4) / 2;
2241                var v = Math.round(from + (to - from) * e);
2242                if (v <= last && p < 1) v = Math.min(last + 1, to);   // 单调推进
2243                last = v;
2244                el.textContent = fmt(v, comma);
2245                if (p < 1) requestAnimationFrame(step);
2246                else if (done) done();
2247            }
2248            requestAnimationFrame(step);
2249        }
2250        // 粉丝缓存回填:以最近一次同步值为首屏目标(比静态默认更贴近当前)
2251        try {
2252            var cached = parseInt(localStorage.getItem('nb_bili_fans') || '', 
225210);
2253            if (cached > 0 && fansEl) fansEl.setAttribute('data-to', cached);
2254        } catch (e) {}
2255        var entryDone = false;   // 入场动画是否全部完成
2256        var fanPending = null;   // 入场期间取回的更新值:完成后补一次短动画
2257        var remain = boxes.length;
2258        function fanDone() {
2259            remain--;
2260            if (remain > 0) return;
2261            entryDone = true;
2262            if (fanPending != null && fansEl) {
2263                var cur = parseInt(String(fansEl.textContent).replace(/[^\d]/g, ''), 10) || 0;
2264                var v = fanPending;
2265                fanPending = null;
2266                if (v !== cur) anim(fansEl, cur, v, 900, null);
2267            }
2268        }
2269        function run() {
2270            Array.prototype.forEach.call(boxes, function (el) {
2271                var to = parseInt(el.getAttribute('data-to') || '0', 10);
2272                if (isNaN(to) || to <= 0) { el.textContent = '0'; fanDone(); return; }
2273                anim(el, 0, to, 4200, fanDone);
2274            });
2275        }
2276        // 轮询间隔默认 30s;?fansPoll=ms 可调(测试用)
2277        var pollMs = 30000;
2278        try {
2279            var pm = /[?&]fansPoll=(\d+)/.exec(location.search);
2280            if (pm) pollMs = Math.max(2000, parseInt(pm[1], 10));
2281        } catch (e) {}
2282        function curFans() {
2283            return parseInt(String(fansEl.textContent).replace(/[^\d]/g, ''), 10) || 0;
2284        }
2285        function applyFans(n) {
2286            if (!fansEl || !(n > 0)) return;
2287            try { localStorage.setItem('nb_bili_fans', String(n)); } catch (e) {}
2288            if (!entryDone) { fanPending = n; return; }   // 入场动画未结束:记下,结束时补动
2289            var cur = curFans();
2290            if (n === cur) return;
2291            var dur = Math.min(600 + Math.abs(n - cur) * 6, 2600);
2292            anim(fansEl, cur, n, dur, null);
2293        }
2294        // ===== 粉丝数:优先读静态文件,失败或过期才回退到接口 =====
2295        // 为什么改:
2296        //   原先直接请求 PythonAnywhere 的 /api/bili-fans —— 那个端点不能加 Key
2297        //   (首页要调它,加 Key 就得把 Key 写进公开 HTML),所以谁都能刷,
2298        //   而 PythonAnywhere 免费版每个请求约 1.1 秒 worker 开销,刷多了会拖垮后端。
2299        //   现在改读 data/fans.json(由 .github/workflows/update-fans.yml 每小时
2300        //   更新),请求打到 GitHub Pages 的 CDN,被刷不消耗任何后端资源。
2301        //   接口保留为兜底:文件缺失、或文件超过 6 小时没更新时才回退。
2302        var FANS_STATIC = '/data/fans.json';
2303        var FANS_API = 'https://nbchannel.pythonanywhere.com/api/bili-fans';
2304        var FANS_STALE_MS = 6 * 60 * 60 * 1000;   // 静态文件超过 6 小时视为过期
2305
2306        function fallbackFans() {
2307            fetch(FANS_API, { cache: 'no-store' })
2308                .then(function (r) { return r.ok ? r.json() : null; })
2309                .then(function (j) { if (j) applyFans(parseInt(j.follower, 10)); })
2310                .catch(function () {});
2311        }
2312        function pollFans() {
2313            if (document.hidden) return;   // 标签页在后台时不打扰
2314            // 不加 cache-busting 参数:GitHub Pages 自带 max-age 缓存,
2315            // 让浏览器/CDN 复用即可,轮询本身几乎不产生请求。
2316            fetch(FANS_STATIC)
2317                .then(function (r) { return r.ok ? r.json() : null; })
2318                .then(function (j) {
2319                    var n = j && parseInt(j.follower, 10);
2320                    if (!(n > 0)) { fallbackFans(); return; }
2321                    // 文件太旧(定时任务可能挂了)→ 用接口拿实时值
2322                    var t = j.updated_at_iso ? Date.parse(j.updated_at_iso) : 0;
2323                    if (t && (Date.now() - t) > FANS_STALE_MS) { fallbackFans(); return; }
2324                    applyFans(n);
2325                })
2326                .catch(function () { fallbackFans(); });
2327        }
2328        if ('IntersectionObserver' in window) {
2329            var statsBox = document.querySelector('.stats');
2330            if (statsBox) {
2331                var io2 = new IntersectionObserver(function (es) {
2332                    es.forEach(function (en) {
2333                        if (en.isIntersecting) { run(); io2.disconnect(); }
2334                    });
2335                }, { threshold: 0.4 });
2336                io2.observe(statsBox);
2337            }
2338        } else run();
2339        document.addEventListener('visibilitychange', function () {
2340            if (!document.hidden) pollFans();
2341        });
2342        pollFans();
2343        setInterval(pollFans, pollMs);
2344    })();
2345
2346    // ===== Hero LOGO 视频:就绪后淡入(避免刷新时视频区先黑、就绪瞬间"突然跳出") =====
2347    (function () {
2348        var box = document.querySelector('.hero-video');
2349        var vid = box && box.querySelector('video');
2350        if (!vid) return;
2351        function show() { vid.classList.add('ready'); }
2352        if (vid.readyState >= 2) { show(); return; }
2353        vid.addEventListener('loadeddata', show);
2354        vid.addEventListener('error', show);
2355        // 用 <source> 时,某个源加载失败会在 <source> 上派发 error,而不是 <video> ——
2356        // 不挂这个监听,视频挂了要等 6 秒兜底才显形(显出来的是 poster 静态图)
2357        var srcs = vid.querySelectorAll('source');
2358        for (var i = 0; i < srcs.length; i++) { srcs[i].addEventListener('error', show); }
2359        setTimeout(show, 6000);   // 兜底:网络极慢/自动播放被拦时不至于一直透明
2360    })();
2361
2362    // ===== 导航滚动态(吸顶加深) + 回到顶部按钮 =====
2363    (function () {
2364        var nav = document.querySelector('.nav');
2365        var backTop = document.getElementById('backTop');
2366        function onScroll() {
2367            var y = window.scrollY || document.documentElement.scrollTop;
2368            if (nav) nav.classList.toggle('scrolled', y > 10);
2369            if (backTop) backTop.classList.toggle('show', y > 600);
2370        }
2371        window.addEventListener('scroll', onScroll, { passive: true });
2372        onScroll();
2373        if (backTop) backTop.addEventListener('click', function () {
2374            window.scrollTo({ top: 0, behavior: 'smooth' });
2375        });
2376    })();
2377
2378    // ===== 导航栏北京时间(UTC+8,日期+精确到秒;Intl 时区格式化,不受本地时区影响) =====
2379    (function () {
2380        var tEl = document.getElementById('bjTime');
2381        var dEl = document.getElementById('bjDate');
2382        if (!tEl) return;
2383        var fmtT = new Intl.DateTimeFormat('zh-CN', {
2384            timeZone: 'Asia/Shanghai', hour: '2-digit', minute: '2-digit',
2385            second: '2-digit', hour12: false, hourCycle: 'h23'
2386        });
2387        var fmtD = new Intl.DateTimeFormat('zh-CN', {
2388            timeZone: 'Asia/Shanghai', year: 'numeric', month: '2-digit',
2389            day: '2-digit', weekday: 'short'
2390        });
2391        function tick() {
2392            var now = new Date();
2393            tEl.textContent = fmtT.format(now);
2394            if (dEl) dEl.textContent = fmtD.format(now).replace(/\//g, '.');
2395        }
2396        tick();
2397        setInterval(tick, 1000);
2398    })();
2399
2400    // ===== 夜间模式切换(兼容旧键 theme) =====
2401    (function () {
2402        var btn = document.getElementById('themeBtn');
2403        var root = document.documentElement;
2404        function isDark() { return root.classList.contains('dark'); }
2405        function render() {
2406            if (btn) { btn.textContent = isDark() ? '☀️' : '🌙'; btn.title = isDark() ? '切换到日间模式' : '切换到夜间模式'; }
2407        }
2408        function apply(dark) {
2409            root.classList.toggle('dark', dark);
2410            try { localStorage.setItem('theme', dark ? 'dark' : 'light'); } catch (e) {}
2411            render();
2412        }
2413        if (btn) btn.addEventListener('click', function () {
2414            var dark = !isDark();
2415            if (document.startViewTransition) {
2416                try { document.startViewTransition(function () { apply(dark); }); } catch (e) { apply(dark); }
2417            } else apply(dark);
2418        });
2419        render();
2420    })();
2421
2422    // ===== 移动端抽屉导航 =====
2423    (function () {
2424        var burger = document.getElementById('navBurger');
2425        var drawer = document.getElementById('drawer');
2426        var mask = document.getElementById('drawerMask');
2427        var closeBtn = document.getElementById('drawerClose');
2428        if (!burger || !drawer || !mask) return;
2429        function open() { drawer.classList.add('open'); mask.style.display = 'block'; document.body.style.overflow = 'hidden'; }
2430        function close() { drawer.classList.remove('open'); mask.style.display = 'none'; document.body.style.overflow = ''; }
2431        burger.addEventListener('click', open);
2432        closeBtn.addEventListener('click', close);
2433        mask.addEventListener('click', close);
2434        drawer.querySelectorAll('a').forEach(function (a) {
2435            a.addEventListener('click', close);
2436        });
2437    })();
2438
2439    // ===== 防查看代码(反调试,静默无提示):屏蔽 F12 / Ctrl+Shift+I·J·C / Ctrl+U / 右键;debugger 陷阱 =====
2440    // 逃生门:URL 含 ?dev=1 或 #dev 完全放行(F12/右键/debugger 陷阱全跳过),去掉即自动恢复屏蔽;
2441    // 另支持 window.__allowDevTools = true(当前页内存,刷新失效)
2442    (function () {
2443        var DEV = window.__allowDevTools ||
2444                  /(?:^|[?&#])dev=1(?=&|#|$)/.test(location.href) ||
2445                  /#dev\b/.test(location.hash);
2446        if (DEV) return;
2447        // 1) 静默屏蔽开发者工具快捷键(F12 / Ctrl+Shift+I·J·C / Ctrl+U)
2448        document.addEventListener('keydown', function (e) {
2449            var k = e.keyCode || e.which;
2450            var ctrl = e.ctrlKey || e.metaKey;
2451            if (k === 123 || e.key === 'F12' ||
2452                (ctrl && e.shiftKey && (k === 73 || k === 74 || k === 67)) ||
2453                (ctrl && k === 85)) {
2454                e.preventDefault();
2455                e.stopPropagation();
2456                return false;
2457            }
2458        });
2459        // 2) 静默屏蔽右键菜单
2460        document.addEventListener('contextmenu', function (e) {
2461            e.preventDefault();
2462            return false;
2463        });
2464        // 3) debugger 陷阱:开启控制台时会反复中断(不开控制台则零开销);需调试时访问 ?dev=1
2465        setInterval(function () { debugger; }, 600);
2466    })();
2467</script>
2467
2468
2469<script>
2470    // ===== 会话令牌自动注入(安全修复):高危 RPC 自动附带 nb_session =====
2471    (function () {
2472        function patch(sb) {
2473            if (!sb || sb.__secPatched) return;
2474            sb.__secPatched = true;
2475            var names = ['update_bio', 'bankrupt_company', 'transfer_nb', 'claim_redpacket', 'do_check_in', 'do_lottery', 'buy_shop_item', 'sell_shop_item', 'use_shop_item', 'set_item_settings', 'get_bank_account', 'get_bank_logs', 'get_my_items', 'get_my_titles', 'sync_my_titles', 'get_my_achievements', 'check_achievements', 'get_my_lottery_records', 'get_my_transfers', 'get_checkin_status'];
2476            var orig = sb.rpc.bind(sb);
2477            sb.rpc = function (fn, args) {
2478                if (names.indexOf(fn) !== -1) {
2479                    args = Object.assign({}, args || {});
2480                    var t = null;
2481                    try { t = localStorage.getItem('nb_session'); } catch (e) {}
2482                    args.p_session = t;
2483                }
2484                return orig(fn, args);
2485            };
2486        }
2487        var cands = [];
2488        try { if (window.supabaseClient) cands.push(window.supabaseClient); } catch (e) {}
2489        try { if (window.__nbSupabase) cands.push(window.__nbSupabase); } catch (e) {}
2490        try { if (typeof supabaseClient !== 'undefined') cands.push(supabaseClient); } catch (e) {}
2491        cands.forEach(patch);
2492    })();
2493</script>
2493
2494
2495
2496<script>
2497    // ===== 安全强制重登:本地有用户信息但无会话令牌 => 视为未登录 =====
2498    (function () {
2499        try {
2500            var u = localStorage.getItem('nb_user');
2501            var s = localStorage.getItem('nb_session');
2502            if (u && !s) {
2503                localStorage.removeItem('nb_user');
2504                location.reload();
2505            }
2506        } catch (e) {}
2507    })();
2508</script>
2508
2509
2510
2511<script>
2512    // ===== 余额自动刷新补丁(v3 后改用 get_my_balance RPC) =====
2513    (function () {
2514        function applyBal() {
2515            var u = null, t = null;
2516            try { u = JSON.parse(localStorage.getItem('nb_user') || 'null'); t = localStorage.getItem('nb_session'); } catch (e) {}
2517            if (!u || !u.id || !t) return;
2518            var el = document.getElementById('balanceDisplay') ||
2519                     document.getElementById('balanceSpan') ||
2520                     document.getElementById('tradeBalance');
2521            if (!el) return;
2522            var sb = null;
2523            try { if (window.supabaseClient) sb = window.supabaseClient; } catch (e) {}
2524            try { if (!sb && typeof supabaseClient !== 'undefined') sb = supabaseClient; } catch (e) {}
2525            if (!sb) return;
2526            sb.rpc('get_my_balance', { p_user_id: u.id, p_session: t }).then(function (r) {
2527                if (r && r.data && r.data.ok && el) el.textContent = Number(r.data.balance).toLocaleString('zh-CN');
2528            }).catch(function () {});
2529        }
2530        var tries = 0;
2531        (function loop() { applyBal(); if (++tries < 6) setTimeout(loop, 1300); })();
2532    })();
2533</script>
2533
2534
2535
2536<script>
2537// ===== APP 自更新(仅 APP 内生效) =====
2538(function () {
2539    var ua = navigator.userAgent || '';
2540    var m = /NBChannelApp\/([0-9.]+)/i.exec(ua);
2541    if (!m) return;                       // 不是 APP 环境,不打扰网页用户
2542    var localVer = m[1];
2543    var APK = '/download/NBChannel.apk';
2544
2545    function cmp(a, b) {
2546        var x = String(a).split('.').map(Number), y = String(b).split('.').map(Number);
2547        for (var i = 0; i < 3; i++) { var p = x[i] || 0, q = y[i] || 0; if (p !== q) return p - q; }
2548        return 0;
2549    }
2550
2551    var checking = false;
2552    function check(manual) {
2553        if (checking) return;
2554        checking = true;
2555        fetch('/download/version.json?t=' + Date.now(), { cache: 'no-store' })
2556            .then(function (r) { if (!r.ok) throw new Error('HTTP ' + r.status); return r.json(); })
2557            .then(function (info) {
2558                checking = false;
2559                if (!info || !info.versionName) throw new Error('版本信息不完整');
2560                if (cmp(info.versionName, localVer) > 0) showBar(info, manual);
2561                else if (manual) toast('✅ 已是最新版 v' + localVer);
2562            })
2563            .catch(function (e) {
2564                checking = false;
2565                if (manual) toast('检查更新失败:' + e.message + '\n请检查网络后重试');
2566            });
2567    }
2568
2569    // ---- 底部更新条 ----
2570    function showBar(info, manual) {
2571        if (document.getElementById('nbUpdateBar')) return;
2572        var bar = document.createElement('div');
2573        bar.id = 'nbUpdateBar';
2574        bar.style.cssText = 'position:fixed;left:0;right:0;bottom:0;z-index:99999;' +
2575            'padding:14px 16px calc(14px + env(safe-area-inset-bottom,0px));' +
2576            'background:linear-gradient(135deg,#3b82f6,#6366f1);color:#fff;' +
2577            'display:flex;align-items:center;gap:10px;font-size:.86rem;' +
2578            'box-shadow:0 -8px 28px rgba(0,0,0,.3);';
2579        bar.innerHTML =
2580            '<div style="flex:1;line-height:1.6;min-width:0;">
2580' +
2581                '<b>发现新版本 v' + info.versionName + '</b><br>' +
2582                '<span style="opacity:.9;font-size:.78rem;">当前 v' + localVer + ' · ' +
2583                (info.note || '修复与优化') + '</span>' +
2584            '</div>' +
2585            '<button id="nbUpdGo" style="background:#fff;color:#3b82f6;border:none;border-radius:999px;' +
2586                'padding:9px 18px;font-weight:800;font-family:inherit;font-size:.86rem;cursor:pointer;white-space:nowrap;">立即更新</button>' +
2587            '<button id="nbUpdNo" style="background:transparent;border:none;color:#fff;opacity:.75;' +
2588                'font-size:1.05rem;cursor:pointer;padding:4px 6px;">✕</button>';
2589        document.body.appendChild(bar);
2590        document.getElementById('nbUpdNo').onclick = function () { bar.remove(); };
2591        document.getElementById('nbUpdGo').onclick = function () {
2592            var b = this;
2593            b.textContent = '下载中…'; b.disabled = true; b.style.opacity = '.8';
2594            location.href = info.url || APK;
2595            setTimeout(function () { b.textContent = '立即更新'; b.disabled = false; b.style.opacity = '1'; }, 12000);
2596        };
2597    }
2598
2599    // ---- 轻提示 ----
2600    function toast(msg) {
2601        var t = document.createElement('div');
2602        t.style.cssText = 'position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:99999;' +
2603            'background:rgba(15,23,42,.94);color:#fff;padding:14px 20px;border-radius:14px;' +
2604            'font-size:.86rem;line-height:1.7;max-width:80vw;text-align:center;white-space:pre-line;' +
2605            'box-shadow:0 12px 36px rgba(0,0,0,.4);';
2606        t.textContent = msg;
2607        document.body.appendChild(t);
2608        setTimeout(function () { t.remove(); }, 2600);
2609    }
2610
2611    // ---- 在导航「更多功能」下拉 + 手机抽屉里注入入口 ----
2612    function inject() {
2613        var panel = document.querySelector('.nav-more-panel');
2614        if (panel && !panel.querySelector('[data-nb-upd]')) {
2615            var a = document.createElement('a');
2616            a.href = 'javascript:void(0)';
2617            a.setAttribute('data-nb-upd', '1');
2618            a.textContent = '🔄 检查更新';
2619            a.addEventListener('click', function (e) {
2620                e.preventDefault();
2621                var more = document.querySelector('.nav-more');
2622                if (more) more.classList.remove('open');
2623                check(true);
2624            });
2625            panel.insertBefore(a, panel.firstChild);
2626        }
2627        var authBox = document.querySelector('.drawer .d-auth');
2628        if (authBox && !document.querySelector('[data-nb-upd-d]')) {
2629            var d = document.createElement('a');
2630            d.className = 'd-item';
2631            d.href = 'javascript:void(0)';
2632            d.setAttribute('data-nb-upd-d', '1');
2633            d.textContent = '🔄 检查更新(v' + localVer + ')';
2634            d.addEventListener('click', function (e) { e.preventDefault(); check(true); });
2635            authBox.parentNode.insertBefore(d, authBox);
2636        }
2637    }
2638
2639    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', inject);
2640    else inject();
2641    setTimeout(inject, 1200);      // 兜底:导航是脚本渲染时再试一次
2642
2643    // 启动自动查一次,之后每 30 分钟
2644    setTimeout(function () { check(false); }, 2000);
2645    setInterval(function () { check(false); }, 30 * 60 * 1000);
2646})();
2647</script>
2647
2648
2649<script>
2650/* ===== Android 上让 B站链接优先打开 B站 APP ===== */
2651(function () {
2652    try {
2653        if (!/Android/i.test(navigator.userAgent)) return;   // 只在安卓上改,桌面/iPhone 保持原样
2654        function toIntent(href) {
2655            var m = href.match(/space\.bilibili\.com\/(\d+)/);
2656            if (!m) return null;
2657            // intent:// 是安卓专属协议:能打开 APP 就打开,打不开就回退到 browser_fallback_url
2658            return 'intent://space/' + m[1] + '#Intent;scheme=bilibili;package=tv.danmaku.bili' +
2659                   ';S.browser_fallback_url=' + encodeURIComponent(href) + ';end';
2660        }
2661        function patch() {
2662            var links = document.querySelectorAll('a[href*="space.bilibili.com"]');
2663            for (var i = 0; i < links.length; i++) {
2664                var a = links[i];
2665                if (a.dataset.nbIntent) continue;
2666                var it = toIntent(a.getAttribute('href') || a.href);
2667                if (it) { a.setAttribute('href', it); a.dataset.nbIntent = '1'; }
2668            }
2669        }
2670        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', patch);
2671        else patch();
2672        setTimeout(patch, 1500);
2673    } catch (e) {}
2674})();
2675
2676/* ===== 双击视频铺满屏幕 ===== */
2677(function () {
2678    try {
2679        var style = document.createElement('style');
2680        style.textContent =
2681            'video.nb-fs{position:fixed!important;left:0!important;top:0!important;' +
2682            'width:100vw!important;height:100vh!important;max-width:100vw!important;' +
2683            'max-height:100vh!important;margin:0!important;border-radius:0!important;' +
2684            'z-index:99999!important;background:#000!important;object-fit:contain!important;}' +
2685            'body.nb-fs-lock{overflow:hidden!important;}';
2686        document.head.appendChild(style);
2687
2688        var current = null;
2689        function exit() {
2690            if (!current) return;
2691            current.classList.remove('nb-fs');
2692            document.body.classList.remove('nb-fs-lock');
2693            current = null;
2694        }
2695        function enter(v) {
2696            exit();
2697            v.classList.add('nb-fs');
2698            document.body.classList.add('nb-fs-lock');
2699            current = v;
2700            try { v.play(); } catch (e) {}
2701        }
2702        // 双击:进/出全屏
2703        document.addEventListener('dblclick', function (e) {
2704            var v = e.target && e.target.closest ? e.target.closest('video') : null;
2705            if (!v) return;
2706            e.preventDefault();
2707            if (current === v) exit(); else enter(v);
2708        }, true);
2709        // 全屏状态下点空白处/按 Esc 退出
2710        document.addEventListener('click', function (e) {
2711            if (current && !(e.target.closest && e.target.closest('video'))) exit();
2712        }, true);
2713        document.addEventListener('keydown', function (e) { if (e.key === 'Escape') exit(); });
2714        // 页面切换/尺寸变化时兜底
2715        window.addEventListener('resize', function () { if (current) exit(); });
2716    } catch (e) {}
2717})();
2718</script>
2718
2719
2720<!--
2721  ══════════════════════════════════════════════════════════════════
2722   NB-CHANNEL ORIGINAL · 本页面由 NB频道 原创制作
2723   构建指纹 NBC-20261001-1E6D2EC11C21
2724   官方域名 nb-channel.top (github. / cloudflare. / edgeone. / pythonanywhere.)
2725   如果你在其他域名看到本页,说明该站点未经授权复制了 NB频道 的内容,
2726   欢迎举报:[email protected]
2727  ══════════════════════════════════════════════════════════════════
2728-->
2729
2730<script>/* 构建元信息(请勿删除,侵权取证用) */
2731(function () {
2732    try {
2733        var _nb = "TkJDLTIwMjYxMDAxLTFFNkQyRUMxMUMyMQ";
2734        if (typeof window !== "undefined") window.__nbBuild = _nb;
2735    } catch (e) {}
2736})();
2737</script>
vendor: 1 bytes, line 2737
2737
2738<script defer src="../js/science-tips.js?v=26"></script>
vendor: 1 bytes, line 2738
2738
2739<script src="../js/pomodoro.js?v=7" defer></script>
vendor: 1 bytes, line 2739
2739
2740<script type="module" src="https://static.cloudflareinsights.com/beacon.min.js/v31edd6df95cf4e85bb4c19e7a9bdbcba1788362987495" integrity="sha512-iIg7k2xntmwu6/uSb5tpc/hySgZc4eoL31yB29W6tJFo2akwjPWcEqnCEdJvGexCL0KEQwVYv5BlowfhVz26hg==" data-cf-beacon='{"version":"2024.11.0","token":"2694d6c1b70c42ee8414dc6303584599","r":1,"spa":2}' crossorigin="anonymous"></script>
2740
2741</body>
2742</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.