<template>
  <view class="test-name-detail" :class="{ 'test-name-detail--desktop': desktopLayout }">
    <view class="starry-bg">
      <view v-for="star in stars" :key="star.id" class="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="glow-top"></view>
      <view class="glow-bottom"></view>
    </view>

    <view class="detail-header">
      <view class="detail-header-back" @click="emit('back')">
        <text class="back-icon">‹</text>
        <text class="back-text">返回</text>
      </view>
      <text class="detail-header-title">测名详解</text>
      <view class="detail-header-download" @click="handleDownloadPdf">
        <text class="detail-header-download-icon">↓</text>
        <text class="detail-header-download-text">下载PDF</text>
      </view>
    </view>

    <scroll-view scroll-y class="detail-content" :class="{ 'detail-content--desktop': desktopLayout }">
      <!-- 1 分数 -->
      <view class="score-card mystic-score-card clickable module-order-1" @click="openDetail('总分详解', dataObj?.header?.details?.nodes)">
        <view class="mystic-score-card__glow" />
        <view class="score-corner score-corner-tl"></view>
        <view class="score-corner score-corner-tr"></view>
        <view class="score-corner score-corner-bl"></view>
        <view class="score-corner score-corner-br"></view>

        <view class="score-label">命盘总分</view>
        <view class="score-value-wrap">
          <view v-if="desktopLayout" class="score-bagua-aura">
            <view class="score-yinyang-bg" aria-hidden="true"></view>
            <view class="score-rune-particles" aria-hidden="true">
              <view class="score-rune-dot" />
              <view class="score-rune-dot" />
              <view class="score-rune-dot" />
              <view class="score-rune-dot" />
              <view class="score-rune-dot" />
              <view class="score-rune-dot" />
              <view class="score-rune-dot" />
              <view class="score-rune-dot" />
              <text class="score-rune-glyph">✦</text>
              <text class="score-rune-glyph">✧</text>
              <text class="score-rune-glyph">⋆</text>
              <text class="score-rune-glyph">⁂</text>
            </view>
            <view class="score-bagua-core"></view>
            <view class="score-bagua-ring-2"></view>
            <view class="score-bagua-symbols">
              <text class="score-bagua-symbol">☰</text>
              <text class="score-bagua-symbol">☱</text>
              <text class="score-bagua-symbol">☲</text>
              <text class="score-bagua-symbol">☳</text>
              <text class="score-bagua-symbol">☴</text>
              <text class="score-bagua-symbol">☵</text>
              <text class="score-bagua-symbol">☶</text>
              <text class="score-bagua-symbol">☷</text>
            </view>
          </view>
          <text class="score">{{ num(dataObj?.header?.score) }}</text>
          <view class="score-ring"></view>
        </view>
        <view class="score-stars">
          <text v-for="i in 5" :key="i" class="score-star" :class="{ active: i <= num(dataObj?.header?.star_rating) }">★</text>
        </view>
        <view class="score-divider"></view>
        <text class="name">{{ toText(dataObj?.header?.name) || '—' }}</text>
        <text class="quote">{{ toText(dataObj?.header?.poem_quote) }}</text>
        <view v-if="desktopLayout" class="score-mystic-ornaments">
          <view class="score-mystic-line"></view>
          <view class="score-mystic-bagua">
            <text class="score-mystic-symbol">乾</text>
            <text class="score-mystic-dot">·</text>
            <text class="score-mystic-symbol">坤</text>
            <text class="score-mystic-dot">·</text>
            <text class="score-mystic-symbol">震</text>
            <text class="score-mystic-dot">·</text>
            <text class="score-mystic-symbol">巽</text>
            <text class="score-mystic-dot">·</text>
            <text class="score-mystic-symbol">坎</text>
            <text class="score-mystic-dot">·</text>
            <text class="score-mystic-symbol">离</text>
          </view>
          <view class="score-mystic-sigil">
            <text class="score-mystic-sigil-text">☯</text>
          </view>
        </view>
        <view class="detail-entry">
          <text class="detail-entry-text">查看总分详解</text>
        </view>
      </view>

      <!-- 2 五行八卦（相生相克） -->
      <view v-if="hasWuxingBagua" class="section mystic-panel module-order-2">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">卦</text>
          <text class="section-text">五行八卦</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="wuge-card mystic-inner-card clickable" @click="openWuxingBaguaDetail">
          <view v-if="hasWuxingSupplement" class="wuxing-adapt-grid">
            <text v-if="toText(dataObj?.wuxing_bagua?.lacking)" class="wuxing-adapt-item"><text class="wuxing-adapt-label">先天偏缺</text>{{ toText(dataObj?.wuxing_bagua?.lacking) }}</text>
            <text v-if="toText(dataObj?.wuxing_bagua?.supplement)" class="wuxing-adapt-item"><text class="wuxing-adapt-label">补益建议</text>{{ toText(dataObj?.wuxing_bagua?.supplement) }}</text>
          </view>
          <text v-if="toText(dataObj?.wuxing_bagua?.wuxing_sketch)" class="line line-strong">五行：{{ toText(dataObj?.wuxing_bagua?.wuxing_sketch) }}</text>
          <text v-if="toText(dataObj?.wuxing_bagua?.bagua_profile)" class="line">八卦：{{ toText(dataObj?.wuxing_bagua?.bagua_profile) }}</text>
          <text v-if="toText(dataObj?.wuxing_bagua?.mutual_sketch)" class="line">{{ toText(dataObj?.wuxing_bagua?.mutual_sketch) }}</text>
          <text v-if="toText(dataObj?.wuxing_bagua?.summary)" class="line">{{ toText(dataObj?.wuxing_bagua?.summary) }}</text>
        </view>

        <!-- 五行单字详解（放到五行八卦下面） -->
        <view v-if="hasCharDetail" class="mystic-panel mystic-panel--extra">
          <view class="section-title mystic-section-title">
            <text class="section-glyph">字</text>
            <text class="section-text">五行单字详解</text>
            <text class="section-detail-tip">详解</text>
          </view>
          <view class="info-card mystic-inner-card clickable" @click="openCharDetail">
            <view v-for="(it, idx) in arr(dataObj?.char_detail?.items)" :key="idx" class="line">
              {{ toText(it?.char) }}
              <text v-if="toText(it?.element)"> · {{ toText(it?.element) }}</text>
              <text v-if="toText(it?.yin_yang_element) || toText(it?.yin_yang)"> · {{ toText(it?.yin_yang_element) || toText(it?.yin_yang) }}</text>
              <text v-if="toText(it?.bagua_trigram_symbol) || toText(it?.bagua_trigram)"> · 卦：{{ toText(it?.bagua_trigram_symbol) }}{{ toText(it?.bagua_trigram_symbol) && toText(it?.bagua_trigram) ? ' ' : '' }}{{ toText(it?.bagua_trigram) }}</text>
              <text v-if="toText(it?.stroke)"> · {{ toText(it?.stroke) }}</text>
              <text v-if="toText(it?.tone)"> · {{ toText(it?.tone) }}</text>
            </view>
          </view>
        </view>
      </view>

      <!-- 3 五行相生还是相克 -->
      <view v-if="hasBaziNameFit" class="section mystic-panel module-order-3">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">☯</text>
          <text class="section-text">八字与姓名五行</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="zodiac-card mystic-inner-card clickable" @click="openDetail('八字与姓名五行详解', dataObj?.bazi_name_fit?.details?.nodes)">
          <view class="zodiac-header">
            <view class="zodiac-info">
              <text v-if="toText(dataObj?.bazi_name_fit?.xiyongshen)" class="line">喜用神：{{ toText(dataObj?.bazi_name_fit?.xiyongshen) }}</text>
              <text v-if="toText(dataObj?.bazi_name_fit?.name_wuxing_profile)" class="line">姓名五行：{{ toText(dataObj?.bazi_name_fit?.name_wuxing_profile) }}</text>
              <text v-if="toText(dataObj?.bazi_name_fit?.complement_summary)" class="line">{{ toText(dataObj?.bazi_name_fit?.complement_summary) }}</text>
            </view>
            <text
              v-if="dataObj?.bazi_name_fit?.fit_score !== undefined && dataObj?.bazi_name_fit?.fit_score !== null && dataObj?.bazi_name_fit?.fit_score !== ''"
              class="zodiac-score"
            >{{ num(dataObj?.bazi_name_fit?.fit_score) }}</text>
          </view>
        </view>
      </view>

      <!-- 4 三才五格 -->
      <view class="section mystic-panel module-order-4">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">爻</text>
          <text class="section-text">笔画 · 五格 · 三才</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="wuge-card mystic-inner-card clickable" @click="openDetail('笔画数理详解', dataObj?.strokes_wuge_sancai?.details?.nodes)">
          <view v-for="(it, idx) in arr(dataObj?.strokes_wuge_sancai?.wuge)" :key="idx" class="line">
            {{ toText(it?.name) }}：{{ toText(it?.desc) }}
          </view>
          <text class="line">三才：{{ toText(dataObj?.strokes_wuge_sancai?.sancai?.result) }}</text>
        </view>
      </view>

      <!-- 5 六爻 -->
      <view v-if="hasLiuyao" class="section mystic-panel module-order-5">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">卦</text>
          <text class="section-text">六爻</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="meaning-card mystic-inner-card clickable" @click="openDetail('六爻详解', dataObj?.liuyao?.details?.nodes)">
          <text v-if="toText(dataObj?.liuyao?.hexagram_title)" class="line line-strong">{{ toText(dataObj?.liuyao?.hexagram_title) }}</text>
          <text v-if="toText(dataObj?.liuyao?.changing_summary)" class="line">动爻：{{ toText(dataObj?.liuyao?.changing_summary) }}</text>
          <text v-if="toText(dataObj?.liuyao?.interpretation)" class="line">{{ toText(dataObj?.liuyao?.interpretation) }}</text>
          <view v-for="(yl, yi) in arr(dataObj?.liuyao?.yao_lines).slice(0, 6)" :key="yi" class="line">{{ toText(yl) }}</view>
        </view>
      </view>

      <!-- 6 属相 -->
      <view v-if="hasZodiacSign" class="section mystic-panel module-order-6">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">肖</text>
          <text class="section-text">属相</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="zodiac-card mystic-inner-card clickable" @click="openZodiacDetail">
          <view class="zodiac-header">
            <view class="zodiac-icon-wrap">
              <text class="zodiac-icon">{{ toText(dataObj?.zodiac_sign?.animal_icon) || toText(dataObj?.zodiac_sign?.animal) || '属相' }}</text>
            </view>
            <view class="zodiac-info">
              <text class="zodiac-label">属相</text>
              <text class="zodiac-value">{{ toText(dataObj?.zodiac_sign?.animal) }} {{ toText(dataObj?.zodiac_sign?.earthly_branch) }}</text>
              <text v-if="toText(dataObj?.zodiac_sign?.trait_summary)" class="line trait-line">{{ toText(dataObj?.zodiac_sign?.trait_summary) }}</text>
              <text v-if="toText(dataObj?.zodiac_sign?.name_harmony)" class="line trait-line">与名：{{ toText(dataObj?.zodiac_sign?.name_harmony) }}</text>
              <view v-if="hasZodiacAdaptation" class="zodiac-adapt-grid">
                <text v-if="toText(dataObj?.zodiac_sign?.sanhe)" class="zodiac-adapt-item"><text class="zodiac-adapt-label">三合</text>{{ toText(dataObj?.zodiac_sign?.sanhe) }}</text>
                <text v-if="toText(dataObj?.zodiac_sign?.liuhe)" class="zodiac-adapt-item"><text class="zodiac-adapt-label">六合</text>{{ toText(dataObj?.zodiac_sign?.liuhe) }}</text>
                <text v-if="toText(dataObj?.zodiac_sign?.xiangchong)" class="zodiac-adapt-item"><text class="zodiac-adapt-label">相冲</text>{{ toText(dataObj?.zodiac_sign?.xiangchong) }}</text>
                <text v-if="toText(dataObj?.zodiac_sign?.xianghai)" class="zodiac-adapt-item"><text class="zodiac-adapt-label">相害</text>{{ toText(dataObj?.zodiac_sign?.xianghai) }}</text>
                <text v-if="toText(dataObj?.zodiac_sign?.xiangxing)" class="zodiac-adapt-item"><text class="zodiac-adapt-label">相刑</text>{{ toText(dataObj?.zodiac_sign?.xiangxing) }}</text>
              </view>
            </view>
          </view>
        </view>
      </view>

      <!-- 7 幸运数字 -->
      <view v-if="hasLuckyNumbers" class="section mystic-panel module-order-7">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">数</text>
          <text class="section-text">幸运数字</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="info-card mystic-inner-card clickable" @click="openDetail('幸运数字详解', dataObj?.lucky_numbers?.details?.nodes)">
          <text v-if="toText(dataObj?.lucky_numbers?.primary)" class="line line-strong">主推：{{ toText(dataObj?.lucky_numbers?.primary) }}</text>
          <text v-if="luckyNumbersDisplay" class="line">{{ luckyNumbersDisplay }}</text>
          <text v-if="toText(dataObj?.lucky_numbers?.meaning)" class="line">{{ toText(dataObj?.lucky_numbers?.meaning) }}</text>
        </view>
      </view>

      <!-- 8 颜色 -->
      <view v-if="hasLuckyColors" class="section mystic-panel module-order-8">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">色</text>
          <text class="section-text">幸运颜色</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="info-card mystic-inner-card clickable" @click="openDetail('幸运颜色详解', dataObj?.lucky_colors?.details?.nodes)">
          <text v-if="toText(dataObj?.lucky_colors?.primary)" class="line line-strong">主推：{{ toText(dataObj?.lucky_colors?.primary) }}</text>
          <view v-for="(c, ci) in arr(dataObj?.lucky_colors?.colors)" :key="ci" class="lucky-color-row">
            <view
              v-if="toText(c?.hex)"
              class="lucky-color-swatch"
              :style="{ backgroundColor: toText(c?.hex) }"
            />
            <text class="line lucky-color-text">{{ toText(c?.name) }}{{ toText(c?.note) ? ' — ' + toText(c.note) : '' }}</text>
          </view>
          <text v-if="toText(dataObj?.lucky_colors?.meaning)" class="line">{{ toText(dataObj?.lucky_colors?.meaning) }}</text>
        </view>
      </view>

      <!-- 9 事业规划 -->
      <view v-if="hasCareerPlan" class="section mystic-panel module-order-9">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">业</text>
          <text class="section-text">事业规划</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="info-card mystic-inner-card clickable" @click="openDetail('事业规划详解', dataObj?.career_plan?.details?.nodes)">
          <text v-if="toText(dataObj?.career_plan?.summary)" class="line">{{ toText(dataObj?.career_plan?.summary) }}</text>
          <view v-for="(m, mi) in arr(dataObj?.career_plan?.milestones)" :key="mi" class="career-milestone">
            <text class="line line-strong">{{ toText(m?.phase) }}{{ toText(m?.period) ? ' · ' + toText(m.period) : '' }}</text>
            <text v-if="toText(m?.focus)" class="line">{{ toText(m.focus) }}</text>
            <text v-if="toText(m?.advice)" class="line">{{ toText(m.advice) }}</text>
          </view>
        </view>
      </view>

      <!-- 10 开运锦囊 -->
      <view class="section mystic-panel module-order-10">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">锦</text>
          <text class="section-text">开运锦囊</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="info-card mystic-inner-card clickable" @click="openDetail('开运锦囊详解', dataObj?.lucky_tips?.details?.nodes)">
          <view v-for="(it, idx) in arr(dataObj?.lucky_tips?.items)" :key="idx" class="line">
            {{ toText(it?.icon) }} {{ toText(it?.label) }}：{{ toText(it?.value) }}
          </view>
        </view>
      </view>

      <!-- 11 人生运程 -->
      <view class="section mystic-panel module-order-11">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">运</text>
          <text class="section-text">人生运程</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="wuxing-card mystic-inner-card clickable" @click="openDetail('人生运程详解', dataObj?.lifespan?.details?.nodes)">
          <view v-for="(it, idx) in arr(dataObj?.lifespan?.items)" :key="idx" class="line">
            {{ toText(it?.age) }}：{{ toText(it?.desc) }}
          </view>
        </view>
      </view>

      <!-- 12 诗词出处 -->
      <view class="section mystic-panel module-order-12">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">诗</text>
          <text class="section-text">诗词出处</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="meaning-card mystic-inner-card clickable" @click="openDetail('诗词出处详解', dataObj?.poetry_source?.details?.nodes)">
          <text class="line">{{ toText(dataObj?.poetry_source?.text) }}</text>
          <text class="line">{{ toText(dataObj?.poetry_source?.source) }}</text>
        </view>
      </view>

      <!-- 13 六维格局 -->
      <view class="section mystic-panel section--sixdim-compact module-order-13">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">维</text>
          <text class="section-text">六维格局</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="clickable mystic-inner-card mystic-inner-card--flush" @click="openDetail('六维格局详解', dataObj?.six_dimension?.details?.nodes)">
          <SixDimensionRadarDesktopEchart
            v-if="desktopLayout"
            :labels="arr(dataObj?.six_dimension?.labels)"
            :values="arr(dataObj?.six_dimension?.values)"
            :remark="toText(dataObj?.six_dimension?.remark)"
          />
          <SixDimensionRadar
            v-else
            :labels="arr(dataObj?.six_dimension?.labels)"
            :values="arr(dataObj?.six_dimension?.values)"
            :remark="toText(dataObj?.six_dimension?.remark)"
          />
        </view>
      </view>

      <!-- 14 大师寄语 -->
      <view class="section mystic-panel module-order-14">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">师</text>
          <text class="section-text">大师寄语</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="meaning-card mystic-inner-card clickable" @click="openDetail('大师寄语详解', dataObj?.master_message?.details?.nodes)">
          <text class="line">{{ toText(dataObj?.master_message?.text) }}</text>
        </view>
      </view>

      <!-- 其余模块置后 -->
      <view class="section mystic-panel mystic-panel--extra">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">意</text>
          <text class="section-text">字义与生肖</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="meaning-card mystic-inner-card clickable" @click="openDetail('字义与生肖详解', dataObj?.meaning_and_zodiac?.details?.nodes)">
          <view class="meaning-quote">「</view>
          <text class="meaning-label">{{ toText(dataObj?.meaning_and_zodiac?.imagery_title) || "组合意象" }}</text>
          <text class="meaning-text">{{ toText(dataObj?.meaning_and_zodiac?.imagery_text) }}</text>
          <view class="meaning-judge">
            <text class="meaning-judge-label">判断：</text>
            <text class="meaning-judge-text">{{ toText(dataObj?.meaning_and_zodiac?.judgement) }}</text>
          </view>
        </view>
        <view class="zodiac-card mystic-inner-card clickable" @click="openDetail('字义与生肖详解', dataObj?.meaning_and_zodiac?.details?.nodes)">
          <view class="zodiac-header">
            <view class="zodiac-icon-wrap">
              <text class="zodiac-icon">{{ toText(dataObj?.meaning_and_zodiac?.zodiac?.animal) || "🐉" }}</text>
            </view>
            <view class="zodiac-info">
              <text class="zodiac-label">生肖适配</text>
              <text class="zodiac-value">{{ toText(dataObj?.meaning_and_zodiac?.zodiac?.label) }}</text>
            </view>
            <text class="zodiac-score">{{ num(dataObj?.meaning_and_zodiac?.zodiac?.score) }}</text>
          </view>
        </view>
      </view>

      <!-- 单字详解已移到“五行八卦”下面 -->

      <view v-if="hasPhonetics" class="section mystic-panel mystic-panel--extra">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">音</text>
          <text class="section-text">音律音韵</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="meaning-card mystic-inner-card clickable" @click="openDetail('音律音韵详解', dataObj?.phonetics?.details?.nodes)">
          <text v-if="toText(dataObj?.phonetics?.pinyin_full)" class="line line-strong">{{ toText(dataObj?.phonetics?.pinyin_full) }}</text>
          <text v-if="toText(dataObj?.phonetics?.pingze)" class="line">声调与平仄：{{ toText(dataObj?.phonetics?.pingze) }}</text>
          <text v-if="toText(dataObj?.phonetics?.rhythm_summary)" class="line">{{ toText(dataObj?.phonetics?.rhythm_summary) }}</text>
          <view v-if="toText(dataObj?.phonetics?.judgement)" class="meaning-judge">
            <text class="meaning-judge-label">简评：</text>
            <text class="meaning-judge-text">{{ toText(dataObj?.phonetics?.judgement) }}</text>
          </view>
        </view>
      </view>

      <view class="section mystic-panel mystic-panel--extra">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">易</text>
          <text class="section-text">周易卦象</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="zodiac-card mystic-inner-card clickable" @click="openGuaDetail">
          <text class="line line-strong">{{ toText(dataObj?.gua?.name) }}</text>
          <text v-if="dataObj?.gua?.hexagram_code !== undefined && dataObj?.gua?.hexagram_code !== null && dataObj?.gua?.hexagram_code !== ''" class="line">卦序：{{ toText(dataObj?.gua?.hexagram_code) }}</text>
          <text v-if="toText(dataObj?.gua?.bgText)" class="line">卦名：{{ toText(dataObj?.gua?.bgText) }}</text>
          <text v-if="toText(dataObj?.gua?.badge)" class="line">卦象：{{ toText(dataObj?.gua?.badge) }}</text>
          <text class="line">{{ toText(dataObj?.gua?.desc) }}</text>
        </view>
      </view>

      <view v-if="hasNamePopularity" class="section mystic-panel mystic-panel--extra">
        <view class="section-title mystic-section-title">
          <text class="section-glyph">名</text>
          <text class="section-text">重名与流行度</text>
          <text class="section-detail-tip">详解</text>
        </view>
        <view class="info-card mystic-inner-card clickable" @click="openDetail('重名与流行度详解', dataObj?.name_popularity?.details?.nodes)">
          <text v-if="toText(dataObj?.name_popularity?.duplicate_rate_label)" class="line line-strong">
            重名提示：{{ toText(dataObj?.name_popularity?.duplicate_rate_label) }}
          </text>
          <text v-if="toText(dataObj?.name_popularity?.popularity_tier)" class="line">
            流行度：{{ toText(dataObj?.name_popularity?.popularity_tier) }}
          </text>
          <text v-if="toText(dataObj?.name_popularity?.interpretation)" class="line">{{ toText(dataObj?.name_popularity?.interpretation) }}</text>
        </view>
      </view>

      <view class="bottom-spacer"></view>
    </scroll-view>

    <view v-if="showModal" class="modal-mask" @click="closeDetail">
      <view class="detail-modal" @click.stop="">
        <view class="detail-modal-orn detail-modal-orn--tl" />
        <view class="detail-modal-orn detail-modal-orn--tr" />
        <view class="detail-modal-orn detail-modal-orn--bl" />
        <view class="detail-modal-orn detail-modal-orn--br" />
        <view class="detail-modal-card">
          <view class="modal-title-wrap">
            <view class="modal-title-seal">符</view>
            <text class="modal-title">{{ modalTitle }}</text>
          </view>
          <view class="close" @click="closeDetail">×</view>
        </view>
        <scroll-view scroll-y class="detail-modal-body">
          <view v-for="(node, idx) in modalNodes" :key="idx" class="node">
            <text v-if="node?.type === 'text'" class="line">{{ toText(node.text) }}</text>
            <view v-else-if="node?.type === 'list'">
              <text v-for="(item, j) in arr(node.items)" :key="j" class="line">- {{ toText(item) }}</text>
            </view>
            <view v-else-if="node?.type === 'kv'">
              <text v-for="(item, j) in arr(node.items)" :key="j" class="line">{{ toText(item?.label) }}：{{ toText(item?.value) }}</text>
            </view>
          </view>
        </scroll-view>
      </view>
    </view>
  </view>
</template>

<script setup lang="ts">
import { computed, ref } from "vue";
import SixDimensionRadar from "../SixDimensionRadar.vue";
import SixDimensionRadarDesktopEchart from "../SixDimensionRadarDesktopEchart.vue";
import pdfBgUrl from "../../utils/pdf/background.png";

const props = withDefaults(
  defineProps<{ data: any; desktopLayout?: boolean }>(),
  { desktopLayout: false },
);
const emit = defineEmits<{ back: [] }>();

const tryParse = (v: any) => {
  if (v && typeof v === "object") return v;
  if (typeof v !== "string") return {};
  try {
    return JSON.parse(v);
  } catch {
    return {};
  }
};

const dataObj = computed(() => tryParse(props.data));
const toText = (v: any) => String(v ?? "").trim();

const hasPhonetics = computed(() => {
  const p = dataObj.value?.phonetics;
  if (!p || typeof p !== "object") return false;
  return !!(
    toText(p.pinyin_full) ||
    toText(p.pingze) ||
    toText(p.rhythm_summary) ||
    toText(p.judgement) ||
    arr(p.details?.nodes).length
  );
});

const hasBaziNameFit = computed(() => {
  const b = dataObj.value?.bazi_name_fit;
  if (!b || typeof b !== "object") return false;
  const rawScore = b.fit_score;
  const scoreNum = Number(rawScore);
  const hasScore =
    rawScore !== undefined &&
    rawScore !== null &&
    rawScore !== "" &&
    Number.isFinite(scoreNum);
  return !!(
    toText(b.xiyongshen) ||
    toText(b.name_wuxing_profile) ||
    toText(b.complement_summary) ||
    hasScore ||
    arr(b.details?.nodes).length
  );
});

const hasNamePopularity = computed(() => {
  const n = dataObj.value?.name_popularity;
  if (!n || typeof n !== "object") return false;
  return !!(
    toText(n.duplicate_rate_label) ||
    toText(n.popularity_tier) ||
    toText(n.interpretation) ||
    arr(n.details?.nodes).length
  );
});

const hasLiuyao = computed(() => {
  const x = dataObj.value?.liuyao;
  if (!x || typeof x !== "object") return false;
  return !!(
    toText(x.hexagram_title) ||
    toText(x.changing_summary) ||
    toText(x.interpretation) ||
    arr(x.yao_lines).length ||
    arr(x.details?.nodes).length
  );
});

const hasWuxingBagua = computed(() => {
  const x = dataObj.value?.wuxing_bagua;
  if (!x || typeof x !== "object") return false;
  return !!(
    toText(x.lacking) ||
    toText(x.supplement) ||
    toText(x.wuxing_sketch) ||
    toText(x.bagua_profile) ||
    toText(x.mutual_sketch) ||
    toText(x.summary) ||
    arr(x.details?.nodes).length
  );
});
const hasWuxingSupplement = computed(() => {
  const x = dataObj.value?.wuxing_bagua;
  if (!x || typeof x !== "object") return false;
  return !!(toText(x.lacking) || toText(x.supplement));
});

const hasCharDetail = computed(() => {
  const items = arr(dataObj.value?.char_detail?.items);
  return items.length > 0;
});

const hasZodiacSign = computed(() => {
  const x = dataObj.value?.zodiac_sign;
  if (!x || typeof x !== "object") return false;
  return !!(
    toText(x.animal) ||
    toText(x.animal_icon) ||
    toText(x.earthly_branch) ||
    toText(x.sanhe) ||
    toText(x.liuhe) ||
    toText(x.xiangchong) ||
    toText(x.xianghai) ||
    toText(x.xiangxing) ||
    toText(x.trait_summary) ||
    toText(x.name_harmony) ||
    arr(x.details?.nodes).length
  );
});
const hasZodiacAdaptation = computed(() => {
  const x = dataObj.value?.zodiac_sign;
  if (!x || typeof x !== "object") return false;
  return !!(
    toText(x.sanhe) ||
    toText(x.liuhe) ||
    toText(x.xiangchong) ||
    toText(x.xianghai) ||
    toText(x.xiangxing)
  );
});

const hasCareerPlan = computed(() => {
  const x = dataObj.value?.career_plan;
  if (!x || typeof x !== "object") return false;
  return !!(
    toText(x.summary) ||
    arr(x.milestones).length ||
    arr(x.details?.nodes).length
  );
});

const hasLuckyNumbers = computed(() => {
  const x = dataObj.value?.lucky_numbers;
  if (!x || typeof x !== "object") return false;
  return !!(
    toText(x.primary) ||
    toText(x.meaning) ||
    arr(x.numbers).length ||
    arr(x.details?.nodes).length
  );
});

const hasLuckyColors = computed(() => {
  const x = dataObj.value?.lucky_colors;
  if (!x || typeof x !== "object") return false;
  return !!(
    toText(x.primary) ||
    toText(x.meaning) ||
    arr(x.colors).length ||
    arr(x.details?.nodes).length
  );
});

const luckyNumbersDisplay = computed(() => {
  const nums = arr(dataObj.value?.lucky_numbers?.numbers);
  if (!nums.length) return "";
  return nums.map((n: any) => String(n)).join("、");
});

const arr = (v: any) => (Array.isArray(v) ? v : []);
const num = (v: any) => {
  const n = Number(v);
  return Number.isFinite(n) ? n : 0;
};

const showModal = ref(false);
const modalTitle = ref("详解");
const modalNodes = ref<any[]>([]);
const openDetail = (title: string, nodes: any) => {
  modalTitle.value = title;
  modalNodes.value = arr(nodes);
  showModal.value = true;
};
const closeDetail = () => {
  showModal.value = false;
};

// —— 五行八卦：把“每个字的五行/阴阳/卦象”拼到详解弹层最前面 ——
const formatCharDetailLine = (it: any): string => {
  const parts: string[] = [];
  const char = toText(it?.char);
  if (!char) return "";
  parts.push(char);

  const element = toText(it?.element);
  if (element) parts.push(element);

  const yinYang = toText(it?.yin_yang_element) || toText(it?.yin_yang);
  if (yinYang) parts.push(yinYang);

  const sym = toText(it?.bagua_trigram_symbol);
  const tri = toText(it?.bagua_trigram);
  if (sym || tri) {
    const trigram = tri ? tri : "";
    parts.push(`卦：${sym}${sym && trigram ? ' ' : ''}${trigram}`.trim());
  }

  const stroke = toText(it?.stroke);
  if (stroke) parts.push(`笔画：${stroke}`);

  const tone = toText(it?.tone);
  if (tone) parts.push(`声调：${tone}`);

  return parts.join(" · ");
};

const charDetailNodes = computed(() => {
  const items = arr(dataObj.value?.char_detail?.items);
  const nodes = items
    .map((it: any) => {
      const line = formatCharDetailLine(it);
      return line ? { type: "text", text: line } : null;
    })
    .filter(Boolean) as any[];
  return nodes;
});

const openCharDetail = () => {
  const fallbackNodes = arr(dataObj.value?.char_detail?.details?.nodes);
  const nodes = charDetailNodes.value.length ? charDetailNodes.value : fallbackNodes;
  openDetail("五行单字详解", nodes);
};

const openWuxingBaguaDetail = () => {
  const x = dataObj.value?.wuxing_bagua || {};
  const extraNodes = [
    toText(x?.lacking) ? { type: "text", text: `先天偏缺：${toText(x.lacking)}` } : null,
    toText(x?.supplement) ? { type: "text", text: `补益建议：${toText(x.supplement)}` } : null,
    toText(x?.wuxing_sketch) ? { type: "text", text: `五行：${toText(x.wuxing_sketch)}` } : null,
    toText(x?.bagua_profile) ? { type: "text", text: `八卦：${toText(x.bagua_profile)}` } : null,
    toText(x?.mutual_sketch) ? { type: "text", text: toText(x.mutual_sketch) } : null,
    toText(x?.summary) ? { type: "text", text: toText(x.summary) } : null,
  ].filter(Boolean) as any[];
  const baguaFallbackNodes = arr(dataObj.value?.wuxing_bagua?.details?.nodes);
  const nodes = charDetailNodes.value.length
    ? [...charDetailNodes.value, ...extraNodes, ...baguaFallbackNodes]
    : [...extraNodes, ...baguaFallbackNodes];
  openDetail("五行八卦详解", nodes);
};

const openZodiacDetail = () => {
  const z = dataObj.value?.zodiac_sign || {};
  const extraNodes = [
    (toText(z?.animal) || toText(z?.earthly_branch))
      ? { type: "text", text: `属相：${toText(z.animal)} ${toText(z.earthly_branch)}`.trim() }
      : null,
    toText(z?.trait_summary) ? { type: "text", text: `特性：${toText(z.trait_summary)}` } : null,
    toText(z?.name_harmony) ? { type: "text", text: `与名契合：${toText(z.name_harmony)}` } : null,
    toText(z?.sanhe) ? { type: "text", text: `三合：${toText(z.sanhe)}` } : null,
    toText(z?.liuhe) ? { type: "text", text: `六合：${toText(z.liuhe)}` } : null,
    toText(z?.xiangchong) ? { type: "text", text: `相冲：${toText(z.xiangchong)}` } : null,
    toText(z?.xianghai) ? { type: "text", text: `相害：${toText(z.xianghai)}` } : null,
    toText(z?.xiangxing) ? { type: "text", text: `相刑：${toText(z.xiangxing)}` } : null,
  ].filter(Boolean) as any[];
  openDetail("属相详解", [...extraNodes, ...arr(z?.details?.nodes)]);
};

const openGuaDetail = () => {
  const g = dataObj.value?.gua || {};
  const extraNodes = [
    toText(g?.name) ? { type: "text", text: `卦象：${toText(g.name)}` } : null,
    (g?.hexagram_code !== undefined && g?.hexagram_code !== null && g?.hexagram_code !== "")
      ? { type: "text", text: `卦序：${toText(g.hexagram_code)}` }
      : null,
    toText(g?.bgText) ? { type: "text", text: `卦名：${toText(g.bgText)}` } : null,
    toText(g?.badge) ? { type: "text", text: `卦位：${toText(g.badge)}` } : null,
    toText(g?.desc) ? { type: "text", text: toText(g.desc) } : null,
  ].filter(Boolean) as any[];
  openDetail("周易卦象详解", [...extraNodes, ...arr(g?.details?.nodes)]);
};

const handleDownloadPdf = async () => {
  // H5/浏览器环境生成 PDF 下载到本地；若非浏览器则直接返回
  if (typeof window === "undefined" || typeof document === "undefined") return;
  uni.showLoading({ title: "正在生成PDF..." });
  const raw = dataObj.value || {};
  const header = raw?.header || {};
  const measuredName = toText(header?.name);
  const score = header?.score ?? "";
  const starRating = header?.star_rating ?? header?.stars ?? 0;

  const preferredModules: Array<{ key: string; title: string }> = [
    { key: "wuxing_bagua", title: "五行八卦（相生相克）" },
    { key: "char_detail", title: "五行单字详解" },
    { key: "bazi_name_fit", title: "八字与姓名五行" },
    { key: "strokes_wuge_sancai", title: "三才五格" },
    { key: "liuyao", title: "六爻" },
    { key: "zodiac_sign", title: "属相" },
    { key: "wuxing_bagua", title: "五行八卦" },
    { key: "lucky_numbers", title: "幸运数字" },
    { key: "lucky_colors", title: "幸运颜色" },
    { key: "career_plan", title: "事业规划" },
    { key: "lucky_tips", title: "开运锦囊" },
    { key: "lifespan", title: "人生规划" },
    { key: "poetry_source", title: "诗词出处" },
    { key: "six_dimension", title: "六维格局" },
    { key: "master_message", title: "大师寄语" },
  ];
  const keyToTitle = new Map(preferredModules.map((m) => [m.key, m.title]));

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

  const flattenNodesToLines = (nodes: any[]): string[] => {
    const out: string[] = [];
    for (const n of nodes || []) {
      if (!n || typeof n !== "object") continue;
      if (n.type === "text") {
        const t = toText(n.text);
        if (t) out.push(t);
      } else if (n.type === "list") {
        const items = Array.isArray(n.items) ? n.items : [];
        for (const it of items) {
          const t = toText(it);
          if (t) out.push(`- ${t}`);
        }
      } else if (n.type === "kv") {
        const items = Array.isArray(n.items) ? n.items : [];
        for (const it of items) {
          const k = toText(it?.label);
          const v = toText(it?.value);
          if (k || v) out.push(`${k ? k : "项"}：${v}`);
        }
      }
      // 空行作为模块间隔（避免所有内容挤在一起）
      out.push("");
    }
    // 去掉尾部多余空行
    while (out.length && !out[out.length - 1]) out.pop();
    return out;
  };

  const labelForKey = (rawKey: string): string => {
    const k = String(rawKey || "").trim();
    if (!k) return "";
    const dict: Record<string, string> = {
      // 通用
      header: "基础信息",
      details: "详解",
      nodes: "详解内容",
      items: "条目",
      list: "列表",
      text: "内容",
      title: "标题",
      summary: "总结",
      remark: "批注",
      note: "说明",
      result: "结果",
      score: "评分",
      star_rating: "星级",
      stars: "星级",
      name: "名称",
      label: "标签",
      value: "内容",
      desc: "描述",
      // 单字详解
      char: "字",
      element: "五行",
      stroke: "笔画",
      tone: "声调",
      pinyin: "拼音",
      // 卦象
      gua: "周易卦象",
      bg: "卦象",
      // 五行/生肖
      wuxing: "五行",
      zodiac: "生肖",
      shuxiang: "属相",
      animal: "生肖",
      earthly_branch: "地支",
      // 其它常见
      phase: "阶段",
      period: "周期",
      focus: "重点",
      advice: "建议",
      colors: "颜色",
      hex: "色值",
      primary: "主推",
      meaning: "含义",
      numbers: "数字",
      labels: "维度",
      values: "分值",
      lucky: "吉凶",
      is_lucky: "吉凶",
      color: "颜色",
      wuxing_sketch: "五行概述",
      bagua_profile: "八卦画像",
      mutual_sketch: "生克互助",
      trait_summary: "特性概述",
      name_harmony: "与名字契合",
      lacking: "先天偏缺",
      supplement: "补益建议",
      sanhe: "三合",
      liuhe: "六合",
      xiangchong: "相冲",
      xianghai: "相害",
      xiangxing: "相刑",
      hexagram_code: "卦序",
      bgText: "卦名",
      badge: "卦位",
    };

    if (dict[k]) return dict[k];

    // snake_case → 简单中文化（仅用于兜底，避免直接露出字段名）
    const snake = k.replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`);
    const parts = snake.split("_").filter(Boolean);
    const partDict: Record<string, string> = {
      char: "字",
      detail: "详解",
      analysis: "分析",
      bagua: "八卦",
      wuxing: "五行",
      zodiac: "生肖",
      score: "评分",
      name: "名称",
      plan: "规划",
      lucky: "幸运",
      color: "颜色",
      number: "数字",
      career: "事业",
      master: "大师",
      message: "寄语",
      poetry: "诗词",
      source: "出处",
      stroke: "笔画",
      gua: "卦象",
      tips: "锦囊",
    };
    const mapped = parts.map((p) => partDict[p] || "");
    const s = mapped.filter(Boolean).join("");
    return s || "信息";
  };

  const noisyKeys = new Set(["details", "nodes", "type", "meta", "status", "code", "raw"]);
  const hiddenPdfKeys = new Set([
    "id",
    "report_id",
    "is_unlocked",
    "unlock_price",
    "locked",
    "lock",
    "price",
  ]);
  const isNoisyInfoLine = (label: string, line: string) => {
    if (label !== "信息") return false;
    const s = String(line || "").trim();
    if (!s) return true;
    if (/^(true|false)$/i.test(s)) return true;
    if (/^[a-z_]+$/i.test(s) && s.length <= 16) return true;
    return false;
  };
  const flattenAnyToLines = (val: any, prefix = "", depth = 0): string[] => {
    if (depth > 4) return [];
    const out: string[] = [];
    if (val === null || val === undefined) return out;
    if (typeof val === "string" || typeof val === "number" || typeof val === "boolean") {
      const s = toText(val);
      // 布尔值在报告中大多是内部标记位，默认不直接导出，避免“信息：true/false”噪音
      if (typeof val === "boolean") return out;
      if (s) out.push(prefix ? `${prefix}：${s}` : s);
      return out;
    }
    if (Array.isArray(val)) {
      val.forEach((it) => {
        const child = flattenAnyToLines(it, "", depth + 1);
        if (!child.length) return;
        child.forEach((line) => out.push(line));
      });
      return out;
    }
    if (typeof val === "object") {
      Object.keys(val).forEach((k) => {
        if (noisyKeys.has(k)) return;
        if (hiddenPdfKeys.has(k)) return;
        // 隐藏纯容器字段
        if (k === "items" && Array.isArray((val as any)[k])) {
          const child = flattenAnyToLines((val as any)[k], "", depth + 1);
          child.forEach((line) => out.push(line));
          return;
        }
        const label = labelForKey(k);
        const child = flattenAnyToLines((val as any)[k], "", depth + 1);
        child.forEach((line) => {
          if (isNoisyInfoLine(label, line)) return;
          const shouldKeepRaw = line.includes("：") && (label === "信息" || !label);
          out.push(shouldKeepRaw ? line : (label ? `${label}：${line}` : line));
        });
      });
    }
    return out;
  };
  const uniqueLines = (lines: string[]) => {
    const seen = new Set<string>();
    const out: string[] = [];
    lines.forEach((line) => {
      const s = String(line || "").trim();
      if (!s || seen.has(s)) return;
      seen.add(s);
      out.push(s);
    });
    return out;
  };
  const isNumericLike = (s: string) => /^\d+(\.\d+)?$/.test(String(s || "").trim());
  const shouldSkipModule = (title: string, lines: string[]) => {
    if (title.includes("解锁状态") || title.includes("解锁价格")) return true;
    const clean = (lines || []).map((x) => String(x || "").trim()).filter(Boolean);
    if (!clean.length) return true;
    const numericCount = clean.filter((x) => isNumericLike(x)).length;
    // 过滤无语义章节：标题“信息”且内容仅为数字（如 302/337）
    if (title === "信息" && numericCount === clean.length) return true;
    // 兜底：标题“信息”且几乎没有文本语义
    if (title === "信息" && clean.length <= 2 && numericCount >= 1) return true;
    return false;
  };
  const linesFromSixDimension = (v: any): string[] => {
    if (!v || typeof v !== "object") return [];
    const labels = arr(v.labels).map((x: any) => toText(x)).filter(Boolean);
    const values = arr(v.values).map((x: any) => toText(x)).filter(Boolean);
    const lines: string[] = [];
    const len = Math.max(labels.length, values.length);
    for (let i = 0; i < len; i++) {
      const lb = labels[i] || `维度${i + 1}`;
      const val = values[i];
      if (!lb && !val) continue;
      lines.push(`${lb}：${val || "-"}`);
    }
    if (toText(v.remark)) lines.push(`批注：${toText(v.remark)}`);
    return lines;
  };

  const prettifyKey = (k: string) => keyToTitle.get(k) || labelForKey(k);

  // 固定优先模块 + 后端动态模块（确保不漏返回内容）
  const usedKeys = new Set<string>();
  const moduleOrder: Array<{ key: string; title: string; lines: string[] }> = [];

  for (const p of preferredModules) {
    const mod = (raw as any)?.[p.key];
    if (!mod) continue;
    const nodeLines = flattenNodesToLines(arr(mod?.details?.nodes));
    const plainLines = p.key === "six_dimension" ? linesFromSixDimension(mod) : flattenAnyToLines(mod);
    const merged = uniqueLines([...plainLines, ...nodeLines]);
    if (shouldSkipModule(p.title, merged)) continue;
    moduleOrder.push({ key: p.key, title: p.title, lines: merged });
    usedKeys.add(p.key);
  }

  Object.keys(raw || {}).forEach((k) => {
    if (usedKeys.has(k)) return;
    if (hiddenPdfKeys.has(k)) return;
    const v = (raw as any)[k];
    const nodeLines = v && typeof v === "object" ? flattenNodesToLines(arr((v as any)?.details?.nodes)) : [];
    const plainLines = k === "six_dimension" ? linesFromSixDimension(v) : flattenAnyToLines(v);
    const merged = uniqueLines([...plainLines, ...nodeLines]);
    const title = prettifyKey(k);
    if (shouldSkipModule(title, merged)) return;
    moduleOrder.push({ key: k, title, lines: merged });
  });

  if (!moduleOrder.length) return;

  const visualLineCost = (line: string) => {
    const s = String(line || "").trim();
    if (!s) return 1;
    return Math.max(1, Math.ceil(s.length / 30));
  };
  const splitByLines = (lines: string[], maxLinesPerPage: number) => {
    if (!lines.length) return [[]];
    const pages: string[][] = [];
    let bucket: string[] = [];
    let cost = 0;
    lines.forEach((line) => {
      const lc = visualLineCost(line);
      if (bucket.length && cost + lc > maxLinesPerPage) {
        pages.push(bucket);
        bucket = [];
        cost = 0;
      }
      bucket.push(line);
      cost += lc;
    });
    if (bucket.length) pages.push(bucket);
    return pages;
  };

  // 以 96dpi 折算：A4 = 793.7 x 1122.5 px
  const PAGE_W = 794;
  const PAGE_H = 1123;
  // 分页容量：区分“单章节切分容量”和“页面总容量”
  const MAX_SECTION_LINES_PER_PAGE = 30;
  const MAX_PAGE_COST = 42;

  // 动态加载 pdf 依赖（减少首屏包体积）
  const [{ jsPDF }, html2canvasMod] = await Promise.all([
    import("jspdf"),
    import("html2canvas"),
  ]);
  const html2canvas = (html2canvasMod as any).default || (html2canvasMod as any);

  // 样式只注入一次
  const styleId = "pdf-gen-mystic-style";
  let style = document.getElementById(styleId) as HTMLStyleElement | null;
  if (!style) {
    style = document.createElement("style");
    style.id = styleId;
    document.head.appendChild(style);
  }
  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:48px;
        right:48px;
        top:116px;
        bottom:92px;
        background: rgba(10, 10, 15, 0.62);
        border: 1px solid rgba(212, 175, 55, 0.38);
        border-radius: 16px;
        padding: 30px 28px;
        box-sizing:border-box;
        overflow:hidden;
      }
      .pdf-cover-kicker{
        text-align:center;
        font-size: 12px;
        letter-spacing: .28em;
        color: rgba(212,175,55,0.72);
        margin-bottom: 12px;
      }
      .pdf-cover-title{
        text-align:center;
        font-size: 25px;
        letter-spacing: .16em;
        font-weight: 700;
        color: rgba(212,175,55,0.95);
        margin-bottom: 28px;
      }
      .pdf-name{
        text-align:center;
        font-size: 50px;
        font-weight: 800;
        margin-bottom: 16px;
        color: rgba(242,230,216,0.98);
        text-shadow: 0 0 18px rgba(212,175,55,0.18);
      }
      .pdf-score-row{
        display:flex;
        align-items:center;
        justify-content:center;
        gap: 16px;
        margin-top: 8px;
      }
      .pdf-score{
        font-size: 38px;
        font-weight: 900;
        color: rgba(212,175,55,0.98);
      }
      .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-stars{
        display:flex;
        gap: 8px;
        font-size: 16px;
        color: rgba(212,175,55,0.95);
        letter-spacing: .08em;
      }
      .pdf-dir-title{
        font-size: 20px;
        font-weight: 800;
        letter-spacing: .14em;
        color: rgba(212,175,55,0.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,0.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,0.96);
        font-weight: 800;
        letter-spacing: .02em;
      }
      .pdf-dir-name{
        flex: 1;
        font-size: 16px;
        color: rgba(232,232,232,0.96);
        font-weight: 700;
        letter-spacing: .04em;
      }
      .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,0.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:48px;
        right:48px;
        top:78px;
        height: 48px;
        border-radius: 12px;
        background: rgba(212,175,55,0.08);
        border: 1px solid rgba(212,175,55,0.24);
        display:flex;
        align-items:center;
        justify-content:flex-start;
        padding: 0 22px;
        box-sizing:border-box;
      }
      .pdf-chapter-no{
        font-size: 16px;
        font-weight: 900;
        color: rgba(212,175,55,0.98);
        margin-right: 14px;
      }
      .pdf-chapter-title{
        font-size: 16px;
        font-weight: 800;
        color: rgba(242,230,216,0.98);
      }
      .pdf-content-pre{
        white-space: pre-wrap;
        word-break: break-word;
        font-size: 13px;
        line-height: 1.85;
        color: rgba(226,226,226,0.94);
      }
      .pdf-flow-wrap{
        display:block;
      }
      .pdf-flow-section{
        margin-bottom: 10px;
        border-radius: 10px;
        border: 1px solid rgba(212,175,55,0.16);
        background: rgba(11,16,38,0.55);
        overflow: hidden;
      }
      .pdf-flow-section:last-child{
        margin-bottom:0;
      }
      .pdf-flow-head{
        height: 38px;
        display:flex;
        align-items:center;
        gap:10px;
        padding: 0 14px;
        border-bottom: 1px solid rgba(212,175,55,0.16);
        background: linear-gradient(90deg, rgba(212,175,55,0.12), rgba(212,175,55,0.03));
      }
      .pdf-flow-bullet{
        color: rgba(212,175,55,0.96);
        font-size: 15px;
      }
      .pdf-flow-title{
        color: rgba(245,236,218,0.98);
        font-size: 16px;
        font-weight: 800;
        letter-spacing: .05em;
      }
      .pdf-flow-body{
        padding: 10px 14px 12px;
      }
      .pdf-flow-line{
        font-size: 15px;
        line-height: 1.72;
        color: rgba(228,230,238,0.95);
        margin-bottom: 2px;
        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);

  try {
    const pages: HTMLElement[] = [];
    const bgUrl = pdfBgUrl;

    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(${bgUrl})`;
      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);
    };

    // 1) 封面页
    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(measuredName || "未命名")}</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-score-row">
        <div class="pdf-score">${escapeHtml(String(score))}</div>
        <div class="pdf-stars">${"★".repeat(Math.max(0, Math.min(5, Number(starRating) || 0)))}${"☆".repeat(Math.max(0, 5 - Math.max(0, Math.min(5, Number(starRating) || 0))))}</div>
      </div>
      <div class="pdf-cover-seal">玄</div>
    </div>
  `;
    appendFooter(coverEl, 1, "测名详解报告 · 封面");

    // 2) 目录页（先占位，pageNo 在后面计算后再重建）
    const dirEl = createPageEl(1);
    dirEl.innerHTML = `
    <div class="pdf-gen-panel">
      <div class="pdf-dir-title">目录</div>
      <div class="pdf-content-pre">${escapeHtml("正在生成目录...")}</div>
    </div>
  `;

    // 3) 分章分页：章节结束后可无缝衔接下一章，避免留白过多
    let currentPageNo = 3; // 封面=1，目录=2，所以正文从 3 开始
    const dirItems: Array<{ no: number; title: string; start: number; end: number }> = [];
    type ChapterSection = { chapterNo: number; title: string; lines: string[]; continued?: boolean };
    type ContentPage = { sections: ChapterSection[]; used: number };
    const contentPages: ContentPage[] = [];
    const sectionCost = (lines: string[]) => {
      const bodyCost = lines.reduce((sum, line) => sum + visualLineCost(line), 0);
      // 节标题+边距固定开销
      return bodyCost + 6;
    };

    let chapterCounter = 0;
    for (const m of moduleOrder) {
      chapterCounter += 1;
      const chapterNo = chapterCounter;
      const lines = Array.isArray(m.lines) ? m.lines : [];
      if (!lines.length) continue;
      const chunks = splitByLines(lines, MAX_SECTION_LINES_PER_PAGE);
      const startPage = currentPageNo;

      chunks.forEach((chunkLines, chunkIdx) => {
        const section: ChapterSection = {
          chapterNo,
          title: m.title,
          lines: chunkLines,
          continued: chunkIdx > 0,
        };
        const cost = sectionCost(chunkLines);
        const prevPage = contentPages[contentPages.length - 1];
        const canAppend =
          !!prevPage &&
          prevPage.used + cost <= MAX_PAGE_COST;
        if (canAppend) {
          prevPage.sections.push(section);
          prevPage.used += cost;
        } else {
          contentPages.push({ sections: [section], used: cost });
          currentPageNo += 1;
        }
      });

      const endPage = Math.max(startPage, currentPageNo - 1);
      dirItems.push({ no: chapterNo, title: m.title, start: startPage, end: endPage });
    }

    // 重建目录页内容
    const dirHtml = `
      <div class="pdf-dir-list">
        ${dirItems
          .map((it) => {
            const idxText = `第${it.no}章`;
            const pageText = it.start === it.end ? `${it.start}` : `${it.start}-${it.end}`;
            return `<div class="pdf-dir-item">
              <div class="pdf-dir-idx">${escapeHtml(idxText)}</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>
    `;
    dirEl.innerHTML = `
      <div class="pdf-gen-panel">
        <div class="pdf-dir-kicker">卷 一 · 纲 目</div>
        <div class="pdf-dir-title">目录</div>
        ${dirHtml}
      </div>
    `;
    appendFooter(dirEl, 2, "测名详解报告 · 目录");

    // 创建内容页 DOM（同页可容纳多个章节）
    contentPages.forEach((page, i) => {
      const el = createPageEl(2 + i);
      const sectionsHtml = page.sections
        .map((section) => {
          const sectionTitle = `第${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(sectionTitle)}</div>
            </div>
            <div class="pdf-flow-body">${linesHtml}</div>
          </div>`;
        })
        .join("");
      el.innerHTML = `
        <div class="pdf-gen-panel">
          <div class="pdf-flow-wrap">${sectionsHtml}</div>
        </div>
      `;
      appendFooter(el, 3 + i);
    });

    // 捕获页面并写入 PDF
    const doc = new jsPDF({ unit: "pt", format: "a4" });
    const pageW = doc.internal.pageSize.getWidth();
    const pageH = doc.internal.pageSize.getHeight();
    // 提速关键：降低渲染倍率并使用 JPEG，通常可显著减少导出耗时
    const renderScale = Math.min(1.45, Math.max(1.15, Number((window as any)?.devicePixelRatio || 1)));
    const jpegQuality = 0.86;

    const renderPageCanvasSafely = async (sourceEl: HTMLElement): Promise<HTMLCanvasElement> => {
      try {
        return await html2canvas(sourceEl, {
          scale: renderScale,
          useCORS: true,
          allowTaint: true,
          backgroundColor: "#0a0a0f",
          logging: false, // 关闭 html2canvas 大量控制台日志
        });
      } catch (err: any) {
        const msg = String(err?.message || err || "");
        // html2canvas 偶发：createPattern 传入 0x0 canvas，重试时移除背景图规避
        if (!msg.includes("createPattern")) throw err;

        const cloned = sourceEl.cloneNode(true) as HTMLElement;
        const list = [cloned, ...Array.from(cloned.querySelectorAll("*"))] as HTMLElement[];
        list.forEach((node) => {
          node.style.backgroundImage = "none";
        });
        cloned.style.position = "absolute";
        cloned.style.left = "-99999px";
        cloned.style.top = "0";
        document.body.appendChild(cloned);
        try {
          return await html2canvas(cloned, {
            scale: renderScale,
            useCORS: true,
            allowTaint: true,
            backgroundColor: "#0a0a0f",
            logging: false,
          });
        } finally {
          cloned.remove();
        }
      }
    };

    for (let i = 0; i < pages.length; i++) {
      const canvas = await renderPageCanvasSafely(pages[i]);
      const imgData = canvas.toDataURL("image/jpeg", jpegQuality);
      if (i > 0) doc.addPage();
      doc.addImage(imgData, "JPEG", 0, 0, pageW, pageH, undefined, "FAST");
    }

    // 下载到本地
    const safeName = measuredName ? measuredName.replace(/[\\\\/:*?\"<>|]/g, "_") : "测名详解";
    doc.save(`${safeName}-测名详解.pdf`);
    uni.showToast({ title: "PDF下载成功", icon: "success" });
  } catch (e) {
    console.error("生成PDF失败:", e);
    uni.showToast({ title: "PDF下载失败", icon: "none" });
  } finally {
    // 清理 DOM
    wrapper.remove();
    uni.hideLoading();
  }
};

const stars = Array.from({ length: 24 }, (_, i) => ({
  id: i + 1,
  top: `${Math.random() * 100}%`,
  left: `${Math.random() * 100}%`,
  size: Math.random() * 2 + 1,
  duration: Math.random() * 4 + 3,
  delay: Math.random() * 4,
}));
</script>

<style scoped>
.test-name-detail {
  min-height: 100vh;
  background: #0a0a0f;
  position: relative;
  color: #e2e2e2;
}

.detail-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: rgba(10, 10, 15, 0.8);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

.detail-header-back {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  color: #a0a0a0;
}

.back-icon {
  font-size: 24px;
  margin-right: 8rpx;
}
.back-text {
  font-size: 14px;
  letter-spacing: 0.1em;
}

.detail-header-title {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  font-size: 18px;
  font-weight: 700;
  color: #d4af37;
  letter-spacing: 0.3em;
  pointer-events: none;
}

.detail-header-placeholder { width: 80rpx; }

.detail-header-download {
  position: relative;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8rpx;
  padding: 10rpx 16rpx;
  border-radius: 999rpx;
  border: 1px solid rgba(212, 175, 55, 0.35);
  background: rgba(212, 175, 55, 0.10);
  color: rgba(255, 242, 210, 0.92);
  backdrop-filter: blur(8px);
}

.detail-header-download-icon {
  font-size: 18px;
  line-height: 1;
}

.detail-header-download-text {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.detail-content {
  position: relative;
  z-index: 10;
  padding: 180rpx 32rpx 60rpx;
  height: 100vh;
  box-sizing: border-box;
}

.clickable { cursor: pointer; }

.score-card {
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.05));
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 8rpx;
  padding: 48rpx;
  text-align: center;
  position: relative;
  overflow: hidden;
  margin-bottom: 48rpx;
}

.score-label {
  font-size: 10px;
  color: #d4af37;
  letter-spacing: 0.5em;
  text-transform: uppercase;
  opacity: 0.8;
  margin-bottom: 16rpx;
}

.score-corner {
  position: absolute;
  width: 12rpx;
  height: 12rpx;
  border-color: #d4af37;
  border-style: solid;
}
.score-corner-tl { top: 0; left: 0; border-width: 4rpx 0 0 4rpx; }
.score-corner-tr { top: 0; right: 0; border-width: 4rpx 4rpx 0 0; }
.score-corner-bl { bottom: 0; left: 0; border-width: 0 0 4rpx 4rpx; }
.score-corner-br { bottom: 0; right: 0; border-width: 0 4rpx 4rpx 0; }

.score-value-wrap {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 280rpx;
  height: 280rpx;
  margin-bottom: 16rpx;
}

.score {
  position: relative;
  z-index: 2;
  font-size: 72px;
  font-weight: 700;
  background: linear-gradient(to bottom, #d4af37, #8a6e1e);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

.score-ring {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 3;
  width: 100%;
  height: 100%;
  border: 1px dashed rgba(212, 175, 55, 0.2);
  border-radius: 50%;
  animation: rotate 10s linear infinite;
}

.score-bagua-aura {
  position: absolute;
  inset: 0;
  z-index: 0;
  border-radius: 50%;
  pointer-events: none;
}

.score-yinyang-bg {
  position: absolute;
  inset: 22%;
  border-radius: 50%;
  opacity: 0.11;
  background:
    radial-gradient(circle at 50% 25%, rgba(12, 14, 28, 0.95) 11%, transparent 11.5%),
    radial-gradient(circle at 50% 75%, rgba(212, 175, 55, 0.55) 11%, transparent 11.5%),
    linear-gradient(90deg, rgba(12, 14, 28, 0.98) 50%, rgba(212, 175, 55, 0.35) 50%);
  box-shadow: inset 0 0 24px rgba(212, 175, 55, 0.08);
  animation: scoreYinyangSpin 48s linear infinite reverse;
}

.score-rune-particles {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.score-rune-dot {
  position: absolute;
  width: 3px;
  height: 3px;
  border-radius: 50%;
  background: rgba(255, 224, 160, 0.95);
  box-shadow:
    0 0 6px rgba(255, 210, 120, 0.85),
    0 0 14px rgba(212, 175, 55, 0.35);
  animation: scoreRuneTwinkle 2.8s ease-in-out infinite;
}

.score-rune-dot:nth-child(1) { top: 12%; left: 38%; animation-delay: 0s; }
.score-rune-dot:nth-child(2) { top: 22%; right: 14%; animation-delay: 0.4s; }
.score-rune-dot:nth-child(3) { top: 48%; left: 8%; animation-delay: 0.8s; }
.score-rune-dot:nth-child(4) { bottom: 18%; left: 22%; animation-delay: 1.1s; }
.score-rune-dot:nth-child(5) { bottom: 12%; right: 28%; animation-delay: 0.2s; }
.score-rune-dot:nth-child(6) { top: 62%; right: 10%; animation-delay: 1.5s; }
.score-rune-dot:nth-child(7) { top: 8%; right: 42%; animation-delay: 0.6s; }
.score-rune-dot:nth-child(8) { bottom: 38%; right: 6%; animation-delay: 1.9s; }

.score-rune-glyph {
  position: absolute;
  font-size: 9px;
  color: rgba(212, 175, 55, 0.55);
  text-shadow: 0 0 8px rgba(212, 175, 55, 0.35);
  animation: scoreRuneDrift 6s ease-in-out infinite;
}

.score-rune-glyph:nth-child(9) { top: 28%; left: 18%; animation-delay: 0s; }
.score-rune-glyph:nth-child(10) { top: 18%; right: 22%; animation-delay: 1.2s; }
.score-rune-glyph:nth-child(11) { bottom: 28%; right: 16%; animation-delay: 2.1s; }
.score-rune-glyph:nth-child(12) { bottom: 20%; left: 14%; animation-delay: 0.7s; }

.score-bagua-core {
  position: absolute;
  inset: 18%;
  border-radius: 50%;
  background:
    radial-gradient(circle at 50% 50%, rgba(212, 175, 55, 0.08) 0%, rgba(212, 175, 55, 0.02) 42%, rgba(7, 12, 30, 0) 72%);
  box-shadow: inset 0 0 30px rgba(212, 175, 55, 0.06);
}

.score-bagua-ring-2 {
  position: absolute;
  inset: 6%;
  border-radius: 50%;
  border: 1px solid rgba(212, 175, 55, 0.14);
  box-shadow: 0 0 14px rgba(212, 175, 55, 0.08);
}

.score-bagua-symbols {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  animation: rotate 26s linear infinite;
}

.score-bagua-symbol {
  position: absolute;
  left: 50%;
  top: 50%;
  color: rgba(212, 175, 55, 0.65);
  font-size: 11px;
  text-shadow: 0 0 10px rgba(212, 175, 55, 0.18);
  transform-origin: 0 -134px;
}

.score-bagua-symbol:nth-child(1) { transform: rotate(0deg) translateY(-134px) rotate(0deg); }
.score-bagua-symbol:nth-child(2) { transform: rotate(45deg) translateY(-134px) rotate(-45deg); }
.score-bagua-symbol:nth-child(3) { transform: rotate(90deg) translateY(-134px) rotate(-90deg); }
.score-bagua-symbol:nth-child(4) { transform: rotate(135deg) translateY(-134px) rotate(-135deg); }
.score-bagua-symbol:nth-child(5) { transform: rotate(180deg) translateY(-134px) rotate(-180deg); }
.score-bagua-symbol:nth-child(6) { transform: rotate(225deg) translateY(-134px) rotate(-225deg); }
.score-bagua-symbol:nth-child(7) { transform: rotate(270deg) translateY(-134px) rotate(-270deg); }
.score-bagua-symbol:nth-child(8) { transform: rotate(315deg) translateY(-134px) rotate(-315deg); }

.score-stars {
  display: flex;
  justify-content: center;
  gap: 8rpx;
  margin-bottom: 24rpx;
}
.score-star { font-size: 14px; color: #5a5a5a; }
.score-star.active { color: #d4af37; }

.score-divider {
  height: 1px;
  background: linear-gradient(to right, transparent, rgba(212, 175, 55, 0.3), transparent);
  margin: 16rpx 0;
}

.name {
  display: block;
  font-size: 28px;
  font-weight: 700;
  color: #e2e2e2;
  letter-spacing: 0.2em;
  margin-top: 16rpx;
  margin-bottom: 8rpx;
}
.quote {
  display: block;
  color: #a0a0a0;
  font-size: 14px;
  line-height: 1.8;
}

.detail-entry {
  margin-top: 16rpx;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 8rpx 18rpx;
  border-radius: 999rpx;
  border: 1px solid rgba(212, 175, 55, 0.4);
  background: rgba(212, 175, 55, 0.08);
}

.sixdim-entry {
  margin-top: -20rpx;
  margin-left: 24rpx;
}

.detail-entry-text {
  font-size: 11px;
  color: #d4af37;
  letter-spacing: 0.08em;
}

.score-mystic-ornaments {
  display: none;
}

/* —— 玄幻古风：模块外框 —— */
.mystic-score-card {
  position: relative;
  overflow: visible;
  background:
    linear-gradient(155deg, rgba(28, 32, 56, 0.95) 0%, rgba(14, 18, 36, 0.92) 45%, rgba(18, 22, 42, 0.96) 100%);
  border: 1px solid rgba(212, 175, 55, 0.35);
  border-radius: 16rpx;
  box-shadow:
    0 0 0 1px rgba(212, 175, 55, 0.12) inset,
    0 16rpx 56rpx rgba(0, 0, 0, 0.5),
    0 0 40rpx rgba(120, 90, 200, 0.12);
  backdrop-filter: blur(14px);
}

.mystic-score-card__glow {
  pointer-events: none;
  position: absolute;
  inset: -2rpx;
  border-radius: 18rpx;
  background: radial-gradient(ellipse 80% 60% at 50% 0%, rgba(212, 175, 55, 0.18), transparent 55%);
  opacity: 0.85;
}

.mystic-panel {
  position: relative;
  margin-bottom: 48rpx;
  padding: 28rpx 24rpx 32rpx;
  border-radius: 16rpx;
  background: linear-gradient(160deg, rgba(22, 26, 48, 0.92) 0%, rgba(10, 12, 28, 0.94) 100%);
  border: 1px solid rgba(212, 175, 55, 0.28);
  box-shadow:
    0 0 0 1px rgba(212, 175, 55, 0.1) inset,
    0 12rpx 48rpx rgba(0, 0, 0, 0.42),
    0 0 32rpx rgba(80, 60, 140, 0.1);
  backdrop-filter: blur(12px);
}

.mystic-panel::before {
  content: "";
  position: absolute;
  inset: 10rpx;
  border-radius: 12rpx;
  border: 1px solid rgba(212, 175, 55, 0.14);
  pointer-events: none;
}

.mystic-panel--extra {
  opacity: 0.98;
  border-style: dashed;
  border-color: rgba(212, 175, 55, 0.22);
}

.mystic-section-title {
  display: flex;
  align-items: center;
  gap: 16rpx;
  margin-bottom: 22rpx;
  padding-bottom: 18rpx;
  border-bottom: none;
  background: linear-gradient(90deg, transparent, rgba(212, 175, 55, 0.2), transparent) bottom / 100% 1px no-repeat;
}

.section-glyph {
  flex-shrink: 0;
  min-width: 56rpx;
  padding: 6rpx 10rpx;
  text-align: center;
  font-size: 22rpx;
  font-weight: 700;
  color: #c9a227;
  letter-spacing: 0.12em;
  background: linear-gradient(180deg, rgba(212, 175, 55, 0.22), rgba(212, 175, 55, 0.06));
  border: 1px solid rgba(212, 175, 55, 0.35);
  border-radius: 8rpx;
  box-shadow: 0 0 16rpx rgba(212, 175, 55, 0.12);
}

.mystic-inner-card {
  position: relative;
  z-index: 1;
  background: linear-gradient(165deg, rgba(255, 255, 255, 0.06), rgba(255, 255, 255, 0.02)) !important;
  border: 1px solid rgba(212, 175, 55, 0.18) !important;
  border-radius: 12rpx !important;
  box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.25);
}

.mystic-inner-card--flush {
  background: transparent !important;
  border: none !important;
  box-shadow: none !important;
  padding: 0 !important;
}

.section { margin-bottom: 48rpx; }
.mystic-panel.section {
  margin-bottom: 48rpx;
}
.section-title { display: flex; align-items: center; margin-bottom: 24rpx; padding-left: 8rpx; }
.section-icon { font-size: 16px; color: #d4af37; margin-right: 12rpx; }
.section-text {
  flex: 1;
  font-size: 16px;
  font-weight: 700;
  color: #f0e8d8;
  letter-spacing: 0.18em;
  text-shadow: 0 0 20rpx rgba(212, 175, 55, 0.2);
}
.section-detail-tip {
  margin-left: auto;
  font-size: 11px;
  color: #c9a227;
  opacity: 0.9;
  letter-spacing: 0.14em;
}

.card {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12rpx;
  padding: 32rpx;
}

.line {
  display: block;
  font-size: 14px;
  color: #a0a0a0;
  line-height: 1.8;
  margin-bottom: 10rpx;
}
.line-strong { color: #e2e2e2; font-weight: 700; }

.meaning-card {
  background: #1a1a2e;
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12rpx;
  padding: 32rpx;
  position: relative;
  overflow: hidden;
  margin-bottom: 20rpx;
}
.meaning-quote {
  position: absolute;
  top: 8rpx;
  right: 20rpx;
  font-size: 56px;
  line-height: 1;
  color: rgba(212, 175, 55, 0.18);
  font-family: "Songti SC", "SimSun", serif;
}
.meaning-label { display: block; font-size: 14px; font-weight: 700; color: #d4af37; margin-bottom: 16rpx; }
.meaning-text { display: block; font-size: 14px; color: #a0a0a0; line-height: 1.8; margin-bottom: 16rpx; }
.meaning-judge { display: flex; flex-wrap: wrap; }
.meaning-judge-label { font-size: 14px; color: #d4af37; }
.meaning-judge-text { font-size: 14px; color: #a0a0a0; }

.zodiac-card {
  background: linear-gradient(to right, #16213e, #1a1a2e);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12rpx;
  padding: 32rpx;
}
.zodiac-header { display: flex; align-items: center; margin-bottom: 8rpx; }
.zodiac-icon-wrap {
  width: 80px;
  height: 80px;
  border-radius: 50%;
  background: rgba(212, 175, 55, 0.2);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-right: 20rpx;
}
.zodiac-icon { font-size: 24px; }
.zodiac-info { flex: 1; }
.zodiac-label { display: block; font-size: 12px; color: #a0a0a0; margin-bottom: 4rpx; }
.zodiac-value { display: block; font-size: 16px; font-weight: 700; color: #e2e2e2; }
.zodiac-score { font-size: 24px; font-weight: 700; color: #d4af37; }

.info-card {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.1);
  border-radius: 12rpx;
  padding: 32rpx;
}

.wuge-card {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 12rpx;
  padding: 32rpx;
}
.wuxing-card {
  background: rgba(255, 255, 255, 0.05);
  border: 1px solid rgba(255, 255, 255, 0.05);
  border-radius: 12rpx;
  padding: 32rpx;
}

.trait-line {
  display: block;
  font-size: 13px;
  color: #a0a0a0;
  line-height: 1.65;
  margin-top: 8rpx;
}

.wuxing-adapt-grid,
.zodiac-adapt-grid {
  margin: 10rpx 0 12rpx;
  display: flex;
  flex-direction: column;
  gap: 10rpx;
}

.wuxing-adapt-item,
.zodiac-adapt-item {
  display: block;
  font-size: 13px;
  line-height: 1.65;
  color: #b8b8b8;
  background: rgba(212, 175, 55, 0.05);
  border: 1px solid rgba(212, 175, 55, 0.14);
  border-radius: 12rpx;
  padding: 8rpx 12rpx;
}

.wuxing-adapt-label,
.zodiac-adapt-label {
  display: inline-block;
  margin-right: 10rpx;
  color: #d4af37;
  font-weight: 700;
  font-size: 12px;
}

.career-milestone {
  margin-top: 20rpx;
  padding-top: 20rpx;
  border-top: 1px solid rgba(255, 255, 255, 0.06);
}

.career-milestone:first-of-type {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

.lucky-color-row {
  display: flex;
  align-items: flex-start;
  gap: 16rpx;
  margin-bottom: 12rpx;
}

.lucky-color-swatch {
  width: 32rpx;
  height: 32rpx;
  border-radius: 50%;
  flex-shrink: 0;
  margin-top: 6rpx;
  border: 1px solid rgba(255, 255, 255, 0.25);
}

.lucky-color-text {
  flex: 1;
  margin-bottom: 0;
}

.bottom-spacer { height: 80rpx; }

.modal-mask {
  position: fixed;
  inset: 0;
  background:
    radial-gradient(900px 520px at 50% 20%, rgba(212, 175, 55, 0.12), transparent 55%),
    radial-gradient(900px 520px at 20% 80%, rgba(88, 120, 210, 0.10), transparent 55%),
    rgba(0, 0, 0, 0.72);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24rpx;
  z-index: 1000;
}

.detail-modal {
  width: min(720rpx, 92vw);
  max-height: 72vh;
  border-radius: 12rpx;
  overflow: hidden;
  border: 1px solid rgba(212, 175, 55, 0.34);
  background:
    radial-gradient(900px 320px at 50% 0%, rgba(212, 175, 55, 0.14), transparent 60%),
    linear-gradient(180deg, rgba(18, 16, 36, 0.985), rgba(8, 10, 18, 0.99));
  box-shadow:
    0 16rpx 52rpx rgba(0, 0, 0, 0.55),
    0 0 0 1px rgba(212, 175, 55, 0.14) inset,
    0 0 38rpx rgba(212, 175, 55, 0.08);
  backdrop-filter: blur(10px);
  position: relative;
}

.detail-modal-orn {
  position: absolute;
  width: 30rpx;
  height: 30rpx;
  pointer-events: none;
  opacity: 0.65;
  background:
    radial-gradient(circle at 30% 30%, rgba(212, 175, 55, 0.34), transparent 55%),
    linear-gradient(135deg, rgba(212, 175, 55, 0.55), rgba(212, 175, 55, 0.08));
  mask:
    linear-gradient(#000, #000) content-box,
    linear-gradient(#000, #000);
  -webkit-mask:
    linear-gradient(#000, #000) content-box,
    linear-gradient(#000, #000);
  padding: 6rpx;
  border-radius: 8rpx;
  box-shadow: 0 0 14rpx rgba(212, 175, 55, 0.08);
}

.detail-modal-orn--tl { top: 8rpx; left: 8rpx; transform: rotate(0deg); }
.detail-modal-orn--tr { top: 8rpx; right: 8rpx; transform: rotate(90deg); }
.detail-modal-orn--bl { bottom: 8rpx; left: 8rpx; transform: rotate(-90deg); }
.detail-modal-orn--br { bottom: 8rpx; right: 8rpx; transform: rotate(180deg); }

.detail-modal-card {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 14rpx 16rpx 12rpx;
  border-bottom: 1px solid rgba(212, 175, 55, 0.22);
  background:
    linear-gradient(90deg, rgba(212, 175, 55, 0.10), rgba(212, 175, 55, 0.02), rgba(212, 175, 55, 0.10));
  position: relative;
}

.detail-modal-card::after {
  content: "";
  position: absolute;
  left: 16rpx;
  right: 16rpx;
  bottom: 0;
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(212, 175, 55, 0.4), transparent);
  opacity: 0.45;
}

.modal-title {
  font-size: 14px;
  color: #f4e6c8;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-shadow: 0 0 10rpx rgba(212, 175, 55, 0.16);
}

.modal-title-wrap {
  display: flex;
  align-items: center;
  gap: 10rpx;
  min-width: 0;
}

.modal-title-seal {
  width: 30rpx;
  height: 30rpx;
  border-radius: 8rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 16rpx;
  font-weight: 800;
  color: rgba(255, 242, 210, 0.96);
  letter-spacing: 0.12em;
  background: linear-gradient(180deg, rgba(212, 175, 55, 0.34), rgba(212, 175, 55, 0.10));
  border: 1px solid rgba(212, 175, 55, 0.42);
  box-shadow:
    0 0 0 1px rgba(212, 175, 55, 0.1) inset,
    0 6rpx 12rpx rgba(0, 0, 0, 0.18);
}

.close {
  width: 30rpx;
  height: 30rpx;
  border-radius: 999rpx;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 14px;
  color: rgba(255, 242, 210, 0.88);
  line-height: 1;
  border: 1px solid rgba(212, 175, 55, 0.28);
  background:
    radial-gradient(circle at 30% 30%, rgba(212, 175, 55, 0.14), rgba(212, 175, 55, 0.02) 58%, transparent 72%),
    rgba(0, 0, 0, 0.16);
  box-shadow:
    0 0 0 1px rgba(212, 175, 55, 0.08) inset,
    0 4rpx 10rpx rgba(0, 0, 0, 0.18);
  transition: all 0.2s ease;
}

.detail-modal-body {
  max-height: calc(72vh - 70rpx);
  padding: 14rpx 16rpx 18rpx;
}

.node {
  margin-bottom: 12rpx;
  padding: 10rpx 10rpx 8rpx;
  border-radius: 8rpx;
  background: linear-gradient(180deg, rgba(255, 255, 255, 0.025), rgba(255, 255, 255, 0.005));
  border: 1px solid rgba(212, 175, 55, 0.08);
}

.node:last-child {
  margin-bottom: 0;
}

.detail-modal-body .line {
  color: rgba(228, 232, 244, 0.86);
  margin-bottom: 8rpx;
  line-height: 1.65;
}

.detail-modal-body .line-strong {
  color: rgba(255, 246, 224, 0.94);
}

/* 桌面端：使用固定像素上限，避免 rpx 导致弹窗接近满屏 */
.test-name-detail--desktop .modal-mask {
  padding: 20px;
}

.test-name-detail--desktop .detail-modal {
  width: min(760px, 68vw);
  max-width: 760px;
  max-height: 74vh;
  border-radius: 14px;
}

.test-name-detail--desktop .detail-modal-card {
  padding: 12px 14px 10px;
}

.test-name-detail--desktop .detail-modal-body {
  max-height: calc(74vh - 64px);
  padding: 12px 14px 16px;
}

.test-name-detail--desktop .close {
  width: 28px;
  height: 28px;
  font-size: 14px;
}

.starry-bg {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
  background: linear-gradient(to bottom, #050508, #10101a, #1a1a2e);
}

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

.glow-top {
  position: absolute;
  top: -10%;
  left: -10%;
  width: 50%;
  height: 50%;
  background: #2a3d5d;
  opacity: 0.2;
  filter: blur(100px);
  border-radius: 50%;
}

.glow-bottom {
  position: absolute;
  bottom: -10%;
  right: -10%;
  width: 50%;
  height: 50%;
  background: #9c2a2a;
  opacity: 0.1;
  filter: blur(100px);
  border-radius: 50%;
}

@keyframes twinkle {
  0%, 100% { opacity: 0.1; transform: scale(1); }
  50% { opacity: 0.5; transform: scale(1.2); }
}

@keyframes rotate {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes scoreYinyangSpin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}

@keyframes scoreRuneTwinkle {
  0%, 100% { opacity: 0.35; transform: scale(0.85); }
  50% { opacity: 1; transform: scale(1.15); }
}

@keyframes scoreRuneDrift {
  0%, 100% { opacity: 0.35; transform: translateY(0); }
  50% { opacity: 0.85; transform: translateY(-3px); }
}

/* —— 电脑端（/test-name-live）：双列排版 + 登录页同系玻璃金边，减少纵向滚动 —— */
.test-name-detail--desktop {
  min-height: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: linear-gradient(165deg, #070a12 0%, #12102a 42%, #0a1628 100%);
  color: #e8e4dc;
}

.test-name-detail--desktop .starry-bg {
  opacity: 0.28;
}

.test-name-detail--desktop .detail-header {
  background: rgba(15, 23, 42, 0.72);
  border-bottom: 1px solid rgba(212, 175, 55, 0.2);
  backdrop-filter: blur(12px);
  padding: 12px 18px !important;
}

.test-name-detail--desktop .detail-header-title {
  color: #f2e6d8;
  text-shadow: 0 0 20px rgba(212, 175, 55, 0.22);
}

.test-name-detail--desktop .detail-header-back {
  color: #d4af37;
}

.test-name-detail--desktop .detail-content--desktop {
  flex: 1;
  min-height: 0;
  height: auto !important;
  max-height: none;
  box-sizing: border-box;
  padding: 92px 14px 16px !important;
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-content: start;
  /* 桌面端：内容宽度缩成约一半，居中更舒服 */
  max-width: 660px !important;
  margin: 0 auto !important;
}

.test-name-detail--desktop .score-card {
  grid-column: auto;
  margin-bottom: 0 !important;
  padding: 12px 14px !important;
  border-radius: 12px;
  background: rgba(15, 23, 42, 0.55);
  border: 1px solid rgba(212, 175, 55, 0.22);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35);
  backdrop-filter: blur(10px);
}

/* 桌面端：缩小“分数”模块，避免过大导致空旷 */
.test-name-detail--desktop .score-label {
  font-size: 8px !important;
  margin-bottom: 6rpx !important;
}

.test-name-detail--desktop .score-value-wrap {
  width: 184rpx !important;
  height: 184rpx !important;
  margin-bottom: 6rpx !important;
}

.test-name-detail--desktop .score-ring {
  border-width: 1px !important;
  border-style: dashed !important;
  border-color: rgba(255, 224, 160, 0.62) !important;
  box-shadow:
    0 0 18px rgba(212, 175, 55, 0.35),
    0 0 36px rgba(212, 175, 55, 0.12),
    inset 0 0 14px rgba(255, 224, 160, 0.08);
}

.test-name-detail--desktop .score-bagua-ring-2 {
  border-color: rgba(255, 224, 160, 0.42) !important;
  box-shadow:
    0 0 14px rgba(212, 175, 55, 0.28),
    inset 0 0 10px rgba(212, 175, 55, 0.06) !important;
}

.test-name-detail--desktop .score-yinyang-bg {
  opacity: 0.15;
  inset: 20% !important;
}

.test-name-detail--desktop .score-rune-dot {
  width: 2px;
  height: 2px;
}

.test-name-detail--desktop .score-rune-glyph {
  font-size: 8px;
}

.test-name-detail--desktop .score-bagua-symbol {
  font-size: 10px;
  transform-origin: 0 -88px;
}

.test-name-detail--desktop .score-bagua-symbol:nth-child(1) { transform: rotate(0deg) translateY(-88px) rotate(0deg); }
.test-name-detail--desktop .score-bagua-symbol:nth-child(2) { transform: rotate(45deg) translateY(-88px) rotate(-45deg); }
.test-name-detail--desktop .score-bagua-symbol:nth-child(3) { transform: rotate(90deg) translateY(-88px) rotate(-90deg); }
.test-name-detail--desktop .score-bagua-symbol:nth-child(4) { transform: rotate(135deg) translateY(-88px) rotate(-135deg); }
.test-name-detail--desktop .score-bagua-symbol:nth-child(5) { transform: rotate(180deg) translateY(-88px) rotate(-180deg); }
.test-name-detail--desktop .score-bagua-symbol:nth-child(6) { transform: rotate(225deg) translateY(-88px) rotate(-225deg); }
.test-name-detail--desktop .score-bagua-symbol:nth-child(7) { transform: rotate(270deg) translateY(-88px) rotate(-270deg); }
.test-name-detail--desktop .score-bagua-symbol:nth-child(8) { transform: rotate(315deg) translateY(-88px) rotate(-315deg); }

.test-name-detail--desktop .score {
  font-size: 44px !important;
}

.test-name-detail--desktop .score-stars {
  gap: 4rpx !important;
  margin-bottom: 8rpx !important;
}

.test-name-detail--desktop .score-star {
  font-size: 10px !important;
}

.test-name-detail--desktop .score-divider {
  margin: 8rpx 0 !important;
}

.test-name-detail--desktop .name {
  font-size: 18px !important;
  margin-top: 6rpx !important;
  margin-bottom: 4rpx !important;
}

.test-name-detail--desktop .quote {
  font-size: 12px !important;
  line-height: 1.45 !important;
}

.test-name-detail--desktop .detail-entry {
  margin-top: 10rpx !important;
  padding: 6rpx 14rpx !important;
}

.test-name-detail--desktop .score-mystic-ornaments {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
  margin-bottom: 2px;
}

.test-name-detail--desktop .score-mystic-line {
  width: min(320px, 72%);
  height: 1px;
  background: linear-gradient(90deg, transparent, rgba(212, 175, 55, 0.45), transparent);
}

.test-name-detail--desktop .score-mystic-bagua {
  display: flex;
  align-items: center;
  gap: 5px;
  color: rgba(212, 175, 55, 0.9);
}

.test-name-detail--desktop .score-mystic-symbol {
  font-size: 12px;
  letter-spacing: 0.08em;
  text-shadow: 0 0 10px rgba(212, 175, 55, 0.2);
}

.test-name-detail--desktop .score-mystic-dot {
  font-size: 10px;
  color: rgba(212, 175, 55, 0.45);
}

.test-name-detail--desktop .score-mystic-sigil {
  width: 24px;
  height: 24px;
  border-radius: 999px;
  border: 1px solid rgba(212, 175, 55, 0.35);
  background: radial-gradient(circle at 30% 30%, rgba(212, 175, 55, 0.16), rgba(212, 175, 55, 0.04));
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 0 14px rgba(212, 175, 55, 0.12);
}

.test-name-detail--desktop .score-mystic-sigil-text {
  font-size: 12px;
  color: #d4af37;
}

.test-name-detail--desktop .detail-entry-text {
  font-size: 10px !important;
}

.test-name-detail--desktop .section {
  margin-bottom: 0 !important;
}

.test-name-detail--desktop .detail-content--desktop > .section {
  order: 999;
}

.test-name-detail--desktop .detail-content--desktop > .score-card {
  order: 1;
}

.test-name-detail--desktop .module-order-1 {
  order: 1;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-2 {
  order: 2;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-3 {
  order: 3;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-4 {
  order: 4;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-5 {
  order: 5;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-6 {
  order: 6;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-7 {
  order: 7;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-8 {
  order: 8;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-9 {
  order: 9;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-10 {
  order: 10;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-11 {
  order: 11;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-12 {
  order: 12;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-13 {
  order: 13;
}
.test-name-detail--desktop .detail-content--desktop > .section.module-order-14 {
  order: 14;
}

.test-name-detail--desktop .section--desktop-wide {
  grid-column: 1 / -1;
}

/* 六维格局：电脑端不再超大占位，保持与其它卡片一致密度 */
.test-name-detail--desktop .section--sixdim-compact {
  grid-column: auto;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-section) {
  margin-bottom: 0 !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-bar) {
  margin-bottom: 6px !important;
  gap: 8rpx !important;
  padding: 0 2rpx !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-icon-grid) {
  width: 20rpx !important;
  height: 20rpx !important;
  gap: 1.5rpx !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-icon-dot) {
  width: 4rpx !important;
  height: 4rpx !important;
  border-radius: 1.5rpx !important;
  opacity: 0.8;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-bar-title) {
  font-size: 12px !important;
  letter-spacing: 0.1em !important;
  color: rgba(246, 236, 209, 0.95) !important;
  text-shadow: 0 0 10px rgba(212, 175, 55, 0.22);
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-card) {
  padding: 8px 8px 6px !important;
  border-radius: 10px !important;
  background: rgba(15, 23, 42, 0.48) !important;
  border: 1px solid rgba(212, 175, 55, 0.16) !important;
  backdrop-filter: blur(8px);
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-radar) {
  max-width: 170rpx !important;
  padding-top: 0 !important;
  height: 236px !important;
  border-radius: 12rpx !important;
  border: 1px solid rgba(212, 175, 55, 0.14);
  background:
    radial-gradient(circle at 50% 48%, rgba(245, 186, 54, 0.14), rgba(245, 186, 54, 0.02) 42%, transparent 72%),
    linear-gradient(160deg, rgba(255, 255, 255, 0.025), rgba(255, 255, 255, 0));
  box-shadow:
    inset 0 0 14px rgba(212, 175, 55, 0.08),
    0 4px 12px rgba(0, 0, 0, 0.24);
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-echart-inner) {
  top: 0 !important;
  inset: 0 !important;
  height: 100% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-radar-svg) {
  opacity: 0.84;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-radar-svg polygon) {
  stroke: rgba(212, 220, 236, 0.58) !important;
  stroke-width: 0.58 !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-radar-fill) {
  inset: 31% !important;
  background: linear-gradient(180deg, rgba(255, 205, 96, 0.86), rgba(230, 129, 38, 0.72)) !important;
  opacity: 0.9 !important;
  filter: drop-shadow(0 0 6px rgba(244, 176, 61, 0.2));
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label) {
  font-size: 12rpx !important;
  color: rgba(228, 232, 244, 0.84) !important;
  text-shadow: 0 0 6px rgba(95, 126, 196, 0.14);
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-0) {
  top: 10% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-1),
.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-5) {
  top: 29% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-1) {
  right: 11% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-5) {
  left: 11% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-2),
.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-4) {
  bottom: 30% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-2) {
  right: 9% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-4) {
  left: 9% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-label-3) {
  bottom: 8% !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-remark-center) {
  margin-top: 6px !important;
  padding: 0 4px !important;
}

.test-name-detail--desktop .section--sixdim-compact :deep(.sixdim-remark) {
  font-size: 9.5px !important;
  line-height: 1.35 !important;
}

.test-name-detail--desktop .section-title,
.test-name-detail--desktop .mystic-section-title {
  margin-bottom: 6px !important;
  padding-bottom: 4px !important;
  border-bottom: none !important;
  background: linear-gradient(90deg, transparent, rgba(212, 175, 55, 0.22), transparent) bottom / 100% 1px no-repeat !important;
}

.test-name-detail--desktop .section-text {
  font-size: 16px !important;
  letter-spacing: 0.12em !important;
}

.test-name-detail--desktop .section-detail-tip {
  font-size: 12px !important;
  opacity: 0.75;
}

.test-name-detail--desktop .meaning-card,
.test-name-detail--desktop .zodiac-card,
.test-name-detail--desktop .info-card,
.test-name-detail--desktop .wuge-card,
.test-name-detail--desktop .wuxing-card,
.test-name-detail--desktop .mystic-inner-card {
  padding: 12px 14px !important;
  border-radius: 10px;
  background: linear-gradient(165deg, rgba(255, 255, 255, 0.07), rgba(15, 23, 42, 0.45)) !important;
  border: 1px solid rgba(212, 175, 55, 0.22) !important;
  backdrop-filter: blur(8px);
  margin-bottom: 0 !important;
}

.test-name-detail--desktop .mystic-inner-card--flush {
  padding: 0 !important;
  background: transparent !important;
  border: none !important;
}

.test-name-detail--desktop .mystic-panel {
  padding: 14px 16px 16px !important;
  border-radius: 12px;
}

.test-name-detail--desktop .section-glyph {
  min-width: 36px;
  font-size: 13px;
  padding: 4px 8px;
}

.test-name-detail--desktop .line {
  font-size: 13px !important;
  line-height: 1.55 !important;
}

.test-name-detail--desktop .name {
  font-size: 22px !important;
}

.test-name-detail--desktop .quote {
  font-size: 13px !important;
  line-height: 1.5 !important;
}

.test-name-detail--desktop .bottom-spacer {
  display: none;
}

@media (min-width: 1200px) {
  .test-name-detail--desktop .detail-content--desktop {
    display: flex;
    flex-direction: column;
    grid-template-columns: none;
  }

  .test-name-detail--desktop .score-card {
    grid-column: auto;
  }

  /* 六维格局：三列大屏下占两列，避免单列显得突兀/空旷 */
  .test-name-detail--desktop .section--sixdim-compact {
    grid-column: auto;
  }
}
</style>
