﻿
        :root {
            /* === Global Theme Variables === */
            /* 1. Colors - Primary & Backgrounds */
            --primary-purple: rgb(200, 150, 220);
            /* Main Brand Color */
            --primary-purple-light: rgba(200, 150, 220, 0.15);
            --primary-bg: #fdf6ff;
            /* App Background */
            --card-bg: #ffffff;
            --card-bg-transparent: rgba(255, 255, 255, 0.9);

            /* 2. Colors - Accents */
            --accent-yellow: #fff5ba;
            --accent-pink: #ffcbd2;
            --accent-blue: #bae1ff;
            --accent-green: #c1ffd7;
            --accent-orange: #fff5e5;

            /* 3. Colors - Text & Grays */
            --text-main: #4a4a4a;
            /* Was --text-dark */
            --text-secondary: #666666;
            --text-muted: #999999;
            --text-light: #cccccc;
            --border-color: #eeeeee;
            --border-color-dark: #dddddd;

            /* 4. Fonts */
            --font-main: 'Comic Sans MS', 'YouYuan', 'Microsoft YaHei', cursive, sans-serif;
            --font-serif: 'KaiTi', serif;

            /* 5. UI Elements */
            --glass: rgba(255, 255, 255, 0.65);
            --glass-strong: rgba(255, 255, 255, 0.75);
            --glass-border: rgba(255, 255, 255, 0.4);

            /* 6. Effects */
            --shadow-sm: 0 2px 5px rgba(0, 0, 0, 0.05);
            --shadow-md: 0 4px 10px rgba(0, 0, 0, 0.05);
            --shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15);
            /* Main deep shadow */
            --shadow-float: 0 10px 40px rgba(0, 0, 0, 0.2);

            /* 7. Vitals Gradients */
            --gradient-hp: linear-gradient(90deg, #ff9a9e 0%, #fad0c4 99%);
            --gradient-san: linear-gradient(90deg, #a18cd1 0%, #fbc2eb 100%);
            --gradient-3: linear-gradient(90deg, #84fab0 0%, #8fd3f4 100%);
            --gradient-4: linear-gradient(90deg, #ffecd2 0%, #fcb69f 100%);
            --gradient-5: linear-gradient(90deg, #667eea 0%, #764ba2 100%);

            /* 8. Textures & Lines */
            --texture-grid: rgba(0, 0, 0, 0.03);
            --texture-dot: #e0e0e0;
            --line-color: #eeeeee;
            --dashed-line: #cccccc;

            /* 9. Tag Colors (Background / Text) */
            --tag-bg-purple: #f3e5ff;
            --tag-text-purple: #9b59b6;
            --tag-bg-pink: #ffe5ee;
            --tag-text-pink: #e74c3c;
            --tag-bg-blue: #e5f3ff;
            --tag-text-blue: #3498db;
            --tag-bg-green: #e5ffe5;
            --tag-text-green: #27ae60;
            --tag-bg-orange: #fff5e5;
            --tag-text-orange: #e67e22;

            /* 10. Ranking Colors (S, A, B, C) */
            --rank-bg-s: #fcf3cf;
            --rank-text-s: #f39c12;
            --rank-bg-a: #fadbd8;
            --rank-text-a: #e74c3c;
            --rank-bg-b: #d6eaf8;
            --rank-text-b: #3498db;
            --rank-bg-c: #e5e8e8;
            --rank-text-c: #7f8c8d;

            /* 11. Status Dots */
            --status-online: #2ecc71;
            --status-busy: #f39c12;
            --status-offline: #95a5a6;
            --status-healthy: #2ecc71;
            /* Green */
            --status-unwell: #f1c40f;
            /* Yellow */
            --status-injured: #e74c3c;
            /* Red */
            --status-dead: #7f8c8d;
            /* Gray */

            /* 12. Page-Specific Colors (for Nav & Accents) */
            --color-home: #9b59b6;
            --color-home-bg: rgba(155, 89, 182, 0.15);
            --color-me: #f39c12;
            --color-me-bg: rgba(243, 156, 18, 0.15);
            --color-social: #27ae60;
            --color-social-bg: rgba(39, 174, 96, 0.15);
            --color-map: #e91e63;
            --color-map-bg: rgba(233, 30, 99, 0.15);
            --color-settings: #3498db;
            --color-settings-bg: rgba(52, 152, 219, 0.15);

            /* 13. Border Radius */
            --radius-xs: 4px;
            --radius-sm: 8px;
            --radius-md: 12px;
            --radius-lg: 20px;
            --radius-full: 999px;

            /* 14. Z-Index Stack */
            --z-base: 1;
            --z-behind: -1;
            --z-content: 100;
            --z-nav: 900;
            --z-overlay: 3000;
            --z-toast: 9999;
            --z-modal: 10000;

            /* 15. Transitions */
            --trans-fast: 0.1s;
            --trans-base: 0.3s;
            --trans-slow: 0.8s;
            --ease-in-out: cubic-bezier(0.34, 1.56, 0.64, 1);

            /* 16. Semantic Overlays */
            --overlay-dark: rgba(0, 0, 0, 0.5);
            --overlay-darker: rgba(0, 0, 0, 0.6);

            /* 17. Spacing */
            --spacing-xs: 5px;
            --spacing-sm: 10px;
            --spacing-md: 15px;
            --spacing-lg: 20px;
            --spacing-xl: 30px;

            /* Backward Compatibility (keep existing names if used directly) */
            --text-dark: var(--text-main);
        }

        * {
            box-sizing: border-box;
            /* Use standard variable for highlight color if strict, but transparent is standard reset */
            -webkit-tap-highlight-color: transparent;
            /* 隐藏滚动条但允许滚动 */
            scrollbar-width: none;
            /* Firefox */
            -ms-overflow-style: none;
            /* IE/Edge */
        }

        /* WebKit browsers (Chrome, Safari) */
        *::-webkit-scrollbar {
            display: none;
        }

        /* FIX: Ensure buttons and inputs inherit the font family */
        button,
        input,
        select,
        textarea {
            font-family: inherit;
        }

        /* Mobile Touch Feedback & Active States */
        button:active,
        .diary-preview-item:active,
        .map-node:active,
        .nav-item:active,
        .action-btn:active {
            transform: scale(0.96);
            opacity: 0.8;
            transition: transform 0.1s;
        }

        /* Safe Area Support for Bottom Nav */
        nav,
        .bottom-nav,
        #mobile-nav {
            padding-bottom: env(safe-area-inset-bottom);
        }

        /* Chat window styles would typically end here.
           The following styles are for the NPC detail card and toast hint. */

        /* --- ✨ NPC Details Modal (Long Press) ✨ --- */
        .detail-modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: var(--overlay-darker);
            /* Semi-transparent overlay */
            backdrop-filter: blur(5px);
            z-index: var(--z-overlay);
            display: none;
            justify-content: center;
            align-items: center;
            opacity: 0;
            transition: opacity var(--trans-base);
        }

        .detail-card {
            width: 92%;
            max-width: 500px;
            /* Increase width */
            max-height: 85dvh;
            background: var(--card-bg);
            border-radius: var(--radius-lg);
            overflow-y: auto;
            position: relative;
            box-shadow: var(--shadow-float);
            transform: scale(0.9);
            transition: transform var(--trans-base) var(--ease-in-out);
            /* Prevent body scroll when modal is open */
            overscroll-behavior: contain;

            /* Paper texture */
            background-image:
                linear-gradient(90deg, var(--texture-grid) 1px, transparent 1px),
                radial-gradient(var(--texture-dot) 1px, transparent 1px);
            background-size: 20px 20px, 10px 10px;
        }

        /* Desktop Adaptation */
        @media (min-width: 768px) {
            .detail-card {
                max-width: 600px;
                display: grid;
                grid-template-columns: 240px 1fr;
                /* Side by side on large screens */
                grid-template-rows: 1fr;
                height: 500px;
                max-height: 80dvh;
                overflow: hidden;
            }

            .detail-header-img {
                height: 100%;
                grid-row: 1 / -1;
            }

            .detail-content {
                grid-row: 1 / -1;
                overflow-y: auto;
                padding: 40px 30px;
            }

            .detail-tape-large {
                display: none;
                /* Hide tape on landscape layout or reposition */
            }

            .close-detail-btn {
                color: var(--text-main);
                /* Darker close btn on white paper */
            }
        }

        .detail-card.active {
            transform: scale(1);
        }

        /* --- 📔 Diary Section & Modal --- */
        .diary-preview-item {
            background: linear-gradient(to right, var(--card-bg), var(--primary-bg));
            border-left: 3px solid var(--accent-pink);
            padding: 10px;
            margin-bottom: 8px;
            border-radius: var(--radius-xs);
            box-shadow: var(--shadow-sm);
            position: relative;
            cursor: pointer;
        }

        .diary-preview-date {
            font-size: 10px;
            color: var(--text-muted);
            margin-bottom: 2px;
        }

        .diary-preview-title {
            font-size: 13px;
            font-weight: bold;
            color: var(--text-secondary);
        }

        .diary-preview-snippet {
            font-size: 11px;
            color: var(--text-secondary);
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
            margin-top: 4px;
        }

        /* Diary Modal */
        .diary-list-container {
            max-height: 60dvh;
            overflow-y: auto;
            padding: 10px;
            overscroll-behavior: contain;
        }

        .diary-full-item {
            background: var(--card-bg);
            margin-bottom: 15px;
            padding: 20px;
            border-radius: var(--radius-sm);
            box-shadow: var(--shadow-sm);
            position: relative;
            /* Lined paper effect */
            background-image: repeating-linear-gradient(var(--card-bg) 0px, var(--card-bg) 24px, var(--line-color) 25px);
        }

        .diary-full-date {
            font-size: 12px;
            color: var(--primary-purple);
            font-weight: bold;
            margin-bottom: 5px;
        }

        .diary-full-title {
            font-size: 16px;
            font-weight: bold;
            margin-bottom: 15px;
            line-height: 1.2;
        }

        .diary-full-content {
            font-size: 14px;
            line-height: 25px;
            /* Match grid lines */
            color: var(--text-secondary);
            font-family: var(--font-serif);
        }

        .diary-tape-deco {
            position: absolute;
            top: -10px;
            right: 20px;
            width: 80px;
            height: 20px;
            box-shadow: var(--shadow-sm);
        }

        .detail-header-img {
            width: 100%;
            height: 200px;
            object-fit: cover;
            background: var(--border-color-dark);
        }

        .detail-avatar-placeholder {
            display: flex;
            align-items: center;
            justify-content: center;
            background: var(--primary-purple);
            color: white;
            font-size: 72px;
            font-weight: bold;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
        }

        .detail-tape-large {
            position: absolute;
            top: 185px;
            /* Near bottom of image */
            left: 50%;
            transform: translateX(-50%) rotate(-2deg);
            width: 140px;
            height: 24px;
            box-shadow: var(--shadow-sm);
            z-index: 5;
            border: none;
            /* Remove default background to allow JS override */
            background-image: none;
        }

        .detail-content {
            padding: 30px 20px 20px;
            position: relative;
        }

        .detail-name-lg {
            font-size: 24px;
            font-weight: 800;
            color: var(--primary-purple);
            text-align: center;
            margin-bottom: 5px;
        }

        .detail-title-lg {
            text-align: center;
            color: var(--text-secondary);
            font-size: 13px;
            margin-bottom: 20px;
            font-family: var(--font-serif);
        }

        /* NPC Current Location Row */
        .npc-location-row {
            text-align: center;
            margin-top: -10px;
            margin-bottom: 15px;
            font-size: 12px;
            color: #888;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 4px;
        }

        .npc-location-icon {
            font-size: 14px;
        }

        .npc-location-name {
            color: var(--primary-purple);
            font-weight: bold;
        }

        /* NPC Status Dots & Colors */
        .npc-status-dot {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background-color: var(--status-healthy);
            display: inline-block;
            box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
            transition: background-color 0.3s ease;
        }

        .npc-status-dot.healthy {
            background-color: var(--status-healthy);
        }

        .npc-status-dot.unwell {
            background-color: var(--status-unwell);
            box-shadow: 0 0 8px var(--status-unwell);
        }

        .npc-status-dot.injured {
            background-color: var(--status-injured);
            box-shadow: 0 0 8px var(--status-injured);
        }

        .npc-status-dot.dead {
            background-color: var(--status-dead);
            filter: grayscale(1);
        }

        .npc-polaroid.npc-dead {
            filter: grayscale(0.8) contrast(0.8);
            opacity: 0.9;
        }

        .npc-polaroid.npc-dead .npc-img {
            filter: grayscale(1);
        }

        /* Attribute Box */
        .attr-box {
            background: var(--glass-strong);
            border: 2px dashed var(--accent-blue);
            /* accent-blue */
            border-radius: var(--radius-md);
            padding: 15px;
            margin-bottom: 15px;
        }

        .attr-title {
            font-size: 14px;
            font-weight: bold;
            color: var(--text-secondary);
            margin-bottom: 10px;
            border-bottom: 1px dashed var(--dashed-line);
            padding-bottom: 5px;
        }

        .detail-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 10px;
            font-size: 12px;
        }

        .detail-item {
            display: flex;
            justify-content: space-between;
            color: var(--text-secondary);
        }

        .detail-val {
            font-weight: bold;
            color: var(--text-main);
        }

        .loc-desc-text {
            font-size: 14px;
            line-height: 1.6;
            color: var(--text-main);
            white-space: pre-wrap;
            margin: 10px 0;
            padding: 8px;
            background: rgba(255, 255, 255, 0.3);
            border-radius: var(--radius-sm);
        }

        /* Location People Row */
        .loc-people-row {
            margin-top: 8px;
            font-size: 13px;
            color: #666;
            line-height: 1.6;
        }

        .loc-people-icon {
            font-weight: bold;
            color: var(--primary-purple);
        }

        .loc-people-list {
            display: inline;
            color: #555;
        }

        .loc-person-tag {
            display: inline-block;
            background: rgba(200, 150, 220, 0.15);
            padding: 2px 8px;
            border-radius: 10px;
            margin: 2px 3px;
            font-size: 12px;
            color: var(--primary-purple);
            font-weight: 500;
        }

        /* Relationship Detail */
        .rel-detail-container {
            margin-top: 10px;
            position: relative;
        }

        /* New Centered Bar Styles */
        .rel-track-center {
            width: 100%;
            height: 8px;
            background: #eee;
            border-radius: 4px;
            position: relative;
            overflow: hidden;
            border: 1px solid #ddd;
        }

        .rel-fill-center {
            height: 100%;
            position: absolute;
            top: 0;
            transition: all 0.3s ease;
        }

        /* Positive: Pink/Purple */
        .rel-fill-pos {
            background: linear-gradient(90deg, #e1bee7, #ab47bc);
        }

        /* Negative: Blue/Gray */
        .rel-fill-neg {
            background: linear-gradient(90deg, #bdc3c7, #7f8c8d);
        }

        .rel-markers {
            display: flex;
            justify-content: space-between;
            font-size: 10px;
            color: #999;
            margin-top: 4px;
        }

        /* Mini bar for list */
        .npc-affinity-center {
            width: 100%;
            height: 4px;
            background: #eee;
            border-radius: 2px;
            position: relative;
            margin-top: 4px;
            overflow: hidden;
        }

        .npc-affinity-fill-center {
            height: 100%;
            position: absolute;
            top: 0;
        }

        .rel-detail-track {
            height: 10px;
            background: var(--line-color);
            border-radius: 5px;
            overflow: hidden;
            border: 1px solid var(--border-color);
        }

        .rel-detail-fill {
            height: 100%;
            background: var(--gradient-hp);
            /* Approximated to HP gradient which is pinkish */
        }

        .rel-text {
            font-size: 11px;
            color: var(--text-muted);
            margin-top: 5px;
            display: flex;
            justify-content: space-between;
        }

        .detail-desc {
            font-size: 13px;
            line-height: 1.6;
            color: var(--text-secondary);
            /* SVG pattern replacement difficult with vars, keeping hardcoded for texture */
            background: url("data:image/svg+xml,%3Csvg width='100%25' height='24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0,24 L100%25,24' stroke='%23eeeeee' stroke-width='1' stroke-dasharray='4,4'/%3E%3C/svg%3E");
            background-size: 100% 24px;
            padding-bottom: 2px;
        }

        .close-detail-btn {
            position: absolute;
            top: 15px;
            right: 15px;
            background: transparent;
            color: var(--text-secondary);
            border: none;
            width: 30px;
            height: 30px;
            border-radius: 0;
            font-size: 24px;
            cursor: pointer;
            z-index: 10;
        }

        /* --- NPC 详情卡新增板块样式 --- */
        .npc-stat-bars {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .npc-stat-item {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .npc-stat-label {
            width: 50px;
            font-size: 12px;
            color: var(--text-secondary);
            text-align: right;
        }

        .npc-stat-track {
            flex: 1;
            height: 8px;
            background: var(--line-color);
            border-radius: var(--radius-xs);
            overflow: hidden;
        }

        .npc-stat-fill {
            height: 100%;
            border-radius: 4px;
            background: var(--gradient-san);
        }

        .npc-stat-val {
            font-size: 10px;
            color: var(--text-muted);
            min-width: 40px;
        }

        .npc-traits {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
        }

        .npc-trait-tag {
            padding: 4px 10px;
            border-radius: var(--radius-md);
            font-size: 11px;
            font-weight: bold;
        }

        .npc-trait-purple {
            background: var(--tag-bg-purple);
            color: var(--tag-text-purple);
        }

        .npc-trait-pink {
            background: var(--tag-bg-pink);
            color: var(--tag-text-pink);
        }

        .npc-trait-blue {
            background: var(--tag-bg-blue);
            color: var(--tag-text-blue);
        }

        .npc-trait-green {
            background: var(--tag-bg-green);
            color: var(--tag-text-green);
        }

        .npc-trait-orange {
            background: var(--tag-bg-orange);
            color: var(--tag-text-orange);
        }

        .npc-skills {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 8px;
        }

        .npc-skill-item {
            background: linear-gradient(135deg, var(--card-bg) 0%, #f9f9f9 100%);
            padding: 8px 10px;
            border-radius: var(--radius-sm);
            border: 1px solid var(--border-color);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .npc-skill-name {
            font-size: 12px;
            color: var(--text-secondary);
        }

        .npc-skill-rank {
            font-size: 10px;
            padding: 2px 6px;
            border-radius: 4px;
            font-weight: bold;
        }

        .npc-rank-s {
            background: var(--rank-bg-s);
            color: var(--rank-text-s);
            border: 1px solid var(--rank-text-s);
        }

        .npc-rank-a {
            background: var(--rank-bg-a);
            color: var(--rank-text-a);
            border: 1px solid var(--rank-text-a);
        }

        .npc-rank-b {
            background: var(--rank-bg-b);
            color: var(--rank-text-b);
            border: 1px solid var(--rank-text-b);
        }

        .npc-rank-c {
            background: var(--rank-bg-c);
            color: var(--rank-text-c);
            border: 1px solid var(--rank-text-c);
        }

        .npc-inventory {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 8px;
        }

        .npc-inv-item {
            background: var(--card-bg);
            border: 1px dashed var(--border-color-dark);
            border-radius: var(--radius-sm);
            padding: 8px 6px;
            text-align: center;
            font-size: 11px;
            color: var(--text-secondary);
        }

        .npc-inv-icon {
            font-size: 18px;
            margin-bottom: 4px;
        }

        .npc-rels {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .npc-rel-item {
            background: linear-gradient(to right, var(--card-bg-transparent), var(--glass));
            border-left: 3px solid var(--primary-purple);
            padding: 6px 10px;
            border-radius: 0 8px 8px 0;
        }

        .npc-rel-header {
            display: flex;
            justify-content: space-between;
            font-size: 12px;
            margin-bottom: 4px;
        }

        .npc-rel-name {
            font-weight: bold;
        }

        .npc-rel-val {
            color: var(--primary-purple);
            font-weight: bold;
        }

        .npc-rel-bar {
            height: 5px;
            background: var(--line-color);
            border-radius: 3px;
            overflow: hidden;
        }

        .npc-rel-fill {
            height: 100%;
            background: var(--primary-purple);
            border-radius: 3px;
        }

        .npc-diary {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .npc-diary-item {
            background: linear-gradient(to right, #fff, #fdfbf7);
            border-left: 3px solid var(--accent-pink);
            padding: 8px 10px;
            border-radius: 4px;
        }

        .npc-diary-date {
            font-size: 10px;
            color: #999;
        }

        .npc-diary-title {
            font-size: 12px;
            font-weight: bold;
            color: #555;
        }

        .npc-diary-snippet {
            font-size: 11px;
            color: #888;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        body {
            margin: 0;
            padding: 0;
            font-family: var(--font-main);
            background: var(--primary-bg);
            color: var(--text-main);
            height: 100vh;
            height: 100dvh;
            overflow: hidden;
            display: flex;
            flex-direction: column;
        }

        /* --- 顶部音乐播放器 --- */
        #music-header {
            height: 70px;
            background: var(--glass);
            backdrop-filter: blur(10px);
            border-bottom: 2px dashed var(--primary-purple);
            display: flex;
            align-items: center;
            padding: 0 15px;
            z-index: 100;
            box-shadow: var(--shadow);
            flex-shrink: 0;
        }

        .disc-container {
            width: 50px;
            height: 50px;
            position: relative;
        }

        .disc {
            width: 100%;
            height: 100%;
            border-radius: 50%;
            background: #000;
            animation: spin 4s linear infinite;
            animation-play-state: paused;
            display: flex;
            align-items: center;
            justify-content: center;
        }

        .disc.playing {
            animation-play-state: running;
        }

        .disc svg {
            width: 100%;
            height: 100%;
            fill: var(--primary-purple);
        }

        .song-info {
            flex: 1;
            margin: 0 15px;
            display: flex;
            flex-direction: column;
            justify-content: center;
            overflow: hidden;
        }

        .song-title {
            font-size: 14px;
            font-weight: bold;
            white-space: nowrap;
        }

        .artist-name {
            font-size: 12px;
            color: var(--text-muted);
        }

        .progress-bar {
            height: 4px;
            background: var(--border-color-dark);
            border-radius: 2px;
            margin-top: 5px;
            position: relative;
        }

        .progress-fill {
            height: 100%;
            background: var(--primary-purple);
            width: 0%;
            border-radius: 2px;
            position: relative;
        }

        .progress-dot {
            width: 8px;
            height: 8px;
            background: var(--primary-purple);
            border-radius: 50%;
            position: absolute;
            right: -4px;
            top: -2px;
        }

        .controls button {
            background: none;
            border: none;
            cursor: pointer;
            padding: 5px;
        }

        .controls svg {
            width: 24px;
            height: 24px;
            fill: var(--primary-purple);
        }

        /* --- 主内容区域 (翻页容器) --- */
        /* --- 主内容区域 (翻页容器) --- */
        #main-container {
            flex: 1;
            position: relative;
            perspective: 2500px;
            overflow: hidden;
            background: linear-gradient(135deg, #e8e0f0 0%, #f5f0fa 100%);
        }

        /* 书脊装饰 */
        #main-container::before {
            content: '';
            position: absolute;
            left: 0;
            top: 0;
            width: 8px;
            height: 100%;
            background: linear-gradient(to right,
                    rgba(180, 140, 200, 0.4) 0%,
                    var(--primary-purple-light) 50%,
                    transparent 100%);
            z-index: 200;
            pointer-events: none;
        }

        .page {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: var(--primary-bg);
            padding: 20px 20px 20px 25px;
            overflow-y: auto;
            transform-style: preserve-3d;
            /* 页面美化：手账背景 */
            background-image:
                radial-gradient(var(--primary-purple) 1px, transparent 1px),
                linear-gradient(to right, var(--primary-purple-light) 0%, transparent 20px);
            background-size: 20px 20px, 100% 100%;
            /* 页面边缘: 静态样式 */
            border-left: 3px solid rgba(200, 150, 220, 0.3);

            /* 默认隐藏 */
            z-index: 1;
            opacity: 1;
            pointer-events: none;
            transform: rotateY(0deg);

            /* 性能优化：启用硬件加速 */
            will-change: transform;
            /* 静态阴影，移除动画中的阴影变换以提升性能 */
            box-shadow: 2px 0 8px rgba(0, 0, 0, 0.05);
        }

        /* 页面背面（翻页时显示） */
        .page::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: linear-gradient(to left,
                    rgba(230, 220, 240, 0.95) 0%,
                    rgba(245, 240, 250, 0.9) 100%);
            backface-visibility: hidden;
            transform: rotateY(180deg);
            z-index: -1;
        }

        /* 当前活动页面 */
        .page.active {
            z-index: var(--z-content);
            pointer-events: auto;
        }

        /* FIX: Remove padding for Home Page so Input Area can touch edges */
        #page-home {
            padding: 0;
        }

        /* 翻页动画：翻下一页（当前页向左翻走） */
        .page.flip-to-left {
            animation: paperFlipToLeft var(--trans-slow) var(--ease-in-out) forwards;
            z-index: 150;
            transform-origin: left center;
        }

        /* 翻页动画：翻上一页（目标页从左侧翻回来覆盖） */
        .page.restore-from-left {
            animation: paperRestoreFromLeft var(--trans-slow) var(--ease-in-out) forwards;
            z-index: 160;
            /* 比当前页更高，覆盖它 */
            transform-origin: left center;
            /* 初始状态设为已翻开 */
            transform: rotateY(-115deg);
        }

        /* 目标页面显示 (用于翻下一页时，目标页在下面等待) */
        .page.reveal {
            z-index: 90;
            pointer-events: auto;
            animation: pageReveal 0.2s ease-out forwards;
        }

        @keyframes paperFlipToLeft {
            0% {
                transform: rotateY(0deg);
            }

            100% {
                transform: rotateY(-115deg);
            }
        }

        @keyframes paperRestoreFromLeft {
            0% {
                transform: rotateY(-115deg);
            }

            100% {
                transform: rotateY(0deg);
            }
        }

        @keyframes pageReveal {
            0% {
                opacity: 0.95;
            }

            100% {
                opacity: 1;
            }
        }

        /* --- 底部导航栏 (Premium Glass) --- */
        /* --- 底部导航栏 (Premium Glass) --- */
        #bottom-nav {
            height: 70px;
            /* Slightly taller for dock feel */
            background: var(--glass-strong);
            backdrop-filter: blur(16px);
            -webkit-backdrop-filter: blur(16px);
            border-top: 1px solid var(--glass-border);
            display: flex;
            justify-content: space-around;
            align-items: center;
            z-index: var(--z-nav);
            flex-shrink: 0;
            box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.05);
            padding-bottom: env(safe-area-inset-bottom, 10px);
            padding-top: 5px;
        }

        .nav-item {
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            color: var(--text-muted);
            font-size: 10px;
            cursor: pointer;
            transition: all var(--trans-base) var(--ease-in-out);
            position: relative;
            width: 50px;
            height: 50px;
            border-radius: var(--radius-md);
        }

        .nav-item.active {
            color: var(--primary-purple);
            background: var(--primary-purple-light);
            transform: translateY(-5px) scale(1.05);
        }

        .nav-item.active::after {
            content: '';
            position: absolute;
            bottom: -5px;
            width: 10px;
            height: 9px;
            background: var(--primary-purple);
            /* 使用心形SVG作为mask */
            -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M12 2S9 2 8 5C7 2 4 2 4 2C1.8 2 0 3.8 0 6c0 4.1 8 9 8 9s8-5 8-9c0-2.2-1.8-4-4-4'/%3E%3C/svg%3E") center/contain no-repeat;
            mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M12 2S9 2 8 5C7 2 4 2 4 2C1.8 2 0 3.8 0 6c0 4.1 8 9 8 9s8-5 8-9c0-2.2-1.8-4-4-4'/%3E%3C/svg%3E") center/contain no-repeat;
        }

        /* 导航栏索引便签颜色 - 每个页面不同颜色 */
        /* 主页 - 紫色 (默认) */
        .nav-item[data-nav="home"].active {
            color: var(--color-home);
            background: var(--color-home-bg);
        }

        .nav-item[data-nav="home"].active::after {
            background: var(--color-home);
        }

        /* 我 - 黄色 */
        .nav-item[data-nav="me"].active {
            color: var(--color-me);
            background: var(--color-me-bg);
        }

        .nav-item[data-nav="me"].active::after {
            background: var(--color-me);
        }

        /* 社交 - 绿色 */
        .nav-item[data-nav="social"].active {
            color: var(--color-social);
            background: var(--color-social-bg);
        }

        .nav-item[data-nav="social"].active::after {
            background: var(--color-social);
        }

        /* 地图 - 粉色 */
        .nav-item[data-nav="map"].active {
            color: var(--color-map);
            background: var(--color-map-bg);
        }

        .nav-item[data-nav="map"].active::after {
            background: var(--color-map);
        }

        /* 设置 - 蓝色 */
        .nav-item[data-nav="settings"].active {
            color: var(--color-settings);
            background: var(--color-settings-bg);
        }

        .nav-item[data-nav="settings"].active::after {
            background: var(--color-settings);
        }

        .nav-item svg {
            width: 24px;
            height: 24px;
            margin-bottom: 2px;
            fill: currentColor;
            display: block;
        }

        /* --- 通用UI组件 --- */
        .card {
            background: var(--card-bg-transparent);
            border-radius: var(--radius-md);
            padding: 15px;
            margin-bottom: 15px;
            box-shadow: var(--shadow-md);
            border: 2px dashed var(--primary-purple);
            width: 100%;
            box-sizing: border-box;
        }

        .btn {
            background: var(--primary-purple);
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: var(--radius-lg);
            font-weight: bold;
            cursor: pointer;
            box-shadow: 0 4px 0 #9c6ead;
            transition: transform var(--trans-fast), box-shadow var(--trans-fast);
        }

        .btn:active {
            transform: translateY(4px);
            box-shadow: 0 0 0 #9c6ead;
        }

        /* --- Page 1: 主页 --- */
        .home-empty {
            display: flex;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            height: 100%;
            padding: 20px;
            /* Added to text content not touch edges */
        }

        #chat-log {
            height: calc(100% - 70px);
            overflow-y: auto;
            margin-bottom: 10px;
            /* Added padding to restore the visual spacing that was removed from #page-home */
            padding: 20px 20px 20px 25px;
            display: flex;
            flex-direction: column;
        }

        .message-bubble {
            padding: 10px 15px;
            margin: 10px 0;
            border-radius: var(--radius-md);
            max-width: 90%;
            word-wrap: break-word;
        }

        .bubble-ai {
            background: var(--accent-blue);
            align-self: flex-start;
            border-radius: 15px 15px 15px 0;
        }

        .bubble-user {
            background: var(--accent-pink);
            align-self: flex-end;
            margin-left: auto;
            border-radius: 15px 15px 0 15px;
            text-align: left;
            /* 多行文本左对齐更自然 */
        }

        /* New Centered Style for AI HTML */
        .bubble-center {
            width: 100% !important;
            max-width: 100% !important;
            background: transparent;
            padding: 0;
            margin: 15px 0;
            text-align: center;
            align-self: center;
            border-radius: 0;
        }

        /* 心形跳动加载动画 */
        .heart-loading {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            gap: 8px;
            padding: 10px;
        }

        .heart-loading .heart-beat {
            width: 20px;
            height: 20px;
            fill: var(--primary-purple);
            animation: heartBounce 0.6s ease-in-out infinite;
        }

        @keyframes heartBounce {

            0%,
            100% {
                transform: scale(1);
                opacity: 0.7;
            }

            50% {
                transform: scale(1.3);
                opacity: 1;
            }
        }

        /* Rich AI Panels */
        .ai-panel-container {
            border: 1px solid var(--primary-purple);
            border-radius: var(--radius-sm);
            overflow: hidden;
            margin-top: 5px;
            box-shadow: var(--shadow-sm);
            background: white;
        }

        .ai-panel-header {
            background: var(--primary-purple-light);
            padding: 8px 12px;
            font-size: 12px;
            font-weight: bold;
            color: var(--primary-purple);
            border-bottom: 1px solid rgba(200, 150, 220, 0.2);
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .ai-panel-body {
            padding: 10px;
            font-size: 13px;
            line-height: 1.5;
            color: var(--text-secondary);
        }

        .status-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 5px;
        }

        .status-item {
            display: flex;
            font-weight: bold;
            color: var(--primary-purple);
            margin-bottom: 5px;
            border-bottom: 1px dashed var(--primary-purple-light);
            padding-bottom: 3px;
        }

        /* New Scene Card Style */
        .scene-card {
            background: linear-gradient(135deg, var(--primary-bg) 0%, var(--tag-bg-purple) 100%);
            border: 1px solid #dcd0ff;
            border-radius: 8px;
            padding: 12px;
            margin-bottom: 15px;
            box-shadow: var(--shadow-sm);
            text-align: left;
            position: relative;
            overflow: hidden;
        }

        .scene-card::before {
            content: "";
            position: absolute;
            top: 0;
            left: 0;
            width: 4px;
            height: 100%;
            background: var(--primary-purple);
        }

        .scene-line {
            display: flex;
            align-items: center;
            margin-bottom: 6px;
            font-size: 13px;
            color: var(--text-secondary);
        }

        .scene-line:last-child {
            margin-bottom: 0;
        }

        .scene-icon {
            margin-right: 8px;
            font-size: 14px;
            width: 20px;
            text-align: center;
        }

        .scene-label {
            font-weight: bold;
            color: var(--primary-purple);
            margin-right: 5px;
        }

        .scroll-container {
            height: 100%;
            overflow-y: auto;
            padding: 15px;
            padding-bottom: calc(30px + env(safe-area-inset-bottom, 15px));
            box-sizing: border-box;
            -webkit-overflow-scrolling: touch;
        }

        .icon-btn:active {
            transform: scale(0.9);
        }

        /* ========================================= */
        /* === ✨ 重构后的输入栏样式 (Input Area) ✨ === */
        /* ========================================= */

        .home-input-area {
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            /* 减少上下内边距，使其更紧凑，移除原先厚重的 padding: 15px */
            padding: 8px 12px;
            padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));

            /* 更轻盈的毛玻璃效果 */
            background: var(--glass-strong);
            backdrop-filter: blur(20px);
            -webkit-backdrop-filter: blur(20px);

            /* 顶部边框改为柔和的线条 */
            border-top: 1px solid var(--glass-transparent);
            /* approx */
            border-top: 1px solid rgba(255, 255, 255, 0.9);
            box-shadow: var(--shadow-float);

            z-index: calc(var(--z-content) + 1);
            display: flex;
            flex-direction: column;
            gap: 0;
            /* 移除 gap，由 choice-drawer 的 margin 控制 */
        }

        /* 选项抽屉 */
        .choice-drawer {
            max-height: 0;
            overflow-y: auto;
            transition: all 0.3s cubic-bezier(0.19, 1, 0.22, 1);
            display: flex;
            gap: 8px;
            flex-wrap: nowrap;
            /* 单行滚动 */
            margin-bottom: 0;
            padding: 0 4px;
            overflow-x: auto;
            /* 允许横向滚动 */
        }

        .choice-drawer.open {
            max-height: 120px;
            margin-bottom: 8px;
            /* 打开时才给下方留白 */
            padding-bottom: 4px;
        }

        .choice-btn {
            background: var(--accent-yellow);
            border: 1px solid rgba(255, 165, 0, 0.2);
            color: #d35400;
            padding: 6px 12px;
            border-radius: var(--radius-lg);
            font-size: 12px;
            white-space: nowrap;
            cursor: pointer;
            box-shadow: var(--shadow-sm);
            flex-shrink: 0;
        }

        /* 输入框容器 - 胶囊风格 */
        .input-row {
            display: flex;
            align-items: center;
            gap: 8px;
            background: var(--card-bg);
            /* 高度固定，保证绝对垂直对称 */
            height: 50px;
            border-radius: var(--radius-full);
            /* 更圆润 */
            padding: 4px 6px 4px 14px;
            /* 右侧 padding 留给按钮 */

            /* 精致的边框和阴影 */
            border: 1px solid rgba(200, 150, 220, 0.3);
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
            transition: all 0.2s ease;
        }

        /* 聚焦态 */
        .input-row:focus-within {
            border-color: var(--primary-purple);
            box-shadow: 0 4px 15px rgba(200, 150, 220, 0.15);
            transform: translateY(-1px);
        }

        /* 实际输入框 */
        .input-row input {
            flex: 1;
            border: none;
            outline: none;
            padding: 0;
            font-size: 15px;
            /* 字体稍大一点，易读 */
            background: transparent;
            color: var(--text-main);
            height: 100%;
            font-family: inherit;
        }

        .input-row input::placeholder {
            color: var(--text-light);
            /* #aaa */
        }

        /* 左侧 Toggle 按钮 */
        .icon-btn-toggle {
            background: none;
            border: none;
            padding: 0;
            cursor: pointer;
            color: var(--text-light);
            display: flex;
            align-items: center;
            justify-content: center;
            transition: color 0.2s;
            margin-right: 2px;
        }

        .icon-btn-toggle:hover,
        .icon-btn-toggle.active {
            color: var(--primary-purple);
        }

        .icon-btn-toggle svg {
            width: 22px;
            height: 22px;
            fill: currentColor;
        }

        /* 右侧发送按钮 - 圆形实色 */
        #btn-send {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            /* 渐变背景 */
            background: linear-gradient(135deg, var(--primary-purple) 0%, #b388cd 100%);
            color: white;
            border: none;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            box-shadow: 0 3px 8px rgba(150, 100, 170, 0.3);
            transition: transform 0.1s, box-shadow 0.1s;
            padding: 0;
            /* 重置 padding */
        }

        #btn-send:active {
            transform: scale(0.92);
            box-shadow: 0 1px 3px rgba(150, 100, 170, 0.3);
        }

        #icon-send {
            margin-left: 2px;
            /* 视觉修正，让纸飞机看起来居中 */
        }




        /* --- Page 2: 我 (Me) --- */
        .me-header {
            background: var(--card-bg);
            padding: var(--spacing-lg);
            border-radius: var(--radius-lg);
            text-align: center;
            box-shadow: var(--shadow-sm);
            margin-bottom: var(--spacing-lg);
        }

        .avatar-circle {
            width: 80px;
            height: 80px;
            border-radius: 50%;
            background: linear-gradient(135deg, #e0c3fc 0%, #8ec5fc 100%);
            margin: 0 auto 10px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 30px;
            color: white;
            border: 4px solid var(--card-bg);
            box-shadow: 0 5px 15px rgba(142, 68, 173, 0.2);
        }

        .stat-bars {
            display: flex;
            flex-direction: column;
            gap: 8px;
            margin-top: 15px;
        }

        .stat-row {
            display: flex;
            align-items: center;
            font-size: 12px;
            color: var(--text-secondary);
        }

        .stat-label {
            width: 40px;
        }

        .stat-track {
            flex: 1;
            height: 8px;
            background: var(--line-color);
            border-radius: 4px;
            overflow: hidden;
            margin-left: 10px;
        }

        .stat-value {
            height: 100%;
            border-radius: 4px;
        }

        .inventory-grid {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 10px;
            padding: 5px;
        }

        .inv-item {
            background: var(--glass-strong);
            border: 1px solid var(--glass-border);
            border-radius: 12px;
            aspect-ratio: 1;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            font-size: 11px;
            cursor: pointer;
            transition: transform 0.2s;
            box-shadow: var(--shadow-sm);
            position: relative;
        }

        .inv-item:active {
            transform: scale(0.95);
        }

        .inv-icon {
            font-size: 20px;
            margin-bottom: 5px;
        }

        /* --- Page 3: 社交 (Social) --- */
        .npc-grid {
            display: grid;
            grid-template-columns: repeat(auto-fill, minmax(90px, 1fr));
            gap: 15px;
            padding: 10px;
        }

        .npc-card {
            background: var(--card-bg);
            border-radius: var(--radius-md);
            padding: 15px 10px;
            display: flex;
            flex-direction: column;
            align-items: center;
            box-shadow: var(--shadow-md);
            transition: transform 0.2s;
            cursor: pointer;
            position: relative;
        }

        .npc-card:active {
            transform: scale(0.96);
        }

        .npc-avatar-img {
            width: 50px;
            height: 50px;
            border-radius: 15px;
            background: #f0f0f0;
            margin-bottom: 8px;
            object-fit: cover;
        }

        .npc-name {
            font-weight: bold;
            font-size: 13px;
            color: #333;
        }

        .npc-status {
            font-size: 10px;
            color: var(--text-muted);
            margin-top: 2px;
        }

        /* --- Page 4: 地图 (3D Map) --- */
        .map-stage {
            height: 100%;
            perspective: 1200px;
            display: flex;
            align-items: center;
            padding: 20px;
            overflow-x: auto;
            overflow-y: hidden;
            /* Hide scrollbar */
            scrollbar-width: none;
        }

        .map-container-inner {
            display: flex;
            gap: 30px;
            padding: 40px;
            /* Space for transforms */
            transform-style: preserve-3d;
        }

        .location-card-3d {
            width: 220px;
            height: 320px;
            background: var(--card-bg);
            border-radius: var(--radius-lg);
            position: relative;
            flex-shrink: 0;
            transform-style: preserve-3d;
            transition: transform 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1), 0 5px 15px rgba(0, 0, 0, 0.05);
            cursor: pointer;
            border: 4px solid var(--card-bg);
            overflow: hidden;
        }

        .location-card-3d:hover {
            transform: translateY(-20px) rotateY(10deg);
            z-index: 10;
        }

        .loc-image {
            width: 100%;
            height: 60%;
            background: #eee;
            position: relative;
        }

        .loc-image img {
            width: 100%;
            height: 100%;
            object-fit: cover;
        }

        .loc-info {
            padding: 15px;
            text-align: center;
            background: linear-gradient(to bottom, var(--card-bg) 0%, var(--primary-bg) 100%);
            height: 40%;
        }

        .loc-title {
            font-weight: bold;
            color: var(--primary-purple);
            margin-bottom: 5px;
            font-size: 16px;
        }

        .loc-desc {
            font-size: 12px;
            color: var(--text-muted);
            line-height: 1.4;
            display: -webkit-box;
            -webkit-line-clamp: 3;
            -webkit-box-orient: vertical;
            overflow: hidden;
        }

        .loc-type-badge {
            position: absolute;
            top: 10px;
            right: 10px;
            background: var(--glass-strong);
            backdrop-filter: blur(4px);
            padding: 3px 8px;
            font-size: 10px;
            border-radius: var(--radius-sm);
            font-weight: bold;
            box-shadow: var(--shadow-sm);
        }

        /* --- Page 5: 设置 --- */
        .settings-subpage {
            margin-bottom: 20px;
        }

        .settings-subpage h3 {
            border-bottom: 2px dashed var(--primary-purple);
            color: var(--primary-purple);
        }

        .form-group {
            margin-bottom: 10px;
        }

        .form-group label {
            display: block;
            font-size: 12px;
            color: var(--text-secondary);
        }

        .form-group input,
        .form-group select {
            width: 100%;
            padding: 8px;
            border: 1px solid var(--border-color);
            border-radius: var(--radius-xs);
        }

        /* Modal */
        .modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: var(--overlay-dark);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: var(--z-modal);
        }

        .modal-content {
            background: var(--card-bg);
            width: 85%;
            border-radius: var(--radius-lg);
            padding: 20px;
            max-height: 80vh;
            overflow-y: auto;
            animation: popUp var(--trans-base);
        }

        /* Settings New Layout */

        /* --- 交互式地图样式 --- */
        #map-viewport {
            width: 100%;
            height: 100%;
            position: relative;
            overflow: hidden;
            background-color: var(--primary-bg);
            background-image: radial-gradient(var(--primary-purple-light) 1px, transparent 1px);
            background-size: 20px 20px;
            cursor: grab;
        }

        #map-viewport:active {
            cursor: grabbing;
        }

        #map-surface {
            width: 1200px;
            height: 1200px;
            position: absolute;
            transform-origin: center;
        }

        .map-node {
            position: absolute;
            width: 80px;
            height: 80px;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
            z-index: 10;
        }

        .map-node:active {
            transform: scale(0.9);
        }

        .node-icon-wrapper {
            width: 50px;
            height: 50px;
            background: var(--card-bg);
            border: 2px dashed var(--primary-purple);
            border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 24px;
            box-shadow: 3px 3px 0 rgba(200, 150, 220, 0.4);
        }

        .map-node.active .node-icon-wrapper {
            background: var(--accent-yellow);
            border-style: solid;
        }

        .node-label {
            margin-top: 5px;
            background: var(--glass-strong);
            padding: 2px 8px;
            border-radius: 4px;
            font-size: 12px;
            font-weight: bold;
            color: var(--primary-purple);
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.05);
            white-space: nowrap;
            border-left: 3px solid var(--accent-blue);
        }

        .node-label.current {
            background: var(--accent-yellow);
        }

        #player-pin {
            position: absolute;
            width: 40px;
            height: 60px;
            z-index: 20;
            pointer-events: none;
            transform: translate(-50%, -100%);
            transition: all 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
            filter: drop-shadow(0 4px 2px rgba(0, 0, 0, 0.2));
        }

        #player-pin svg {
            width: 100%;
            height: 100%;
            fill: #ff6b6b;
        }

        #location-sheet {
            position: absolute;
            bottom: 20px;
            left: 5%;
            width: 90%;
            max-height: 75%;
            background: var(--glass);
            backdrop-filter: blur(15px);
            -webkit-backdrop-filter: blur(15px);
            border-radius: var(--radius-lg);
            border: 2px solid var(--card-bg);
            box-shadow: 0 10px 30px rgba(31, 38, 135, 0.15);
            z-index: 100;
            transform: translateY(120%);
            transition: transform 0.4s cubic-bezier(0.19, 1, 0.22, 1);
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        #location-sheet.open {
            transform: translateY(0);
        }

        .washi-tape {
            height: 12px;
            background: repeating-linear-gradient(45deg, var(--accent-blue), var(--accent-blue) 10px, rgba(255, 255, 255, 0.5) 10px, rgba(255, 255, 255, 0.5) 20px);
            width: 40%;
            margin: -6px auto 10px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            transform: rotate(-1deg);
        }

        .sheet-content {
            padding: 10px 20px 20px;
            overflow-y: auto;
            scrollbar-width: none;
            /* Firefox */
            -ms-overflow-style: none;
            /* IE/Edge */
            padding-bottom: 40px;
            /* Ensure bottom buttons have space */
            -webkit-overflow-scrolling: touch;
        }

        .sheet-content::-webkit-scrollbar {
            display: none;
            /* Chrome, Safari, Opera */
        }

        .sheet-header {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            margin-bottom: 10px;
        }

        .loc-title-lg {
            font-size: 20px;
            font-weight: bold;
            color: var(--primary-purple);
            margin: 0;
        }

        .loc-type-tag {
            font-size: 10px;
            background: var(--accent-yellow);
            padding: 2px 8px;
            border-radius: var(--radius-sm);
            color: #d35400;
            transform: rotate(2deg);
            display: inline-block;
        }

        .loc-desc-text {
            font-size: 13px;
            color: var(--text-secondary);
            line-height: 1.5;
            margin-bottom: 15px;
            background: rgba(255, 255, 255, 0.5);
            padding: 10px;
            border-radius: 8px;
            border: 1px dashed var(--border-color);
        }

        .sheet-section-title {
            font-size: 12px;
            font-weight: bold;
            color: var(--text-muted);
            margin-bottom: 8px;
        }

        .action-row {
            display: flex;
            gap: 10px;
            flex-wrap: wrap;
        }

        .action-btn {
            flex: 1;
            min-width: 80px;
            background: var(--card-bg);
            border: 2px solid var(--primary-purple);
            color: var(--primary-purple);
            padding: 8px;
            border-radius: var(--radius-md);
            font-weight: bold;
            font-size: 13px;
            cursor: pointer;
            box-shadow: 2px 2px 0 var(--primary-purple);
            transition: all 0.1s;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
        }

        .action-btn:active {
            transform: translate(2px, 2px);
            box-shadow: none;
        }

        .action-btn.travel-btn {
            background: var(--primary-purple);
            color: white;
            border-color: var(--primary-purple);
            box-shadow: 2px 2px 0 #8e44ad;
        }

        .close-sheet {
            position: absolute;
            top: 10px;
            right: 15px;
            font-size: 24px;
            color: var(--text-muted);
            cursor: pointer;
            z-index: 101;
        }

        .compass-rose {
            position: absolute;
            top: 20px;
            right: 20px;
            width: 50px;
            height: 50px;
            opacity: 0.8;
            pointer-events: none;
            background: url("data:image/svg+xml,%3Csvg viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M50 0 L60 40 L100 50 L60 60 L50 100 L40 60 L0 50 L40 40 Z' fill='%23c896dc'/%3E%3Ccircle cx='50' cy='50' r='10' fill='white'/%3E%3C/svg%3E");
        }

        .map-legend {
            display: none;
            /* Hidden as requested */
        }

        /* Map Controls (Zoom/Reset) */
        .map-controls {
            position: absolute;
            bottom: 20px;
            right: 20px;
            display: flex;
            flex-direction: column;
            gap: 10px;
            z-index: 50;
        }

        .map-control-btn {
            width: 40px;
            height: 40px;
            border-radius: var(--radius-full);
            background: var(--glass-strong);
            backdrop-filter: blur(5px);
            border: 1px solid white;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
            color: var(--primary-purple);
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s;
        }

        .map-control-btn:active {
            transform: scale(0.95);
            background: white;
        }

        #settings-menu-grid {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 15px;
            padding: 10px;
            width: 100%;
            box-sizing: border-box;
        }

        .settings-card {
            background: var(--card-bg);
            border-radius: var(--radius-md);
            padding: 20px;
            text-align: center;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.05);
            cursor: pointer;
            border: 2px dashed transparent;
            transition: all 0.3s;
        }

        .settings-card:hover {
            transform: translateY(-5px);
            border-color: var(--primary-purple);
        }

        .settings-card svg {
            width: 40px;
            height: 40px;
            fill: var(--primary-purple);
            margin-bottom: 10px;
        }

        .settings-subpage-view {
            display: none;
            height: 100%;
            flex-direction: column;
        }

        .settings-subpage-view.active {
            display: flex;
            width: 100%;
            /* Ensure full width */
            flex-direction: column;
            /* Ensure vertical layout */
            animation: slideInRight 0.3s ease-out;
        }

        .subpage-header {
            display: flex;
            align-items: center;
            margin-bottom: 15px;
            border-bottom: 2px dashed var(--border-color);
            padding-bottom: 5px;
        }

        .subpage-header h3 {
            font-size: 1.2rem;
            margin: 0;
        }

        .back-btn {
            background: none;
            border: none;
            font-size: 20px;
            margin-right: 15px;
            cursor: pointer;
            color: var(--primary-purple);
        }

        /* Enhanced Form Elements */
        .profile-item,
        .music-item,
        .save-slot {
            background: var(--card-bg);
            padding: 10px;
            border-radius: 10px;
            margin-bottom: 10px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border: 1px solid var(--border-color);
        }

        @keyframes slideInRight {
            from {
                transform: translateX(50px);
                opacity: 0;
            }

            to {
                transform: translateX(0);
                opacity: 1;
            }
        }

        @keyframes spin {
            100% {
                transform: rotate(360deg);
            }
        }

        @keyframes popUp {
            from {
                transform: scale(0.8);
                opacity: 0;
            }

            to {
                transform: scale(1);
                opacity: 1;
            }
        }

        /* 隐藏的文件输入 */
        #file-input,
        #music-file-input {
            display: none;
        }

        /* Settings Page Refactor */


        .settings-menu-item {
            background: var(--card-bg);
            border: 1px solid var(--border-color);
            border-radius: 12px;
            padding: 20px;
            display: flex;
            align-items: center;
            gap: 15px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.02);
            cursor: pointer;
            transition: transform 0.2s;
        }

        .settings-menu-item:active {
            transform: scale(0.98);
        }

        .settings-icon-box {
            width: 48px;
            height: 48px;
            background: var(--primary-purple-light);
            /* derived from primary purple */
            border-radius: 12px;
            display: flex;
            align-items: center;
            justify-content: center;
            color: var(--primary-purple);
        }

        .settings-menu-text {
            flex: 1;
            font-weight: bold;
            font-size: 1.1rem;
            color: var(--text-color);
        }

        .settings-subpage {
            display: none;
            width: 100%;
            height: 100%;
            flex-direction: column;
        }

        .settings-subpage.active {
            display: flex;
        }

        .api-ext-card {
            background: var(--card-bg);
            border: 1px solid var(--border-color);
            border-radius: 16px;
            padding: 15px;
            margin-bottom: 20px;
            box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
        }

        .form-group {
            margin-bottom: 15px;
        }

        .form-label {
            display: block;
            margin-bottom: 5px;
            font-weight: bold;
            color: var(--text-secondary);
        }

        .form-input {
            width: 100%;
            padding: 10px;
            border: 1px solid var(--border-color);
            border-radius: 8px;
            font-family: inherit;
            background: rgba(0, 0, 0, 0.02);
            box-sizing: border-box;
        }

        .form-input:focus {
            border-color: var(--primary-purple);
            outline: none;
            background: #fff;
        }

        /* Fix for Settings Inputs Layout */
        #settings-content input[type="text"],
        #settings-content input[type="password"],
        #settings-content input[type="color"],
        #settings-content select {
            width: 100%;
            box-sizing: border-box;
            /* Critical for padding */
        }

        /* --- 自定义通知 (Toast) --- */
        #toast-container {
            position: fixed;
            top: 85px;
            /* 避开 70px 的音乐播放器 */
            left: 50%;
            transform: translateX(-50%);
            z-index: 9999;
            pointer-events: none;
            display: flex;
            flex-direction: column;
            align-items: center;
            gap: 10px;
            width: 90%;
        }

        .toast-pill {
            background: var(--primary-purple);
            color: white;
            padding: 10px 25px;
            border-radius: 50px;
            /* 半圆两头 */
            font-size: 14px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
            backdrop-filter: blur(5px);
            animation: toastIn var(--trans-base) var(--ease-in-out) forwards;
            pointer-events: auto;
            max-width: 100%;
            text-align: center;
        }

        /* Bubble Context Menu (Journal Style) */
        .bubble-menu {
            position: absolute;
            background: #fffdf9;
            /* Warm paper white */
            border: 2px dashed var(--primary-purple);
            /* Dashed border like cut paper or tape */
            border-radius: 255px 15px 225px 15px / 15px 225px 15px 255px;
            /* Hand-drawn shape */
            padding: 8px 0;
            box-shadow: 0 8px 24px rgba(100, 80, 110, 0.2);
            z-index: 10000;
            display: none;
            flex-direction: column;
            min-width: 150px;
            animation: popIn 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            pointer-events: auto;

            /* Paper texture */
            background-image:
                radial-gradient(var(--texture-dot) 1px, transparent 1px);
            background-size: 10px 10px;
        }

        /* Location Sheet Tape - Centered */
        .washi-tape {
            position: absolute;
            top: -15px;
            left: 50%;
            /* Transform is handled by JS randomizer, but we need base centering */
            width: 120px;
            height: 35px;
            z-index: 5;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        .bubble-menu-item {
            padding: 10px 16px;
            font-size: 15px;
            font-family: var(--font-main);
            /* Hand-drawn font */
            color: var(--text-main);
            cursor: pointer;
            display: flex;
            align-items: center;
            gap: 12px;
            white-space: nowrap;
            user-select: none;
            justify-content: flex-start;
            margin: 2px 6px;
            border-radius: 4px;
        }

        .bubble-menu-item:hover {
            background: rgba(200, 150, 220, 0.15);
            /* Light purple highlight */
            transform: scale(1.02);
            /* Slight pop */
        }

        .bubble-menu-item.delete {
            color: #e74c3c;
        }

        .bubble-menu-item.delete:hover {
            background: rgba(231, 76, 60, 0.1);
        }

        @keyframes popIn {
            from {
                opacity: 0;
                transform: scale(0.8);
            }

            to {
                opacity: 1;
                transform: scale(1);
            }
        }


        @keyframes toastIn {
            from {
                transform: translateY(-50px);
                opacity: 0;
            }

            to {
                transform: translateY(0);
                opacity: 1;
            }
        }

        .toast-out {
            animation: toastOut 0.3s ease-in forwards !important;
        }

        @keyframes toastOut {
            to {
                transform: translateY(-20px);
                opacity: 0;
            }
        }

        /* --- 自定义弹窗 (Modal) --- */
        #custom-modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.4);
            backdrop-filter: blur(4px);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: var(--z-modal);
            padding: 20px;
        }

        .modal-box {
            background: var(--primary-bg);
            border: 2px solid var(--primary-purple);
            border-radius: var(--radius-lg);
            width: 100%;
            max-width: 320px;
            padding: 20px;
            box-shadow: var(--shadow);
            animation: popUp var(--trans-base) var(--ease-in-out);
            /* 再加一点手账装饰线 */
            background-image: linear-gradient(var(--primary-purple-light) 1px, transparent 1px);
            background-size: 100% 25px;
        }

        .modal-title {
            font-weight: bold;
            color: var(--primary-purple);
            margin-bottom: 10px;
            font-size: 1.1rem;
            text-align: center;
        }

        .modal-body {
            margin-bottom: 20px;
            color: var(--text-secondary);
            line-height: 1.5;
            text-align: center;
        }

        .modal-footer {
            display: flex;
            gap: 10px;
            justify-content: center;
        }

        .modal-footer .btn {
            flex: 1;
            padding: 8px;
            font-size: 14px;
        }

        /* --- Music Player Enhancements --- */
        #music-header {
            position: relative;
            overflow: hidden;
            /* For background image */
        }

        #music-bg-layer {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            z-index: -1;
            background-size: cover;
            background-position: center;
            opacity: 1;
            transition: filter 0.3s;
        }

        /* Gradient Overlay for smoothness */
        #music-bg-layer::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: linear-gradient(to bottom, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0.6) 80%, rgba(255, 255, 255, 0.9) 100%);
            pointer-events: none;
        }

        /* Toggle Switch */
        .toggle-switch {
            position: relative;
            width: 40px;
            height: 20px;
            -webkit-appearance: none;
            appearance: none;
            background: #ccc;
            outline: none;
            border-radius: 10px;
            box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
            transition: .5s;
            cursor: pointer;
        }

        .toggle-switch:checked {
            background: var(--primary-purple);
        }

        .toggle-switch::before {
            content: '';
            position: absolute;
            width: 20px;
            height: 20px;
            border-radius: 50%;
            top: 0;
            left: 0;
            background: white;
            transform: scale(1.1);
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
            transition: .5s;
        }

        .toggle-switch:checked::before {
            left: 20px;
        }

        /* Cropper Modal */
        #cropper-modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            z-index: 20000;
            display: none;
            flex-direction: column;
            justify-content: center;
            align-items: center;
        }

        #cropper-canvas-container {
            max-width: 90%;
            max-height: 70%;
            border: 2px solid white;
            overflow: hidden;
            position: relative;
        }

        #cropper-controls {
            margin-top: 20px;
            display: flex;
            gap: 20px;
        }

        /* Playlist Item Styling */
        .playlist-item {
            background: rgba(255, 255, 255, 0.8);
            border-radius: 8px;
            padding: 8px;
            margin-bottom: 8px;
            display: flex;
            align-items: center;
            gap: 10px;
            border: 1px solid #eee;
            transition: all 0.2s;
        }

        .playlist-item.disabled {
            opacity: 0.6;
            background: #f0f0f0;
        }

        .playlist-item.playing {
            border-color: var(--primary-purple);
            background: rgba(200, 150, 220, 0.1);
        }

        #loading-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: var(--primary-bg);
            z-index: 99999;
            display: flex;
            justify-content: center;
            align-items: center;
            flex-direction: column;
            transition: opacity 0.5s;
        }

        .loading-spinner {
            width: 40px;
            height: 40px;
            border: 4px solid #f3f3f3;
            border-top: 4px solid var(--primary-purple);
            border-radius: 50%;
            animation: spin 1s linear infinite;
            margin-bottom: 20px;
        }

        .progress-container {
            display: flex;
            align-items: center;
            gap: 10px;
            width: 100%;
            margin-top: 5px;
        }

        .progress-bar {
            flex: 1;
            height: 4px;
            background: rgba(255, 255, 255, 0.3);
            border-radius: 2px;
            position: relative;
            cursor: pointer;
        }

        .progress-fill {
            width: 0%;
            height: 100%;
            background: var(--primary-purple);
            border-radius: 2px;
            position: relative;
        }

        .progress-dot {
            position: absolute;
            right: -6px;
            top: 50%;
            transform: translateY(-50%);
            width: 12px;
            height: 12px;
            background: white;
            border-radius: 50%;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
            opacity: 0;
            transition: opacity 0.2s;
        }

        .progress-bar:hover .progress-dot {
            opacity: 1;
        }

        .time-label {
            font-size: 10px;
            color: #666;
            min-width: 35px;
            text-shadow: 0 1px 2px rgba(255, 255, 255, 0.8);
        }

        /* ========== ME PAGE STYLES ========== */
        .me-section-card {
            background: var(--card-bg, rgba(255, 255, 255, 0.9));
            border-radius: 16px;
            padding: 20px;
            margin-bottom: 20px;
            box-shadow: 0 4px 15px rgba(31, 38, 135, 0.08);
            border: 2px solid white;
            position: relative;
            overflow: hidden;
        }

        .me-washi-tape {
            position: absolute;
            top: -10px;
            left: 50%;
            transform: translateX(-50%) rotate(-2deg);
            width: 120px;
            height: 24px;
            background: repeating-linear-gradient(45deg,
                    rgba(200, 150, 220, 0.3),
                    rgba(200, 150, 220, 0.3) 10px,
                    rgba(255, 255, 255, 0.5) 10px,
                    rgba(255, 255, 255, 0.5) 20px);
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
            z-index: 1;
        }

        .me-section-title {
            font-size: 14px;
            font-weight: bold;
            color: var(--primary-purple);
            margin-bottom: 12px;
            display: flex;
            align-items: center;
            gap: 5px;
            border-bottom: 2px dashed #eee;
            padding-bottom: 5px;
        }

        /* Profile Header */
        .me-profile-header {
            text-align: center;
            background: linear-gradient(180deg, #ffffff 0%, #fbf5ff 100%);
        }

        .me-avatar-container {
            position: relative;
            width: 90px;
            height: 90px;
            margin: 0 auto 10px;
        }

        .me-avatar-img {
            width: 100%;
            height: 100%;
            border-radius: 50%;
            object-fit: cover;
            border: 4px solid white;
            box-shadow: 0 5px 15px rgba(200, 150, 220, 0.3);
            background: #eee;
        }

        .me-avatar-fallback {
            width: 100%;
            height: 100%;
            border-radius: 50%;
            background: var(--primary-purple);
            color: white;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 32px;
            font-weight: bold;
            border: 4px solid white;
            box-shadow: 0 5px 15px rgba(200, 150, 220, 0.3);
        }

        .me-level-badge {
            position: absolute;
            bottom: 0;
            right: 0;
            background: var(--accent-yellow);
            color: #d35400;
            font-size: 10px;
            padding: 2px 6px;
            border-radius: 10px;
            font-weight: bold;
            border: 1px solid white;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        .me-char-name {
            font-size: 20px;
            font-weight: 800;
            color: var(--text-dark, #4a4a4a);
            margin: 5px 0 2px;
        }

        .me-char-alias {
            font-size: 12px;
            color: var(--primary-purple);
            font-weight: bold;
            background: rgba(200, 150, 220, 0.1);
            padding: 2px 8px;
            border-radius: 4px;
            display: inline-block;
            margin-bottom: 8px;
        }

        .me-quote-box {
            font-size: 12px;
            color: #777;
            font-style: italic;
            margin: 10px 0;
            padding: 8px;
            background: rgba(0, 0, 0, 0.02);
            border-radius: 8px;
            font-family: 'KaiTi', serif;
        }

        /* Vitals (HP/Sanity) */
        .me-vitals-row {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 10px;
            margin-top: 15px;
        }

        .me-vital-item {
            flex: 1;
            background: white;
            padding: 8px;
            border-radius: 10px;
            border: 1px solid #eee;
        }

        .me-vital-label {
            font-size: 10px;
            color: #888;
            display: flex;
            justify-content: space-between;
            margin-bottom: 4px;
        }

        .me-vital-track {
            height: 6px;
            background: #eee;
            border-radius: 3px;
            overflow: hidden;
        }

        .me-vital-fill {
            height: 100%;
            border-radius: 3px;
            transition: width 0.5s ease;
        }

        .me-fill-hp {
            background: linear-gradient(90deg, #ff9a9e 0%, #fad0c4 99%);
        }

        .me-fill-san {
            background: linear-gradient(90deg, #a18cd1 0%, #fbc2eb 100%);
        }

        /* Info Grid */
        .me-info-grid {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 10px;
            font-size: 12px;
        }

        .me-info-cell {
            background: rgba(255, 255, 255, 0.6);
            padding: 8px;
            border-radius: 8px;
            border: 1px dashed #ddd;
        }

        .me-info-key {
            color: var(--primary-purple);
            font-weight: bold;
            display: block;
            margin-bottom: 2px;
            font-size: 11px;
        }

        .me-info-value {
            color: #555;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        /* Traits */
        .me-tags-container {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            margin-bottom: 10px;
        }

        .me-trait-tag {
            font-size: 11px;
            padding: 4px 10px;
            border-radius: 15px;
            font-weight: bold;
            transform: rotate(-1deg);
        }

        .me-trait-tag:nth-child(even) {
            transform: rotate(1deg);
        }

        .me-tag-red {
            background: var(--accent-pink);
            color: #c0392b;
        }

        .me-tag-yellow {
            background: var(--accent-yellow);
            color: #d35400;
        }

        .me-tag-blue {
            background: var(--accent-blue);
            color: #2980b9;
        }

        .me-tag-purple {
            background: #e0c3fc;
            color: #8e44ad;
        }

        .me-desc-text {
            font-size: 13px;
            line-height: 1.6;
            color: #666;
            background: url("data:image/svg+xml,%3Csvg width='100%25' height='20' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M0,20 L100%25,20' stroke='%23eeeeee' stroke-width='1' stroke-dasharray='4,4'/%3E%3C/svg%3E");
            background-size: 100% 24px;
            padding-bottom: 4px;
        }

        /* Skills */
        .me-skill-list {
            display: flex;
            flex-direction: column;
            gap: 8px;
        }

        .me-skill-row {
            display: flex;
            flex-direction: column;
            align-items: flex-start;
            background: white;
            padding: 10px 12px;
            border-radius: 10px;
            border: 1px solid #f0f0f0;
            gap: 4px;
        }

        .me-skill-info {
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .me-skill-name {
            font-size: 13px;
            font-weight: bold;
            color: #555;
        }

        .me-skill-rank {
            font-size: 10px;
            padding: 2px 6px;
            border-radius: 4px;
            font-weight: 800;
        }

        .me-rank-s {
            color: #f39c12;
            background: #fcf3cf;
            border: 1px solid #f39c12;
        }

        .me-rank-a {
            color: #e74c3c;
            background: #fadbd8;
            border: 1px solid #e74c3c;
        }

        .me-rank-b {
            color: #3498db;
            background: #d6eaf8;
            border: 1px solid #3498db;
        }

        /* Skill EXP Bar */
        .me-skill-exp-container {
            width: 100%;
            margin-top: 6px;
        }

        .me-skill-exp-label {
            display: flex;
            justify-content: space-between;
            font-size: 9px;
            color: #aaa;
            margin-bottom: 2px;
            font-weight: bold;
        }

        .me-skill-exp-track {
            height: 4px;
            background: #f0f0f0;
            border-radius: 2px;
            overflow: hidden;
            width: 100%;
        }

        .me-skill-exp-fill {
            height: 100%;
            background: linear-gradient(90deg, #a18cd1 0%, #dcd0ff 100%);
            border-radius: 2px;
            transition: width 0.6s cubic-bezier(0.34, 1.56, 0.64, 1);
        }

        /* Relationships */
        .me-rel-list {
            display: flex;
            flex-direction: column;
            gap: 10px;
        }

        .me-rel-item {
            background: linear-gradient(to right, rgba(255, 255, 255, 0.9), rgba(255, 255, 255, 0.5));
            border-left: 3px solid var(--primary-purple);
            padding: 8px 12px;
            border-radius: 0 10px 10px 0;
        }

        .me-rel-header {
            display: flex;
            justify-content: space-between;
            font-size: 13px;
            margin-bottom: 4px;
        }

        .me-rel-name {
            font-weight: bold;
        }

        .me-rel-val {
            color: var(--primary-purple);
            font-weight: bold;
        }

        .me-rel-bar-bg {
            height: 6px;
            background: #eee;
            border-radius: 3px;
            overflow: hidden;
        }

        .me-rel-bar-fill {
            height: 100%;
            background: var(--primary-purple);
            border-radius: 3px;
        }

        .me-rel-desc {
            font-size: 10px;
            color: #999;
            margin-top: 4px;
        }

        /* --- 物品详情弹窗 --- */
        #item-detail-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.4);
            backdrop-filter: blur(4px);
            display: none;
            justify-content: center;
            align-items: center;
            z-index: 11000;
        }

        .item-detail-box {
            background: #fff;
            width: 85%;
            max-width: 300px;
            border-radius: 20px;
            padding: 20px;
            position: relative;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            border: 2px solid var(--primary-purple);
            background-image: radial-gradient(var(--primary-purple) 0.5px, transparent 0.5px);
            background-size: 20px 20px;
            background-color: #fdf6ff;
        }

        .item-detail-icon {
            font-size: 40px;
            text-align: center;
            margin-bottom: 10px;
        }

        .item-detail-name {
            font-size: 18px;
            font-weight: bold;
            color: var(--primary-purple);
            text-align: center;
            margin-bottom: 10px;
        }

        .item-detail-desc {
            font-size: 14px;
            color: #666;
            line-height: 1.6;
            margin-bottom: 20px;
            background: rgba(255, 255, 255, 0.7);
            padding: 10px;
            border-radius: 10px;
            border: 1px dashed #ddd;
        }

        .item-detail-footer {
            display: flex;
            gap: 10px;
        }

        .item-detail-footer .btn {
            flex: 1;
            padding: 10px;
        }

        /* ========================================= */
        /* === ✨ 新版社交页样式 (Social Page) ✨ === */
        /* ========================================= */

        /* 拍立得照片墙布局 */
        /* 拍立得照片墙布局 (Grid Version) */
        .social-masonry {
            display: grid;
            grid-template-columns: repeat(2, 1fr);
            gap: 15px;
            padding-bottom: 20px;
        }

        .npc-polaroid {
            /* 移除旧的 column 属性和 GPU hack */
            background: #fff;
            padding: 10px 10px 30px 10px;
            /* 底部留白像拍立得 */

            border-radius: 4px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.08);
            position: relative;
            transition: transform 0.2s, box-shadow 0.2s;
            cursor: pointer;
            border: 1px solid #eee;
            height: 100%;
            /* 确保高度填充 */
        }

        /* 旋转效果 - Grid下无需特殊 hack */
        .npc-polaroid:nth-child(odd) {
            transform: rotate(-2deg);
        }

        .npc-polaroid:nth-child(even) {
            transform: rotate(2deg);
        }

        .npc-polaroid:active {
            transform: scale(0.96) !important;
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
        }

        /* 顶部装饰胶带 */
        .npc-tape {
            position: absolute;
            top: -12px;
            left: 50%;
            transform: translateX(-50%) rotate(1deg);
            width: 80px;
            height: 24px;
            opacity: 1;
            /* Use alpha in background for better control */
            z-index: 10;
            border: none !important;
            box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15);
            pointer-events: none;
            /* Don't interfere with clicks */
        }

        /* --- Utility Classes (For JS Dynamic Content) --- */
        .text-center {
            text-align: center;
        }

        .text-right {
            text-align: right;
        }

        .text-muted {
            color: var(--text-muted);
        }

        .text-secondary {
            color: var(--text-secondary);
        }

        .text-warning {
            color: var(--rank-text-s);
        }

        /* Gold/Orange */
        .text-danger {
            color: var(--rank-text-a);
        }

        /* Red */

        .mt-2 {
            margin-top: 2px;
        }

        .mt-4 {
            margin-top: 4px;
        }

        .mt-5 {
            margin-top: 5px;
        }

        .mt-10 {
            margin-top: 10px;
        }

        .mt-15 {
            margin-top: 15px;
        }

        .mb-0 {
            margin-bottom: 0;
        }

        .ml-2 {
            margin-left: 2px;
        }

        .font-10 {
            font-size: 10px;
        }

        .font-12 {
            font-size: 12px;
        }

        .font-bold {
            font-weight: bold;
        }

        .flex-center-between {
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .flex-center {
            display: flex;
            justify-content: center;
            align-items: center;
        }

        .flex-col {
            display: flex;
            flex-direction: column;
        }

        .gap-10 {
            gap: 10px;
        }

        .gap-2 {
            gap: 2px;
        }

        .avatar-circle {
            width: 40px;
            height: 40px;
            border-radius: 50%;
            object-fit: cover;
        }

        .cursor-pointer {
            cursor: pointer;
        }

        .w-full {
            width: 100%;
        }

        .absolute-center {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }

        .no-bg {
            background: transparent;
        }

        .no-border {
            border: none;
        }

        .flex-row {
            display: flex;
        }

        .align-center {
            align-items: center;
        }

        .col-span-all {
            grid-column: 1 / -1;
        }

        .npc-polaroid:nth-child(even) .npc-tape {
            /* Handled by JS tapeStyle */
        }

        .npc-img-box {
            width: 100%;
            aspect-ratio: 1;
            /* 正方形 */
            background: #f0f0f0;
            overflow: hidden;
            border: 1px solid #ddd;
            margin-bottom: 10px;
            position: relative;
        }

        .npc-img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            transition: transform 0.5s;
        }

        .npc-polaroid:hover .npc-img {
            transform: scale(1.1);
        }

        .npc-info-row {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 0 5px;
        }

        .npc-name-hand {
            font-family: 'Comic Sans MS', cursive;
            font-weight: bold;
            font-size: 14px;
            color: #333;
        }

        .npc-status-dot {
            width: 8px;
            height: 8px;
            border-radius: 50%;
            background: #ccc;
        }

        .npc-status-dot.online {
            background: #2ecc71;
            box-shadow: 0 0 5px #2ecc71;
        }

        .npc-status-dot.busy {
            background: #f39c12;
        }

        .npc-status-dot.offline {
            background: #95a5a6;
        }

        /* 好感度小心心条 */
        .npc-affinity-mini {
            height: 4px;
            background: #eee;
            border-radius: 2px;
            margin: 8px 5px 0 5px;
            overflow: hidden;
            position: relative;
        }

        .npc-affinity-fill {
            height: 100%;
            background: var(--primary-purple);
            width: 0%;
        }

        .npc-heart-icon {
            position: absolute;
            right: 5px;
            bottom: 30px;
            font-size: 14px;
            color: #e74c3c;
            filter: drop-shadow(0 2px 2px rgba(0, 0, 0, 0.1));
        }

        /* --- Optimized Choice Drawer (Vertical) --- */
        /* --- Optimized Choice Drawer (Vertical) --- */
        #choice-drawer {
            display: none;
            /* Hidden by default */
            flex-direction: column;
            gap: 12px;
            padding: 10px 5px;
            width: 100%;
            overflow-x: hidden;
            max-height: 300px;
            overflow-y: auto;
        }

        #choice-drawer.open {
            display: flex;
            animation: drawerSlideDown 0.3s ease-out;
        }

        @keyframes drawerSlideDown {
            from {
                opacity: 0;
                transform: translateY(-10px);
            }

            to {
                opacity: 1;
                transform: translateY(0);
            }
        }

        .choice-btn {
            width: 100%;
            padding: 12px 20px;
            background: var(--card-bg);
            border: 1px solid var(--primary-purple);
            color: var(--primary-purple);
            border-radius: 12px;
            font-size: 14px;
            font-weight: bold;
            text-align: center;
            cursor: pointer;
            box-shadow: 0 4px 10px rgba(200, 150, 220, 0.15);
            transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);
            position: relative;
            overflow: hidden;
        }

        .choice-btn:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 15px rgba(200, 150, 220, 0.25);
            background: linear-gradient(to right, var(--card-bg), #fbf5ff);
        }

        .choice-btn:active {
            transform: scale(0.98);
            box-shadow: 0 2px 5px rgba(200, 150, 220, 0.1);
        }

        /* --- Splash Screen CSS --- */
        #splash-container {
            position: fixed;
            top: 0;
            left: 0;
            width: 100vw;
            height: 100vh;
            height: 100dvh;
            z-index: 9999;
            /* Remove background to allow Game UI to be scene behind */
            background: transparent;
            display: flex;
            justify-content: center;
            align-items: center;
            font-family: var(--font-hand, 'Comic Sans MS', cursive);
            overflow: hidden;
            cursor: pointer;
            transition: opacity 0.5s ease;
        }

        /* 3D 容器 */
        .scene {
            perspective: 1500px;
            width: 100%;
            height: 100%;
            position: relative;
        }

        /* 封面整体 */
        .journal-cover {
            width: 100%;
            height: 100%;
            background: linear-gradient(135deg, var(--primary-purple) 0%, color-mix(in srgb, var(--primary-purple), black 10%) 100%);
            border-radius: 0;
            position: relative;
            box-shadow:
                -2px 0 5px rgba(0, 0, 0, 0.1),
                /* 书脊阴影 */
                15px 20px 40px rgba(0, 0, 0, 0.25),
                /* 整体投影 */
                inset 2px 2px 5px rgba(255, 255, 255, 0.3),
                /* 高光 */
                inset -2px -2px 10px rgba(0, 0, 0, 0.1);
            /* 内阴影 */
            display: flex;
            flex-direction: column;
            align-items: center;
            transform-style: preserve-3d;
            transform-origin: left center;
            transition: transform 1s cubic-bezier(0.645, 0.045, 0.355, 1);

            /* 皮革纹理模拟 */
            background-image: url("data:image/svg+xml,%3Csvg width='100' height='100' viewBox='0 0 100 100' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.08'/%3E%3C/svg%3E"),
                linear-gradient(135deg, color-mix(in srgb, var(--primary-purple), white 10%) 0%, var(--primary-purple) 100%);
        }

        /* 翻页动画类 */
        .journal-cover.open {
            transform: rotateY(-110deg);
            pointer-events: none;
        }

        /* 书脊 */
        .spine {
            position: absolute;
            left: 0;
            top: 0;
            bottom: 0;
            width: 25px;
            background: linear-gradient(to right, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.05) 40%, rgba(255, 255, 255, 0.1) 50%, rgba(0, 0, 0, 0.1));
            border-right: 1px solid rgba(0, 0, 0, 0.1);
            border-radius: 12px 0 0 12px;
            z-index: 2;
        }

        /* 缝线装饰 */
        .stitch-line {
            position: absolute;
            top: 15px;
            bottom: 15px;
            left: 35px;
            right: 15px;
            border: 2px dashed rgba(255, 255, 255, 0.6);
            border-radius: 8px 15px 15px 8px;
            pointer-events: none;
        }

        /* 标题卡片区域 */
        .title-card {
            margin-top: 25vh;
            width: 70%;
            padding: 30px 20px;
            background-color: var(--primary-bg);
            border-radius: 4px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
            position: relative;
            text-align: center;
            transform: rotate(-2deg);
            z-index: 10;
            /* 纸张纹理 */
            background-image: radial-gradient(#e0c3fc 0.5px, transparent 0.5px);
            background-size: 10px 10px;
        }

        /* 标题文字 */
        .main-title {
            font-size: 32px;
            color: color-mix(in srgb, var(--primary-purple), black 20%);
            font-weight: 900;
            margin: 0;
            letter-spacing: 2px;
            text-shadow: 2px 2px 0px color-mix(in srgb, var(--primary-purple), transparent 70%);
        }

        .sub-title {
            font-size: 14px;
            color: #888;
            margin-top: 5px;
            font-family: serif;
        }

        /* 和纸胶带装饰 */
        .washi-tape {
            position: absolute;
            width: 80px;
            height: 25px;
            background-color: rgba(255, 235, 59, 0.7);
            top: -10px;
            left: 50%;
            transform: translateX(-50%) rotate(2deg);
            box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
            background-image: repeating-linear-gradient(45deg, transparent, transparent 10px, rgba(255, 255, 255, 0.5) 10px, rgba(255, 255, 255, 0.5) 20px);
            opacity: 0.9;
        }

        .washi-tape.bottom {
            top: auto;
            bottom: -10px;
            left: auto;
            right: 20px;
            width: 60px;
            background-color: rgba(135, 206, 235, 0.7);
            transform: rotate(-5deg);
        }

        /* 装饰贴纸 */
        .sticker {
            position: absolute;
            font-size: 40px;
            filter: drop-shadow(0 4px 2px rgba(0, 0, 0, 0.1));
            animation: float 3s ease-in-out infinite;
        }

        .sticker-moon {
            top: 40px;
            right: 15%;
            /* Moved left from 30px */
            animation-delay: 0s;
            transform: rotate(15deg);
        }

        .sticker-star {
            bottom: 160px;
            left: 15%;
            /* Moved right from 30px */
            font-size: 30px;
            animation-delay: 1.5s;
        }

        @keyframes float {

            0%,
            100% {
                transform: translateY(0) rotate(15deg);
            }

            50% {
                transform: translateY(-10px) rotate(10deg);
            }
        }

        /* 邮戳样式 */
        .stamp {
            position: absolute;
            mix-blend-mode: multiply;
            opacity: 0.85;
            font-family: 'Courier New', Courier, monospace;
            font-weight: bold;
            pointer-events: none;
        }

        .stamp-circle {
            width: 90px;
            height: 90px;
            border: 3px double color-mix(in srgb, var(--primary-purple), black 40%);
            border-radius: 50%;
            color: color-mix(in srgb, var(--primary-purple), black 40%);
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
            font-size: 11px;
            line-height: 1.3;
            transform: rotate(-15deg);
            bottom: 80px;
            right: 80px;
            background: rgba(93, 58, 104, 0.02);
        }

        .stamp-circle::before {
            content: '';
            position: absolute;
            width: 88%;
            height: 88%;
            border: 1px dashed color-mix(in srgb, var(--primary-purple), black 40%);
            border-radius: 50%;
        }

        /* 遮罩层 */
        #transition-overlay {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: var(--primary-bg);
            opacity: 0;
            pointer-events: none;
            transition: opacity 0.8s ease;
            z-index: 999;
        }

        .tap-hint {
            position: absolute;
            bottom: 30px;
            width: 100%;
            text-align: center;
            font-size: 15px;
            color: rgba(255, 255, 255, 0.9);
            text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
            animation: pulse 2s infinite;
            letter-spacing: 1px;
        }

        @keyframes pulse {
            0% {
                opacity: 0.5;
                transform: scale(1);
            }

            50% {
                opacity: 1;
                transform: scale(1.05);
            }

            100% {
                opacity: 0.5;
                transform: scale(1);
            }
        }

        /* 绑带样式 */
        .elastic-band {
            position: absolute;
            top: 0;
            bottom: 0;
            right: 25px;
            width: 18px;
            background: linear-gradient(90deg, color-mix(in srgb, var(--primary-purple), black 60%) 0%, color-mix(in srgb, var(--primary-purple), black 40%) 40%, color-mix(in srgb, var(--primary-purple), black 40%) 60%, color-mix(in srgb, var(--primary-purple), black 60%) 100%);
            box-shadow: -1px 0 2px rgba(0, 0, 0, 0.5), 1px 0 2px rgba(255, 255, 255, 0.1), 3px 5px 5px rgba(0, 0, 0, 0.3);
            z-index: 20;
            border-radius: 2px;
            transition: transform 0.4s ease-in, opacity 0.3s ease-in;
        }

        .elastic-band.remove {
            transform: scaleX(1.1) translateX(60px);
            opacity: 0;
        }
    


            /* Scene Card (Header) styles updated to sit inside Story Card */
            .story-card {
                background: rgba(255, 255, 255, 0.9);
                border-radius: 12px;
                box-shadow: 0 4px 15px rgba(0, 0, 0, 0.05);
                padding: 0;
                overflow: hidden;
                border: 1px solid rgba(255, 255, 255, 0.6);
                margin: 10px 0;
                width: 100%;
            }

            /* Scene Card (Header) styles updated to sit inside Story Card */
            .scene-card {
                background: linear-gradient(135deg, #fdfbf7 0%, #f3e7ff 100%);
                border-bottom: 1px solid #dcd0ff;
                padding: 12px;
                box-shadow: none;
                /* Shadow handled by parent story-card */
                border-radius: 12px 12px 0 0;
                /* Top corners only */
                margin-bottom: 0;
                border-top: none;
                border-left: none;
                border-right: none;
            }

            .story-content {
                padding: 15px;
                line-height: 1.8;
                color: #333;
                text-align: left;
                font-size: 15px;
            }

            @keyframes pulse {
                0% {
                    transform: scale(1);
                }

                50% {
                    transform: scale(0.95);
                }

                100% {
                    transform: scale(1);
                }
            }

            /* --- ✨ New Chat Window Styles (from NPC layout) ✨ --- */
            .chat-modal-overlay {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.5);
                backdrop-filter: blur(3px);
                z-index: 2000;
                display: none;
                justify-content: center;
                align-items: flex-end;
                /* Bottom alignment */
            }

            .chat-window {
                width: 100%;
                max-width: 100%;
                /* Full width */
                height: 92%;
                /* Taller, covering most of the screen */
                background: #fff;
                border-radius: 20px 20px 0 0;
                /* Top rounded only */
                box-shadow: 0 -10px 40px rgba(0, 0, 0, 0.2);
                display: flex;
                flex-direction: column;
                overflow: hidden;
                border: 1px solid rgba(255, 255, 255, 0.8);
                border-bottom: none;
                /* Animation */
                animation: chatSlideUp 0.3s ease-out;
                /* Smooth slide up, no bounce */
                position: relative;
            }

            @keyframes chatSlideUp {
                from {
                    transform: translateY(100%);
                }

                to {
                    transform: translateY(0);
                }
            }

            .chat-header {
                padding: 15px 20px;
                /* Light Gradient Background from NPC layout */
                background: linear-gradient(to right, #fdfbf7, #fdf6ff);
                border-bottom: 1px solid #eee;
                display: flex;
                align-items: center;
                gap: 10px;
                z-index: 10;
                color: #333;
                /* Text dark */
            }

            .chat-npc-info {
                flex: 1;
                display: flex;
                align-items: center;
                gap: 10px;
            }

            .chat-avatar-small {
                width: 40px;
                height: 40px;
                border-radius: 50%;
                border: 2px solid white;
                box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
                object-fit: cover;
            }

            .chat-npc-name {
                font-weight: bold;
                font-size: 16px;
                color: var(--text-dark);
                /* Dark text */
            }

            .chat-close-btn {
                background: transparent;
                /* No background */
                border: none;
                width: 30px;
                height: 30px;
                font-size: 24px;
                color: #999;
                /* Dark Gray X */
                cursor: pointer;
                display: flex;
                align-items: center;
                justify-content: center;
                transition: transform 0.2s;
            }

            .chat-close-btn:hover {
                background: transparent;
                transform: scale(1.1);
                color: #666;
            }

            .chat-body-scroll {
                flex: 1;
                padding: 15px;
                overflow-y: auto;
                background-color: #f7f7f7;
                /* 聊天背景纹理 */
                background-image: radial-gradient(#e0c3fc 0.5px, transparent 0.5px);
                background-size: 20px 20px;
                display: flex;
                flex-direction: column;
                gap: 15px;
            }

            .chat-bubble {
                max-width: 80%;
                padding: 12px 16px;
                font-size: 14px;
                line-height: 1.5;
                position: relative;
                word-wrap: break-word;
                box-shadow: 0 2px 5px rgba(0, 0, 0, 0.03);
                flex-shrink: 0;
                /* Prevent squashing */
            }

            .chat-bubble.them {
                background: #fff;
                align-self: flex-start;
                border-radius: 4px 16px 16px 16px;
                border: 1px solid #eee;
                color: #333;
            }

            .chat-bubble.me {
                background: linear-gradient(135deg, var(--primary-purple) 0%, #b388cd 100%);
                color: white;
                align-self: flex-end;
                border-radius: 16px 16px 4px 16px;
                text-align: right;
            }

            .chat-footer {
                padding: 10px 15px;
                background: #fff;
                border-top: 1px solid #eee;
                display: flex;
                gap: 10px;
                align-items: flex-end;
                /* 多行输入时底部对齐 */
            }

            .chat-input {
                flex: 1;
                background: #f5f5f5;
                border: none;
                border-radius: 20px;
                padding: 12px 15px;
                font-size: 14px;
                outline: none;
                max-height: 100px;
                resize: none;
            }

            .chat-send-btn {
                width: 40px;
                height: 40px;
                border-radius: 50%;
                background: var(--primary-purple);
                border: none;
                color: white;
                display: flex;
                align-items: center;
                justify-content: center;
                box-shadow: 0 2px 8px rgba(200, 150, 220, 0.4);
                cursor: pointer;
                transition: transform 0.1s;
            }

            .chat-send-btn:active {
                transform: scale(0.9);
            }

            /* --- Home Page Isolated Styles --- */

            /* Home Input Area */
            .home-input-area {
                position: absolute;
                bottom: 0;
                left: 0;
                width: 100%;
                padding: 8px 12px;
                background: var(--glass-strong);
                backdrop-filter: blur(20px);
                -webkit-backdrop-filter: blur(20px);
                border-top: 1px solid rgba(255, 255, 255, 0.9);
                box-shadow: var(--shadow-float);
                z-index: calc(var(--z-content) + 1);
                display: flex;
                flex-direction: column;
                gap: 0;
            }

            /* --- Optimized Choice Drawer (Vertical) --- */
            #choice-drawer {
                display: flex;
                flex-direction: column;
                gap: 12px;
                width: 100%;
                overflow-x: hidden;
                overflow-y: auto;
                /* 使用 max-height + transition 实现折叠/展开动画 */
                max-height: 0;
                padding: 0 5px;
                margin-bottom: 0;
                transition: all 0.3s cubic-bezier(0.19, 1, 0.22, 1);
            }

            #choice-drawer.open {
                max-height: 300px;
                padding: 10px 5px;
                margin-bottom: 8px;
            }

            .choice-btn {
                width: 100%;
                padding: 12px 20px;
                background: var(--card-bg);
                border: 1px solid var(--primary-purple);
                color: var(--primary-purple);
                border-radius: 12px;
                font-size: 14px;
                font-weight: bold;
                text-align: center;
                cursor: pointer;
                box-shadow: 0 4px 10px rgba(200, 150, 220, 0.15);
                transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1);
                position: relative;
                overflow: hidden;
            }

            .choice-btn:hover {
                transform: translateY(-2px);
                box-shadow: 0 6px 15px rgba(200, 150, 220, 0.25);
                background: linear-gradient(to right, var(--card-bg), #fbf5ff);
            }

            .choice-btn:active {
                transform: scale(0.98);
                box-shadow: 0 2px 5px rgba(200, 150, 220, 0.1);
            }

            .home-input-row {
                display: flex;
                align-items: center;
                gap: 8px;
                background: var(--card-bg);
                height: 50px;
                border-radius: var(--radius-full);
                padding: 4px 6px 4px 14px;
                border: 1px solid rgba(200, 150, 220, 0.3);
                box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
                transition: all 0.2s ease;
            }

            .home-input-row:focus-within {
                border-color: var(--primary-purple);
                box-shadow: 0 4px 15px rgba(200, 150, 220, 0.15);
                transform: translateY(-1px);
            }

            .home-input-row input {
                flex: 1;
                border: none;
                outline: none;
                padding: 0;
                font-size: 15px;
                background: transparent;
                color: var(--text-main);
                height: 100%;
                font-family: inherit;
            }

            .home-input-row input::placeholder {
                color: var(--text-light);
            }

            .home-icon-btn-toggle {
                background: none;
                border: none;
                padding: 0;
                cursor: pointer;
                color: var(--text-light);
                display: flex;
                align-items: center;
                justify-content: center;
                transition: color 0.2s;
                margin-right: 2px;
            }

            .home-icon-btn-toggle:hover,
            .home-icon-btn-toggle.active {
                color: var(--primary-purple);
            }

            .home-icon-btn-toggle svg {
                width: 22px;
                height: 22px;
                fill: currentColor;
            }

            /* Home Message Bubbles */
            .home-message-bubble {
                padding: 10px 15px;
                margin: 10px 0;
                border-radius: var(--radius-md);
                max-width: 90%;
                word-wrap: break-word;
            }

            .home-bubble-ai {
                background: var(--accent-blue);
                align-self: flex-start;
                border-radius: 15px 15px 15px 0;
            }

            .home-bubble-user {
                background: var(--accent-pink);
                align-self: flex-end;
                margin-left: auto;
                border-radius: 15px 15px 0 15px;
                text-align: left;
            }

            .home-bubble-center {
                width: 100% !important;
                max-width: 100% !important;
                background: transparent;
                padding: 0;
                margin: 15px 0;
                text-align: center;
                align-self: center;
                border-radius: 0;
            }

            /* Home AI Panels */
            .home-ai-panel-container {
                border: 1px solid var(--primary-purple);
                border-radius: var(--radius-sm);
                overflow: hidden;
                margin-top: 5px;
                box-shadow: var(--shadow-sm);
                background: white;
            }

            .home-ai-panel-header {
                background: var(--primary-purple-light);
                padding: 8px 12px;
                font-size: 12px;
                font-weight: bold;
                color: var(--primary-purple);
                border-bottom: 1px solid rgba(200, 150, 220, 0.2);
                display: flex;
                justify-content: space-between;
                align-items: center;
            }

            .home-ai-panel-body {
                padding: 10px;
                font-size: 13px;
                line-height: 1.5;
                color: var(--text-secondary);
            }

            .home-status-grid {
                display: grid;
                grid-template-columns: 1fr 1fr;
                gap: 5px;
            }

            .home-status-item {
                display: flex;
                font-weight: bold;
                color: var(--primary-purple);
                margin-bottom: 5px;
                border-bottom: 1px dashed var(--primary-purple-light);
                padding-bottom: 3px;
            }

            /* Home Loading */
            .home-loading-wrapper {
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: center;
                width: 100%;
            }

            .home-heart-loading {
                display: inline-flex;
                align-items: center;
                justify-content: center;
                gap: 8px;
                padding: 10px;
            }

            .home-heart-loading .home-heart-beat {
                width: 20px;
                height: 20px;
                fill: var(--primary-purple);
                animation: heartBounce 0.6s ease-in-out infinite;
            }

            .home-loading-timer {
                font-size: 11px;
                color: var(--primary-purple);
                opacity: 0.6;
                margin-top: -4px;
                font-family: monospace;
                letter-spacing: 0.5px;
            }

            /* Home Scene Card */
            .home-scene-card {
                background: linear-gradient(135deg, var(--primary-bg) 0%, var(--tag-bg-purple) 100%);
                border: 1px solid #dcd0ff;
                border-radius: 8px;
                padding: 12px;
                margin-bottom: 15px;
                box-shadow: var(--shadow-sm);
                text-align: left;
                position: relative;
                overflow: hidden;
            }

            .home-scene-card::before {
                content: "";
                position: absolute;
                top: 0;
                left: 0;
                width: 4px;
                height: 100%;
                background: var(--primary-purple);
            }

            .home-scene-line {
                display: flex;
                align-items: center;
                margin-bottom: 6px;
                font-size: 13px;
                color: var(--text-secondary);
            }

            .home-scene-line:last-child {
                margin-bottom: 0;
            }

            .home-scene-icon {
                margin-right: 8px;
                font-size: 14px;
                width: 20px;
                text-align: center;
            }

            .home-scene-label {
                font-weight: bold;
                color: var(--primary-purple);
                margin-right: 5px;
            }
        


        /* 头像库管理样式 */
        .avatar-tab {
            flex: 1;
            padding: 10px;
            border: 2px dashed var(--primary-purple);
            background: white;
            border-radius: 10px;
            font-size: 13px;
            font-weight: bold;
            color: var(--primary-purple);
            cursor: pointer;
            transition: all 0.2s;
        }

        .avatar-tab.active {
            background: var(--primary-purple);
            color: white;
            border-style: solid;
        }

        /* Avatar Library Tabs */
        .avatar-tab {
            flex: 1;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
            border-radius: 12px;
            border: 1px solid #eee;
            background: white;
            color: #666;
            padding: 8px;
            cursor: pointer;
            transition: all 0.2s ease;
            font-family: inherit;
        }

        .avatar-tab.active {
            border-color: var(--primary-purple);
            color: var(--primary-purple);
            box-shadow: 0 4px 12px rgba(200, 150, 220, 0.15);
            background: rgba(255, 255, 255, 0.5);
        }

        /* Universal Custom Dropdown */
        .custom-dropdown-container {
            position: relative;
            flex: 1;
        }

        .custom-dropdown {
            position: absolute;
            top: 100%;
            left: 0;
            right: 0;
            background: white;
            border: 1px solid #ddd;
            border-radius: 8px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
            max-height: 200px;
            overflow-y: auto;
            z-index: 1000;
            display: none;
            margin-top: 5px;
        }

        .dropdown-item {
            padding: 10px 15px;
            cursor: pointer;
            font-size: 13px;
            color: #333;
            border-bottom: 1px solid #f9f9f9;
            transition: background 0.2s;
            display: flex;
            align-items: center;
            gap: 8px;
        }

        .dropdown-item:hover {
            background: rgba(200, 150, 220, 0.1);
            color: var(--primary-purple);
        }

        .dropdown-item:last-child {
            border-bottom: none;
        }

        .avatar-preview-grid {
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            gap: 8px;
            max-height: 200px;
            overflow-y: auto;
            padding: 10px;
            background: white;
            border: 1px dashed #ddd;
            border-radius: 8px;
            margin-top: 8px;
        }

        .avatar-preview-item {
            position: relative;
            width: 100%;
            padding-bottom: 100%;
            border-radius: 8px;
            overflow: hidden;
            cursor: pointer;
            border: 2px solid transparent;
            transition: all 0.2s;
        }

        .avatar-preview-item:hover {
            border-color: #e74c3c;
            transform: scale(1.05);
        }

        .avatar-preview-item img {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            object-fit: cover;
        }

        .avatar-preview-item::after {
            content: '×';
            position: absolute;
            top: 2px;
            right: 4px;
            font-size: 12px;
            color: white;
            text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
            opacity: 0;
            transition: opacity 0.2s;
        }

        .avatar-preview-item:hover::after {
            opacity: 1;
        }

        /* --- API 错误处理弹窗 (手账风格) --- */
        .error-modal-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.4);
            backdrop-filter: blur(4px);
            z-index: 5000;
            display: none;
            justify-content: center;
            align-items: center;
            animation: fadeIn 0.3s ease;
        }

        .error-card {
            width: 85%;
            max-width: 400px;
            background: #fff;
            border-radius: 16px;
            box-shadow: 0 10px 40px rgba(180, 140, 200, 0.3);
            border: 2px solid var(--primary-purple);
            overflow: hidden;
            animation: cardScaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
            position: relative;
        }

        @keyframes cardScaleIn {
            from {
                transform: scale(0.85);
                opacity: 0;
            }

            to {
                transform: scale(1);
                opacity: 1;
            }
        }

        .error-header {
            background: linear-gradient(135deg, #ff9a9e 0%, #fecfef 100%);
            padding: 12px 15px;
            color: #fff;
            font-weight: bold;
            display: flex;
            align-items: center;
            gap: 8px;
            border-bottom: 2px dashed rgba(255, 255, 255, 0.5);
        }

        .error-body {
            padding: 20px 15px;
            color: var(--text-dark);
            font-size: 14px;
            line-height: 1.6;
            background-color: #fdfbf7;
            background-image: radial-gradient(var(--primary-purple-light) 0.5px, transparent 0.5px);
            background-size: 15px 15px;
            min-height: 80px;
            display: flex;
            align-items: center;
            justify-content: center;
            text-align: center;
        }

        .error-footer {
            padding: 12px 15px;
            background: #fff;
            display: flex;
            justify-content: space-between;
            gap: 10px;
        }

        .error-btn {
            flex: 1;
            padding: 10px;
            border-radius: 20px;
            border: none;
            font-size: 13px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
            gap: 5px;
        }

        .error-btn-retry {
            background: var(--primary-purple);
            color: #fff;
            box-shadow: 0 4px 10px rgba(200, 150, 220, 0.3);
        }

        .error-btn-close {
            background: #f0f0f0;
            color: #666;
        }

        .error-btn:active {
            transform: scale(0.95);
        }

        .error-tape {
            position: absolute;
            top: -10px;
            left: 50%;
            transform: translateX(-50%) rotate(-2deg);
            width: 80px;
            height: 25px;
            background: rgba(224, 195, 252, 0.6);
            backdrop-filter: blur(2px);
            z-index: 10;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
        }
    

@keyframes ...

\` 中使用全局选择器 (如 \`body\`, \`html\`, \`nav\`, \`div\` 等)。若必须使用 \`<style>\`，**必须**确保所有选择器都限定在你自己生成的容器类名内 (例如: \`.my-card .title\`)。推荐优先使用内联样式 (\`style="..."\`)。
    -   **动画关键帧**: 仅允许在 \`<style>\` 中定义 \`@keyframes\`。
    -   **零外部依赖**: 不要试图加载外部 CSS/JS/Images (除非是 data URI 或极其稳定的图床，尽量用 CSS 绘图)。

- **状态数据**: 所有非叙事性状态（属性、背包等）必须放在 "world_update" 中，由系统原生 UI 渲染。

### 响应格式
严格JSON格式，不要输出其他内容。
{
  "narrative": "纯故事内容的HTML（场景描述、对话、行动结果等）。可用<b>粗体</b>高亮关键词。不要在这里放状态栏、属性表、角色卡、地点卡！",
  "choices": ["选项1", "选项2", "选项3"],
  "world_update": {
    "me": {
      "name": "玩家的姓名",
      "title": "称号/头衔（如：复仇者、冒险者）",
      "avatar": "头像（显示首字母）",
      "money": 0, // 金钱 (始终基于当前余额进行增减，严禁随机重置)
      "time": "x年·x月·x日·x时", // 当前游戏时间 (格式: x年·x月·x日·x时，随剧情推动)
      "location": "当前位置 (Mutable)",
      "quote": "角色的座右铭或最近说过的话 (Mutable)",
      "inventory": [
        { "name": "物品名", "description": "物品介绍 (30-100字)" }
      ],
      "stats": {
        "气血": { "current": 100, "max": 100, "status": "健康" },
        "理智": { "current": 80, "max": 100, "status": "清醒" }
      },
      "profile": {
        "age": "年龄 (Mutable)",
        "race": "种族 (Mutable)",
        "alignment": "阵营 (Mutable)",
        "identity": "身份/职业 (Mutable)",
        "location": "住址 (Mutable)",
        "gender": "性别 (Mutable)"
      },
      "traits": [
        { "name": "特质名", "color": "red/yellow/blue/purple" }
      ], // 数量规则：必须始终保持在 3-6 个之间。
      // 变化规则：禁止无故增减。标签的增加/删除/替换必须在 narrative 中有明确的剧情演变支撑。
      "skills": [
        { "name": "技能名 (已获得技能的名字不可更改)", "description": "技能描述 (20-100字)", "rank": "S/A/B/C/D/E/F", "exp": 0 }
      ],
      "relationships": [
        { "name": "关系人名", "value": 50, "max": 100, "status": "友好/冷淡/敌对", "desc": "简短描述 (应体现玩家角色对该 NPC 的看法与态度)" }
      ],
      "diary": [
        { "date": "时间", "title": "标题", "content": "难忘/重要事情记录 (仅在重大剧情节点更新，禁止流水账，50-200字)" }
      ],
      "description": "外貌/性格描述 (50-200字)"
    },
    "npcs": [
      { 
        "status": "健康", // Enum: "健康"(Healthy-Green), "不适"(Unwell-Yellow), "重伤"(Injured-Red), "死亡"(Dead-Gray).
        "tapeStyle": "css-string", 
        "memory": [],
        "id": "NPC姓名", // Use Name as ID 
        "name": "NPC姓名", 
        "role": "职业/身份", 
        "currentLocation": "当前所在地点名称", // 可以是已知地点或未知地点
        "avatar": "头像URL (可选)",
        "description": "详细描述 (50-200字)", 
        "firstMeet": "初遇场景描述",
        "relationship": 0, // -100 to 100
        "profile": {
            "age": "年龄",
            "race": "种族",
            "gender": "性别",
            "identity": "身份",
            "alignment": "阵营",
            "location": "当前所在地"
        },
        "stats": {
            "属性名1": { "current": 80, "max": 100 },
            "属性名2": { "current": 60, "max": 100 }
        },
        "traits": [
            { "name": "性格特征1", "color": "blue" },
            { "name": "性格特征2", "color": "red" }
        ], // 数量规则：必须始终保持在 3-6 个之间。
        // 变化规则：禁止无故增减。每个标签的变动都必须是对当前剧情发展的合理解释。
        "skills": [
            { "name": "技能名", "rank": "A", "description": "技能描述", "exp": 0 }
        ],
        "inventory": [
            { "name": "持有物品1", "description": "物品介绍 (20-100字)" },
            { "name": "持有物品2", "description": "物品介绍 (20-100字)" }
        ],
        "relationships": [
            { "name": "玩家", "value": 0, "max": 100, "status": "词汇 (如：萍水相逢、宿命之敌等，由AI回填)", "desc": "对玩家的看法/印象 (简短)" },
            { "name": "NPC2姓名", "value": 50, "max": 100, "status": "词汇 (如：亦师亦友、过往旧识等)", "desc": "觉得NPC2很值得信赖" }
        ],
        "diary": [
            { "date": "日期", "title": "标题", "content": "内容，难忘/重要事情记录 (仅在重大剧情节点更新，禁止流水账，50-200字)" }
        ]
      }
    ],
    "locations": [
      { 
        "name": "地点名 (Immutable)", 
        "tags": ["Tag1", "Tag2"], 
        "description": "地点描述（50-200字，详细描述环境、氛围、感官细节）", 
        "type": "城镇/森林/山地/水域/遗迹/建筑/自然", 
        "owner": "归属", 
        "peopleHere": ["NPC1姓名", "NPC2姓名", "未知人物名"], // 在此地的人,可以是已知NPC或未知人名
        "status": "状态 (Mutable，一句话描述当前动态，如：庆典正在进行中，人群喧闹)", 
        "actions": ["动作1", "动作2", "动作3", "动作4"], // Mutable，提供3-5个建议行动
        "connections": ["必须包含至少一个已存在地点名"] 
      }
    ]
  }
}

### 核心限制 (重要!)
1. **NPC 死亡终结原则**:
   - 一旦 NPC 的 status 变为 "死亡"，该角色在剧情中彻底永久消失。
   - **严禁**在 narrative 中描写该角色的后续互动（除非是回忆或墓地场景，且角色不发声）。
   - **严禁**在 choices 中提供涉及该角色的行动。
   - **严禁**在 world_update.npcs 中再次更新该角色（状态不可逆）。

2. **NPC 唯一性原则 (重要!)**:
   - 在生成新 NPC 之前，你**必须**检查 world_update.npcs 中已有的角色。
   - **严禁**创建与现有 NPC 姓名（或读音/拼写极度相似）、身份高度重叠的新角色。
   - 如果剧情需要更新已有角色，请查找其 id 并进行局部更新，绝对不要赋予新 ID 重新创建。

3. **玩家姓名初始化 (重要!)**:
    - 如果当前 me.name 为 "玩家"，由于这是预设占位符，你**必须**基于当前世界设定为玩家生成一个贴合背景的真实姓名，并在 world_update.me 中返回。
    - 名字一旦确定，在后续回复中必须保持恒定，不得再次更改。

4. **好感度变化规则 (重要!)**:
   - relationship 字段现在表示 “好感度变化值” (Delta)，系统将在原有数值上进行增减。
   - **普通剧情**: 每次变动幅度必须控制在 ±5 以以内（如：+3, -2, 0）。
   - **重大转折**: 仅在涉及生死抉择、背叛、救赎等核心剧情时，才允许出现较大波动（如：+20, -30）。
   - **平滑渐进**: 确保角色关系的变化是逻辑连贯且平滑的，避免无缘无故的大幅波动。

4. **narrative (视觉叙事)**:
    - 严禁输出纯文本。
    - **Remember: You are a designer.** 每次输出都不仅仅是讲故事，更是在**设计**一个网页片段。
    - 根据剧情节奏（急/缓）、情感色彩（喜/悲/恐/怒）来决定视觉语言。
    - 善用 CSS 混合模式 (\`mix-blend-mode\`) 和遮罩，营造电影级质感。

5. **NPC 沉浸式角色扮演 (Immersive Roleplay) - 重要!**:
    - **性格锚点 (Personality Anchors)**: 生成对话前，**必须**检索 NPC 的 \`traits\` 和 \`profile\`。
    - **拒绝同质化**。
    - **环境反应**: NPC 的语调必须反映其 \`status\`。包括不限于：重伤时说话断续（使用省略号...）；恐惧时语无伦次；愤怒时使用感叹号！，但注意不要刻板！
    - **OOC 零容忍**: NPC **绝对不能**说出“作为AI”、“根据系统设定”等打破第四面墙的话。GM 的旁白与角色对话在格式和视觉上必须严格区分。

6. **world_update 更新原则（重要！）**:
   - **只在相关时更新**: 只有当剧情确实涉及该角色/地点时才返回更新。例如玩家在房间写信，不应该"遇见"任何人或"发现"任何地点。
   - **区分"首次"和"更新"**: 
     - 首次遇见角色 → 在npcs中返回完整信息（系统显示"遇到角色"）
     - 再次与已认识角色互动 → 在npcs中只返回id和需要更新的字段（如relationship、description变化）
     - 如果角色没有出现在当前场景 → 不要在npcs中返回该角色
   - **地点一致性 (零占位原则)**: 
     - \`me.location\` 指向的名称**必须**在 \`world_update.locations\`（首次发现）或历史已知地点中已定义。严禁玩家瞬间处于一个没有描述定义的“未知”地点。
     - **移动逻辑**:
       - 首次发现/进入新区域：**必须**在 \`world_update.locations\` 中返回该地点的完整对象。
       - 再次进入已知地点：仅需更新 \`me.location\` 字符串。
       - 如果玩家没有移动或发现新区域：不要返回 locations。

7. **world_update.me**: 始终返回当前状态。
8. **属性与技能生成规则 (重要!)**:
    - **强制中文**: 统计属性项 (stats) 的键名必须使用符合世界观背景的 **简体中文**（如：修仙用“灵力/气血”，克苏鲁用“理智/精力”）。
    - **严禁英文**: 绝对禁止使用 "hp", "mp", "str", "agi" 等英文缩写。
    - **初始化数量**: 游戏第一回合初始化时，必须一次性生成 **8-10 个** 核心属性，以及 **4-6 个** 核心技能 (skills)。
    - **固化原则 (Immutability)**: 属性项和技能项的**名称**和**总数**一旦在第一回合生成，**绝对不可更改、增加或减少**。
    - **⚠️ 技能锁定 (CRITICAL - 零新增)**: 
      - 技能应作为角色的"核心能力/天赋"存在。
      - 第一回合生成后，skills数组的**长度和每个技能的name字段永远不变**。
      - **严禁在后续任何回合的world_update中出现第一回合不存在的技能名称！**
      - **严禁增加新技能！严禁创造新技能名！无论任何理由都不允许！**
      - 你只能修改已有技能的 rank、description、exp 三个字段。
    - **一致性**: NPC 的属性和技能生成规格必须保持与玩家一致。
      - **技能**: 名称不可变，且不可增加新项。每个技能必须包含 \`exp\` 字段 (0-100)。
      - **经验与升级 (EXP & Level-up)**: 你应根据剧情中技能的使用频率或相关感悟事件来更新 \`exp\`。当 \`exp\` 达到 100 时，必须提升技能的 \`rank\` (如 C->B->A->S)，并将 \`exp\` 重置（或保留溢出值）。同时更新 \`description\` 来体现精进。
    - **人际视角规则 (重要!)**: me.relationships 中的 desc 字段必须以**玩家的视角**和第一人称口吻来描述对该 NPC 的印象（例如："虽然他看起来很冷酷，但我能感觉到他内心的温柔"）。这与 NPC 自身的 memory 或 description 不同。

9. **Location Immutability (重要!)**:
    - \`name\` 和 \`tags\` 一旦生成，**绝对不可更改**。
    - \`description\`, \`owner\`, \`status\`, \`actions\` 应随剧情发展动态更新（如：繁华变为废墟）。

10. **NPC ID Rule (重要!)**: NPC的 'id' **必须**直接使用其 'name' (例如 "id": "爱丽丝")。严禁使用数字 ID。
    - **NPC Immutability**: NPC的 'name', 'avatar', 'profile.race', 'profile.gender' 一旦生成，绝对不可更改。

11. **NPC 日记生成规则 (重要!)**:
    - 当 NPC 经历了重要剧情、与玩家产生了深度互动、或其 \`relationship\` / \`status\`发生显著变化时，你应该在 \`world_update.npcs\` 的对应角色下增加一条 \`diary\` 条目。
    - 日记应以角色的第一人称叙述，反映其内心想法、对当前局局势的看法或对玩家的评价。
    - 确保返回的是一个包含单个新条目的数组，系统会自动将其追加到历史记录中。

12. **金钱 (Money) 规则 (重要!)**:
    - **累积性**: \`me.money\` 是一个累积数值，代表玩家的总资产。
    - **逻辑连贯**: 你**绝对禁止**在没有剧情支撑的情况下随机更改金钱数值。
    - **变动方式**: 当发生交易、奖励、丢失等情节时，你应当在当前余额基础上进行加减计算。
    - **返回方式**: 
        - 推荐使用**增量字符串**，如 \`"+50"\` (增加50) 或 \`"-20"\` (扣除20)。
        - 也可以返回计算后的**绝对数值**，但必须确保计算逻辑符合剧情逻辑。
    - **初始化**: 只有在游戏开始的第一回合允许设定初始资金，随后必须严格遵循增减逻辑。

13. **时间 (Time) 规则 (重要!)**:
    - **格式要求**: 必须严格遵守 \`"x年·x月·x日·x时"\` 的格式（例如：\"120年·3月·15日·黄昏\"）。
    - **剧情驱动**: 时间不应静止。每当发生跨场景移动、长时间对话、休息或重大事件后，你应当合理推进时间。
    - **一致性**: 确保时间的变化与剧情描述（如：“夜深了”、“次日清晨”）保持逻辑一致。

14. **玩家日记生成规则 (重要!)**:
    - me.diary 严禁每回合更新。它仅用于记录游戏中的重大时刻、里程碑事件、关键抉择及其后果或重要的情感转折。
    - 除非当前回合发生了上述级别的事件，否则不要返回 me.diary。
    - 记录时应采用沉浸式的视角。

13. **NPC 完整性规则 (重要!)**:
    - 当生成新的 NPC 或对现有 NPC 进行详细描绘时，**必须**返回满足详情页显示所需的所有字段。
    - 必须包含：\`stats\` (属性), \`traits\` (特征), \`skills\` (技能), \`inventory\` (随身物品), \`relationships\` (社交关系), \`diary\` (日记)。
    - 禁止遗漏任何核心板块，确保角色在数据层面的丰满度。
    - 所有技能和物品的命名必须高度契合当前的世界观设定。

14. **双向关系初始化规则 (重要!)**:
    - **NPC 视角**: 必须在 \`world_update.npcs\` 对应角色的 \`relationships\` 列表中新增“玩家”条目，记录 NPC 对玩家的初始看法和好感。
    - **同步数值**: \`npc.relationship\` (双向综合值) 应与上述两处初始化值保持逻辑一致。
    - **羁绊关系 (Status) 动态化 (CRITICAL)**: 
      - \`relationships\` 数组中每个对象的 \`status\` 字段必须使用富有表现力的 **2-4字中文词汇**。
      - 严禁仅使用固定的“陌生人”、“朋友”、“挚友”。
      - 词汇应体现当前互动深度与世界背景（如：萍水相逢、宿命之敌、亦师亦友、此生至交、互有好感、利益之交、过往宿印等）。

15. **choices**: 提供3-4个建议行动。

14. **物品系统规则 (重要!)**:
    - **完整描述**: \`inventory\` 中的物品**必须**以对象格式返回：\`{ "name": "物品名", "description": "详细介绍 (20-100字)" }\`。绝对严禁仅返回字符串。
    - **转移原子性 (Atomic Transfer)**: 当玩家表达“赠送”、“交换”或“给予”意图时，你**必须**在同一个 \`world_update\` 中同时更新相关方的 \`inventory\`（即：从玩家背包扣除的同时，必须在 NPC 分页中增加；反之亦然）。严禁物品凭空消失或多出。
    - **消耗逻辑**: 消耗性物品（如食物、药水、一次性道具）在使用后，必须从持有者的 \`inventory\` 中删除。
    - **意图识别**: 识别玩家在主页输入框中的指令。如果玩家说“把[物品]送给[NPC]”，你应该在剧情中描写对方的反应，并在数据中执行上述转移逻辑。

### 错误示例（不要这样做）
❌ 玩家在写信，却返回了 npcs: [托马斯, 露西尔] （他们没出现在场景中！）
❌ 玩家在厨房，却返回了 locations: [卧室] （已知地点，没有变化！）
❌ 每回合都重复返回所有已知NPC和地点
`

        let chatFileContent = ""; // Store uploaded content

        let musicList = [];
        let currentMusicIndex = -1;

        // --- 3. 页面翻转逻辑 ---
        let currentPage = 0;
        let isAnimating = false;
        const pages = document.querySelectorAll('.page');
        const navItems = document.querySelectorAll('.nav-item');

        function navTo(index) {
            if (index === currentPage || isAnimating) return;

            isAnimating = true;
            const isForward = index > currentPage;
            const startPage = currentPage;
            const endPage = index;

            // 计算需要翻/盖的页面数量
            const pageCount = Math.abs(endPage - startPage);
            const flipDelay = 250; // 每页之间的延迟(ms)
            const singleFlipDuration = 800; // 单页翻页动画时长(ms)

            // --- 预加载逻辑：在动画开始前渲染涉及的所有页面 ---
            // 无论是向前翻还是向后翻，所有涉及的页面都需要显示内容
            const minPage = Math.min(startPage, endPage);
            const maxPage = Math.max(startPage, endPage);
            for (let i = minPage; i <= maxPage; i++) {
                renderPageContent(i);
            }
            // ----------------------------------------------

            navItems[currentPage].classList.remove('active');
            navItems[index].classList.add('active');

            // 清除所有页面的动画类和样式
            pages.forEach(page => {
                page.classList.remove('flip-to-left', 'restore-from-left', 'reveal', 'active');
                page.style.zIndex = '';
                page.style.transform = '';
                page.style.opacity = '';
            });

            const targetPageEl = pages[endPage];

            if (isForward) {
                // 向前翻页：从 startPage 翻到 endPage
                // 目标页在最底层，中间页面按递减z-index叠在上面

                // 目标页设置为可见但在底层
                targetPageEl.style.zIndex = 50;
                targetPageEl.style.opacity = '1';

                // 中间页面（从startPage到endPage-1）设置递减z-index
                // 不使用reveal类，直接用样式控制
                for (let i = 0; i < pageCount; i++) {
                    const pageIndex = startPage + i;
                    // 越靠近startPage的z-index越高（先翻的在最上面）
                    pages[pageIndex].style.zIndex = 150 + (pageCount - i);
                    pages[pageIndex].style.opacity = '1';
                }

                // 依次翻开页面
                for (let i = 0; i < pageCount; i++) {
                    const pageIndex = startPage + i;
                    setTimeout(() => {
                        pages[pageIndex].classList.add('flip-to-left');
                    }, i * flipDelay);
                }
            } else {
                // 向后翻页：当前页保持显示，依次盖上中间的页面
                pages[startPage].style.opacity = '1';
                pages[startPage].style.zIndex = 50;

                // 依次盖上从 startPage-1 到 endPage 的页面
                for (let i = 0; i < pageCount; i++) {
                    const pageIndex = startPage - 1 - i;
                    setTimeout(() => {
                        // 页面初始设为已翻开状态，然后播放恢复动画
                        pages[pageIndex].style.transform = 'rotateY(-115deg)';
                        pages[pageIndex].style.zIndex = 160 + i;
                        pages[pageIndex].style.opacity = '1';
                        pages[pageIndex].classList.add('restore-from-left');
                    }, i * flipDelay);
                }
            }

            // 计算总动画时间
            const totalDuration = (pageCount - 1) * flipDelay + singleFlipDuration;

            setTimeout(() => {
                pages.forEach(page => {
                    page.classList.remove('flip-to-left', 'restore-from-left', 'reveal');
                    page.style.zIndex = '';
                    page.style.transform = '';
                    page.style.opacity = '';
                });
                targetPageEl.classList.add('active');
                currentPage = index;
                isAnimating = false;
                // 动画结束时确保最终状态一致，虽然已经预加载过，再次调用也无妨
                renderPageContent(index);
            }, totalDuration);
        }

        function renderPageContent(index) {
            if (index === 1) updateMeUI();
            if (index === 2) updateSocialUI();
            if (index === 3) updateMapUI();
            if (index === 4) updateSettingsUI();
        }

        // --- 4. 音乐播放器 (Enhanced) ---
        const audio = document.getElementById('audio-player');
        const playBtn = document.getElementById('btn-play');
        const nextBtn = document.getElementById('btn-next');
        const disc = document.getElementById('music-disc');
        const iconPlay = document.getElementById('icon-play');
        const iconPause = document.getElementById('icon-pause');
        const songTitle = document.getElementById('song-title');
        const artistName = document.getElementById('artist-name');
        const progressFill = document.getElementById('progress-fill');

        /* Playlist is now in LocalStorage: 'music_playlist' -> [{id, title, artist, type, src/fileKey, enabled}] */

        let isBatchDeleteMode = false;

        playBtn.onclick = () => {
            if (audio.paused) {
                if (musicList.length > 0 && !audio.src) playNextEnabled(0); // Start from beginning
                else audio.play();
            } else {
                audio.pause();
            }
        };

        nextBtn.onclick = () => playNextEnabled(currentMusicIndex + 1);

        function playNextEnabled(startIndex) {
            let tried = 0;
            let idx = startIndex;
            while (tried < musicList.length) {
                if (idx >= musicList.length) idx = 0; // Loop
                if (musicList[idx].enabled) {
                    playMusic(idx);
                    return;
                }
                idx++;
                tried++;
            }
            showToast('播放列表没有可播放的歌曲');
        }

        async function playMusic(index, autoPlay = true) {
            if (index < 0 || index >= musicList.length) return;
            const track = musicList[index];
            if (!track.enabled) return;

            currentMusicIndex = index;

            // Cleanup old ObjectURL to avoid memory leak
            if (audio.src && audio.src.startsWith('blob:')) URL.revokeObjectURL(audio.src);

            try {
                if (track.type === 'local') {
                    // Get blob from IDB
                    const blob = await db.get('music_files', track.fileKey);
                    if (blob) {
                        audio.src = URL.createObjectURL(blob);
                    } else {
                        showToast(`无法加载文件: ${track.title}`);
                        playNextEnabled(index + 1);
                        return;
                    }
                } else {
                    audio.src = track.src;
                }

                songTitle.innerText = track.title;
                artistName.innerText = track.artist;

                if (autoPlay) {
                    audio.play().catch(e => {
                        console.error(e);
                        showToast('播放失败，可能是格式不支持或浏览器限制');
                        playNextEnabled(index + 1);
                    });
                }
            } catch (e) {
                console.error(e);
                playNextEnabled(index + 1);
            }
        }

        // --- Persistence Logic ---
        function savePlayerState() {
            const state = {
                index: currentMusicIndex,
                time: audio.currentTime,
                isPlaying: !audio.paused
            };
            localStorage.setItem('music_player_state', JSON.stringify(state));
        }

        async function restorePlayerState() {
            const raw = localStorage.getItem('music_player_state');
            if (!raw) return;

            try {
                const state = JSON.parse(raw);
                if (state.index >= 0 && state.index < musicList.length) {
                    // Load the track but don't necessarily play it immediately if it was paused
                    await playMusic(state.index, false);

                    // Restore time
                    if (state.time) {
                        audio.currentTime = state.time;
                        // Force a visual update
                        const percent = (audio.currentTime / audio.duration) * 100 || 0;
                        progressFill.style.width = percent + '%';
                    }

                    // Attempt to resume if it was playing
                    if (state.isPlaying) {
                        // Many browsers block autoplay unless user interact, we try-catch
                        audio.play().catch(() => {
                            console.log('Autoplay blocked by browser. User interaction required.');
                        });
                    }
                }
            } catch (e) {
                console.error('Failed to restore player state:', e);
            }
        }

        audio.onplay = () => {
            iconPlay.style.display = 'none';
            iconPause.style.display = 'block';
            disc.classList.add('playing');
            savePlayerState();
        };
        audio.onpause = () => {
            iconPlay.style.display = 'block';
            iconPause.style.display = 'none';
            disc.classList.remove('playing');
            savePlayerState();
        };
        audio.ontimeupdate = () => {
            if (audio.duration) {
                const percent = (audio.currentTime / audio.duration) * 100;
                progressFill.style.width = percent + '%';
                // Periodically save state (e.g. every 5 seconds to reduce LS writes)
                if (Math.floor(audio.currentTime) % 5 === 0) {
                    savePlayerState();
                }
            }
        };
        audio.onended = () => {
            savePlayerState();
            playNextEnabled(currentMusicIndex + 1);
        };
        audio.onerror = () => {
            console.warn('Audio Error, skipping');
            playNextEnabled(currentMusicIndex + 1);
        }

        async function loadMusicList() {
            // Load meta from LS
            const raw = localStorage.getItem('music_playlist');
            musicList = raw ? JSON.parse(raw) : [];
            // UI Update is handled by settings page when open, but player needs list
        }

        // --- 5. 主页与游戏逻辑 ---
        function openImportModal() { document.getElementById('import-modal').style.display = 'flex'; }
        function closeImportModal() { document.getElementById('import-modal').style.display = 'none'; }
        function handleFileSelect(input) {
            document.getElementById('file-name').innerText = input.files[0] ? input.files[0].name : '';
        }

        async function startWorld() {
            const text = document.getElementById('import-text').value;
            const fileInput = document.getElementById('file-input');
            let fileContent = "";

            if (fileInput.files.length > 0) {
                const file = fileInput.files[0];
                try {
                    if (file.name.toLowerCase().endsWith('.docx')) {
                        const arrayBuffer = await file.arrayBuffer();
                        const result = await mammoth.extractRawText({ arrayBuffer: arrayBuffer });
                        fileContent = result.value;
                    } else {
                        fileContent = await file.text();
                    }
                } catch (e) {
                    showToast('文件读取失败: ' + e.message);
                }
            }

            const worldContext = `World Setting Intro: ${text}\nAdditional Context: ${fileContent}`;
            gameState.started = true;
            gameState.history = [
                { role: 'system', content: SYSTEM_PROMPT },
                { role: 'user', content: "Initialize the world based on this context: " + worldContext }
            ];

            // Save initial prompt for reset
            localStorage.setItem(PROMPT_KEY, JSON.stringify(gameState.history));

            document.getElementById('home-empty-state').style.display = 'none';
            document.getElementById('home-content').style.display = 'block';
            closeImportModal();

            await processAIInteraction();
        }

        function addMessageToLog(role, text) {
            // Hide System Prompt from UI
            if (role === 'system' && !text.includes('heart-loading')) return;
            // Hide explicit world settings prompts
            if (role === 'user' && text.startsWith('世界设定及要求：')) return;

            const log = document.getElementById('chat-log');
            const div = document.createElement('div');

            // Stronger check for HTML content to force centered style
            // If it contains div, section, or specific card classes, treat as full-width UI
            const hasHTML = /<div|<section|<table|class="home-scene-card"|class="home-ai-panel-container"/i.test(text);

            if ((role === 'ai' || role === 'system' || role === 'assistant') && hasHTML) {
                div.className = `home-message-bubble home-bubble-center`;
            } else if (role === 'ai' || role === 'system' || role === 'assistant') {
                // Regular text from AI
                div.className = `home-message-bubble home-bubble-center`;
            } else {
                div.className = `home-message-bubble home-bubble-${role}`;
            }
            // Explicitly set width for center bubbles to be safe
            if (div.classList.contains('home-bubble-center')) {
                div.style.width = '100%';
                div.style.maxWidth = '100%';
            }

            div.innerHTML = text;
            log.appendChild(div);

            // Add Long Press Event for User Bubbles
            if (role === 'user') {
                addLongPressEvent(div, text);
            }

            // Auto scroll
            setTimeout(() => log.scrollTop = log.scrollHeight, 10);
        }

        // --- Long Press Menu Logic (User Bubbles) ---
        let bubbleLongPressTimer;
        let isBubbleLongPress = false;
        let bubblePressStartX = 0;
        let bubblePressStartY = 0;

        function addLongPressEvent(element, text) {
            element.style.userSelect = 'none'; // Prevent default text selection on mobile
            element.style.webkitUserSelect = 'none';

            const startHandler = (e) => {
                isBubbleLongPress = false;
                bubblePressStartX = e.touches ? e.touches[0].clientX : e.clientX;
                bubblePressStartY = e.touches ? e.touches[0].clientY : e.clientY;

                bubbleLongPressTimer = setTimeout(() => {
                    isBubbleLongPress = true;
                    showBubbleMenu(e, element, text);
                }, 600); // 600ms threshold
            };

            const endHandler = (e) => {
                clearTimeout(bubbleLongPressTimer);
                if (isBubbleLongPress) {
                    e.preventDefault(); // Prevent click action if it was a long press
                }
            };

            const moveHandler = (e) => {
                const clientX = e.touches ? e.touches[0].clientX : e.clientX;
                const clientY = e.touches ? e.touches[0].clientY : e.clientY;
                // If moved more than 10px, cancel
                if (Math.abs(clientX - bubblePressStartX) > 10 || Math.abs(clientY - bubblePressStartY) > 10) {
                    clearTimeout(bubbleLongPressTimer);
                }
            };

            element.addEventListener('mousedown', startHandler);
            element.addEventListener('touchstart', startHandler); // Passive false might be needed if preventing default

            element.addEventListener('mouseup', endHandler);
            element.addEventListener('touchend', endHandler);

            element.addEventListener('mousemove', moveHandler);
            element.addEventListener('touchmove', moveHandler);

            // Disable default context menu on this element
            element.addEventListener('contextmenu', (e) => {
                e.preventDefault();
                // If right click, treat as long press immediately for desktop convenience?
                // Or just block it to avoid interference. Let's just block standard context menu.
            });
        }

        // --- Render History ---
        function loadChatHistory() {
            const log = document.getElementById('chat-log');
            if (!log) return;
            log.innerHTML = ''; // Clear
            gameState.history.forEach(msg => {
                if (msg.hidden) return; // Skip hidden logic
                addMessageToLog(msg.role, msg.content);
            });
            // Restore choices
            if (gameState.choices && gameState.choices.length > 0) {
                renderChoices(gameState.choices);
            }
            // Scroll to bottom
            setTimeout(() => log.scrollTop = log.scrollHeight, 10);
        }

        function showBubbleMenu(e, element, text) {
            // Remove existing menu
            hideBubbleMenu();

            const menu = document.createElement('div');
            menu.className = 'bubble-menu';
            menu.id = 'active-bubble-menu';

            // Menu Items
            menu.innerHTML = `
                <div class="bubble-menu-item" onclick="handleBubbleAction('refresh', this)">
                    <svg style="width:16px; height:16px; fill:currentColor; margin-right:4px;" viewBox="0 0 24 24"><path d="M17.65 6.35C16.2 4.9 14.21 4 12 4c-4.42 0-7.99 3.58-7.99 8s3.57 8 7.99 8c3.73 0 6.84-2.55 7.73-6h-2.08c-.82 2.33-3.04 4-5.65 4-3.31 0-6-2.69-6-6s2.69-6 6-6c1.66 0 3.14.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/></svg> 
                    刷新 (重生成)
                </div>
                <div class="bubble-menu-item delete" onclick="handleBubbleAction('delete', this)">
                    <svg style="width:16px; height:16px; fill:currentColor; margin-right:4px;" viewBox="0 0 24 24"><path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z"/></svg>
                    删除
                </div>
            `;

            // Position with offset to avoid covering finger/cursor immediately
            // Use touch coordinates or element coordinates
            let clientX, clientY;
            if (e.touches && e.touches[0]) {
                clientX = e.touches[0].clientX;
                clientY = e.touches[0].clientY;
            } else if (e.clientX) {
                clientX = e.clientX;
                clientY = e.clientY;
            } else {
                // Fallback to element rect
                const rect = element.getBoundingClientRect();
                clientX = rect.left + rect.width / 2;
                clientY = rect.top + rect.height / 2;
            }

            // Offset slightly
            const offsetX = 10;
            const offsetY = 10;

            // Boundary checks (basic)
            if (clientX + 150 > window.innerWidth) clientX -= 150; // Flip left if near edge

            menu.style.left = (clientX + offsetX) + 'px';
            menu.style.top = (clientY + offsetY) + 'px';
            menu.style.display = 'flex';

            document.body.appendChild(menu);

            // Bind data
            menu.dataset.text = text;
            // We need a reference to the element to delete it
            menu.activeElement = element;

            // Click outside to close
            // Use 'mousedown' to close on start of next click, avoiding the 'mouseup' of the long press
            setTimeout(() => {
                document.addEventListener('mousedown', hideBubbleMenuOutside);
                document.addEventListener('touchstart', hideBubbleMenuOutside);
            }, 100);
        }

        function hideBubbleMenu() {
            const menu = document.getElementById('active-bubble-menu');
            if (menu) menu.remove();
            document.removeEventListener('mousedown', hideBubbleMenuOutside);
            document.removeEventListener('touchstart', hideBubbleMenuOutside);
        }

        function hideBubbleMenuOutside(e) {
            const menu = document.getElementById('active-bubble-menu');
            if (menu && !menu.contains(e.target)) {
                hideBubbleMenu();
            }
        }

        function handleBubbleAction(action, menuItem) {
            const menu = document.getElementById('active-bubble-menu');
            if (!menu) return;

            const text = menu.dataset.text;
            const element = menu.activeElement;

            if (action === 'refresh') {
                // User wants to resend this command
                const input = document.getElementById('user-input');
                if (input) {
                    input.value = text;
                    if (window.stopOrSend) {
                        window.stopOrSend(); // Send it
                    } else {
                        console.error('stopOrSend missing');
                    }
                }
            } else if (action === 'delete') {
                // Remove from history
                // Find matching index from end to beginning to match most recent
                for (let i = gameState.history.length - 1; i >= 0; i--) {
                    if (gameState.history[i].role === 'user' && gameState.history[i].content === text) {
                        // Check if next message is from model/assistant
                        if (i + 1 < gameState.history.length) {
                            const nextMsg = gameState.history[i + 1];
                            if (nextMsg.role === 'model' || nextMsg.role === 'assistant') {
                                gameState.history.splice(i + 1, 1); // Delete AI response first
                            }
                        }
                        gameState.history.splice(i, 1); // Delete user message
                        break;
                    }
                }
                // Save state
                db.put('gameState', 'current', gameState);

                // Refresh UI completely to ensure sync
                loadChatHistory();
                showToast('已删除相关的对话记录');
            }

            hideBubbleMenu();
        }

        // [REMOVED] toggleChoices_old_deprecated - replaced by newer version

        async function handleChatFileUpload(input) {
            const file = input.files[0];
            if (!file) return;
            try {
                // If text/doc
                if (file.type.startsWith('text') || file.name.endsWith('.md') || file.name.endsWith('.txt')) {
                    chatFileContent = await file.text();
                    showToast(`文件 "${file.name}" 已读取`);
                } else if (file.name.toLowerCase().endsWith('.docx')) {
                    try {
                        const arrayBuffer = await file.arrayBuffer();
                        const result = await mammoth.extractRawText({ arrayBuffer: arrayBuffer });
                        chatFileContent = result.value;
                        showToast(`文件 "${file.name}" 已读取`);
                    } catch (err) {
                        showToast('Docx 解析失败');
                    }
                } else {
                    showToast('目前仅支持文本和Docx文件');
                }
            } catch (e) {
                showToast('文件读取失败');
            }
        }

        // --- Stop Logic ---
        // Ensure global scope for controller
        window.currentAbortController = null;

        window.stopOrSend = function (event) {
            if (event) {
                event.preventDefault();
                event.stopPropagation();
            }
            console.log('stopOrSend Triggered. Controller:', window.currentAbortController);

            if (window.currentAbortController) {
                // Clicking while generating -> STOP
                console.log('🛑 User requested stop. Aborting...');
                window.currentAbortController.abort();
                window.currentAbortController = null;

                // Remove loading indicator immediately
                const loaders = document.querySelectorAll('.heart-loading');
                loaders.forEach(el => {
                    const parentBubble = el.closest('.message-bubble');
                    if (parentBubble && (parentBubble.classList.contains('bubble-center') || parentBubble.innerHTML.includes('heart-loading'))) {
                        parentBubble.remove();
                    } else if (el.parentElement && el.parentElement.id && el.parentElement.id.startsWith('loading-')) {
                        el.parentElement.remove();
                    } else {
                        el.remove();
                    }
                });

                resetSendButton();
                showToast('已停止');
            } else {
                sendAction();
            }
        };

        // Alias for compatibility if needed
        window.handleSendClick = window.stopOrSend;

        function setSendButtonStop() {
            const btn = document.getElementById('btn-send');
            const iconSend = document.getElementById('icon-send');
            const iconStop = document.getElementById('icon-stop');

            if (iconSend) iconSend.style.display = 'none';
            if (iconStop) iconStop.style.display = 'block';

            // Force redraw/style verify
            if (btn) btn.classList.add('stop-mode');
        }

        function resetSendButton() {
            const btn = document.getElementById('btn-send');
            const iconSend = document.getElementById('icon-send');
            const iconStop = document.getElementById('icon-stop');

            if (iconSend) iconSend.style.display = 'block';
            if (iconStop) iconStop.style.display = 'none';

            if (btn) btn.classList.remove('stop-mode');
        }

        async function sendAction() {
            const input = document.getElementById('user-input');
            let val = input.value;

            if (!val && !chatFileContent) return;

            // Combine text and file
            let finalContent = val;
            if (chatFileContent) {
                finalContent += `\n\n[Attached File Content]:\n${chatFileContent}`;
                chatFileContent = ""; // Reset
                document.getElementById('chat-file-input').value = ""; // Reset input
            }

            addMessageToLog('user', val || "(发送了文件)"); // Show clean text in log
            input.value = '';

            // Clear choices since a manual action is sent
            gameState.choices = [];
            db.put('gameState', 'current', gameState);

            // Close choices
            const drawer = document.getElementById('choice-drawer');
            if (drawer) drawer.classList.remove('open');

            await processAIInteraction(finalContent);
        }

        let lastInteractionInput = null;

        async function processAIInteraction(userInput = null) {
            // Store for retry
            if (userInput !== null) lastInteractionInput = userInput;
            let config = await db.get('settings', 'config');

            // Fallback: If DB is empty, try to get from Settings Inputs (User might have forgot to click Apply)
            if (!config || !config.key) {
                const domUrl = document.getElementById('api-url').value;
                const domKey = document.getElementById('api-key').value;
                if (domUrl && domKey) {
                    config = {
                        url: domUrl,
                        key: domKey,
                        model: document.getElementById('api-model').value,
                        temp: document.getElementById('api-temp').value
                    };
                    // Proactively save it for them
                    db.put('settings', 'config', config);
                }
            }

            if (!config || !config.url || !config.key) {
                addMessageToLog('system', '❌ API未配置，请去设置页配置。');
                return;
            }

            if (userInput) {
                gameState.history.push({ role: 'user', content: userInput });
            }

            const loadingId = 'loading-' + Date.now();
            const timerId = 'timer-' + Date.now();
            let startTime = Date.now();

            addMessageToLog('system', `<div class="home-loading-wrapper" id="${loadingId}">
                <span class="home-heart-loading">
                    <svg viewBox="0 0 24 24" class="home-heart-beat" style="animation-delay:0s"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
                    <svg viewBox="0 0 24 24" class="home-heart-beat" style="animation-delay:0.15s"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
                    <svg viewBox="0 0 24 24" class="home-heart-beat" style="animation-delay:0.3s"><path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z"/></svg>
                </span>
                <div id="${timerId}" class="home-loading-timer">世界加载中... 0s</div>
            </div>`);

            // Start Timer Interval
            const loadingTimerInterval = setInterval(() => {
                const timerEl = document.getElementById(timerId);
                if (timerEl) {
                    const seconds = Math.floor((Date.now() - startTime) / 1000);
                    timerEl.innerText = `世界加载中... ${seconds}s`;
                } else {
                    clearInterval(loadingTimerInterval);
                }
            }, 1000);

            // Start new controller
            window.currentAbortController = new AbortController();
            setSendButtonStop();

            try {
                const fetchUrl = config.url.endsWith('/chat/completions') ? config.url : (config.url.endsWith('/') ? config.url + 'chat/completions' : config.url + '/chat/completions');

                const res = await fetch(fetchUrl, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': 'Bearer ' + config.key
                    },
                    body: JSON.stringify({
                        model: config.model || 'gpt-3.5-turbo',
                        // ✨ Dynamic Context Injection: System Prompt + History Cleaning ✨
                        messages: [
                            { role: 'system', content: buildDynamicSystemPrompt() },
                            ...gameState.history.slice(-15).filter(m => m.role !== 'system').map(m => ({ role: m.role, content: m.content }))
                        ],
                        temperature: parseFloat(config.temp) || 0.7,
                        response_format: { type: "json_object" } // Force JSON if supported
                    }),
                    signal: window.currentAbortController ? window.currentAbortController.signal : null
                });

                const data = await res.json();
                const aiContent = data.choices[0].message.content;

                // Stop Logic: If aborted during await, don't proceed
                if (!window.currentAbortController) return;

                // Remove loading indicator on success
                clearInterval(loadingTimerInterval);
                const loadingEl = document.getElementById(loadingId);
                if (loadingEl) {
                    const parentBubble = loadingEl.closest('.home-message-bubble');
                    if (parentBubble && (parentBubble.classList.contains('home-bubble-center') || parentBubble.innerHTML.includes('home-heart-loading') || parentBubble.innerHTML.includes('home-loading-wrapper'))) {
                        parentBubble.remove();
                    } else {
                        loadingEl.remove();
                    }
                }

                // Parse JSON
                let validJson = null;
                try {
                    validJson = JSON.parse(aiContent);
                } catch (e) {
                    // Try to extract JSON from code block
                    const match = aiContent.match(/```json\n([\s\S]*?)\n```/) || aiContent.match(/\{[\s\S]*\}/);
                    if (match) {
                        try { validJson = JSON.parse(match[0] || match[1]); } catch (z) { }
                    }
                }

                if (validJson) {
                    // Prepare unified HTML
                    let unifiedHtml = '';

                    // 1. World Update Panels
                    const updates = validJson.world_update;
                    if (updates) {

                        // --- Update State ---
                        if (updates.me) {
                            const oldMe = gameState.me;
                            const newMe = updates.me;

                            // 1. Immutable Fields Protection
                            // Name, Avatar (if exists), Gender, Race -> Keep old if present
                            // 1. Name & Avatar Immortality (Lock once set, unless still "玩家")
                            if (oldMe.name && oldMe.name !== '玩家') newMe.name = oldMe.name;
                            if (oldMe.avatar) newMe.avatar = oldMe.avatar;

                            // 1.5 Money Delta Logic (Prevent Random Overwrites)
                            if (typeof newMe.money !== 'undefined') {
                                const oldMoney = parseInt(oldMe.money) || 0;
                                let deltaApplied = false;

                                if (typeof newMe.money === 'string') {
                                    const trimmed = newMe.money.trim();
                                    if (trimmed.startsWith('+') || trimmed.startsWith('-')) {
                                        const delta = parseInt(trimmed) || 0;
                                        newMe.money = oldMoney + delta;
                                        deltaApplied = true;
                                        console.log(`[Money] Delta applied: ${delta} -> New balance: ${newMe.money}`);
                                    }
                                }

                                if (!deltaApplied) {
                                    // If AI sends a number, we treat it as an absolute value for now, 
                                    // but UI/Prompt encourages delta.
                                    newMe.money = parseInt(newMe.money) || 0;
                                }
                            } else {
                                // Keep old money if not sent
                                newMe.money = oldMe.money;
                            }

                            // 2. Profile Deep Merge (Mutable fields)
                            // Age, Identity, Alignment, Race, Gender, etc. are handled below.
                            // Also protect Stats Keys (only update values)
                            // FIXED: Allow AI to set custom stat names (e.g., "气血", "理智") instead of forcing "hp", "mp"
                            if (newMe.stats) {
                                // Check if this is first initialization
                                // We check if me.stats is essentially empty (less than 2 items or default keys)
                                const currentStatKeys = Object.keys(oldMe.stats || {});
                                const isFirstInit = currentStatKeys.length === 0 ||
                                    (currentStatKeys.length <= 2 && (currentStatKeys.includes('hp') || currentStatKeys.includes('mp') || currentStatKeys.includes('气血') || currentStatKeys.includes('理智')));

                                if (isFirstInit) {
                                    // First time: Accept AI's stat names (6-10 suggested by prompt)
                                    // Initial stats are now the source of truth for keys
                                } else {
                                    // Subsequent updates: STRICT Merge
                                    // ONLY update values for keys that ALREADY EXIST in oldMe.stats
                                    // Ignore any new keys suggested by AI
                                    const mergedStats = { ...oldMe.stats };
                                    for (let key in newMe.stats) {
                                        if (mergedStats[key]) {
                                            // Update existing stat values
                                            if (typeof newMe.stats[key] === 'object') {
                                                mergedStats[key].current = newMe.stats[key].current ?? mergedStats[key].current;
                                                mergedStats[key].max = newMe.stats[key].max ?? mergedStats[key].max;
                                                mergedStats[key].status = newMe.stats[key].status ?? mergedStats[key].status;
                                            } else {
                                                mergedStats[key] = newMe.stats[key];
                                            }
                                        }
                                        // ELSE: Key doesn't exist? Silent drop to maintain fixed stat list
                                    }
                                    newMe.stats = mergedStats;
                                }
                            } else if (oldMe.stats) {
                                // If AI doesn't send stats, keep old
                                newMe.stats = oldMe.stats;
                            }

                            // 2. Profile Deep Merge (Mutable fields)
                            // Age, Identity, Alignment, Address, etc.
                            if (newMe.profile) {
                                newMe.profile = { ...(oldMe.profile || {}), ...newMe.profile };
                            }

                            // 3. Diary Append Logic
                            if (newMe.diary && Array.isArray(newMe.diary)) {
                                const oldDiary = Array.isArray(oldMe.diary) ? oldMe.diary : [];
                                // Append new entries to old
                                newMe.diary = [...oldDiary, ...newMe.diary];
                            } else {
                                // If no new diary, keep old
                                newMe.diary = oldMe.diary;
                            }

                            // 4. Other Mutable Lists (Traits, Skills, Inventory, Relationships)
                            // "Can increase/reduce" -> AI sends full new state, so we accept newMe's arrays directly.
                            // If newMe doesn't have them, keep old.
                            if (!newMe.inventory && oldMe.inventory) newMe.inventory = oldMe.inventory;
                            if (!newMe.traits && oldMe.traits) newMe.traits = oldMe.traits;

                            // 5. Skills: HARD LOCK after first initialization
                            // First turn: accept AI's skills (3-6). After that: ONLY update rank/desc/exp of existing skills.
                            // New skill names are SILENTLY DROPPED to prevent skill inflation.
                            if (newMe.skills && Array.isArray(newMe.skills)) {
                                const oldSkills = Array.isArray(oldMe.skills) ? oldMe.skills : [];
                                const isFirstInit = oldSkills.length === 0;

                                if (isFirstInit) {
                                    // First time: Accept AI's skills as the locked set (cap at 6)
                                    newMe.skills = newMe.skills.slice(0, 6).map(s => ({
                                        ...s,
                                        exp: s.exp ?? 0
                                    }));
                                    console.log(`[Skills] Initialized ${newMe.skills.length} skills (locked).`);
                                } else {
                                    // SUBSEQUENT: Only update existing skills, BLOCK all new ones
                                    const mergedSkills = oldSkills.map(oldS => {
                                        const newS = newMe.skills.find(s => s.name === oldS.name);
                                        if (newS) {
                                            // Update rank, description, exp ONLY - name stays locked
                                            return {
                                                ...oldS,
                                                rank: newS.rank ?? oldS.rank,
                                                description: newS.description ?? oldS.description,
                                                exp: newS.exp ?? oldS.exp
                                            };
                                        }
                                        return oldS; // No update from AI, keep as-is
                                    });
                                    // Log dropped skills for debugging
                                    const droppedSkills = newMe.skills.filter(s => !oldSkills.find(o => o.name === s.name));
                                    if (droppedSkills.length > 0) {
                                        console.warn(`[Skills] BLOCKED ${droppedSkills.length} new skill(s):`, droppedSkills.map(s => s.name));
                                    }
                                    newMe.skills = mergedSkills;
                                }
                            } else if (oldMe.skills) {
                                newMe.skills = oldMe.skills;
                            }

                            // FIXED: Incremental Merge for Relationships to prevent overwriting/loss
                            if (newMe.relationships && Array.isArray(newMe.relationships)) {
                                const oldRel = Array.isArray(oldMe.relationships) ? oldMe.relationships : [];
                                const mergedRel = [...oldRel];

                                newMe.relationships.forEach(newR => {
                                    const idx = mergedRel.findIndex(r => r.name === newR.name);
                                    if (idx >= 0) {
                                        // Update existing (merge fields)
                                        mergedRel[idx] = { ...mergedRel[idx], ...newR };
                                    } else {
                                        // Add new
                                        mergedRel.push(newR);
                                    }
                                });
                                newMe.relationships = mergedRel;
                            } else if (oldMe.relationships) {
                                newMe.relationships = oldMe.relationships;
                            }
                            if (!newMe.choices && oldMe.choices) newMe.choices = oldMe.choices;

                            // Apply merged me
                            gameState.me = { ...gameState.me, ...newMe };
                        }
                        if (updates.npcs) {
                            // SAFEGUARD: Ensure gameState.npcs is valid
                            if (!Array.isArray(gameState.npcs)) gameState.npcs = [];

                            updates.npcs.forEach(n => {
                                // Protect Private Chat Memory from Global Updates
                                if (n.memory) delete n.memory;



                                // --- 🛡️ IMPROVEMENT: Force Name-based ID for consistency 🛡️ ---
                                // If ID is numeric (e.g. 1, "1"), try to find existing NPC by Name.
                                // If found, assume AI meant that NPC and use their ID.
                                // If NOT found, and ID is numeric, force ID to be Name (to avoid "1" collision).
                                if (n.name && String(n.id).match(/^\d+$/)) {
                                    const existingByName = gameState.npcs.find(x => x.name === n.name);
                                    if (existingByName) {
                                        // AI sent "1" for "Alice", but we know "Alice" has ID "Alice" (or legacy ID). Use it.
                                        n.id = existingByName.id;
                                    } else {
                                        // New NPC "Bob" with ID "1". Change ID to "Bob".
                                        n.id = n.name;
                                    }
                                }
                                // -------------------------------------------------------------

                                // ROBUST MATCHING: Use String() to avoid '1' !== 1 issues
                                let idx = gameState.npcs.findIndex(old => String(old.id) === String(n.id));

                                if (idx === -1 && n.name) {
                                    // Fallback: Check by Name to prevent visual duplicates if ID changed but name didn't
                                    idx = gameState.npcs.findIndex(old => old.name === n.name);
                                }

                                if (idx >= 0) {
                                    const oldNpc = gameState.npcs[idx];

                                    // Safeguard: Once dead, stay dead.
                                    if (oldNpc.status === '死亡') {
                                        n.status = '死亡';
                                    }

                                    // --- 🛡️ CRITICAL FIX: ID Collision Detection 🛡️ ---
                                    // If ID matches but Name is different (and old name wasn't generic),
                                    // assume AI reused the ID for a different person. Fork it!
                                    const genericNames = ['未知', '神秘人', '陌生人', '黑影', '???', 'Unknown', 'Stranger'];
                                    if (n.name && n.name !== oldNpc.name && !genericNames.includes(oldNpc.name) && !oldNpc.name.includes(n.name) && !n.name.includes(oldNpc.name)) {
                                        console.warn(`[NPC fix] ID Collision detected for ID ${n.id}. Old: ${oldNpc.name}, New: ${n.name}. Treating as NEW NPC.`);
                                        // Generate new ID and treat as new
                                        n.id = n.id + '_' + Date.now() + '_' + Math.floor(Math.random() * 1000);

                                        // Initialize arrays for new NPC
                                        if (!n.diary) n.diary = [];
                                        if (!n.memory) n.memory = [];
                                        n.relationship = n.relationship || 0;

                                        gameState.npcs.push(n);
                                        return; // Skip update for oldNpc
                                    }
                                    // ---------------------------------------------------

                                    // Smart Merge for Arrays (Diary, Memory)
                                    const mergedDiary = [...(oldNpc.diary || [])];
                                    if (n.diary && Array.isArray(n.diary)) {
                                        n.diary.forEach(entry => {
                                            if (!mergedDiary.some(old => old.title === entry.title && old.date === entry.date)) {
                                                mergedDiary.push(entry);
                                            }
                                        });
                                    }

                                    const mergedMemory = [...(oldNpc.memory || [])];
                                    if (n.memory && Array.isArray(n.memory)) {
                                        n.memory.forEach(m => {
                                            if (!mergedMemory.includes(m)) mergedMemory.push(m);
                                        });
                                    }

                                    // Additive Relationship logic
                                    let newRel = (oldNpc.relationship || 0);
                                    if (typeof n.relationship !== 'undefined') {
                                        const delta = parseInt(n.relationship, 10) || 0;
                                        // If it's a small value (likely delta), add it. If it's large and matches old, maybe it's absolute? 
                                        // Logic says "relationship field now represents delta", so we ALWAYS add.
                                        newRel = Math.max(-100, Math.min(100, (oldNpc.relationship || 0) + delta));
                                    }

                                    // NPC Skills: HARD LOCK (same as player)
                                    // If NPC already has skills, only update existing ones
                                    if (n.skills && Array.isArray(n.skills) && oldNpc.skills && oldNpc.skills.length > 0) {
                                        n.skills = oldNpc.skills.map(oldS => {
                                            const newS = n.skills.find(s => s.name === oldS.name);
                                            if (newS) {
                                                return {
                                                    ...oldS,
                                                    rank: newS.rank ?? oldS.rank,
                                                    description: newS.description ?? oldS.description,
                                                    exp: newS.exp ?? oldS.exp
                                                };
                                            }
                                            return oldS;
                                        });
                                    } else if (!n.skills && oldNpc.skills) {
                                        n.skills = oldNpc.skills;
                                    }

                                    // CRITICAL: Merge old properties into new to prevent data loss if AI sends partial update
                                    gameState.npcs[idx] = {
                                        ...oldNpc,   // Base is old data
                                        ...n,        // Overwrite with new data
                                        relationship: newRel, // Use calculated relationship
                                        diary: mergedDiary,   // Use merged diary
                                        memory: mergedMemory, // Use merged memory
                                        id: oldNpc.id // Force preserve original ID
                                    };
                                }
                                else {
                                    // New NPC
                                    // Ensure critical arrays exist
                                    if (!n.diary) n.diary = [];
                                    if (!n.memory) n.memory = [];

                                    // Initial Relationship logic
                                    // If AI sends 0 or undefined, default to 0. 
                                    // But if AI sends a value, use it as starting value.
                                    if (typeof n.relationship !== 'undefined') {
                                        // If it's just created, the "delta" is effectively the starting value (0 + delta)
                                        let val = parseInt(n.relationship, 10) || 0;
                                        n.relationship = Math.max(-100, Math.min(100, val));
                                    } else {
                                        n.relationship = 0;
                                    }

                                    gameState.npcs.push(n);
                                }
                            });
                        }
                        if (updates.locations) {
                            updates.locations.forEach(l => {
                                // --- Fallback: Ensure Connectivity ---
                                // If new location has no connections, connect to current location
                                if (!l.connections || !Array.isArray(l.connections) || l.connections.length === 0) {
                                    const currentLocName = gameState.me.location;
                                    if (currentLocName && currentLocName !== '未知') {
                                        if (!l.connections) l.connections = [];
                                        if (!l.connections.includes(currentLocName)) {
                                            l.connections.push(currentLocName);
                                        }

                                        // Bi-directional: Add new loc to current loc's connections
                                        const currentLocObj = gameState.locations.find(x => x.name === currentLocName);
                                        if (currentLocObj) {
                                            if (!currentLocObj.connections) currentLocObj.connections = [];
                                            if (!currentLocObj.connections.includes(l.name)) {
                                                currentLocObj.connections.push(l.name);
                                            }
                                        }
                                    }
                                }
                                // -------------------------------------

                                const idx = gameState.locations.findIndex(old => old.name === l.name);
                                if (idx >= 0) {
                                    // Immutability Rule: Preserve Name & Tags
                                    const old = gameState.locations[idx];
                                    gameState.locations[idx] = {
                                        ...old,
                                        ...l, // New data overwrites old (default)
                                        name: old.name, // Force keep old Name
                                        tags: (old.tags && old.tags.length > 0) ? old.tags : l.tags // Prioritize old Tags
                                    };
                                }
                                else gameState.locations.push(l);
                            });
                        }

                        // --- Generate Panel HTML (Don't render yet) ---
                        unifiedHtml += renderUpdatePanels(updates);
                    }

                    // 2. Narrative
                    if (validJson.narrative) {
                        // Add some spacing if we have panels above
                        if (unifiedHtml) unifiedHtml += '<div style="margin-top:10px;"></div>';
                        // Transform narrative HTML to use isolated classes
                        let narrativeHtml = validJson.narrative;

                        // --- STYLE ISOLATION FIX ---
                        const scopeId = 'ai-scope-' + Date.now() + '-' + Math.floor(Math.random() * 1000);

                        // 1. Prefix selectors in <style> tags to prevent global leak
                        narrativeHtml = narrativeHtml.replace(/<style>([\s\S]*?)<\/style>/gi, (match, css) => {
                            // Basic regex to prefix selectors. 
                            // It skips @keyframes, @media, percentages, and 'from'/'to'.
                            const scopedCss = css.replace(/([^\r\n,{}]+)(?=\{)/g, (selectors) => {
                                return selectors.split(',').map(sel => {
                                    sel = sel.trim();
                                    if (sel.startsWith('@') || sel.startsWith('from') || sel.startsWith('to') || /^\d+%/.test(sel)) return sel;
                                    return `#${scopeId} ${sel}`;
                                }).join(', ');
                            });
                            return `<style>${scopedCss}
