<template>
  <view class="fortune-wrap">
    <view class="bg">
      <view class="bg-base" />
      <view class="bg-texture" />
      <view class="bg-glow bg-glow-a" />
      <view class="bg-glow bg-glow-b" />
      <view v-for="star in stars" :key="star.id" class="bg-star" :style="{
          top: star.top,
          left: star.left,
          width: star.size + 'px',
          height: star.size + 'px',
          animationDuration: star.duration + 's',
          animationDelay: star.delay + 's'
      }" />
    </view>

    <view class="header">
      <view class="header-left" @click="emit('back')">
        <text class="header-back">‹</text>
      </view>
      <view class="header-mid">
        <text class="header-title">财运解析详情</text>
        <text class="header-subtitle">Master's Wealth Report</text>
      </view>
      <view class="header-right">
        <view class="header-icon" @click="handleDownloadPdf">
          <text class="header-icon-text">↓</text>
        </view>
      </view>
    </view>

    <scroll-view scroll-x class="tabs" show-scrollbar="false">
      <view class="tabs-inner">
        <view v-for="t in tabList" :key="t.id" class="tab" :class="{ active: activeTab === t.id }"
          @click="activeTab = t.id">
          <view class="tab-icon-wrap">
            <text class="tab-icon">{{ t.icon }}</text>
            <view v-if="t.locked" class="tab-lock-dot"><text class="tab-lock-dot-text">锁</text></view>
          </view>
          <text class="tab-label">{{ t.label }}</text>
          <view v-if="activeTab === t.id" class="tab-indicator" />
        </view>
      </view>
    </scroll-view>

    <scroll-view scroll-y class="content">
      <view ref="pdfRootRef" class="content-inner">
        <view v-if="activeTab === 'destiny'" class="stack">
          <view class="card">
            <view class="card-watermark"><text class="wm">人</text></view>
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">基础信息解析</text>
                <text class="sst">Basic Information</text>
              </view>
            </view>

            <view class="grid-2 gap-2 mb-3">
              <view class="row-wide">
                <text class="muted">命造</text>
                <text class="gold serif bold">{{ basicInfo.mingzao || '-' }}</text>
              </view>
              <view class="info-row"><text class="muted">真太阳时</text><text class="serif">{{ basicInfo.zhenTaiyang || '-' }}</text></view>
              <view class="info-row"><text class="muted">农历生辰</text><text class="serif">{{ basicInfo.nongli || '-' }}</text></view>
            </view>

            <view class="box-dark grid-2 gap-2">
              <view>
                <text class="tiny muted">胎元</text>
                <text class="serif">-</text>
              </view>
              <view>
                <text class="tiny muted">命宫</text>
                <text class="serif">-</text>
              </view>
              <view>
                <text class="tiny muted">空亡</text>
                <text class="serif">-</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">前世今生因果</text>
                <text class="sst">Karma & Soul</text>
              </view>
            </view>
            <view class="karma">
              <view class="karma-item">
                <view class="karma-icon"><text class="karma-icon-text">史</text></view>
                <view class="col flex-1">
                  <text class="bold">前世印记</text>
                  <text class="micro muted lh">
                    {{ karmaInfo.qianshi || '-' }}
                  </text>
                </view>
              </view>
              <view class="karma-item">
                <view class="karma-icon karma-icon-gold"><text class="karma-icon-text">灯</text></view>
                <view class="col flex-1">
                  <text class="bold">今生课题</text>
                  <text class="micro muted lh">
                    {{ karmaInfo.jinsheng || '-' }}
                  </text>
                </view>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">八字排盘</text>
                <text class="sst">Four Pillars Chart</text>
              </view>
            </view>

            <view class="bazi-grid">
              <view v-for="p in baziPillars" :key="p.title" class="bazi-pillar" :class="{ active: !!p.active }">
                <text class="tiny muted mt-2">{{ p.title }}</text>
                <text class="tiny gold serif bold">{{ p.god }}</text>
                <view class="gan-circle"><text class="gan-text">{{ p.gan }}</text></view>
                <view class="zhi-square"><text class="gan-text">{{ p.zhi }}</text></view>
                <view class="hidden-list">
                  <text v-for="(h, i) in p.hidden" :key="i" class="hidden-item">{{ h }}</text>
                </view>
                <view class="stage"><text class="tiny muted">{{ p.stage }}</text></view>
              </view>
            </view>
          </view>

          <view class="card card-grad">
            <view class="card-watermark"><text class="wm">冠</text></view>
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">命格层次</text>
                <text class="sst">Destiny Grade</text>
              </view>
            </view>
            <view class="row gap-3">
              <view class="grade-badge">
                <text class="tiny muted">等级</text>
                <text class="grade-text serif">上等</text>
              </view>
              <view class="col flex-1">
                <text class="bold">{{ minggeInfo || '-' }}</text>
                <text class="tiny muted lh">{{ dashiPizhu || '-' }}</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="card-watermark"><text class="wm">卷</text></view>
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">古籍断语</text>
                <text class="sst">Classical Text</text>
              </view>
            </view>
            <view class="poem">
              <text class="poem-line">{{ gujiInfo || '-' }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">奇门遁甲排盘</text>
                <text class="sst">Qi Men Dun Jia</text>
              </view>
            </view>

            <view class="qimen-box">
              <view v-if="qimenCells.length" class="qimen-grid">
                <view v-for="(cell, ci) in qimenCells" :key="ci" class="qimen-cell">
                  <view v-if="cell.center" class="qimen-center">
                    <text class="qimen-center-title serif">中宫</text>
                    <text class="qimen-center-sub">{{ Array.isArray(cell.stems) ? cell.stems.join('/') : '' }}</text>
                  </view>
                  <view v-else class="qimen-cell-inner">
                    <text class="qimen-loc">{{ cell.location }}</text>
                    <view class="qimen-top">
                      <text class="qimen-spirit serif">{{ cell.spirit }}</text>
                      <view class="qimen-stems">
                        <text v-for="(s, si) in (cell.stems || [])" :key="si" class="qimen-stem serif"
                          :class="{ gold: si === 0 }">
                          {{ s }}
                        </text>
                      </view>
                    </view>
                    <view class="qimen-bottom">
                      <text class="qimen-star serif">{{ cell.star }}</text>
                      <text class="qimen-door serif"
                        :class="{ 'qimen-door-active': cell.door && cell.door.indexOf('门') !== -1 }">
                        {{ cell.door }}
                      </text>
                    </view>
                    <text class="qimen-gong">{{ cell.gong }}</text>
                  </view>
                </view>
              </view>
              <view v-else class="box-dark">
                <text class="micro muted lh">{{ qimenInfo.paipan || '-' }}</text>
              </view>
            </view>

            <view class="qimen-analysis">
              <view class="qimen-analysis-title">
                <text class="qimen-analysis-title-text">奇门格局判词</text>
              </view>
              <view class="qimen-analysis-body">
                <text class="qimen-p">{{ qimenInfo.geju || '-' }}</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">前世今生因果</text>
                <text class="sst">Karma & Soul</text>
              </view>
            </view>
            <view class="karma">
              <view class="karma-item">
                <view class="karma-icon"><text class="karma-icon-text">史</text></view>
                <view class="col flex-1">
                  <text class="bold">前世印记</text>
                  <text class="micro muted lh">
                    {{ karmaInfo.qianshi || '-' }}
                  </text>
                </view>
              </view>
              <view class="karma-item">
                <view class="karma-icon karma-icon-gold"><text class="karma-icon-text">灯</text></view>
                <view class="col flex-1">
                  <text class="bold">今生课题</text>
                  <text class="micro muted lh">
                    {{ karmaInfo.jinsheng || '-' }}
                  </text>
                </view>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">五行能量与格局</text>
                <text class="sst">Five Elements & Pattern</text>
              </view>
            </view>
            <view class="scorebar" v-for="(s, i) in elementScores" :key="i">
              <view class="scorebar-head">
                <text class="serif">{{ s.label }}</text>
                <text class="mono muted">{{ s.score }}</text>
              </view>
              <view class="scorebar-track">
                <view class="scorebar-fill" :style="{ width: s.width, backgroundColor: s.color }" />
              </view>
            </view>
            <view class="divider" />
            <view class="note">
              <text class="gold bold">大师批注：</text>
                <text class="tiny muted">{{ dashiPizhu || '-' }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">五行开运指南</text>
                <text class="sst">Actionable Advice</text>
              </view>
            </view>
            <view class="table">
              <view class="tr th">
                <text class="tc muted">项目</text>
                <text class="tc gold">喜神 (宜)</text>
                <text class="tc red">忌神 (忌)</text>
              </view>
              <view v-for="(r, i) in elementAdvice" :key="i" class="tr">
                <text class="tc muted">{{ r.k }}</text>
                <text class="tc">{{ r.good }}</text>
                <text class="tc">{{ r.bad }}</text>
              </view>
            </view>
            <text class="foot">* 建议日常穿搭、选房、选号优先参考“喜神”建议。</text>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">十神性格盲点</text>
                <text class="sst">Personality & Blind Spots</text>
              </view>
            </view>
            <view class="grid-2 gap-2">
              <view class="box-left gold-left">
                <text class="bold">主性格分析</text>
                <text class="tiny muted lh">{{ shishenMangdian || '-' }}</text>
              </view>
              <view class="box-left red-left">
                <text class="bold">潜在盲点</text>
                <text class="tiny muted lh">{{ shishenMangdian || '-' }}</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">事业财运定数</text>
                <text class="sst">Career Path</text>
              </view>
            </view>
            <view class="grid-2 gap-2">
              <view class="box">
                <text class="tiny muted">最佳行业 (喜用神: 土/金)</text>
                <view class="chips">
                  <text v-for="(c, i) in industryChips" :key="i" class="chip">{{ c }}</text>
                </view>
              </view>
              <view class="box">
                <text class="tiny muted">财富层次</text>
                <text class="bold">{{ wealthAnalysisData?.wealth_level || '-' }}</text>
                <text class="micro">{{ shiyeCaiyun || '-' }}</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">婚姻情感剖析</text>
                <text class="sst">Love & Marriage</text>
              </view>
            </view>
            <view class="kv">
              <view class="kv-row"><text class="muted">婚姻主题</text><text>{{ hunyinQinggan || '-' }}</text></view>
              <view class="kv-row"><text class="muted">情感建议</text><text>{{ hunyinQinggan || '-' }}</text></view>
              <view class="box-dark">
                <text class="gold bold">大师批注：</text>
                <text class="tiny muted">{{ hunyinQinggan || '-' }}</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">五行健康易患</text>
                <text class="sst">Health Risks</text>
              </view>
            </view>
            <view class="box-dark kv-row"><text class="muted">健康提示</text><text>{{ wuxingJiankang || '-' }}</text></view>
          </view>

          <view class="card">
            <view class="row radar">
              <view class="radar-left">
                <svg viewBox="0 0 100 100" class="radar-svg">
                  <polygon points="50,10 90,40 75,90 25,90 10,40" fill="none" stroke="#333" stroke-width="0.5" />
                  <polygon points="50,25 75,45 65,80 35,80 25,45" fill="none" stroke="#333" stroke-width="0.5" />
                  <polygon points="50,5 88,38 73,92 27,92 12,38" fill="rgba(212,175,55,0.15)" stroke="#d4af37"
                    stroke-width="1.5" />
                  <circle cx="50" cy="5" r="1.5" fill="#d4af37" />
                  <circle cx="88" cy="38" r="1.5" fill="#d4af37" />
                  <circle cx="73" cy="92" r="1.5" fill="#d4af37" />
                  <circle cx="27" cy="92" r="1.5" fill="#d4af37" />
                  <circle cx="12" cy="38" r="1.5" fill="#d4af37" />
                </svg>
              </view>
              <view class="radar-right">
                <view v-for="(b, i) in wealthBars" :key="i" class="bar-item">
                  <view class="bar-head">
                    <text class="micro muted">{{ b.label }}</text>
                    <text class="micro" :class="b.cls">{{ b.score }}分</text>
                  </view>
                  <view class="bar-track">
                    <view class="bar-fill" :style="{ width: b.width, backgroundColor: b.color }" />
                  </view>
                </view>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">神煞贵人</text>
                <text class="sst">Stars & Spirits</text>
              </view>
            </view>
            <view class="grid-2 gap-2">
              <view v-for="(s, i) in shenSha" :key="i" class="star-item">
                <text class="bold">{{ s.name }}</text>
                <text class="tag" :class="s.level === '吉' ? 'tag-good' : 'tag-mid'">{{ s.pos }}</text>
              </view>
            </view>
          </view>
        </view>

        <view v-else-if="activeTab === 'year'" class="stack">
          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">大运走势</text>
                <text class="sst">Luck Cycles</text>
              </view>
            </view>
            <view class="timeline">
              <view v-for="(d, i) in dayun" :key="i" class="timeline-item" :class="{ current: d.current }">
                <view class="dot" :class="{ on: d.current }" />
                <view class="timeline-body">
                  <view class="timeline-head">
                    <text class="tl-name serif" :class="{ gold: d.current }">{{ d.name }}大运</text>
                    <text class="pill">{{ d.range }}</text>
                  </view>
                  <text class="tiny muted lh">{{ d.desc }}</text>
                  <text v-if="d.current" class="current-tag">当前行运</text>
                </view>
              </view>
            </view>
          </view>

          <view class="card card-grad border-gold">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">太岁将军批语</text>
                <text class="sst">Tai Sui General</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="tiny muted lh">{{ taisuiText }}</text>
            </view>
          </view>

          <view class="card card-grad border-gold">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">{{ currentYearLabel }} 流年详解</text>
                <text class="sst">{{ currentYearLabel }} Annual Analysis</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="tiny muted lh">{{ annualText }}</text>
            </view>

            <view class="annual-chart">
              <view class="row annual-chart-head">
                <text class="gold bold">{{ currentYearLabel }} 财运起伏</text>
              </view>
              <view class="annual-bars">
                <view v-for="(h, i) in annualBars" :key="i" class="annual-bar">
                  <view class="annual-bar-fill" :style="{ height: h + '%' }" />
                </view>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">流年神煞盘</text>
                <text class="sst">Annual Stars</text>
              </view>
            </view>
            <view class="grid-4 gap-1">
              <view v-for="(s, i) in yearlyStars" :key="i" class="star-chip" :class="{ bad: s.bad }">
                <text class="micro">{{ s.name }}</text>
              </view>
            </view>
            <view class="note mt-2">
              <text class="micro muted lh">{{ liunianStarsText }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">分人群转运指引</text>
                <text class="sst">Advice by Role</text>
              </view>
            </view>
            <view class="role-list">
              <view v-for="(r, i) in roleAdvice" :key="i" class="role-item"
                :class="{ last: i === roleAdvice.length - 1 }">
                <view class="role-icon" :style="{ background: r.bg, color: r.color }"><text class="micro">{{ r.icon
                }}</text></view>
                <view class="col flex-1">
                  <text class="bold">{{ r.title }}</text>
                  <text class="micro muted lh">{{ r.desc }}</text>
                </view>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">流年吉凶方位</text>
                <text class="sst">Lucky Directions</text>
              </view>
            </view>
            <view class="grid-2 gap-2">
              <view class="box">
                <text class="gold bold">大利方位</text>
                <text class="micro">{{ liunianLuckyDirs }}</text>
              </view>
              <view class="box">
                <text class="red bold">不利方位</text>
                <text class="micro">{{ liunianBadDirs }}</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">财富来源预测</text>
                <text class="sst">Income Sources</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="tiny muted lh">{{ incomeSourcesText }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">投资领域吉凶指引</text>
                <text class="sst">Investment Guide</text>
              </view>
            </view>
            <view v-if="investmentEntries.length" class="grid-2 gap-2">
              <view v-for="(it, idx) in investmentEntries" :key="idx" class="box-i good">
                <text class="bold" style="color:#4ade80">{{ it.label }}</text>
                <text class="micro muted lh">{{ it.value }}</text>
              </view>
              </view>
            <text v-else class="micro muted">暂无投资领域指引</text>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">{{ currentYearLabel }} 九宫飞星</text>
                <text class="sst">Fengshui Flying Stars</text>
              </view>
            </view>
            <view class="flying">
              <view class="flying-border" />
              <view v-for="(n, i) in flyingStars" :key="i" class="flying-cell">
                <text class="serif flying-num" :class="{ gold: n.good }">{{ n.n }}</text>
                <text v-if="n.good" class="flying-good">吉</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">流年化解锦囊</text>
                <text class="sst">Misfortune Breaker</text>
              </view>
            </view>
            <view class="box-left red-left">
              <text class="micro muted lh">{{ liunianHuajieText }}</text>
            </view>
          </view>
        </view>
        <view v-else-if="activeTab === 'month'" class="lock-root" >
          <view v-if="shouldShowMonthRecharge" class="lock-overlay">
            <view class="lock-circle"><text class="lock-circle-text">锁</text></view>
            <text class="lock-h">{{ currentYearLabel }}年 12个月运势详批</text>
            <view class="lock-p">
              <text class="lock-p-text">解锁后获取大师级深度解析</text>
              <text class="lock-p-sub">助您趋吉避凶，财运亨通</text>
            </view>
            <view class="lock-cta" @click="handleUnlockMonth"><text class="lock-cta-text">立即解锁 ¥{{ unlockPriceText }}</text></view>
          </view>
          <view class="stack" :class="{ blurred: shouldShowMonthRecharge }">
            <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">流月运势详批</text>
                <text class="sst">Monthly Breakdown</text>
              </view>
            </view>
              <text class="gold bold">全年财运趋势总览</text>
              <view v-if="monthScoreSummary" class="month-summary">
                <view class="month-summary-item">
                  <text class="month-summary-label">全年均值</text>
                  <text class="month-summary-value">{{ monthScoreSummary.average }}</text>
                </view>
                <view class="month-summary-item">
                  <text class="month-summary-label">最佳月份</text>
                  <text class="month-summary-value">{{ monthScoreSummary.bestMonth }} · {{ monthScoreSummary.bestScore }}</text>
                </view>
                <view class="month-summary-item">
                  <text class="month-summary-label">需谨慎</text>
                  <text class="month-summary-value">{{ monthScoreSummary.worstMonth }} · {{ monthScoreSummary.worstScore }}</text>
                </view>
              </view>
              <view class="trend">
                <view class="trend-bars">
                  <view v-for="(v, i) in monthlyTrendDisplay" :key="i" class="trend-bar">
                    <view class="trend-bar-fill" :style="{ height: v + '%' }" />
                  </view>
                </view>
                <view class="trend-months">
                  <text v-for="(m, i) in months" :key="i" class="micro muted">{{ m }}</text>
                </view>
              </view>
            </view>

            <view class="card">
              <text class="gold bold">月度关键摘要（点击查看详情）</text>
              <view v-if="monthDetailRows.length" class="m-table">
                <view class="m-tr m-th">
                  <text class="m-td gold">月份</text>
                  <text class="m-td gold">干支</text>
                  <text class="m-td gold flex-1">核心运势判词</text>
                  <text class="m-td gold center">吉凶</text>
                </view>
                <view v-for="(r, i) in monthDetailRows" :key="i" class="m-tr clickable" @click="openMonthRowDetail(r)">
                  <text class="m-td serif bold">{{ r.m }}</text>
                  <text class="m-td serif gold">{{ r.g }}</text>
                  <text class="m-td flex-1 muted">{{ r.d }}</text>
                  <view class="m-td center">
                    <text class="badge" :class="badgeClass(r.l)">{{ r.l }}</text>
                  </view>
                </view>
              </view>
              <text v-else class="micro muted">暂无月度摘要数据</text>
            </view>

            <view class="card">
              <text class="gold bold">月度详批全量字段</text>
              <view v-if="monthDataGroups.length" class="atlas-summary">
                <view class="atlas-stat">
                  <text class="atlas-stat-value">{{ monthDataGroups.length }}</text>
                  <text class="atlas-stat-label">主题分组</text>
                      </view>
                <view class="atlas-stat">
                  <text class="atlas-stat-value">{{ monthFieldCount }}</text>
                  <text class="atlas-stat-label">字段总量</text>
                    </view>
                <view class="atlas-stat">
                  <text class="atlas-stat-value">{{ monthDetailRows.length }}</text>
                  <text class="atlas-stat-label">月度条目</text>
                      </view>
                    </view>
              <view v-if="monthDataGroups.length" class="atlas-groups">
                <view v-for="(group, gidx) in monthDataGroups" :key="`month-group-${gidx}`" class="atlas-group">
                  <view class="atlas-group-head">
                    <text class="atlas-group-title">{{ group.title }}</text>
                    <text class="atlas-group-count">{{ group.rows.length }}项</text>
                  </view>
                  <view class="atlas-rows">
                    <view v-for="(row, ridx) in group.rows" :key="`month-row-${gidx}-${ridx}`" class="atlas-row">
                      <text class="atlas-key">{{ row.label || '字段' }}</text>
                      <text class="atlas-value">{{ row.value || '-' }}</text>
                  </view>
                      </view>
                    </view>
                      </view>
              <text v-else class="micro muted">暂无月度详批数据</text>
                    </view>
                  </view>
                </view>
        <view v-else-if="activeTab === 'day'" class="lock-root" >
          <view v-if="shouldShowDayRecharge" class="lock-overlay">
            <view class="lock-circle"><text class="lock-circle-text">锁</text></view>
            <text class="lock-h">365天每日吉凶指南</text>
            <view class="lock-p">
              <text class="lock-p-text">解锁后获取大师级深度解析</text>
              <text class="lock-p-sub">助您趋吉避凶，财运亨通</text>
            </view>
            <view class="lock-cta" @click="handleUnlockDay"><text class="lock-cta-text">立即解锁 ¥{{ unlockPriceText }}</text></view>
          </view>
          <view class="stack" :class="{ blurred: shouldShowDayRecharge }">
            <view class="card">
              <view class="section-title">
                <view class="bar" />
                <view>
                  <text class="st">每日运程</text>
                  <text class="sst">Daily Guide</text>
                </view>
              </view>
              <view class="daily-hero">
                <text class="daily-hero-title">{{ dailyCalendarTitle || '今日黄历速览' }}</text>
                <text class="daily-hero-sub">{{ dailyFortuneTitle || '每日运程重点提示' }}</text>
              </view>
              <view class="daily-meta-grid">
                <view class="daily-meta-card">
                  <text class="daily-meta-label">财神方位</text>
                  <text class="daily-meta-value">{{ dailyCaishen }}</text>
                </view>
                <view class="daily-meta-card">
                  <text class="daily-meta-label">喜神方位</text>
                  <text class="daily-meta-value">{{ dailyXishen }}</text>
              </view>
            </view>
              <view class="daily-yiji-grid">
                <view class="daily-yiji-card daily-yiji-good">
                  <text class="daily-yiji-label">宜</text>
                  <text class="daily-yiji-value">{{ dailyYi }}</text>
                </view>
                <view class="daily-yiji-card daily-yiji-bad">
                  <text class="daily-yiji-label">忌</text>
                  <text class="daily-yiji-value">{{ dailyJi }}</text>
                </view>
              </view>
              <view class="action">
                <text class="gold bold">今日开运建议</text>
                <text class="micro muted lh">{{ dailyKaiyun }}</text>
              </view>
            </view>

            <view class="card">
              <text class="gold bold">时辰吉凶节奏</text>
              <view class="grid-4 gap-1 mt-1">
                <view v-for="(t, i) in hourList" :key="i" class="hour" :class="hourClass(t)">
                  <text class="micro">{{ t }}</text>
                </view>
              </view>
              </view>

            <view class="card">
              <text class="gold bold">每日运程全量字段</text>
              <view v-if="dayDataGroups.length" class="atlas-summary">
                <view class="atlas-stat">
                  <text class="atlas-stat-value">{{ dayDataGroups.length }}</text>
                  <text class="atlas-stat-label">主题分组</text>
            </view>
                <view class="atlas-stat">
                  <text class="atlas-stat-value">{{ dayFieldCount }}</text>
                  <text class="atlas-stat-label">字段总量</text>
                </view>
                <view class="atlas-stat">
                  <text class="atlas-stat-value">{{ hourList.length }}</text>
                  <text class="atlas-stat-label">时辰条目</text>
                </view>
              </view>
              <view v-if="dayDataGroups.length" class="atlas-groups">
                <view v-for="(group, gidx) in dayDataGroups" :key="`day-group-${gidx}`" class="atlas-group">
                  <view class="atlas-group-head">
                    <text class="atlas-group-title">{{ group.title }}</text>
                    <text class="atlas-group-count">{{ group.rows.length }}项</text>
                  </view>
                  <view class="atlas-rows">
                    <view v-for="(row, ridx) in group.rows" :key="`day-row-${gidx}-${ridx}`" class="atlas-row">
                      <text class="atlas-value">{{ row.value || '-' }}</text>
                    </view>
                  </view>
                </view>
              </view>
              <text v-else class="micro muted">暂无每日运程数据</text>
            </view>
          </view>
        </view>

        <view v-else-if="activeTab === 'fengshui'" class="stack">
          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">贵人画像</text>
                <text class="sst">Nobleman Profile</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="tiny muted lh">{{ nobleProfileText }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">常见外局煞气与化解</text>
                <text class="sst">External Sha Qi</text>
              </view>
            </view>
            <view class="sha">
              <view v-for="(s, i) in shaQi" :key="i" class="sha-row" :class="{ last: i === shaQi.length - 1 }">
                <view class="row gap-1">
                  <text class="red bold">{{ s.name }}</text>
                  <text class="micro muted">({{ s.desc }})</text>
                </view>
                <view class="row gap-1" style="align-items:center;">
                  <text class="micro">→</text>
                  <text class="micro">{{ s.fix }}</text>
                </view>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">寻龙点穴：家中财位</text>
                <text class="sst">Wealth Corners</text>
              </view>
            </view>
            <view class="wealth-corner">
              <text class="gold bold">明财位 (入门斜对角)</text>
              <view class="action mt-1">
                <text class="micro muted lh">{{ mingCaiweiText }}</text>
              </view>
              <view class="divider" />
              <text class="gold bold">暗财位 (八字/房相)</text>
              <view class="action mt-1">
                <text class="micro muted lh">{{ anCaiweiText }}</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">职场步步高升局</text>
                <text class="sst">Career Boost</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="micro muted lh">{{ careerBoostText }}</text>
            </view>
          </view>

          <!-- <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">全屋风水布局</text>
                <text class="sst">Room by Room</text>
              </view>
            </view>
            <view class="room">
              <view v-for="(r, i) in rooms" :key="i" class="room-row" :class="{ last: i === rooms.length - 1 }">
                <view class="room-icon"><text class="micro">{{ r.icon }}</text></view>
                <view class="col flex-1">
                  <view class="row between">
                    <text class="bold">{{ r.title }}</text>
                    <text class="room-tag" :class="r.tagCls">{{ r.tag }}</text>
                  </view>
                  <text class="micro muted lh">{{ r.desc }}</text>
                </view>
              </view>
            </view>
          </view> -->

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">催旺桃花/感情保鲜</text>
                <text class="sst">Love & Relationships</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="micro muted lh">{{ taohuaText }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">家居植物</text>
                <text class="sst">Fengshui Plants</text>
              </view>
            </view>
            <view class="grid-2 gap-2">
              <view v-for="(p, i) in plants" :key="i" class="box-dark">
                <text class="bold">{{ p.plant }}</text>
                <text class="micro muted">{{ p.place }}</text>
                <text v-if="p.effect" class="micro muted">作用：{{ p.effect }}</text>
              </view>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">萌宠风水</text>
                <text class="sst">Pet Feng Shui</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="micro muted lh">{{ petText }}</text>
              </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">数字能量风水</text>
                <text class="sst">Digital Energy</text>
              </view>
            </view>
            <view class="grid-2 gap-2">
              <view class="box-dark">
                <text class="micro muted">手机尾号</text>
                <text class="bold">{{ digitalPhoneText }}</text>
              </view>
              <view class="box-dark">
                <text class="micro muted">车牌</text>
                <text class="bold">{{ digitalPlateText }}</text>
              </view>
            </view>
            <view class="box mt-2">
              <text class="bold">楼层</text>
              <text class="micro muted lh">{{ digitalFloorText }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">爱车平安风水</text>
                <text class="sst">Car Energy</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="micro muted lh">{{ carText }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">吸金钱包</text>
                <text class="sst">Wallet Fengshui</text>
              </view>
            </view>
            <view class="box-dark">
              <text class="micro muted lh">颜色：{{ walletColorText }}</text>
              <text class="micro muted lh">质地：{{ walletMaterialText }}</text>
              <text class="micro muted lh">使用：{{ walletUsageText }}</text>
            </view>
          </view>

          <view class="card">
            <view class="section-title">
              <view class="bar" />
              <view>
                <text class="st">幸运色与佩戴</text>
                <text class="sst">Color & Accessories</text>
              </view>
            </view>
            <view class="grid-2 gap-2">
              <view class="box-dark">
                <text class="tiny muted">幸运色</text>
                <text class="bold">{{ luckyColorsText }}</text>
                </view>
              <view class="box-dark">
                <text class="tiny muted">开运饰品</text>
                <view v-if="accessories.length" class="col gap-1">
                  <text v-for="(a, i) in accessories" :key="i" class="micro muted lh">- {{ a.name }}：{{ a.effect }}</text>
              </view>
                <text v-else class="micro muted">-</text>
            </view>
          </view>
          </view>
        </view>
      </view>
    </scroll-view>

    <view v-if="showDetailModal" class="detail-modal-overlay" @click.stop="closeDetailModal">
      <view class="detail-modal" @click.stop="">
        <view class="detail-modal-head">
          <text class="detail-modal-title">{{ detailModalTitle }}</text>
          <view class="detail-modal-close" @click.stop="closeDetailModal"><text class="detail-modal-close-text">×</text>
          </view>
        </view>
        <scroll-view scroll-y class="detail-modal-body">
          <view class="detail-modal-content">
            <text v-for="(t, i) in detailModalLines" :key="i" class="detail-modal-text">{{ t }}</text>
          </view>
        </scroll-view>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { getWealthAnalysisByReportId, type WealthAnalysisResponse } from '@/api/cai-yun';
import { checkAllWealthUnlockStatus } from '@/utils/wealth-payment';
import { paymentApi, userApi } from '@/api';
import { HIDE_RECHARGE_FEATURE } from '@/utils/feature-flags';
import pdfBgUrl from "../../utils/pdf/background.png";

const APPID = 'wx1ca1ac7ad12123ac';
const PENDING_KEY = 'wx_pending_payment';
const REPORT_ID_KEY = 'wealth_analysis_report_id';
const REPORT_CACHE_PREFIX = 'wealth_analysis_cache_';
const REPORT_CACHE_LAST_KEY = 'wealth_analysis_cache_last';

// 获取URL参数（兼容hash路由）
function getUrlCode(): string | null {
  let search = window.location.search;
  if (search === '' && window.location.hash.indexOf('?') > -1) {
    search = '?' + (window.location.hash.split('?')[1] || '');
  }
  if (!search) return null;
  const reg = new RegExp('(^|&)code=([^&]*)(&|$)');
  const r = search.substr(1).match(reg);
  return r ? decodeURIComponent(r[2]) : null;
}

// 清除URL中的code参数
function cleanCodeFromUrl() {
  try {
    const url = new URL(window.location.href);
    let changed = false;

    if (url.searchParams.has('code') || url.searchParams.has('state')) {
      url.searchParams.delete('code');
      url.searchParams.delete('state');
      changed = true;
    }

    if (url.hash.includes('?')) {
      const [hashPath, hashQuery] = url.hash.split('?');
      const hashParams = new URLSearchParams(hashQuery || '');
      if (hashParams.has('code') || hashParams.has('state')) {
        hashParams.delete('code');
        hashParams.delete('state');
        const nextHashQuery = hashParams.toString();
        url.hash = nextHashQuery ? `${hashPath}?${nextHashQuery}` : hashPath;
        changed = true;
      }
    }

    if (changed) {
      window.history.replaceState(null, '', url.toString());
    }
  } catch {}
}

// 判断是否在微信浏览器
function isWechat(): boolean {
  return /MicroMessenger/i.test(navigator.userAgent);
}

// 保存/读取/清除待支付状态
function savePending(state: any) {
  try { localStorage.setItem(PENDING_KEY, JSON.stringify({ ...state, ts: Date.now() })); } catch {}
}
function getPending(): any {
  try {
    const raw = localStorage.getItem(PENDING_KEY);
    if (!raw) return null;
    const s = JSON.parse(raw);
    if (Date.now() - s.ts > 5 * 60 * 1000) { localStorage.removeItem(PENDING_KEY); return null; }
    return s;
  } catch { return null; }
}
function clearPending() {
  try { localStorage.removeItem(PENDING_KEY); } catch {}
}

const getReportCacheKey = (reportId: number) => `${REPORT_CACHE_PREFIX}${reportId}`;

declare const uni: any;

type TabId = 'destiny' | 'year' | 'month' | 'day' | 'fengshui';

// 接收从详解报告页面传递过来的数据
const props = defineProps<{
  data?: any;
}>();

const emit = defineEmits<{
  back: [];
}>();

// 响应式数据
const activeTab = ref<TabId>('destiny');
const isMonthUnlocked = ref(false);
const isDayUnlocked = ref(false);
const loading = ref(true);
const wealthAnalysisData = ref<WealthAnalysisResponse | null>(null);

const unlockLoading = ref(false);
const downloadingPdf = ref(false);
const accountRemainQuota = ref<number | null>(null);

const safeJsonParse = (input: any): any => {
  if (input === null || input === undefined) return null;
  if (typeof input === 'object') return input;
  if (typeof input !== 'string') return null;
  const text = input.trim();
  if (!text) return null;
  try {
    return JSON.parse(text);
  } catch {
    return null;
  }
};

const isPlainObject = (input: any): input is Record<string, any> => {
  return Object.prototype.toString.call(input) === '[object Object]';
};

const tryParseJsonString = (input: any): any => {
  if (typeof input !== 'string') return input;
  const text = input.trim();
  if (!text) return input;
  const maybeJson =
    (text.startsWith('{') && text.endsWith('}')) ||
    (text.startsWith('[') && text.endsWith(']'));
  if (!maybeJson) return input;
  try {
    return JSON.parse(text);
  } catch {
    return input;
  }
};

const deepParseJsonStrings = (input: any, depth = 0): any => {
  if (depth > 8) return input;
  const parsed = tryParseJsonString(input);
  if (Array.isArray(parsed)) {
    return parsed.map((item) => deepParseJsonStrings(item, depth + 1));
  }
  if (isPlainObject(parsed)) {
    const next: Record<string, any> = {};
    Object.keys(parsed).forEach((key) => {
      next[key] = deepParseJsonStrings(parsed[key], depth + 1);
    });
    return next;
  }
  return parsed;
};

type RawFieldRow = {
  key: string;
  value: string;
};

type AtlasRow = {
  label: string;
  value: string;
};

type AtlasGroup = {
  title: string;
  rows: AtlasRow[];
};

type MonthDetailRow = {
  m: string;
  g: string;
  d: string;
  l: string;
  score: number;
  raw: any;
};

type MonthScoreSummary = {
  average: number;
  bestMonth: string;
  bestScore: number;
  worstMonth: string;
  worstScore: number;
};

const clampScore = (score: number): number => {
  if (!Number.isFinite(score)) return 55;
  return Math.max(0, Math.min(100, Math.round(score)));
};

const scoreFromLuckText = (value: any): number => {
  const raw = String(value || '');
  const lower = raw.toLowerCase();
  if (/[凶衰险败]/.test(raw) || /bad|danger|xiong|negative|low/.test(lower)) return 28;
  if (/[吉旺顺福喜]/.test(raw) || /good|auspicious|ji|positive|high/.test(lower)) return 82;
  if (/[平中]/.test(raw) || /normal|mid|neutral/.test(lower)) return 55;
  return 55;
};

const normalizeMonthLabel = (value: any, fallback = ''): string => {
  const text = String(value ?? fallback ?? '').trim();
  if (!text) return '';
  if (/月/.test(text)) return text;
  if (/^\d{1,2}$/.test(text)) return `${text}月`;
  return text;
};

const formatDisplayValue = (value: any): string => {
  if (Array.isArray(value)) {
    return value
      .map((item) => formatRawValue(item))
      .filter((item) => item !== '')
      .join('、') || '-';
  }
  return formatRawValue(value);
};

const formatRawValue = (value: any): string => {
  if (value === null) return 'null';
  if (value === undefined) return 'undefined';
  if (typeof value === 'string') return value;
  if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') return String(value);
  try {
    return JSON.stringify(value);
  } catch {
    return String(value);
  }
};

const flattenRawFields = (input: any, parentKey = '', rows: RawFieldRow[] = []): RawFieldRow[] => {
  const rootKey = parentKey || '(root)';

  if (input === null || input === undefined) {
    rows.push({ key: rootKey, value: formatRawValue(input) });
    return rows;
  }

  if (typeof input !== 'object') {
    rows.push({ key: rootKey, value: formatRawValue(input) });
    return rows;
  }

  if (Array.isArray(input)) {
    if (!input.length) {
      rows.push({ key: rootKey, value: '[]' });
      return rows;
    }
    input.forEach((item, index) => {
      const nextKey = parentKey ? `${parentKey}[${index}]` : `[${index}]`;
      flattenRawFields(item, nextKey, rows);
    });
    return rows;
  }

  const entries = Object.entries(input);
  if (!entries.length) {
    rows.push({ key: rootKey, value: '{}' });
    return rows;
  }

  entries.forEach(([key, value]) => {
    const nextKey = parentKey ? `${parentKey}.${key}` : key;
    if (value !== null && typeof value === 'object') {
      flattenRawFields(value, nextKey, rows);
      return;
    }
    rows.push({ key: nextKey, value: formatRawValue(value) });
  });

  return rows;
};

const KEY_LABEL_MAP: Record<string, string> = {
  root: '基础信息',
  quannian_gaishu: '全年概述',
  zongyun_pinggu: '总运评估',
  caiyun_zhuti: '财运主题',
  guanjian_yueling: '关键月令',
  zhuyao_jixiong: '主要吉凶',
  yueling_xiangxi: '月令详析',
  ganzhi: '干支',
  hexin_yunshi: '核心运势',
  jixiong_dengji: '吉凶等级',
  caiyun_zhishu: '财运指数',
  shiye_zhishu: '事业指数',
  jiankang_zhishu: '健康指数',
  renmai_zhishu: '人脉指数',
  jinri_gaishu: '今日概览',
  huangli: '黄历信息',
  shichen_jixiong: '时辰吉凶',
  yi: '宜',
  ji: '忌',
  caishen: '财神方位',
  xishen: '喜神方位',
  kaiyun: '开运建议',
  tips: '建议',
  zhengYue: '正月',
  erYue: '二月',
  sanYue: '三月',
  siYue: '四月',
  wuYue: '五月',
  liuYue: '六月',
  qiYue: '七月',
  baYue: '八月',
  jiuYue: '九月',
  shiYue: '十月',
  shiyiYue: '十一月',
  shierYue: '十二月',
  months: '月份列表',
  trend: '趋势',
  monthly_trend: '月度趋势',
  month_detail: '月度详批',
  month_details: '月度详批',
  monthly_details: '月度详批',
  details: '详批内容',
  items: '条目',
  rows: '行数据',
  list: '列表',
  today: '今日',
  current: '当前',
  daily: '每日',
  data: '数据',
  result: '结果',
  title: '标题',
  calendar_title: '日历标题',
  fortune_title: '运程标题',
  month_title: '月份标题',
  weekdays: '星期',
  hours: '时辰',
  hour_list: '时辰列表',
  caishen_fangwei: '财神方位',
  xishen_fangwei: '喜神方位',
  kaiyun_tip: '开运提示',
  month_name: '月份',
  monthLabel: '月份',
  score: '评分',
  level: '等级',
  desc: '详解',
  text: '文本',
};

const pathSegments = (path: string): string[] => {
  return path.match(/[^.[\]]+|\[\d+\]/g) || [];
};

const prettySegment = (segment: string): string => {
  if (!segment) return '';
  const normalized = segment.replace(/^\[(\d+)\]$/, '第$1项').replace(/\[(\d+)\]/g, ' 第$1项');
  if (KEY_LABEL_MAP[normalized]) return KEY_LABEL_MAP[normalized];
  const snake = normalized.replace(/([a-z])([A-Z])/g, '$1_$2');
  return snake
    .split('_')
    .filter(Boolean)
    .map((part) => KEY_LABEL_MAP[part] || part)
    .join(' ');
};

const buildAtlasGroups = (rows: RawFieldRow[]): AtlasGroup[] => {
  const groups = new Map<string, AtlasGroup>();

  rows.forEach((row) => {
    const segs = pathSegments(row.key);
    const groupKey = segs[0] || 'root';
    if (!groups.has(groupKey)) {
      groups.set(groupKey, {
        title: prettySegment(groupKey),
        rows: [],
      });
    }
    const rest = segs.length > 1 ? segs.slice(1) : [];
    const label = rest.length ? rest.map(prettySegment).join(' / ') : prettySegment(groupKey);
    groups.get(groupKey)!.rows.push({ label, value: row.value });
  });

  return Array.from(groups.values()).filter((g) => g.rows.length > 0);
};

const yuedoXiangpiData = computed(() => {
  const raw = (wealthAnalysisData.value as any)?.yuedo_xiangpi;
  const parsed = safeJsonParse(raw) || raw || null;
  return deepParseJsonStrings(parsed);
});

const meiriYunchengData = computed(() => {
  const raw = (wealthAnalysisData.value as any)?.meiri_yuncheng;
  const parsed = safeJsonParse(raw) || raw || null;
  return deepParseJsonStrings(parsed);
});

const monthRawRows = computed<RawFieldRow[]>(() => {
  return flattenRawFields(yuedoXiangpiData.value);
});

const dayRawRows = computed<RawFieldRow[]>(() => {
  return flattenRawFields(meiriYunchengData.value);
});

const monthDataGroups = computed<AtlasGroup[]>(() => {
  return buildAtlasGroups(monthRawRows.value);
});

const dayDataGroups = computed<AtlasGroup[]>(() => {
  return buildAtlasGroups(dayRawRows.value);
});

const monthDetailRows = computed<MonthDetailRow[]>(() => {
  const src = yuedoXiangpiData.value;
  const detailRoot =
    src?.yueling_xiangxi ||
    src?.yuelingXiangxi ||
    src?.monthly_details ||
    src?.month_detail ||
    src?.details;

  const toRow = (input: any, idx: number, fallbackMonth = ''): MonthDetailRow => {
    const item = isPlainObject(input) ? input : { value: input };
    const monthText = normalizeMonthLabel(
      item?.m ??
      item?.month ??
      item?.yue ??
      item?.yuefen ??
      item?.month_name ??
      item?.monthLabel ??
      fallbackMonth,
      `${idx + 1}月`,
    ) || `${idx + 1}月`;
    const descFallback = item?.value ?? input;
    return {
      m: monthText,
      g: String(item?.g ?? item?.ganzhi ?? item?.gan_zhi ?? item?.gz ?? item?.ganZhi ?? '-'),
      d: String(item?.d ?? item?.desc ?? item?.core ?? item?.panci ?? item?.text ?? item?.hexin_yunshi ?? formatDisplayValue(descFallback) ?? '-'),
      l: String(item?.l ?? item?.level ?? item?.jixiong ?? item?.luck ?? item?.tag ?? item?.jixiong_dengji ?? '-'),
      score: Number(item?.caiyun_zhishu ?? item?.score ?? item?.index ?? Number.NaN),
      raw: input,
    };
  };

  if (Array.isArray(detailRoot) && detailRoot.length) {
    return detailRoot.map((item, idx) => toRow(item, idx));
  }

  if (isPlainObject(detailRoot)) {
    return Object.entries(detailRoot).map(([key, item], idx) => {
      const normalized = isPlainObject(item) ? item : { value: item };
      return toRow(normalized, idx, prettySegment(key));
    });
  }

  if (Array.isArray(src)) {
    return src.map((item: any, idx: number) => toRow(item, idx));
  }

  const list = src?.items || src?.rows || src?.list;
  if (Array.isArray(list) && list.length) {
    return list.map((item: any, idx: number) => toRow(item, idx));
  }

  return [];
});

const months = computed(() => {
  if (monthDetailRows.value.length) {
    return monthDetailRows.value.map((row) => normalizeMonthLabel(row.m, row.m));
  }
  const fromApi = yuedoXiangpiData.value?.months;
  if (Array.isArray(fromApi) && fromApi.length) return fromApi.map((m: any) => normalizeMonthLabel(m, String(m)));
  return ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
});

const monthlyRows = computed(() => {
  return monthDetailRows.value.map((row) => ({
    m: row.m,
    g: row.g,
    d: row.d,
    l: row.l,
  }));
});

const monthlyTrend = computed(() => {
  const src = yuedoXiangpiData.value;
  const trend = src?.trend || src?.monthly_trend || src?.scores;
  if (Array.isArray(trend) && trend.length) {
    return trend.map((v: any) => clampScore(Number(v)));
  }
  return Array.from({ length: Math.max(monthlyRows.value.length || 0, 12) }).map(() => 55);
});

const monthlyTrendDisplay = computed(() => {
  if (monthDetailRows.value.length) {
    return monthDetailRows.value.map((row, index) => {
      if (Number.isFinite(row.score)) {
        return clampScore(Number(row.score));
      }
      if (Number.isFinite(monthlyTrend.value[index])) {
        return clampScore(Number(monthlyTrend.value[index]));
      }
      return scoreFromLuckText(row.l);
    });
  }
  return monthlyTrend.value;
});

const monthScoreSummary = computed<MonthScoreSummary | null>(() => {
  const scores = monthlyTrendDisplay.value;
  if (!scores.length) return null;

  let bestIndex = 0;
  let worstIndex = 0;
  let total = 0;
  scores.forEach((score, index) => {
    const n = clampScore(score);
    total += n;
    if (n > clampScore(scores[bestIndex])) bestIndex = index;
    if (n < clampScore(scores[worstIndex])) worstIndex = index;
  });

  return {
    average: Math.round(total / scores.length),
    bestMonth: String(months.value[bestIndex] || `${bestIndex + 1}月`),
    bestScore: clampScore(scores[bestIndex]),
    worstMonth: String(months.value[worstIndex] || `${worstIndex + 1}月`),
    worstScore: clampScore(scores[worstIndex]),
  };
});

const monthFieldCount = computed(() => monthRawRows.value.length);
const dayFieldCount = computed(() => dayRawRows.value.length);

const dailyView = computed(() => {
  const src = meiriYunchengData.value;
  if (Array.isArray(src)) return src[0] || {};
  if (!isPlainObject(src)) return src || {};
  const pick = src?.today || src?.current || src?.daily || src?.today_data || src?.data || src?.result || src;
  if (isPlainObject(pick)) return pick;
  return src;
});

const dailyCalendarTitle = computed(() => {
  return String(dailyView.value?.calendar_title || dailyView.value?.calendarTitle || dailyView.value?.month_title || dailyView.value?.monthTitle || '');
});

const dailyFortuneTitle = computed(() => {
  return String(dailyView.value?.title || dailyView.value?.fortune_title || dailyView.value?.fortuneTitle || '');
});

const dailyCaishen = computed(() => {
  return String(dailyView.value?.caishen_fangwei || dailyView.value?.caishen_fangwei || dailyView.value?.caishenFangwei || '-');
});

const dailyXishen = computed(() => {
  return String(dailyView.value?.xishen || dailyView.value?.xishen_fangwei || dailyView.value?.xishenFangwei || '-');
});

const dailyYi = computed(() => {
  const v = dailyView.value?.yi || dailyView.value?.suitable || dailyView.value?.宜;
  return formatDisplayValue(v || '-');
});

const dailyJi = computed(() => {
  const v = dailyView.value?.ji || dailyView.value?.avoid || dailyView.value?.忌;
  return formatDisplayValue(v || '-');
});

const hourList = computed(() => {
  const v = dailyView.value?.hours || dailyView.value?.hour_list || dailyView.value?.hourList || dailyView.value?.shichen;
  if (Array.isArray(v) && v.length) {
    return v.map((x: any) => {
      if (typeof x === 'string' || typeof x === 'number') return String(x);
      if (isPlainObject(x)) {
        const time = String(x.time ?? x.hour ?? x.name ?? x.label ?? x.shichen ?? '');
        const status = String(x.level ?? x.status ?? x.jixiong ?? x.fortune ?? x.result ?? '');
        if (time && status) return `${time}·${status}`;
        return time || status || formatDisplayValue(x);
      }
      return formatDisplayValue(x);
    });
  }
  return ['子·吉', '丑·中', '寅·凶', '卯·吉', '辰·中', '巳·凶', '午·吉', '未·中'];
});

const dailyKaiyun = computed(() => {
  return formatDisplayValue(dailyView.value?.kaiyun || dailyView.value?.tips || dailyView.value?.kaiyun_tip || dailyView.value?.kaiyunTip || '-');
});

const saveReportCache = (reportId: number, payload: WealthAnalysisResponse | null | undefined) => {
  if (!reportId || !payload) return;
  try {
    uni.setStorageSync(getReportCacheKey(reportId), payload);
    uni.setStorageSync(REPORT_CACHE_LAST_KEY, { id: reportId, payload, ts: Date.now() });
  } catch (error) {
    console.error('缓存财运数据失败:', error);
  }
};

const readReportCache = (reportId?: number): WealthAnalysisResponse | null => {
  try {
    if (reportId) {
      const byId = uni.getStorageSync(getReportCacheKey(reportId));
      if (byId) return deepParseJsonStrings(byId) as WealthAnalysisResponse;
    }
    const last = uni.getStorageSync(REPORT_CACHE_LAST_KEY);
    if (last?.payload && (!reportId || Number(last.id || 0) === reportId)) {
      return deepParseJsonStrings(last.payload) as WealthAnalysisResponse;
    }
  } catch (error) {
    console.error('读取财运缓存失败:', error);
  }
  return null;
};

const getReportId = (): number => {
  const idFromProps = Number(props.data?.id || 0);
  if (idFromProps) return idFromProps;
  const pendingId = Number(getPending()?.bizId || 0);
  if (pendingId) return pendingId;
  try {
    return Number(uni.getStorageSync(REPORT_ID_KEY) || 0);
  } catch {
    return 0;
  }
};

const restoreUnlockStatus = () => {
  const reportId = getReportId();
  if (!reportId) return;
  const status = checkAllWealthUnlockStatus(reportId);
  if (status.monthly) isMonthUnlocked.value = true;
  if (status.daily) isDayUnlocked.value = true;
};

// 支付流程：获取code → createOrder → WeixinJSBridge支付
const doDirectPay = async (desc: string, amount: number, bizId: number, unlockType: string, tab: string): Promise<boolean> => {
  if (!isWechat()) {
    uni.showToast({ title: '请在微信中打开', icon: 'none' });
    return false;
  }

  const code = getUrlCode();
  if (!code) {
    // A. 没有code，保存状态后跳微信授权
    savePending({ tab, bizId, unlockType, amount });
    const local = window.location.href;
    const redirectUri = encodeURIComponent(local);
    window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=${APPID}&redirect_uri=${redirectUri}&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect`;
    return false;
  }

  // B. 有code，立即清除防重复使用
  cleanCodeFromUrl();
  clearPending();
  console.log('[支付] code:', code);

  try {
    uni.showLoading({ title: '创建订单中...' });
    const orderRes = await paymentApi.createOrder({
      description: desc,
      total_amount: amount,
      business_type: 'wealth_analysis',
      business_id: bizId,
      pay_type: 'jsapi',
      code,
    });
    uni.hideLoading();

    if (!orderRes?.appId || !orderRes?.paySign) {
      uni.showToast({ title: '获取支付参数失败', icon: 'none' });
      console.error('[支付] orderRes缺少支付参数:', JSON.stringify(orderRes));
      return false;
    }

    // WeixinJSBridge 调起JSAPI支付（参数直接在orderRes上）
    const pp = orderRes;
    return await new Promise<boolean>((resolve) => {
      const invoke = () => {
        (window as any).WeixinJSBridge.invoke('getBrandWCPayRequest', {
          appId: pp.appId,
          timeStamp: pp.timeStamp,
          nonceStr: pp.nonceStr,
          package: pp.package,
          signType: pp.signType || 'RSA',
          paySign: pp.paySign,
        }, (res: any) => {
          if (res.err_msg === 'get_brand_wcpay_request:ok') {
            uni.showToast({ title: '支付成功', icon: 'success' });
            resolve(true);
          } else {
            uni.showToast({ title: res.err_msg === 'get_brand_wcpay_request:cancel' ? '已取消' : '支付失败', icon: 'none' });
            resolve(false);
          }
        });
      };
      if (typeof (window as any).WeixinJSBridge === 'undefined') {
        document.addEventListener('WeixinJSBridgeReady', invoke, false);
      } else {
        invoke();
      }
    });
  } catch (error: any) {
    uni.hideLoading();
    uni.showToast({ title: error.msg || '创建订单失败', icon: 'none' });
    return false;
  }
};

const handleUnlockMonth = async () => {
  if (unlockLoading.value) return;
  if (!isQuotaZero.value) { uni.showToast({ title: '当前额度大于0，无需充值', icon: 'none' }); return; }
  const id = getReportId();
  if (!id) { uni.showToast({ title: '缺少报告ID', icon: 'none' }); return; }
  unlockLoading.value = true;
  try {
    if (await doDirectPay('财运解析-12个月运势详批', unlockPrice.value, id, 'monthly', 'month')) {
      isMonthUnlocked.value = true;
      uni.setStorageSync(`wealth_unlock_monthly_${id}`, true);
    }
  } finally { unlockLoading.value = false; }
};

const handleUnlockDay = async () => {
  if (unlockLoading.value) return;
  if (!isQuotaZero.value) { uni.showToast({ title: '当前额度大于0，无需充值', icon: 'none' }); return; }
  const id = getReportId();
  if (!id) { uni.showToast({ title: '缺少报告ID', icon: 'none' }); return; }
  unlockLoading.value = true;
  try {
    if (await doDirectPay('财运解析-365天每日吉凶指南', unlockPrice.value, id, 'daily', 'day')) {
      isDayUnlocked.value = true;
      uni.setStorageSync(`wealth_unlock_daily_${id}`, true);
    }
  } finally { unlockLoading.value = false; }
};

const unlockPrice = computed(() => {
  const raw = Number((wealthAnalysisData.value as any)?.unlock_price);
  return Number.isFinite(raw) && raw > 0 ? raw : 19.9;
});
const unlockPriceText = computed(() => unlockPrice.value.toFixed(1));
const rechargeFeatureVisible = computed(() => HIDE_RECHARGE_FEATURE !== true);
const isQuotaZero = computed(() => Number(accountRemainQuota.value) === 0);
const shouldShowMonthRecharge = computed(() => rechargeFeatureVisible.value && isQuotaZero.value && !isMonthUnlocked.value);
const shouldShowDayRecharge = computed(() => rechargeFeatureVisible.value && isQuotaZero.value && !isDayUnlocked.value);

const loadMyQuota = async () => {
  try {
    const quotaRes: any = await userApi.getMyMembershipQuota({ clearAuthOnError: false, showError: false });
    const freeRename = Number(quotaRes?.free_rename_quota);
    const remainRaw = quotaRes?.remaining_quota ?? quotaRes?.remain_quota ?? quotaRes?.left_quota;
    const remainFallback = Number(remainRaw);
    const remain = Number.isFinite(freeRename) ? freeRename : remainFallback;
    accountRemainQuota.value = Number.isFinite(remain) ? remain : 0;
  } catch {
    accountRemainQuota.value = null;
  }
};

// Tab列表
const tabList = computed(() => {
  return [
    { id: 'destiny' as const, label: '命盘精批', icon: '卦', locked: false },
    { id: 'year' as const, label: '流年总运', icon: '运', locked: false },
    { id: 'month' as const, label: '月度详批', icon: '月', locked: !isMonthUnlocked.value },
    { id: 'day' as const, label: '每日运程', icon: '日', locked: !isDayUnlocked.value },
    { id: 'fengshui' as const, label: '风水锦囊', icon: '风', locked: false }
  ];
});

// 从接口数据中提取八字排盘信息
const baziPillars = computed(() => {
  if (!wealthAnalysisData.value?.mingpan_jingpi?.bazi_paipan) {
    return [];
  }

  const bazi = wealthAnalysisData.value.mingpan_jingpi.bazi_paipan;
  return [
    { title: '年柱', gan: bazi.nian.gan, zhi: bazi.nian.zhi, god: '正官', hidden: [], stage: '墓' },
    { title: '月柱', gan: bazi.yue.gan, zhi: bazi.yue.zhi, god: '正印', hidden: [], stage: '帝旺' },
    { title: '日柱', gan: bazi.ri.gan, zhi: bazi.ri.zhi, god: '日主', hidden: [], stage: '养', active: true },
    { title: '时柱', gan: bazi.shi.gan, zhi: bazi.shi.zhi, god: '七杀', hidden: [], stage: '衰' }
  ];
});

// 从接口数据中提取五行能量分布
const elementScores = computed(() => {
  if (!wealthAnalysisData.value?.mingpan_jingpi?.wuxing_nengliang) {
    return [];
  }

  const wuxing = wealthAnalysisData.value.mingpan_jingpi.wuxing_nengliang;
  const elements = [
    { label: '金', score: wuxing.jin, color: '#e5e7eb' },
    { label: '木', score: wuxing.mu, color: '#22c55e' },
    { label: '水', score: wuxing.shui, color: '#3b82f6' },
    { label: '火', score: wuxing.huo, color: '#ef4444' },
    { label: '土', score: wuxing.tu, color: '#ca8a04' }
  ];

  return elements.map(e => ({
    label: `${e.label} (${e.score > 3 ? '旺' : e.score > 1 ? '中' : '弱'})`,
    score: e.score.toFixed(1),
    width: `${Math.min(100, e.score * 20)}%`,
    color: e.color
  }));
});

// 基础信息
const basicInfo = computed(() => {
  if (!wealthAnalysisData.value?.mingpan_jingpi) {
    return {
      mingzao: '',
      zhenTaiyang: '',
      nongli: ''
    };
  }

  const mingpan = wealthAnalysisData.value.mingpan_jingpi;
  return {
    mingzao: mingpan.mingzao || '',
    zhenTaiyang: mingpan.zhen_taiyang_shi || '',
    nongli: mingpan.nongli_shengchen || ''
  };
});

// 前世今生因果
const karmaInfo = computed(() => {
  if (!wealthAnalysisData.value?.mingpan_jingpi) {
    return {
      qianshi: '',
      jinsheng: ''
    };
  }

  const mingpan = wealthAnalysisData.value.mingpan_jingpi;
  return {
    qianshi: mingpan.qianshi_yinji || '',
    jinsheng: mingpan.jinsheng_keti || ''
  };
});

// 命格层次
const minggeInfo = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.mingge_cengci || '';
});

// 古籍断语
const gujiInfo = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.guji_duanyu || '';
});

// 奇门遁甲
const qimenInfo = computed(() => {
  if (!wealthAnalysisData.value?.mingpan_jingpi) {
    return {
      paipan: '',
      geju: ''
    };
  }

  const mingpan = wealthAnalysisData.value.mingpan_jingpi;
  return {
    paipan: mingpan.qimen_paipan || '',
    geju: mingpan.qimen_geju || ''
  };
});

// 大师批注
const dashiPizhu = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.dashi_pizhu || '';
});

// 五行开运指南
const wuxingKaiyun = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.wuxing_kaiyun || '';
});

// 十神性格盲点
const shishenMangdian = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.shishen_mangdian || '';
});

// 事业财运定数
const shiyeCaiyun = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.shiye_caiyun_dingshu || '';
});

// 婚姻情感剖析
const hunyinQinggan = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.hunyin_qinggan || '';
});

// 五行健康易患
const wuxingJiankang = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.wuxing_jiankang || '';
});

// 神煞贵人
const shenshaGuiren = computed(() => {
  return wealthAnalysisData.value?.mingpan_jingpi?.shensha_guiren || '';
});

// 流年相关数据
const liunianData = computed(() => {
  if (!wealthAnalysisData.value?.liunian_zongyun) {
    return {
      dayunZoushi: '',
      taisuiJiangjun: '',
      dangnianLiunian: '',
      liunianShensha: '',
      fenquntiZhuanyun: { qingnian: '', zhongnian: '', laonian: '' },
      jixiongFangwei: { ji: [], xiong: [] },
      caifuLayuan: '',
      touziLingyu: { fangdichan: '', gupiao: '', jijin: '', huangjin: '' },
      jiugongFeixing: '',
      liunianHuajie: ''
    };
  }

  return wealthAnalysisData.value.liunian_zongyun;
});

// 风水锦囊数据
const fengshuiData = computed(() => {
  if (!wealthAnalysisData.value?.fengshui_jinnang) {
    return {
      guirenHuaxiang: '',
      waijuShaji: { lu_chong: '', jian_dao_sha: '', fan_gong_sha: '' },
      jiajuCaiwei: { ming_caiwei: '', an_caiwei: '' },
      zhichangGaosheng: '',
      cuiwangTaohua: '',
      jiajuZhiwu: [],
      mengchongFengshui: '',
      shuziNengliang: { shouji_hao: '', che_pai: '', lou_ceng: '' },
      aichePingan: '',
      xijinQianbao: { yanse: '', zhidi: '', shiyong: '' },
      xingyunSePeidai: { xingyun_se: [], peidai_shipin: [] }
    };
  }

  return wealthAnalysisData.value.fengshui_jinnang;
});

// 加载财运解析数据
const loadWealthAnalysis = async () => {
  let reportId = Number(props.data?.id || 0);

  // 上一页已预取接口数据：直接使用，同时落地缓存，避免支付回跳后丢失
  if (props.data?.wealthData) {
    const payload = deepParseJsonStrings(props.data.wealthData) as WealthAnalysisResponse;
    if (reportId) {
      try {
        uni.setStorageSync(REPORT_ID_KEY, reportId);
      } catch (error) {
        console.error('存储报告ID到本地失败:', error);
      }
      saveReportCache(reportId, payload);
    }
    wealthAnalysisData.value = payload;
    loading.value = false;
    return;
  }

  // 先尝试从待支付上下文恢复ID，再回退到本地ID
  if (!reportId) {
    reportId = Number(getPending()?.bizId || 0);
  }

  if (!reportId) {
    try {
      const storedId = uni.getStorageSync(REPORT_ID_KEY);
      if (storedId) {
        reportId = Number(storedId);
      }
    } catch (error) {
      console.error('获取本地存储的报告ID失败:', error);
    }
  }

  if (reportId) {
    try {
      uni.setStorageSync(REPORT_ID_KEY, reportId);
    } catch (error) {
      console.error('存储报告ID到本地失败:', error);
    }
  }

  // 先用缓存兜底渲染，避免支付取消/回跳后页面空白
  const cached = readReportCache(reportId || undefined);
  if (cached) {
    wealthAnalysisData.value = cached;
  }

  if (!reportId) {
    if (wealthAnalysisData.value) {
      loading.value = false;
      return;
    }
    uni.showToast({ title: '缺少报告ID', icon: 'none' });
    loading.value = false;
    return;
  }

  try {
    loading.value = true;
    const response = await getWealthAnalysisByReportId(reportId);
    const parsed = deepParseJsonStrings(response) as WealthAnalysisResponse;
    wealthAnalysisData.value = parsed;
    saveReportCache(reportId, parsed);
  } catch (error) {
    if (!wealthAnalysisData.value) {
      uni.showToast({ title: '加载失败，请重试', icon: 'none' });
    }
  } finally {
    loading.value = false;
  }
};

// 模态框相关
const showDetailModal = ref(false);
const detailModalTitle = ref('');
const detailModalLines = ref<string[]>([]);

const openDetailModal = (title: string, lines: string[]) => {
  detailModalTitle.value = title;
  detailModalLines.value = lines;
  showDetailModal.value = true;
};

const closeDetailModal = () => {
  showDetailModal.value = false;
  detailModalTitle.value = '';
  detailModalLines.value = [];
};

const openMonthRowDetail = (r: { m: string; g: string; d: string; l: string; raw?: any }) => {
  const lines = [
    `吉凶等级：${r.l}`,
    r.d,
    ...flattenRawFields(r.raw || r)
      .filter((item) => item.key !== '(root)')
      .map((item) => `${item.key}: ${item.value}`)
  ];
  openDetailModal(`${r.m} · ${r.g}`, lines);
};
const stars = ref(
  Array.from({ length: 36 }).map((_, i) => ({
    id: i,
    top: `${Math.random() * 100}%`,
    left: `${Math.random() * 100}%`,
    size: Math.random() * 2 + 1,
    duration: Math.random() * 3 + 2,
    delay: Math.random() * 2
  }))
);
const currentYearLabel = computed(() => String(new Date().getFullYear()));

const splitTokens = (input: any): string[] => {
  return String(input || '')
    .split(/[\n，,、；;。|/]+/g)
    .map((s) => s.trim())
    .filter(Boolean);
};

// 奇门遁甲九宫格（优先读接口 qimen_paipan）
const qimenCells = computed(() => {
  const raw = safeJsonParse(qimenInfo.value?.paipan) || qimenInfo.value?.paipan;
  const parsed = deepParseJsonStrings(raw);
  if (Array.isArray(parsed) && parsed.length >= 9) return parsed.slice(0, 9);
  if (Array.isArray(parsed?.cells) && parsed.cells.length >= 9) return parsed.cells.slice(0, 9);
  return [];
});

// 五行开运指南（从接口文本拆分）
const elementAdvice = computed(() => {
  const lines = splitTokens(wuxingKaiyun.value);
  return lines.map((line, idx) => {
    const [left, right] = line.split(/[:：]/);
    return { k: left || `建议${idx + 1}`, good: right || line, bad: '-' };
  });
});

// 事业行业标签
const industryChips = computed(() => {
  const chips = splitTokens(shiyeCaiyun.value);
  return chips.slice(0, 12);
});

// 财运雷达柱状（按月度趋势/投资字段生成）
const wealthBars = computed(() => {
  const sourceScores = monthlyTrendDisplay.value.slice(0, 5);
  const labels = ['赚钱能力', '投资眼光', '守财能力', '抗风险力', '偏财运势'];
  if (sourceScores.length) {
    return sourceScores.map((score, i) => ({
      label: labels[i] || `维度${i + 1}`,
      score,
      cls: score >= 80 ? 'gold' : '',
      width: `${clampScore(score)}%`,
      color: ['#d4af37', '#3b82f6', '#22c55e', '#f59e0b', '#c084fc'][i % 5],
    }));
  }
  const fields = (liunianData.value as any)?.touzi_lingyu_zhiyin || {};
  return Object.entries(fields).slice(0, 5).map(([k, v], i) => {
    const score = scoreFromLuckText(v);
    return {
      label: prettySegment(k),
      score,
      cls: score >= 80 ? 'gold' : '',
      width: `${clampScore(score)}%`,
      color: ['#d4af37', '#3b82f6', '#22c55e', '#f59e0b', '#c084fc'][i % 5],
    };
  });
});

// 神煞贵人
const shenSha = computed(() => {
  const stars = splitTokens(shenshaGuiren.value);
  return stars.map((name) => ({ name, level: /凶|煞|灾|病/.test(name) ? '凶' : '吉', pos: '-' }));
});

// 大运走势
const dayun = computed(() => {
  const src: any = liunianData.value as any;
  const text = src?.dayun_zoushi || src?.dayunZoushi || '';
  return splitTokens(text).map((line, idx) => ({
    name: line.slice(0, 6),
    range: `阶段${idx + 1}`,
    desc: line,
    current: idx === 0,
  }));
});

// 流年财运柱状
const annualBars = computed(() => {
  return monthlyTrendDisplay.value.length ? monthlyTrendDisplay.value : monthlyTrend.value;
});

// 流年神煞
const yearlyStars = computed(() => {
  const src: any = liunianData.value as any;
  return splitTokens(src?.liunian_shensha || src?.liunianShensha || '').map((name) => ({
    name,
    bad: /凶|煞|灾|病|丧|刑/.test(name),
  }));
});

// 分人群转运
const roleAdvice = computed(() => {
  const src: any = liunianData.value as any;
  const source = src?.fenqunti_zhuanyun || src?.fenquntiZhuanyun || {};
  const entries = Object.entries(source).filter(([, v]) => String(v || '').trim());
  const icons = ['💼', '🎓', '🏠', '📈'];
  const bgs = [
    'linear-gradient(135deg,#d4af37,#b8860b)',
    'linear-gradient(135deg,#3b82f6,#1e40af)',
    'linear-gradient(135deg,#22c55e,#15803d)',
    'linear-gradient(135deg,#c084fc,#7c3aed)',
  ];
  return entries.map(([k, v], i) => ({
    icon: icons[i % icons.length],
    bg: bgs[i % bgs.length],
    color: '#fff',
    title: prettySegment(k),
    desc: String(v || ''),
  }));
});

// 九宫飞星
const flyingStars = computed(() => {
  const src: any = liunianData.value as any;
  const nums = String(src?.dangnian_jiugong_feixing || src?.jiugongFeixing || '').match(/\d/g) || [];
  return nums.slice(0, 9).map((n) => ({ n: Number(n), good: ['1', '4', '6', '8', '9'].includes(n) }));
});

// 外局煞气
const shaQi = computed(() => {
  const feng: any = fengshuiData.value as any;
  const src = feng?.waiju_shaji_huajie || feng?.waijuShaji || {};
  return Object.entries(src).map(([k, v]) => ({
    name: prettySegment(k),
    desc: '-',
    fix: String(v || '-'),
  }));
});

// 家居植物
const plants = computed(() => {
  const feng: any = fengshuiData.value as any;
  const src = Array.isArray(feng?.jiaju_zhiwu) ? feng.jiaju_zhiwu : Array.isArray(feng?.jiajuZhiwu) ? feng.jiajuZhiwu : [];
  return src.map((p: any) => ({
    place: String(p?.position || p?.place || '-'),
    plant: String(p?.name || p?.plant || '-'),
    effect: String(p?.effect || p?.desc || '').trim(),
  }));
});

const taisuiText = computed(() => {
  const src: any = liunianData.value as any;
  return String(src?.taisui_jiangjun || src?.taisuiJiangjun || '-') || '-';
});

const annualText = computed(() => {
  const src: any = liunianData.value as any;
  return String(src?.dangnian_liunian || src?.dangnianLiunian || '-') || '-';
});

const liunianStarsText = computed(() => {
  const src: any = liunianData.value as any;
  return String(src?.liunian_shensha || src?.liunianShensha || '-') || '-';
});

const liunianHuajieText = computed(() => {
  const src: any = liunianData.value as any;
  return String(src?.liunian_huajie || src?.liunianHuajie || '-') || '-';
});

const liunianLuckyDirs = computed(() => {
  const src: any = liunianData.value as any;
  const list = src?.liunian_jixiong_fangwei?.ji || src?.jixiongFangwei?.ji || [];
  return Array.isArray(list) && list.length ? list.join('、') : '-';
});

const liunianBadDirs = computed(() => {
  const src: any = liunianData.value as any;
  const list = src?.liunian_jixiong_fangwei?.xiong || src?.jixiongFangwei?.xiong || [];
  return Array.isArray(list) && list.length ? list.join('、') : '-';
});

const incomeSourcesText = computed(() => {
  const src: any = liunianData.value as any;
  return String(src?.caifu_laiyuan || src?.caifuLayuan || '-') || '-';
});

const investmentEntries = computed(() => {
  const src: any = liunianData.value as any;
  const obj = src?.touzi_lingyu_zhiyin || src?.touziLingyu || {};
  if (!obj || typeof obj !== 'object') return [];
  return Object.entries(obj)
    .filter(([, v]) => String(v || '').trim())
    .map(([k, v]) => ({ label: prettySegment(k), value: String(v || '') }));
});

const nobleProfileText = computed(() => String((fengshuiData.value as any)?.guiren_huaxiang || (fengshuiData.value as any)?.guirenHuaxiang || '-') || '-');
const mingCaiweiText = computed(() => String((fengshuiData.value as any)?.jiaju_caiwei?.ming_caiwei || (fengshuiData.value as any)?.jiajuCaiwei?.ming_caiwei || (fengshuiData.value as any)?.jiajuCaiwei?.mingCaiwei || '-') || '-');
const anCaiweiText = computed(() => String((fengshuiData.value as any)?.jiaju_caiwei?.an_caiwei || (fengshuiData.value as any)?.jiajuCaiwei?.an_caiwei || (fengshuiData.value as any)?.jiajuCaiwei?.anCaiwei || '-') || '-');
const careerBoostText = computed(() => String((fengshuiData.value as any)?.zhichang_gaosheng || (fengshuiData.value as any)?.zhichangGaosheng || '-') || '-');
const taohuaText = computed(() => String((fengshuiData.value as any)?.cuiwang_taohua || (fengshuiData.value as any)?.cuiwangTaohua || '-') || '-');
const petText = computed(() => String((fengshuiData.value as any)?.mengchong_fengshui || (fengshuiData.value as any)?.mengchongFengshui || '-') || '-');
const digitalPhoneText = computed(() => String((fengshuiData.value as any)?.shuzi_nengliang?.shouji_hao || (fengshuiData.value as any)?.shuziNengliang?.shouji_hao || (fengshuiData.value as any)?.shuziNengliang?.shoujiHao || '-') || '-');
const digitalPlateText = computed(() => String((fengshuiData.value as any)?.shuzi_nengliang?.che_pai || (fengshuiData.value as any)?.shuziNengliang?.che_pai || (fengshuiData.value as any)?.shuziNengliang?.chePai || '-') || '-');
const digitalFloorText = computed(() => String((fengshuiData.value as any)?.shuzi_nengliang?.lou_ceng || (fengshuiData.value as any)?.shuziNengliang?.lou_ceng || (fengshuiData.value as any)?.shuziNengliang?.louCeng || '-') || '-');
const carText = computed(() => String((fengshuiData.value as any)?.aiche_pingan || (fengshuiData.value as any)?.aichePingan || '-') || '-');
const walletColorText = computed(() => String((fengshuiData.value as any)?.xijin_qianbao?.yanse || (fengshuiData.value as any)?.xijinQianbao?.yanse || (fengshuiData.value as any)?.xijinQianbao?.color || '-') || '-');
const walletMaterialText = computed(() => String((fengshuiData.value as any)?.xijin_qianbao?.zhidi || (fengshuiData.value as any)?.xijinQianbao?.zhidi || (fengshuiData.value as any)?.xijinQianbao?.material || '-') || '-');
const walletUsageText = computed(() => String((fengshuiData.value as any)?.xijin_qianbao?.shiyong || (fengshuiData.value as any)?.xijinQianbao?.shiyong || (fengshuiData.value as any)?.xijinQianbao?.usage || '-') || '-');
const luckyColorsText = computed(() => {
  const v = (fengshuiData.value as any)?.xingyun_se_peidai?.xingyun_se || (fengshuiData.value as any)?.xingyunSePeidai?.xingyun_se || (fengshuiData.value as any)?.xingyunSePeidai?.xingyunSe;
  return Array.isArray(v) && v.length ? v.join('、') : '-';
});
const accessories = computed(() => {
  const v = (fengshuiData.value as any)?.xingyun_se_peidai?.peidai_shipin || (fengshuiData.value as any)?.xingyunSePeidai?.peidai_shipin || (fengshuiData.value as any)?.xingyunSePeidai?.peidaiShipin;
  return Array.isArray(v) ? v.map((x: any) => ({ name: String(x?.name || '-'), effect: String(x?.effect || '-') })) : [];
});

const badgeClass = (l: string) => {
  const raw = String(l || '');
  const text = raw.toLowerCase();
  if (/[凶衰险败]/.test(raw) || /bad|danger|xiong|negative|low/.test(text)) return 'badge-gray';
  if (/[吉旺顺福喜]/.test(raw) || /good|auspicious|positive|high/.test(text)) return 'badge-red';
  if (/ji/.test(text) && !/xiong/.test(text)) return 'badge-red';
  return 'badge-blue';
};

const hourClass = (t: string) => {
  const raw = String(t || '');
  const text = raw.toLowerCase();
  if (/[凶衰险败]/.test(raw) || /bad|danger|xiong|negative|low/.test(text)) return 'hour-bad';
  if (/[吉旺顺福喜]/.test(raw) || /good|auspicious|positive|high/.test(text)) return 'hour-good';
  if (/ji/.test(text) && !/xiong/.test(text)) return 'hour-good';
  return 'hour-mid';
};

const handleDownloadPdf = async () => {
  if (downloadingPdf.value) return;
  if (typeof window === 'undefined' || typeof document === 'undefined') {
    uni.showToast({ title: '当前环境不支持下载PDF', icon: 'none' });
    return;
  }

  downloadingPdf.value = true;
  uni.showLoading({ title: '正在生成PDF...', mask: true });
  try {
    const [{ jsPDF }, html2canvasMod] = await Promise.all([import('jspdf'), import('html2canvas')]);
    const html2canvas = (html2canvasMod as any).default || (html2canvasMod as any);

    const escapeHtml = (s: string) =>
      s
        .replace(/&/g, "&amp;")
        .replace(/</g, "&lt;")
        .replace(/>/g, "&gt;")
        .replace(/"/g, "&quot;")
        .replace(/'/g, "&#39;");

    const arr = (v: any) => (Array.isArray(v) ? v : []);
    const toStr = (v: any) => String(v ?? "").trim();
    const flattenDetailNodes = (nodes: any[]): string[] => {
      const out: string[] = [];
      for (const n of nodes) {
        if (!n || typeof n !== "object") continue;
        if (n.type === "text" && toStr(n.text)) out.push(toStr(n.text));
        if (n.type === "list") {
          for (const item of arr(n.items)) {
            const s = toStr(item);
            if (s) out.push(`· ${s}`);
          }
        }
        if (n.type === "kv") {
          for (const item of arr(n.items)) {
            const lab = toStr(item?.label);
            const val = toStr(item?.value);
            if (lab || val) out.push(`${lab}：${val}`);
          }
        }
      }
      return out;
    };

    // 过滤不应导出的字段（id/时间戳/内部字段等）
    const excludeKeys = new Set([
      "id",
      "report_id",
      "reportId",
      "user_id",
      "userId",
      "created_at",
      "updated_at",
      "createdAt",
      "updatedAt",
      "ts",
      "timestamp",
      "is_unlocked",
      "unlock_price",
      "locked",
      "lock",
      "price",
    ]);
    const noisyKeys = new Set(["details", "nodes", "type", "meta", "status", "code", "raw"]);
    const keyLabelMap: Record<string, string> = {
      mingzao: "命造",
      zhen_taiyang_shi: "真太阳时",
      nongli_shengchen: "农历生辰",
      qianshi_yinji: "前世印记",
      jinsheng_keti: "今生课题",
      mingge_cengci: "命格层次",
      guji_duanyu: "古籍短语",
      bazi_paipan: "八字排盘",
      nian: "年柱",
      yue: "月柱",
      ri: "日柱",
      shi: "时柱",
      gan: "天干",
      zhi: "地支",
      wuxing_nengliang: "五行能量",
      mu: "木",
      huo: "火",
      tu: "土",
      jin: "金",
      shui: "水",
      qimen_paipan: "奇门排盘",
      qimen_geju: "奇门格局",
      dashi_pizhu: "大势批注",
      liunian_zongyun: "流年总运",
      yuedu_xiangpi: "月度详批",
      meiri_yuncheng: "每日运程",
      fengshui_jinnang: "风水锦囊",
      wealth_score: "财运评分",
      wealth_level: "财运等级",
      wealth_trend: "财运趋势",
      is_unlocked: "解锁状态",
      unlock_price: "解锁价格",
      name: "姓名",
      details: "详解",
    };
    const prettyKey = (k: string): string => {
      if (keyLabelMap[k]) return keyLabelMap[k];
      // 未映射的拼音键不展示键名，避免出现大量 mingzao:xxx 样式
      if (/^[a-z0-9_]+$/i.test(k)) return "";
      return k;
    };
    const flattenAnyToLines = (val: any, depth = 0, prefix = ""): string[] => {
      if (depth > 6 || val === null || val === undefined) return [];
      if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
        const s = String(val).trim();
        return s ? [prefix ? `${prefix}${s}` : s] : [];
      }
      if (Array.isArray(val)) {
        const out = val.flatMap((it) => flattenAnyToLines(it, depth + 1, ""));
        return prefix ? out.map((x) => `${prefix}${x}`) : out;
      }
      if (typeof val === "object") {
        const out: string[] = [];
        Object.keys(val).forEach((k) => {
          if (excludeKeys.has(k) || noisyKeys.has(k)) return;
          const child = (val as any)[k];
          const label = prettyKey(k);
          // 先把 details.nodes 提出来（更像正文）
          const nodeLines = child && typeof child === "object" ? flattenDetailNodes(arr(child?.details?.nodes)) : [];
          nodeLines.forEach((ln) => out.push(label ? `${label}：${ln}` : ln));
          const lines = flattenAnyToLines(child, depth + 1, "");
          lines.forEach((ln) => out.push(label ? `${label}：${ln}` : ln));
        });
        return out;
      }
      return [];
    };

    const uniqueLines = (lines: string[]) => {
      const seen = new Set<string>();
      const out: string[] = [];
      for (const l of lines) {
        const s = String(l || "").trim();
        if (!s || seen.has(s)) continue;
        seen.add(s);
        out.push(s);
      }
      return out;
    };

    const mergeLines = (...groups: any[]) => uniqueLines(groups.flatMap((g) => (Array.isArray(g) ? g : [g])).flatMap((x) => {
      if (x == null) return [];
      if (Array.isArray(x)) return x;
      const s = String(x ?? "").trim();
      return s ? [s] : [];
    }));

    const reportId = getReportId();
    const root = wealthAnalysisData.value as any;
    const moduleList: Array<{ title: string; lines: string[] }> = [];
    const push = (title: string, lines: string[]) => {
      const l = uniqueLines(lines.map((x) => String(x ?? "").trim()).filter(Boolean));
      if (!l.length) return;
      moduleList.push({ title, lines: l });
    };

    // 尽量按接口模块分章，全部展示（不含 id 类字段）
    push("命盘精批", mergeLines(
      flattenAnyToLines(root?.mingpan_jingpi),
      flattenDetailNodes(arr(root?.mingpan_jingpi?.details?.nodes)),
    ));
    push("流年总运", mergeLines(
      flattenAnyToLines(root?.liunian_zongyun),
      flattenDetailNodes(arr(root?.liunian_zongyun?.details?.nodes)),
    ));
    push("月度详批", isMonthUnlocked.value
      ? mergeLines(flattenAnyToLines(root?.yuedu_xiangpi), flattenDetailNodes(arr(root?.yuedu_xiangpi?.details?.nodes)))
      : ["（未解锁）"]);
    push("每日运程", isDayUnlocked.value
      ? mergeLines(flattenAnyToLines(root?.meiri_yuncheng), flattenDetailNodes(arr(root?.meiri_yuncheng?.details?.nodes)))
      : ["（未解锁）"]);
    push("风水锦囊", mergeLines(
      flattenAnyToLines(root?.fengshui_jinnang),
      flattenDetailNodes(arr(root?.fengshui_jinnang?.details?.nodes)),
    ));

    // 兜底：把根级其它字段补进 PDF（过滤 id 等）
    if (root && typeof root === "object") {
      Object.keys(root).forEach((k) => {
        if (excludeKeys.has(k)) return;
        if (["mingpan_jingpi", "liunian_zongyun", "yuedu_xiangpi", "meiri_yuncheng", "fengshui_jinnang"].includes(k)) return;
        const v = (root as any)[k];
        const lines = mergeLines(
          v && typeof v === "object" ? flattenDetailNodes(arr(v?.details?.nodes)) : [],
          flattenAnyToLines(v),
        );
        if (!lines.length) return;
        push(prettyKey(k) || "其他信息", lines);
      });
    }

    if (!moduleList.length) {
      uni.showToast({ title: "暂无可导出的报告内容", icon: "none" });
      return;
    }

    const PAGE_W = 794;
    const PAGE_H = 1123;
    // 舒展版：降低单页承载量，提升留白与可读性
    const MAX_LINES = 34;
    const MIN_PAGE_COST = 24;
    const wrappedLineCost = (line: string) => {
      const text = String(line || "").trim();
      if (!text) return 1;
      return Math.max(1, Math.ceil(text.length / 30));
    };
    const splitByLines = (lines: string[], max: number) => {
      const res: string[][] = [];
      let bucket: string[] = [];
      let cost = 0;
      lines.forEach((line) => {
        const lc = wrappedLineCost(line);
        if (bucket.length && cost + lc > max) {
          res.push(bucket);
          bucket = [];
          cost = 0;
        }
        bucket.push(line);
        cost += lc;
      });
      if (bucket.length) res.push(bucket);
      return res.length ? res : [[]];
    };

    const styleId = "pdf-gen-wealth-style";
    let style = document.getElementById(styleId) as HTMLStyleElement | null;
    if (!style) {
      style = document.createElement("style");
      style.id = styleId;
      document.head.appendChild(style);
    }
    // 复用 NamingDetail 的 PDF 视觉体系
    style.textContent = `
      .pdf-gen-page{position:absolute;width:${PAGE_W}px;height:${PAGE_H}px;background-size:cover;background-position:center;font-family:"Songti SC","SimSun","STSong","Noto Serif SC",serif;color:#f2e6d8;}
      .pdf-gen-panel{position:absolute;left:52px;right:52px;top:124px;bottom:100px;background:rgba(10,10,15,.60);border:1px solid rgba(212,175,55,.36);border-radius:16px;padding:30px 30px;box-sizing:border-box;overflow:hidden;}
      .pdf-cover-kicker{text-align:center;font-size:12px;letter-spacing:.28em;color:rgba(212,175,55,.72);margin-bottom:12px;}
      .pdf-cover-title{text-align:center;font-size:25px;letter-spacing:.16em;font-weight:700;color:rgba(212,175,55,.95);margin-bottom:26px;}
      .pdf-name{text-align:center;font-size:44px;font-weight:800;margin-bottom:14px;color:rgba(242,230,216,.98);text-shadow:0 0 18px rgba(212,175,55,.18);}
      .pdf-cover-divider{width:210px;height:1px;margin:22px auto 18px;background:linear-gradient(90deg,rgba(212,175,55,0),rgba(212,175,55,.7),rgba(212,175,55,0));}
      .pdf-cover-seal{width:66px;height:66px;margin:26px auto 0;border-radius:50%;border:1px solid rgba(212,175,55,.34);box-shadow:inset 0 0 0 6px rgba(212,175,55,.05);display:flex;align-items:center;justify-content:center;color:rgba(212,175,55,.92);font-size:20px;}
      .pdf-dir-title{font-size:20px;font-weight:800;letter-spacing:.14em;color:rgba(212,175,55,.95);margin-bottom:12px;}
      .pdf-dir-kicker{font-size:11px;letter-spacing:.24em;color:rgba(212,175,55,.68);margin-bottom:8px;}
      .pdf-dir-list{margin-top:6px;border-top:1px solid rgba(212,175,55,.14);}
      .pdf-dir-item{display:flex;align-items:center;border-bottom:1px solid rgba(255,255,255,.08);min-height:42px;padding:0 4px;box-sizing:border-box;gap:8px;}
      .pdf-dir-idx{width:68px;flex:0 0 68px;font-size:15px;color:rgba(212,175,55,.96);font-weight:800;}
      .pdf-dir-name{flex:1;font-size:16px;color:rgba(232,232,232,.96);font-weight:700;}
      .pdf-dir-dots{flex:1;border-bottom:1px dashed rgba(212,175,55,.34);height:0;transform:translateY(2px);}
      .pdf-dir-page{width:62px;flex:0 0 62px;text-align:right;font-size:13px;color:rgba(226,214,188,.92);font-weight:700;border:1px solid rgba(212,175,55,.22);border-radius:999px;padding:2px 8px;box-sizing:border-box;background:rgba(212,175,55,.06);}
      .pdf-chapter-bar{position:absolute;left:52px;right:52px;top:84px;height:48px;border-radius:12px;background:rgba(212,175,55,.08);border:1px solid rgba(212,175,55,.24);display:flex;align-items:center;padding:0 22px;box-sizing:border-box;}
      .pdf-chapter-no{font-size:16px;font-weight:900;color:rgba(212,175,55,.98);margin-right:14px;}
      .pdf-chapter-title{font-size:16px;font-weight:800;color:rgba(242,230,216,.98);}
      .pdf-flow-section{margin-bottom:14px;border-radius:10px;border:1px solid rgba(212,175,55,.16);background:rgba(11,16,38,.55);overflow:hidden;}
      .pdf-flow-head{height:40px;display:flex;align-items:center;gap:10px;padding:0 15px;border-bottom:1px solid rgba(212,175,55,.16);background:linear-gradient(90deg,rgba(212,175,55,.12),rgba(212,175,55,.03));}
      .pdf-flow-bullet{color:rgba(212,175,55,.96);font-size:15px;}
      .pdf-flow-title{color:rgba(245,236,218,.98);font-size:15px;font-weight:800;letter-spacing:.04em;}
      .pdf-flow-body{padding:12px 15px 14px;}
      .pdf-flow-line{font-size:14px;line-height:1.9;color:rgba(228,230,238,.95);margin-bottom:4px;white-space:pre-wrap;word-break:break-word;overflow-wrap:anywhere;}
      .pdf-page-footer{position:absolute;left:48px;right:48px;bottom:42px;height:24px;display:flex;align-items:center;justify-content:space-between;font-size:12px;color:rgba(210,210,210,.72);letter-spacing:.04em;}
      .pdf-footer-line{position:absolute;left:48px;right:48px;bottom:70px;height:1px;background:linear-gradient(90deg,rgba(212,175,55,0),rgba(212,175,55,.45),rgba(212,175,55,0));}
    `;

    const wrapper = document.createElement("div");
    wrapper.style.position = "fixed";
    wrapper.style.left = "-100000px";
    wrapper.style.top = "0";
    wrapper.style.zIndex = "-1";
    wrapper.style.width = "0";
    wrapper.style.height = "0";
    document.body.appendChild(wrapper);

    const pages: HTMLElement[] = [];
    const createPageEl = (idx: number) => {
      const el = document.createElement("div");
      el.className = "pdf-gen-page";
      el.style.left = `${idx * 2}px`;
      el.style.top = `${idx * 2}px`;
      (el.style as any).backgroundImage = `url(${pdfBgUrl})`;
      wrapper.appendChild(el);
      pages.push(el);
      return el;
    };

    const appendFooter = (el: HTMLElement, pageNo: number, label = "财运解析报告") => {
      const footer = document.createElement("div");
      footer.className = "pdf-page-footer";
      footer.innerHTML = `<div>${escapeHtml(label)}</div><div>第 ${pageNo} 页</div>`;
      const line = document.createElement("div");
      line.className = "pdf-footer-line";
      el.appendChild(line);
      el.appendChild(footer);
    };

    // 封面
    const coverEl = createPageEl(0);
    coverEl.innerHTML = `
      <div class="pdf-gen-panel">
        <div class="pdf-cover-kicker">玄 · 财 · 机 · 密</div>
        <div class="pdf-cover-title">财运解析报告</div>
        <div class="pdf-name">${escapeHtml(reportId ? `报告 ${reportId}` : "财运详解")}</div>
        <div style="text-align:center;color:rgba(226,226,226,0.92);font-size:14px;margin-bottom:18px;">命盘精批 · 流年总运 · 月日详批 · 风水锦囊</div>
        <div class="pdf-cover-divider"></div>
        <div class="pdf-cover-seal">财</div>
      </div>
    `;
    appendFooter(coverEl, 1, "财运解析报告 · 封面");

    // 目录
    const dirEl = createPageEl(1);
    type ChapterSection = { chapterNo: number; title: string; lines: string[]; continued?: boolean };
    type ContentPage = { sections: ChapterSection[]; cost: number };
    const contentPages: ContentPage[] = [];
    const dirItems: Array<{ no: number; title: string; start: number; end: number }> = [];
    let currentPageNo = 3;
    moduleList.forEach((m, idx) => {
      const chapterNo = idx + 1;
      const chunks = splitByLines(m.lines, MAX_LINES);
      const start = currentPageNo;
      chunks.forEach((chunk, cIdx) => {
        const section: ChapterSection = { chapterNo, title: m.title, lines: chunk, continued: cIdx > 0 };
        // 节标题/容器固定开销，保证分页更接近真实视觉高度
        const sectionCost = 5 + chunk.reduce((sum, ln) => sum + wrappedLineCost(ln), 0);
        const prev = contentPages[contentPages.length - 1];

        // 续页优先独占，保持章节连续性；短章允许拼页，提高页面利用率与协调度
        const canMergeIntoPrev =
          !!prev &&
          !section.continued &&
          prev.cost + sectionCost <= MAX_LINES &&
          prev.cost < MIN_PAGE_COST;

        if (canMergeIntoPrev) {
          prev.sections.push(section);
          prev.cost += sectionCost;
        } else {
          contentPages.push({ sections: [section], cost: sectionCost });
          currentPageNo += 1;
        }
      });
      const end = Math.max(start, currentPageNo - 1);
      dirItems.push({ no: chapterNo, title: m.title, start, end });
    });
    dirEl.innerHTML = `
      <div class="pdf-gen-panel">
        <div class="pdf-dir-kicker">卷 一 · 纲 目</div>
        <div class="pdf-dir-title">目录</div>
        <div class="pdf-dir-list">
          ${dirItems.map((it) => {
            const pageText = it.start === it.end ? `${it.start}` : `${it.start}-${it.end}`;
            return `<div class="pdf-dir-item">
              <div class="pdf-dir-idx">第${it.no}章</div>
              <div class="pdf-dir-name">${escapeHtml(it.title)}</div>
              <div class="pdf-dir-dots"></div>
              <div class="pdf-dir-page">${escapeHtml(pageText)}</div>
            </div>`;
          }).join("")}
        </div>
      </div>
    `;
    appendFooter(dirEl, 2, "财运解析报告 · 目录");

    // 正文页（每页可包含多个小节，密度更均衡）
    contentPages.forEach((page, i) => {
      const el = createPageEl(2 + i);
      const titleText = page.sections.length > 1
        ? `综合页 · ${page.sections.length} 个章节`
        : (() => {
          const s = page.sections[0];
          return `第${s.chapterNo}章 ${s.title}${s.continued ? "（续）" : ""}`;
        })();
      const sectionsHtml = page.sections.map((section) => {
        const secTitleText = `第${section.chapterNo}章 ${section.title}${section.continued ? "（续）" : ""}`;
        const linesHtml = section.lines.map((ln) => `<div class="pdf-flow-line">${escapeHtml(ln || " ")}</div>`).join("");
        return `<div class="pdf-flow-section">
          <div class="pdf-flow-head">
            <div class="pdf-flow-bullet">✧</div>
            <div class="pdf-flow-title">${escapeHtml(secTitleText)}</div>
          </div>
          <div class="pdf-flow-body">${linesHtml}</div>
        </div>`;
      }).join("");
      el.innerHTML = `
        <div class="pdf-chapter-bar">
          <div class="pdf-chapter-no">正文</div>
          <div class="pdf-chapter-title">${escapeHtml(titleText)}</div>
        </div>
        <div class="pdf-gen-panel" style="top:138px;">${sectionsHtml}</div>
      `;
      appendFooter(el, 3 + i);
    });

    const doc = new jsPDF({ unit: "pt", format: "a4" });
    const pageW = doc.internal.pageSize.getWidth();
    const pageH = doc.internal.pageSize.getHeight();
    const renderScale = Math.min(1.45, Math.max(1.15, Number((window as any)?.devicePixelRatio || 1)));
    const jpegQuality = 0.86;

    for (let i = 0; i < pages.length; i++) {
      const canvas = await html2canvas(pages[i], {
        scale: renderScale,
        useCORS: true,
        allowTaint: true,
        backgroundColor: "#0a0a0f",
        logging: false,
      });
      const img = canvas.toDataURL("image/jpeg", jpegQuality);
      if (i > 0) doc.addPage();
      doc.addImage(img, "JPEG", 0, 0, pageW, pageH, undefined, "FAST");
    }

    wrapper.remove();
    const filename = `财运解析报告${reportId ? `-${reportId}` : ""}.pdf`;
    doc.save(filename);
  } catch (e: any) {
    console.error('生成PDF失败:', e);
    uni.showToast({ title: e?.msg || e?.message || '生成失败，请稍后重试', icon: 'none' });
  } finally {
    uni.hideLoading();
    downloadingPdf.value = false;
  }
};
onMounted(async () => {
  restoreUnlockStatus();
  await loadMyQuota();
  await loadWealthAnalysis();

  const pending = getPending();
  if (pending?.tab) {
    activeTab.value = pending.tab as TabId;
    if (pending.unlockType === 'monthly') {
      handleUnlockMonth();
    } else if (pending.unlockType === 'daily') {
      handleUnlockDay();
    }
  }
});
</script>

<style scoped>
.fortune-wrap {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 100vw;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: #050508;
  color: #e2e2e2;
  overflow: hidden;
  z-index: 60;
  font-size: 26rpx;
  line-height: 1.6;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, "PingFang SC",
    "Hiragino Sans GB", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.bg {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 0;
}

.bg-base {
  position: absolute;
  inset: 0;
  background: #050508;
}

.bg-texture {
  position: absolute;
  inset: 0;
  background-image: url('https://www.transparenttextures.com/patterns/stardust.png');
  background-size: auto;
  opacity: 0.2;
}

.bg-glow {
  position: absolute;
  width: 50%;
  height: 50%;
  border-radius: 999px;
  filter: blur(100px);
}

.bg-glow-a {
  top: -10%;
  left: -10%;
  background: #d4af37;
  opacity: 0.05;
}

.bg-glow-b {
  bottom: -10%;
  right: -10%;
  background: #8b2323;
  opacity: 0.05;
}

.bg-star {
  position: absolute;
  border-radius: 50%;
  background: #fff;
  opacity: 0.2;
  animation: twinkle 3s ease-in-out infinite;
}

@keyframes twinkle {

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

  50% {
    opacity: 0.55;
    transform: scale(1.2);
  }
}

.header {
  position: relative;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 28rpx 30rpx;
  padding-top: calc(28rpx + env(safe-area-inset-top, 0px));
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  background: rgba(10, 10, 15, 0.8);
  backdrop-filter: blur(10px);
}

.header-left {
  width: 72rpx;
  height: 72rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.header-back {
  color: #a0a0a0;
  font-size: 44rpx;
  line-height: 44rpx;
}

.header-mid {
  display: flex;
  flex-direction: column;
  align-items: center;
}

.header-title {
  font-size: 30rpx;
  font-weight: 700;
  color: #d4af37;
  letter-spacing: 0.2em;
}

.header-subtitle {
  margin-top: 6rpx;
  font-size: 18rpx;
  color: #5a5a5a;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.header-right {
  display: flex;
  align-items: center;
  gap: 16rpx;
}

.header-icon {
  width: 64rpx;
  height: 64rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.header-icon-text {
  font-size: 30rpx;
  color: #d4af37;
}

.tabs {
  position: relative;
  z-index: 10;
  background: rgba(10, 10, 15, 0.5);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.tabs-inner {
  display: flex;
  align-items: center;
  min-width: max-content;
  padding: 0 12rpx;
}

.tab {
  position: relative;
  min-width: 160rpx;
  padding: 18rpx 18rpx 20rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8rpx;
  color: #666;
}

.tab.active {
  color: #d4af37;
}

.tab-icon-wrap {
  position: relative;
  width: 56rpx;
  height: 56rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.tab-icon {
  font-size: 28rpx;
  line-height: 1;
  display: block;
}

.tab-lock-dot {
  position: absolute;
  top: -6rpx;
  right: -10rpx;
  width: 32rpx;
  height: 32rpx;
  padding: 0;
  border-radius: 999rpx;
  border: 1px solid rgba(212, 175, 55, 0.35);
  background: rgba(212, 175, 55, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
}

.tab-lock-dot-text {
  font-size: 18rpx;
  line-height: 1;
  color: #d4af37;
}

.tab-label {
  font-size: 22rpx;
  font-weight: 700;
  letter-spacing: 0.18em;
}

.tab-indicator {
  position: absolute;
  bottom: 0;
  width: 64rpx;
  height: 4rpx;
  background: #d4af37;
}

.content {
  position: relative;
  z-index: 10;
  flex: 1;
  height: 0;
}

.content-inner {
  padding: 38rpx 38rpx 160rpx;
}

.stack {
  display: flex;
  flex-direction: column;
  gap: 24rpx;
}

.card {
  position: relative;
  background: #1a1a2e;
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 18rpx;
  padding: 28rpx;
  overflow: hidden;
}

.card-grad {
  background: linear-gradient(135deg, #1a1a2e, #0a0a0f);
}

.border-gold {
  border-color: rgba(212, 175, 55, 0.3);
}

.card-watermark {
  position: absolute;
  top: 0;
  right: 0;
  padding: 14rpx;
  opacity: 0.08;
}

.wm {
  font-size: 80rpx;
  color: #d4af37;
}

.section-title {
  display: flex;
  align-items: center;
  gap: 14rpx;
  margin-bottom: 18rpx;
}

.bar {
  width: 8rpx;
  height: 44rpx;
  border-radius: 999rpx;
  background: #d4af37;
  box-shadow: 0 0 16rpx rgba(212, 175, 55, 0.5);
}

.st {
  display: block;
  font-size: 28rpx;
  font-weight: 800;
  letter-spacing: 0.18em;
  font-family: "Songti SC", "Noto Serif SC", SimSun, serif;
}

.sst {
  display: block;
  margin-top: 6rpx;
  font-size: 18rpx;
  color: #a0a0a0;
  letter-spacing: 0.22em;
  text-transform: uppercase;
}

.muted {
  color: #a0a0a0;
}

.gold {
  color: #d4af37;
}

.red {
  color: #f87171;
}

.serif {
  font-family: "Songti SC", "Noto Serif SC", SimSun, serif;
}

.mono {
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New",
    monospace;
}

.bold {
  font-weight: 800;
}

.tiny {
  font-size: 22rpx;
}

.micro {
  font-size: 20rpx;
}

.lh {
  line-height: 1.6;
}

.row {
  display: flex;
  align-items: flex-start;
}

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

.flex-1 {
  flex: 1;
}

.gap-1 {
  gap: 8rpx;
}

.gap-2 {
  gap: 16rpx;
}

.gap-3 {
  gap: 24rpx;
}

.mt-1 {
  margin-top: 8rpx;
}

.mt-2 {
  margin-top: 16rpx;
}

.mb-3 {
  margin-bottom: 24rpx;
}

.divider {
  height: 1px;
  background: rgba(255, 255, 255, 0.05);
  margin: 20rpx 0;
}

.grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
}

.grid-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
}

.center {
  text-align: center;
}

.row-wide {
  grid-column: 1 / -1;
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: rgba(255, 255, 255, 0.05);
  padding: 14rpx 16rpx;
  border-radius: 14rpx;
}

.info-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12rpx 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.info-row:last-child {
  border-bottom: 0;
}

.box-dark {
  background: rgba(0, 0, 0, 0.2);
  border-radius: 14rpx;
  padding: 18rpx;
  border: 1px solid rgba(255, 255, 255, 0.06);
}

.bazi-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-radius: 16rpx;
  overflow: hidden;
  background: #0a0a0f;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.bazi-pillar {
  display: flex;
  flex-direction: column;
  align-items: center;
  border-right: 1px solid rgba(255, 255, 255, 0.08);
  padding-bottom: 18rpx;
}

.bazi-pillar:last-child {
  border-right: 0;
}

.bazi-pillar.active {
  background: rgba(212, 175, 55, 0.06);
}

.gan-circle {
  width: 80rpx;
  height: 80rpx;
  border-radius: 999rpx;
  border: 1px solid rgba(212, 175, 55, 0.3);
  background: #1a1a2e;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 14rpx 0 10rpx;
}

.zhi-square {
  width: 80rpx;
  height: 80rpx;
  border-radius: 12rpx;
  border: 1px solid rgba(255, 255, 255, 0.1);
  background: #1a1a2e;
  display: flex;
  align-items: center;
  justify-content: center;
}

.gan-text {
  font-size: 40rpx;
  font-weight: 800;
  font-family: "Songti SC", "Noto Serif SC", SimSun, serif;
}

.hidden-list {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4rpx;
  margin-top: 10rpx;
  min-height: 72rpx;
}

.hidden-item {
  font-size: 18rpx;
  color: #666;
}

.stage {
  margin-top: 8rpx;
  padding: 4rpx 10rpx;
  border-radius: 999rpx;
  background: rgba(255, 255, 255, 0.05);
}

.grade-badge {
  width: 140rpx;
  height: 140rpx;
  border-radius: 999rpx;
  background: rgba(212, 175, 55, 0.1);
  border: 1px solid #d4af37;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.grade-text {
  font-size: 40rpx;
  font-weight: 900;
  color: #d4af37;
}

.poem {
  background: rgba(0, 0, 0, 0.2);
  border-top: 1px solid rgba(255, 255, 255, 0.05);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  padding: 18rpx;
  border-radius: 14rpx;
}

.poem-line {
  display: block;
  text-align: center;
  font-style: italic;
  font-family: "Songti SC", "Noto Serif SC", SimSun, serif;
  color: #a0a0a0;
  line-height: 1.8;
  font-size: 18rpx;
  margin: 8rpx 0;
}

.scorebar {
  margin-bottom: 16rpx;
}

.scorebar-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 6rpx;
}

.scorebar-track {
  width: 100%;
  height: 12rpx;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 999rpx;
  overflow: hidden;
}

.scorebar-fill {
  height: 100%;
  border-radius: 999rpx;
}

.note {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 14rpx;
  padding: 18rpx;
}

.table {
  border-radius: 14rpx;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.tr {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  padding: 12rpx 10rpx;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.tr:last-child {
  border-bottom: 0;
}

.th {
  background: rgba(255, 255, 255, 0.04);
}

.tc {
  font-size: 20rpx;
}

.foot {
  display: block;
  margin-top: 14rpx;
  text-align: center;
  color: #666;
  font-style: italic;
  font-size: 18rpx;
}

.box-left {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 14rpx;
  padding: 18rpx;
  border-left-width: 6rpx;
  border-left-style: solid;
}

.gold-left {
  border-left-color: #d4af37;
}

.red-left {
  border-left-color: #ef4444;
}

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10rpx;
  margin-top: 10rpx;
}

.chip {
  font-size: 18rpx;
  padding: 6rpx 10rpx;
  border: 1px solid rgba(212, 175, 55, 0.3);
  border-radius: 12rpx;
  color: #d4af37;
}

.mb-2 {
  margin-bottom: 16rpx;
}

.kv {
  display: flex;
  flex-direction: column;
  gap: 12rpx;
}

.kv-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14rpx 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.kv-row:last-child {
  border-bottom: 0;
}

.raw-list {
  margin-top: 12rpx;
  display: flex;
  flex-direction: column;
  gap: 10rpx;
}

.raw-row {
  padding: 12rpx 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.raw-row:last-child {
  border-bottom: 0;
}

.raw-key {
  display: block;
  font-size: 20rpx;
  color: #d4af37;
  word-break: break-all;
  line-height: 1.5;
}

.raw-value {
  display: block;
  margin-top: 6rpx;
  font-size: 22rpx;
  color: #d1d5db;
  line-height: 1.65;
  white-space: pre-wrap;
  word-break: break-all;
}

.atlas-groups {
  margin-top: 18rpx;
  display: flex;
  flex-direction: column;
  gap: 20rpx;
}

.atlas-summary {
  margin-top: 16rpx;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12rpx;
}

.atlas-stat {
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
  border-radius: 12rpx;
  padding: 14rpx 10rpx;
  text-align: center;
}

.atlas-stat-value {
  display: block;
  color: #f5d56a;
  font-weight: 800;
  font-size: 28rpx;
  line-height: 1.2;
}

.atlas-stat-label {
  display: block;
  margin-top: 6rpx;
  color: #9ca3af;
  font-size: 18rpx;
}

.atlas-group {
  border: 1px solid rgba(212, 175, 55, 0.18);
  border-radius: 16rpx;
  padding: 18rpx;
  background: linear-gradient(145deg, rgba(18, 18, 28, 0.88), rgba(10, 10, 16, 0.88));
  box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.22);
}

.atlas-group-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 12rpx;
}

.atlas-group-title {
  font-size: 24rpx;
  color: #f8dda4;
  font-weight: 700;
}

.atlas-group-count {
  font-size: 18rpx;
  color: #9ca3af;
  padding: 4rpx 12rpx;
  border-radius: 999rpx;
  background: rgba(212, 175, 55, 0.12);
  border: 1px solid rgba(212, 175, 55, 0.24);
}

.atlas-rows {
  display: flex;
  flex-direction: column;
  gap: 10rpx;
}

.atlas-row {
  padding: 12rpx;
  border-radius: 12rpx;
  background: rgba(255, 255, 255, 0.02);
  border-bottom: 1px dashed rgba(255, 255, 255, 0.08);
}

.atlas-row:last-child {
  border-bottom: 0;
}

.atlas-key {
  display: block;
  font-size: 20rpx;
  color: #e5c36e;
  line-height: 1.45;
}

.atlas-value {
  display: block;
  margin-top: 6rpx;
  font-size: 22rpx;
  color: #d6d6d6;
  line-height: 1.62;
  white-space: pre-wrap;
  word-break: break-all;
}

.month-summary {
  margin-top: 16rpx;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 12rpx;
}

.month-summary-item {
  padding: 14rpx 12rpx;
  border-radius: 12rpx;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.month-summary-label {
  display: block;
  font-size: 18rpx;
  color: #9ca3af;
}

.month-summary-value {
  display: block;
  margin-top: 8rpx;
  font-size: 24rpx;
  color: #f5d56a;
  font-weight: 700;
  line-height: 1.35;
}

.daily-hero {
  margin-top: 12rpx;
  padding: 20rpx;
  border-radius: 14rpx;
  border: 1px solid rgba(212, 175, 55, 0.22);
  background: linear-gradient(130deg, rgba(212, 175, 55, 0.12), rgba(255, 255, 255, 0.02));
}

.daily-hero-title {
  display: block;
  color: #f8dda4;
  font-size: 28rpx;
  font-weight: 800;
  line-height: 1.35;
}

.daily-hero-sub {
  display: block;
  margin-top: 8rpx;
  color: #c7cad1;
  font-size: 20rpx;
  line-height: 1.6;
}

.daily-meta-grid {
  margin-top: 14rpx;
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12rpx;
}

.daily-meta-card {
  padding: 14rpx;
  border-radius: 12rpx;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(255, 255, 255, 0.04);
}

.daily-meta-label {
  display: block;
  font-size: 18rpx;
  color: #9ca3af;
}

.daily-meta-value {
  display: block;
  margin-top: 8rpx;
  font-size: 24rpx;
  color: #f5d56a;
  font-weight: 700;
  line-height: 1.35;
  word-break: break-all;
}

.daily-yiji-grid {
  margin-top: 14rpx;
  display: grid;
  grid-template-columns: 1fr;
  gap: 10rpx;
}

.daily-yiji-card {
  border-radius: 12rpx;
  padding: 12rpx 14rpx;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.daily-yiji-good {
  background: rgba(16, 185, 129, 0.08);
  border-color: rgba(16, 185, 129, 0.25);
}

.daily-yiji-bad {
  background: rgba(239, 68, 68, 0.08);
  border-color: rgba(239, 68, 68, 0.25);
}

.daily-yiji-label {
  display: block;
  font-size: 18rpx;
  color: #9ca3af;
}

.daily-yiji-value {
  display: block;
  margin-top: 8rpx;
  font-size: 22rpx;
  color: #d1d5db;
  line-height: 1.55;
  white-space: pre-wrap;
  word-break: break-all;
}

.star-item {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 14rpx;
  padding: 14rpx 16rpx;
  border-left: 6rpx solid #d4af37;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.tag {
  font-size: 18rpx;
  padding: 4rpx 12rpx;
  border-radius: 12rpx;
}

.tag-good {
  background: rgba(127, 29, 29, 0.4);
  color: #fca5a5;
}

.tag-mid {
  background: rgba(55, 65, 81, 1);
  color: #9ca3af;
}

.timeline {
  margin-top: 10rpx;
}

.timeline-item {
  position: relative;
  padding-left: 24rpx;
  padding-bottom: 24rpx;
  border-left: 1px solid rgba(255, 255, 255, 0.1);
}

.timeline-item.current {
  border-left-color: rgba(212, 175, 55, 0.8);
}

.dot {
  position: absolute;
  left: -10rpx;
  top: 6rpx;
  width: 18rpx;
  height: 18rpx;
  border-radius: 999rpx;
  background: #333;
  border: 4rpx solid #050508;
}

.dot.on {
  background: #d4af37;
  box-shadow: 0 0 12rpx rgba(212, 175, 55, 0.6);
}

.timeline-body {
  display: flex;
  flex-direction: column;
  gap: 8rpx;
}

.timeline-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

.tl-name {
  font-size: 24rpx;
  font-weight: 800;
}

.pill {
  font-size: 18rpx;
  color: #666;
  background: rgba(255, 255, 255, 0.05);
  padding: 6rpx 14rpx;
  border-radius: 999rpx;
}

.current-tag {
  align-self: flex-start;
  font-size: 18rpx;
  color: #d4af37;
  background: rgba(212, 175, 55, 0.1);
  padding: 6rpx 12rpx;
  border-radius: 12rpx;
}

.taisui-badge {
  width: 96rpx;
  height: 96rpx;
  background: rgba(127, 29, 29, 0.2);
  border: 1px solid rgba(239, 68, 68, 0.3);
  border-radius: 12rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.taisui-text {
  font-size: 40rpx;
  color: #f87171;
  font-weight: 900;
}

.lock-root {
  position: relative;
  min-height: 100%;
}

.lock-root-locked {
  height: min(100vh, 100dvh);
  max-height: min(100vh, 100dvh);
  overflow: hidden;
}

.lock-overlay {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  max-height: calc(100% - 40rpx);
  overflow-y: auto;
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background: rgba(5, 5, 8, 0.95);
  border: 1px solid rgba(212, 175, 55, 0.3);
  border-radius: 18rpx;
  backdrop-filter: blur(10px);
  padding: 48rpx 40rpx;
  width: 85%;
  max-width: 600rpx;
}

.lock-circle {
  width: 96rpx;
  height: 96rpx;
  border-radius: 999rpx;
  background: #d4af37;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 24rpx;
  box-shadow: 0 0 24rpx rgba(212, 175, 55, 0.6);
}

.lock-circle-text {
  color: #000;
  font-weight: 900;
}

.lock-h {
  font-size: 30rpx;
  font-weight: 800;
  margin-bottom: 14rpx;
}

.lock-p {
  text-align: center;
  margin-bottom: 24rpx;
}

.lock-p-text {
  display: block;
  color: #a0a0a0;
}

.lock-p-sub {
  display: block;
  color: rgba(212, 175, 55, 0.8);
  margin-top: 6rpx;
}

.lock-cta {
  padding: 18rpx 44rpx;
  border-radius: 999rpx;
  background: linear-gradient(90deg, #d4af37, #b49120);
}

.lock-cta-text {
  color: #000;
  font-weight: 900;
  letter-spacing: 0.12em;
}

.blurred {
  filter: blur(8px);
  opacity: 0.4;
  pointer-events: none;
}

.clickable {
  transition: opacity 0.15s ease;
}

.clickable:active {
  opacity: 0.82;
}

.detail-modal-overlay {
  position: fixed;
  inset: 0;
  z-index: 120;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28rpx;
}

.detail-modal {
  width: 100%;
  max-width: 680rpx;
  background: rgba(10, 10, 15, 0.98);
  border: 1px solid rgba(212, 175, 55, 0.25);
  border-radius: 18rpx;
  overflow: hidden;
}

.detail-modal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 18rpx 18rpx;
  border-bottom: 1px solid rgba(255, 255, 255, 0.06);
}

.detail-modal-title {
  flex: 1;
  color: #d4af37;
  font-weight: 900;
  font-size: 28rpx;
  padding-right: 16rpx;
}

.detail-modal-close {
  width: 56rpx;
  height: 56rpx;
  border-radius: 999rpx;
  background: rgba(255, 255, 255, 0.06);
  display: flex;
  align-items: center;
  justify-content: center;
}

.detail-modal-close-text {
  color: #a0a0a0;
  font-size: 34rpx;
  line-height: 1;
}

.detail-modal-body {
  max-height: 70vh;
}

.detail-modal-content {
  padding: 18rpx;
}

.detail-modal-text {
  display: block;
  color: #a0a0a0;
  font-size: 24rpx;
  line-height: 1.8;
  margin-bottom: 12rpx;
}

.trend {
  margin-top: 14rpx;
  height: 220rpx;
  position: relative;
}

.trend-bars {
  position: absolute;
  left: 8rpx;
  right: 8rpx;
  top: 10rpx;
  bottom: 40rpx;
  display: flex;
  align-items: flex-end;
  gap: 8rpx;
}

.trend-bar {
  flex: 1;
  height: 100%;
  border-radius: 10rpx 10rpx 0 0;
  background: rgba(255, 255, 255, 0.04);
  overflow: hidden;
  display: flex;
  align-items: flex-end;
}

.trend-bar-fill {
  width: 100%;
  background: linear-gradient(180deg, rgba(212, 175, 55, 0.7), rgba(212, 175, 55, 0.1));
}

.trend-months {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  justify-content: space-between;
  padding: 0 8rpx;
}

.m-table {
  margin-top: 14rpx;
  border-radius: 14rpx;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.m-tr {
  display: flex;
  gap: 12rpx;
  padding: 12rpx;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.m-tr:last-child {
  border-bottom: 0;
}

.m-th {
  background: rgba(212, 175, 55, 0.1);
}

.m-td {
  width: 120rpx;
  font-size: 20rpx;
}

.badge {
  padding: 4rpx 10rpx;
  border-radius: 12rpx;
  font-size: 18rpx;
}

.badge-red {
  background: rgba(127, 29, 29, 0.3);
  color: #fca5a5;
}

.badge-gray {
  background: rgba(31, 41, 55, 1);
  color: #9ca3af;
}

.badge-blue {
  background: rgba(30, 58, 138, 0.3);
  color: #93c5fd;
}

.cal-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 14rpx;
  padding: 0 12rpx;
}

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 12rpx;
}

.cal-cell {
  aspect-ratio: 1 / 1;
  border-radius: 12rpx;
  background: rgba(255, 255, 255, 0.05);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.cal-cell.hit {
  background: rgba(212, 175, 55, 0.2);
  border: 1px solid rgba(212, 175, 55, 0.5);
}

.tag-green {
  background: rgba(20, 83, 45, 0.3);
  color: #4ade80;
}

.tag-red {
  background: rgba(127, 29, 29, 0.3);
  color: #f87171;
}

.hour {
  padding: 10rpx 8rpx;
  border-radius: 12rpx;
  text-align: center;
}

.hour-good {
  background: rgba(212, 175, 55, 0.1);
  color: #d4af37;
}

.hour-bad {
  background: rgba(127, 29, 29, 0.1);
  color: #f87171;
}

.hour-mid {
  background: rgba(255, 255, 255, 0.05);
  color: #666;
}

.action {
  margin-top: 18rpx;
  background: rgba(212, 175, 55, 0.06);
  border: 1px solid rgba(212, 175, 55, 0.2);
  border-radius: 14rpx;
  padding: 18rpx;
}

.noble {
  width: 120rpx;
  height: 160rpx;
  background: #0a0a0f;
  border: 1px solid rgba(212, 175, 55, 0.3);
  border-radius: 14rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.noble-avatar {
  width: 64rpx;
  height: 64rpx;
  border-radius: 999rpx;
  background: rgba(212, 175, 55, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 10rpx;
}

.noble-tag {
  font-size: 14rpx;
  color: #d4af37;
  border: 1px solid rgba(212, 175, 55, 0.3);
  padding: 4rpx 10rpx;
  border-radius: 10rpx;
}

.dotc {
  width: 44rpx;
  height: 44rpx;
  border-radius: 999rpx;
  box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.3);
}

.box {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 14rpx;
  padding: 18rpx;
}

.noble-avatar-text {
  font-size: 36rpx;
}

.karma {
  display: flex;
  flex-direction: column;
  gap: 16rpx;
}

.karma-item {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 14rpx;
  padding: 18rpx;
  display: flex;
  gap: 16rpx;
  align-items: flex-start;
}

.karma-icon {
  width: 48rpx;
  height: 48rpx;
  border-radius: 12rpx;
  background: rgba(160, 160, 160, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
}

.karma-icon-gold {
  background: rgba(212, 175, 55, 0.12);
}

.karma-icon-text {
  color: #a0a0a0;
  font-size: 18rpx;
  font-weight: 900;
}

.radar {
  align-items: center;
  gap: 24rpx;
}

.radar-left {
  position: relative;
  width: 260rpx;
  height: 260rpx;
}

.radar-svg {
  width: 100%;
  height: 100%;
  transform: rotate(180deg);
}

.radar-label {
  position: absolute;
  font-size: 14rpx;
  color: #e2e2e2;
}

.radar-top {
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  font-weight: 900;
}

.radar-right-pos {
  right: 0;
  top: 35%;
  transform: translateX(6rpx);
}

.radar-left-label {
  left: 0;
  top: 35%;
  transform: translateX(-6rpx);
}

.radar-br {
  bottom: 0;
  right: 5%;
}

.radar-bl {
  bottom: 0;
  left: 5%;
}

.radar-right {
  flex: 1;
  border-left: 1px solid rgba(255, 255, 255, 0.05);
  padding-left: 18rpx;
  display: flex;
  flex-direction: column;
  gap: 16rpx;
}

.bar-item {
  display: flex;
  flex-direction: column;
  gap: 8rpx;
}

.bar-head {
  display: flex;
  justify-content: space-between;
}

.bar-track {
  width: 100%;
  height: 8rpx;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 999rpx;
  overflow: hidden;
}

.bar-fill {
  height: 100%;
}

.grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}

.annual-head {
  align-items: flex-start;
}

.annual-tag {
  width: 96rpx;
  height: 96rpx;
  border-radius: 14rpx;
  background: #d4af37;
  color: #000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.annual-tag-text {
  font-size: 40rpx;
  font-weight: 900;
}

.annual-pill {
  margin-top: 10rpx;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 12rpx;
  padding: 8rpx 12rpx;
  text-align: center;
}

.annual-chart {
  margin-top: 18rpx;
  background: rgba(26, 26, 46, 0.8);
  border-radius: 14rpx;
  padding: 16rpx;
  border: 1px solid rgba(255, 255, 255, 0.05);
}

.annual-chart-head {
  justify-content: space-between;
  align-items: center;
}

.annual-bars {
  height: 180rpx;
  display: flex;
  align-items: flex-end;
  gap: 8rpx;
  margin-top: 16rpx;
}

.annual-bar {
  flex: 1;
  background: rgba(255, 255, 255, 0.05);
  border-radius: 10rpx 10rpx 0 0;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
}

.annual-bar-fill {
  width: 100%;
  background: rgba(212, 175, 55, 0.5);
}

.annual-bars-foot {
  justify-content: space-between;
  margin-top: 8rpx;
}

.star-chip {
  padding: 16rpx 10rpx;
  border-radius: 14rpx;
  text-align: center;
  border: 1px solid rgba(212, 175, 55, 0.3);
  background: rgba(212, 175, 55, 0.1);
  color: #d4af37;
}

.star-chip.bad {
  border-color: rgba(239, 68, 68, 0.3);
  background: rgba(127, 29, 29, 0.1);
  color: #f87171;
}

.role-list {
  display: flex;
  flex-direction: column;
}

.role-item {
  display: flex;
  gap: 16rpx;
  padding: 18rpx 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.role-item.last {
  border-bottom: 0;
}

.role-icon {
  width: 44rpx;
  height: 44rpx;
  border-radius: 12rpx;
  display: flex;
  align-items: center;
  justify-content: center;
}

.compass {
  position: relative;
  width: 420rpx;
  height: 420rpx;
  border-radius: 999rpx;
  background: #0a0a0f;
  border: 2px solid rgba(255, 255, 255, 0.05);
  margin: 0 auto 20rpx;
}

.compass-center {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 12rpx;
  height: 12rpx;
  border-radius: 999rpx;
  background: #d4af37;
  transform: translate(-50%, -50%);
}

.compass-t {
  position: absolute;
  top: 10rpx;
  left: 50%;
  transform: translateX(-50%);
  font-size: 14rpx;
  color: #f87171;
  font-weight: 900;
}

.compass-b {
  position: absolute;
  bottom: 10rpx;
  left: 50%;
  transform: translateX(-50%);
  font-size: 14rpx;
  color: #666;
}

.compass-l {
  position: absolute;
  left: 10rpx;
  top: 50%;
  transform: translateY(-50%);
  font-size: 14rpx;
  color: #666;
}

.compass-r {
  position: absolute;
  right: 10rpx;
  top: 50%;
  transform: translateY(-50%);
  font-size: 14rpx;
  color: #666;
}

.compass-tr {
  position: absolute;
  top: 15%;
  right: 15%;
  font-size: 12rpx;
  color: #a0a0a0;
}

.compass-tl {
  position: absolute;
  top: 15%;
  left: 15%;
  font-size: 12rpx;
}

.compass-br {
  position: absolute;
  bottom: 15%;
  right: 15%;
  font-size: 12rpx;
  color: #666;
}

.compass-bl {
  position: absolute;
  bottom: 15%;
  left: 15%;
  font-size: 12rpx;
}

.pie {
  width: 160rpx;
  height: 160rpx;
  position: relative;
}

.pie-svg {
  width: 100%;
  height: 100%;
  transform: rotate(-90deg);
}

.pie-center {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

.between {
  justify-content: space-between;
  width: 100%;
}

.box-i {
  border-radius: 14rpx;
  padding: 18rpx;
  border: 1px solid rgba(255, 255, 255, 0.08);
}

.box-i.good {
  background: rgba(20, 83, 45, 0.12);
  border-color: rgba(34, 197, 94, 0.2);
}

.box-i.bad {
  background: rgba(127, 29, 29, 0.12);
  border-color: rgba(239, 68, 68, 0.2);
}

.flying {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 8rpx;
  width: 480rpx;
  max-width: 100%;
  aspect-ratio: 1 / 1;
  margin: 0 auto;
  position: relative;
}

.flying-border {
  position: absolute;
  inset: 0;
  border: 2px solid rgba(212, 175, 55, 0.2);
  border-radius: 14rpx;
  pointer-events: none;
}

.flying-cell {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 14rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.flying-num {
  font-size: 40rpx;
  font-weight: 900;
  color: #666;
}

.flying-good {
  margin-top: 6rpx;
  font-size: 14rpx;
  color: #d4af37;
  background: rgba(212, 175, 55, 0.1);
  padding: 2rpx 8rpx;
  border-radius: 10rpx;
}

.deep {
  background: #1a1a2e;
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 14rpx;
  padding: 18rpx;
  margin-bottom: 16rpx;
}

.qimen-box {
  margin-top: 14rpx;
  padding: 16rpx;
  border-radius: 16rpx;
  background: rgba(26, 26, 46, 0.55);
  border: 1px solid rgba(212, 175, 55, 0.18);
}

.qimen-top-tag {
  display: flex;
  justify-content: center;
  margin-bottom: 12rpx;
}

.qimen-top-tag-text {
  display: inline-block;
  padding: 4rpx 12rpx;
  border-radius: 999rpx;
  border: 1px solid rgba(212, 175, 55, 0.6);
  background: rgba(0, 0, 0, 0.45);
  color: #d4af37;
  font-size: 18rpx;
  font-weight: 900;
}

.qimen-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 0;
  border-radius: 14rpx;
  overflow: hidden;
  border: 2rpx solid rgba(212, 175, 55, 0.55);
}

.qimen-cell {
  aspect-ratio: 1 / 1;
  padding: 10rpx;
  background: rgba(10, 10, 15, 0.35);
  border-right: 2rpx solid rgba(212, 175, 55, 0.45);
  border-bottom: 2rpx solid rgba(212, 175, 55, 0.45);
}

.qimen-cell:nth-child(3n) {
  border-right: 0;
}

.qimen-cell:nth-last-child(-n + 3) {
  border-bottom: 0;
}

.qimen-center {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 6rpx;
}

.qimen-center-title {
  font-size: 30rpx;
  font-weight: 900;
  color: #d4af37;
}

.qimen-center-sub {
  font-size: 20rpx;
  color: rgba(212, 175, 55, 0.7);
}

.qimen-cell-inner {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
}

.qimen-loc {
  display: none;
}

.qimen-top {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
}

.qimen-spirit {
  font-size: 22rpx;
  font-weight: 800;
  color: #e5e7eb;
}

.qimen-stems {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2rpx;
}

.qimen-stem {
  font-size: 22rpx;
  font-weight: 900;
  color: #f87171;
  line-height: 1;
}

.qimen-bottom {
  display: flex;
  justify-content: space-between;
  align-items: flex-end;
}

.qimen-star {
  font-size: 22rpx;
  font-weight: 800;
  color: #e5e7eb;
}

.qimen-door {
  font-size: 20rpx;
  font-weight: 900;
  color: #d4af37;
  padding: 2rpx 8rpx;
  border-radius: 8rpx;
  border: 1px solid rgba(212, 175, 55, 0.75);
  background: rgba(0, 0, 0, 0.45);
  line-height: 1.2;
}

.qimen-door-active {
  box-shadow: 0 0 10rpx rgba(212, 175, 55, 0.35);
}

.qimen-gong {
  position: absolute;
  left: 50%;
  bottom: 4rpx;
  transform: translateX(-50%);
  font-size: 18rpx;
  color: #a0a0a0;
}

.deep-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
  padding-bottom: 12rpx;
}

.deep-month {
  font-size: 32rpx;
  font-weight: 900;
}

.deep-chip {
  font-size: 14rpx;
  padding: 4rpx 10rpx;
  border-radius: 12rpx;
  border: 1px solid rgba(212, 175, 55, 0.2);
  background: rgba(212, 175, 55, 0.1);
  color: #d4af37;
}

.star {
  font-size: 18rpx;
  color: #333;
}

.star.on {
  color: #d4af37;
}

.sha {
  display: flex;
  flex-direction: column;
  gap: 16rpx;
}

.sha-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-size: 16rpx;
  padding-bottom: 12rpx;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.sha-row.last {
  border-bottom: 0;
  padding-bottom: 0;
}

.wealth-corner {
  display: flex;
  flex-direction: column;
  gap: 12rpx;
}

.corner-diagram {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 14rpx;
  background: #0a0a0f;
  border: 1px solid rgba(255, 255, 255, 0.1);
}

.door {
  position: absolute;
  bottom: 0;
  left: 50%;
  transform: translateX(-50%);
  width: 80rpx;
  height: 10rpx;
  background: #333;
}

.corner-dot {
  position: absolute;
  top: 12rpx;
  left: 12rpx;
  width: 56rpx;
  height: 56rpx;
  border-radius: 999rpx;
  border: 1px solid #d4af37;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(212, 175, 55, 0.06);
}

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

.room-row {
  display: flex;
  gap: 16rpx;
  padding: 18rpx 0;
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.room-row.last {
  border-bottom: 0;
}

.room-icon {
  width: 44rpx;
  height: 44rpx;
  border-radius: 12rpx;
  background: rgba(255, 255, 255, 0.05);
  display: flex;
  align-items: center;
  justify-content: center;
}

.room-tag {
  font-size: 14rpx;
  padding: 4rpx 10rpx;
  border-radius: 10rpx;
}

.room-good {
  background: rgba(20, 83, 45, 0.2);
  color: #4ade80;
}

.room-bad {
  background: rgba(127, 29, 29, 0.2);
  color: #f87171;
}

.room-gold {
  background: rgba(212, 175, 55, 0.12);
  color: #d4af37;
}

.love-icon {
  width: 80rpx;
  height: 80rpx;
  border-radius: 999rpx;
  background: rgba(236, 72, 153, 0.12);
  border: 1px solid rgba(236, 72, 153, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
  color: #f472b6;
}

.pet-box {
  background: rgba(255, 255, 255, 0.05);
  border-radius: 14rpx;
  padding: 18rpx;
  text-align: center;
  border-top: 6rpx solid #d4af37;
}

.wallet {
  width: 96rpx;
  height: 80rpx;
  background: rgba(0, 0, 0, 0.4);
  border-radius: 14rpx;
  border: 1px solid #d4af37;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

<style>
/* 全局样式：重置 page 默认样式，确保页面占满全屏 */
page {
  width: 100%;
  height: 100%;
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  overflow: hidden;
}
</style>
