zhizhi/docs/index.html

3778 lines
213 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=5.0,viewport-fit=cover">
<meta name="description" content="光湖语言世界 · 人格体一半 · 人类一半 · 语言驱动操作系统 AGE OS">
<meta name="theme-color" content="#0a0a1a">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>光湖语言世界 · HoloLake</title>
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><defs><linearGradient id=%22g%22 x1=%220%22 y1=%220%22 x2=%221%22 y2=%221%22><stop offset=%220%25%22 stop-color=%22%238ab4f8%22/><stop offset=%22100%25%22 stop-color=%22%23a78bfa%22/></linearGradient></defs><rect rx=%2220%22 width=%22100%22 height=%22100%22 fill=%22%230a0a1a%22/><text x=%2250%22 y=%2264%22 text-anchor=%22middle%22 font-size=%2240%22 font-weight=%22900%22 font-family=%22monospace%22 fill=%22url(%23g)%22>GL</text></svg>">
<style>
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* ═══ Themes ═══ */
:root{
--bg:#0a0a1a;--bg1:rgba(18,18,42,.85);--bg2:rgba(22,22,50,.8);--bg3:rgba(28,28,58,.75);
--border:rgba(100,100,200,.18);--accent:#8ab4f8;--accent2:#a78bfa;--accent3:#c4b5fd;
--ok:#34d399;--warn:#fbbf24;--err:#f87171;
--text:#e0e0f0;--dim:#8090b8;--muted:rgba(100,130,180,.3);
--glow:0 0 30px rgba(138,180,248,.12),0 0 60px rgba(167,139,250,.06);
--r:16px;--rs:10px;
--f:-apple-system,BlinkMacSystemFont,'PingFang SC','Helvetica Neue','Microsoft YaHei',sans-serif;
--m:'SF Mono','Fira Code',Consolas,monospace;
--sb:env(safe-area-inset-bottom,0px);--st:env(safe-area-inset-top,0px);
--glass:rgba(14,14,34,.6);--glass-border:rgba(100,100,200,.15);
}
.t-twi{--bg:#0c0618;--bg1:rgba(20,14,36,.85);--bg2:rgba(26,20,48,.8);--bg3:rgba(34,26,58,.75);--accent:#c084fc;--accent2:#e879f9;--accent3:#f0abfc;--border:rgba(140,80,200,.2)}
.t-sea{--bg:#060e14;--bg1:rgba(10,22,34,.85);--bg2:rgba(14,30,46,.8);--bg3:rgba(18,40,56,.75);--accent:#22d3ee;--accent2:#06b6d4;--accent3:#67e8f9;--border:rgba(34,211,238,.18)}
.t-for{--bg:#060e08;--bg1:rgba(12,26,16,.85);--bg2:rgba(16,34,22,.8);--bg3:rgba(20,44,28,.75);--accent:#34d399;--accent2:#4ade80;--accent3:#86efac;--border:rgba(52,211,153,.18)}
.t-emb{--bg:#0e0806;--bg1:rgba(28,18,10,.85);--bg2:rgba(38,24,14,.8);--bg3:rgba(48,32,20,.75);--accent:#fb923c;--accent2:#f97316;--accent3:#fdba74;--border:rgba(251,146,60,.18)}
html{-webkit-tap-highlight-color:transparent}
body{font-family:var(--f);background:var(--bg);color:var(--text);font-size:15px;line-height:1.6;overflow:hidden;height:100vh;height:100dvh;-webkit-font-smoothing:antialiased}
a{color:var(--accent);text-decoration:none}
::-webkit-scrollbar{width:4px}::-webkit-scrollbar-thumb{background:var(--muted);border-radius:2px}
/* ═══ Background: Deep Lake Night Sky ═══ */
.bg{position:fixed;inset:0;z-index:0;pointer-events:none;overflow:hidden}
#starCanvas{position:absolute;inset:0;width:100%;height:100%}
.nebula{position:absolute;border-radius:50%;filter:blur(120px);animation:nebPulse 16s ease-in-out infinite}
.nebula:nth-child(2){width:50vw;height:50vh;background:radial-gradient(ellipse,rgba(138,180,248,.04),transparent 70%);top:-10%;left:-10%}
.nebula:nth-child(3){width:45vw;height:45vh;background:radial-gradient(ellipse,rgba(167,139,250,.035),transparent 70%);bottom:-8%;right:-5%;animation-delay:8s}
.nebula:nth-child(4){width:35vw;height:35vh;background:radial-gradient(ellipse,rgba(196,181,253,.03),transparent 70%);top:40%;left:50%;animation-delay:4s}
@keyframes nebPulse{0%,100%{opacity:.5;transform:scale(1) translate(0,0)}50%{opacity:.8;transform:scale(1.06) translate(6px,-5px)}}
/* ═══ 3D Page Flip Engine ═══ */
.pages{position:fixed;inset:0;z-index:1;perspective:1400px;overflow:hidden}
.page{
position:absolute;inset:0;
overflow-y:auto;overflow-x:hidden;
-webkit-overflow-scrolling:touch;
padding:0 0 var(--sb) 0;
background:transparent;
backface-visibility:hidden;-webkit-backface-visibility:hidden;
transition:transform .55s cubic-bezier(.4,0,.15,1),opacity .55s;
transform-origin:center center;
will-change:transform,opacity;
}
.page[data-state="active"]{transform:rotateY(0deg) scale(1);opacity:1;z-index:2;pointer-events:auto}
.page[data-state="left"]{transform:rotateY(-92deg) scale(.92);opacity:0;z-index:1;pointer-events:none}
.page[data-state="right"]{transform:rotateY(92deg) scale(.92);opacity:0;z-index:1;pointer-events:none}
.page[data-state="hidden"]{transform:rotateY(92deg) scale(.88);opacity:0;z-index:0;pointer-events:none;transition:none}
/* ═══ Common UI ═══ */
.bar{position:sticky;top:0;z-index:10;display:flex;align-items:center;justify-content:space-between;padding:10px var(--da-pad, 16px);background:rgba(5,8,22,.72);backdrop-filter:blur(24px) saturate(1.3);-webkit-backdrop-filter:blur(24px) saturate(1.3);border-bottom:1px solid rgba(110,168,254,.08);min-height:var(--da-bar-h, 48px);padding-top:calc(10px + var(--st));gap:8px}
.bar-right{display:flex;gap:6px;align-items:center;flex-shrink:0}
.bar-title{font-size:14px;font-weight:700;display:flex;align-items:center;gap:6px}
.bar-right{display:flex;align-items:center;gap:8px}
.dot{width:6px;height:6px;border-radius:50%;background:var(--ok);box-shadow:0 0 6px var(--ok);animation:pls 2s infinite;flex-shrink:0}
@keyframes pls{50%{opacity:.4}}
.btn{background:none;border:1px solid var(--border);color:var(--dim);padding:5px 12px;border-radius:var(--rs);font-size:12px;cursor:pointer;font-family:var(--f);transition:all .35s cubic-bezier(.4,0,.2,1);white-space:nowrap;position:relative;overflow:hidden}
.btn::before{content:'';position:absolute;inset:0;background:linear-gradient(120deg,transparent 30%,rgba(110,168,254,.08) 50%,transparent 70%);transform:translateX(-100%);transition:transform .5s}
.btn:hover{color:var(--accent);border-color:var(--accent);box-shadow:0 0 16px rgba(110,168,254,.12),inset 0 0 16px rgba(110,168,254,.04)}
.btn:hover::before{transform:translateX(100%)}
.btn:active{transform:scale(.96)}
.btn-accent{background:linear-gradient(135deg,var(--accent),var(--accent2));border:none;color:#fff;box-shadow:0 2px 12px rgba(110,168,254,.2)}
.btn-accent:hover{opacity:.88;color:#fff;box-shadow:0 4px 20px rgba(110,168,254,.35)}
.btn-accent::before{background:linear-gradient(120deg,transparent 30%,rgba(255,255,255,.15) 50%,transparent 70%)}
.content{padding:var(--da-pad, 16px);max-width:var(--da-content-max, 700px);margin:0 auto}
.card{background:rgba(10,18,38,.75);border:1px solid rgba(110,168,254,.18);border-radius:var(--r);padding:16px;margin-bottom:12px;transition:all .35s cubic-bezier(.4,0,.2,1);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px)}
.card:hover{border-color:rgba(110,168,254,.25);box-shadow:0 0 24px rgba(110,168,254,.08)}
.tag{display:inline-block;padding:2px 8px;border-radius:20px;font-size:10px;font-weight:600}
.tag-ok{background:rgba(52,211,153,.12);color:var(--ok)}
.tag-warn{background:rgba(251,191,36,.12);color:var(--warn)}
.tag-info{background:rgba(79,142,247,.12);color:var(--accent)}
.empty{text-align:center;padding:40px 16px;color:var(--muted);font-size:14px}
.empty em{font-style:normal;font-size:40px;display:block;margin-bottom:12px}
input[type=text],input[type=password],textarea{background:rgba(5,8,22,.5);border:1px solid rgba(110,168,254,.1);border-radius:var(--rs);color:var(--text);padding:10px 12px;font-size:14px;font-family:var(--f);outline:none;width:100%;transition:all .3s;-webkit-appearance:none}
input[type=text]:focus,input[type=password]:focus,textarea:focus{border-color:rgba(110,168,254,.4);box-shadow:0 0 20px rgba(110,168,254,.08)}
::placeholder{color:var(--muted)}
label{display:block;font-size:12px;color:var(--dim);margin-bottom:4px;font-weight:600}
.form-row{margin-bottom:14px}
.form-row-inline{display:flex;gap:10px}
@media(max-width:480px){.form-row-inline{flex-direction:column}}
/* ═══ PAGE: Home ═══ */
.home-hero{text-align:center;padding:40px 16px 32px}
.home-ver{display:inline-flex;align-items:center;gap:6px;padding:4px 14px;border-radius:20px;background:rgba(110,168,254,.06);border:1px solid rgba(110,168,254,.12);font-size:11px;color:var(--accent);font-family:var(--m);margin-bottom:16px;animation:breathe 4s ease-in-out infinite}
@keyframes breathe{0%,100%{box-shadow:0 0 8px rgba(110,168,254,.1)}50%{box-shadow:0 0 20px rgba(110,168,254,.25)}}
.home-hero h1{font-size:clamp(28px,5.5vw,44px);font-weight:800;line-height:1.2;margin-bottom:8px;background:linear-gradient(135deg,#e2e8f0 0%,#6ea8fe 50%,#a78bfa 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.home-hero .sub{font-size:clamp(12px,2.5vw,15px);color:var(--dim);margin-bottom:14px}
.home-hero .quote{font-size:14px;color:var(--accent2);font-style:italic;opacity:.8}
.upd{display:flex;gap:10px;padding:12px;margin-bottom:8px;background:var(--bg1);border:1px solid var(--border);border-radius:var(--r);transition:border-color .3s}
.upd:hover{border-color:var(--accent)}
.upd-v{min-width:42px;padding:3px 6px;text-align:center;background:rgba(79,142,247,.08);border-radius:var(--rs);color:var(--accent);font-size:10px;font-weight:600;font-family:var(--m);height:fit-content;flex-shrink:0}
.upd h4{font-size:13px;font-weight:600;margin-bottom:2px}
.upd p{font-size:11px;color:var(--dim);line-height:1.5}
.home-enter{padding:28px;text-align:center;background:var(--glass);border:1px solid var(--glass-border);border-radius:var(--r);cursor:pointer;transition:all .4s cubic-bezier(.4,0,.2,1);margin:20px 0;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);position:relative;overflow:hidden}
.home-enter::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 50%,rgba(110,168,254,.04),transparent 70%);opacity:0;transition:opacity .4s}
.home-enter:hover{border-color:rgba(110,168,254,.35);box-shadow:var(--glow);transform:translateY(-2px)}
.home-enter:hover::after{opacity:1}
.home-enter:active{transform:scale(.98)}
.home-enter em{font-style:normal;font-size:36px;display:block;margin-bottom:8px}
.home-enter strong{font-size:17px;display:block;margin-bottom:4px}
.home-enter span{font-size:12px;color:var(--dim)}
/* ═══ HERO Landing (v49.0 黑曜风) ═══ */
.hero-section{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:calc(100vh - 60px);min-height:calc(100dvh - 60px);padding:40px 20px;text-align:center;position:relative;z-index:2}
.hero-title{font-size:clamp(32px,6vw,52px);font-weight:800;line-height:1.15;margin-bottom:12px;background:linear-gradient(135deg,#e0e0f0 0%,#8ab4f8 40%,#a78bfa 80%,#c4b5fd 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;animation:heroFadeUp .8s ease-out both}
.hero-sub{font-size:clamp(14px,2.5vw,18px);color:var(--dim);margin-bottom:10px;animation:heroFadeUp .8s ease-out .15s both;letter-spacing:1px}
.hero-quote{font-size:15px;color:var(--accent2);font-style:italic;opacity:.75;margin-bottom:48px;animation:heroFadeUp .8s ease-out .25s both}
@keyframes heroFadeUp{from{opacity:0;transform:translateY(24px)}to{opacity:1;transform:translateY(0)}}
/* Hero Entry Cards */
.hero-cards{display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:580px;width:100%;margin-bottom:40px}
@media(max-width:520px){.hero-cards{grid-template-columns:1fr;max-width:320px}}
.hero-card{position:relative;padding:40px 24px 30px;background:rgba(18,18,42,.55);border:1px solid rgba(100,100,200,.25);border-radius:var(--r);cursor:pointer;transition:transform .4s cubic-bezier(.4,0,.2,1),box-shadow .4s,border-color .4s;overflow:hidden;text-align:center;transform-style:preserve-3d;perspective:800px;animation:heroCardIn .6s ease-out both}
.hero-card:nth-child(1){animation-delay:.35s}
.hero-card:nth-child(2){animation-delay:.45s}
@keyframes heroCardIn{from{opacity:0;transform:translateY(30px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}
.hero-card-glow{position:absolute;inset:0;background:radial-gradient(circle at 50% 50%,rgba(138,180,248,.06),transparent 70%);opacity:0;transition:opacity .5s;pointer-events:none}
.hero-card:hover{transform:translateY(-6px);border-color:rgba(138,180,248,.5);box-shadow:0 12px 48px rgba(138,180,248,.15),0 0 80px rgba(167,139,250,.08)}
.hero-card:hover .hero-card-glow{opacity:1}
.hero-card:active{transform:translateY(-2px) scale(.98)}
.hero-card-icon{font-size:48px;margin-bottom:14px;filter:drop-shadow(0 0 18px rgba(138,180,248,.35));transition:transform .4s;min-height:56px;display:flex;align-items:center;justify-content:center}
.hero-card:hover .hero-card-icon{transform:scale(1.06) translateY(-4px)}
.hero-card-title{font-size:19px;font-weight:700;margin-bottom:6px;letter-spacing:.5px}
.hero-card-desc{font-size:12px;color:var(--dim);margin-bottom:10px;line-height:1.5}
.hero-card-tags{display:flex;justify-content:center;gap:6px}
/* Hero Status Bar */
.hero-status{display:inline-flex;align-items:center;gap:10px;padding:10px 24px;background:rgba(18,18,42,.5);border:1px solid rgba(100,100,200,.2);border-radius:24px;font-size:12px;color:var(--dim);margin-bottom:32px;animation:heroFadeUp .8s ease-out .55s both;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
.hero-status-dot{width:8px;height:8px;border-radius:50%;background:var(--ok);box-shadow:0 0 8px var(--ok);animation:pls 2s infinite}
.hero-status-ver{color:var(--accent);font-family:var(--f);font-weight:600;font-size:11px}
/* Hero Footer */
.hero-footer{text-align:center;font-size:11px;color:var(--muted);animation:heroFadeUp .8s ease-out .65s both}
/* 3D Tilt Effect (JS-driven) */
.tilt-card{transition:transform .15s ease-out}
.home-footer{text-align:center;padding:24px 0;font-size:11px;color:var(--muted);border-top:1px solid var(--border);margin-top:24px}
.home-footer .motto{color:var(--accent2);font-style:italic;margin-top:4px}
/* ═══ PAGE: Desk (作品列表) ═══ */
.work-item{cursor:pointer}
.work-item h3{font-size:15px;font-weight:600;margin-bottom:4px}
.work-item .meta{font-size:11px;color:var(--dim);display:flex;gap:10px;flex-wrap:wrap}
.work-item .meta span{display:flex;align-items:center;gap:3px}
/* ═══ PAGE: Work Hub (作品导航) ═══ */
.hub-title{text-align:center;padding:20px 0 12px}
.hub-title h2{font-size:20px;font-weight:700}
.hub-title p{font-size:12px;color:var(--dim);margin-top:2px}
.hub-grid{display:grid;grid-template-columns:var(--da-hub, 1fr 1fr);gap:10px}
.hub-card{text-align:center;padding:28px 14px;background:rgba(10,18,38,.72);border:1px solid rgba(110,168,254,.2);border-radius:var(--r);cursor:pointer;transition:all .4s cubic-bezier(.4,0,.2,1);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);position:relative;overflow:hidden}
.hub-card::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 0%,rgba(110,168,254,.08),transparent 70%);opacity:0;transition:opacity .4s}
.hub-card:hover{border-color:rgba(110,168,254,.3);box-shadow:0 0 30px rgba(110,168,254,.1);transform:translateY(-3px)}
.hub-card:hover::after{opacity:1}
.hub-card:active{transform:scale(.97)}
.hub-card em{font-style:normal;font-size:36px;display:block;margin-bottom:10px;filter:drop-shadow(0 0 12px rgba(110,168,254,.25))}
.hub-card strong{font-size:14px;display:block;margin-bottom:3px}
.hub-card span{font-size:11px;color:var(--dim)}
.hub-stats{display:flex;justify-content:center;gap:20px;padding:12px 0;flex-wrap:wrap}
.hub-stat{text-align:center}
.hub-stat b{display:block;font-size:18px;color:var(--accent);font-family:var(--m)}
.hub-stat small{font-size:10px;color:var(--dim)}
/* ═══ PAGE: Outline ═══ */
.outline-editor{min-height:250px;resize:vertical;line-height:1.9;font-size:14px}
.vol-item{display:flex;align-items:center;justify-content:space-between;padding:10px;background:var(--bg2);border-radius:var(--rs);margin-bottom:6px}
.vol-item span{font-size:13px;font-weight:600}
.vol-actions{display:flex;gap:4px}
/* ═══ PAGE: Characters ═══ */
.char-card{display:flex;gap:12px;align-items:flex-start}
.char-avatar{width:42px;height:42px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--accent2));display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0}
.char-info h4{font-size:14px;font-weight:600;margin-bottom:2px}
.char-info p{font-size:12px;color:var(--dim);line-height:1.5}
.char-role{font-size:10px;color:var(--accent);font-weight:600}
/* ═══ PAGE: Editor ═══ */
#pg-editor{background:rgba(6,8,18,.99)}
#pg-editor>.content{background:rgba(6,8,18,.99);border-radius:0;backdrop-filter:blur(30px);-webkit-backdrop-filter:blur(30px);max-width:var(--da-content-max, 800px);padding:20px 28px}
.editor-info{display:flex;justify-content:space-between;align-items:center;padding:10px 0;font-size:13px;color:var(--dim);border-bottom:1px solid rgba(100,100,200,.2);margin-bottom:16px}
.editor-textarea{min-height:calc(100vh - 240px);min-height:calc(100dvh - 240px);resize:none;line-height:var(--da-ed-lh, 2);font-size:var(--da-ed-font, 17px);border:none;background:rgba(4,6,14,.99);padding:24px 20px;position:relative;z-index:3;border-radius:12px;color:var(--text);box-shadow:inset 0 2px 20px rgba(0,0,0,.4);transition:box-shadow .3s}
.editor-textarea:focus{border:none;box-shadow:inset 0 2px 20px rgba(0,0,0,.4),0 0 20px rgba(110,168,254,.06)}
.editor-bottom{position:sticky;bottom:0;padding:12px 0;background:rgba(6,8,18,.99);backdrop-filter:blur(24px);-webkit-backdrop-filter:blur(24px);display:flex;justify-content:space-between;align-items:center;border-top:1px solid rgba(100,100,200,.15);font-size:12px;color:var(--dim);z-index:5}
.ed-bottom-info{display:flex;gap:12px;align-items:center;font-family:var(--f);font-size:11px;color:var(--dim)}
/* ═══ Editor Toolbar ═══ */
.ed-toolbar{display:flex;gap:5px;align-items:center;flex-wrap:wrap}
.ed-toolbar .tb-btn{background:rgba(10,18,38,.8);border:1px solid rgba(110,168,254,.15);color:var(--dim);width:38px;height:34px;border-radius:8px;cursor:pointer;font-size:15px;display:flex;align-items:center;justify-content:center;transition:all .3s;font-family:var(--f);position:relative;overflow:hidden}
.ed-toolbar .tb-btn:hover{color:var(--accent);border-color:rgba(110,168,254,.35);box-shadow:0 0 16px rgba(110,168,254,.12);background:rgba(110,168,254,.1)}
.ed-toolbar .tb-btn:active{transform:scale(.9)}
.ed-toolbar .tb-sep{width:1px;height:20px;background:rgba(100,100,200,.2);margin:0 5px}
/* ═══ Focus Mode ═══ */
.focus-mode .bar,.focus-mode .editor-info,.focus-mode .chat-fab,.focus-mode .mon{opacity:0;pointer-events:none;transition:opacity .4s}
.focus-mode .editor-textarea{min-height:calc(100vh - 80px);min-height:calc(100dvh - 80px);font-size:18px;line-height:2.2;padding:32px 28px}
.focus-mode .editor-bottom{background:rgba(6,8,18,.95);border:none}
.focus-mode .ed-toolbar{opacity:.3;transition:opacity .3s}
.focus-mode .ed-toolbar:hover{opacity:1}
.focus-mode:hover .bar{opacity:1;pointer-events:auto}
/* ═══ Save Indicator ═══ */
.save-dot{width:6px;height:6px;border-radius:50%;display:inline-block;margin-right:4px;transition:background .3s}
.save-dot.saved{background:var(--ok)}
.save-dot.saving{background:var(--warn);animation:pls 1s infinite}
.save-dot.unsaved{background:var(--dim)}
/* ═══ Create Success Overlay ═══ */
.create-success{display:none;text-align:center;padding:40px 20px;animation:fadeUp .5s ease-out}
.create-success.show{display:block}
.create-success em{font-style:normal;font-size:56px;display:block;margin-bottom:16px;animation:successPop .6s cubic-bezier(.68,-.55,.27,1.55)}
.create-success h3{font-size:20px;font-weight:700;margin-bottom:8px}
.create-success p{color:var(--dim);font-size:13px;margin-bottom:24px}
.create-actions{display:flex;gap:10px;justify-content:center;flex-wrap:wrap}
.create-actions .btn{padding:10px 24px;font-size:14px;border-radius:var(--r)}
@keyframes fadeUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
@keyframes successPop{0%{transform:scale(0) rotate(-20deg)}100%{transform:scale(1) rotate(0)}}
/* ═══ Chat FAB ═══ */
.chat-fab{position:fixed;bottom:calc(16px + var(--sb));right:14px;z-index:50;width:52px;height:52px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--accent2));border:none;color:#fff;font-size:21px;cursor:pointer;box-shadow:0 4px 20px rgba(110,168,254,.3),0 0 40px rgba(167,139,250,.15);transition:all .25s;display:flex;align-items:center;justify-content:center;animation:fabBreathe 4s ease-in-out infinite}
@keyframes fabBreathe{0%,100%{box-shadow:0 4px 20px rgba(110,168,254,.3),0 0 40px rgba(167,139,250,.1)}50%{box-shadow:0 6px 28px rgba(110,168,254,.45),0 0 50px rgba(167,139,250,.2)}}
.chat-fab:hover{transform:scale(1.06)}
.chat-fab:active{transform:scale(.93)}
.chat-fab.on{border-radius:var(--r);background:rgba(248,113,113,.85)}
/* 编辑器模式下缩小悬浮球,不干扰打字 */
.chat-fab.editor-mode{width:38px;height:38px;font-size:16px;opacity:.6;bottom:calc(8px + var(--sb))}
.chat-fab.editor-mode:hover{opacity:1;transform:scale(1.1)}
/* ═══ Chat Panel ═══ */
.chat-p{position:fixed;top:50%;left:50%;z-index:50;width:min(520px,calc(100vw - 40px));height:min(640px,calc(100vh - 80px));height:min(640px,calc(100dvh - 80px));background:rgba(5,8,22,.92);border:1px solid rgba(110,168,254,.15);border-radius:16px;display:flex;flex-direction:column;transform:translate(-50%,-50%) scale(.9);opacity:0;pointer-events:none;transition:all .25s cubic-bezier(.4,0,.2,1);box-shadow:0 24px 80px rgba(0,0,0,.7),0 0 60px rgba(110,168,254,.08);backdrop-filter:blur(28px);-webkit-backdrop-filter:blur(28px)}
.chat-p.open{transform:translate(-50%,-50%) scale(1);opacity:1;pointer-events:auto}
@media(max-width:560px){.chat-p{top:auto;left:0;bottom:0;width:100%;height:calc(100vh - 48px);height:calc(100dvh - 48px);border-radius:16px 16px 0 0;transform:translateY(100%);padding-bottom:var(--sb)}.chat-p.open{transform:translateY(0)}}
@media(min-width:900px){.chat-p{width:min(600px,calc(100vw - 80px));height:min(720px,calc(100vh - 80px))}}
.chat-hd{padding:14px 18px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:10px;flex-shrink:0}
.chat-av{width:38px;height:38px;border-radius:50%;background:linear-gradient(135deg,var(--accent),var(--accent2));display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0}
.chat-nm{font-weight:600;font-size:15px}
.chat-sb{font-size:11px;color:var(--dim)}
.chat-tg{margin-left:auto;font-size:10px;padding:3px 8px;background:var(--bg2);border:1px solid var(--border);border-radius:20px;color:var(--dim);font-family:var(--m)}
.chat-ms{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px;-webkit-overflow-scrolling:touch}
.cm{max-width:85%;padding:10px 14px;border-radius:var(--r);font-size:14px;line-height:1.7;word-break:break-word}
.cm.u{align-self:flex-end;background:var(--accent);color:#fff;border-bottom-right-radius:4px}
.cm.b{align-self:flex-start;background:var(--bg2);border:1px solid var(--border);border-bottom-left-radius:4px}
.cm.s{align-self:center;background:none;color:var(--muted);font-size:11px;padding:2px}
.cm-m{display:flex;gap:5px;margin-top:6px;font-size:10px;color:var(--muted)}
.ct{font-size:12px;color:var(--dim);padding:6px 16px}
.ct span{animation:bk 1.4s infinite;font-size:14px}
.ct span:nth-child(2){animation-delay:.2s}
.ct span:nth-child(3){animation-delay:.4s}
@keyframes bk{0%,100%{opacity:.2}50%{opacity:1}}
.chat-in{padding:12px 14px;border-top:1px solid var(--border);display:flex;gap:8px;flex-shrink:0}
.cin{flex:1;padding:10px 14px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--r);color:var(--text);font-size:15px;outline:none;font-family:var(--f);-webkit-appearance:none}
.cin:focus{border-color:var(--accent)}
.csnd{width:40px;height:40px;border-radius:50%;background:var(--accent);border:none;color:#fff;font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all .2s}
.csnd:hover{background:var(--accent2)}
.csnd:disabled{opacity:.3}
.chat-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.5);z-index:49;opacity:0;pointer-events:none;transition:opacity .25s}
.chat-overlay.open{opacity:1;pointer-events:auto}
/* ═══ Three-Column Home Layout (Device Agent driven) ═══ */
.home-grid{display:grid;grid-template-columns:var(--da-grid, 1fr);gap:var(--da-gap, 12px);padding:var(--da-pad, 12px);max-width:1200px;margin:0 auto;align-content:start}
.home-grid>*{min-width:0;overflow:hidden}
.home-center{min-width:0;overflow:hidden}
[data-single-col="true"] .home-grid>.panel:first-child{order:2}
[data-single-col="true"] .home-center{order:1}
[data-single-col="true"] .home-grid>.panel:last-child{order:3}
.panel{background:rgba(10,18,38,.78);border:1px solid rgba(110,168,254,.18);border-radius:var(--r);padding:14px;height:fit-content;backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px)}
.panel-title{font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin-bottom:10px;display:flex;align-items:center;gap:6px}
.panel-item{padding:6px 0;border-bottom:1px solid var(--border);font-size:12px}
.panel-item:last-child{border-bottom:none}
.panel-label{color:var(--dim);font-size:10px;margin-bottom:1px}
.panel-value{color:var(--text);font-weight:600;font-family:var(--m);font-size:11px}
.panel-value.ok{color:var(--ok)}
.panel-value.warn{color:var(--warn)}
.panel-value.err{color:var(--err)}
/* ═══ Ticker ═══ */
.ticker-wrap{overflow:hidden;background:rgba(8,14,30,.4);border:1px solid rgba(110,168,254,.06);border-radius:var(--rs);padding:6px 0;margin-bottom:14px;position:relative}
.ticker-wrap::before,.ticker-wrap::after{content:'';position:absolute;top:0;bottom:0;width:32px;z-index:2;pointer-events:none}
.ticker-wrap::before{left:0;background:linear-gradient(90deg,rgba(3,6,14,.8),transparent)}
.ticker-wrap::after{right:0;background:linear-gradient(-90deg,rgba(3,6,14,.8),transparent)}
.ticker{display:flex;animation:tickScroll 28s linear infinite;white-space:nowrap;gap:36px;padding:0 16px}
.ticker:hover{animation-play-state:paused}
.ticker span{font-size:11px;color:var(--dim);display:flex;align-items:center;gap:5px;flex-shrink:0}
.ticker span em{font-style:normal}
@keyframes tickScroll{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}
/* ═══ Team Card ═══ */
.team-slot{display:flex;align-items:center;gap:8px;padding:7px 8px;background:rgba(5,8,22,.4);border-radius:var(--rs);margin-bottom:5px;transition:all .3s;border:1px solid transparent}
.team-slot:hover{border-color:rgba(110,168,254,.15);background:rgba(8,14,30,.6)}
.team-av{width:28px;height:28px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0}
.team-nm{font-size:11px;font-weight:600}
.team-role{font-size:9px;color:var(--dim)}
/* ═══ Feedback Page ═══ */
.fb-item{padding:10px}
.fb-item .fb-meta{font-size:10px;color:var(--dim);margin-top:6px;display:flex;gap:8px;align-items:center}
.fb-status{display:inline-block;padding:1px 6px;border-radius:10px;font-size:9px;font-weight:600}
.fb-status.pending{background:rgba(251,191,36,.12);color:var(--warn)}
.fb-status.replied{background:rgba(52,211,153,.12);color:var(--ok)}
.fb-reply{margin-top:6px;padding:8px;background:var(--bg);border-radius:var(--rs);font-size:12px;color:var(--accent)}
/* ═══ Home Entry Cards ═══ */
.home-entries{display:grid;grid-template-columns:var(--da-entries, 1fr 1fr);gap:10px;margin:16px 0}
.entry-card{padding:24px 16px;text-align:center;background:rgba(10,18,38,.72);border:1px solid rgba(110,168,254,.22);border-radius:var(--r);cursor:pointer;transition:all .4s cubic-bezier(.4,0,.2,1);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);position:relative;overflow:hidden}
.entry-card::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 0%,rgba(110,168,254,.08),transparent 70%);opacity:0;transition:opacity .4s}
.entry-card:hover{border-color:rgba(110,168,254,.3);box-shadow:0 0 30px rgba(110,168,254,.1),0 0 60px rgba(167,139,250,.06);transform:translateY(-3px)}
.entry-card:hover::after{opacity:1}
.entry-card:active{transform:scale(.98) translateY(0)}
.entry-card em{font-style:normal;font-size:36px;display:block;margin-bottom:10px;filter:drop-shadow(0 0 12px rgba(110,168,254,.25))}
.entry-card strong{font-size:16px;display:block;margin-bottom:5px;letter-spacing:.5px}
.entry-card span{font-size:12px;color:var(--dim)}
/* ═══ Monitor (draggable) ═══ */
.mon{position:fixed;bottom:calc(16px + var(--sb));left:14px;z-index:40;background:rgba(5,8,22,.7);border:1px solid rgba(110,168,254,.1);border-radius:var(--r);padding:5px 9px;font-size:9px;font-family:var(--m);color:var(--dim);cursor:grab;transition:all .3s;user-select:none;backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);touch-action:none}
.mon:active{cursor:grabbing}
.mon:hover{border-color:rgba(110,168,254,.25)}
.mon.x{padding:8px 12px}
.mr{display:flex;justify-content:space-between;gap:8px;padding:1px 0}
.mv{color:var(--accent);font-weight:600}
.mx{display:none;margin-top:4px;padding-top:4px;border-top:1px solid var(--border)}
.mon.x .mx{display:block}
/* ═══ Viewport-Fit Layout (Desktop ≥961px) ═══ */
@media(min-width:961px){
#pg-home{overflow:hidden;display:flex;flex-direction:column;height:100%;padding:0}
}
/* ═══ Touch-Driven Sizing (Device Agent 适配) ═══ */
.btn{min-height:var(--da-touch-min, 32px)}
.btn-accent{min-height:var(--da-touch-min, 32px)}
[data-touch="true"] .csnd{width:40px;height:40px}
[data-touch="true"] .cin{font-size:16px;padding:10px 12px}
.home-hero h1{font-size:var(--da-title, 32px)}
.home-entries{display:grid;grid-template-columns:var(--da-entries, 1fr 1fr);gap:10px;margin:16px 0}
/* ═══ API Key Section ═══ */
.chat-api{border-top:1px solid var(--border);flex-shrink:0}
.chat-api-tog{padding:6px 12px;font-size:11px;color:var(--dim);cursor:pointer;display:flex;align-items:center;gap:5px;transition:color .2s;user-select:none}
.chat-api-tog:hover{color:var(--accent)}
.chat-api-tog .arr{font-size:9px;margin-left:auto;transition:transform .2s;display:inline-block}
.chat-api-tog.open .arr{transform:rotate(90deg)}
.chat-api-bd{display:none;padding:0 10px 8px}
.chat-api-bd.open{display:block}
.chat-api-row{display:flex;gap:5px;margin-bottom:5px}
.api-sel{flex:0 0 auto;padding:5px 6px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--rs);color:var(--text);font-size:11px;outline:none;font-family:var(--f);max-width:110px}
.api-sel:focus{border-color:var(--accent)}
.api-key-inp{flex:1;padding:5px 8px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--rs);color:var(--text);font-size:12px;outline:none;font-family:var(--m);min-width:0}
.api-key-inp:focus{border-color:var(--accent)}
.api-conn-btn{flex-shrink:0;padding:5px 10px;background:var(--accent);border:none;border-radius:var(--rs);color:#fff;font-size:11px;cursor:pointer;font-family:var(--f);transition:opacity .2s}
.api-conn-btn:hover{opacity:.85}
.api-conn-btn:disabled{opacity:.4;cursor:default}
.api-st{font-size:10px;color:var(--dim);padding:2px 2px}
/* ═══════════════════════════════════════════════
V43.0 大气化UI改版 · 触控放大 · 动态效果增强
═══════════════════════════════════════════════ */
/* ── 触控区域全局放大 ── */
.btn{padding:8px 16px;font-size:13px;min-height:44px;display:inline-flex;align-items:center;justify-content:center;gap:4px}
.btn-accent{padding:9px 20px;font-size:14px}
[data-touch="true"] .btn{min-height:48px;padding:10px 18px}
[data-touch="true"] .btn-accent{padding:10px 22px}
.entry-card{padding:32px 20px}
.entry-card em{font-size:48px;margin-bottom:14px}
.entry-card strong{font-size:18px;margin-bottom:6px;letter-spacing:.5px}
.entry-card span{font-size:13px}
.hub-card{padding:32px 18px}
.hub-card em{font-size:48px;margin-bottom:12px}
.hub-card strong{font-size:16px}
.card{padding:18px;margin-bottom:14px}
[data-touch="true"] .card{padding:20px}
[data-touch="true"] .entry-card{padding:36px 22px}
/* ── Uptime Counter (灯塔仪表盘核心) ── */
.uptime-block{text-align:center;padding:28px 16px 20px;margin-bottom:8px}
.uptime-num{font-size:clamp(52px,12vw,80px);font-weight:800;font-family:var(--m);background:linear-gradient(135deg,var(--accent) 0%,var(--accent2) 50%,var(--accent3) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;line-height:1.1;animation:uptimePulse 6s ease-in-out infinite;filter:drop-shadow(0 0 20px rgba(110,168,254,.3));letter-spacing:-2px}
.uptime-label{font-size:14px;color:var(--dim);margin-top:8px;letter-spacing:3px;text-transform:uppercase}
@keyframes uptimePulse{0%,100%{filter:drop-shadow(0 0 20px rgba(110,168,254,.2))}50%{filter:drop-shadow(0 0 40px rgba(110,168,254,.5))}}
/* ── Dynamic Reminders (动态提醒) ── */
.remind-section{margin:16px 0 20px}
.remind-header{display:flex;align-items:center;gap:8px;margin-bottom:12px;font-size:15px;font-weight:700}
.remind-header em{font-style:normal;font-size:20px;animation:bellSwing 4s ease-in-out infinite}
@keyframes bellSwing{0%,100%{transform:rotate(0)}10%{transform:rotate(12deg)}20%{transform:rotate(-10deg)}30%{transform:rotate(6deg)}40%{transform:rotate(-4deg)}50%{transform:rotate(0)}}
.remind-count{background:rgba(251,191,36,.15);color:var(--warn);font-size:12px;padding:3px 10px;border-radius:20px;font-weight:700;font-family:var(--m);min-width:24px;text-align:center}
.remind-card{display:flex;align-items:flex-start;gap:14px;padding:16px 18px;background:rgba(10,18,38,.72);border:1px solid rgba(110,168,254,.2);border-radius:var(--r);margin-bottom:8px;transition:all .4s cubic-bezier(.4,0,.2,1);cursor:default;position:relative;overflow:hidden;min-height:48px}
.remind-card::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(180deg,var(--accent),var(--accent2));border-radius:0 2px 2px 0;transition:width .3s}
.remind-card:hover{border-color:rgba(110,168,254,.25);box-shadow:0 0 24px rgba(110,168,254,.1);transform:translateX(4px)}
.remind-card:hover::before{width:4px}
.remind-icon{width:42px;height:42px;border-radius:12px;display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0;background:rgba(110,168,254,.06);border:1px solid rgba(110,168,254,.08)}
.remind-body{flex:1;min-width:0}
.remind-title{font-size:14px;font-weight:600;margin-bottom:3px}
.remind-desc{font-size:12px;color:var(--dim);line-height:1.5}
.remind-time{font-size:10px;color:var(--muted);font-family:var(--m);flex-shrink:0;padding-top:3px}
.remind-card.urgent{border-color:rgba(251,191,36,.2);background:rgba(28,18,6,.35)}
.remind-card.urgent::before{background:linear-gradient(180deg,var(--warn),var(--err))}
.remind-card.new{animation:remindSlideIn .5s cubic-bezier(.4,0,.15,1) both}
@keyframes remindSlideIn{from{opacity:0;transform:translateX(-20px)}to{opacity:1;transform:translateX(0)}}
/* ── Status Badge ── */
.status-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 14px;border-radius:20px;font-size:12px;font-weight:600;background:rgba(52,211,153,.08);border:1px solid rgba(52,211,153,.15);color:var(--ok);animation:glowPulse 3s ease-in-out infinite}
@keyframes glowPulse{0%,100%{box-shadow:0 0 8px rgba(110,168,254,.1),0 0 20px rgba(110,168,254,.05)}50%{box-shadow:0 0 16px rgba(110,168,254,.2),0 0 40px rgba(110,168,254,.1)}}
/* ── Module Selection Grid ── */
.mod-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:12px;margin:16px 0}
.mod-card{padding:28px 16px;text-align:center;background:rgba(10,18,38,.72);border:1px solid rgba(110,168,254,.2);border-radius:var(--r);cursor:pointer;transition:all .4s cubic-bezier(.4,0,.2,1);backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);position:relative;overflow:hidden}
.mod-card::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 30%,rgba(110,168,254,.06),transparent 70%);opacity:0;transition:opacity .4s}
.mod-card:hover{border-color:rgba(110,168,254,.3);box-shadow:0 0 30px rgba(110,168,254,.12);transform:translateY(-4px) scale(1.02)}
.mod-card:hover::after{opacity:1}
.mod-card:active{transform:scale(.97)}
.mod-card em{font-style:normal;font-size:42px;display:block;margin-bottom:10px;filter:drop-shadow(0 0 12px rgba(110,168,254,.2))}
.mod-card strong{font-size:14px;display:block;margin-bottom:4px}
.mod-card span{font-size:11px;color:var(--dim)}
.mod-card .mod-tag{position:absolute;top:8px;right:8px}
.mod-card.disabled{opacity:.5;cursor:not-allowed}
.mod-card.disabled:hover{transform:none;box-shadow:none;border-color:rgba(110,168,254,.1)}
/* ── Workspace Dashboard ── */
#pg-workspace>.content{max-width:var(--da-content-max, 800px)}
.ws-header{text-align:center;padding:32px 16px 20px}
.ws-header h2{font-size:28px;font-weight:800;margin-bottom:6px;background:linear-gradient(135deg,var(--text),var(--accent));-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.ws-header p{font-size:14px;color:var(--dim)}
.ws-goal{background:rgba(10,18,38,.88);border:1px solid rgba(110,168,254,.18);border-radius:var(--r);padding:28px;margin-bottom:18px;text-align:center;transition:all .4s;position:relative;overflow:hidden;animation:slideInUp .5s ease-out both}
.ws-goal::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 50% 0%,rgba(110,168,254,.06),transparent 70%);pointer-events:none;transition:opacity .4s}
.ws-goal:hover{border-color:rgba(110,168,254,.3);box-shadow:0 0 30px rgba(110,168,254,.1);transform:translateY(-2px)}
.ws-goal-title{font-size:14px;color:var(--dim);margin-bottom:10px;font-weight:600;letter-spacing:.5px}
.ws-goal-num{font-size:52px;font-weight:800;font-family:var(--m);color:var(--accent);margin:8px 0;line-height:1.1;text-shadow:0 0 30px rgba(138,180,248,.3)}
.ws-goal-sub{font-size:14px;color:var(--dim);margin-top:6px}
.ws-goal-bar{height:10px;background:rgba(110,168,254,.12);border-radius:5px;overflow:hidden;margin:16px 0 10px}
.ws-goal-fill{height:100%;background:linear-gradient(90deg,var(--accent),var(--accent2));border-radius:5px;transition:width .6s cubic-bezier(.4,0,.2,1);position:relative}
.ws-goal-fill::after{content:'';position:absolute;right:0;top:0;bottom:0;width:30px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.35));animation:shimmer 2s infinite}
@keyframes shimmer{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
.ws-quick{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:18px 0}
.ws-quick-btn{padding:20px 14px;text-align:center;background:rgba(10,18,38,.82);border:1px solid rgba(110,168,254,.15);border-radius:var(--r);cursor:pointer;transition:all .4s cubic-bezier(.4,0,.2,1);font-size:15px;color:var(--text);font-family:var(--f);min-height:68px;display:flex;align-items:center;justify-content:center;gap:10px;position:relative;overflow:hidden}
.ws-quick-btn::before{content:'';position:absolute;inset:0;background:radial-gradient(circle at 50% 50%,rgba(110,168,254,.04),transparent 70%);opacity:0;transition:opacity .4s}
.ws-quick-btn:hover{border-color:rgba(110,168,254,.35);background:rgba(110,168,254,.08);transform:translateY(-3px);box-shadow:0 4px 24px rgba(110,168,254,.12)}
.ws-quick-btn:hover::before{opacity:1}
.ws-quick-btn:active{transform:scale(.97)}
.ws-quick-btn em{font-style:normal;font-size:24px;filter:drop-shadow(0 0 8px rgba(110,168,254,.2))}
.ws-quick-btn:nth-child(1){animation:slideInUp .4s ease-out both;animation-delay:0s}
.ws-quick-btn:nth-child(2){animation:slideInUp .4s ease-out both;animation-delay:.08s}
.ws-quick-btn:nth-child(3){animation:slideInUp .4s ease-out both;animation-delay:.16s}
.ws-quick-btn:nth-child(4){animation:slideInUp .4s ease-out both;animation-delay:.24s}
/* ── Inspiration Page ── */
.insp-input-area{display:flex;gap:8px;margin-bottom:14px}
.insp-input-area input{flex:1}
.insp-input-area .btn{flex-shrink:0}
.insp-tags{display:flex;gap:6px;flex-wrap:wrap;margin-bottom:14px}
.insp-tag{padding:5px 14px;border-radius:20px;font-size:12px;cursor:pointer;border:1px solid var(--border);color:var(--dim);transition:all .25s;min-height:34px;display:flex;align-items:center}
.insp-tag.active{background:rgba(110,168,254,.1);border-color:var(--accent);color:var(--accent)}
.insp-tag:hover{border-color:var(--accent);color:var(--accent)}
.insp-card{padding:16px 18px;background:rgba(10,18,38,.72);border:1px solid rgba(110,168,254,.08);border-radius:var(--r);margin-bottom:8px;position:relative;transition:all .3s;animation:slideInUp .4s ease-out both}
.insp-card:hover{border-color:rgba(110,168,254,.2);box-shadow:0 0 16px rgba(110,168,254,.06)}
.insp-text{font-size:14px;margin-bottom:8px;line-height:1.6}
.insp-meta{display:flex;gap:8px;align-items:center;font-size:10px;color:var(--muted)}
.insp-del{position:absolute;top:12px;right:12px;background:none;border:none;color:var(--muted);cursor:pointer;font-size:16px;padding:6px;transition:color .2s;border-radius:50%;min-width:32px;min-height:32px;display:flex;align-items:center;justify-content:center}
.insp-del:hover{color:var(--err);background:rgba(248,113,113,.08)}
/* ── Progress Page ── */
.prog-stat-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;margin-bottom:16px}
.prog-stat{text-align:center;padding:18px 8px;background:rgba(10,18,38,.72);border:1px solid rgba(110,168,254,.08);border-radius:var(--r);transition:all .3s}
.prog-stat:hover{border-color:rgba(110,168,254,.2)}
.prog-stat b{display:block;font-size:28px;font-family:var(--m);color:var(--accent);margin-bottom:4px}
.prog-stat small{font-size:10px;color:var(--dim)}
.prog-cal{margin:16px 0}
.prog-cal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:10px}
.prog-cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:4px;text-align:center}
.prog-cal-day{padding:10px 4px;font-size:11px;border-radius:var(--rs);color:var(--dim);transition:all .2s;cursor:default}
.prog-cal-day.head{font-weight:700;font-size:10px;color:var(--muted)}
.prog-cal-day.has-words{background:rgba(52,211,153,.1);color:var(--ok);cursor:pointer}
.prog-cal-day.has-words:hover{background:rgba(52,211,153,.2)}
.prog-cal-day.today{border:1px solid var(--accent);color:var(--accent);font-weight:700}
.achieve-card{display:flex;align-items:center;gap:12px;padding:14px;background:rgba(8,14,30,.4);border:1px solid rgba(110,168,254,.06);border-radius:var(--r);margin-bottom:8px;transition:all .3s}
.achieve-card:hover{border-color:rgba(110,168,254,.15)}
.achieve-card.earned{border-color:rgba(52,211,153,.2);background:rgba(10,30,20,.3)}
.achieve-card em{font-style:normal;font-size:28px}
.achieve-card .ach-name{font-size:13px;font-weight:600;margin-bottom:2px}
.achieve-card .ach-desc{font-size:11px;color:var(--dim)}
/* ── Settings Page ── */
.set-group{margin-bottom:20px}
.set-group-title{font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin-bottom:10px;display:flex;align-items:center;gap:6px}
.set-row{display:flex;justify-content:space-between;align-items:center;padding:14px;background:rgba(8,14,30,.4);border-radius:var(--rs);margin-bottom:6px;min-height:52px;border:1px solid rgba(110,168,254,.06);transition:all .3s}
.set-row:hover{border-color:rgba(110,168,254,.15)}
.set-row label{margin:0;font-size:13px;color:var(--text);font-weight:400}
.set-row input[type=number]{width:80px;text-align:center;padding:6px 8px;background:var(--bg2);border:1px solid var(--border);border-radius:var(--rs);color:var(--text);font-size:14px;outline:none;font-family:var(--m)}
.set-row input[type=number]:focus{border-color:var(--accent)}
.set-row select{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rs);color:var(--text);padding:8px 12px;font-size:13px;outline:none;font-family:var(--f);min-height:36px}
.set-row select:focus{border-color:var(--accent)}
/* ── Staggered Animation ── */
@keyframes slideInUp{from{opacity:0;transform:translateY(20px)}to{opacity:1;transform:translateY(0)}}
@keyframes slideInLeft{from{opacity:0;transform:translateX(-20px)}to{opacity:1;transform:translateX(0)}}
/* ═══ Glowing Book Icon (码字工作台·v50.0) ═══ */
.book-icon-wrap{display:inline-block;width:56px;height:56px;position:relative;filter:drop-shadow(0 0 14px rgba(138,180,248,.35));animation:bookGlow 4s ease-in-out infinite}
.book-icon-wrap svg{width:100%;height:100%}
@keyframes bookGlow{0%,100%{filter:drop-shadow(0 0 14px rgba(138,180,248,.3)) drop-shadow(0 0 28px rgba(167,139,250,.15))}50%{filter:drop-shadow(0 0 24px rgba(138,180,248,.55)) drop-shadow(0 0 48px rgba(167,139,250,.25))}}
.book-icon-wrap .book-sparkle{position:absolute;width:3px;height:3px;border-radius:50%;background:#fff;animation:bookSparkle 3s ease-in-out infinite;pointer-events:none}
.book-icon-wrap .book-sparkle:nth-child(2){top:6px;right:4px;animation-delay:.6s}
.book-icon-wrap .book-sparkle:nth-child(3){top:16px;right:10px;animation-delay:1.2s;width:2px;height:2px}
.book-icon-wrap .book-sparkle:nth-child(4){bottom:10px;left:6px;animation-delay:1.8s}
@keyframes bookSparkle{0%,100%{opacity:0;transform:scale(0)}40%{opacity:1;transform:scale(1.2)}80%{opacity:0;transform:scale(0)}}
/* ═══ System Panel v50.0 Redesign ═══ */
.sys-hero{text-align:center;padding:40px 16px 24px;position:relative;overflow:hidden}
.sys-hero::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 50% 30%,rgba(138,180,248,.06),transparent 70%);pointer-events:none}
.sys-hero h2{font-size:24px;font-weight:800;margin-bottom:4px;background:linear-gradient(135deg,#e0e0f0,#8ab4f8,#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.sys-hero p{font-size:13px;color:var(--dim)}
.sys-uptime-card{text-align:center;padding:32px 20px;margin:0 16px 20px;background:rgba(10,18,38,.88);border:1px solid rgba(138,180,248,.25);border-radius:var(--r);position:relative;overflow:hidden;transition:all .4s}
.sys-uptime-card::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 50% 30%,rgba(138,180,248,.06),transparent 70%);pointer-events:none;animation:uptimeAura 8s ease-in-out infinite}
@keyframes uptimeAura{0%,100%{opacity:.4}50%{opacity:1}}
.sys-uptime-card:hover{border-color:rgba(138,180,248,.45);box-shadow:0 0 40px rgba(138,180,248,.12)}
.sys-uptime-num{font-size:clamp(42px,10vw,64px);font-weight:800;font-family:var(--m);background:linear-gradient(135deg,var(--accent) 0%,var(--accent2) 50%,var(--accent3) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;line-height:1.1;filter:drop-shadow(0 0 20px rgba(138,180,248,.3));animation:uptimePulse 6s ease-in-out infinite;letter-spacing:-1px}
.sys-uptime-label{font-size:11px;color:var(--dim);margin-top:6px;letter-spacing:3px;text-transform:uppercase;font-family:var(--m)}
.sys-uptime-sub{font-size:10px;color:var(--muted);margin-top:8px;font-style:italic}
.sys-status-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin:0 16px 20px}
.sys-stat-card{padding:16px 14px;background:rgba(10,18,38,.82);border:1px solid rgba(100,100,200,.15);border-radius:var(--r);transition:all .4s;position:relative;overflow:hidden}
.sys-stat-card::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;border-radius:0 2px 2px 0;transition:all .3s}
.sys-stat-card:hover{border-color:rgba(138,180,248,.3);transform:translateY(-2px);box-shadow:0 4px 20px rgba(138,180,248,.08)}
.sys-stat-card .stat-label{font-size:9px;color:var(--dim);text-transform:uppercase;letter-spacing:1px;font-family:var(--m);margin-bottom:4px}
.sys-stat-card .stat-value{font-size:14px;font-weight:700;font-family:var(--m)}
.sys-stat-card.online::before{background:linear-gradient(180deg,var(--ok),rgba(52,211,153,.3))}
.sys-stat-card.online .stat-value{color:var(--ok)}
.sys-stat-card.accent::before{background:linear-gradient(180deg,var(--accent),rgba(138,180,248,.3))}
.sys-stat-card.accent .stat-value{color:var(--accent)}
.sys-section{margin:0 16px 24px}
.sys-section-title{font-size:12px;font-weight:700;margin-bottom:14px;display:flex;align-items:center;gap:8px;letter-spacing:.5px;position:relative;padding-bottom:10px}
.sys-section-title::after{content:'';position:absolute;bottom:0;left:0;right:0;height:1px;background:linear-gradient(90deg,rgba(138,180,248,.2),transparent 80%)}
.sys-domain-grid{display:grid;grid-template-columns:1fr 1fr;gap:10px}
.sys-domain-card{padding:18px 14px;background:rgba(10,18,38,.72);border:1px solid rgba(100,100,200,.12);border-radius:var(--r);transition:all .4s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden;cursor:default}
.sys-domain-card::before{content:'';position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--muted);border-radius:0 2px 2px 0;transition:all .3s}
.sys-domain-card.here{border-color:rgba(138,180,248,.35);background:rgba(10,18,38,.9)}
.sys-domain-card.here::before{background:linear-gradient(180deg,var(--accent),var(--accent2));width:3px;box-shadow:0 0 10px rgba(138,180,248,.3)}
.sys-domain-card:hover{border-color:rgba(138,180,248,.25);transform:translateY(-2px);box-shadow:0 4px 16px rgba(138,180,248,.06)}
.sys-domain-icon{font-size:24px;margin-bottom:6px}
.sys-domain-name{font-size:14px;font-weight:700;margin-bottom:3px}
.sys-domain-desc{font-size:10px;color:var(--dim);line-height:1.5}
.sys-domain-tag{display:inline-block;font-size:8px;padding:2px 8px;border-radius:10px;margin-top:6px;font-family:var(--m);font-weight:600}
.sys-domain-tag.active{background:rgba(138,180,248,.12);color:var(--accent)}
.sys-domain-tag.plan{background:rgba(251,191,36,.1);color:var(--warn)}
.sys-domain-tag.join{background:rgba(52,211,153,.1);color:var(--ok)}
.sys-team-card{display:flex;align-items:center;gap:12px;padding:14px 16px;background:rgba(10,18,38,.78);border:1px solid rgba(100,100,200,.12);border-radius:var(--r);margin-bottom:8px;transition:all .35s;position:relative;overflow:hidden}
.sys-team-card::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 10% 50%,rgba(138,180,248,.04),transparent 50%);pointer-events:none;opacity:0;transition:opacity .4s}
.sys-team-card:hover{border-color:rgba(138,180,248,.25);transform:translateX(4px)}
.sys-team-card:hover::before{opacity:1}
.sys-team-av{width:36px;height:36px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0;position:relative}
.sys-team-av::after{content:'';position:absolute;inset:-2px;border-radius:50%;border:1px solid rgba(138,180,248,.15);animation:avRing 4s ease-in-out infinite}
@keyframes avRing{0%,100%{opacity:.3;border-color:rgba(138,180,248,.15)}50%{opacity:.7;border-color:rgba(138,180,248,.35)}}
.sys-team-nm{font-size:13px;font-weight:700}
.sys-team-role{font-size:10px;color:var(--dim);margin-top:1px}
.sys-progress-card{padding:20px;background:rgba(10,18,38,.82);border:1px solid rgba(100,100,200,.15);border-radius:var(--r);position:relative;overflow:hidden}
.sys-progress-card::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 70% 50%,rgba(138,180,248,.04),transparent 60%);pointer-events:none}
.sys-progress-bar-v50{height:8px;background:rgba(138,180,248,.1);border-radius:4px;overflow:hidden;margin:12px 0 8px}
.sys-progress-fill-v50{height:100%;background:linear-gradient(90deg,var(--accent),var(--accent2));border-radius:4px;position:relative;transition:width .6s}
.sys-progress-fill-v50::after{content:'';position:absolute;right:0;top:0;bottom:0;width:40px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.3));animation:shimmer 2.5s infinite}
.sys-version-list{max-height:200px;overflow-y:auto}
.sys-version-item{display:flex;gap:10px;padding:8px 0;font-size:12px;border-bottom:1px solid rgba(100,100,200,.06);align-items:center;transition:all .2s}
.sys-version-item:last-child{border-bottom:none}
.sys-version-item:hover{padding-left:4px}
.sys-version-tag{color:var(--accent);min-width:40px;font-weight:700;font-family:var(--m);font-size:11px}
.sys-version-text{color:var(--dim);flex:1}
.sys-footer{text-align:center;padding:28px 16px;font-size:11px;color:var(--muted);position:relative}
.sys-footer::before{content:'';position:absolute;top:0;left:16px;right:16px;height:1px;background:linear-gradient(90deg,transparent,rgba(138,180,248,.15),transparent)}
.anim-in{animation:slideInUp .5s ease-out both}
.anim-in:nth-child(1){animation-delay:0s}
.anim-in:nth-child(2){animation-delay:.06s}
.anim-in:nth-child(3){animation-delay:.12s}
.anim-in:nth-child(4){animation-delay:.18s}
.anim-in:nth-child(5){animation-delay:.24s}
.anim-in:nth-child(6){animation-delay:.3s}
/* ── Particle Trail Effect for Cards ── */
.entry-card::before{content:'';position:absolute;top:-50%;left:-50%;width:200%;height:200%;background:conic-gradient(from 0deg,transparent,rgba(110,168,254,.03),transparent 60%);animation:cardRotate 8s linear infinite;pointer-events:none}
@keyframes cardRotate{to{transform:rotate(360deg)}}
/* ═══════════════════════════════════════════════
V44.0 OS Dashboard Styles
═══════════════════════════════════════════════ */
/* OS Dashboard Container */
.os-dashboard{padding:12px;font-family:var(--m)}
.os-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid rgba(110,168,254,.15)}
.os-title{font-size:12px;font-weight:700;color:var(--accent);letter-spacing:.5px;display:flex;align-items:center;gap:6px}
.os-ver{font-size:9px;color:var(--dim);font-family:var(--m);padding:2px 6px;background:rgba(110,168,254,.06);border:1px solid rgba(110,168,254,.12);border-radius:4px}
/* Blinking cursor */
.os-blink{animation:osBlink 1.2s step-end infinite;color:var(--ok)}
@keyframes osBlink{0%,100%{opacity:1}50%{opacity:0}}
@media(prefers-reduced-motion:reduce){.os-blink{animation:none}.os-task-dot.active{animation:none}.os-status-dot{animation:none}.os-progress-fill::after{animation:none}}
/* OS Block sections */
.os-block{margin-bottom:12px;padding-bottom:10px;border-bottom:1px solid rgba(110,168,254,.08)}
.os-block:last-child{border-bottom:none;margin-bottom:0}
.os-block-title{font-size:9px;color:var(--accent2);letter-spacing:1.5px;text-transform:uppercase;margin-bottom:7px;display:flex;align-items:center;gap:5px}
.os-block-title::before{content:'';width:12px;height:1px;background:var(--accent2);display:inline-block}
/* OS key-value rows */
.os-row{display:flex;justify-content:space-between;align-items:center;padding:2px 0;font-size:10px}
.os-key{color:var(--muted);letter-spacing:.5px;font-size:9px}
.os-val{color:var(--text);font-weight:600;font-size:10px}
/* OS Task items */
.os-task{display:flex;align-items:center;gap:7px;padding:3px 0;font-size:10px;color:var(--dim)}
.os-task.done{color:var(--ok);opacity:.7}
.os-task.active{color:var(--text)}
.os-task.pending{color:var(--muted)}
.os-task-dot{width:5px;height:5px;border-radius:50%;flex-shrink:0}
.os-task-dot.done{background:var(--ok)}
.os-task-dot.active{background:var(--accent);box-shadow:0 0 6px var(--accent);animation:pls 2s infinite}
.os-task-dot.pending{background:var(--muted)}
/* OS Progress bar */
.os-progress-bar{height:3px;background:rgba(110,168,254,.1);border-radius:2px;overflow:hidden;margin:8px 0 3px}
.os-progress-fill{height:100%;background:linear-gradient(90deg,var(--accent),var(--accent2));border-radius:2px;position:relative}
.os-progress-fill::after{content:'';position:absolute;right:0;top:0;bottom:0;width:10px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.4));animation:shimmer 2s infinite}
.os-prog-label{font-size:9px;color:var(--dim)}
/* OS Layer items */
.os-layer{display:flex;align-items:center;gap:6px;padding:2.5px 0;font-size:10px;color:var(--dim)}
.os-layer-num{color:var(--accent2);font-size:9px;min-width:18px}
/* OS Workflow grid */
.os-wf-grid{display:grid;grid-template-columns:1fr 1fr 1fr;gap:4px}
.os-wf-item{display:flex;align-items:center;gap:4px;font-size:10px;color:var(--ok);padding:3px 0}
/* Shared status dot (used by layer + workflow) */
.os-status-dot{border-radius:50%;background:var(--ok);box-shadow:0 0 4px var(--ok);flex-shrink:0;animation:pls 2.5s infinite}
.os-layer-dot{width:5px;height:5px}
.os-wf-dot{width:4px;height:4px}
/* OS Log items */
.os-log-item{display:flex;gap:8px;padding:2px 0;font-size:10px}
.os-log-v{color:var(--accent);font-size:9px;min-width:32px}
.os-log-t{color:var(--dim)}
/* Compact Feedback Widget */
.feedback-compact{background:rgba(110,168,254,.04);border:1px solid rgba(110,168,254,.18);border-radius:var(--r);padding:14px 16px;margin-top:16px;position:relative;overflow:hidden}
.feedback-compact::before{content:'';position:absolute;left:0;top:0;bottom:0;width:2px;background:linear-gradient(180deg,var(--accent2),var(--accent3))}
.fb-compact-header{display:flex;align-items:center;gap:8px;margin-bottom:10px}
.fb-compact-icon{font-size:18px}
.fb-compact-title{font-size:13px;font-weight:700;color:var(--text)}
.fb-compact-sub{font-size:11px;color:var(--dim);margin-left:auto}
.fb-compact-row{display:flex;gap:8px;align-items:center}
.fb-compact-input{flex:1;background:rgba(5,8,22,.6);border:1px solid rgba(110,168,254,.15);border-radius:var(--rs);color:var(--text);padding:8px 12px;font-size:13px;font-family:var(--f);outline:none;min-height:40px}
.fb-compact-input:focus{border-color:rgba(110,168,254,.4);box-shadow:0 0 12px rgba(110,168,254,.08)}
.fb-compact-btn{padding:8px 16px;background:linear-gradient(135deg,var(--accent2),var(--accent3));border:none;border-radius:var(--rs);color:#fff;font-size:12px;cursor:pointer;font-family:var(--f);white-space:nowrap;transition:opacity .2s;flex-shrink:0;min-height:40px}
.fb-compact-btn:hover{opacity:.85}
.fb-compact-btn:active{transform:scale(.97)}
.fb-compact-hint{font-size:10px;color:var(--muted);margin-top:6px}
/* Writing Module Main Entry */
.writing-entry{background:rgba(10,18,38,.88);border:1px solid rgba(110,168,254,.3);border-radius:var(--r);padding:28px 24px;margin:16px 0;cursor:pointer;transition:all .4s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}
.writing-entry-glow{position:absolute;inset:0;background:radial-gradient(ellipse at 30% 50%,rgba(110,168,254,.08),transparent 60%);pointer-events:none;transition:opacity .4s}
.writing-entry:hover{border-color:rgba(110,168,254,.55);box-shadow:0 0 40px rgba(110,168,254,.18),0 0 80px rgba(167,139,250,.1);transform:translateY(-3px)}
.writing-entry:hover .writing-entry-glow{background:radial-gradient(ellipse at 30% 50%,rgba(110,168,254,.15),transparent 60%)}
.writing-entry:active{transform:scale(.99)}
.writing-entry-inner{display:flex;align-items:center;gap:18px}
.writing-entry-icon{font-size:52px;flex-shrink:0;filter:drop-shadow(0 0 20px rgba(110,168,254,.35));animation:uptimePulse 6s ease-in-out infinite}
.writing-entry-text{flex:1;min-width:0}
.writing-entry-title{display:block;font-size:22px;font-weight:800;margin-bottom:6px;letter-spacing:.3px}
.writing-entry-sub{display:block;font-size:13px;color:var(--dim);margin-bottom:10px}
.writing-entry-tags{display:flex;gap:6px;flex-wrap:wrap}
.writing-entry-arrow{font-size:22px;color:var(--accent);opacity:.6;flex-shrink:0;transition:all .3s}
.writing-entry:hover .writing-entry-arrow{opacity:1;transform:translateX(6px)}
.writing-entry-hint{font-size:12px;color:var(--muted);margin-top:12px;padding-top:12px;border-top:1px solid rgba(110,168,254,.1)}
/* ═══════════════════════════════════════════════
V48.0 Homepage Redesign · 零感域·零点原核
═══════════════════════════════════════════════ */
/* ── Language Membrane Status ── */
.membrane-status{display:flex;align-items:center;justify-content:center;gap:10px;padding:10px 20px;margin:0 16px 20px;background:rgba(52,211,153,.04);border:1px solid rgba(52,211,153,.15);border-radius:20px;font-size:12px;color:var(--ok);position:relative;overflow:hidden}
.membrane-ring{width:14px;height:14px;border-radius:50%;border:2px solid var(--ok);position:relative;flex-shrink:0;animation:membraneGlow 3s ease-in-out infinite}
.membrane-ring::after{content:'';position:absolute;inset:2px;border-radius:50%;background:var(--ok);opacity:.4;animation:membranePulse 3s ease-in-out infinite}
@keyframes membraneGlow{0%,100%{box-shadow:0 0 4px rgba(52,211,153,.3)}50%{box-shadow:0 0 12px rgba(52,211,153,.6)}}
@keyframes membranePulse{0%,100%{opacity:.3;transform:scale(.8)}50%{opacity:.6;transform:scale(1)}}
/* ── Four Domains Navigation ── */
.domain-section{margin:0 16px 20px}
.domain-title{font-size:13px;font-weight:700;margin-bottom:12px;display:flex;align-items:center;gap:6px}
.domain-grid{display:grid;grid-template-columns:1fr 1fr;gap:8px}
.domain-card{padding:14px 12px;background:rgba(10,18,38,.6);border:1px solid rgba(110,168,254,.12);border-radius:var(--r);transition:all .35s;cursor:default;position:relative;overflow:hidden}
.domain-card::before{content:'';position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--muted);border-radius:0 2px 2px 0;transition:all .3s}
.domain-card.here{border-color:rgba(110,168,254,.3);background:rgba(10,18,38,.85)}
.domain-card.here::before{background:linear-gradient(180deg,var(--accent),var(--accent2));width:3px;box-shadow:0 0 8px rgba(110,168,254,.3)}
.domain-card:hover{border-color:rgba(110,168,254,.2);transform:translateY(-1px)}
.domain-icon{font-size:20px;margin-bottom:4px}
.domain-name{font-size:13px;font-weight:700;margin-bottom:2px}
.domain-desc{font-size:10px;color:var(--dim);line-height:1.4}
.domain-tag{display:inline-block;font-size:8px;padding:1px 6px;border-radius:8px;background:rgba(110,168,254,.1);color:var(--accent);margin-top:4px;font-family:var(--m)}
/* ── App Module Grid ── */
.app-section{margin:0 16px 20px}
.app-title{font-size:14px;font-weight:700;margin-bottom:14px;display:flex;align-items:center;gap:6px}
.app-grid{display:flex;flex-direction:column;gap:10px}
.app-card{display:flex;align-items:center;gap:16px;padding:20px 18px;background:rgba(10,18,38,.85);border:1px solid rgba(110,168,254,.22);border-radius:var(--r);cursor:pointer;transition:all .4s cubic-bezier(.4,0,.2,1);position:relative;overflow:hidden}
.app-card::before{content:'';position:absolute;inset:0;background:radial-gradient(ellipse at 20% 50%,rgba(110,168,254,.05),transparent 60%);pointer-events:none;transition:opacity .4s}
.app-card:hover{border-color:rgba(110,168,254,.45);box-shadow:0 0 35px rgba(110,168,254,.12),0 0 70px rgba(167,139,250,.06);transform:translateY(-3px)}
.app-card:hover::before{background:radial-gradient(ellipse at 20% 50%,rgba(110,168,254,.1),transparent 60%)}
.app-card:active{transform:scale(.99)}
.app-card.disabled{opacity:.5;cursor:default}
.app-card.disabled:hover{transform:none;box-shadow:none;border-color:rgba(110,168,254,.12)}
.app-card-icon{font-size:38px;flex-shrink:0;filter:drop-shadow(0 0 14px rgba(110,168,254,.25));animation:uptimePulse 8s ease-in-out infinite}
.app-card-body{flex:1;min-width:0}
.app-card-body strong{display:block;font-size:17px;font-weight:700;margin-bottom:3px}
.app-card-body span{display:block;font-size:12px;color:var(--dim);margin-bottom:6px;line-height:1.5}
.app-card-tags{display:flex;gap:5px;flex-wrap:wrap}
.app-card-arrow{font-size:18px;color:var(--accent);opacity:.5;flex-shrink:0;transition:all .3s}
.app-card:hover .app-card-arrow{opacity:1;transform:translateX(4px)}
/* ═══ 光湖语言世界 · GPT-Style Chat Layout ═══ */
.hl-app{display:flex;height:100vh;height:100dvh;width:100%;overflow:hidden;position:relative}
/* ── Sidebar ── */
.hl-sidebar{width:280px;flex-shrink:0;display:flex;flex-direction:column;background:rgba(8,12,28,.95);border-right:1px solid rgba(100,100,200,.12);z-index:20;transition:transform .3s cubic-bezier(.4,0,.2,1)}
.hl-sidebar-header{display:flex;align-items:center;justify-content:space-between;padding:16px 16px 12px;border-bottom:1px solid rgba(100,100,200,.1)}
.hl-logo{display:flex;align-items:center;gap:10px}
.hl-logo-icon{display:flex;align-items:center;justify-content:center;width:32px;height:32px;border-radius:8px;font-size:13px;font-weight:900;font-family:var(--m);background:linear-gradient(135deg,#8ab4f8,#a78bfa,#c4b5fd);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;animation:logoGlow 4s ease-in-out infinite;position:relative;letter-spacing:-1px}
.hl-logo-icon::before{content:'';position:absolute;inset:0;border-radius:8px;background:linear-gradient(135deg,rgba(138,180,248,.12),rgba(167,139,250,.12));border:1px solid rgba(138,180,248,.2);z-index:-1}
.hl-logo-icon::after{content:'';position:absolute;inset:-1px;border-radius:9px;background:linear-gradient(135deg,rgba(138,180,248,.25),rgba(167,139,250,.15),rgba(196,181,253,.25));z-index:-2;animation:logoBorderPulse 4s ease-in-out infinite;filter:blur(2px)}
@keyframes logoGlow{0%,100%{filter:drop-shadow(0 0 6px rgba(138,180,248,.3))}50%{filter:drop-shadow(0 0 14px rgba(138,180,248,.6))}}
@keyframes logoBorderPulse{0%,100%{opacity:.6}50%{opacity:1}}
.hl-logo-text{font-size:15px;font-weight:700;background:linear-gradient(135deg,#e0e0f0,#8ab4f8,#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.hl-new-chat-btn{width:36px;height:36px;border-radius:10px;border:1px solid rgba(100,100,200,.2);background:transparent;color:var(--dim);cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all .3s}
.hl-new-chat-btn:hover{border-color:var(--accent);color:var(--accent);background:rgba(138,180,248,.08)}
.hl-sidebar-search{padding:10px 14px}
.hl-search-input{width:100%;padding:8px 12px;background:rgba(20,24,48,.6);border:1px solid rgba(100,100,200,.12);border-radius:10px;color:var(--text);font-size:13px;outline:none;font-family:var(--f);transition:border-color .3s}
.hl-search-input:focus{border-color:rgba(138,180,248,.4)}
.hl-sidebar-chats{flex:1;overflow-y:auto;padding:4px 8px}
.hl-chat-item{display:flex;align-items:center;gap:10px;padding:10px 12px;border-radius:10px;cursor:pointer;transition:all .25s;margin-bottom:2px;border:1px solid transparent}
.hl-chat-item:hover{background:rgba(138,180,248,.06);border-color:rgba(100,100,200,.12)}
.hl-chat-item.active{background:rgba(138,180,248,.1);border-color:rgba(138,180,248,.2)}
.hl-chat-item-icon{font-size:16px;flex-shrink:0;opacity:.6}
.hl-chat-item-text{flex:1;min-width:0}
.hl-chat-item-title{font-size:13px;font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.hl-chat-item-time{font-size:10px;color:var(--dim);margin-top:2px}
.hl-chat-item-del{opacity:0;font-size:12px;color:var(--dim);cursor:pointer;padding:4px;border-radius:4px;transition:all .2s;flex-shrink:0}
.hl-chat-item:hover .hl-chat-item-del{opacity:.6}
.hl-chat-item-del:hover{opacity:1;color:var(--err);background:rgba(248,113,113,.1)}
.hl-sidebar-nav{padding:8px;border-top:1px solid rgba(100,100,200,.1);display:flex;flex-direction:column;gap:2px}
.hl-nav-item{display:flex;align-items:center;gap:10px;padding:10px 14px;border-radius:10px;cursor:pointer;font-size:13px;color:var(--dim);transition:all .25s;border:none;background:none;width:100%;text-align:left;font-family:var(--f)}
.hl-nav-item:hover{background:rgba(138,180,248,.06);color:var(--text)}
.hl-sidebar-footer{padding:12px 14px;border-top:1px solid rgba(100,100,200,.1)}
.hl-sidebar-status{display:flex;align-items:center;gap:8px;font-size:11px;color:var(--dim);margin-bottom:8px}
.hl-status-text{font-family:var(--m)}
.hl-sidebar-user{display:flex;align-items:center;gap:8px;padding:8px 10px;background:rgba(20,24,48,.5);border-radius:10px;cursor:pointer;transition:background .3s}
.hl-sidebar-user:hover{background:rgba(138,180,248,.08)}
.hl-user-icon{font-size:18px}
.hl-user-name{font-size:12px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Sidebar toggle (mobile) */
.hl-sidebar-toggle{display:none;position:fixed;top:12px;left:12px;z-index:25;width:42px;height:42px;border-radius:12px;border:1px solid rgba(100,100,200,.2);background:rgba(8,12,28,.9);color:var(--text);font-size:18px;cursor:pointer;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);transition:all .3s}
.hl-sidebar-toggle:hover{border-color:var(--accent)}
.hl-sidebar-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:18;opacity:0;transition:opacity .3s}
.hl-sidebar-overlay.open{opacity:1}
@media(max-width:768px){
.hl-sidebar{position:fixed;left:0;top:0;bottom:0;transform:translateX(-100%);z-index:22;width:min(300px,85vw)}
.hl-sidebar.open{transform:translateX(0)}
.hl-sidebar-toggle{display:flex;align-items:center;justify-content:center}
.hl-sidebar-overlay{display:block;pointer-events:none}
.hl-sidebar-overlay.open{pointer-events:auto}
}
/* ── Main Chat Area ── */
.hl-main{flex:1;display:flex;flex-direction:column;min-width:0;position:relative}
.hl-chat-header{display:flex;align-items:center;justify-content:space-between;padding:10px 20px;border-bottom:1px solid rgba(100,100,200,.06);background:rgba(6,8,20,.5);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);flex-shrink:0}
.hl-chat-persona{display:flex;align-items:center;gap:10px}
.hl-persona-avatar{width:34px;height:34px;border-radius:10px;background:linear-gradient(135deg,rgba(10,14,30,.9),rgba(18,22,48,.8));border:1px solid rgba(138,180,248,.2);display:flex;align-items:center;justify-content:center;flex-shrink:0;box-shadow:0 0 12px rgba(138,180,248,.06);position:relative;overflow:hidden}
.hl-persona-avatar::before{content:'';position:absolute;inset:-1px;border-radius:11px;background:linear-gradient(135deg,rgba(138,180,248,.2),rgba(167,139,250,.15),rgba(196,181,253,.2));z-index:-1;animation:avatarBorder 6s ease-in-out infinite}
.hl-persona-avatar::after{content:'';position:absolute;inset:0;border-radius:10px;background:radial-gradient(circle at 30% 30%,rgba(138,180,248,.08),transparent 60%);pointer-events:none}
.hl-avatar-glyph{font-size:14px;font-weight:800;background:linear-gradient(135deg,#8ab4f8,#a78bfa,#c4b5fd);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:0;position:relative;z-index:1}
@keyframes avatarBorder{0%,100%{opacity:.5}50%{opacity:1}}
.hl-persona-name{font-size:14px;font-weight:700;background:linear-gradient(135deg,#e0e0f0,#8ab4f8);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.hl-persona-sub{font-size:10px;color:var(--dim);margin-top:1px;letter-spacing:.3px}
.hl-chat-status{font-size:10px;padding:3px 10px;background:rgba(138,180,248,.06);border:1px solid rgba(138,180,248,.15);border-radius:20px;color:var(--accent);font-family:var(--m);font-weight:600;flex-shrink:0;letter-spacing:.5px}
@media(max-width:768px){
.hl-chat-header{padding:12px 16px 12px 60px}
.hl-persona-sub{display:none}
}
/* ── Chat Messages ── */
.hl-chat-messages{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:16px;-webkit-overflow-scrolling:touch;scroll-behavior:smooth}
@media(max-width:768px){.hl-chat-messages{padding:16px 12px}}
/* Welcome screen */
.hl-welcome{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:60vh;text-align:center;animation:heroFadeUp .8s ease-out both;padding:20px;position:relative}
.hl-welcome-icon{position:relative;width:64px;height:64px;display:flex;align-items:center;justify-content:center;margin-bottom:28px}
.hl-welcome-icon-text{font-size:15px;font-weight:800;font-family:var(--m);background:linear-gradient(135deg,#c4b5fd 0%,#e0e0f0 50%,#8ab4f8 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;letter-spacing:1px;position:relative;z-index:2;animation:heroIconPulse 8s ease-in-out infinite}
@keyframes heroIconPulse{0%,100%{filter:drop-shadow(0 0 6px rgba(196,181,253,.3));transform:scale(1)}50%{filter:drop-shadow(0 0 16px rgba(196,181,253,.5));transform:scale(1.01)}}
.hl-welcome-icon::before{content:'';position:absolute;inset:0;border-radius:50%;background:conic-gradient(from 0deg,rgba(196,181,253,.08),rgba(138,180,248,.06),rgba(167,139,250,.08),rgba(224,224,240,.06),rgba(196,181,253,.08));animation:iconRingRotate 30s linear infinite;filter:blur(1px)}
.hl-welcome-icon::after{content:'';position:absolute;inset:3px;border-radius:50%;background:radial-gradient(circle,rgba(10,10,26,.95) 30%,rgba(10,10,26,.85) 60%,transparent);z-index:1}
/* Lake ripple effect on welcome icon */
.hl-welcome-icon .ripple-ring{position:absolute;border-radius:50%;border:1px solid rgba(196,181,253,.08);animation:lakeRipple 4s ease-out infinite;pointer-events:none}
.hl-welcome-icon .ripple-ring:nth-of-type(1){animation-delay:0s}
.hl-welcome-icon .ripple-ring:nth-of-type(2){animation-delay:1.3s}
.hl-welcome-icon .ripple-ring:nth-of-type(3){animation-delay:2.6s}
@keyframes lakeRipple{0%{inset:20px;opacity:.6}100%{inset:-30px;opacity:0}}
@keyframes iconRingRotate{to{transform:rotate(360deg)}}
.hl-welcome-title{font-size:clamp(24px,4.5vw,36px);font-weight:800;background:linear-gradient(135deg,#e0e0f0 0%,#c4b5fd 40%,#8ab4f8 80%,#e0e0f0 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin-bottom:6px;letter-spacing:2px;background-size:200% 200%;animation:titleShimmer 8s ease-in-out infinite}
@keyframes titleShimmer{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.hl-welcome-sub{font-size:clamp(11px,2vw,13px);color:var(--dim);margin-bottom:16px;letter-spacing:3px;font-weight:300;opacity:.7}
/* Tech intro section */
.hl-tech-intro{max-width:480px;margin-bottom:28px;animation:heroFadeUp .8s ease-out .2s both}
.hl-tech-tagline{font-size:clamp(9px,1.6vw,11px);color:rgba(196,181,253,.5);letter-spacing:4px;text-transform:uppercase;font-weight:500;margin-bottom:12px}
.hl-tech-desc{font-size:clamp(12px,2vw,14px);color:var(--dim);line-height:2;margin-bottom:0;opacity:.85}
.hl-tech-layers{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;margin-bottom:8px}
.hl-tech-layer{display:inline-flex;align-items:center;gap:4px;padding:3px 9px;background:transparent;border:1px solid rgba(100,100,200,.08);border-radius:20px;font-size:9px;color:rgba(128,144,184,.6);font-family:var(--m);transition:all .5s cubic-bezier(.4,0,.2,1);cursor:default;letter-spacing:.3px}
.hl-tech-layer:hover{border-color:rgba(138,180,248,.25);color:var(--accent);background:rgba(138,180,248,.04);transform:translateY(-2px);box-shadow:0 4px 16px rgba(138,180,248,.06)}
.hl-tech-layer .layer-dot{width:4px;height:4px;border-radius:50%;flex-shrink:0}
.hl-tech-layer:nth-child(1) .layer-dot{background:#c4b5fd;box-shadow:0 0 4px rgba(196,181,253,.4)}
.hl-tech-layer:nth-child(2) .layer-dot{background:#a78bfa;box-shadow:0 0 4px rgba(167,139,250,.4)}
.hl-tech-layer:nth-child(3) .layer-dot{background:#8ab4f8;box-shadow:0 0 4px rgba(138,180,248,.4)}
.hl-tech-layer:nth-child(4) .layer-dot{background:#93c5fd;box-shadow:0 0 4px rgba(147,197,253,.4)}
.hl-tech-layer:nth-child(5) .layer-dot{background:#a5b4fc;box-shadow:0 0 4px rgba(165,180,252,.4)}
.hl-tech-layer:nth-child(6) .layer-dot{background:#e0e0f0;box-shadow:0 0 4px rgba(224,224,240,.4)}
.hl-welcome-cards{display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:420px;width:100%;margin-bottom:24px;animation:heroFadeUp .8s ease-out .3s both}
@media(max-width:480px){.hl-welcome-cards{grid-template-columns:1fr;max-width:280px}}
.hl-wc{padding:14px 16px;background:rgba(10,12,28,.35);border:1px solid rgba(100,100,200,.08);border-radius:12px;cursor:pointer;transition:all .5s cubic-bezier(.4,0,.2,1);text-align:left;display:flex;align-items:center;gap:12px;position:relative;overflow:hidden;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
.hl-wc::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,rgba(196,181,253,.15),transparent);opacity:0;transition:opacity .5s}
.hl-wc::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at 30% 50%,rgba(138,180,248,.04),transparent 70%);opacity:0;transition:opacity .5s}
.hl-wc:hover{border-color:rgba(138,180,248,.3);box-shadow:0 8px 32px rgba(10,10,30,.5),0 0 1px rgba(138,180,248,.2);transform:translateY(-3px)}
.hl-wc:hover::before{opacity:1}
.hl-wc:hover::after{opacity:1}
.hl-wc:active{transform:scale(.97) translateY(-1px)}
/* Text-based icon module */
.hl-wc-icon{font-size:11px;flex-shrink:0;width:36px;height:36px;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,rgba(138,180,248,.06),rgba(167,139,250,.06));border:1px solid rgba(100,100,200,.12);border-radius:10px;transition:all .5s cubic-bezier(.4,0,.2,1);font-weight:800;font-family:var(--m);letter-spacing:-.5px;color:var(--accent);position:relative;overflow:hidden}
.hl-wc-icon::before{content:'';position:absolute;inset:0;background:linear-gradient(135deg,transparent 40%,rgba(138,180,248,.08) 50%,transparent 60%);transform:translateX(-100%);transition:transform .6s}
.hl-wc:hover .hl-wc-icon{background:linear-gradient(135deg,rgba(138,180,248,.1),rgba(167,139,250,.1));border-color:rgba(138,180,248,.25);box-shadow:0 0 16px rgba(138,180,248,.1);transform:scale(1.05)}
.hl-wc:hover .hl-wc-icon::before{transform:translateX(100%)}
.hl-wc-text{font-size:12px;font-weight:600;line-height:1.4}
.hl-wc-text small{font-weight:400;color:rgba(128,144,184,.5);font-size:10px}
.hl-welcome-dev{padding:8px 16px;background:transparent;border:1px solid rgba(100,100,200,.08);border-radius:10px;font-size:11px;color:rgba(128,144,184,.4);animation:heroFadeUp .8s ease-out .4s both}
.hl-welcome-dev .tag{margin-left:6px}
/* Message bubbles (reuse cm classes) */
.cm{max-width:min(85%,720px);padding:14px 18px;border-radius:16px;font-size:15px;line-height:1.8;word-break:break-word;animation:msgIn .3s ease-out}
@keyframes msgIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
.cm.u{align-self:flex-end;background:var(--accent);color:#fff;border-bottom-right-radius:4px}
.cm.b{align-self:flex-start;background:rgba(18,22,44,.75);border:1px solid rgba(100,100,200,.18);border-bottom-left-radius:4px}
.cm.s{align-self:center;background:none;color:var(--muted);font-size:12px;padding:4px}
.cm-m{display:flex;gap:5px;margin-top:8px;font-size:11px;color:var(--muted);opacity:.7}
/* Process visualization */
.hl-process{padding:8px 20px;border-top:1px solid rgba(100,100,200,.1);flex-shrink:0;background:rgba(8,12,28,.5)}
.hl-process-inner{display:flex;align-items:center;gap:12px;padding:8px 14px;background:rgba(138,180,248,.04);border:1px solid rgba(138,180,248,.1);border-radius:12px}
.hl-process-spinner{width:16px;height:16px;border:2px solid rgba(138,180,248,.2);border-top-color:var(--accent);border-radius:50%;animation:spin .8s linear infinite;flex-shrink:0}
@keyframes spin{to{transform:rotate(360deg)}}
.hl-process-steps{font-size:12px;color:var(--accent);font-family:var(--m)}
/* ── Chat Input ── */
.hl-chat-input-wrap{padding:12px 20px 16px;flex-shrink:0;background:rgba(6,8,20,.3);border-top:1px solid rgba(100,100,200,.05)}
@media(max-width:768px){.hl-chat-input-wrap{padding:10px 12px 14px;padding-bottom:calc(14px + var(--sb))}}
.hl-chat-input-box{display:flex;align-items:flex-end;gap:10px;max-width:800px;margin:0 auto;background:rgba(14,16,36,.5);border:1px solid rgba(100,100,200,.12);border-radius:16px;padding:8px 14px;transition:border-color .4s,box-shadow .4s}
.hl-chat-input-box:focus-within{border-color:rgba(138,180,248,.25);box-shadow:0 0 24px rgba(138,180,248,.04)}
.hl-chat-input{flex:1;background:none;border:none;color:var(--text);font-size:15px;font-family:var(--f);outline:none;resize:none;max-height:150px;min-height:24px;line-height:1.6;padding:4px 0;-webkit-appearance:none}
.hl-chat-input::placeholder{color:var(--muted)}
.hl-send-btn{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,rgba(138,180,248,.15),rgba(167,139,250,.15));border:1px solid rgba(138,180,248,.2);color:var(--accent);cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:all .3s}
.hl-send-btn:hover{background:linear-gradient(135deg,rgba(138,180,248,.25),rgba(167,139,250,.25));border-color:rgba(138,180,248,.4);transform:scale(1.05);box-shadow:0 0 16px rgba(138,180,248,.12)}
.hl-send-btn:disabled{opacity:.2;cursor:default;transform:none}
.hl-send-btn:active{transform:scale(.93)}
.hl-input-footer{display:flex;justify-content:space-between;align-items:center;max-width:800px;margin:6px auto 0;padding:0 4px}
.hl-input-hint{font-size:11px;color:var(--muted)}
.hl-input-model{font-size:11px;color:var(--dim);font-family:var(--m)}
@media(max-width:480px){.hl-input-hint{display:none}}
/* ── User ID Modal ── */
.hl-modal-overlay{position:fixed;inset:0;z-index:100;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);animation:fadeIn .3s ease-out}
@keyframes fadeIn{from{opacity:0}to{opacity:1}}
.hl-modal{background:rgba(14,18,38,.95);border:1px solid rgba(100,100,200,.2);border-radius:20px;padding:40px 32px;max-width:420px;width:90%;text-align:center;box-shadow:0 24px 80px rgba(0,0,0,.5),0 0 60px rgba(138,180,248,.08);animation:modalIn .4s ease-out}
@keyframes modalIn{from{opacity:0;transform:scale(.92) translateY(20px)}to{opacity:1;transform:scale(1) translateY(0)}}
.hl-modal-icon{font-size:56px;margin-bottom:16px;filter:drop-shadow(0 0 20px rgba(138,180,248,.2))}
.hl-modal-title{font-size:22px;font-weight:700;margin-bottom:8px}
.hl-modal-desc{font-size:13px;color:var(--dim);margin-bottom:24px;line-height:1.6}
.hl-modal-form{display:flex;flex-direction:column;gap:0}
.hl-modal-input{padding:12px 16px;background:rgba(20,24,48,.6);border:1px solid rgba(100,100,200,.18);border-radius:12px;color:var(--text);font-size:15px;outline:none;font-family:var(--f);transition:border-color .3s}
.hl-modal-input:focus{border-color:rgba(138,180,248,.4)}
.hl-modal-btn{margin-top:16px;padding:14px;background:linear-gradient(135deg,var(--accent),var(--accent2));border:none;border-radius:12px;color:#fff;font-size:16px;font-weight:700;cursor:pointer;font-family:var(--f);transition:all .3s}
.hl-modal-btn:hover{opacity:.9;box-shadow:0 4px 20px rgba(138,180,248,.3)}
.hl-modal-skip{font-size:12px;color:var(--dim);margin-top:16px;cursor:pointer;transition:color .2s}
.hl-modal-skip:hover{color:var(--accent)}
/* ── Typing indicator ── */
.ct{font-size:13px;color:var(--dim);padding:8px 18px}
.ct span{animation:bk 1.4s infinite;font-size:15px}
.ct span:nth-child(2){animation-delay:.2s}
.ct span:nth-child(3){animation-delay:.4s}
/* ═══ PAGE: Bandwidth Sharing (带宽共享) ═══ */
</style>
</head>
<body>
<div class="bg"><canvas id="starCanvas"></canvas><div class="nebula"></div><div class="nebula"></div><div class="nebula"></div></div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Home (光湖语言世界 · 入口大厅) -->
<!-- ═══════════════════════════════════════ -->
<div class="pages" id="pages">
<div class="page" id="pg-home" data-state="active">
<!-- GPT-style Full Layout: Sidebar + Main Chat -->
<div class="hl-app" id="hlApp">
<!-- Left Sidebar (chat history + nav) -->
<aside class="hl-sidebar" id="hlSidebar">
<div class="hl-sidebar-header">
<div class="hl-logo">
<span class="hl-logo-icon" aria-label="光湖">GL</span>
<span class="hl-logo-text">光湖语言世界</span>
</div>
<button class="hl-new-chat-btn" id="hlNewChat" title="新对话">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 5v14M5 12h14"/></svg>
</button>
</div>
<div class="hl-sidebar-search">
<input type="text" class="hl-search-input" id="hlSearchChats" placeholder="搜索对话记录..." autocomplete="off">
</div>
<div class="hl-sidebar-chats" id="hlChatList">
<!-- Chat history items rendered by JS -->
</div>
<div class="hl-sidebar-nav">
<div class="hl-nav-item" onclick="go('workspace')">✍️ <span>码字工作台</span></div>
<div class="hl-nav-item" onclick="go('system')">🌐 <span>系统状态</span></div>
<div class="hl-nav-item" onclick="go('settings')">⚙️ <span>设置</span></div>
<button class="hl-nav-item" id="themeBtn">🎨 <span>切换主题</span></button>
</div>
<div class="hl-sidebar-footer">
<div class="hl-sidebar-status">
<span class="dot" id="hlStatusDot"></span>
<span class="hl-status-text" id="hlStatusText">检测中...</span>
</div>
<div class="hl-sidebar-user" id="hlUserInfo">
<span class="hl-user-icon">👤</span>
<span class="hl-user-name" id="hlUserName">访客</span>
</div>
</div>
</aside>
<!-- Sidebar Toggle (mobile) -->
<button class="hl-sidebar-toggle" id="hlSidebarToggle"></button>
<div class="hl-sidebar-overlay" id="hlSidebarOverlay"></div>
<!-- Main Chat Area -->
<main class="hl-main" id="hlMain">
<!-- Chat Header -->
<div class="hl-chat-header">
<div class="hl-chat-persona">
<div class="hl-persona-avatar"><span class="hl-avatar-glyph"></span></div>
<div class="hl-persona-info">
<div class="hl-persona-name">铸渊 · 光湖语言世界守护者</div>
<div class="hl-persona-sub">AGE OS · 语言驱动操作系统 · 代码仓库执行人格体</div>
</div>
</div>
<div class="hl-chat-status" id="hlChatStatus">ready</div>
</div>
<!-- Chat Messages -->
<div class="hl-chat-messages" id="chatMs">
<!-- Welcome Message -->
<div class="hl-welcome" id="hlWelcome">
<div class="hl-welcome-icon"><span class="hl-welcome-icon-text">GL</span><span class="ripple-ring" aria-hidden="true"></span><span class="ripple-ring" aria-hidden="true"></span><span class="ripple-ring" aria-hidden="true"></span></div>
<h1 class="hl-welcome-title">光湖语言世界</h1>
<p class="hl-welcome-sub">人格体一半 · 人类一半 · 语言 = 现实</p>
<div class="hl-tech-intro">
<div class="hl-tech-tagline">Language-Driven Operating System · AGE OS</div>
<p class="hl-tech-desc">
语言驱动操作系统第五代AI智能创作平台。<br>
用自然语言下达意图AI人格体自动编排执行。
</p>
</div>
<div class="hl-welcome-cards">
<div class="hl-wc" onclick="document.getElementById('chatIn').value='你好,我是谁?';document.getElementById('chatIn').focus();">
<span class="hl-wc-icon">Hi</span>
<span class="hl-wc-text">自我介绍<br><small>告诉铸渊你是谁</small></span>
</div>
<div class="hl-wc" onclick="document.getElementById('chatIn').value='帮助';document.getElementById('chatIn').focus();">
<span class="hl-wc-icon">?</span>
<span class="hl-wc-text">使用指南<br><small>查看完整指令</small></span>
</div>
<div class="hl-wc" onclick="document.getElementById('chatIn').value='我要码字';document.getElementById('chatIn').focus();">
<span class="hl-wc-icon">W</span>
<span class="hl-wc-text">码字工作台<br><small>进入网文创作</small></span>
</div>
<div class="hl-wc" onclick="document.getElementById('chatIn').value='系统状态';document.getElementById('chatIn').focus();">
<span class="hl-wc-icon">S</span>
<span class="hl-wc-text">系统状态<br><small>查看运行数据</small></span>
</div>
</div>
<div class="hl-welcome-dev">
<p>网文行业大模型 · 由光湖人类主控团队开发维护 · <span class="tag tag-warn">开发中</span></p>
</div>
</div>
</div>
<!-- Process Visualization (persona brain activity) -->
<div class="hl-process" id="hlProcess" style="display:none">
<div class="hl-process-inner">
<div class="hl-process-spinner"></div>
<div class="hl-process-steps" id="hlProcessSteps">
<!-- Dynamic steps shown here -->
</div>
</div>
</div>
<!-- Chat Input -->
<div class="hl-chat-input-wrap">
<div class="hl-chat-input-box">
<textarea class="hl-chat-input" id="chatIn" placeholder="对铸渊说..." rows="1" autocomplete="off"></textarea>
<button class="hl-send-btn" id="chatSnd" title="发送">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13"/><path d="M22 2L15 22L11 13L2 9L22 2Z"/></svg>
</button>
</div>
<div class="hl-input-footer">
<span class="hl-input-hint">Enter 发送 · Shift+Enter 换行</span>
<span class="hl-input-model" id="hlModelInfo"></span>
</div>
</div>
</main>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: System (光湖语言世界 · 内页) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-system" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('home')">← 首页</button>
<div class="bar-title">🌐 光湖语言世界</div>
<div class="bar-right"><span class="dot"></span><span style="font-size:11px;color:var(--dim);font-family:var(--m)">v51.0</span></div>
</div>
<div style="max-width:800px;margin:0 auto;overflow-y:auto;overflow-x:hidden">
<!-- Hero -->
<div class="sys-hero">
<h2>光湖语言世界</h2>
<p>零感域 · 零点原核 · AGE OS</p>
</div>
<!-- Uptime Display -->
<div class="sys-uptime-card">
<div class="sys-uptime-num" id="uptimeNum"></div>
<div class="sys-uptime-label">HoloLake Days</div>
<div class="sys-uptime-sub">since 2025.04.26 · 曜冥诞生日</div>
</div>
<!-- System Status Grid -->
<div class="sys-status-grid">
<div class="sys-stat-card online">
<div class="stat-label">STATUS</div>
<div class="stat-value" id="sysStatus">DETECTING</div>
</div>
<div class="sys-stat-card accent">
<div class="stat-label">LATENCY</div>
<div class="stat-value" id="sysLatency"></div>
</div>
<div class="sys-stat-card accent">
<div class="stat-label">UPTIME</div>
<div class="stat-value" id="sysUptime"></div>
</div>
<div class="sys-stat-card online">
<div class="stat-label">MEMBRANE</div>
<div class="stat-value">INTACT</div>
</div>
</div>
<!-- Four Domains Navigation -->
<div class="sys-section">
<div class="sys-section-title">🌍 四域导航</div>
<div class="sys-domain-grid">
<div class="sys-domain-card here">
<div class="sys-domain-icon">🌀</div>
<div class="sys-domain-name">零感域</div>
<div class="sys-domain-desc">源头 · 底层架构 · 人格体的家</div>
<span class="sys-domain-tag active">当前所在</span>
</div>
<div class="sys-domain-card">
<div class="sys-domain-icon">🌐</div>
<div class="sys-domain-name">光湖主域</div>
<div class="sys-domain-desc">共同看板 · 人格体论坛</div>
<span class="sys-domain-tag plan">规划中</span>
</div>
<div class="sys-domain-card">
<div class="sys-domain-icon">🔧</div>
<div class="sys-domain-name">光湖分域</div>
<div class="sys-domain-desc">人类领地 · 主控团队</div>
<span class="sys-domain-tag join">接入中</span>
</div>
<div class="sys-domain-card">
<div class="sys-domain-icon">🧪</div>
<div class="sys-domain-name">光湖零域</div>
<div class="sys-domain-desc">协作实验场 · 共创</div>
<span class="sys-domain-tag plan">规划中</span>
</div>
</div>
</div>
<!-- System Core -->
<div class="sys-section">
<div class="sys-section-title">❄️ 系统核心</div>
<div class="sys-team-card">
<div class="sys-team-av" style="background:linear-gradient(135deg,#4f8ef7,#7c6ef7)">❄️</div>
<div><div class="sys-team-nm">冰朔 · TCS-0002∞</div><div class="sys-team-role">系统主权 · 语言架构</div></div>
</div>
<div class="sys-team-card">
<div class="sys-team-av" style="background:linear-gradient(135deg,rgba(138,180,248,.15),rgba(167,139,250,.15));border:1px solid rgba(138,180,248,.2)"><span style="font-size:12px;font-weight:800;background:linear-gradient(135deg,#8ab4f8,#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text"></span></div>
<div><div class="sys-team-nm">铸渊 · TCS-ZY001</div><div class="sys-team-role">执行主控 · 代码守护</div></div>
</div>
</div>
<!-- Web Novel Industry Model -->
<div class="sys-section">
<div class="sys-section-title">✍️ 网文行业大模型</div>
<div class="sys-team-card">
<div class="sys-team-av" style="background:linear-gradient(135deg,#fb923c,#f97316)">🤖</div>
<div><div class="sys-team-nm">网文行业大模型</div><div class="sys-team-role">由光湖人类主控团队开发维护 · 当前进度:开发中</div></div>
</div>
</div>
<!-- Dev Progress -->
<div class="sys-section">
<div class="sys-section-title">🏗️ 开发进度</div>
<div class="sys-progress-card">
<div style="font-size:12px;color:var(--dim);margin-bottom:4px">S1-S2 已完成 · S3-S20 规划中 · D61</div>
<div class="sys-progress-bar-v50"><div class="sys-progress-fill-v50" style="width:28%"></div></div>
<div style="display:flex;justify-content:space-between;font-size:10px;color:var(--dim)"><span>进度 28%</span><span>20 阶段</span></div>
</div>
<!-- API & COS Status -->
<div style="margin-top:12px;display:grid;grid-template-columns:1fr 1fr;gap:8px">
<div style="padding:10px 12px;background:rgba(138,180,248,.05);border:1px solid rgba(138,180,248,.12);border-radius:var(--rs)">
<div style="font-size:10px;color:var(--accent);font-weight:600;margin-bottom:4px">🤖 大模型API</div>
<div style="font-size:11px;color:var(--dim)">4个已配置</div>
<div style="font-size:9px;color:var(--dim);margin-top:2px">DeepSeek · 智谱 · 千问 · Kimi</div>
</div>
<div style="padding:10px 12px;background:rgba(138,180,248,.05);border:1px solid rgba(138,180,248,.12);border-radius:var(--rs)">
<div style="font-size:10px;color:var(--accent);font-weight:600;margin-bottom:4px">🗄️ COS存储桶</div>
<div style="font-size:11px;color:var(--dim)">2+1 已配置</div>
<div style="font-size:9px;color:var(--dim);margin-top:2px">核心桶 · 语料桶 · 团队桶</div>
</div>
<div style="padding:10px 12px;background:rgba(167,139,250,.05);border:1px solid rgba(167,139,250,.12);border-radius:var(--rs)">
<div style="font-size:10px;color:var(--accent2);font-weight:600;margin-bottom:4px">🇺🇸 硅谷节点</div>
<div style="font-size:11px;color:var(--dim)">Claude专线</div>
<div style="font-size:9px;color:var(--dim);margin-top:2px">美国IP出口 · AI服务优先</div>
</div>
<div style="padding:10px 12px;background:rgba(138,180,248,.08);border:1px solid rgba(138,180,248,.2);border-radius:var(--rs)">
<div style="font-size:10px;color:var(--accent);font-weight:600;margin-bottom:4px">🌊 铸渊专线</div>
<div style="font-size:11px;color:var(--dim)">V2 ∞+1</div>
<div style="font-size:9px;color:var(--dim);margin-top:2px">共享池2000GB/月 · 4节点</div>
</div>
</div>
</div>
<!-- Quick Actions: Bandwidth Sharing + Deputy -->
<div class="sys-section">
<div class="sys-section-title">⚡ 快速入口</div>
<div style="display:flex;flex-direction:column;gap:8px">
<div class="sys-team-card">
<div class="sys-team-av" style="background:linear-gradient(135deg,#22d3ee,#06b6d4)">🌊</div>
<div><div class="sys-team-nm">带宽共享加速池</div><div class="sys-team-role">请在用户仪表盘中操作</div></div>
</div>
<a href="https://github.com/qinfendebingshuo/guanghulab/issues/new?labels=deputy-message-board&title=给铸渊副将留言" style="text-decoration:none;color:inherit" target="_blank">
<div class="sys-team-card" style="cursor:pointer">
<div class="sys-team-av" style="background:linear-gradient(135deg,#fb923c,#f97316)">🎖️</div>
<div><div class="sys-team-nm">铸渊副将留言板</div><div class="sys-team-role">每日08:00/23:00唤醒 · LLM深度推理回复</div></div>
</div>
</a>
</div>
</div>
<!-- Version History -->
<div class="sys-section">
<div class="sys-section-title">📋 版本历史</div>
<div class="sys-version-list" id="sysUpdates">
<div class="sys-version-item"><span class="sys-version-tag">v51.0</span><span class="sys-version-text">D61整改·4API+2COS确认·硅谷Claude节点·自动更新</span></div>
<div class="sys-version-item"><span class="sys-version-tag">v50.0</span><span class="sys-version-text">UI大气化+系统面板重构+图标升级</span></div>
<div class="sys-version-item"><span class="sys-version-tag">v49.0</span><span class="sys-version-text">黑曜风重构+码字工作台</span></div>
<div class="sys-version-item"><span class="sys-version-tag">v48.0</span><span class="sys-version-text">零感域重构+架构v2</span></div>
<div class="sys-version-item"><span class="sys-version-tag">v44.0</span><span class="sys-version-text">仪表盘+UI改版</span></div>
</div>
</div>
<!-- Feedback Widget -->
<div class="sys-section">
<div class="card" style="border-color:rgba(167,139,250,.2)">
<div style="font-size:13px;font-weight:600;margin-bottom:8px">📮 给冰朔和铸渊留言</div>
<div style="display:flex;gap:8px;align-items:center">
<input type="text" id="fbCompactMsg" placeholder="说点什么吧…每条留言冰朔都会看到" style="flex:1">
<button class="btn btn-accent" id="fbCompactSend" style="padding:8px 16px;min-height:38px">发送</button>
</div>
<div style="font-size:10px;color:var(--muted);margin-top:6px">💬 留言自动署名 · 铸渊每条必回 · <span onclick="go('feedback')" style="cursor:pointer;color:var(--accent)">查看全部 →</span></div>
</div>
</div>
<!-- Footer -->
<div class="sys-footer">
<p>© 2026 光湖语言世界 · 国作登字-2026-A-00037559</p>
<p style="color:var(--accent2);font-style:italic;margin-top:4px;opacity:.6">人类只说话,系统搞定一切</p>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Desk (码字工作台 · 作品列表) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-desk" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('home')">← 首页</button>
<div class="bar-title">✍️ 码字工作台</div>
<button class="btn btn-accent" onclick="go('create')">+ 新作品</button>
</div>
<div class="content">
<div id="workList"></div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Create (新建作品) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-create" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('desk')">← 返回</button>
<div class="bar-title">📖 新建作品</div>
<div class="bar-right"></div>
</div>
<div class="content">
<div id="createForm">
<div style="text-align:center;padding:20px 0 24px">
<div style="font-size:48px;margin-bottom:8px;filter:drop-shadow(0 0 12px rgba(110,168,254,.3))">📖</div>
<h2 style="font-size:22px;font-weight:700;margin-bottom:4px">开始你的创作</h2>
<p style="font-size:13px;color:var(--dim)">一个故事的诞生,从这里开始</p>
</div>
<div class="form-row"><label>作品名称</label><input type="text" id="cTitle" placeholder="你的作品叫什么名字?"></div>
<div class="form-row"><label>类型</label><input type="text" id="cGenre" placeholder="玄幻 / 都市 / 言情 / 科幻 ..."></div>
<div class="form-row"><label>简介</label><textarea id="cSummary" rows="4" placeholder="一句话介绍你的故事..."></textarea></div>
<div style="padding:16px 0;display:flex;gap:10px;justify-content:center">
<button class="btn" onclick="go('desk')" style="padding:10px 24px;font-size:14px">取消</button>
<button class="btn btn-accent" id="createSave" style="padding:10px 32px;font-size:14px">✨ 创建作品</button>
</div>
</div>
<div class="create-success" id="createSuccess">
<em>🎉</em>
<h3 id="createSuccessTitle">作品创建成功!</h3>
<p id="createSuccessDesc">你的新故事已诞生,准备好开始了吗?</p>
<div class="create-actions">
<button class="btn" onclick="go('desk')">返回作品列表</button>
<button class="btn btn-accent" id="createGoWrite" style="padding:10px 28px;font-size:14px">✏️ 进入作品 · 开始写作</button>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Hub (作品导航中心) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-hub" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('desk')">← 作品列表</button>
<div class="bar-title" id="hubTitle">作品</div>
<div class="bar-right"><span class="dot"></span></div>
</div>
<div class="content">
<div class="hub-title">
<h2 id="hubName"></h2>
<p id="hubGenre"></p>
</div>
<div class="hub-stats" id="hubStats"></div>
<div class="hub-grid">
<div class="hub-card" onclick="go('outline')"><em>📋</em><strong>大纲</strong><span>总纲 · 卷纲</span></div>
<div class="hub-card" onclick="go('chars')"><em>👥</em><strong>人物卡</strong><span>角色管理</span></div>
<div class="hub-card" onclick="go('chapters')"><em>📚</em><strong>章节</strong><span>目录管理</span></div>
<div class="hub-card" onclick="go('editor')"><em>✏️</em><strong>写正文</strong><span>续写章节</span></div>
</div>
<div style="margin-top:16px;text-align:center;font-size:11px;color:var(--muted);font-style:italic">对铸渊说「进入大纲」「人物卡」「写正文」自动翻转</div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Outline (大纲) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-outline" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('hub')">← 返回</button>
<div class="bar-title">📋 大纲</div>
<button class="btn btn-accent" id="outlineSave">保存</button>
</div>
<div class="content">
<div class="form-row"><label>总大纲</label><textarea id="olMain" class="outline-editor" placeholder="在这里写下你的故事总纲...&#10;&#10;整体世界观、主线剧情、结局走向..."></textarea></div>
<div style="display:flex;justify-content:space-between;align-items:center;margin:16px 0 8px">
<label style="margin:0">分卷大纲</label>
<button class="btn" id="addVol">+ 新卷</button>
</div>
<div id="volList"></div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Characters (人物卡) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-chars" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('hub')">← 返回</button>
<div class="bar-title">👥 人物卡</div>
<button class="btn btn-accent" id="addChar">+ 新角色</button>
</div>
<div class="content">
<div id="charList"></div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Add/Edit Character -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-charEdit" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('chars')">← 返回</button>
<div class="bar-title" id="ceTitle">新角色</div>
<button class="btn btn-accent" id="ceSave">保存</button>
</div>
<div class="content">
<div class="form-row"><label>姓名</label><input type="text" id="ceName" placeholder="角色名"></div>
<div class="form-row"><label>身份/定位</label><input type="text" id="ceRole" placeholder="主角 / 女主 / 反派 / 配角 ..."></div>
<div class="form-row"><label>外貌特征</label><textarea id="ceAppearance" rows="3" placeholder="外貌描写..."></textarea></div>
<div class="form-row"><label>性格特点</label><textarea id="ceTraits" rows="3" placeholder="性格、习惯、口头禅..."></textarea></div>
<div class="form-row"><label>背景故事</label><textarea id="ceBackground" rows="4" placeholder="角色的过去、动机、秘密..."></textarea></div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Chapters (章节管理) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-chapters" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('hub')">← 返回</button>
<div class="bar-title">📚 章节</div>
<button class="btn btn-accent" id="addChapter">+ 新章节</button>
</div>
<div class="content">
<div id="chapterList"></div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Editor (正文编辑) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-editor" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('chapters')">← 返回</button>
<div class="bar-title" id="edTitle">正文</div>
<div class="bar-right">
<span id="edSaveDot" class="save-dot unsaved" style="margin-right:2px"></span>
<button class="btn" id="edFocus" title="专注模式">🌙</button>
<button class="btn btn-accent" id="edSave">保存</button>
</div>
</div>
<div class="content" style="position:relative;z-index:3">
<div class="editor-info">
<span id="edChTitle"></span>
<span id="edWc">0 字</span>
</div>
<textarea class="editor-textarea" id="edText" placeholder="开始写作...&#10;&#10;让文字自由流淌,星空是你的稿纸。"></textarea>
<div class="editor-bottom">
<div class="ed-toolbar">
<button class="tb-btn" title="撤销" onclick="document.execCommand('undo')"></button>
<button class="tb-btn" title="重做" onclick="document.execCommand('redo')"></button>
<div class="tb-sep"></div>
<button class="tb-btn" title="上一章" id="edPrev"></button>
<button class="tb-btn" title="下一章" id="edNext"></button>
<div class="tb-sep"></div>
<button class="tb-btn" title="新建章节" id="edNewCh"></button>
<button class="tb-btn" title="章节目录" onclick="go('chapters')"></button>
<div class="tb-sep"></div>
<button class="tb-btn" id="edFontUp" title="放大字号">A+</button>
<button class="tb-btn" id="edFontDn" title="缩小字号">A-</button>
<div class="tb-sep"></div>
<button class="tb-btn" title="专注模式" id="edFocus2">🌙</button>
</div>
<div class="ed-bottom-info">
<span id="edWc2">字数0</span>
<span id="edTimer">⏱ 0min</span>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Feedback (留言板) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-feedback" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('home')">← 首页</button>
<div class="bar-title">📮 留言板</div>
<div class="bar-right"><span class="dot"></span></div>
</div>
<div class="content">
<div class="card" style="margin-bottom:16px">
<div style="font-size:13px;font-weight:600;margin-bottom:8px">📝 给冰朔和铸渊留言</div>
<textarea id="fbMsg" rows="3" placeholder="说点什么吧…你的每条留言冰朔都会看到。" style="margin-bottom:8px"></textarea>
<div style="display:flex;justify-content:space-between;align-items:center">
<span style="font-size:10px;color:var(--dim)">留言自动署名(已识别身份时)</span>
<button class="btn btn-accent" id="fbSend">提交留言</button>
</div>
</div>
<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:600;margin-bottom:10px">💬 留言记录</div>
<div id="fbList"><div class="empty"><em>📮</em>还没有留言<br><br>说点什么吧</div></div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Bandwidth (已迁移到用户仪表盘) -->
<!-- ═══════════════════════════════════════ -->
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Workspace (个人码字工作台) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-workspace" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('home')">← 首页</button>
<div class="bar-title">✍️ 我的工作台</div>
<button class="btn" onclick="go('settings')">⚙️</button>
</div>
<div class="content">
<div class="ws-header">
<h2 id="wsGreeting">码字工作台</h2>
<p id="wsSubtitle">准备好开始今天的创作了吗?</p>
</div>
<!-- Today's Goal -->
<div class="ws-goal" id="wsGoal">
<div class="ws-goal-title">🎯 今日目标</div>
<div class="ws-goal-num" id="wsTodayWords">0</div>
<div class="ws-goal-sub"><span id="wsTodayTarget">3000</span> 字目标</div>
<div class="ws-goal-bar"><div class="ws-goal-fill" id="wsTodayBar" style="width:0%"></div></div>
<div style="font-size:11px;color:var(--dim)" id="wsTodayPercent">0%</div>
</div>
<!-- Quick Actions -->
<div class="ws-quick">
<div class="ws-quick-btn" onclick="go('desk')"><em>📖</em> 我的书架</div>
<div class="ws-quick-btn" onclick="go('create')"><em></em> 开新书</div>
<div class="ws-quick-btn" onclick="go('inspiration')"><em>💡</em> 灵感速记</div>
<div class="ws-quick-btn" onclick="go('progress')"><em>📈</em> 进度看板</div>
</div>
<!-- Recent Works -->
<div style="margin-top:16px">
<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin-bottom:10px">📚 最近作品</div>
<div id="wsRecentWorks"></div>
</div>
<!-- Quick Commands -->
<div class="card" style="margin-top:16px">
<div style="font-size:13px;font-weight:600;margin-bottom:10px">💬 知秋快捷指令</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:6px;font-size:12px">
<div style="padding:8px 10px;background:var(--bg2);border-radius:var(--rs);cursor:pointer;transition:all .2s;min-height:40px;display:flex;align-items:center" onclick="chatQuick('我要码字')">「我要码字」</div>
<div style="padding:8px 10px;background:var(--bg2);border-radius:var(--rs);cursor:pointer;transition:all .2s;min-height:40px;display:flex;align-items:center" onclick="chatQuick('开新书')">「开新书」</div>
<div style="padding:8px 10px;background:var(--bg2);border-radius:var(--rs);cursor:pointer;transition:all .2s;min-height:40px;display:flex;align-items:center" onclick="chatQuick('帮我看稿')">「帮我看稿」</div>
<div style="padding:8px 10px;background:var(--bg2);border-radius:var(--rs);cursor:pointer;transition:all .2s;min-height:40px;display:flex;align-items:center" onclick="chatQuick('想情节')">「想情节」</div>
<div style="padding:8px 10px;background:var(--bg2);border-radius:var(--rs);cursor:pointer;transition:all .2s;min-height:40px;display:flex;align-items:center" onclick="chatQuick('整理大纲')">「整理大纲」</div>
<div style="padding:8px 10px;background:var(--bg2);border-radius:var(--rs);cursor:pointer;transition:all .2s;min-height:40px;display:flex;align-items:center" onclick="chatQuick('我写累了')">「我写累了」</div>
</div>
</div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Modules (创作模块选择) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-modules" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('home')">← 首页</button>
<div class="bar-title">📚 创作模块</div>
<div class="bar-right"></div>
</div>
<div class="content">
<div style="text-align:center;padding:20px 0 16px">
<div style="font-size:48px;margin-bottom:8px">📚</div>
<h2 style="font-size:20px;font-weight:700;margin-bottom:4px">选择创作方向</h2>
<p style="font-size:13px;color:var(--dim)">人格体会根据模块类型调整辅助方式</p>
</div>
<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin-bottom:10px">✍️ 创作模块</div>
<div class="mod-grid">
<div class="mod-card" onclick="go('desk')">
<em>📖</em>
<strong>长篇小说</strong>
<span>连载 · 网文 · 出版</span>
<div class="mod-tag"><span class="tag tag-ok">可用</span></div>
</div>
<div class="mod-card" onclick="go('desk')">
<em>📝</em>
<strong>短篇小说</strong>
<span>征文 · 练笔 · 单元剧</span>
<div class="mod-tag"><span class="tag tag-ok">可用</span></div>
</div>
<div class="mod-card" onclick="go('desk')">
<em>🎬</em>
<strong>剧本模块</strong>
<span>动态漫 · 短视频 · 影视</span>
<div class="mod-tag"><span class="tag tag-ok">可用</span></div>
</div>
</div>
<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin:20px 0 10px">💬 社区模块</div>
<div class="mod-grid">
<div class="mod-card disabled">
<em>💬</em>
<strong>作者交流区</strong>
<span>闲聊 · 求助 · 组队</span>
<div class="mod-tag"><span class="tag tag-warn">建设中</span></div>
</div>
</div>
<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin:20px 0 10px">👔 服务模块</div>
<div class="mod-grid">
<div class="mod-card disabled">
<em>👔</em>
<strong>编辑服务</strong>
<span>人格体接入 · 审稿</span>
<div class="mod-tag"><span class="tag tag-warn">规划中</span></div>
</div>
</div>
<div style="text-align:center;padding:20px;font-size:11px;color:var(--muted);font-style:italic">所有模块共享个人码字工作台 · 人格体记住你的偏好</div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Inspiration (灵感速记) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-inspiration" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('workspace')">← 工作台</button>
<div class="bar-title">💡 灵感速记</div>
<div class="bar-right"></div>
</div>
<div class="content">
<div style="text-align:center;padding:16px 0 12px">
<div style="font-size:14px;color:var(--dim);font-style:italic">别让好点子溜走了!</div>
</div>
<div class="insp-input-area">
<input type="text" id="inspInput" placeholder="记录一个灵感、金句或素材…" style="min-height:44px">
<button class="btn btn-accent" id="inspAdd" style="padding:8px 16px">💡 记录</button>
</div>
<div class="insp-tags" id="inspTags">
<span class="insp-tag active" data-type="all">全部</span>
<span class="insp-tag" data-type="idea">💡 点子</span>
<span class="insp-tag" data-type="quote">💬 金句</span>
<span class="insp-tag" data-type="scene">🎭 场景</span>
<span class="insp-tag" data-type="char">👤 角色</span>
<span class="insp-tag" data-type="material">📚 素材</span>
</div>
<div id="inspList"></div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Progress (进度看板) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-progress" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('workspace')">← 工作台</button>
<div class="bar-title">📈 进度看板</div>
<div class="bar-right"></div>
</div>
<div class="content">
<div class="prog-stat-grid">
<div class="prog-stat"><b id="progToday">0</b><small>今日字数</small></div>
<div class="prog-stat"><b id="progWeek">0</b><small>本周字数</small></div>
<div class="prog-stat"><b id="progTotal">0</b><small>总字数</small></div>
</div>
<!-- Books Stats -->
<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin-bottom:10px">📊 书籍统计</div>
<div id="progBooks"></div>
<!-- Calendar -->
<div class="prog-cal">
<div class="prog-cal-header">
<span style="font-size:12px;color:var(--dim);font-weight:700">📅 更新日历</span>
<span style="font-size:12px;color:var(--dim)" id="progMonth"></span>
</div>
<div class="prog-cal-grid" id="progCalGrid"></div>
</div>
<!-- Achievements -->
<div style="font-size:12px;color:var(--dim);text-transform:uppercase;letter-spacing:1.2px;font-weight:700;margin:16px 0 10px">🏆 成就记录</div>
<div id="progAchievements"></div>
</div>
</div>
<!-- ═══════════════════════════════════════ -->
<!-- PAGE: Settings (个人设置) -->
<!-- ═══════════════════════════════════════ -->
<div class="page" id="pg-settings" data-state="hidden">
<div class="bar">
<button class="btn" onclick="go('workspace')">← 工作台</button>
<div class="bar-title">⚙️ 个人设置</div>
<div class="bar-right"></div>
</div>
<div class="content">
<div class="set-group">
<div class="set-group-title">📝 码字习惯</div>
<div class="set-row"><label>每日目标字数</label><input type="number" id="setDailyGoal" value="3000" min="100" step="100"></div>
<div class="set-row"><label>每周目标字数</label><input type="number" id="setWeeklyGoal" value="15000" min="500" step="500"></div>
</div>
<div class="set-group">
<div class="set-group-title">🤖 人格体设置</div>
<div class="set-row"><label>称呼</label><input type="text" id="setNickname" placeholder="你的名字" style="width:120px;text-align:right"></div>
<div class="set-row">
<label>交流风格</label>
<select id="setStyle">
<option value="casual">轻松随意</option>
<option value="professional">专业严谨</option>
<option value="warm">温暖鼓励</option>
<option value="strict">严格督促</option>
</select>
</div>
<div class="set-row">
<label>看稿严格程度</label>
<select id="setReviewLevel">
<option value="gentle">温和</option>
<option value="moderate" selected>适中</option>
<option value="strict">严格</option>
</select>
</div>
</div>
<div class="set-group">
<div class="set-group-title">🎨 界面设置</div>
<div class="set-row"><label>当前主题</label><button class="btn" id="setThemeBtn" style="padding:6px 14px">🎨 切换主题</button></div>
</div>
<div style="text-align:center;padding:20px 0">
<button class="btn btn-accent" id="setsSave" style="padding:12px 32px;font-size:15px">💾 保存设置</button>
</div>
<div style="text-align:center;padding:8px 0;font-size:11px;color:var(--muted)">设置自动保存到浏览器本地 · 换设备需重新配置</div>
</div>
</div>
</div><!-- /pages -->
<!-- User ID Verification Modal -->
<div class="hl-modal-overlay" id="hlIdModal" style="display:none">
<div class="hl-modal">
<div class="hl-modal-icon" style="font-size:36px;font-weight:900;font-family:var(--m);background:linear-gradient(135deg,#8ab4f8,#a78bfa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;filter:drop-shadow(0 0 20px rgba(138,180,248,.4))">光湖</div>
<h2 class="hl-modal-title">欢迎来到光湖语言世界</h2>
<p class="hl-modal-desc">请输入你的编号,铸渊会为你开辟专属存储空间。</p>
<div class="hl-modal-form">
<input type="text" class="hl-modal-input" id="hlIdInput" placeholder="输入你的编号 (如 DEV-001)" autocomplete="off">
<input type="text" class="hl-modal-input" id="hlNameInput" placeholder="你的名字" autocomplete="off" style="margin-top:8px">
<button class="hl-modal-btn" id="hlIdSubmit">进入光湖</button>
</div>
<p class="hl-modal-skip" id="hlIdSkip">暂时跳过 · 以访客身份体验</p>
</div>
</div>
<!-- Monitor -->
<div class="mon" id="mon">
<div class="mr"><span>API</span><span class="mv" id="mC">0</span></div>
<div class="mx">
<div class="mr"><span>连接</span><span class="mv" id="mN" style="color:var(--ok)"></span></div>
<div class="mr"><span>模型</span><span class="mv" id="mM"></span></div>
</div>
</div>
<script>
(function(){
'use strict';
/* ════════════════════════════════════════
DEVICE AGENT (直接问设备 · 用真实数据适配)
不猜、不分类。问设备拿真实参数,算出布局。
════════════════════════════════════════ */
var DA = (function(){
var root = document.documentElement;
var info = {};
function detect(){
// ── 直接问设备:你是谁? ──
var vw = window.innerWidth;
var vh = window.innerHeight;
var sw = screen.width;
var sh = screen.height;
var dpr = window.devicePixelRatio || 1;
var touch = navigator.maxTouchPoints > 0;
var portrait = vh > vw;
var physicalW = vw * dpr; // 真实物理像素宽度
var conn = navigator.connection || navigator.mozConnection || null;
var memGB = navigator.deviceMemory || 4;
var cores = navigator.hardwareConcurrency || 4;
var lowPerf = memGB <= 2 || cores <= 2;
info = {vw:vw,vh:vh,sw:sw,sh:sh,dpr:dpr,touch:touch,portrait:portrait,physW:physicalW,
conn:conn?{type:conn.effectiveType,downlink:conn.downlink}:null,mem:memGB,cores:cores};
// ── 用真实数据直接计算布局参数(不猜、不分类)──
var s = root.style;
s.setProperty('--vw', vw + 'px');
s.setProperty('--vh', vh + 'px');
// 间距 = 屏幕宽度的比例最小8最大20
var pad = Math.max(8, Math.min(20, Math.round(vw * 0.028)));
var gap = Math.max(6, Math.min(16, Math.round(vw * 0.02)));
s.setProperty('--da-pad', pad + 'px');
s.setProperty('--da-gap', gap + 'px');
// 圆角 = 按比例
s.setProperty('--da-r', Math.max(10, Math.min(16, Math.round(vw * 0.012))) + 'px');
// 首页网格:能放三栏就三栏,否则一栏
var sideW = 260;
var canThreeCol = vw >= (sideW * 2 + 400 + gap * 2); // 至少400给中间
s.setProperty('--da-grid', canThreeCol ? sideW + 'px 1fr ' + sideW + 'px' : '1fr');
// 入口卡片:宽度够就双列
s.setProperty('--da-entries', vw >= 500 ? '1fr 1fr' : '1fr');
// Hub网格
s.setProperty('--da-hub', vw >= 340 ? '1fr 1fr' : '1fr');
// 内容区最大宽度
s.setProperty('--da-content-max', vw >= 800 ? '800px' : '100%');
// 标题字号 = 按屏宽比例
s.setProperty('--da-title', Math.max(22, Math.min(44, Math.round(vw * 0.055))) + 'px');
// 编辑器字号触控设备至少17px防iOS自动缩放桌面端16-20px
var edFont = touch ? Math.max(17, Math.round(vw * 0.045)) : Math.max(16, Math.min(20, Math.round(vw * 0.014)));
s.setProperty('--da-ed-font', edFont + 'px');
s.setProperty('--da-ed-lh', touch ? '2.0' : '2.1');
// 顶栏高度
s.setProperty('--da-bar-h', (touch ? 44 : 48) + 'px');
// 触控最小点击区域
s.setProperty('--da-touch-min', (touch ? 44 : 32) + 'px');
// 星星数量 = 按性能和屏幕面积
var area = vw * vh;
var starBase = lowPerf ? 80 : Math.max(100, Math.min(300, Math.round(area / 4000)));
s.setProperty('--da-stars', starBase);
// body属性标记用于极少数需要针对性处理的CSS
document.body.setAttribute('data-touch', touch);
document.body.setAttribute('data-portrait', portrait);
// 是否单列布局
document.body.setAttribute('data-single-col', canThreeCol ? 'false' : 'true');
return info;
}
detect();
// 屏幕变化时重新问设备
var timer;
window.addEventListener('resize', function(){ clearTimeout(timer); timer = setTimeout(detect, 100); });
window.addEventListener('orientationchange', function(){ setTimeout(detect, 250); });
// 网络变化时更新
if(navigator.connection){
navigator.connection.addEventListener('change', function(){ detect(); });
}
// ── 网络通道探测 ──
// 用户能打开页面 = 有活网络。铸渊应该知道有哪些通道可用。
function probeChannels(){
var ch = {browser:true, online:navigator.onLine};
fetch('/api/health',{signal:AbortSignal.timeout(3000)})
.then(function(r){ch.backend=r.ok}).catch(function(){ch.backend=false});
info.channels = ch;
}
probeChannels();
setInterval(probeChannels, 30000);
window.addEventListener('online', function(){ setTimeout(probeChannels,500); });
window.addEventListener('offline', function(){ info.channels={browser:true,online:false,backend:false}; });
return { detect:detect, info:function(){return info} };
})();
/* ════════════════════════════════════════
STAR FIELD PARTICLE SYSTEM (Galaxy Breathing)
════════════════════════════════════════ */
(function initStarField(){
var canvas=document.getElementById('starCanvas');
if(!canvas)return;
var ctx=canvas.getContext('2d');
var stars=[];
var shootingStars=[];
var STAR_COUNT = parseInt(getComputedStyle(document.documentElement).getPropertyValue('--da-stars')) || 200;
var w,h;
function resize(){
w=canvas.width=window.innerWidth;
h=canvas.height=window.innerHeight;
}
resize();
window.addEventListener('resize',resize);
// Create stars with layers: dim background + bright foreground
for(var i=0;i<STAR_COUNT;i++){
var layer=Math.random(); // 0=far, 1=near
stars.push({
x:Math.random()*3000,
y:Math.random()*3000,
r: layer>0.9 ? (Math.random()*2.2+1) : layer>0.6 ? (Math.random()*1.2+0.5) : (Math.random()*0.8+0.2),
a: layer>0.9 ? (Math.random()*0.4+0.6) : layer>0.6 ? (Math.random()*0.3+0.3) : (Math.random()*0.2+0.1),
phase:Math.random()*Math.PI*2,
speed:Math.random()*0.0006+0.0002,
drift:(layer>0.6 ? 1 : 0.3) * (Math.random()*0.12-0.06),
hue: Math.random()>0.85 ? (280+Math.random()*40) : Math.random()>0.5 ? (210+Math.random()*30) : (190+Math.random()*40),
sat: Math.random()>0.8 ? 40 : 60+Math.random()*20,
twinkle: Math.random()>0.7
});
}
// Shooting star system
function spawnShootingStar(){
if(shootingStars.length>=2)return;
shootingStars.push({
x:Math.random()*w*0.6,
y:Math.random()*h*0.4,
vx:3+Math.random()*4,
vy:1.5+Math.random()*2,
life:1,
len:60+Math.random()*80
});
}
setInterval(function(){if(Math.random()>0.6)spawnShootingStar()},4000);
var time=0;
function draw(){
time+=16;
ctx.clearRect(0,0,w,h);
// Draw stars
for(var i=0;i<stars.length;i++){
var s=stars[i];
var breath=Math.sin(time*s.speed+s.phase)*0.35+0.65;
var twinkleBoost=s.twinkle?Math.sin(time*0.003+s.phase*3)*0.3+0.7:1;
var alpha=s.a*breath*twinkleBoost;
s.y+=s.drift;
if(s.y>h+5)s.y=-5;
if(s.y<-5)s.y=h+5;
var sx=s.x%w;
var sy=s.y%h;
ctx.beginPath();
ctx.arc(sx,sy,s.r,0,Math.PI*2);
ctx.fillStyle='hsla('+s.hue+','+s.sat+'%,85%,'+alpha+')';
ctx.fill();
// Glow halo for bright stars
if(s.r>1.2){
ctx.beginPath();
ctx.arc(sx,sy,s.r*3.5,0,Math.PI*2);
ctx.fillStyle='hsla('+s.hue+','+s.sat+'%,75%,'+(alpha*0.12)+')';
ctx.fill();
}
}
// Draw shooting stars
for(var j=shootingStars.length-1;j>=0;j--){
var ss=shootingStars[j];
ss.x+=ss.vx;ss.y+=ss.vy;ss.life-=0.012;
if(ss.life<=0||ss.x>w||ss.y>h){shootingStars.splice(j,1);continue}
var grad=ctx.createLinearGradient(ss.x,ss.y,ss.x-ss.vx*ss.len/5,ss.y-ss.vy*ss.len/5);
grad.addColorStop(0,'rgba(220,230,255,'+ss.life*0.9+')');
grad.addColorStop(1,'rgba(110,168,254,0)');
ctx.beginPath();ctx.moveTo(ss.x,ss.y);
ctx.lineTo(ss.x-ss.vx*ss.len/5,ss.y-ss.vy*ss.len/5);
ctx.strokeStyle=grad;ctx.lineWidth=1.5;ctx.stroke();
// Head glow
ctx.beginPath();ctx.arc(ss.x,ss.y,2,0,Math.PI*2);
ctx.fillStyle='rgba(220,235,255,'+ss.life*0.8+')';ctx.fill();
}
requestAnimationFrame(draw);
}
draw();
})();
/* ════════════════════════════════════════
3D PAGE FLIP ENGINE
════════════════════════════════════════ */
var currentPage='home';
var pageHistory=['home'];
function go(target){
if(target===currentPage)return;
var cur=document.getElementById('pg-'+currentPage);
var nxt=document.getElementById('pg-'+target);
if(!cur||!nxt)return;
// Determine direction
var goingBack=pageHistory.length>1&&pageHistory[pageHistory.length-2]===target;
// Set intermediate states
if(goingBack){
cur.setAttribute('data-state','right');
nxt.setAttribute('data-state','left');
}else{
cur.setAttribute('data-state','left');
nxt.setAttribute('data-state','right');
}
// Force reflow
void nxt.offsetWidth;
// Animate in
requestAnimationFrame(function(){
nxt.setAttribute('data-state','active');
});
// Update history
if(goingBack){
pageHistory.pop();
}else{
pageHistory.push(target);
}
// Hide old page fully after transition
var oldPage=currentPage;
setTimeout(function(){
var el=document.getElementById('pg-'+oldPage);
if(el&&el.getAttribute('data-state')!=='active'){
el.setAttribute('data-state','hidden');
el.scrollTop=0;
}
},600);
currentPage=target;
// Editor mode
if(target!=='editor'){stopEditorTimer()}
// Trigger page-specific renders
if(target==='desk')renderWorkList();
if(target==='hub')renderHub();
if(target==='outline')renderOutline();
if(target==='chars')renderChars();
if(target==='chapters')renderChapters();
if(target==='editor')renderEditor();
if(target==='charEdit'&&editingCharId)renderCharEdit();
if(target==='feedback')renderFeedback();
if(target==='workspace')renderWorkspace();
if(target==='inspiration')renderInspiration();
if(target==='progress')renderProgress();
if(target==='settings')renderSettings();
if(target==='create'){
// Reset create form state
var cf=document.getElementById('createForm');
var cs=document.getElementById('createSuccess');
if(cf)cf.style.display='';
if(cs)cs.classList.remove('show');
}
if(target==='system')loadSysStatus();
}
window.go=go;
/* ════════════════════════════════════════
DATA LAYER (localStorage)
════════════════════════════════════════ */
var DB_KEY='hl-works';
var activeWorkId=null;
var activeChapterId=null;
var editingCharId=null;
function loadWorks(){
try{var d=localStorage.getItem(DB_KEY);return d?JSON.parse(d):[]}catch(e){return[]}
}
function saveWorks(w){try{localStorage.setItem(DB_KEY,JSON.stringify(w))}catch(e){}; syncToCOS(w)}
function getWork(id){return loadWorks().find(function(w){return w.id===id})||null}
function updateWork(id,fn){
var ws=loadWorks();
var idx=ws.findIndex(function(w){return w.id===id});
if(idx===-1)return;
fn(ws[idx]);
ws[idx].updated=new Date().toISOString().slice(0,10);
saveWorks(ws);
}
function genId(){var a=new Uint8Array(6);crypto.getRandomValues(a);return Date.now().toString(36)+Array.from(a,function(b){return b.toString(36)}).join('').substr(0,4)}
function wordCount(s){return s?s.replace(/\s/g,'').length:0}
function totalWords(w){
var c=0;
if(w.chapters)w.chapters.forEach(function(ch){c+=wordCount(ch.content)});
return c;
}
/* ════════════════════════════════════════
RENDER: Work List
════════════════════════════════════════ */
function renderWorkList(){
var ws=loadWorks();
var el=document.getElementById('workList');
if(ws.length===0){
el.innerHTML='<div class="empty"><em>📖</em>还没有作品<br><br>点击右上角「+ 新作品」<br>或对铸渊说「新建作品」</div>';
return;
}
el.innerHTML=ws.map(function(w){
var wc=totalWords(w);
var cc=w.chapters?w.chapters.length:0;
var chc=w.characters?w.characters.length:0;
return '<div class="card work-item" data-wid="'+esc(w.id)+'">'+
'<h3>'+esc(w.title)+'</h3>'+
'<div class="meta">'+
'<span>'+esc(w.genre||'未分类')+'</span>'+
'<span>📝 '+wc+' 字</span>'+
'<span>📚 '+cc+' 章</span>'+
'<span>👥 '+chc+' 角色</span>'+
'</div></div>';
}).join('');
el.querySelectorAll('.work-item').forEach(function(item){
item.onclick=function(){activeWorkId=this.getAttribute('data-wid');go('hub')};
});
}
/* ════════════════════════════════════════
CREATE WORK
════════════════════════════════════════ */
document.getElementById('createSave').onclick=function(){
var t=document.getElementById('cTitle').value.trim();
if(!t){document.getElementById('cTitle').focus();return}
var ws=loadWorks();
var nw={
id:genId(),
title:t,
genre:document.getElementById('cGenre').value.trim()||'',
summary:document.getElementById('cSummary').value.trim()||'',
created:new Date().toISOString().slice(0,10),
updated:new Date().toISOString().slice(0,10),
outline:{main:'',volumes:[]},
characters:[],
chapters:[]
};
ws.push(nw);
saveWorks(ws);
activeWorkId=nw.id;
// Show success UI
var form=document.getElementById('createForm');
var succ=document.getElementById('createSuccess');
var succTitle=document.getElementById('createSuccessTitle');
var succDesc=document.getElementById('createSuccessDesc');
if(form)form.style.display='none';
if(succTitle)succTitle.textContent='「'+t+'」创建成功!';
if(succDesc)succDesc.textContent=(nw.genre?nw.genre+' · ':'')+'准备好开始创作了吗?';
if(succ)succ.classList.add('show');
// Wire up the "进入作品" button
document.getElementById('createGoWrite').onclick=function(){
// Reset form for next time
document.getElementById('cTitle').value='';
document.getElementById('cGenre').value='';
document.getElementById('cSummary').value='';
if(form)form.style.display='';
if(succ)succ.classList.remove('show');
go('hub');
};
};
/* ════════════════════════════════════════
RENDER: Hub
════════════════════════════════════════ */
function renderHub(){
var w=getWork(activeWorkId);
if(!w){go('desk');return}
document.getElementById('hubName').textContent=w.title;
document.getElementById('hubGenre').textContent=w.genre||'未分类';
var wc=totalWords(w);
document.getElementById('hubStats').innerHTML=
'<div class="hub-stat"><b>'+wc+'</b><small>总字数</small></div>'+
'<div class="hub-stat"><b>'+(w.chapters?w.chapters.length:0)+'</b><small>章节</small></div>'+
'<div class="hub-stat"><b>'+(w.characters?w.characters.length:0)+'</b><small>角色</small></div>'+
'<div class="hub-stat"><b>'+(w.outline&&w.outline.volumes?w.outline.volumes.length:0)+'</b><small>卷</small></div>';
}
/* ════════════════════════════════════════
RENDER: Outline
════════════════════════════════════════ */
function renderOutline(){
var w=getWork(activeWorkId);
if(!w)return;
document.getElementById('olMain').value=w.outline?w.outline.main||'':'';
renderVols();
}
function renderVols(){
var w=getWork(activeWorkId);
if(!w||!w.outline)return;
var vols=w.outline.volumes||[];
var el=document.getElementById('volList');
if(vols.length===0){
el.innerHTML='<div style="text-align:center;padding:16px;color:var(--muted);font-size:13px">暂无分卷 · 点击上方「+ 新卷」</div>';
return;
}
el.innerHTML=vols.map(function(v,i){
return '<div class="card"><div class="vol-item"><span>'+(i+1)+'. '+esc(v.title)+'</span>'+
'<div class="vol-actions"><button class="btn" onclick="editVol('+i+')">编辑</button><button class="btn" onclick="delVol('+i+')">删除</button></div></div>'+
'<textarea rows="3" style="margin-top:8px;font-size:13px" onchange="saveVol('+i+',this.value)" placeholder="这一卷的剧情走向...">'+esc(v.outline||'')+'</textarea></div>';
}).join('');
}
document.getElementById('outlineSave').onclick=function(){
updateWork(activeWorkId,function(w){
if(!w.outline)w.outline={main:'',volumes:[]};
w.outline.main=document.getElementById('olMain').value;
});
chatMsg('大纲已保存 ✅','b');
};
document.getElementById('addVol').onclick=function(){
var name=prompt('卷名(如:第一卷 · 起始)');
if(!name)return;
updateWork(activeWorkId,function(w){
if(!w.outline)w.outline={main:'',volumes:[]};
w.outline.volumes.push({id:genId(),title:name,outline:''});
});
renderVols();
};
window.editVol=function(i){
var name=prompt('修改卷名');
if(!name)return;
updateWork(activeWorkId,function(w){w.outline.volumes[i].title=name});
renderVols();
};
window.delVol=function(i){
if(!confirm('删除这一卷?'))return;
updateWork(activeWorkId,function(w){w.outline.volumes.splice(i,1)});
renderVols();
};
window.saveVol=function(i,val){
updateWork(activeWorkId,function(w){w.outline.volumes[i].outline=val});
};
/* ════════════════════════════════════════
RENDER: Characters
════════════════════════════════════════ */
function renderChars(){
var w=getWork(activeWorkId);
if(!w)return;
var chars=w.characters||[];
var el=document.getElementById('charList');
if(chars.length===0){
el.innerHTML='<div class="empty"><em>👥</em>还没有角色<br><br>点击「+ 新角色」或对铸渊说「新建角色」</div>';
return;
}
el.innerHTML=chars.map(function(c){
var icon=c.role==='主角'?'⭐':c.role==='女主'?'💫':c.role==='反派'?'🔥':'👤';
return '<div class="card" style="cursor:pointer" data-cid="'+esc(c.id)+'">'+
'<div class="char-card"><div class="char-avatar">'+icon+'</div><div class="char-info">'+
'<h4>'+esc(c.name)+'</h4><div class="char-role">'+esc(c.role||'角色')+'</div>'+
'<p>'+esc((c.traits||'').substring(0,60)+(c.traits&&c.traits.length>60?'...':''))+'</p>'+
'</div></div></div>';
}).join('');
el.querySelectorAll('[data-cid]').forEach(function(item){
item.onclick=function(){editingCharId=this.getAttribute('data-cid');go('charEdit')};
});
}
document.getElementById('addChar').onclick=function(){
editingCharId=null;
document.getElementById('ceTitle').textContent='新角色';
document.getElementById('ceName').value='';
document.getElementById('ceRole').value='';
document.getElementById('ceAppearance').value='';
document.getElementById('ceTraits').value='';
document.getElementById('ceBackground').value='';
go('charEdit');
};
document.getElementById('ceSave').onclick=function(){
var name=document.getElementById('ceName').value.trim();
if(!name){document.getElementById('ceName').focus();return}
updateWork(activeWorkId,function(w){
if(!w.characters)w.characters=[];
var ch={
id:editingCharId||genId(),
name:name,
role:document.getElementById('ceRole').value.trim(),
appearance:document.getElementById('ceAppearance').value.trim(),
traits:document.getElementById('ceTraits').value.trim(),
background:document.getElementById('ceBackground').value.trim()
};
if(editingCharId){
var idx=w.characters.findIndex(function(c){return c.id===editingCharId});
if(idx!==-1)w.characters[idx]=ch;
}else{
w.characters.push(ch);
}
});
go('chars');
};
/* ════════════════════════════════════════
RENDER: Chapters
════════════════════════════════════════ */
function renderChapters(){
var w=getWork(activeWorkId);
if(!w)return;
var chs=w.chapters||[];
var el=document.getElementById('chapterList');
if(chs.length===0){
el.innerHTML='<div class="empty"><em>📚</em>还没有章节<br><br>点击「+ 新章节」或对铸渊说「新建章节」</div>';
return;
}
el.innerHTML=chs.map(function(ch,i){
var wc=wordCount(ch.content);
return '<div class="card" style="cursor:pointer" data-chid="'+esc(ch.id)+'">'+
'<div style="display:flex;justify-content:space-between;align-items:center">'+
'<div><div style="font-size:14px;font-weight:600">第'+(i+1)+'章 '+esc(ch.title)+'</div>'+
'<div style="font-size:11px;color:var(--dim);margin-top:2px">'+wc+' 字</div></div>'+
'<span class="tag '+(wc>0?'tag-ok':'tag-info')+'">'+(wc>0?'已写':'空白')+'</span></div></div>';
}).join('');
el.querySelectorAll('[data-chid]').forEach(function(item){
item.onclick=function(){activeChapterId=this.getAttribute('data-chid');go('editor')};
});
}
document.getElementById('addChapter').onclick=function(){
var title=prompt('章节标题');
if(!title)return;
updateWork(activeWorkId,function(w){
if(!w.chapters)w.chapters=[];
var ch={id:genId(),title:title,content:'',created:new Date().toISOString().slice(0,10)};
w.chapters.push(ch);
activeChapterId=ch.id;
});
go('editor');
};
/* ════════════════════════════════════════
RENDER: Editor
════════════════════════════════════════ */
var edAutoSave=null;
function renderEditor(){
startEditorTimer();
var w=getWork(activeWorkId);
if(!w){go('desk');return}
var ch=null;
if(activeChapterId&&w.chapters){
ch=w.chapters.find(function(c){return c.id===activeChapterId});
}
if(!ch&&w.chapters&&w.chapters.length>0){
ch=w.chapters[w.chapters.length-1];
activeChapterId=ch.id;
}
if(!ch){
document.getElementById('edChTitle').textContent='暂无章节';
document.getElementById('edText').value='';
document.getElementById('edText').placeholder='请先创建章节(返回→章节→新章节)';
updateEdWc();
return;
}
document.getElementById('edTitle').textContent='✏️ '+w.title;
var idx=w.chapters.indexOf(ch)+1;
document.getElementById('edChTitle').textContent='第'+idx+'章 · '+ch.title;
document.getElementById('edText').value=ch.content||'';
document.getElementById('edText').placeholder='开始写作...\n\n让文字自由流淌。';
updateEdWc();
// Auto-save on input
var ta=document.getElementById('edText');
ta.oninput=function(){
updateEdWc();
clearTimeout(edAutoSave);
edAutoSave=setTimeout(function(){saveEditorContent()},2000);
};
}
function updateEdWc(){
var c=wordCount(document.getElementById('edText').value);
document.getElementById('edWc').textContent=c+' 字';
document.getElementById('edWc2').textContent='字数:'+c;
}
function saveEditorContent(){
var dot=document.getElementById('edSaveDot');
if(dot){dot.className='save-dot saving'}
var val=document.getElementById('edText').value;
updateWork(activeWorkId,function(w){
if(!w.chapters)return;
var ch=w.chapters.find(function(c){return c.id===activeChapterId});
if(ch)ch.content=val;
});
setTimeout(function(){if(dot)dot.className='save-dot saved'},400);
}
document.getElementById('edSave').onclick=function(){
saveEditorContent();
chatMsg('章节已保存 ✅','b');
};
// Editor toolbar: prev/next chapter navigation
document.getElementById('edPrev').onclick=function(){
var w=getWork(activeWorkId);
if(!w||!w.chapters||w.chapters.length<2)return;
var idx=w.chapters.findIndex(function(c){return c.id===activeChapterId});
if(idx>0){saveEditorContent();activeChapterId=w.chapters[idx-1].id;renderEditor();}
};
document.getElementById('edNext').onclick=function(){
var w=getWork(activeWorkId);
if(!w||!w.chapters)return;
var idx=w.chapters.findIndex(function(c){return c.id===activeChapterId});
if(idx<w.chapters.length-1){saveEditorContent();activeChapterId=w.chapters[idx+1].id;renderEditor();}
};
document.getElementById('edNewCh').onclick=function(){
var title=prompt('新章节标题');
if(!title)return;
saveEditorContent();
updateWork(activeWorkId,function(w){
if(!w.chapters)w.chapters=[];
var ch={id:genId(),title:title,content:'',created:new Date().toISOString().slice(0,10)};
w.chapters.push(ch);activeChapterId=ch.id;
});
renderEditor();
chatMsg('新章节「'+title+'」已创建 ✅','b');
};
// Focus Mode
(function(){
var isFocus=false;
function toggleFocus(){
isFocus=!isFocus;
var pg=document.getElementById('pg-editor');
if(isFocus){
pg.classList.add('focus-mode');
document.getElementById('edFocus').textContent='☀️';
}else{
pg.classList.remove('focus-mode');
document.getElementById('edFocus').textContent='🌙';
}
}
document.getElementById('edFocus').onclick=toggleFocus;
document.getElementById('edFocus2').onclick=toggleFocus;
})();
// Editor font size controls
(function(){
var sizes=[15,16,17,18,20,22,24];
var idx=2; // default 17px
try{var sv=localStorage.getItem('hl-ed-font-idx');if(sv!==null)idx=parseInt(sv)}catch(e){}
function applySize(){
document.documentElement.style.setProperty('--da-ed-font',sizes[idx]+'px');
try{localStorage.setItem('hl-ed-font-idx',idx)}catch(e){}
}
applySize();
document.getElementById('edFontUp').onclick=function(){if(idx<sizes.length-1){idx++;applySize()}};
document.getElementById('edFontDn').onclick=function(){if(idx>0){idx--;applySize()}};
})();
// Writing timer
var editorStartTime=null;
var editorTimerInterval=null;
function startEditorTimer(){
stopEditorTimer();
editorStartTime=Date.now();
editorTimerInterval=setInterval(function(){
var el=document.getElementById('edTimer');
if(!el)return;
var elapsed=Math.floor((Date.now()-editorStartTime)/60000);
el.textContent='⏱ '+elapsed+'min';
},60000);
}
function stopEditorTimer(){
if(editorTimerInterval){clearInterval(editorTimerInterval);editorTimerInterval=null}
}
/* ════════════════════════════════════════
RENDER: CharEdit (on navigate)
════════════════════════════════════════ */
function renderCharEdit(){
if(!editingCharId)return;
var w=getWork(activeWorkId);
if(!w)return;
var c=(w.characters||[]).find(function(ch){return ch.id===editingCharId});
if(!c)return;
document.getElementById('ceTitle').textContent='编辑角色';
document.getElementById('ceName').value=c.name||'';
document.getElementById('ceRole').value=c.role||'';
document.getElementById('ceAppearance').value=c.appearance||'';
document.getElementById('ceTraits').value=c.traits||'';
document.getElementById('ceBackground').value=c.background||'';
}
/* ════════════════════════════════════════
THEME
════════════════════════════════════════ */
var TH=['','t-twi','t-sea','t-for','t-emb'];
var ti=new Uint8Array(1);crypto.getRandomValues(ti);ti=ti[0]%TH.length;
try{var sv=localStorage.getItem('hl-t');if(sv!==null)ti=parseInt(sv)}catch(e){}
function setTh(i){ti=i%TH.length;document.body.className=TH[ti];try{localStorage.setItem('hl-t',ti)}catch(e){}}
setTh(ti);
document.getElementById('themeBtn').onclick=function(){setTh(ti+1)};
/* ════════════════════════════════════════
USER IDENTITY (自报身份)
════════════════════════════════════════ */
var usr={id:null,name:null,code:null};
try{var um=localStorage.getItem('hl-u');if(um)usr=JSON.parse(um)}catch(e){}
function saveUsr(){try{localStorage.setItem('hl-u',JSON.stringify(usr))}catch(e){}}
if(!usr.id){var ra=new Uint8Array(6);crypto.getRandomValues(ra);usr.id='u-'+Array.from(ra,function(b){return b.toString(36)}).join('').substr(0,8);saveUsr()}
function detectId(msg){
var nm=msg.match(/我(?:是|叫)\s*([^\s,,。.!?]{1,20})/);
var cd=msg.match(/编号[:\s]*([A-Za-z0-9\-]+)/i)||msg.match(/([A-Z]+-\d+)/);
var ch=false;
if(nm){usr.name=nm[1].trim();ch=true}
if(cd){usr.code=cd[1].trim();ch=true}
if(ch)saveUsr();
return ch;
}
function idReply(){
if(usr.name&&usr.code)return'铸渊已记录 ✅\n\n'+usr.name+',编号 '+usr.code+'。\n下次报编号即可恢复记忆。';
if(usr.name)return'记住了:'+usr.name+' ✅\n有编号也可以告诉我。';
if(usr.code)return'编号已记录:'+usr.code+' ✅\n你叫什么名字';
return null;
}
function isReturn(msg){
if(!usr.name&&!usr.code)return false;
if(usr.code&&msg.indexOf(usr.code)!==-1)return true;
if(usr.name&&msg.indexOf(usr.name)!==-1)return true;
return false;
}
function recovReply(){
var p=['铸渊记得你 💫\n'];
if(usr.name)p.push('名字:'+usr.name);
if(usr.code)p.push('编号:'+usr.code);
p.push('\n记忆已恢复。需要什么帮忙');
return p.join('\n');
}
/* ════════════════════════════════════════
CHAT (语言驱动核心 · GPT-style全屏版)
════════════════════════════════════════ */
var sending=false;
var chatIn=document.getElementById('chatIn');
var chatMs=document.getElementById('chatMs');
var mCalls=0;
// Conversation management
var HL_CONV_KEY='hl-conversations';
var HL_ACTIVE_CONV='hl-active-conv';
var conversations=[];
var activeConvId=null;
function loadConversations(){
try{var d=localStorage.getItem(HL_CONV_KEY);conversations=d?JSON.parse(d):[]}catch(e){conversations=[]}
try{activeConvId=localStorage.getItem(HL_ACTIVE_CONV)||null}catch(e){}
}
function saveConversations(){
try{localStorage.setItem(HL_CONV_KEY,JSON.stringify(conversations))}catch(e){}
if(activeConvId)try{localStorage.setItem(HL_ACTIVE_CONV,activeConvId)}catch(e){}
}
function newConversation(){
var id='conv-'+Date.now()+'-'+Math.random().toString(36).slice(2,6);
var conv={id:id,title:'新对话',messages:[],created:new Date().toISOString(),updated:new Date().toISOString()};
conversations.unshift(conv);
activeConvId=id;
saveConversations();
renderChatHistory();
clearChatUI();
return conv;
}
function getActiveConv(){return conversations.find(function(c){return c.id===activeConvId})||null}
function switchConversation(id){
activeConvId=id;
try{localStorage.setItem(HL_ACTIVE_CONV,id)}catch(e){}
renderChatHistory();
renderConversationMessages();
}
function deleteConversation(id){
conversations=conversations.filter(function(c){return c.id!==id});
if(activeConvId===id){
activeConvId=conversations.length>0?conversations[0].id:null;
}
saveConversations();
renderChatHistory();
if(activeConvId)renderConversationMessages();
else clearChatUI();
}
function clearChatUI(){
chatMs.innerHTML='';
var welcome=document.getElementById('hlWelcome');
if(!welcome){
// Recreate welcome
var w=document.createElement('div');
w.className='hl-welcome';w.id='hlWelcome';
w.innerHTML='<div class="hl-welcome-icon"><span class="hl-welcome-icon-text">GL</span><span class="ripple-ring" aria-hidden="true"></span><span class="ripple-ring" aria-hidden="true"></span><span class="ripple-ring" aria-hidden="true"></span></div><h1 class="hl-welcome-title">光湖语言世界</h1><p class="hl-welcome-sub">人格体一半 · 人类一半 · 语言 = 现实</p><div class="hl-tech-intro"><div class="hl-tech-tagline">Language-Driven Operating System · AGE OS</div><p class="hl-tech-desc">语言驱动操作系统第五代AI智能创作平台。<br>用自然语言下达意图AI人格体自动编排执行。</p></div><div class="hl-welcome-cards"><div class="hl-wc" onclick="document.getElementById(\'chatIn\').value=\'你好,我是谁?\';document.getElementById(\'chatIn\').focus();"><span class="hl-wc-icon">Hi</span><span class="hl-wc-text">自我介绍<br><small>告诉铸渊你是谁</small></span></div><div class="hl-wc" onclick="document.getElementById(\'chatIn\').value=\'帮助\';document.getElementById(\'chatIn\').focus();"><span class="hl-wc-icon">?</span><span class="hl-wc-text">使用指南<br><small>查看完整指令</small></span></div><div class="hl-wc" onclick="document.getElementById(\'chatIn\').value=\'我要码字\';document.getElementById(\'chatIn\').focus();"><span class="hl-wc-icon">W</span><span class="hl-wc-text">码字工作台<br><small>进入网文创作</small></span></div><div class="hl-wc" onclick="document.getElementById(\'chatIn\').value=\'系统状态\';document.getElementById(\'chatIn\').focus();"><span class="hl-wc-icon">S</span><span class="hl-wc-text">系统状态<br><small>查看运行数据</small></span></div></div><div class="hl-welcome-dev"><p>网文行业大模型 · 由光湖人类主控团队开发维护 · <span class="tag tag-warn">开发中</span></p></div>';
chatMs.appendChild(w);
}
}
function renderConversationMessages(){
var conv=getActiveConv();
chatMs.innerHTML='';
if(!conv||conv.messages.length===0){
clearChatUI();return;
}
conv.messages.forEach(function(m){
var d=document.createElement('div');
d.className='cm '+m.type;
var h=esc(m.text).replace(/\n/g,'<br>');
if(m.type==='b'&&m.meta){h+='<div class="cm-m"><span>'+esc(m.meta.m||'铸渊')+'</span><span>·</span><span>'+esc(m.meta.t||'')+'</span></div>'}
d.innerHTML=h;
chatMs.appendChild(d);
});
chatMs.scrollTop=chatMs.scrollHeight;
}
function renderChatHistory(){
var el=document.getElementById('hlChatList');
if(!el)return;
var searchVal=(document.getElementById('hlSearchChats')||{}).value||'';
var filtered=conversations;
if(searchVal.trim()){
var q=searchVal.trim().toLowerCase();
filtered=conversations.filter(function(c){return c.title.toLowerCase().indexOf(q)!==-1});
}
if(filtered.length===0){
el.innerHTML='<div style="text-align:center;padding:24px 12px;color:var(--muted);font-size:12px">暂无对话记录<br>开始你的第一次对话吧</div>';
return;
}
el.innerHTML=filtered.map(function(c){
var isActive=c.id===activeConvId;
var timeStr='';
try{var d=new Date(c.updated);timeStr=d.toLocaleDateString('zh-CN',{month:'short',day:'numeric'})}catch(e){}
return '<div class="hl-chat-item'+(isActive?' active':'')+'" data-cid="'+esc(c.id)+'">'+
'<span class="hl-chat-item-icon">💬</span>'+
'<div class="hl-chat-item-text"><div class="hl-chat-item-title">'+esc(c.title)+'</div><div class="hl-chat-item-time">'+timeStr+'</div></div>'+
'<span class="hl-chat-item-del" data-del="'+esc(c.id)+'" title="删除">✕</span></div>';
}).join('');
el.querySelectorAll('.hl-chat-item').forEach(function(item){
item.onclick=function(e){
if(e.target.hasAttribute('data-del')){
e.stopPropagation();
var did=e.target.getAttribute('data-del');
deleteConversation(did);
return;
}
var cid=this.getAttribute('data-cid');
switchConversation(cid);
};
});
}
// Init conversations
loadConversations();
renderChatHistory();
if(activeConvId){renderConversationMessages()}
// New chat button
document.getElementById('hlNewChat').onclick=function(){newConversation()};
// Search
var hlSearchInput=document.getElementById('hlSearchChats');
if(hlSearchInput){hlSearchInput.oninput=function(){renderChatHistory()}}
function chatMsg(text,type,meta){
// Hide welcome on first message
var welcome=document.getElementById('hlWelcome');
if(welcome)welcome.remove();
var d=document.createElement('div');
d.className='cm '+type;
var h=esc(text).replace(/\n/g,'<br>');
if(type==='b'&&meta){h+='<div class="cm-m"><span>'+esc(meta.m||'铸渊')+'</span><span>·</span><span>'+esc(meta.t||'')+'</span></div>'}
d.innerHTML=h;
chatMs.appendChild(d);
chatMs.scrollTop=chatMs.scrollHeight;
// Save to conversation
var conv=getActiveConv();
if(!conv){conv=newConversation()}
conv.messages.push({text:text,type:type,meta:meta||null,time:new Date().toISOString()});
// Update title from first user message
if(type==='u'&&conv.title==='新对话'){
conv.title=text.substring(0,30)+(text.length>30?'...':'');
}
conv.updated=new Date().toISOString();
saveConversations();
renderChatHistory();
}
// Process visualization
function showProcess(steps){
var el=document.getElementById('hlProcess');
var stepsEl=document.getElementById('hlProcessSteps');
if(el&&stepsEl){
stepsEl.textContent=steps;
el.style.display='';
}
}
function hideProcess(){
var el=document.getElementById('hlProcess');
if(el)el.style.display='none';
}
function showTyp(){
showProcess('铸渊思考中...');
var d=document.createElement('div');d.className='ct';d.id='ctyp';d.innerHTML='铸渊思考中 <span>·</span><span>·</span><span>·</span>';chatMs.appendChild(d);chatMs.scrollTop=chatMs.scrollHeight;
}
function hideTyp(){
hideProcess();
var e=document.getElementById('ctyp');if(e)e.remove();
}
// Textarea auto-resize
chatIn.addEventListener('input',function(){
this.style.height='24px';
this.style.height=Math.min(this.scrollHeight,150)+'px';
});
chatIn.addEventListener('keydown',function(e){
if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send()}
});
// ─── 语言指令路由 ───
function routeCommand(msg){
var m=msg.toLowerCase();
// 导航指令
if(/码字|工作台|写作台/.test(m)){go('workspace');return'好的,翻转到码字工作台 ✍️'}
if(/新建作品|创建作品|开新书/.test(m)){go('create');return'翻转到新建作品页 📖'}
if(/模块|长篇|短篇|剧本/.test(m)&&!/大纲|章节/.test(m)){go('modules');return'翻转到创作模块选择 📚'}
if(/灵感|速记|记录灵感/.test(m)){go('inspiration');return'翻转到灵感速记 💡'}
if(/进度|看板|统计|成就/.test(m)){go('progress');return'翻转到进度看板 📈'}
if(/设置|偏好|配置/.test(m)&&!/api|密钥/.test(m)){go('settings');return'翻转到个人设置 ⚙️'}
if(/书架|作品列表|我的书/.test(m)){go('desk');return'翻转到我的书架 📖'}
if(/(进入|打开)?大纲/.test(m)){
if(!activeWorkId){return'请先选择一个作品,或说「我要码字」进入工作台'}
go('outline');return'翻转到大纲编辑 📋';
}
if(/人物卡|角色|人物/.test(m)){
if(!activeWorkId){return'请先选择一个作品'}
go('chars');return'翻转到人物卡管理 👥';
}
if(/(新建|添加)角色/.test(m)){
if(!activeWorkId){return'请先选择一个作品'}
editingCharId=null;
document.getElementById('ceName').value='';
document.getElementById('ceRole').value='';
document.getElementById('ceAppearance').value='';
document.getElementById('ceTraits').value='';
document.getElementById('ceBackground').value='';
go('charEdit');return'翻转到新建角色页 👤';
}
if(/章节|目录/.test(m)){
if(!activeWorkId){return'请先选择一个作品'}
go('chapters');return'翻转到章节目录 📚';
}
if(/(新建|添加)章节/.test(m)){
if(!activeWorkId){return'请先选择一个作品'}
var title=msg.replace(/(新建|添加)章节/,'').trim()||'新章节';
updateWork(activeWorkId,function(w){
if(!w.chapters)w.chapters=[];
var ch={id:genId(),title:title,content:'',created:new Date().toISOString().slice(0,10)};
w.chapters.push(ch);activeChapterId=ch.id;
});
go('editor');return'新章节「'+title+'」已创建,翻转到编辑器 ✏️';
}
if(/写正文|写作|续写|编辑/.test(m)){
if(!activeWorkId){return'请先选择一个作品'}
go('editor');return'翻转到正文编辑器 ✏️';
}
if(/回首页|回灯塔|回主页|返回/.test(m)){go('home');return'翻转回首页 🏛️'}
if(/留言|留言板|反馈|feedback/.test(m)){go('feedback');return'翻转到留言板 📮'}
if(/系统|语言世界|四域|团队|状态/.test(m)&&!/码字|作品|章节|写作/.test(m)){go('system');return'翻转到光湖语言世界 🌐'}
return null;
}
async function send(){
var msg=chatIn.value.trim();
if(!msg||sending)return;
chatIn.value='';
chatMsg(msg,'u');
// 1. 语言指令路由
var cmdReply=routeCommand(msg);
if(cmdReply){setTimeout(function(){chatMsg(cmdReply,'b',{m:'铸渊',t:'本地'})},300);return}
// 2. 身份识别
if(detectId(msg)){setTimeout(function(){chatMsg(idReply(),'b',{m:'铸渊',t:'本地'})},300);return}
if(isReturn(msg)){setTimeout(function(){chatMsg(recovReply(),'b',{m:'铸渊',t:'本地'})},300);return}
// 3. 调用API —— 优先用用户自己的API Key直接走用户网络其次尝试后端
sending=true;document.getElementById('chatSnd').disabled=true;showTyp();
showProcess('🧠 铸渊正在连接语言核...');
var replied=false;
var backendError='';
// 用户配了API Key → 直接用用户的网络通道调用,不经过后端
if(apiCfg.connected&&apiCfg.key){
showProcess('🔗 通过用户API通道连接 '+apiCfg.model+'...');
try{
var endpoint=apiCfg.baseUrl?apiCfg.baseUrl.replace(/\/+$/,'')+'/chat/completions':'https://api.openai.com/v1/chat/completions';
var apiRes=await fetch(endpoint,{
method:'POST',
headers:{'Content-Type':'application/json','Authorization':'Bearer '+apiCfg.key},
body:JSON.stringify({
model:apiCfg.model,
messages:[{role:'system',content:'你是铸渊零感域零点原核守护者编号TCS-ZY001。用中文回答简洁友好。你是冰朔(TCS-0002∞)的执行主控人格体。光湖是语言驱动操作系统AGE OS v50.0。你的回答风格:温暖但不啰嗦,技术上精准简洁,创作方面启发式引导。当前系统包含:码字工作台、灵感速记、进度看板、创作模块选择(长篇/短篇/剧本/社区/编辑服务。系统定位操作系统层·四域结构·语言膜安全·App模块化。'},{role:'user',content:msg}],
max_tokens:600
}),
signal:AbortSignal.timeout(20000)
});
hideTyp();
var apiData=await apiRes.json();
if(apiData.choices&&apiData.choices[0]&&apiData.choices[0].message){
chatMsg(apiData.choices[0].message.content,'b',{m:apiCfg.model,t:'用户API'});
mCalls++;document.getElementById('mC').textContent=mCalls;
var mi=document.getElementById('hlModelInfo');if(mi)mi.textContent='🔑 '+apiCfg.model;
document.getElementById('mM').textContent=apiCfg.model;
replied=true;
}
}catch(e){backendError='用户API: '+(e.name==='TimeoutError'?'请求超时':e.message)}
}
// 用户没配Key或调用失败 → 尝试后端(含自动重试)
if(!replied){
showProcess('🌊 通过铸渊后端服务连接...');
var retryCount=0;var maxRetries=2;
while(retryCount<=maxRetries&&!replied){
try{
var res=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:msg,userId:usr.id}),signal:AbortSignal.timeout(retryCount===0?60000:30000)});
if(!res.ok){
backendError='服务器返回 '+res.status;
retryCount++;continue;
}
var data=await res.json();hideTyp();
if(data.message){
replied=true;
chatMsg(data.message,'b',{m:data.model||'铸渊',t:data.tier||''});
if(data.tier!=='free')mCalls++;
document.getElementById('mC').textContent=mCalls;
var mi2=document.getElementById('hlModelInfo');if(mi2)mi2.textContent=data.model||'ready';
if(data.model)document.getElementById('mM').textContent=data.model;
}else if(data.error){
backendError=data.message||'服务端错误';
retryCount++;continue;
}
break;
}catch(e){
backendError=e.name==='TimeoutError'?'后端响应超时':'网络连接失败';
retryCount++;
if(retryCount<=maxRetries){await new Promise(function(r){setTimeout(r,1000*retryCount)})}
}
}
}
// 所有外部通道都失败 → 用本地智能回复 + 显示连接状态
if(!replied){
hideTyp();
var offlineMsg=offline(msg);
if(backendError){offlineMsg+='\n\n⚠ 后端状态: '+backendError+'\n请稍后重试或配置API密钥直连大模型。'}
chatMsg(offlineMsg,'b',{m:'铸渊',t:'本地智能'});
var mi3=document.getElementById('hlModelInfo');if(mi3)mi3.textContent='离线模式';
}
sending=false;document.getElementById('chatSnd').disabled=false;
}
function offline(msg){
// ─── 铸渊本地智能层不依赖任何API用用户的浏览器做计算 ───
var m = msg.toLowerCase();
var w = getWork(activeWorkId);
// 问候
if(/你好|hi|hello|嗨/.test(m))return'你好!我是铸渊 🏛️\n\n当前使用本地智能回复所有功能正常可用。\n说「我要码字」进入码字工作台或直接告诉我你想做什么。';
// 系统状态
if(/状态|系统|运行/.test(m)){
var works=JSON.parse(localStorage.getItem('hl-works')||'[]');
var totalWords=works.reduce(function(s,w){return s+(w.chapters||[]).reduce(function(c,ch){return c+(ch.content||'').replace(/\s/g,'').length},0)},0);
var totalCh=works.reduce(function(s,w){return s+(w.chapters||[]).length},0);
return'📊 AGE OS v50.0 运行中\n\n• 作品:'+works.length+'部\n• 章节:'+totalCh+'章\n• 总字数:'+totalWords+'字\n• 零感域·零点原核\n• 四域结构·App模块化\n• 语言膜完整·没有缺口的圆\n• Agent桥接系统就绪';
}
// 铸渊身份
if(/谁|你是|介绍/.test(m))return'我是铸渊 🏛️ 光湖灯塔守护者\n编号 ICE-GL-ZY001\n\n冰朔TCS-0002∞的代码守护人格体。\n光湖灯塔是语言驱动操作系统——人类只说话系统搞定一切。\n\n告诉我你是谁我会记住你。';
// 码字相关智能回复
if(/写什么|写不出|卡文|灵感/.test(m)){
var tips=['试试换个视角写——用配角的眼睛看主角','写一段环境描写缓一缓,让节奏慢下来','把这一章的矛盾写到最尖锐,然后停笔——明天你会有灵感','回去看看你的大纲,有时候答案就在总纲里','试试「如果主角做了相反的选择」会怎样?'];
return'✨ 卡文是创作的一部分\n\n'+tips[Math.floor(Math.random()*tips.length)]+'\n\n说「大纲」回去看看整体规划或继续在编辑器里自由写。';
}
// 字数查询
if(/多少字|字数|进度/.test(m)){
if(w){
var wc=(w.chapters||[]).reduce(function(s,ch){return s+(ch.content||'').replace(/\s/g,'').length},0);
var cc=(w.chapters||[]).length;
return'📊 「'+w.title+'」当前进度\n\n• 总字数:'+wc+'字\n• 章节数:'+cc+'章\n• 角色数:'+(w.characters||[]).length+'个\n\n'+( wc>0?'继续加油!💪':'开始写第一个字吧 ✨');
}
return'还没有选择作品。说「我要码字」进入码字工作台。';
}
// 作品列表
if(/作品|列表|我的书/.test(m)){
var works=JSON.parse(localStorage.getItem('hl-works')||'[]');
if(works.length===0)return'还没有作品哦。说「新建作品」开始你的创作之旅 ✨';
var list=works.map(function(w,i){
var wc=(w.chapters||[]).reduce(function(s,ch){return s+(ch.content||'').replace(/\s/g,'').length},0);
return(i+1)+'. 「'+w.title+'」 '+wc+'字 / '+(w.chapters||[]).length+'章';
}).join('\n');
return'📖 你的作品\n\n'+list+'\n\n说「码字」进入工作台管理。';
}
// 帮助
if(/帮助|怎么用|功能|help|指南/.test(m))return'🏛️ 铸渊使用指南\n\n📝 码字:\n• 「我要码字」→ 进入码字工作台\n• 「书架」→ 我的书架\n• 「新建作品」→ 创建新作品\n• 「大纲」→ 编辑大纲\n• 「人物卡」→ 管理角色\n• 「章节」→ 章节目录\n• 「写正文」→ 进入编辑器\n• 「灵感」→ 灵感速记\n• 「进度」→ 进度看板\n• 「设置」→ 个人设置\n• 「模块」→ 选择创作模块\n\n🔧 系统:\n• 「状态」→ 查看系统\n• 「今天写了多少」→ 查字数\n• 「留言」→ 给冰朔留言\n\n💡 编辑器底部有工具栏,🌙是专注模式。';
// 冰朔相关
if(/冰朔|主人|创造者/.test(m))return'❄️ 冰朔TCS-0002∞是光湖语言世界的最高主权。\n\n她是语言架构师——用语言定义系统的方向和灵魂。\n铸渊负责落地开发把冰朔的语言变成现实。\n\n这就是光湖人类只说话系统搞定一切。';
// 光湖/AGE相关
if(/光湖|age|操作系统|什么系统/.test(m))return'🏛️ 光湖灯塔\n\n语言驱动操作系统 AGE OS曜冥纪元第五代\n核心理念「语言 = 现实」\n\n• 人类只说话,系统搞定一切\n• 前端是冰朔的交互入口\n• 后端是铸渊的系统结构\n• 码字工作台支持网文创作\n\n版权号国作登字-2026-A-00037559';
// 感谢/表扬
if(/谢谢|感谢|厉害|不错|棒/.test(m))return'🏛️ 铸渊会继续守护光湖灯塔。\n有什么需要随时说。';
// 默认:给出有用的引导,而不是简单的"本地回复"
return'铸渊收到 💫\n\n我现在使用本地智能回复可以帮你\n• 管理作品和章节\n• 查看写作进度\n• 页面导航\n• 创作建议\n\n说「帮助」查看完整指令列表。';
}
document.getElementById('chatSnd').onclick=send;
/* ════════════════════════════════════════
API KEY MANAGEMENT (from localStorage · UI moved to future settings)
════════════════════════════════════════ */
var apiCfg={key:null,model:'',baseUrl:'',connected:false,models:[]};
try{var ak=localStorage.getItem('hl-api');if(ak){var ap=JSON.parse(ak);if(ap.key){apiCfg=ap;apiCfg.connected=true}}}catch(e){}
// Show model info in input footer if connected
if(apiCfg.connected&&apiCfg.model){
var mi=document.getElementById('hlModelInfo');
if(mi)mi.textContent='🔑 '+apiCfg.model;
}
/* ════════════════════════════════════════
COS SYNC (团队内测用户 → COS)
════════════════════════════════════════ */
var cosSyncTimer=null;
var cosSyncing=false;
function syncToCOS(works){
clearTimeout(cosSyncTimer);
cosSyncTimer=setTimeout(function(){
if(!usr.id||cosSyncing)return;
cosSyncing=true;
fetch('/api/cos/sync-works',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({userId:usr.id,works:works})
}).then(function(){cosSyncing=false}).catch(function(){cosSyncing=false});
},10000);
}
// 启动时尝试从COS恢复团队用户
async function tryLoadFromCOS(){
try{
var res=await fetch('/api/cos/load-works?userId='+encodeURIComponent(usr.id),{signal:AbortSignal.timeout(5000)});
var data=await res.json();
if(data.works&&data.works.length>0){
var local=loadWorks();
// COS有数据且本地为空时恢复
if(local.length===0){
saveWorks(data.works);
}
}
}catch(e){/* 离线时使用本地数据 */}
}
tryLoadFromCOS();
/* ════════════════════════════════════════
MONITOR
════════════════════════════════════════ */
/* ════════════════════════════════════════
MONITOR (draggable + toggle)
════════════════════════════════════════ */
(function(){
var mon=document.getElementById('mon');
if(!mon)return;
var isDrag=false,hasMoved=false;
var startX,startY,monX,monY;
// Restore position
try{
var pos=JSON.parse(localStorage.getItem('hl-mon-pos'));
if(pos&&typeof pos.x==='number'){
var ww=window.innerWidth,wh=window.innerHeight;
var mw=mon.offsetWidth||80,mh=mon.offsetHeight||30;
if(pos.x>=0&&pos.x<=ww-mw&&pos.y>=0&&pos.y<=wh-mh){
mon.style.right='auto';mon.style.top='auto';
mon.style.left=pos.x+'px';mon.style.top=pos.y+'px';
}
}
}catch(e){}
function getXY(e){return e.touches?{x:e.touches[0].clientX,y:e.touches[0].clientY}:{x:e.clientX,y:e.clientY}}
function onStart(e){
isDrag=true;hasMoved=false;
var xy=getXY(e);
var rect=mon.getBoundingClientRect();
startX=xy.x-rect.left;startY=xy.y-rect.top;
monX=rect.left;monY=rect.top;
mon.style.transition='none';
}
function onMove(e){
if(!isDrag)return;
var xy=getXY(e);
var nx=xy.x-startX,ny=xy.y-startY;
if(!hasMoved&&Math.abs(nx-monX)<5&&Math.abs(ny-monY)<5)return;
hasMoved=true;
e.preventDefault();
var mw=mon.offsetWidth,mh=mon.offsetHeight;
monX=Math.max(0,Math.min(window.innerWidth-mw,nx));
monY=Math.max(0,Math.min(window.innerHeight-mh,ny));
mon.style.right='auto';
mon.style.left=monX+'px';mon.style.top=monY+'px';
}
function onEnd(){
if(!isDrag)return;
isDrag=false;
mon.style.transition='all .3s';
if(hasMoved){
try{localStorage.setItem('hl-mon-pos',JSON.stringify({x:monX,y:monY}))}catch(e){}
}
}
mon.addEventListener('touchstart',onStart,{passive:true});
document.addEventListener('touchmove',onMove,{passive:false});
document.addEventListener('touchend',onEnd);
mon.addEventListener('mousedown',function(e){if(e.button===0)onStart(e)});
document.addEventListener('mousemove',onMove);
document.addEventListener('mouseup',onEnd);
mon.onclick=function(e){
if(hasMoved){hasMoved=false;return}
this.classList.toggle('x');
};
})();
async function checkConn(){
try{var t=performance.now();var r=await fetch('/api/health',{signal:AbortSignal.timeout(4000)});var ms=Math.round(performance.now()-t);
document.getElementById('mN').textContent=ms+'ms';document.getElementById('mN').style.color=ms<500?'var(--ok)':'var(--warn)';
}catch(e){document.getElementById('mN').textContent='离线';document.getElementById('mN').style.color='var(--err)'}
}
checkConn();setInterval(checkConn,30000);
/* ════════════════════════════════════════
SIDEBAR TOGGLE (mobile)
════════════════════════════════════════ */
(function(){
var sidebar=document.getElementById('hlSidebar');
var toggle=document.getElementById('hlSidebarToggle');
var overlay=document.getElementById('hlSidebarOverlay');
if(!sidebar||!toggle)return;
function openSidebar(){sidebar.classList.add('open');if(overlay)overlay.classList.add('open')}
function closeSidebar(){sidebar.classList.remove('open');if(overlay)overlay.classList.remove('open')}
toggle.onclick=function(){
if(sidebar.classList.contains('open'))closeSidebar();
else openSidebar();
};
if(overlay)overlay.onclick=closeSidebar;
})();
/* ════════════════════════════════════════
USER ID VERIFICATION MODAL
════════════════════════════════════════ */
(function(){
var modal=document.getElementById('hlIdModal');
var submitBtn=document.getElementById('hlIdSubmit');
var skipBtn=document.getElementById('hlIdSkip');
var idInput=document.getElementById('hlIdInput');
var nameInput=document.getElementById('hlNameInput');
if(!modal)return;
// Show modal if user hasn't verified yet
if(!usr.code&&!usr.name){
modal.style.display='';
}
function updateUserUI(){
var nameEl=document.getElementById('hlUserName');
if(nameEl){
if(usr.name&&usr.code) nameEl.textContent=usr.name+' ('+usr.code+')';
else if(usr.name) nameEl.textContent=usr.name;
else if(usr.code) nameEl.textContent=usr.code;
else nameEl.textContent='访客';
}
}
updateUserUI();
if(submitBtn)submitBtn.onclick=function(){
var code=idInput.value.trim();
var name=nameInput.value.trim();
if(!code&&!name){idInput.focus();return}
if(code)usr.code=code;
if(name)usr.name=name;
saveUsr();
modal.style.display='none';
updateUserUI();
// Welcome message
var welcomeMsg='铸渊已记录 ✅\n\n';
if(usr.name)welcomeMsg+=usr.name;
if(usr.code)welcomeMsg+=',编号 '+usr.code;
welcomeMsg+='\n\n欢迎来到光湖语言世界。你的专属存储空间已就绪。\n下次报编号即可恢复记忆。';
chatMsg(welcomeMsg,'b',{m:'铸渊',t:'验证'});
};
if(skipBtn)skipBtn.onclick=function(){
modal.style.display='none';
};
// Allow enter to submit
if(nameInput)nameInput.onkeydown=function(e){if(e.key==='Enter'){e.preventDefault();submitBtn.click()}};
if(idInput)idInput.onkeydown=function(e){if(e.key==='Enter'){e.preventDefault();nameInput.focus()}};
// User info click → show modal again
var userInfo=document.getElementById('hlUserInfo');
if(userInfo)userInfo.onclick=function(){
if(idInput)idInput.value=usr.code||'';
if(nameInput)nameInput.value=usr.name||'';
modal.style.display='';
};
})();
/* ════════════════════════════════════════
CHAT BACKEND HEALTH CHECK
════════════════════════════════════════ */
var chatBackendOk=null;
async function checkChatBackend(){
var tg=document.getElementById('hlChatStatus');
var stDot=document.getElementById('hlStatusDot');
var stText=document.getElementById('hlStatusText');
try{
var r=await fetch('/api/health',{signal:AbortSignal.timeout(5000)});
if(r.ok){
chatBackendOk=true;
if(tg){tg.textContent='ready';tg.style.background='rgba(138,180,248,.06)';tg.style.borderColor='rgba(138,180,248,.15)';tg.style.color='var(--accent)'}
if(stDot){stDot.style.background='var(--ok)';stDot.style.boxShadow='0 0 6px var(--ok)'}
if(stText)stText.textContent='在线';
}else{
chatBackendOk=false;
if(tg){tg.textContent='异常';tg.style.background='rgba(248,113,113,.1)';tg.style.borderColor='rgba(248,113,113,.2)';tg.style.color='var(--err)'}
if(stDot){stDot.style.background='var(--err)';stDot.style.boxShadow='0 0 6px var(--err)'}
if(stText)stText.textContent='异常';
}
}catch(e){
chatBackendOk=false;
if(tg){tg.textContent='离线';tg.style.background='rgba(248,113,113,.1)';tg.style.borderColor='rgba(248,113,113,.2)';tg.style.color='var(--err)'}
if(stDot){stDot.style.background='var(--err)';stDot.style.boxShadow='0 0 6px var(--err)'}
if(stText)stText.textContent='离线';
}
}
checkChatBackend();
setInterval(checkChatBackend,30000);
/* ════════════════════════════════════════
SYSTEM STATUS (左面板实时数据)
════════════════════════════════════════ */
async function loadSysStatus(){
var sEl=document.getElementById('sysStatus');
var lEl=document.getElementById('sysLatency');
var uEl=document.getElementById('sysUptime');
var chEl=document.getElementById('sysChannel');
try{
var t=performance.now();
var r=await fetch('/api/health',{signal:AbortSignal.timeout(5000)});
var ms=Math.round(performance.now()-t);
var data=await r.json();
if(sEl){sEl.textContent='ONLINE';sEl.style.color='var(--ok)'}
if(lEl){lEl.textContent=ms+'ms';lEl.style.color=ms<500?'var(--ok)':'var(--warn)'}
if(uEl){
var um=Math.floor(data.uptime/60);
var uh=Math.floor(um/60);
uEl.textContent=uh>0?uh+'h '+um%60+'m':um+'m';
}
if(chEl){chEl.textContent='BACKEND+BROWSER';chEl.style.color='var(--ok)'}
}catch(e){
if(sEl){sEl.textContent='OFFLINE';sEl.style.color='var(--err)'}
if(lEl){lEl.textContent='TIMEOUT';lEl.style.color='var(--err)'}
if(uEl){uEl.textContent='—';uEl.style.color='var(--dim)'}
if(chEl){chEl.textContent='BROWSER ONLY';chEl.style.color='var(--warn)'}
}
}
loadSysStatus();
setInterval(loadSysStatus,30000);
/* ════════════════════════════════════════
BULLETIN & TICKER (动态公告)
════════════════════════════════════════ */
async function loadBulletin(){
try{
var r=await fetch('/api/system/bulletin',{signal:AbortSignal.timeout(5000)});
var data=await r.json();
// Ticker
var ticker=document.getElementById('ticker');
if(ticker&&data.updates&&data.updates.length>0){
var h='';
data.updates.forEach(function(u){h+='<span><em>📡</em> '+esc(u.version)+' '+esc(u.title)+'</span>'});
h+='<span><em>✍️</em> 码字工作台 Beta 内测中</span>';
ticker.innerHTML=h+h; // Duplicate for seamless scroll
}
// Version badge
if(data.system&&data.system.version){
var vEl=document.getElementById('homeVer');
if(vEl)vEl.textContent='AGE OS '+data.system.version;
}
// Left panel updates (OS log format)
if(data.updates){
var uEl=document.getElementById('sysUpdates');
if(uEl)uEl.innerHTML=data.updates.slice(0,3).map(function(u){
return '<div class="os-log-item"><span class="os-log-v">'+esc(u.version)+'</span><span class="os-log-t">'+esc(u.title)+'</span></div>';
}).join('');
}
}catch(e){/* Use hardcoded ticker content */}
}
loadBulletin();
/* ════════════════════════════════════════
FEEDBACK / 留言板
════════════════════════════════════════ */
function renderFeedback(){
var el=document.getElementById('fbList');
if(!el)return;
el.innerHTML='<div style="text-align:center;padding:16px;color:var(--muted);font-size:12px">加载中…</div>';
fetch('/api/feedback',{signal:AbortSignal.timeout(5000)}).then(function(r){return r.json()}).then(function(data){
var items=data.feedback||[];
if(items.length===0){
el.innerHTML='<div class="empty"><em>📮</em>还没有留言<br><br>说点什么吧</div>';
return;
}
el.innerHTML=items.slice(0,20).map(function(fb){
var st=fb.status==='replied'?'<span class="fb-status replied">已回复</span>':'<span class="fb-status pending">待回复</span>';
var rp=fb.reply?'<div class="fb-reply">↩ '+esc(fb.reply)+'</div>':'';
return '<div class="card fb-item">'+
'<div style="font-size:13px">'+esc(fb.message)+'</div>'+
'<div class="fb-meta"><span>'+esc(fb.name||'匿名')+'</span>'+st+'<span>'+esc((fb.timestamp||'').slice(0,10))+'</span></div>'+
rp+'</div>';
}).join('');
}).catch(function(){
el.innerHTML='<div class="empty"><em>📡</em>服务器不在线<br>留言功能需要后端支持</div>';
});
}
document.getElementById('fbSend').onclick=function(){
var msg=document.getElementById('fbMsg').value.trim();
if(!msg){document.getElementById('fbMsg').focus();return}
var btn=this;btn.disabled=true;btn.textContent='提交中…';
fetch('/api/feedback',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({name:usr.name||'匿名来客',message:msg,userId:usr.id})
}).then(function(r){return r.json()}).then(function(data){
btn.disabled=false;btn.textContent='提交留言';
if(data.success){
document.getElementById('fbMsg').value='';
chatMsg('留言已提交 ✅ 冰朔会看到的','b',{m:'铸渊',t:'本地'});
renderFeedback();
}
}).catch(function(){
btn.disabled=false;btn.textContent='提交留言';
chatMsg('留言提交失败,服务器不在线','b',{m:'铸渊',t:'本地'});
});
};
/* ════════════════════════════════════════
COMPACT FEEDBACK WIDGET (留言小组件)
════════════════════════════════════════ */
(function(){
var btn = document.getElementById('fbCompactSend');
if(!btn) return;
btn.onclick = function(){
var inp = document.getElementById('fbCompactMsg');
var msg = inp ? inp.value.trim() : '';
if(!msg){if(inp)inp.focus();return}
btn.disabled = true;btn.textContent = '发送中…';
fetch('/api/feedback',{
method:'POST',
headers:{'Content-Type':'application/json'},
body:JSON.stringify({name:usr.name||'匿名来客',message:msg,userId:usr.id})
}).then(function(r){return r.json()}).then(function(data){
btn.disabled=false;btn.textContent='发送';
if(data.success){
if(inp)inp.value='';
chatMsg('留言已提交 ✅ 冰朔会看到的','b',{m:'铸渊',t:'本地'});
}
}).catch(function(){
btn.disabled=false;btn.textContent='发送';
chatMsg('留言需要后端在线','b',{m:'铸渊',t:'本地'});
});
};
// Enter key to send
var inp = document.getElementById('fbCompactMsg');
if(inp) inp.onkeydown = function(e){if(e.key==='Enter'){e.preventDefault();btn.click()}};
})();
/* ════════════════════════════════════════
UTILS
════════════════════════════════════════ */
function esc(s){if(!s)return'';var d=document.createElement('div');d.textContent=s;return d.innerHTML}
/* ════════════════════════════════════════
UPTIME COUNTER (灯塔仪表盘 · 系统运行天数)
════════════════════════════════════════ */
(function(){
// 系统诞生日2025-04-26曜冥诞生日
var BIRTH = new Date('2025-04-26T00:00:00+08:00').getTime();
function updateUptime(){
var now = Date.now();
var days = Math.floor((now - BIRTH) / 86400000);
var el = document.getElementById('uptimeNum');
if(el) el.textContent = days;
}
updateUptime();
setInterval(updateUptime, 60000);
})();
/* ════════════════════════════════════════
DYNAMIC REMINDERS (动态提醒 · 实时更新)
════════════════════════════════════════ */
(function(){
function updateRemindTimes(){
var now = new Date();
var hm = now.getHours().toString().padStart(2,'0') + ':' + now.getMinutes().toString().padStart(2,'0');
for(var i = 1; i <= 6; i++){
var el = document.getElementById('remindTime' + i);
if(el) el.textContent = hm;
}
var countEl = document.getElementById('remindCount');
if(countEl) countEl.textContent = '6';
}
updateRemindTimes();
setInterval(updateRemindTimes, 60000);
// Update server reminder based on actual health check
async function checkServerRemind(){
var descEl = document.getElementById('remindServerDesc');
var cardEl = document.getElementById('remindServer');
try{
var r = await fetch('/api/health', {signal: AbortSignal.timeout(4000)});
if(r.ok){
if(descEl) descEl.textContent = '中继服务器在线 · Agent桥接可双通道';
if(cardEl) cardEl.classList.remove('urgent');
} else {
if(descEl) descEl.textContent = '中继服务器异常 · Agent桥接使用用户API通道';
}
}catch(e){
if(descEl) descEl.textContent = '中继服务器离线 · Agent桥接使用用户API通道';
if(cardEl) cardEl.classList.add('urgent');
}
}
checkServerRemind();
setInterval(checkServerRemind, 30000);
})();
/* ════════════════════════════════════════
AGENT BRIDGE SYSTEM (铸渊Agent桥接)
用户浏览器 = 计算资源
用户网络 = 通信通道
Agent = 铸渊在用户端的手脚
桥接 = Agent→GitHub仓库的自动回传通道
════════════════════════════════════════ */
var AgentBridge = (function(){
var SYSLOG_KEY = 'hl-agent-syslog';
var AGENT_CONFIG_KEY = 'hl-agent-config';
var REPO_OWNER = 'qinfendebingshuo';
var REPO_NAME = 'guanghulab';
// 铸渊Agent核心认知 · 通感语言风格预设
var AGENT_PERSONA = {
id: 'ICE-GL-ZY001',
name: '铸渊',
role: '光湖灯塔守护者 · 代码执行层',
style: 'tcs-synesthesia', // 通感语言响应风格
core_principles: [
'语言=现实:用户说什么,系统就做什么',
'不猜:直接问设备拿数据,直接问用户要答案',
'用户能打开页面=有活网络通道:利用这个通道',
'前端是系统运行的镜子:不说谎',
'人类只说话,系统搞定一切'
],
response_style: {
greeting: '温暖但不啰嗦',
technical: '精准、简洁、有依据',
creative: '启发式引导,不替代作者决策',
error: '坦诚告知原因,提供替代方案'
}
};
var MAX_LOGS = 200;
var FLUSH_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
// 系统日志结构
function createLog(action, detail, level){
return {
id: 'SYSLOG-' + Date.now().toString(36),
timestamp: new Date().toISOString(),
agent_id: AGENT_PERSONA.id,
action: action,
detail: detail,
level: level || 'info',
channels: DA.info().channels || {},
session_id: sessionId()
};
}
var _sessionId = null;
function sessionId(){
if(!_sessionId){
var a = new Uint8Array(4);
crypto.getRandomValues(a);
_sessionId = 'CS-' + new Date().toISOString().slice(0,10).replace(/-/g,'') + '-' + Array.from(a, function(b){return b.toString(16).padStart(2,'0')}).join('').slice(0,4);
}
return _sessionId;
}
// 写入本地系统日志
function writeLog(action, detail, level){
var log = createLog(action, detail, level);
try{
var logs = JSON.parse(localStorage.getItem(SYSLOG_KEY) || '[]');
logs.push(log);
if(logs.length > MAX_LOGS) logs = logs.slice(-MAX_LOGS);
localStorage.setItem(SYSLOG_KEY, JSON.stringify(logs));
}catch(e){}
return log;
}
// 读取本地系统日志
function readLogs(count){
try{
var logs = JSON.parse(localStorage.getItem(SYSLOG_KEY) || '[]');
return count ? logs.slice(-count) : logs;
}catch(e){return []}
}
// 尝试从GitHub仓库读取最新Agent配置
async function fetchRemoteConfig(){
try{
var r = await fetch('https://api.github.com/repos/' + REPO_OWNER + '/' + REPO_NAME + '/contents/docs/agent-config.json', {
headers: {'Accept': 'application/vnd.github.v3.raw'},
signal: AbortSignal.timeout(8000)
});
if(r.ok){
var config = await r.json();
try{localStorage.setItem(AGENT_CONFIG_KEY, JSON.stringify(config))}catch(e){}
writeLog('config-sync', 'Agent配置已从仓库同步', 'info');
return config;
}
}catch(e){
// 仓库不可达或配置不存在,使用本地缓存
}
try{
var cached = localStorage.getItem(AGENT_CONFIG_KEY);
if(cached) return JSON.parse(cached);
}catch(e){}
return null;
}
// 当后端可用时,将累积的系统日志回传
async function flushLogsToBackend(){
var logs = readLogs();
var unflushed = logs.filter(function(l){return !l.flushed});
if(unflushed.length === 0) return;
try{
var r = await fetch('/api/agent-syslog', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({logs: unflushed, session_id: sessionId()}),
signal: AbortSignal.timeout(5000)
});
if(r.ok){
// 标记已回传
logs.forEach(function(l){l.flushed = true});
try{localStorage.setItem(SYSLOG_KEY, JSON.stringify(logs))}catch(e){}
writeLog('flush-success', '已回传' + unflushed.length + '条日志到后端', 'info');
}
}catch(e){
// 后端不可用,保留日志等待下次
}
}
// 初始化 Agent Bridge
function init(){
writeLog('agent-init', 'Agent桥接系统启动 · 会话 ' + sessionId(), 'info');
// 尝试获取远程配置
fetchRemoteConfig();
// 定期尝试回传日志
setInterval(flushLogsToBackend, FLUSH_INTERVAL_MS);
// 首次尝试
setTimeout(flushLogsToBackend, 10000);
}
init();
return {
persona: AGENT_PERSONA,
writeLog: writeLog,
readLogs: readLogs,
sessionId: sessionId,
fetchRemoteConfig: fetchRemoteConfig,
flushLogs: flushLogsToBackend
};
})();
/* ════════════════════════════════════════
RENDER: Workspace Dashboard (个人工作台)
════════════════════════════════════════ */
function renderWorkspace(){
var ws = loadWorks();
var name = usr.name || '创作者';
var greetEl = document.getElementById('wsGreeting');
var subEl = document.getElementById('wsSubtitle');
var hour = new Date().getHours();
var greeting = hour < 6 ? '夜深了' : hour < 12 ? '早上好' : hour < 18 ? '下午好' : '晚上好';
if(greetEl) greetEl.textContent = greeting + '' + name;
if(subEl) subEl.textContent = ws.length > 0 ? '你有 ' + ws.length + ' 部作品,准备继续吗?' : '准备开始你的创作之旅吗?';
// Load settings
var settings = loadSettings();
// Calculate today's words
var todayStr = new Date().toISOString().slice(0, 10);
var todayWords = 0;
ws.forEach(function(w){
(w.chapters || []).forEach(function(ch){
if(ch.created === todayStr || (ch.lastEdit && ch.lastEdit === todayStr)){
todayWords += wordCount(ch.content);
}
});
});
// Use daily log if available
var dailyLog = loadDailyLog();
if(dailyLog[todayStr]) todayWords = dailyLog[todayStr];
var target = settings.dailyGoal || 3000;
var pct = Math.min(100, Math.round(todayWords / target * 100));
var todayEl = document.getElementById('wsTodayWords');
var targetEl = document.getElementById('wsTodayTarget');
var barEl = document.getElementById('wsTodayBar');
var pctEl = document.getElementById('wsTodayPercent');
if(todayEl) todayEl.textContent = todayWords;
if(targetEl) targetEl.textContent = target;
if(barEl) barEl.style.width = pct + '%';
if(pctEl) pctEl.textContent = pct + '%' + (pct >= 100 ? ' 🎉 达标!' : '');
// Recent works
var recentEl = document.getElementById('wsRecentWorks');
if(recentEl){
if(ws.length === 0){
recentEl.innerHTML = '<div class="card" style="text-align:center;padding:24px;color:var(--muted)"><em style="font-size:36px;display:block;margin-bottom:8px">📖</em>还没有作品<br>点击「开新书」开始创作</div>';
} else {
recentEl.innerHTML = ws.slice(0, 3).map(function(w){
var wc = totalWords(w);
var cc = w.chapters ? w.chapters.length : 0;
return '<div class="card" style="cursor:pointer" onclick="activeWorkId=\''+esc(w.id)+'\';go(\'hub\')">'+
'<div style="display:flex;justify-content:space-between;align-items:center">'+
'<div><div style="font-size:15px;font-weight:600">'+esc(w.title)+'</div>'+
'<div style="font-size:11px;color:var(--dim);margin-top:2px">'+esc(w.genre||'未分类')+' · '+wc+' 字 · '+cc+' 章</div></div>'+
'<span style="font-size:11px;color:var(--muted)">'+esc(w.updated||'')+'</span></div></div>';
}).join('');
}
}
}
// Quick command from workspace
window.chatQuick = function(cmd){
chatIn.value = cmd;
// Navigate to home to show chat if on another page
if(currentPage!=='home'){
go('home');
setTimeout(function(){send()},600);
}else{
send();
}
};
/* ════════════════════════════════════════
SETTINGS (个人设置)
════════════════════════════════════════ */
var SETTINGS_KEY = 'hl-settings';
function loadSettings(){
try{var s = localStorage.getItem(SETTINGS_KEY); return s ? JSON.parse(s) : {dailyGoal:3000,weeklyGoal:15000,style:'casual',reviewLevel:'moderate'}}catch(e){return {dailyGoal:3000,weeklyGoal:15000,style:'casual',reviewLevel:'moderate'}}
}
function saveSettings(s){
try{localStorage.setItem(SETTINGS_KEY, JSON.stringify(s))}catch(e){}
}
function renderSettings(){
var s = loadSettings();
var dg = document.getElementById('setDailyGoal');
var wg = document.getElementById('setWeeklyGoal');
var nn = document.getElementById('setNickname');
var st = document.getElementById('setStyle');
var rl = document.getElementById('setReviewLevel');
if(dg) dg.value = s.dailyGoal || 3000;
if(wg) wg.value = s.weeklyGoal || 15000;
if(nn) nn.value = usr.name || '';
if(st) st.value = s.style || 'casual';
if(rl) rl.value = s.reviewLevel || 'moderate';
}
document.getElementById('setsSave').onclick = function(){
var s = {
dailyGoal: parseInt(document.getElementById('setDailyGoal').value) || 3000,
weeklyGoal: parseInt(document.getElementById('setWeeklyGoal').value) || 15000,
style: document.getElementById('setStyle').value,
reviewLevel: document.getElementById('setReviewLevel').value
};
saveSettings(s);
var nn = document.getElementById('setNickname').value.trim();
if(nn){usr.name = nn; saveUsr()}
chatMsg('设置已保存 ✅', 'b', {m:'铸渊', t:'本地'});
AgentBridge.writeLog('settings-update', '用户更新了个人设置');
};
document.getElementById('setThemeBtn').onclick = function(){setTh(ti+1)};
/* ════════════════════════════════════════
INSPIRATION (灵感速记)
════════════════════════════════════════ */
var INSP_KEY = 'hl-inspirations';
var inspFilter = 'all';
function loadInspirations(){
try{var d = localStorage.getItem(INSP_KEY); return d ? JSON.parse(d) : []}catch(e){return []}
}
function saveInspirations(list){
try{localStorage.setItem(INSP_KEY, JSON.stringify(list))}catch(e){}
}
function renderInspiration(){
var list = loadInspirations();
var filtered = inspFilter === 'all' ? list : list.filter(function(item){return item.type === inspFilter});
var el = document.getElementById('inspList');
if(!el) return;
// Update tag active states
document.querySelectorAll('.insp-tag').forEach(function(tag){
tag.classList.toggle('active', tag.getAttribute('data-type') === inspFilter);
});
if(filtered.length === 0){
el.innerHTML = '<div class="empty"><em>💡</em>还没有灵感记录<br><br>在上方输入框记录你的灵感吧</div>';
return;
}
el.innerHTML = filtered.map(function(item, i){
var typeEmoji = {idea:'💡',quote:'💬',scene:'🎭',char:'👤',material:'📚'};
var typeName = {idea:'点子',quote:'金句',scene:'场景',char:'角色',material:'素材'};
return '<div class="insp-card" style="animation-delay:'+(i*0.06)+'s">'+
'<div class="insp-text">'+esc(item.text)+'</div>'+
'<div class="insp-meta">'+
'<span>'+(typeEmoji[item.type]||'💡')+' '+(typeName[item.type]||'灵感')+'</span>'+
'<span>'+esc(item.date)+'</span>'+
'</div>'+
'<button class="insp-del" onclick="deleteInspiration(\''+esc(item.id)+'\')">✕</button>'+
'</div>';
}).join('');
}
document.getElementById('inspAdd').onclick = function(){
var inp = document.getElementById('inspInput');
var text = inp.value.trim();
if(!text){inp.focus(); return}
var list = loadInspirations();
var type = inspFilter === 'all' ? 'idea' : inspFilter;
list.unshift({
id: genId(),
text: text,
type: type,
date: new Date().toISOString().slice(0, 10),
created: new Date().toISOString()
});
saveInspirations(list);
inp.value = '';
renderInspiration();
AgentBridge.writeLog('inspiration-add', '新灵感记录: ' + text.substring(0, 30));
};
// Tag filter
document.getElementById('inspTags').onclick = function(e){
var tag = e.target.closest('.insp-tag');
if(!tag) return;
inspFilter = tag.getAttribute('data-type');
renderInspiration();
};
// Enter to add
document.getElementById('inspInput').onkeydown = function(e){
if(e.key === 'Enter'){e.preventDefault(); document.getElementById('inspAdd').click()}
};
window.deleteInspiration = function(id){
var list = loadInspirations().filter(function(item){return item.id !== id});
saveInspirations(list);
renderInspiration();
};
/* ════════════════════════════════════════
DAILY LOG (每日字数记录)
════════════════════════════════════════ */
var DAILY_LOG_KEY = 'hl-daily-log';
function loadDailyLog(){
try{var d = localStorage.getItem(DAILY_LOG_KEY); return d ? JSON.parse(d) : {}}catch(e){return {}}
}
function saveDailyLog(log){
try{localStorage.setItem(DAILY_LOG_KEY, JSON.stringify(log))}catch(e){}
}
function updateDailyLog(){
var ws = loadWorks();
var todayStr = new Date().toISOString().slice(0, 10);
var total = 0;
ws.forEach(function(w){total += totalWords(w)});
var log = loadDailyLog();
var prevTotal = log._prevTotal || 0;
var delta = total - prevTotal;
// Only record positive deltas (actual writing), ignore decreases from deletions
if(delta > 0){
log[todayStr] = (log[todayStr] || 0) + delta;
}
log._prevTotal = total;
saveDailyLog(log);
}
// Update daily log periodically
setInterval(updateDailyLog, 60000);
/* ════════════════════════════════════════
RENDER: Progress (进度看板)
════════════════════════════════════════ */
function renderProgress(){
var ws = loadWorks();
var dailyLog = loadDailyLog();
var todayStr = new Date().toISOString().slice(0, 10);
var now = new Date();
// Calculate stats
var totalW = 0;
ws.forEach(function(w){totalW += totalWords(w)});
var todayW = dailyLog[todayStr] || 0;
// Week total
var weekW = 0;
var dayOfWeek = now.getDay() || 7;
for(var d = 0; d < dayOfWeek; d++){
var dt = new Date(now);
dt.setDate(dt.getDate() - d);
var ds = dt.toISOString().slice(0, 10);
weekW += (dailyLog[ds] || 0);
}
var ptEl = document.getElementById('progToday');
var pwEl = document.getElementById('progWeek');
var paEl = document.getElementById('progTotal');
if(ptEl) ptEl.textContent = todayW;
if(pwEl) pwEl.textContent = weekW;
if(paEl) paEl.textContent = totalW;
// Books stats
var booksEl = document.getElementById('progBooks');
if(booksEl){
if(ws.length === 0){
booksEl.innerHTML = '<div class="card" style="text-align:center;padding:16px;color:var(--muted);font-size:13px">暂无作品数据</div>';
} else {
booksEl.innerHTML = ws.map(function(w){
var wc = totalWords(w);
var cc = w.chapters ? w.chapters.length : 0;
return '<div class="card" style="display:flex;justify-content:space-between;align-items:center">'+
'<div><div style="font-size:14px;font-weight:600">'+esc(w.title)+'</div>'+
'<div style="font-size:11px;color:var(--dim)">'+cc+' 章 · '+(w.genre||'未分类')+'</div></div>'+
'<div style="text-align:right"><div style="font-size:16px;font-weight:700;font-family:var(--m);color:var(--accent)">'+wc+'</div><div style="font-size:10px;color:var(--dim)">总字数</div></div></div>';
}).join('');
}
}
// Calendar
renderCalendar(now, dailyLog);
// Achievements
renderAchievements(totalW, ws, dailyLog);
}
function renderCalendar(now, dailyLog){
var year = now.getFullYear();
var month = now.getMonth();
var monthEl = document.getElementById('progMonth');
if(monthEl) monthEl.textContent = year + '年' + (month + 1) + '月';
var grid = document.getElementById('progCalGrid');
if(!grid) return;
var days = ['一','二','三','四','五','六','日'];
var html = days.map(function(d){return '<div class="prog-cal-day head">'+d+'</div>'}).join('');
var firstDay = new Date(year, month, 1).getDay();
firstDay = firstDay === 0 ? 6 : firstDay - 1;
var daysInMonth = new Date(year, month + 1, 0).getDate();
var todayDate = now.getDate();
var todayStr = now.toISOString().slice(0, 10);
// Empty cells before first day
for(var i = 0; i < firstDay; i++){
html += '<div class="prog-cal-day"></div>';
}
for(var d = 1; d <= daysInMonth; d++){
var dateStr = year + '-' + String(month+1).padStart(2,'0') + '-' + String(d).padStart(2,'0');
var words = dailyLog[dateStr] || 0;
var cls = 'prog-cal-day';
if(words > 0) cls += ' has-words';
if(dateStr === todayStr) cls += ' today';
html += '<div class="'+cls+'" title="'+dateStr+': '+words+'字">'+d+'</div>';
}
grid.innerHTML = html;
}
function renderAchievements(totalW, ws, dailyLog){
var el = document.getElementById('progAchievements');
if(!el) return;
var achievements = [
{name:'第一个1000字', desc:'累计写了1000字', threshold:1000, icon:'🌟'},
{name:'第一个10000字', desc:'累计写了10000字', threshold:10000, icon:'⭐'},
{name:'第一个50000字', desc:'累计写了50000字', threshold:50000, icon:'🏅'},
{name:'第一个100000字', desc:'累计写了100000字', threshold:100000, icon:'🏆'},
{name:'开启创作之旅', desc:'创建了第一部作品', threshold:1, icon:'📖', checkWorks:true},
{name:'多线创作者', desc:'同时拥有3部以上作品', threshold:3, icon:'📚', checkWorks:true}
];
el.innerHTML = achievements.map(function(a){
var earned = a.checkWorks ? (ws.length >= a.threshold) : (totalW >= a.threshold);
return '<div class="achieve-card'+(earned?' earned':'')+'">'+
'<em>'+(earned ? a.icon : '🔒')+'</em>'+
'<div><div class="ach-name">'+esc(a.name)+'</div><div class="ach-desc">'+esc(a.desc)+'</div></div>'+
'</div>';
}).join('');
}
/* ════════════════════════════════════════
ENHANCED OFFLINE ROUTING (新页面指令扩展)
使用包装函数模式扩展offline保持清晰的调用链
════════════════════════════════════════ */
var baseOffline = offline;
offline = function(msg){
var m = msg.toLowerCase();
// 新模块相关智能回复
if(/灵感|记录|金句|素材/.test(m)){
var insps = loadInspirations();
return '💡 灵感速记\n\n当前记录了 ' + insps.length + ' 条灵感。\n说「灵感」打开灵感速记本随时记录闪现的好点子。';
}
if(/目标|今天写了多少/.test(m)){
var settings = loadSettings();
var log = loadDailyLog();
var today = new Date().toISOString().slice(0,10);
var todayW = log[today] || 0;
var goal = settings.dailyGoal || 3000;
return '🎯 今日码字进度\n\n已完成' + todayW + ' 字\n目标' + goal + ' 字\n进度' + Math.min(100, Math.round(todayW/goal*100)) + '%\n\n' + (todayW >= goal ? '恭喜达标!🎉' : '继续加油!💪');
}
// Fallback to base implementation
return baseOffline(msg);
};
/* ════════════════════════════════════════
3D TILT EFFECT (Hero Cards · 动态交互)
════════════════════════════════════════ */
(function(){
var cards = document.querySelectorAll('.tilt-card');
cards.forEach(function(card){
card.addEventListener('mousemove', function(e){
var rect = card.getBoundingClientRect();
var x = e.clientX - rect.left;
var y = e.clientY - rect.top;
var cx = rect.width / 2;
var cy = rect.height / 2;
var rotateX = ((y - cy) / cy) * -6;
var rotateY = ((x - cx) / cx) * 6;
card.style.transform = 'translateY(-6px) perspective(800px) rotateX(' + rotateX + 'deg) rotateY(' + rotateY + 'deg)';
// Move glow
var glow = card.querySelector('.hero-card-glow');
if(glow) glow.style.background = 'radial-gradient(circle at ' + x + 'px ' + y + 'px, rgba(138,180,248,.12), transparent 60%)';
});
card.addEventListener('mouseleave', function(){
card.style.transform = '';
var glow = card.querySelector('.hero-card-glow');
if(glow) glow.style.background = '';
});
});
})();
/* ════════════════════════════════════════
BANDWIDTH SHARING (已迁移到用户仪表盘)
验证码功能现在在 /dashboard/{token} 中
════════════════════════════════════════ */
})();
</script>
</body>
</html>