<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>🎀 粉色打卡 · Hello Kitty 学习日记</title>
    <style>
        /* ============================================================
           全局重置 & 粉色主题
           ============================================================ */
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        body {
            font-family: -apple-system, "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
            background: #fff5f7;
            min-height: 100vh;
            padding: 12px 10px 80px;
            color: #5a3d3d;
        }

        /* ---- 主容器 ---- */
        .app {
            max-width: 900px;
            margin: 0 auto;
        }

        /* ---- 标题栏 ---- */
        .header {
            background: linear-gradient(135deg, #ffb6c9 0%, #ff8fab 50%, #ff6b8a 100%);
            border-radius: 28px 28px 28px 28px;
            padding: 18px 22px 16px;
            margin-bottom: 18px;
            box-shadow: 0 6px 20px rgba(255, 107, 138, 0.30);
            text-align: center;
            position: relative;
            overflow: hidden;
        }
        .header::before {
            content: "🌸 🎀 🐱 🌸 🎀 🐱";
            position: absolute;
            top: -10px;
            right: -10px;
            font-size: 28px;
            opacity: 0.20;
            letter-spacing: 8px;
            white-space: nowrap;
            pointer-events: none;
        }
        .header h1 {
            font-size: 22px;
            font-weight: 700;
            color: #ffffff;
            text-shadow: 0 2px 8px rgba(180, 50, 80, 0.30);
            letter-spacing: 1px;
            position: relative;
            z-index: 1;
        }
        .header h1 .emoji {
            font-size: 26px;
        }
        .header .sub {
            font-size: 12px;
            color: rgba(255, 255, 255, 0.85);
            margin-top: 2px;
            letter-spacing: 2px;
            font-weight: 400;
            position: relative;
            z-index: 1;
        }

        /* ---- 分类栏 ---- */
        .category-bar {
            display: flex;
            flex-wrap: wrap;
            gap: 8px 10px;
            background: #ffffffdd;
            backdrop-filter: blur(4px);
            border-radius: 40px;
            padding: 8px 16px 8px 20px;
            margin-bottom: 16px;
            box-shadow: 0 2px 12px rgba(255, 150, 180, 0.15);
            align-items: center;
            border: 1.5px solid #ffd9e4;
        }
        .category-bar .cat-list {
            display: flex;
            flex-wrap: wrap;
            gap: 6px;
            flex: 1;
        }
        .category-bar .cat-btn {
            padding: 6px 18px;
            border-radius: 30px;
            font-size: 14px;
            font-weight: 600;
            border: 2px solid transparent;
            background: #fff0f3;
            color: #b06a7a;
            cursor: pointer;
            transition: 0.25s;
            font-family: inherit;
        }
        .category-bar .cat-btn:hover {
            background: #ffe2e9;
            transform: scale(1.02);
        }
        .category-bar .cat-btn.active {
            background: #ff6b8a;
            color: #fff;
            border-color: #ff4d7a;
            box-shadow: 0 2px 12px rgba(255, 75, 122, 0.30);
        }
        .category-bar .cat-btn .del-cat {
            margin-left: 6px;
            font-size: 12px;
            opacity: 0.5;
            cursor: pointer;
            transition: 0.2s;
            display: inline-block;
        }
        .category-bar .cat-btn .del-cat:hover {
            opacity: 1;
            color: #d94f6a;
        }
        .category-bar .add-cat-btn {
            padding: 6px 16px;
            border-radius: 30px;
            font-size: 13px;
            font-weight: 600;
            border: 2px dashed #ffb6c9;
            background: transparent;
            color: #b06a7a;
            cursor: pointer;
            transition: 0.25s;
            font-family: inherit;
            white-space: nowrap;
        }
        .category-bar .add-cat-btn:hover {
            background: #ffd9e4;
            border-color: #ff6b8a;
        }

        /* ---- 视图切换 + 日期导航 ---- */
        .view-toolbar {
            display: flex;
            flex-wrap: wrap;
            gap: 10px 16px;
            align-items: center;
            justify-content: space-between;
            background: #ffffffdd;
            backdrop-filter: blur(4px);
            border-radius: 30px;
            padding: 10px 18px;
            margin-bottom: 16px;
            border: 1.5px solid #ffd9e4;
            box-shadow: 0 2px 12px rgba(255, 150, 180, 0.10);
        }
        .view-toolbar .view-group {
            display: flex;
            gap: 4px;
            background: #fff0f3;
            border-radius: 30px;
            padding: 3px;
        }
        .view-toolbar .view-btn {
            padding: 6px 18px;
            border-radius: 30px;
            font-size: 13px;
            font-weight: 600;
            border: none;
            background: transparent;
            color: #b06a7a;
            cursor: pointer;
            transition: 0.25s;
            font-family: inherit;
        }
        .view-toolbar .view-btn.active {
            background: #ff6b8a;
            color: #fff;
            box-shadow: 0 2px 10px rgba(255, 75, 122, 0.25);
        }
        .view-toolbar .view-btn:hover:not(.active) {
            background: #ffe2e9;
        }
        .view-toolbar .nav-group {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .view-toolbar .nav-btn {
            width: 36px;
            height: 36px;
            border-radius: 50%;
            border: 1.5px solid #ffd9e4;
            background: #fff;
            color: #b06a7a;
            font-size: 18px;
            cursor: pointer;
            transition: 0.2s;
            display: flex;
            align-items: center;
            justify-content: center;
            font-family: inherit;
        }
        .view-toolbar .nav-btn:hover {
            background: #ffd9e4;
            border-color: #ff6b8a;
        }
        .view-toolbar .nav-label {
            font-size: 14px;
            font-weight: 600;
            color: #5a3d3d;
            min-width: 120px;
            text-align: center;
        }
        .view-toolbar .nav-label .sub-text {
            font-size: 12px;
            font-weight: 400;
            color: #b06a7a;
        }

        /* ---- 打卡网格 ---- */
        .grid-container {
            background: #ffffff;
            border-radius: 28px;
            padding: 18px 14px 14px;
            box-shadow: 0 4px 20px rgba(255, 150, 180, 0.12);
            border: 1.5px solid #ffd9e4;
            margin-bottom: 16px;
        }
        .grid-container .grid-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 0 4px 12px 4px;
            border-bottom: 2px dashed #ffe2e9;
            margin-bottom: 12px;
        }
        .grid-container .grid-header .title {
            font-size: 15px;
            font-weight: 700;
            color: #5a3d3d;
        }
        .grid-container .grid-header .title .cat-name {
            color: #ff6b8a;
        }
        .grid-container .grid-header .hint {
            font-size: 12px;
            color: #b06a7a;
            background: #fff0f3;
            padding: 2px 14px;
            border-radius: 20px;
        }

        /* 星期头 */
        .weekday-row {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            gap: 6px;
            margin-bottom: 8px;
        }
        .weekday-row .wd {
            text-align: center;
            font-size: 12px;
            font-weight: 600;
            color: #b06a7a;
            padding: 4px 0;
            background: #fff8fa;
            border-radius: 12px;
        }
        .weekday-row .wd.weekend {
            color: #ff6b8a;
        }

        /* 日期卡片网格 */
        .day-grid {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            gap: 6px;
        }
        .day-card {
            aspect-ratio: 1 / 0.9;
            background: #fff8fa;
            border-radius: 16px;
            border: 2px solid #ffecf0;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            cursor: pointer;
            transition: 0.2s;
            padding: 4px 2px;
            position: relative;
            min-height: 64px;
        }
        .day-card:hover {
            border-color: #ffb6c9;
            transform: scale(1.02);
            background: #fff5f7;
        }
        .day-card .day-num {
            font-size: 15px;
            font-weight: 600;
            color: #5a3d3d;
            line-height: 1.2;
        }
        .day-card .day-num.other-month {
            opacity: 0.3;
        }
        .day-card .day-val {
            font-size: 18px;
            font-weight: 700;
            color: #ff6b8a;
            line-height: 1.2;
            margin-top: 2px;
        }
        .day-card .day-val.zero {
            color: #e0c8cc;
            font-weight: 400;
            font-size: 14px;
        }
        .day-card .day-unit {
            font-size: 10px;
            color: #b06a7a;
            font-weight: 400;
        }
        .day-card .cat-tag {
            position: absolute;
            top: 3px;
            right: 6px;
            font-size: 8px;
            color: #ffb6c9;
            background: #fff0f3;
            padding: 0 6px;
            border-radius: 8px;
            opacity: 0;
            transition: 0.2s;
        }
        .day-card:hover .cat-tag {
            opacity: 1;
        }
        .day-card.today {
            border-color: #ff6b8a;
            background: #fff0f5;
            box-shadow: 0 0 0 3px #ffd9e4;
        }
        .day-card.today .day-num {
            color: #ff4d7a;
        }
        .day-card.has-record {
            border-color: #ffb6c9;
        }
        .day-card .kitty-paw {
            font-size: 14px;
            position: absolute;
            bottom: 2px;
            right: 4px;
            opacity: 0.15;
        }

        /* ---- 统计信息 ---- */
        .stats-row {
            display: grid;
            grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
            gap: 10px;
            margin-top: 14px;
            padding-top: 14px;
            border-top: 2px dashed #ffe2e9;
        }
        .stats-row .stat-item {
            text-align: center;
            background: #fff8fa;
            border-radius: 16px;
            padding: 8px 6px;
        }
        .stats-row .stat-item .num {
            font-size: 20px;
            font-weight: 700;
            color: #ff6b8a;
        }
        .stats-row .stat-item .label {
            font-size: 11px;
            color: #b06a7a;
            margin-top: 1px;
        }
        .stats-row .stat-item .num.gold {
            color: #d4a06a;
        }
        .stats-row .stat-item .num.pink {
            color: #ff6b8a;
        }

        /* ---- 月视图专用 ---- */
        .month-grid .day-card {
            aspect-ratio: 1 / 0.85;
            min-height: 52px;
        }
        .month-grid .day-card .day-num {
            font-size: 13px;
        }
        .month-grid .day-card .day-val {
            font-size: 15px;
        }

        /* ---- 模态框 ---- */
        .modal-overlay {
            display: none;
            position: fixed;
            inset: 0;
            background: rgba(90, 50, 60, 0.35);
            backdrop-filter: blur(4px);
            z-index: 999;
            align-items: center;
            justify-content: center;
            padding: 20px;
            animation: fadeIn 0.25s ease;
        }
        .modal-overlay.open {
            display: flex;
        }
        @keyframes fadeIn {
            from { opacity: 0; transform: scale(0.96); }
            to { opacity: 1; transform: scale(1); }
        }
        .modal-box {
            background: #ffffff;
            border-radius: 40px;
            max-width: 380px;
            width: 100%;
            padding: 30px 28px 26px;
            box-shadow: 0 20px 60px rgba(180, 70, 100, 0.25);
            border: 2px solid #ffd9e4;
            position: relative;
            overflow: hidden;
        }
        .modal-box::before {
            content: "🌸 🎀 🐱";
            position: absolute;
            top: -6px;
            right: -6px;
            font-size: 36px;
            opacity: 0.10;
            pointer-events: none;
        }
        .modal-box .modal-title {
            font-size: 18px;
            font-weight: 700;
            color: #5a3d3d;
            margin-bottom: 6px;
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .modal-box .modal-title .cat-badge {
            background: #ff6b8a;
            color: #fff;
            font-size: 12px;
            padding: 2px 14px;
            border-radius: 20px;
            font-weight: 600;
        }
        .modal-box .modal-date {
            font-size: 14px;
            color: #b06a7a;
            margin-bottom: 18px;
        }
        .modal-box .modal-input-group {
            display: flex;
            align-items: center;
            gap: 12px;
            margin-bottom: 20px;
        }
        .modal-box .modal-input-group input {
            flex: 1;
            padding: 12px 16px;
            border-radius: 30px;
            border: 2px solid #ffd9e4;
            font-size: 20px;
            font-weight: 600;
            color: #5a3d3d;
            text-align: center;
            background: #fff8fa;
            font-family: inherit;
            outline: none;
            transition: 0.2s;
            width: 100%;
        }
        .modal-box .modal-input-group input:focus {
            border-color: #ff6b8a;
            box-shadow: 0 0 0 4px #ffd9e4;
        }
        .modal-box .modal-input-group .unit {
            font-size: 16px;
            font-weight: 600;
            color: #b06a7a;
            min-width: 20px;
        }
        .modal-box .modal-hint {
            font-size: 12px;
            color: #b06a7a;
            text-align: center;
            margin-bottom: 16px;
        }
        .modal-box .modal-actions {
            display: flex;
            gap: 10px;
        }
        .modal-box .modal-actions button {
            flex: 1;
            padding: 12px;
            border-radius: 30px;
            font-size: 15px;
            font-weight: 600;
            border: none;
            cursor: pointer;
            transition: 0.2s;
            font-family: inherit;
        }
        .modal-box .modal-actions .btn-cancel {
            background: #f0ebe8;
            color: #7a5a5a;
        }
        .modal-box .modal-actions .btn-cancel:hover {
            background: #e5ddd8;
        }
        .modal-box .modal-actions .btn-save {
            background: #ff6b8a;
            color: #fff;
            box-shadow: 0 4px 14px rgba(255, 75, 122, 0.30);
        }
        .modal-box .modal-actions .btn-save:hover {
            background: #ff4d7a;
            transform: scale(1.02);
        }
        .modal-box .modal-actions .btn-delete {
            background: #fff0f3;
            color: #d94f6a;
            flex: 0.6;
        }
        .modal-box .modal-actions .btn-delete:hover {
            background: #ffe2e9;
        }

        /* ---- Toast ---- */
        .toast {
            position: fixed;
            bottom: 90px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(60, 40, 40, 0.92);
            color: #fff5f7;
            padding: 10px 24px;
            border-radius: 30px;
            font-size: 14px;
            font-weight: 500;
            pointer-events: none;
            opacity: 0;
            transition: opacity 0.3s;
            z-index: 1000;
            backdrop-filter: blur(4px);
            box-shadow: 0 4px 20px rgba(0,0,0,0.15);
        }
        .toast.show {
            opacity: 1;
        }

        /* ---- 底部工具栏 ---- */
        .bottom-tools {
            display: flex;
            flex-wrap: wrap;
            gap: 8px;
            justify-content: center;
            margin-top: 8px;
        }
        .bottom-tools button {
            padding: 8px 18px;
            border-radius: 30px;
            border: 1.5px solid #ffd9e4;
            background: #ffffffdd;
            backdrop-filter: blur(4px);
            font-size: 12px;
            font-weight: 500;
            color: #b06a7a;
            cursor: pointer;
            transition: 0.2s;
            font-family: inherit;
        }
        .bottom-tools button:hover {
            background: #ffd9e4;
            border-color: #ff6b8a;
        }
        .bottom-tools button.danger {
            color: #d94f6a;
        }
        .bottom-tools button.danger:hover {
            background: #ffe2e9;
            border-color: #d94f6a;
        }

        /* ---- 响应式 ---- */
        @media (max-width: 640px) {
            .header h1 { font-size: 18px; }
            .category-bar { padding: 6px 12px; border-radius: 30px; }
            .category-bar .cat-btn { font-size: 12px; padding: 4px 12px; }
            .category-bar .add-cat-btn { font-size: 12px; padding: 4px 12px; }
            .view-toolbar { padding: 8px 12px; }
            .view-toolbar .view-btn { font-size: 12px; padding: 4px 12px; }
            .view-toolbar .nav-label { font-size: 12px; min-width: 80px; }
            .view-toolbar .nav-btn { width: 30px; height: 30px; font-size: 14px; }
            .day-card { min-height: 50px; border-radius: 12px; }
            .day-card .day-num { font-size: 13px; }
            .day-card .day-val { font-size: 15px; }
            .month-grid .day-card { min-height: 44px; }
            .month-grid .day-card .day-num { font-size: 11px; }
            .month-grid .day-card .day-val { font-size: 13px; }
            .grid-container { padding: 12px 8px; }
            .stats-row .stat-item .num { font-size: 16px; }
            .modal-box { padding: 24px 20px; }
            .modal-box .modal-input-group input { font-size: 17px; padding: 10px 12px; }
            .weekday-row .wd { font-size: 10px; }
            .day-grid { gap: 4px; }
            .weekday-row { gap: 4px; }
        }
        @media (max-width: 420px) {
            .day-card { min-height: 40px; border-radius: 10px; }
            .day-card .day-num { font-size: 11px; }
            .day-card .day-val { font-size: 13px; }
            .month-grid .day-card { min-height: 36px; }
            .month-grid .day-card .day-num { font-size: 10px; }
            .month-grid .day-card .day-val { font-size: 11px; }
            .view-toolbar .nav-label { font-size: 10px; min-width: 60px; }
            .header h1 { font-size: 16px; }
            .category-bar .cat-btn { font-size: 11px; padding: 3px 10px; }
        }

        /* ---- 滚动条美化 ---- */
        ::-webkit-scrollbar { width: 4px; height: 4px; }
        ::-webkit-scrollbar-track { background: #fff5f7; }
        ::-webkit-scrollbar-thumb { background: #ffb6c9; border-radius: 10px; }
        ::-webkit-scrollbar-thumb:hover { background: #ff8fab; }

        /* ---- 空状态 ---- */
        .empty-state {
            text-align: center;
            padding: 40px 20px;
            color: #b06a7a;
        }
        .empty-state .big-emoji {
            font-size: 48px;
            display: block;
            margin-bottom: 12px;
        }
        .empty-state .text {
            font-size: 15px;
        }
        .empty-state .sub-text {
            font-size: 13px;
            color: #d0b0b8;
        }

        /* ---- 新增分类输入 ---- */
        .add-cat-input-group {
            display: none;
            margin-top: 8px;
            width: 100%;
            gap: 8px;
            align-items: center;
        }
        .add-cat-input-group.open {
            display: flex;
        }
        .add-cat-input-group input {
            flex: 1;
            padding: 6px 14px;
            border-radius: 30px;
            border: 2px solid #ffd9e4;
            font-size: 14px;
            font-family: inherit;
            outline: none;
            background: #fff;
            transition: 0.2s;
            min-width: 80px;
        }
        .add-cat-input-group input:focus {
            border-color: #ff6b8a;
            box-shadow: 0 0 0 4px #ffd9e4;
        }
        .add-cat-input-group .add-confirm {
            padding: 6px 16px;
            border-radius: 30px;
            border: none;
            background: #ff6b8a;
            color: #fff;
            font-weight: 600;
            font-size: 13px;
            cursor: pointer;
            font-family: inherit;
            white-space: nowrap;
        }
        .add-cat-input-group .add-confirm:hover {
            background: #ff4d7a;
        }
        .add-cat-input-group .add-cancel {
            padding: 6px 14px;
            border-radius: 30px;
            border: 1.5px solid #ffd9e4;
            background: transparent;
            color: #b06a7a;
            font-size: 13px;
            cursor: pointer;
            font-family: inherit;
        }
    </style>
</head>
<body>

    <div class="app" id="app">

        <!-- ============================================================
        标题栏
        ============================================================ -->
        <div class="header">
            <h1>
                <span class="emoji">🎀</span>
                粉色打卡 · Hello Kitty 学习日记
                <span class="emoji">🐱</span>
            </h1>
            <div class="sub">🌸 记录每一天的成长 · 可爱又自律 🌸</div>
        </div>

        <!-- ============================================================
        分类栏
        ============================================================ -->
        <div class="category-bar" id="categoryBar">
            <div class="cat-list" id="catList"></div>
            <button class="add-cat-btn" id="addCatBtn">+ 新增分类</button>
            <div class="add-cat-input-group" id="addCatInputGroup">
                <input type="text" id="newCatInput" placeholder="输入分类名称..." maxlength="12">
                <button class="add-confirm" id="addCatConfirm">✅ 添加</button>
                <button class="add-cancel" id="addCatCancel">取消</button>
            </div>
        </div>

        <!-- ============================================================
        视图工具栏
        ============================================================ -->
        <div class="view-toolbar">
            <div class="view-group">
                <button class="view-btn active" data-view="week" id="viewWeek">📅 周视图</button>
                <button class="view-btn" data-view="month" id="viewMonth">📆 月视图</button>
            </div>
            <div class="nav-group">
                <button class="nav-btn" id="navPrev">‹</button>
                <span class="nav-label" id="navLabel">2027年 第1周</span>
                <button class="nav-btn" id="navNext">›</button>
            </div>
        </div>

        <!-- ============================================================
        打卡网格
        ============================================================ -->
        <div class="grid-container" id="gridContainer">
            <div class="grid-header">
                <div class="title">
                    🎀 <span class="cat-name" id="currentCatName">心理</span> · 打卡记录
                </div>
                <div class="hint">🐱 点击日期修改</div>
            </div>

            <!-- 星期头 -->
            <div class="weekday-row" id="weekdayRow">
                <div class="wd">一</div>
                <div class="wd">二</div>
                <div class="wd">三</div>
                <div class="wd">四</div>
                <div class="wd">五</div>
                <div class="wd weekend">六</div>
                <div class="wd weekend">日</div>
            </div>

            <!-- 日期网格 -->
            <div class="day-grid" id="dayGrid"></div>

            <!-- 统计 -->
            <div class="stats-row" id="statsRow">
                <div class="stat-item">
                    <div class="num" id="statTotal">0</div>
                    <div class="label">📚 总时长</div>
                </div>
                <div class="stat-item">
                    <div class="num" id="statDays">0</div>
                    <div class="label">📅 打卡天数</div>
                </div>
                <div class="stat-item">
                    <div class="num" id="statAvg">0</div>
                    <div class="label">📊 日均</div>
                </div>
                <div class="stat-item">
                    <div class="num gold" id="statMax">0</div>
                    <div class="label">⭐ 单日最高</div>
                </div>
            </div>
        </div>

        <!-- ============================================================
        底部工具
        ============================================================ -->
        <div class="bottom-tools">
            <button id="exportBtn">📤 导出备份</button>
            <button id="importBtn">📥 导入备份</button>
            <button class="danger" id="resetBtn">🔄 重置所有数据</button>
            <input type="file" id="importFile" accept=".json" style="display:none">
        </div>

        <!-- ============================================================
        模态框
        ============================================================ -->
        <div class="modal-overlay" id="modalOverlay">
            <div class="modal-box">
                <div class="modal-title">
                    🎀 打卡 <span class="cat-badge" id="modalCatBadge">心理</span>
                </div>
                <div class="modal-date" id="modalDate">2027年1月1日</div>
                <div class="modal-input-group">
                    <input type="number" id="modalInput" step="0.5" min="0" max="24" placeholder="0">
                    <span class="unit">小时</span>
                </div>
                <div class="modal-hint">💡 输入 0 可清除该天记录</div>
                <div class="modal-actions">
                    <button class="btn-cancel" id="modalCancel">取消</button>
                    <button class="btn-delete" id="modalDelete">🗑️</button>
                    <button class="btn-save" id="modalSave">✅ 保存</button>
                </div>
            </div>
        </div>

        <!-- ============================================================
        Toast
        ============================================================ -->
        <div class="toast" id="toast"></div>

    </div>

    <!-- ============================================================
    JavaScript
    ============================================================ -->
    <script>
        (function() {
            'use strict';

            // ============================================================
            // 1. 数据层
            // ============================================================
            const STORAGE_KEY = 'kitty_checkin_data';

            const DEFAULT_CATEGORIES = ['心理', '命理', '计科'];

            function getDefaultData() {
                return {
                    categories: [...DEFAULT_CATEGORIES],
                    records: {}
                };
            }

            function loadData() {
                try {
                    const raw = localStorage.getItem(STORAGE_KEY);
                    if (raw) {
                        const parsed = JSON.parse(raw);
                        // 确保 categories 存在
                        if (!parsed.categories || !Array.isArray(parsed.categories) || parsed.categories.length === 0) {
                            parsed.categories = [...DEFAULT_CATEGORIES];
                        }
                        // 确保 records 存在
                        if (!parsed.records || typeof parsed.records !== 'object') {
                            parsed.records = {};
                        }
                        // 确保每个分类在 records 中都有对象
                        parsed.categories.forEach(cat => {
                            if (!parsed.records[cat] || typeof parsed.records[cat] !== 'object') {
                                parsed.records[cat] = {};
                            }
                        });
                        return parsed;
                    }
                } catch (e) {
                    console.warn('Load data error:', e);
                }
                const def = getDefaultData();
                def.categories.forEach(cat => { def.records[cat] = {}; });
                saveData(def);
                return def;
            }

            function saveData(data) {
                try {
                    localStorage.setItem(STORAGE_KEY, JSON.stringify(data));
                } catch (e) {
                    console.warn('Save data error:', e);
                }
            }

            let appData = loadData();

            // ============================================================
            // 2. 状态
            // ============================================================
            let currentCategory = appData.categories[0] || '心理';
            let currentView = 'week'; // 'week' | 'month'
            let currentOffset = 0; // 周偏移或月偏移

            // 模态框状态
            let modalTarget = null; // { category, date }

            // ============================================================
            // 3. 工具函数
            // ============================================================
            function formatDate(date) {
                const y = date.getFullYear();
                const m = String(date.getMonth() + 1).padStart(2, '0');
                const d = String(date.getDate()).padStart(2, '0');
                return y + '-' + m + '-' + d;
            }

            function parseDate(str) {
                const parts = str.split('-').map(Number);
                return new Date(parts[0], parts[1] - 1, parts[2]);
            }

            function isSameDay(d1, d2) {
                return d1.getFullYear() === d2.getFullYear() &&
                    d1.getMonth() === d2.getMonth() &&
                    d1.getDate() === d2.getDate();
            }

            function getWeekStart(date) {
                const d = new Date(date);
                const day = d.getDay(); // 0=周日
                const diff = (day === 0 ? 6 : day - 1); // 周一为0
                d.setDate(d.getDate() - diff);
                d.setHours(0, 0, 0, 0);
                return d;
            }

            function getWeekEnd(weekStart) {
                const d = new Date(weekStart);
                d.setDate(d.getDate() + 6);
                return d;
            }

            function getWeekNumber(date) {
                const d = new Date(date);
                const start = new Date(d.getFullYear(), 0, 1);
                const diff = (d - start) / (7 * 24 * 60 * 60 * 1000);
                return Math.ceil(diff);
            }

            function getMonthDays(year, month) {
                return new Date(year, month + 1, 0).getDate();
            }

            function getFirstDayOfMonth(year, month) {
                return new Date(year, month, 1).getDay(); // 0=周日
            }

            function getDateStr(year, month, day) {
                return year + '-' + String(month + 1).padStart(2, '0') + '-' + String(day).padStart(2, '0');
            }

            function getChineseDate(date) {
                const d = new Date(date);
                return d.getFullYear() + '年' + (d.getMonth() + 1) + '月' + d.getDate() + '日';
            }

            function getWeekRangeLabel(weekStart) {
                const start = new Date(weekStart);
                const end = getWeekEnd(start);
                const y1 = start.getFullYear();
                const m1 = start.getMonth() + 1;
                const d1 = start.getDate();
                const y2 = end.getFullYear();
                const m2 = end.getMonth() + 1;
                const d2 = end.getDate();
                if (y1 === y2 && m1 === m2) {
                    return y1 + '年 ' + m1 + '月' + d1 + '日-' + d2 + '日';
                } else if (y1 === y2) {
                    return y1 + '年 ' + m1 + '月' + d1 + '日 - ' + m2 + '月' + d2 + '日';
                } else {
                    return y1 + '年' + m1 + '月' + d1 + '日 - ' + y2 + '年' + m2 + '月' + d2 + '日';
                }
            }

            function getTodayStr() {
                return formatDate(new Date());
            }

            function isToday(dateStr) {
                return dateStr === getTodayStr();
            }

            // ============================================================
            // 4. 渲染函数
            // ============================================================
            function render() {
                renderCategories();
                renderGrid();
                updateStats();
                updateNavLabel();
            }

            function renderCategories() {
                const container = document.getElementById('catList');
                const html = appData.categories.map(cat => {
                    const active = cat === currentCategory ? 'active' : '';
                    const isDefault = DEFAULT_CATEGORIES.includes(cat);
                    const delBtn = isDefault ? '' : `<span class="del-cat" data-cat="${cat}">✕</span>`;
                    return `<button class="cat-btn ${active}" data-cat="${cat}">${cat} ${delBtn}</button>`;
                }).join('');
                container.innerHTML = html;

                // 事件绑定
                container.querySelectorAll('.cat-btn').forEach(btn => {
                    btn.addEventListener('click', function(e) {
                        if (e.target.classList.contains('del-cat')) return;
                        const cat = this.dataset.cat;
                        if (cat && cat !== currentCategory) {
                            currentCategory = cat;
                            currentOffset = 0;
                            render();
                        }
                    });
                    // 删除
                    const del = btn.querySelector('.del-cat');
                    if (del) {
                        del.addEventListener('click', function(e) {
                            e.stopPropagation();
                            const cat = this.dataset.cat;
                            if (confirm(`确定要删除分类「${cat}」及其所有打卡数据吗?`)) {
                                delete appData.records[cat];
                                appData.categories = appData.categories.filter(c => c !== cat);
                                if (currentCategory === cat) {
                                    currentCategory = appData.categories[0] || '心理';
                                }
                                saveData(appData);
                                render();
                                showToast('🗑️ 已删除「' + cat + '」');
                            }
                        });
                    }
                });
            }

            function renderGrid() {
                const grid = document.getElementById('dayGrid');
                const catName = document.getElementById('currentCatName');
                catName.textContent = currentCategory;

                if (currentView === 'week') {
                    renderWeekView(grid);
                } else {
                    renderMonthView(grid);
                }
            }

            function renderWeekView(grid) {
                const today = new Date();
                const baseDate = new Date(today);
                baseDate.setDate(baseDate.getDate() + currentOffset * 7);
                const weekStart = getWeekStart(baseDate);
                const weekEnd = getWeekEnd(weekStart);

                let html = '';
                const days = [];
                for (let i = 0; i < 7; i++) {
                    const d = new Date(weekStart);
                    d.setDate(d.getDate() + i);
                    days.push(d);
                }

                const records = appData.records[currentCategory] || {};

                days.forEach(d => {
                    const dateStr = formatDate(d);
                    const val = records[dateStr] || 0;
                    const isTodayFlag = isToday(dateStr);
                    const hasRecord = val > 0;
                    const dayNum = d.getDate();
                    const isWeekend = d.getDay() === 0 || d.getDay() === 6;

                    html += `
                        <div class="day-card ${isTodayFlag ? 'today' : ''} ${hasRecord ? 'has-record' : ''}"
                             data-date="${dateStr}"
                             data-value="${val}"
                             onclick="openModal('${dateStr}')">
                            <div class="day-num">${dayNum}</div>
                            <div class="day-val ${val === 0 ? 'zero' : ''}">${val === 0 ? '-' : val}</div>
                            <div class="day-unit">${val > 0 ? 'h' : ''}</div>
                            ${isTodayFlag ? '<div class="kitty-paw">🐱</div>' : ''}
                        </div>
                    `;
                });

                grid.innerHTML = html;
                grid.classList.remove('month-grid');
            }

            function renderMonthView(grid) {
                const today = new Date();
                const baseDate = new Date(today.getFullYear(), today.getMonth(), 1);
                baseDate.setMonth(baseDate.getMonth() + currentOffset);
                const year = baseDate.getFullYear();
                const month = baseDate.getMonth();
                const daysInMonth = getMonthDays(year, month);
                const firstDay = getFirstDayOfMonth(year, month); // 0=周日

                const records = appData.records[currentCategory] || {};

                let html = '';
                // 补齐前面的空格(周日为0,周一为1... 我们要从周一开始,所以偏移)
                // 如果 firstDay = 0 (周日),前面补6格;否则补 firstDay - 1 格
                let leadingEmpty = firstDay === 0 ? 6 : firstDay - 1;
                for (let i = 0; i < leadingEmpty; i++) {
                    html += `<div class="day-card" style="background:transparent;border-color:transparent;cursor:default;min-height:44px;"></div>`;
                }

                for (let d = 1; d <= daysInMonth; d++) {
                    const dateStr = getDateStr(year, month, d);
                    const val = records[dateStr] || 0;
                    const isTodayFlag = isToday(dateStr);
                    const hasRecord = val > 0;
                    const isWeekend = (new Date(year, month, d).getDay() === 0 || new Date(year, month, d).getDay() === 6);

                    html += `
                        <div class="day-card ${isTodayFlag ? 'today' : ''} ${hasRecord ? 'has-record' : ''}"
                             data-date="${dateStr}"
                             data-value="${val}"
                             onclick="openModal('${dateStr}')">
                            <div class="day-num">${d}</div>
                            <div class="day-val ${val === 0 ? 'zero' : ''}">${val === 0 ? '-' : val}</div>
                            ${val > 0 ? '<div class="day-unit">h</div>' : ''}
                            ${isTodayFlag ? '<div class="kitty-paw">🐱</div>' : ''}
                        </div>
                    `;
                }

                grid.innerHTML = html;
                grid.classList.add('month-grid');
            }

            function updateStats() {
                const records = appData.records[currentCategory] || {};
                let total = 0;
                let days = 0;
                let maxVal = 0;
                let dateCount = 0;

                // 获取当前视图的日期范围
                const today = new Date();
                let startDate, endDate;

                if (currentView === 'week') {
                    const baseDate = new Date(today);
                    baseDate.setDate(baseDate.getDate() + currentOffset * 7);
                    const weekStart = getWeekStart(baseDate);
                    const weekEnd = getWeekEnd(weekStart);
                    startDate = weekStart;
                    endDate = weekEnd;
                } else {
                    const baseDate = new Date(today.getFullYear(), today.getMonth(), 1);
                    baseDate.setMonth(baseDate.getMonth() + currentOffset);
                    startDate = new Date(baseDate.getFullYear(), baseDate.getMonth(), 1);
                    endDate = new Date(baseDate.getFullYear(), baseDate.getMonth() + 1, 0);
                }

                // 遍历日期范围内的记录
                const current = new Date(startDate);
                while (current <= endDate) {
                    const dateStr = formatDate(current);
                    const val = records[dateStr] || 0;
                    if (val > 0) {
                        total += val;
                        days++;
                        dateCount++;
                        if (val > maxVal) maxVal = val;
                    }
                    current.setDate(current.getDate() + 1);
                }

                const avg = days > 0 ? (total / days) : 0;

                document.getElementById('statTotal').textContent = total.toFixed(1);
                document.getElementById('statDays').textContent = days;
                document.getElementById('statAvg').textContent = avg.toFixed(1);
                document.getElementById('statMax').textContent = maxVal.toFixed(1);
            }

            function updateNavLabel() {
                const label = document.getElementById('navLabel');
                const today = new Date();

                if (currentView === 'week') {
                    const baseDate = new Date(today);
                    baseDate.setDate(baseDate.getDate() + currentOffset * 7);
                    const weekStart = getWeekStart(baseDate);
                    const weekNum = getWeekNumber(weekStart);
                    const range = getWeekRangeLabel(weekStart);
                    label.innerHTML = `${weekStart.getFullYear()}年 第${weekNum}周 <span class="sub-text">${range}</span>`;
                } else {
                    const baseDate = new Date(today.getFullYear(), today.getMonth(), 1);
                    baseDate.setMonth(baseDate.getMonth() + currentOffset);
                    const year = baseDate.getFullYear();
                    const month = baseDate.getMonth() + 1;
                    const daysInMonth = getMonthDays(baseDate.getFullYear(), baseDate.getMonth());
                    label.innerHTML = `${year}年 ${month}月 <span class="sub-text">共${daysInMonth}天</span>`;
                }
            }

            // ============================================================
            // 5. 模态框
            // ============================================================
            window.openModal = function(dateStr) {
                modalTarget = {
                    category: currentCategory,
                    date: dateStr
                };

                const records = appData.records[currentCategory] || {};
                const val = records[dateStr] || 0;

                document.getElementById('modalCatBadge').textContent = currentCategory;
                document.getElementById('modalDate').textContent = getChineseDate(parseDate(dateStr));
                document.getElementById('modalInput').value = val;

                document.getElementById('modalOverlay').classList.add('open');
                setTimeout(() => {
                    document.getElementById('modalInput').focus();
                    document.getElementById('modalInput').select();
                }, 100);
            };

            function closeModal() {
                document.getElementById('modalOverlay').classList.remove('open');
                modalTarget = null;
            }

            function saveModal() {
                if (!modalTarget) return;
                const input = document.getElementById('modalInput');
                let val = parseFloat(input.value);
                if (isNaN(val) || val < 0) val = 0;
                if (val > 24) val = 24;

                const category = modalTarget.category;
                const dateStr = modalTarget.date;

                if (!appData.records[category]) appData.records[category] = {};

                if (val === 0) {
                    delete appData.records[category][dateStr];
                } else {
                    appData.records[category][dateStr] = val;
                }

                saveData(appData);
                closeModal();
                render();
                showToast(`✅ ${category} · ${dateStr} 已记录 ${val > 0 ? val + 'h' : '已清除'}`);
            }

            function deleteModalRecord() {
                if (!modalTarget) return;
                const category = modalTarget.category;
                const dateStr = modalTarget.date;

                if (appData.records[category] && appData.records[category][dateStr]) {
                    delete appData.records[category][dateStr];
                    saveData(appData);
                    closeModal();
                    render();
                    showToast(`🗑️ 已清除 ${dateStr} 的记录`);
                } else {
                    closeModal();
                }
            }

            // 模态框事件绑定
            document.getElementById('modalOverlay').addEventListener('click', function(e) {
                if (e.target === this) closeModal();
            });
            document.getElementById('modalCancel').addEventListener('click', closeModal);
            document.getElementById('modalSave').addEventListener('click', saveModal);
            document.getElementById('modalDelete').addEventListener('click', deleteModalRecord);
            document.getElementById('modalInput').addEventListener('keydown', function(e) {
                if (e.key === 'Enter') saveModal();
                if (e.key === 'Escape') closeModal();
            });

            // ============================================================
            // 6. 视图切换 & 导航
            // ============================================================
            document.getElementById('viewWeek').addEventListener('click', function() {
                if (currentView === 'week') return;
                currentView = 'week';
                currentOffset = 0;
                document.getElementById('viewWeek').classList.add('active');
                document.getElementById('viewMonth').classList.remove('active');
                render();
            });

            document.getElementById('viewMonth').addEventListener('click', function() {
                if (currentView === 'month') return;
                currentView = 'month';
                currentOffset = 0;
                document.getElementById('viewMonth').classList.add('active');
                document.getElementById('viewWeek').classList.remove('active');
                render();
            });

            document.getElementById('navPrev').addEventListener('click', function() {
                if (currentView === 'week') {
                    currentOffset--;
                } else {
                    currentOffset--;
                }
                render();
            });

            document.getElementById('navNext').addEventListener('click', function() {
                if (currentView === 'week') {
                    currentOffset++;
                } else {
                    currentOffset++;
                }
                render();
            });

            // ============================================================
            // 7. 分类管理
            // ============================================================
            document.getElementById('addCatBtn').addEventListener('click', function() {
                const group = document.getElementById('addCatInputGroup');
                const input = document.getElementById('newCatInput');
                if (group.classList.contains('open')) {
                    group.classList.remove('open');
                    input.value = '';
                    return;
                }
                group.classList.add('open');
                setTimeout(() => input.focus(), 50);
            });

            document.getElementById('addCatCancel').addEventListener('click', function() {
                document.getElementById('addCatInputGroup').classList.remove('open');
                document.getElementById('newCatInput').value = '';
            });

            document.getElementById('addCatConfirm').addEventListener('click', function() {
                const input = document.getElementById('newCatInput');
                const name = input.value.trim();
                if (!name) {
                    showToast('⚠️ 请输入分类名称');
                    return;
                }
                if (appData.categories.includes(name)) {
                    showToast('⚠️ 分类已存在');
                    return;
                }
                appData.categories.push(name);
                appData.records[name] = {};
                saveData(appData);
                currentCategory = name;
                currentOffset = 0;
                document.getElementById('addCatInputGroup').classList.remove('open');
                input.value = '';
                render();
                showToast('✅ 已新增分类「' + name + '」');
            });

            document.getElementById('newCatInput').addEventListener('keydown', function(e) {
                if (e.key === 'Enter') document.getElementById('addCatConfirm').click();
                if (e.key === 'Escape') document.getElementById('addCatCancel').click();
            });

            // ============================================================
            // 8. 导出 / 导入 / 重置
            // ============================================================
            document.getElementById('exportBtn').addEventListener('click', function() {
                try {
                    const json = JSON.stringify(appData, null, 2);
                    const blob = new Blob([json], { type: 'application/json' });
                    const url = URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = `kitty打卡_${new Date().toISOString().slice(0,10)}.json`;
                    a.click();
                    URL.revokeObjectURL(url);
                    showToast('📤 导出成功');
                } catch (e) {
                    showToast('❌ 导出失败');
                }
            });

            document.getElementById('importBtn').addEventListener('click', function() {
                document.getElementById('importFile').click();
            });

            document.getElementById('importFile').addEventListener('change', function(e) {
                const file = e.target.files[0];
                if (!file) return;
                const reader = new FileReader();
                reader.onload = function(ev) {
                    try {
                        const imported = JSON.parse(ev.target.result);
                        if (!imported.categories || !Array.isArray(imported.categories) || !imported.records) {
                            showToast('❌ 文件格式不匹配');
                            return;
                        }
                        // 确保所有分类都有记录对象
                        imported.categories.forEach(cat => {
                            if (!imported.records[cat]) imported.records[cat] = {};
                        });
                        appData = imported;
                        if (!appData.categories.includes(currentCategory)) {
                            currentCategory = appData.categories[0] || '心理';
                        }
                        saveData(appData);
                        render();
                        showToast('✅ 导入成功');
                    } catch (err) {
                        showToast('❌ 文件解析失败');
                    }
                };
                reader.readAsText(file);
                this.value = '';
            });

            document.getElementById('resetBtn').addEventListener('click', function() {
                if (!confirm('⚠️ 确定要重置所有数据吗?此操作不可恢复!')) return;
                if (!confirm('🐱 再确认一次:所有打卡数据将被清除!')) return;
                appData = getDefaultData();
                appData.categories.forEach(cat => { appData.records[cat] = {}; });
                currentCategory = appData.categories[0] || '心理';
                currentOffset = 0;
                saveData(appData);
                render();
                showToast('🔄 已重置所有数据');
            });

            // ============================================================
            // 9. Toast
            // ============================================================
            function showToast(msg) {
                const el = document.getElementById('toast');
                el.textContent = msg;
                el.classList.add('show');
                clearTimeout(el._timer);
                el._timer = setTimeout(() => el.classList.remove('show'), 2200);
            }

            // 暴露 showToast 给全局
            window.showToast = showToast;

            // ============================================================
            // 10. 启动
            // ============================================================
            // 确保当前分类存在
            if (!appData.categories.includes(currentCategory)) {
                currentCategory = appData.categories[0] || '心理';
            }
            render();

            // 窗口关闭前保存
            window.addEventListener('beforeunload', function() {
                saveData(appData);
            });

        })();
    </script>

</body>
</html>