guanwei 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +202 -0
  3. package/README.md +247 -0
  4. package/dist/assets/index-CsOD5rSo.js +102 -0
  5. package/dist/assets/index-CsOD5rSo.js.map +1 -0
  6. package/dist/assets/index-DQx62TUr.css +1 -0
  7. package/dist/favicon.svg +12 -0
  8. package/dist/index.html +15 -0
  9. package/eslint.config.js +28 -0
  10. package/index.html +25 -0
  11. package/package.json +96 -0
  12. package/postcss.config.js +10 -0
  13. package/public/favicon.svg +12 -0
  14. package/scripts/guanwei +245 -0
  15. package/scripts/install.sh +79 -0
  16. package/scripts/release.sh +42 -0
  17. package/scripts/setup.bat +36 -0
  18. package/scripts/setup.sh +123 -0
  19. package/server/src/data/db.json +12059 -0
  20. package/server/src/data/guanwei.db +0 -0
  21. package/server/src/data/lifeContext.ts +423 -0
  22. package/server/src/data/spreads.ts +255 -0
  23. package/server/src/data/tarotCards.ts +2353 -0
  24. package/server/src/index.ts +71 -0
  25. package/server/src/routes/ai.ts +376 -0
  26. package/server/src/routes/divine.ts +140 -0
  27. package/server/src/routes/hour.ts +40 -0
  28. package/server/src/routes/tarot.ts +129 -0
  29. package/server/src/routes/users.ts +140 -0
  30. package/server/src/services/chartBrief.ts +131 -0
  31. package/server/src/services/divineStore.ts +188 -0
  32. package/server/src/services/hourInference.ts +150 -0
  33. package/server/src/services/llmProvider.ts +181 -0
  34. package/server/src/services/promptBuilder.ts +327 -0
  35. package/server/src/services/relativesCheck.ts +139 -0
  36. package/server/src/services/sixRelatives.ts +70 -0
  37. package/server/src/services/skills.ts +116 -0
  38. package/server/src/types/index.ts +104 -0
  39. package/server/src/utils/semanticAnalyzer.ts +555 -0
  40. package/server/src/utils/tarotEngine.ts +849 -0
  41. package/server/tsconfig.json +18 -0
  42. package/shared/core/data/classics.ts +276 -0
  43. package/shared/core/data/courses.ts +211 -0
  44. package/shared/core/data/duanyu.ts +148 -0
  45. package/shared/core/data/ganzhi.ts +53 -0
  46. package/shared/core/data/gua64.ts +93 -0
  47. package/shared/core/data/liuren.ts +18 -0
  48. package/shared/core/data/liuyao.ts +73 -0
  49. package/shared/core/data/qimen.ts +27 -0
  50. package/shared/core/data/region-full.ts +3 -0
  51. package/shared/core/data/region.ts +310 -0
  52. package/shared/core/data/tarotSpreads.ts +78 -0
  53. package/shared/core/data/xiaoliuren.ts +18 -0
  54. package/shared/core/data/ziwei.ts +97 -0
  55. package/shared/core/data/zodiac.ts +20 -0
  56. package/shared/core/engine/astrology.ts +169 -0
  57. package/shared/core/engine/bazi.ts +306 -0
  58. package/shared/core/engine/calendar.ts +127 -0
  59. package/shared/core/engine/daily.ts +133 -0
  60. package/shared/core/engine/liuren.ts +86 -0
  61. package/shared/core/engine/liuyao.ts +93 -0
  62. package/shared/core/engine/meihua.ts +87 -0
  63. package/shared/core/engine/qimen.ts +78 -0
  64. package/shared/core/engine/questionFit.ts +94 -0
  65. package/shared/core/engine/tarot.ts +69 -0
  66. package/shared/core/engine/trueSolarTime.ts +86 -0
  67. package/shared/core/engine/xiaoliuren.ts +19 -0
  68. package/shared/core/engine/ziwei.ts +217 -0
  69. package/shared/core/engine/ziweiInterpret.ts +161 -0
  70. package/shared/core/types.ts +206 -0
  71. package/src/App.tsx +69 -0
  72. package/src/arts/registry.ts +31 -0
  73. package/src/assets/react.svg +1 -0
  74. package/src/components/Backdrop.tsx +27 -0
  75. package/src/components/BambooArt.tsx +41 -0
  76. package/src/components/CalendarInput.tsx +131 -0
  77. package/src/components/DateInput.tsx +103 -0
  78. package/src/components/Disclaimer.tsx +12 -0
  79. package/src/components/Empty.tsx +8 -0
  80. package/src/components/ErrorBoundary.tsx +38 -0
  81. package/src/components/HomeSideAnchor.tsx +62 -0
  82. package/src/components/InkVein.tsx +10 -0
  83. package/src/components/LifeEventsInput.tsx +47 -0
  84. package/src/components/LocationPicker.tsx +117 -0
  85. package/src/components/Motes.tsx +65 -0
  86. package/src/components/Navigation.tsx +80 -0
  87. package/src/components/ProfileForm.tsx +40 -0
  88. package/src/components/ProfilePicker.tsx +40 -0
  89. package/src/components/QuestionFields.tsx +47 -0
  90. package/src/components/ReportView.tsx +211 -0
  91. package/src/components/ResultCard.tsx +40 -0
  92. package/src/components/SealButton.tsx +18 -0
  93. package/src/components/SiteFooter.tsx +17 -0
  94. package/src/components/SiteNav.tsx +103 -0
  95. package/src/components/SongDialog.tsx +64 -0
  96. package/src/components/SongSearchSelect.tsx +81 -0
  97. package/src/components/SongSelect.tsx +61 -0
  98. package/src/components/StarField.tsx +119 -0
  99. package/src/components/TarotCard.tsx +129 -0
  100. package/src/components/TimeShichenInput.tsx +29 -0
  101. package/src/components/arts/AstrologyArt.tsx +141 -0
  102. package/src/components/arts/BaziArt.tsx +269 -0
  103. package/src/components/arts/LiurenArt.tsx +87 -0
  104. package/src/components/arts/LiuyaoArt.tsx +111 -0
  105. package/src/components/arts/MeihuaArt.tsx +105 -0
  106. package/src/components/arts/QimenArt.tsx +86 -0
  107. package/src/components/arts/TarotArt.tsx +110 -0
  108. package/src/components/arts/XiaoliurenArt.tsx +93 -0
  109. package/src/components/arts/ZiweiArt.tsx +296 -0
  110. package/src/data/arts.ts +84 -0
  111. package/src/data/lifeContext.ts +423 -0
  112. package/src/data/sample-report.json +146 -0
  113. package/src/data/shichen.ts +21 -0
  114. package/src/data/spreads.ts +255 -0
  115. package/src/data/tarotCards.ts +2353 -0
  116. package/src/hooks/useAIInterpret.ts +76 -0
  117. package/src/hooks/useDivine.ts +39 -0
  118. package/src/hooks/useTheme.ts +29 -0
  119. package/src/index.css +190 -0
  120. package/src/lib/utils.ts +6 -0
  121. package/src/main.tsx +12 -0
  122. package/src/pages/AboutPage.tsx +25 -0
  123. package/src/pages/AcademyPage.tsx +128 -0
  124. package/src/pages/AstrologyPage.tsx +33 -0
  125. package/src/pages/AuthPage.tsx +263 -0
  126. package/src/pages/BaZiPage.tsx +33 -0
  127. package/src/pages/ClassicsPage.tsx +120 -0
  128. package/src/pages/DemoPage.tsx +161 -0
  129. package/src/pages/HistoryPage.tsx +133 -0
  130. package/src/pages/Home.tsx +179 -0
  131. package/src/pages/HomePage.tsx +174 -0
  132. package/src/pages/ModulePage.tsx +320 -0
  133. package/src/pages/SpreadEditor.tsx +230 -0
  134. package/src/pages/SpreadEditorPage.tsx +105 -0
  135. package/src/pages/SpreadLibrary.tsx +199 -0
  136. package/src/pages/SpreadLibraryPage.tsx +17 -0
  137. package/src/pages/TarotPage.tsx +738 -0
  138. package/src/pages/ZiWeiPage.tsx +33 -0
  139. package/src/services/api.ts +342 -0
  140. package/src/stores/useDivinationStore.ts +22 -0
  141. package/src/styles/song.css +1066 -0
  142. package/src/styles/tokens.css +60 -0
  143. package/src/types/index.ts +104 -0
  144. package/src/utils/astrologyCalc.ts +61 -0
  145. package/src/utils/comboEngine.ts +134 -0
  146. package/src/utils/dst.ts +24 -0
  147. package/src/utils/panTone.ts +206 -0
  148. package/src/utils/recordStore.ts +60 -0
  149. package/src/utils/semanticAnalyzer.ts +555 -0
  150. package/src/utils/spreadStorage.ts +32 -0
  151. package/src/utils/storage.ts +27 -0
  152. package/src/utils/tarotEngine.ts +849 -0
  153. package/src/utils/userStore.ts +234 -0
  154. package/src/vite-env.d.ts +1 -0
  155. package/tailwind.config.js +52 -0
  156. package/tsconfig.json +40 -0
  157. package/vite.config.ts +34 -0
@@ -0,0 +1 @@
1
+ @import"https://fonts.googleapis.com/css2?family=Cinzel:wght@400;600;700&family=Noto+Sans+SC:wght@300;400;500;700&display=swap";@import"https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300;400;500;600;700&family=Ma+Shan+Zheng&family=Zhi+Mang+Xing&display=swap";*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}html{scroll-behavior:smooth;overflow-x:hidden}body{--tw-bg-opacity: 1;background-color:rgb(10 10 15 / var(--tw-bg-opacity, 1));font-family:Noto Sans SC,sans-serif;--tw-text-opacity: 1;color:rgb(224 224 224 / var(--tw-text-opacity, 1));overflow-x:hidden}::-moz-selection{background-color:#c9a2274d;--tw-text-opacity: 1;color:rgb(224 224 224 / var(--tw-text-opacity, 1))}::selection{background-color:#c9a2274d;--tw-text-opacity: 1;color:rgb(224 224 224 / var(--tw-text-opacity, 1))}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{--tw-bg-opacity: 1;background-color:rgb(10 10 15 / var(--tw-bg-opacity, 1))}::-webkit-scrollbar-thumb{border-radius:9999px;background-color:#c9a2274d}::-webkit-scrollbar-thumb:hover{background-color:#c9a22780}.glass-panel{border-radius:.5rem;border-width:1px;border-color:#c9a22733;background-color:#1a1a2e99;--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.shimmer-text{background:linear-gradient(90deg,#c9a227,gold,#c9a227);background-size:200% auto;-webkit-background-clip:text;-webkit-text-fill-color:transparent;animation:shimmer 3s linear infinite}.pointer-events-none{pointer-events:none}.visible{visibility:visible}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-1{top:.25rem;right:.25rem;bottom:.25rem;left:.25rem}.-bottom-1{bottom:-.25rem}.-top-3{top:-.75rem}.bottom-0{bottom:0}.bottom-1{bottom:.25rem}.bottom-8{bottom:2rem}.left-0{left:0}.left-1{left:.25rem}.left-1\.5{left:.375rem}.left-1\/2{left:50%}.left-3{left:.75rem}.right-0{right:0}.right-1{right:.25rem}.right-1\.5{right:.375rem}.right-3{right:.75rem}.top-0{top:0}.top-1{top:.25rem}.top-1\.5{top:.375rem}.top-1\/2{top:50%}.z-50{z-index:50}.mx-auto{margin-left:auto;margin-right:auto}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-11{margin-left:2.75rem}.ml-auto{margin-left:auto}.mt-0{margin-top:0}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-3\.5{margin-top:.875rem}.mt-4{margin-top:1rem}.mt-5{margin-top:1.25rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.line-clamp-1{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:1}.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-1\.5{height:.375rem}.h-12{height:3rem}.h-16{height:4rem}.h-20{height:5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-48{height:12rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-72{height:18rem}.h-8{height:2rem}.h-full{height:100%}.h-px{height:1px}.max-h-96{max-height:24rem}.min-h-screen{min-height:100vh}.w-12{width:3rem}.w-16{width:4rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-32{width:8rem}.w-4{width:1rem}.w-44{width:11rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-8{width:2rem}.w-full{width:100%}.w-px{width:1px}.max-w-24{max-width:6rem}.max-w-2xl{max-width:42rem}.max-w-4xl{max-width:56rem}.max-w-5xl{max-width:64rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-xl{max-width:36rem}.flex-1{flex:1 1 0%}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-0{--tw-translate-y: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-4{--tw-translate-y: 1rem;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.rotate-45{--tw-rotate: 45deg;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes bounce{0%,to{transform:translateY(-25%);animation-timing-function:cubic-bezier(.8,0,1,1)}50%{transform:none;animation-timing-function:cubic-bezier(0,0,.2,1)}}.animate-bounce{animation:bounce 1s infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.animate-pulse-glow{animation:pulseGlow 2s ease-in-out infinite}@keyframes spin{to{transform:rotate(360deg)}}.animate-spin{animation:spin 1s linear infinite}.cursor-move{cursor:move}.cursor-pointer{cursor:pointer}.resize-none{resize:none}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.25rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem * var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem * var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(1.5rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.break-all{word-break:break-all}.rounded{border-radius:.25rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-sm{border-radius:.125rem}.border{border-width:1px}.border-2{border-width:2px}.border-b{border-bottom-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-ancient{--tw-border-opacity: 1;border-color:rgb(201 162 39 / var(--tw-border-opacity, 1))}.border-ancient\/10{border-color:#c9a2271a}.border-ancient\/20{border-color:#c9a22733}.border-ancient\/30{border-color:#c9a2274d}.border-ancient\/40{border-color:#c9a22766}.border-ancient\/50{border-color:#c9a22780}.border-ancient\/60{border-color:#c9a22799}.border-crimson\/40{border-color:#8b000066}.border-crimson\/60{border-color:#8b000099}.border-green-500\/20{border-color:#22c55e33}.border-mystic\/20{border-color:#7b68ee33}.border-mystic\/30{border-color:#7b68ee4d}.border-mystic\/50{border-color:#7b68ee80}.border-red-500\/20{border-color:#ef444433}.border-stardust\/10{border-color:#e0e0e01a}.border-stardust\/15{border-color:#e0e0e026}.border-stardust\/20{border-color:#e0e0e033}.border-yellow-500\/20{border-color:#eab30833}.bg-abyss{--tw-bg-opacity: 1;background-color:rgb(26 26 46 / var(--tw-bg-opacity, 1))}.bg-abyss\/20{background-color:#1a1a2e33}.bg-abyss\/30{background-color:#1a1a2e4d}.bg-abyss\/40{background-color:#1a1a2e66}.bg-abyss\/60{background-color:#1a1a2e99}.bg-ancient{--tw-bg-opacity: 1;background-color:rgb(201 162 39 / var(--tw-bg-opacity, 1))}.bg-ancient\/10{background-color:#c9a2271a}.bg-ancient\/20{background-color:#c9a22733}.bg-ancient\/40{background-color:#c9a22766}.bg-ancient\/5{background-color:#c9a2270d}.bg-mystic\/10{background-color:#7b68ee1a}.bg-mystic\/20{background-color:#7b68ee33}.bg-mystic\/5{background-color:#7b68ee0d}.bg-void{--tw-bg-opacity: 1;background-color:rgb(10 10 15 / var(--tw-bg-opacity, 1))}.bg-void\/40{background-color:#0a0a0f66}.bg-void\/50{background-color:#0a0a0f80}.bg-void\/80{background-color:#0a0a0fcc}.bg-void\/95{background-color:#0a0a0ff2}.bg-gradient-to-b{background-image:linear-gradient(to bottom,var(--tw-gradient-stops))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-ancient\/20{--tw-gradient-from: rgb(201 162 39 / .2) var(--tw-gradient-from-position);--tw-gradient-to: rgb(201 162 39 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-ancient\/50{--tw-gradient-from: rgb(201 162 39 / .5) var(--tw-gradient-from-position);--tw-gradient-to: rgb(201 162 39 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-crimson\/20{--tw-gradient-from: rgb(139 0 0 / .2) var(--tw-gradient-from-position);--tw-gradient-to: rgb(139 0 0 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.from-mystic\/20{--tw-gradient-from: rgb(123 104 238 / .2) var(--tw-gradient-from-position);--tw-gradient-to: rgb(123 104 238 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-ancient\/20{--tw-gradient-to: rgb(201 162 39 / .2) var(--tw-gradient-to-position)}.to-crimson\/20{--tw-gradient-to: rgb(139 0 0 / .2) var(--tw-gradient-to-position)}.to-mystic\/20{--tw-gradient-to: rgb(123 104 238 / .2) var(--tw-gradient-to-position)}.to-transparent{--tw-gradient-to: transparent var(--tw-gradient-to-position)}.p-1{padding:.25rem}.p-12{padding:3rem}.p-2{padding:.5rem}.p-2\.5{padding:.625rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-10{padding-left:2.5rem;padding-right:2.5rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.pb-16{padding-bottom:4rem}.pl-10{padding-left:2.5rem}.pr-10{padding-right:2.5rem}.pr-3{padding-right:.75rem}.pt-2{padding-top:.5rem}.pt-20{padding-top:5rem}.pt-6{padding-top:1.5rem}.text-left{text-align:left}.text-center{text-align:center}.font-display{font-family:Cinzel,serif}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-5xl{font-size:3rem;line-height:1}.text-\[10px\]{font-size:10px}.text-\[11px\]{font-size:11px}.text-\[9px\]{font-size:9px}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.tabular-nums{--tw-numeric-spacing: tabular-nums;font-variant-numeric:var(--tw-ordinal) var(--tw-slashed-zero) var(--tw-numeric-figure) var(--tw-numeric-spacing) var(--tw-numeric-fraction)}.leading-none{line-height:1}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-ancient{--tw-text-opacity: 1;color:rgb(201 162 39 / var(--tw-text-opacity, 1))}.text-ancient\/30{color:#c9a2274d}.text-ancient\/40{color:#c9a22766}.text-ancient\/60{color:#c9a22799}.text-ancient\/70{color:#c9a227b3}.text-ancient\/80{color:#c9a227cc}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-crimson{--tw-text-opacity: 1;color:rgb(139 0 0 / var(--tw-text-opacity, 1))}.text-crimson\/60{color:#8b000099}.text-crimson\/70{color:#8b0000b3}.text-crimson\/80{color:#8b0000cc}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity, 1))}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-green-400\/60{color:#4ade8099}.text-mystic{--tw-text-opacity: 1;color:rgb(123 104 238 / var(--tw-text-opacity, 1))}.text-mystic\/60{color:#7b68ee99}.text-mystic\/70{color:#7b68eeb3}.text-mystic\/80{color:#7b68eecc}.text-orange-400{--tw-text-opacity: 1;color:rgb(251 146 60 / var(--tw-text-opacity, 1))}.text-red-400\/60{color:#f8717199}.text-stardust{--tw-text-opacity: 1;color:rgb(224 224 224 / var(--tw-text-opacity, 1))}.text-stardust\/30{color:#e0e0e04d}.text-stardust\/40{color:#e0e0e066}.text-stardust\/50{color:#e0e0e080}.text-stardust\/60{color:#e0e0e099}.text-stardust\/70{color:#e0e0e0b3}.text-void{--tw-text-opacity: 1;color:rgb(10 10 15 / var(--tw-text-opacity, 1))}.text-yellow-400\/60{color:#facc1599}.underline{text-decoration-line:underline}.placeholder-stardust\/30::-moz-placeholder{color:#e0e0e04d}.placeholder-stardust\/30::placeholder{color:#e0e0e04d}.accent-ancient{accent-color:#c9a227}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-\[0_0_12px_rgba\(201\,162\,39\,0\.25\)\]{--tw-shadow: 0 0 12px rgba(201,162,39,.25);--tw-shadow-colored: 0 0 12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.blur-3xl{--tw-blur: blur(64px);filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur: blur(12px);-webkit-backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia);backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}.duration-700{transition-duration:.7s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}.perspective-1000{perspective:1000px}.preserve-3d{transform-style:preserve-3d}.backface-hidden{backface-visibility:hidden}.rotate-y-180{transform:rotateY(180deg)}.animate-fade-in{animation:fade-in .5s ease-out forwards}.animate-pulse-glow{animation:pulse-glow 2s ease-in-out infinite}@keyframes shimmer{0%{background-position:200% center}to{background-position:-200% center}}@keyframes fade-in{0%{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-10px)}}@keyframes pulse-glow{0%,to{box-shadow:0 0 5px #c9a2274d}50%{box-shadow:0 0 20px #c9a22799}}@keyframes typing-cursor{0%,to{opacity:1}50%{opacity:0}}@media print{body{background:#fff!important}header,footer,nav,aside,.site-head,.site-nav,.back-link,.module-hero,.mh-intro,.mh-row,.altar-panel,.fit-card,.fit-note,.btn-row,.ai-streaming,.ai-error,.result-placeholder,.disclaimer,.d-title,.motes,.ink-vein,.bamboo,.cal-grid,.profile-card,.no-print{display:none!important}main{display:block!important;padding:0!important}.wrap{max-width:none!important;padding:0!important}.altar{display:block!important}.altar-result{width:100%!important;max-width:none!important}.report-view{color:#000!important;background:#fff!important}.report-view .result-card{-moz-column-break-inside:auto;break-inside:auto;border:1px solid #ddd!important;background:#fff!important;box-shadow:none!important;color:#000!important;margin-bottom:6mm}.report-view .result-card h3{-moz-column-break-after:avoid;break-after:avoid}.report-view .domain-cell,.report-view li,.report-view .traits-grid .trait{-moz-column-break-inside:avoid;break-inside:avoid}.report-view .result-title{-moz-column-break-after:avoid;break-after:avoid}.report-view .result-text,.report-view p,.report-view li,.report-view h3{color:#000!important}.report-view .domain-name{color:#555!important}@page{margin:12mm}}@keyframes pulse-soft{0%,to{opacity:.55}50%{opacity:1}}.hover\:border-ancient:hover{--tw-border-opacity: 1;border-color:rgb(201 162 39 / var(--tw-border-opacity, 1))}.hover\:border-ancient\/30:hover{border-color:#c9a2274d}.hover\:border-ancient\/40:hover{border-color:#c9a22766}.hover\:border-ancient\/50:hover{border-color:#c9a22780}.hover\:border-ancient\/60:hover{border-color:#c9a22799}.hover\:border-crimson:hover{--tw-border-opacity: 1;border-color:rgb(139 0 0 / var(--tw-border-opacity, 1))}.hover\:border-mystic\/60:hover{border-color:#7b68ee99}.hover\:border-stardust\/40:hover{border-color:#e0e0e066}.hover\:border-stardust\/50:hover{border-color:#e0e0e080}.hover\:bg-ancient\/10:hover{background-color:#c9a2271a}.hover\:bg-ancient\/30:hover{background-color:#c9a2274d}.hover\:bg-ancient\/5:hover{background-color:#c9a2270d}.hover\:bg-mystic\/30:hover{background-color:#7b68ee4d}.hover\:bg-mystic\/5:hover{background-color:#7b68ee0d}.hover\:text-ancient:hover{--tw-text-opacity: 1;color:rgb(201 162 39 / var(--tw-text-opacity, 1))}.hover\:text-ancient\/80:hover{color:#c9a227cc}.hover\:text-crimson:hover{--tw-text-opacity: 1;color:rgb(139 0 0 / var(--tw-text-opacity, 1))}.hover\:text-stardust:hover{--tw-text-opacity: 1;color:rgb(224 224 224 / var(--tw-text-opacity, 1))}.focus\:border-ancient\/60:focus{border-color:#c9a22799}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-ancient:focus{--tw-ring-opacity: 1;--tw-ring-color: rgb(201 162 39 / var(--tw-ring-opacity, 1))}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-30:disabled{opacity:.3}@keyframes pulseGlow{0%,to{box-shadow:0 0 5px #c9a2274d}50%{box-shadow:0 0 20px #c9a22799}}.group:hover .group-hover\:animate-pulse-glow{animation:pulseGlow 2s ease-in-out infinite}.group:hover .group-hover\:border-ancient{--tw-border-opacity: 1;border-color:rgb(201 162 39 / var(--tw-border-opacity, 1))}.group:hover .group-hover\:text-ancient{--tw-text-opacity: 1;color:rgb(201 162 39 / var(--tw-text-opacity, 1))}.group:hover .group-hover\:text-ancient\/60{color:#c9a22799}.group:hover .group-hover\:opacity-100{opacity:1}.group:hover .group-hover\:animate-pulse-glow{animation:pulse-glow 2s ease-in-out infinite}@media(min-width:640px){.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.sm\:grid-cols-6{grid-template-columns:repeat(6,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media(min-width:768px){.md\:flex{display:flex}.md\:hidden{display:none}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-8{grid-template-columns:repeat(8,minmax(0,1fr))}.md\:gap-6{gap:1.5rem}.md\:p-8{padding:2rem}.md\:text-4xl{font-size:2.25rem;line-height:2.5rem}.md\:text-7xl{font-size:4.5rem;line-height:1}.md\:text-base{font-size:1rem;line-height:1.5rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media(min-width:1024px){.lg\:grid-cols-10{grid-template-columns:repeat(10,minmax(0,1fr))}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:text-8xl{font-size:6rem;line-height:1}}:root{--paper: #F5F1E7;--paper-deep: #EDE8D9;--paper-dim: #E4DECC;--ink: #37342C;--ink-soft: #6B675C;--ink-faint: #97917F;--line: rgba(55,52,44,.16);--line-soft: rgba(55,52,44,.1);--celadon: #96A88F;--celadon-deep: #5F7660;--celadon-tint: #E3E8DC;--crab: #7C9792;--crab-tint: #DCE4E0;--rice: #D9CDAC;--rice-tint: #EDE5CE;--cinnabar: #A5402D;--cinnabar-deep:#8C3526;--cinnabar-tint:#F3E3DC;--white-paper: #FBF9F3;--font-song: "Noto Serif SC","Songti SC","Source Han Serif SC","SimSun","STSong",serif;--font-kai: "Ma Shan Zheng","Kaiti SC","KaiTi","STKaiti","Noto Serif SC",serif;--font-zm: "Zhi Mang Xing","Ma Shan Zheng","Kaiti SC",serif;--fs-xs: .75rem;--fs-sm: .875rem;--fs-base:1.05rem;--fs-lg: 1.35rem;--fs-xl: clamp(1.6rem, 3.2vw, 2.2rem);--fs-2xl: clamp(2.2rem, 5.5vw, 3.6rem);--fs-3xl: clamp(3rem, 9vw, 6rem);--fs-display: clamp(4.2rem, 14vw, 10rem);--sp-1:.5rem;--sp-2:1rem;--sp-3:1.5rem;--sp-4:2rem;--sp-5:3rem;--sp-6:4.5rem;--sp-7:6.5rem;--sp-8:9rem;--r-sm:2px;--r-md:4px;--r-lg:8px;--shadow-paper: 0 1px 2px rgba(55,52,44,.06), 0 10px 30px -18px rgba(55,52,44,.18);--ease-out: cubic-bezier(.22,1,.36,1);--ease-ink: cubic-bezier(.5,0,.1,1);--dur-fast: .22s;--dur-mid: .6s;--dur-slow: 1.2s}*{margin:0;padding:0;box-sizing:border-box}html{scroll-behavior:smooth}body{font-family:var(--font-song);color:var(--ink);background:var(--paper);font-size:var(--fs-base);line-height:1.85;-webkit-font-smoothing:antialiased;overflow-x:hidden}::-moz-selection{background:var(--cinnabar);color:var(--paper)}::selection{background:var(--cinnabar);color:var(--paper)}img,svg{display:block;max-width:100%}a{color:inherit;text-decoration:none}button{font-family:inherit;cursor:pointer;background:none;border:none;color:inherit}input,select,textarea{font-family:inherit;color:inherit}#paper-grain{position:fixed;top:0;right:0;bottom:0;left:0;z-index:-2;pointer-events:none;background:radial-gradient(120% 90% at 12% 0%,rgba(255,255,255,.55),transparent 55%),radial-gradient(100% 80% at 100% 100%,rgba(198,188,160,.22),transparent 60%)}#paper-grain:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background-image:repeating-linear-gradient(0deg,rgba(55,52,44,.016) 0 1px,transparent 1px 3px),repeating-linear-gradient(90deg,rgba(55,52,44,.012) 0 1px,transparent 1px 5px);opacity:.5}#mountains{position:fixed;left:0;right:0;bottom:0;height:38vh;z-index:-1;pointer-events:none;opacity:.5}.wrap{max-width:1200px;margin:0 auto;padding:0 clamp(1.25rem,4vw,3rem)}.site-head{position:sticky;top:0;z-index:50;-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);background:linear-gradient(180deg,#f5f1e7eb,#f5f1e7b8);border-bottom:1px solid var(--line-soft)}.head-inner{display:flex;align-items:center;justify-content:space-between;padding-block:.8rem;gap:1rem}.brand{display:flex;align-items:baseline;gap:.6rem;flex-shrink:0}.brand .seal{align-self:center;font-family:var(--font-kai);font-size:.72rem;letter-spacing:.12em;color:var(--cinnabar);border:1.5px solid var(--cinnabar);padding:.28rem .34rem;line-height:1;border-radius:2px}.brand .name{font-family:var(--font-kai);font-size:1.45rem;letter-spacing:.32em;color:var(--ink)}.brand .sub{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.3em;white-space:nowrap}.nav-links{display:flex;align-items:center;gap:clamp(.6rem,2vw,1.6rem);flex-wrap:wrap;justify-content:flex-end}.nav-links a{font-size:var(--fs-sm);letter-spacing:.18em;color:var(--ink-soft);padding:.3rem 0;position:relative;transition:color var(--dur-fast) var(--ease-out)}.nav-links a:after{content:"";position:absolute;left:0;right:0;bottom:0;height:1px;background:var(--cinnabar);transform:scaleX(0);transform-origin:right;transition:transform var(--dur-mid) var(--ease-out)}.nav-links a:hover{color:var(--ink)}.nav-links a:hover:after{transform:scaleX(1);transform-origin:left}.nav-links a.active{color:var(--cinnabar)}.view{display:none;animation:viewIn .9s var(--ease-out) both}.view.active{display:block}@keyframes viewIn{0%{opacity:0;transform:translateY(18px)}to{opacity:1;transform:none}}.hero{position:relative;min-height:calc(100vh - 76px);display:flex;flex-direction:column;justify-content:center;padding-block:var(--sp-6)}.hero-kicker{display:flex;align-items:center;gap:1rem;font-size:var(--fs-sm);letter-spacing:.5em;color:var(--ink-faint);margin-bottom:var(--sp-3)}.hero-kicker:before{content:"";width:3.5rem;height:1px;background:var(--ink-faint)}.hero-title{font-family:var(--font-kai);font-size:var(--fs-display);letter-spacing:.12em;line-height:1.05;color:var(--ink);text-shadow:0 2px 30px rgba(255,255,255,.6)}.hero-title .accent{color:var(--celadon-deep);font-family:var(--font-zm)}.hero-line{margin-top:var(--sp-4);font-size:var(--fs-lg);color:var(--ink-soft);max-width:34ch;line-height:2;letter-spacing:.08em}.hero-line em{font-style:normal;color:var(--cinnabar)}.hero-desc{margin-top:var(--sp-3);max-width:52ch;color:var(--ink-faint);font-size:var(--fs-sm);letter-spacing:.14em;line-height:2.1}.hero-actions{margin-top:var(--sp-5);display:flex;gap:1rem;flex-wrap:wrap}.btn-seal{position:relative;padding:.85rem 2.2rem;font-family:var(--font-kai);font-size:1.05rem;letter-spacing:.35em;color:var(--cinnabar);border:1.5px solid var(--cinnabar);border-radius:2px;transition:all var(--dur-mid) var(--ease-out);overflow:hidden;display:inline-block;cursor:pointer}.btn-seal:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:var(--cinnabar);transform:scaleY(0);transform-origin:top;transition:transform var(--dur-mid) var(--ease-out)}.btn-seal:hover:before{transform:scaleY(1)}.btn-seal.btn-ghost{color:var(--ink-soft);border-color:var(--ink-soft)}.btn-seal.btn-ghost:before{background:var(--ink-soft)}.btn-seal.btn-ghost:hover{color:var(--paper)}.hero-vert{position:absolute;right:clamp(1rem,4vw,3rem);top:50%;transform:translateY(-50%);writing-mode:vertical-rl;font-family:var(--font-kai);font-size:var(--fs-sm);letter-spacing:.6em;color:var(--ink-faint);opacity:.75;display:flex;gap:2.2rem}.hero-vert span{white-space:nowrap}.scroll-hint{position:absolute;bottom:2.4rem;left:50%;transform:translate(-50%);writing-mode:vertical-rl;font-size:var(--fs-xs);letter-spacing:.5em;color:var(--ink-faint);display:flex;align-items:center;gap:.6rem;animation:floatHint 3.2s var(--ease-ink) infinite}.scroll-hint:after{content:"";width:1px;height:3rem;background:linear-gradient(180deg,var(--ink-faint),transparent)}.section-head{margin-bottom:var(--sp-5)}.section-eyebrow{display:flex;align-items:center;gap:1rem;font-size:var(--fs-sm);letter-spacing:.5em;color:var(--ink-faint)}.section-eyebrow:before{content:"";width:2.5rem;height:1px;background:var(--ink-faint)}.section-title{font-family:var(--font-kai);font-size:var(--fs-2xl);letter-spacing:.2em;margin-top:.8rem;color:var(--ink)}.section-note{margin-top:.6rem;color:var(--ink-faint);font-size:var(--fs-sm);letter-spacing:.12em}.arts-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(1.2rem,2.5vw,2rem)}@media(max-width:900px){.arts-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:560px){.arts-grid{grid-template-columns:1fr}}.art-card{position:relative;background:linear-gradient(175deg,var(--white-paper),var(--paper) 70%,var(--paper-deep));border:1px solid var(--line);border-radius:var(--r-md);padding:var(--sp-4) var(--sp-3);cursor:pointer;transition:transform var(--dur-mid) var(--ease-out),box-shadow var(--dur-mid) var(--ease-out),border-color var(--dur-mid);overflow:hidden;min-height:240px;display:flex;flex-direction:column;text-decoration:none;color:inherit}.art-card:before{content:"";position:absolute;top:0;left:1.5rem;right:1.5rem;height:1px;background:linear-gradient(90deg,transparent,var(--celadon),transparent)}.art-card:after{content:"";position:absolute;inset:auto -30% -55% -30%;height:90%;background:radial-gradient(ellipse at center,rgba(150,168,143,.16),transparent 65%);opacity:0;transition:opacity var(--dur-slow) var(--ease-ink)}.art-card:hover{transform:translateY(-4px);box-shadow:var(--shadow-paper);border-color:var(--celadon)}.art-card:hover:after{opacity:1}.art-card:focus-visible{outline:2px solid var(--celadon-deep);outline-offset:2px}.art-num{font-family:var(--font-kai);font-size:var(--fs-sm);color:var(--ink-faint);letter-spacing:.3em;display:flex;align-items:center;gap:.5rem}.art-num:after{content:"";flex:1;height:1px;background:var(--line-soft)}.art-symbol{margin:var(--sp-2) 0 var(--sp-2);font-family:var(--font-zm);font-size:var(--fs-2xl);color:var(--celadon-deep);line-height:1;text-align:center;width:3.4rem;height:3.4rem;margin-left:auto;margin-right:auto;display:flex;align-items:center;justify-content:center;border:1px solid var(--line-soft);border-radius:50%;background:radial-gradient(circle at 35% 30%,var(--celadon-tint),transparent 70%)}.art-name{font-family:var(--font-kai);font-size:var(--fs-xl);letter-spacing:.24em;text-align:center;color:var(--ink)}.art-poem{text-align:center;color:var(--ink-faint);font-size:var(--fs-sm);letter-spacing:.18em;margin-top:.6rem}.art-enter{margin-top:auto;padding-top:var(--sp-2);text-align:center;font-size:var(--fs-xs);letter-spacing:.4em;color:var(--ink-faint);transition:color var(--dur-fast)}.art-card:hover .art-enter{color:var(--cinnabar)}.about{background:var(--paper-deep);border-block:1px solid var(--line-soft)}.about-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--sp-4)}@media(max-width:860px){.about-grid{grid-template-columns:1fr}}.about-item{padding-right:var(--sp-3)}.about-item h3{font-family:var(--font-kai);font-size:1.1rem;letter-spacing:.2em;display:flex;align-items:center;gap:.7rem;color:var(--ink)}.about-item h3:before{content:"·";color:var(--cinnabar);font-size:1.4rem}.about-item p{margin-top:.7rem;font-size:var(--fs-sm);color:var(--ink-soft);line-height:2.1;letter-spacing:.06em}.module-hero{padding:var(--sp-7) 0 var(--sp-5);position:relative}.module-kicker{display:flex;align-items:center;gap:1rem;font-size:var(--fs-sm);letter-spacing:.4em;color:var(--ink-faint)}.module-kicker:before{content:"";width:2.5rem;height:1px;background:var(--ink-faint)}.module-title-row{display:flex;align-items:flex-end;gap:var(--sp-3);flex-wrap:wrap;margin-top:1rem}.module-title{font-family:var(--font-kai);font-size:var(--fs-2xl);letter-spacing:.18em;color:var(--ink)}.module-title .cn{display:block;font-family:var(--font-zm);font-size:var(--fs-3xl);color:var(--celadon-deep);letter-spacing:.1em}.module-poem{font-family:var(--font-kai);color:var(--ink-soft);font-size:var(--fs-lg);letter-spacing:.3em;padding-bottom:.5rem}.module-intro{margin-top:var(--sp-2);max-width:62ch;color:var(--ink-soft);font-size:var(--fs-sm);letter-spacing:.08em;line-height:2.2}.module-intro .source{color:var(--ink-faint);font-size:var(--fs-xs)}.altar{display:grid;grid-template-columns:340px 1fr;gap:var(--sp-5);align-items:start}@media(max-width:960px){.altar{grid-template-columns:1fr}}.altar-panel{background:linear-gradient(175deg,var(--white-paper),var(--paper-deep));border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--sp-4);position:sticky;top:88px}@media(max-width:960px){.altar-panel{position:static}}.panel-title{font-family:var(--font-kai);font-size:1.1rem;letter-spacing:.25em;display:flex;align-items:center;gap:.6rem;color:var(--ink);padding-bottom:var(--sp-2);border-bottom:1px solid var(--line-soft)}.panel-title:before{content:"◇";color:var(--celadon-deep);font-size:.8rem}.field{margin-top:var(--sp-3)}.field label{display:block;font-size:var(--fs-sm);letter-spacing:.2em;color:var(--ink-soft);margin-bottom:.4rem}.field .hint{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.08em;margin-top:.3rem}.field-row{display:grid;grid-template-columns:1fr 1fr;gap:.8rem}.input-line{width:100%;padding:.6rem .8rem;background:#fbf9f399;border:1px solid var(--line);border-radius:var(--r-sm);font-size:var(--fs-base);transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}.input-line:focus{outline:none;border-color:var(--celadon-deep);box-shadow:0 0 0 3px #5f76601f}select.input-line{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--ink-soft) 50%),linear-gradient(135deg,var(--ink-soft) 50%,transparent 50%);background-position:calc(100% - 18px) 50%,calc(100% - 13px) 50%;background-size:5px 5px;background-repeat:no-repeat}.btn-divine{margin-top:var(--sp-4);width:100%;padding:1rem;background:var(--celadon-deep);color:var(--paper);font-family:var(--font-kai);font-size:1.15rem;letter-spacing:.5em;text-indent:.5em;border-radius:var(--r-sm);transition:background var(--dur-mid) var(--ease-out),transform var(--dur-mid);cursor:pointer}.btn-divine:hover{background:var(--cinnabar);transform:translateY(-1px)}.btn-divine:disabled{opacity:.5;cursor:wait}.btn-divine .small{display:block;font-size:var(--fs-xs);letter-spacing:.3em;font-family:var(--font-song);color:#fbf9f3bf;margin-top:.15rem}.altar-result{min-height:320px}.result-placeholder{display:flex;flex-direction:column;align-items:center;justify-content:center;min-height:320px;gap:.8rem;color:var(--ink-faint);border:1px dashed var(--line);border-radius:var(--r-lg);padding:var(--sp-4);text-align:center}.result-placeholder .glyph{font-family:var(--font-zm);font-size:3.4rem;color:var(--rice)}.result-placeholder p{letter-spacing:.3em;font-size:var(--fs-sm)}.result-card{background:linear-gradient(175deg,var(--white-paper),var(--paper-deep));border:1px solid var(--line);border-radius:var(--r-lg);padding:var(--sp-4);margin-bottom:var(--sp-3);animation:viewIn .8s var(--ease-out) both}.result-card h3{font-family:var(--font-kai);font-size:1.1rem;letter-spacing:.2em;color:var(--celadon-deep);display:flex;align-items:center;gap:.6rem;padding-bottom:.8rem;margin-bottom:var(--sp-2);border-bottom:1px solid var(--line-soft)}.result-card h3:before{content:"◆";font-size:.8rem}.result-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:var(--sp-2)}.result-cell{padding:.9rem;background:#fbf9f38c;border:1px solid var(--line-soft);border-radius:var(--r-sm)}.result-cell .k{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.25em}.result-cell .v{font-size:1.15rem;color:var(--ink);margin-top:.2rem;letter-spacing:.08em;font-weight:500}.result-cell .v.big{font-size:1.5rem;font-family:var(--font-kai)}.result-cell .v.hot{color:var(--cinnabar)}.result-cell .v.cool{color:var(--celadon-deep)}.result-text{color:var(--ink-soft);font-size:var(--fs-sm);line-height:2.2;letter-spacing:.04em;margin-top:.5rem}.report-view,.report-view p,.report-view li,.report-view h3,.result-text,.result-text p,.result-text li{font-family:system-ui,-apple-system,PingFang SC,Hiragino Sans GB,Microsoft YaHei,sans-serif}.result-text strong{color:var(--ink);font-weight:600}.result-text .tag-hot{color:var(--cinnabar);font-weight:500}.result-text .tag-cool{color:var(--celadon-deep);font-weight:500}.result-text p+p{margin-top:.6rem}.wx-fire{color:var(--cinnabar)}.wx-wood{color:#5e7a4e}.wx-water{color:#3e5f7a}.wx-metal{color:#8c7a4e}.wx-earth{color:#7a6a4a}.disclaimer{margin-top:var(--sp-5);display:flex;gap:1rem;align-items:flex-start;padding:var(--sp-3);background:var(--cinnabar-tint);border:1px solid rgba(165,64,45,.25);border-radius:var(--r-md);font-size:var(--fs-sm);color:#7a3b2c;line-height:2}.disclaimer .seal-mini{flex-shrink:0;font-family:var(--font-kai);color:var(--cinnabar);border:1px solid var(--cinnabar);padding:.3rem .4rem;font-size:.7rem;letter-spacing:.2em;border-radius:2px;margin-top:.15rem}.disclaimer strong{color:var(--cinnabar-deep)}.site-foot{border-top:1px solid var(--line-soft);padding:var(--sp-5) 0 var(--sp-4);margin-top:var(--sp-6)}.foot-inner{display:flex;justify-content:space-between;align-items:center;gap:var(--sp-3);flex-wrap:wrap}.foot-brand{display:flex;align-items:center;gap:.8rem;font-family:var(--font-kai);letter-spacing:.3em;color:var(--ink-soft)}.foot-brand .seal{color:var(--cinnabar);border:1.5px solid var(--cinnabar);padding:.24rem .3rem;font-size:.66rem;border-radius:2px}.foot-meta{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.1em;line-height:2}.pill{display:inline-block;padding:.15rem .7rem;border:1px solid var(--line);border-radius:99px;font-size:var(--fs-xs);color:var(--ink-soft);letter-spacing:.15em;margin:.15rem .3rem .15rem 0;background:#fbf9f380}.pill.hot{color:var(--cinnabar);border-color:#a5402d59}.pill.cool{color:var(--celadon-deep);border-color:#5f766059}.center{text-align:center}.mt-1{margin-top:var(--sp-1)}.mt-2{margin-top:var(--sp-2)}.mt-3{margin-top:var(--sp-3)}.mt-4{margin-top:var(--sp-4)}.mt-5{margin-top:var(--sp-5)}.small{font-size:var(--fs-sm)}.tiny{font-size:var(--fs-xs)}.cinnabar{color:var(--cinnabar)}.celadon{color:var(--celadon-deep)}.muted{color:var(--ink-faint)}.fade-up{animation:fadeUp 1s var(--ease-out) both}@keyframes fadeUp{0%{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}.delay-1{animation-delay:.12s}.delay-2{animation-delay:.24s}.delay-3{animation-delay:.36s}.separator{display:flex;align-items:center;gap:1rem;color:var(--ink-faint);font-size:var(--fs-xs);letter-spacing:.5em;margin:var(--sp-4) 0}.separator:before,.separator:after{content:"";flex:1;height:1px;background:var(--line-soft)}.back-link{display:inline-flex;align-items:center;gap:.5rem;font-size:var(--fs-sm);letter-spacing:.2em;color:var(--ink-faint);transition:color var(--dur-fast);margin-bottom:var(--sp-3)}.back-link:hover{color:var(--cinnabar)}@media(max-width:760px){.hero-vert{display:none}.art-card{min-height:200px}.brand .sub{display:none}.module-title-row{flex-direction:column;align-items:flex-start;gap:.6rem}.nav-links{gap:.6rem}}@media(prefers-reduced-motion:reduce){*{animation-duration:.01ms!important;transition-duration:.01ms!important;scroll-behavior:auto!important}}.coins{display:flex;gap:1rem;justify-content:center;flex-wrap:wrap}.coin{width:74px;height:74px;border-radius:50%;background:radial-gradient(circle at 32% 28%,#d8c9a0,#a8905e 60%,#8a7448);border:3px solid #6E5B38;box-shadow:inset 0 -6px 12px #3c2d1466,inset 0 4px 8px #fffadc80,0 6px 14px #37342c40;display:flex;align-items:center;justify-content:center;font-family:var(--font-kai);font-size:1.2rem;color:#5c4a26;position:relative}.coin:before{content:"〒";position:absolute;font-size:2rem;color:#5c4a26d9}.coin .face{position:relative;z-index:1;text-align:center;line-height:1.15;font-size:.68rem;letter-spacing:.05em}.yao-row{display:flex;align-items:center;gap:1rem;justify-content:center;padding:.55rem 0;border-bottom:1px dotted var(--line-soft)}.yao-label{width:3.2rem;font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.2em;text-align:right;font-variant-numeric:tabular-nums}.yao-glyph{font-size:2rem;line-height:1;letter-spacing:.2em}.yao-move{color:var(--cinnabar);font-size:var(--fs-xs);letter-spacing:.2em}.yao-move .dot{width:.5rem;height:.5rem;border-radius:50%;background:var(--cinnabar);display:inline-block;margin-left:.3rem}.qimen-grid{display:grid;grid-template-columns:repeat(3,1fr);border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;max-width:560px;margin:0 auto;background:var(--paper-deep)}.qm-cell{aspect-ratio:1;border:1px solid var(--line-soft);padding:.7rem;position:relative;background:linear-gradient(160deg,#fbf9f3b3,#ede8d980)}.qm-cell .qm-name{font-family:var(--font-kai);font-size:1.05rem;color:var(--celadon-deep);letter-spacing:.15em}.qm-cell .qm-items{font-size:.72rem;color:var(--ink-soft);line-height:1.7;letter-spacing:.05em;margin-top:.3rem}.qm-cell .qm-star{font-size:.68rem;color:var(--ink-faint)}.ziwei-grid{display:grid;grid-template-columns:repeat(4,1fr);border:1px solid var(--line);border-radius:var(--r-md);overflow:hidden;max-width:640px;margin:0 auto}.zw-cell{border:1px solid var(--line-soft);padding:.55rem;min-height:96px;background:#fbf9f38c}.zw-cell.center{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.2rem;background:var(--paper-deep)}.zw-cell .zw-name{font-family:var(--font-kai);font-size:.8rem;color:var(--celadon-deep);letter-spacing:.1em;display:flex;justify-content:space-between}.zw-cell .zw-stars{font-size:.62rem;color:var(--ink-soft);line-height:1.8;letter-spacing:.04em}.liuren-stage{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:var(--sp-2)}.liuren-item{padding:1rem;background:#fbf9f38c;border:1px solid var(--line-soft);border-radius:var(--r-sm)}.liuren-item .k{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.3em}.liuren-item .v{font-size:1.2rem;margin-top:.3rem;letter-spacing:.2em;font-family:var(--font-kai)}.motes{position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none;overflow:hidden;z-index:0}.mote{position:absolute;font-family:var(--font-zm);color:var(--ink-faint);opacity:.2;letter-spacing:.1em;animation:moteFloat var(--mote-duration,10s) ease-in-out infinite;animation-delay:var(--mote-delay,0s);-webkit-user-select:none;-moz-user-select:none;user-select:none;text-shadow:0 1px 6px rgba(255,255,255,.5)}@keyframes moteFloat{0%,to{transform:translateY(0) translate(0);opacity:.16}50%{transform:translateY(-52px) translate(var(--mote-drift,-12px));opacity:.34}}#mountains{animation:inkDrift 90s ease-in-out infinite alternate}@keyframes inkDrift{0%{transform:translate(0) scale(1)}to{transform:translate(-22px) scale(1.03)}}.mist{position:absolute;top:0;right:0;bottom:0;left:0;pointer-events:none;overflow:hidden;z-index:0}.mist:before,.mist:after{content:"";position:absolute;top:12%;width:60vw;height:34vh;background:radial-gradient(ellipse at center,rgba(150,168,143,.13),transparent 65%);filter:blur(10px);animation:mistDrift 34s ease-in-out infinite alternate}.mist:before{left:-20vw;animation-delay:-8s}.mist:after{right:-22vw;top:26%;width:52vw;animation-delay:-20s}@keyframes mistDrift{0%{transform:translate(0)}to{transform:translate(9vw)}}.hero-glow{position:absolute;width:34vw;height:34vw;border-radius:50%;pointer-events:none;filter:blur(60px);opacity:.5;animation:glowBreath 8s ease-in-out infinite}.hero-glow--left{left:-8vw;top:-6vw;background:radial-gradient(circle,rgba(150,168,143,.22),transparent 70%)}.hero-glow--right{right:-10vw;bottom:-8vw;background:radial-gradient(circle,rgba(124,151,146,.18),transparent 70%);animation-delay:-4s}@keyframes glowBreath{0%,to{opacity:.35;transform:scale(1)}50%{opacity:.6;transform:scale(1.06)}}@media(prefers-reduced-motion:reduce){.mote,#mountains,.mist:before,.mist:after,.hero-glow{animation:none!important}}.tarot-stage{display:flex;justify-content:center;padding:var(--sp-4) 0;position:relative}.tarot-fan{display:flex;justify-content:center;gap:2px;transform:rotate(-4deg)}.tarot-card{width:96px;height:156px;flex-shrink:0;background:linear-gradient(160deg,#f7f2e4,#e6dcbe 70%,#d6c9a4);border:1px solid #C9B98F;border-radius:6px;box-shadow:0 8px 20px -10px #37342c66,inset 0 0 0 4px #c9b98f59;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:transform var(--dur-mid) var(--ease-out);position:relative}.tarot-card:before{content:"✦";font-size:1.4rem;color:#a58d5e}.tarot-card:hover{transform:translateY(-10px) rotate(0)}.tarot-card.picked{transform:translateY(-14px) rotate(0)}.tarot-card .card-inner{position:absolute;top:0;right:0;bottom:0;left:0;padding:.5rem;display:flex;flex-direction:column;align-items:center;justify-content:space-between;background:linear-gradient(170deg,#fdfaf1,#efe6cc);border-radius:5px;opacity:0;transition:opacity var(--dur-mid)}.tarot-card.revealed .card-inner{opacity:1}.card-inner .cn{font-family:var(--font-kai);font-size:1.05rem;color:var(--ink);letter-spacing:.1em}.card-inner .en{font-size:.52rem;color:var(--ink-faint);letter-spacing:.14em}.card-inner .sigil{font-size:1.6rem;color:var(--cinnabar)}.btn-seal:before{z-index:0}.btn-seal>*{position:relative;z-index:1}.btn-seal:hover>*{color:var(--paper)}.btn-seal.btn-ghost:hover>*{color:var(--paper)}#ink-vein{position:fixed;top:0;right:0;bottom:0;left:0;pointer-events:none;z-index:-1;overflow:hidden}#ink-vein .vein-lines{position:absolute;top:0;right:0;bottom:0;left:0;opacity:.05;background-image:repeating-linear-gradient(115deg,transparent 0 38px,rgba(55,52,44,.5) 38px 39px),repeating-linear-gradient(25deg,transparent 0 58px,rgba(55,52,44,.35) 58px 59px);animation:veinFlow 140s linear infinite}@keyframes veinFlow{0%{background-position:0 0,0 0}to{background-position:120px 60px,-80px 40px}}#ink-vein .vein-poem{position:absolute;right:1.6rem;top:50%;transform:translateY(-50%);writing-mode:vertical-rl;font-family:var(--font-kai);font-size:var(--fs-sm);letter-spacing:.9em;color:var(--ink-faint);opacity:.34;-webkit-user-select:none;-moz-user-select:none;user-select:none;animation:poemBreath 9s ease-in-out infinite;white-space:nowrap}@keyframes poemBreath{0%,to{opacity:.22}50%{opacity:.42}}#ink-vein .vein-seal{position:absolute;right:2.2rem;bottom:6.5rem;font-family:var(--font-kai);font-size:.6rem;letter-spacing:.2em;color:var(--cinnabar);opacity:.4;border:1px solid rgba(165,64,45,.5);padding:.3rem .34rem;border-radius:2px;animation:poemBreath 7s ease-in-out infinite;animation-delay:-3s}@media(max-width:860px){#ink-vein .vein-poem,#ink-vein .vein-seal{display:none}}.has-sub{position:relative}.sub-menu{position:absolute;top:calc(100% + .4rem);left:50%;transform:translate(-50%) translateY(-6px);min-width:9.5rem;padding:.5rem 0;background:var(--white-paper);border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--shadow-paper);opacity:0;visibility:hidden;transition:all var(--dur-mid) var(--ease-out);z-index:60}.has-sub:hover .sub-menu,.has-sub:focus-within .sub-menu{opacity:1;visibility:visible;transform:translate(-50%) translateY(0)}.sub-menu a{display:block;padding:.42rem 1.2rem;font-size:var(--fs-sm);letter-spacing:.2em;color:var(--ink-soft);text-align:left;white-space:nowrap}.sub-menu a:hover{color:var(--cinnabar);background:var(--cinnabar-tint)}.song-select{position:relative;width:100%}.song-select .trigger{width:100%;padding:.6rem .8rem;display:flex;align-items:center;justify-content:space-between;gap:.6rem;background:#fbf9f399;border:1px solid var(--line);border-radius:var(--r-sm);font-size:var(--fs-base);color:var(--ink);cursor:pointer;text-align:left;transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}.song-select .trigger:hover{border-color:var(--celadon)}.song-select.open .trigger{border-color:var(--celadon-deep);box-shadow:0 0 0 3px #5f76601f}.song-select .trigger:after{content:"";width:7px;height:7px;flex-shrink:0;border-right:1.5px solid var(--ink-soft);border-bottom:1.5px solid var(--ink-soft);transform:rotate(45deg) translateY(-2px);transition:transform var(--dur-fast)}.song-select.open .trigger:after{transform:rotate(225deg) translateY(1px)}.song-select .panel{position:absolute;top:calc(100% + .35rem);left:0;right:0;max-height:240px;overflow-y:auto;background:var(--white-paper);border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--shadow-paper);z-index:70;padding:.35rem 0;animation:viewIn .25s var(--ease-out) both}.song-select .panel button{display:block;width:100%;padding:.5rem .9rem;text-align:left;font-size:var(--fs-sm);letter-spacing:.1em;color:var(--ink-soft);transition:background var(--dur-fast),color var(--dur-fast)}.song-select .panel button:hover{background:var(--celadon-tint);color:var(--ink)}.song-select .panel button.sel{color:var(--celadon-deep);font-weight:600}.song-select .panel button.sel:before{content:"◆ ";font-size:.62rem}.song-dialog-mask{position:fixed;top:0;right:0;bottom:0;left:0;background:#37342c59;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:200;animation:fadeUp .3s var(--ease-out) both}.song-dialog{width:min(420px,90vw);padding:var(--sp-4);background:linear-gradient(175deg,var(--white-paper),var(--paper-deep));border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-paper);animation:viewIn .35s var(--ease-out) both}.song-dialog .dlg-title{font-family:var(--font-kai);font-size:1.15rem;letter-spacing:.25em;color:var(--ink);display:flex;align-items:center;gap:.6rem;padding-bottom:var(--sp-2);border-bottom:1px solid var(--line-soft);margin-bottom:var(--sp-3)}.song-dialog .dlg-title:before{content:"❖";color:var(--cinnabar);font-size:.9rem}.song-dialog .dlg-body{color:var(--ink-soft);font-size:var(--fs-sm);line-height:2.1;letter-spacing:.05em}.song-dialog .dlg-actions{display:flex;gap:.8rem;justify-content:flex-end;margin-top:var(--sp-4)}.casting-glyph{animation:castSpin 2.4s var(--ease-ink) infinite}@keyframes castSpin{0%{transform:rotate(0) scale(1);opacity:.7}50%{transform:rotate(180deg) scale(1.08);opacity:1}to{transform:rotate(360deg) scale(1);opacity:.7}}.casting-ring{position:relative;width:4.4rem;height:4.4rem;border-radius:50%;margin:0 auto .8rem;border:1px dashed var(--celadon);animation:ringExpand 2.4s var(--ease-ink) infinite}@keyframes ringExpand{0%{transform:scale(.75);opacity:.5}50%{transform:scale(1);opacity:1}to{transform:scale(.75);opacity:.5}}.stagger>*{animation:fadeUp .7s var(--ease-out) both}.stagger>*:nth-child(1){animation-delay:.05s}.stagger>*:nth-child(2){animation-delay:.16s}.stagger>*:nth-child(3){animation-delay:.27s}.stagger>*:nth-child(4){animation-delay:.38s}.stagger>*:nth-child(5){animation-delay:.49s}.bazi-pan{display:grid;grid-template-columns:repeat(4,1fr);gap:var(--sp-2);max-width:640px;margin:0 auto}.bz-pillar{background:#fbf9f38c;border:1px solid var(--line-soft);border-radius:var(--r-sm);padding:.9rem .5rem;text-align:center;position:relative;overflow:hidden;transition:border-color var(--dur-mid),transform var(--dur-mid)}.bz-pillar:hover{border-color:var(--celadon);transform:translateY(-2px)}.bz-pillar .p-name{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.3em;margin-bottom:.5rem}.bz-pillar .p-gan{font-family:var(--font-kai);font-size:2rem;line-height:1.15;display:block}.bz-pillar .p-zhi{font-family:var(--font-kai);font-size:2rem;line-height:1.15;display:block;margin-top:.15rem}.bz-pillar .p-wx{font-size:var(--fs-xs);margin-top:.4rem;letter-spacing:.2em}.bz-pillar .p-shishen{font-size:var(--fs-xs);color:var(--celadon-deep);margin-top:.2rem;letter-spacing:.1em}.bz-pillar .p-nayin{font-size:.62rem;color:var(--ink-faint);margin-top:.3rem;letter-spacing:.08em;border-top:1px dotted var(--line-soft);padding-top:.35rem}.bz-pillar.day-pillar{border-color:var(--cinnabar);background:linear-gradient(175deg,#f3e3dc80,#fbf9f399);box-shadow:0 0 0 1px #a5402d26}.bz-pillar.day-pillar .p-name{color:var(--cinnabar)}.bz-pillar .p-zhu{position:absolute;top:.35rem;right:.5rem;font-size:.6rem;color:var(--ink-faint);letter-spacing:.15em}.zw-cell{transition:background var(--dur-mid),box-shadow var(--dur-mid)}.zw-cell.ming-cell{background:linear-gradient(175deg,#f3e3dc8c,#fbf9f380);box-shadow:inset 0 0 0 1px #a5402d40}.zw-cell .zw-stars .star-name{display:inline-block;padding:.06rem .22rem;border-radius:2px;margin-right:.14rem}.zw-cell .zw-stars .star-name.star-hot{color:var(--cinnabar);background:#a5402d14}.zw-cell .zw-stars .star-name.star-cool{color:var(--celadon-deep);background:#5f766014}.tarot-slot{display:flex;flex-direction:column;align-items:center;gap:.5rem}.tarot-slot .slot-label{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.25em;text-align:center;max-width:110px;line-height:1.6}.tarot-fan .tarot-card.face-down:after{content:"";position:absolute;top:6px;right:6px;bottom:6px;left:6px;border:1px solid rgba(165,92,62,.35);border-radius:3px}@media(prefers-reduced-motion:reduce){#ink-vein .vein-lines,#ink-vein .vein-poem,.casting-glyph,.casting-ring{animation:none!important}}.btn-seal{isolation:isolate}.btn-seal:before{z-index:-1;transform-origin:top}.btn-seal:hover{color:var(--paper)}.btn-seal .sb-label{position:relative;z-index:1}.btn-seal:hover .sb-label{color:var(--paper)}.arts-sub{padding:.6rem .4rem .8rem}.sub-group{margin-bottom:.3rem}.sub-group-name{font-size:.62rem;letter-spacing:.35em;color:var(--ink-faint);padding:.35rem .9rem .2rem;border-bottom:1px dotted var(--line-soft);margin-bottom:.2rem}.sub-group a{display:flex;align-items:baseline;gap:.55rem;font-size:var(--fs-sm);letter-spacing:.18em;color:var(--ink-soft);padding:.4rem .9rem;border-radius:var(--r-sm)}.sub-group a:hover{color:var(--cinnabar);background:var(--cinnabar-tint)}.sub-group .sub-glyph{font-family:var(--font-zm);color:var(--celadon-deep);font-size:.95rem}.sub-group .sub-poem{margin-left:auto;font-size:.6rem;color:var(--ink-faint);letter-spacing:.1em;white-space:nowrap}.user-menu .user-chip{display:flex;align-items:center;gap:.45rem;padding:.28rem .7rem .28rem .32rem;border:1px solid var(--line);border-radius:99px;background:#fbf9f3b3;cursor:pointer;transition:border-color var(--dur-fast),box-shadow var(--dur-fast)}.user-menu .user-chip:hover{border-color:var(--celadon);box-shadow:0 0 0 3px #5f76601a}.user-menu .user-seal{width:1.7rem;height:1.7rem;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--celadon-deep);color:var(--paper);font-family:var(--font-kai);font-size:.85rem;flex-shrink:0}.user-menu .user-name{font-size:var(--fs-sm);letter-spacing:.12em;color:var(--ink);max-width:5rem;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.user-menu .user-caret{font-size:.62rem;color:var(--ink-faint)}.user-menu .sub-menu a{padding:.5rem 1.2rem}.side-anchor{position:fixed;left:1.4rem;top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:.9rem;z-index:30}.side-anchor button{display:flex;align-items:center;gap:.5rem;font-size:var(--fs-xs);letter-spacing:.25em;color:var(--ink-faint);writing-mode:vertical-rl;transition:color var(--dur-fast);cursor:pointer;padding:.15rem 0}.side-anchor .dot{width:5px;height:5px;border-radius:50%;background:currentColor;opacity:.5;flex-shrink:0}.side-anchor button:hover{color:var(--ink)}.side-anchor button.active{color:var(--cinnabar)}.side-anchor button.active .dot{opacity:1;box-shadow:0 0 0 3px #a5402d26}@media(max-width:1100px){.side-anchor{display:none}}.song-search .panel{width:100%}.song-search .ss-search{padding:.5rem .7rem .35rem}.song-search .ss-input{width:100%;padding:.42rem .6rem;background:#fbf9f3cc;border:1px solid var(--line);border-radius:var(--r-sm);font-size:var(--fs-sm)}.song-search .ss-input:focus{outline:none;border-color:var(--celadon-deep)}.song-search .ss-list{max-height:220px;overflow-y:auto;padding:.2rem 0}.song-search .ss-empty{padding:.8rem;text-align:center;color:var(--ink-faint);font-size:var(--fs-xs);letter-spacing:.2em}.song-select .trigger:after{display:none}.song-select .trigger .chev{flex-shrink:0;color:var(--ink-faint);transition:transform var(--dur-fast)}.song-select.open .trigger .chev{transform:rotate(180deg);color:var(--celadon-deep)}.song-select .trigger{line-height:1.6}.song-select .panel{left:0;right:0}.user-menu .sub-menu{right:0;left:auto;transform:none;min-width:8.5rem}.user-menu:hover .sub-menu{transform:none}.cal-input{position:relative}.cal-field{display:flex;gap:.4rem;align-items:center}.cal-field .input-line{flex:1}.cal-field input.cal-error{border-color:var(--cinnabar)}.cal-toggle{display:flex;align-items:center;justify-content:center;width:2.3rem;height:2.3rem;flex-shrink:0;border:1px solid var(--line);border-radius:var(--r-sm);background:#fbf9f399;color:var(--ink-soft);cursor:pointer;transition:border-color var(--dur-fast),color var(--dur-fast)}.cal-toggle:hover{border-color:var(--celadon);color:var(--celadon-deep)}.cal-panel{position:absolute;top:calc(100% + .35rem);left:0;z-index:70;width:min(290px,90vw);padding:.7rem;background:var(--white-paper);border:1px solid var(--line);border-radius:var(--r-md);box-shadow:var(--shadow-paper);animation:viewIn .25s var(--ease-out) both}.cal-head{display:flex;align-items:center;justify-content:space-between;margin-bottom:.5rem}.cal-title{font-family:var(--font-kai);font-size:.95rem;letter-spacing:.15em;color:var(--ink)}.cal-nav{display:flex;padding:.25rem;color:var(--ink-faint);cursor:pointer;border-radius:var(--r-sm)}.cal-nav:hover{color:var(--cinnabar);background:var(--cinnabar-tint)}.cal-week{display:grid;grid-template-columns:repeat(7,1fr);text-align:center;font-size:.62rem;color:var(--ink-faint);letter-spacing:.1em;margin-bottom:.2rem}.cal-grid{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}.cal-grid button{aspect-ratio:1;display:flex;align-items:center;justify-content:center;font-size:var(--fs-sm);color:var(--ink-soft);border-radius:var(--r-sm);cursor:pointer;transition:background var(--dur-fast),color var(--dur-fast)}.cal-grid button:hover{background:var(--celadon-tint);color:var(--ink)}.cal-grid button.sel{background:var(--celadon-deep);color:var(--paper);font-weight:600}.song-search .ss-custom{width:100%;padding:.6rem .9rem;text-align:center;font-size:var(--fs-sm);color:var(--celadon-deep);letter-spacing:.1em;cursor:pointer}.song-search .ss-custom:hover{background:var(--celadon-tint)}.song-select .trigger .chev,.song-search .trigger .chev{width:16px;height:16px;stroke-width:2.4}.nav-links .has-sub>a .chev-mini{width:14px;height:14px;stroke-width:2.2;vertical-align:-2px}.user-menu .user-caret{display:inline-flex;width:14px;height:14px}.user-menu .user-caret svg{width:14px;height:14px;stroke-width:2.2}.song-select .panel button,.song-select .trigger span{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.song-select{min-width:0}.song-select .trigger{min-height:2.55rem}.song-select .panel{min-width:100%;width:-moz-max-content;width:max-content;max-width:16rem}.song-select .panel button{font-size:var(--fs-sm)}.altar-panel{position:sticky;top:84px;max-height:calc(100vh - 108px);overflow-y:auto;scrollbar-width:thin}.altar-panel::-webkit-scrollbar{width:4px}.altar-panel::-webkit-scrollbar-thumb{background:var(--celadon);border-radius:2px}@media(max-width:960px){.altar-panel{position:static;max-height:none;overflow:visible}}.altar-result{padding-bottom:var(--sp-2)}.bamboo-art{position:absolute;right:1rem;top:50%;transform:translateY(-50%);width:min(300px,30vw);pointer-events:none;opacity:.85}.bamboo-art svg{width:100%;height:auto;filter:drop-shadow(0 6px 18px rgba(95,118,96,.12))}.page-decor{position:relative}.page-decor .wrap{position:relative;z-index:1}@media(max-width:900px){.bamboo-art{display:none}}.nav-links a{white-space:nowrap}.nav-links .has-sub>a{display:inline-flex;align-items:center;gap:.28rem}.nav-links .has-sub>a .chev-mini{flex-shrink:0}.tarot-fan{transform:none;align-items:flex-start;gap:.9rem}.tarot-slot{width:104px}.tarot-slot .tarot-card{margin:0 auto}.tarot-slot .slot-label{min-height:2.6em;display:flex;align-items:flex-start;justify-content:center}.tarot-stage .tarot-fan.wrap-fan{max-width:640px}.center-actions{display:flex;gap:.8rem;justify-content:center;align-items:center;flex-wrap:wrap}.today-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-4);align-items:stretch}.today-grid .result-card{display:flex;flex-direction:column;margin-bottom:0;min-height:220px}.today-grid .result-card .result-text{flex:1}@media(max-width:860px){.today-grid{grid-template-columns:1fr}}.scroll-hint{transition:opacity .6s var(--ease-out),visibility .6s}.zw-palace-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:var(--sp-2);margin-top:var(--sp-3)}.zw-palace-cell{padding:.7rem .8rem;background:#fbf9f38c;border:1px solid var(--line-soft);border-radius:var(--r-sm);transition:border-color var(--dur-mid),background var(--dur-mid)}.zw-palace-cell:hover{border-color:var(--celadon)}.zw-palace-cell.is-ming{background:linear-gradient(175deg,#f3e3dc8c,#fbf9f380);border-color:#a5402d59}.zw-palace-cell.empty{opacity:.55}.zp-head{display:flex;align-items:baseline;gap:.4rem;border-bottom:1px dotted var(--line-soft);padding-bottom:.3rem;margin-bottom:.35rem}.zp-name{font-family:var(--font-kai);font-size:.92rem;color:var(--celadon-deep);letter-spacing:.12em}.zp-zhi{font-size:.66rem;color:var(--ink-faint)}.zp-ming{margin-left:auto;font-size:.6rem;color:var(--cinnabar);border:1px solid rgba(165,64,45,.4);padding:0 .3rem;border-radius:2px}.zp-stars{font-size:.72rem;color:var(--ink-soft);line-height:1.7;letter-spacing:.06em;min-height:1.7em}.zp-theme{font-size:.62rem;color:var(--ink-faint);letter-spacing:.15em;margin-top:.2rem}.hero{padding-bottom:calc(var(--sp-6) + 2.2rem)}.scroll-hint{position:absolute;bottom:1.1rem;left:50%;transform:translate(-50%);display:flex;align-items:center;gap:.55rem;font-size:var(--fs-xs);letter-spacing:.35em;color:var(--ink-faint);writing-mode:horizontal-tb;animation:floatHintX 3s var(--ease-ink) infinite;z-index:2}.scroll-hint svg{width:14px;height:14px;animation:hintBounce 1.6s ease-in-out infinite}@keyframes floatHintX{0%,to{transform:translate(-50%) translateY(0)}50%{transform:translate(-50%) translateY(7px)}}@keyframes hintBounce{0%,to{transform:translateY(0)}50%{transform:translateY(3px)}}.scroll-hint.hidden{opacity:0;visibility:hidden}.btn-row{display:flex;gap:.8rem;align-items:center;flex-wrap:wrap}.btn-row .btn-seal,.btn-row a.btn-seal{display:inline-flex;align-items:center;justify-content:center}.dlg-actions,.center-actions{align-items:center}.report-list{margin:0;padding-left:1.2rem;color:var(--ink-soft);font-size:var(--fs-sm);line-height:2.1;letter-spacing:.04em}.report-list li::marker{color:var(--celadon-deep)}.trait-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--sp-2)}.trait-cell{padding:.9rem;background:#fbf9f38c;border:1px solid var(--line-soft);border-radius:var(--r-sm);border-top:2px solid var(--celadon)}.trait-name{font-family:var(--font-kai);font-size:.98rem;color:var(--celadon-deep);letter-spacing:.15em;margin-bottom:.3rem}.trait-desc{font-size:var(--fs-sm);color:var(--ink-soft);line-height:1.9}.timeline{position:relative;padding-left:1.4rem}.timeline:before{content:"";position:absolute;left:.32rem;top:.3rem;bottom:.3rem;width:1px;background:var(--line)}.tl-item{position:relative;padding-bottom:var(--sp-3)}.tl-marker{position:absolute;left:-1.4rem;top:.25rem;width:.66rem;height:.66rem;border-radius:50%;background:var(--paper);border:2px solid var(--celadon-deep);display:flex;align-items:center;justify-content:center}.tl-item:first-child .tl-marker{background:var(--celadon-deep)}.tl-head{display:flex;align-items:baseline;gap:.6rem}.tl-head strong{font-family:var(--font-kai);font-size:.95rem;color:var(--ink);letter-spacing:.12em}.tl-age{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.15em}.tl-summary{font-size:var(--fs-sm);color:var(--ink-soft);line-height:2;margin-top:.2rem}.domain-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:var(--sp-2)}.domain-cell{padding:.9rem;background:#fbf9f38c;border:1px solid var(--line-soft);border-radius:var(--r-sm)}.domain-name{font-family:var(--font-kai);font-size:.9rem;color:var(--cinnabar);letter-spacing:.25em;margin-bottom:.4rem;border-bottom:1px dotted var(--line-soft);padding-bottom:.3rem}.domain-cell p{font-size:var(--fs-sm);color:var(--ink-soft);line-height:1.95;margin-top:.2rem}.fit-note{padding:var(--sp-2) var(--sp-3);margin-bottom:var(--sp-3);background:var(--rice-tint);border:1px solid rgba(217,205,172,.6);border-left:3px solid var(--rice);border-radius:var(--r-md);font-size:var(--fs-sm);color:var(--ink-soft);line-height:2}.fit-note strong{color:var(--ink);letter-spacing:.15em}.fit-note .fit-suggest{color:var(--cinnabar);margin-top:.3rem}.fit-card.fit-partial{border-left-color:var(--rice)}.fit-card.fit-bad{border-left-color:var(--cinnabar)}.fit-card.fit-ok{border-left-color:var(--celadon)}.scroll-hint.inline-hint{position:static;transform:none;margin-top:2.2rem;writing-mode:horizontal-tb;animation:none}.scroll-hint.inline-hint svg{width:15px;height:15px;animation:hintBounce 1.6s ease-in-out infinite}.hero{padding-bottom:var(--sp-6)}.ai-streaming{border:1px dashed var(--celadon);border-radius:var(--r-md);padding:var(--sp-3);background:#e3e8dc40}.ai-stream-head{display:flex;align-items:center;gap:.6rem}.ai-spinner{width:15px;height:15px;border-radius:50%;flex-shrink:0;border:2px solid var(--celadon);border-top-color:transparent;animation:castSpin 1s linear infinite}.ai-stream-body{margin-top:.7rem;max-height:260px;overflow-y:auto;padding:.6rem .8rem;background:#fbf9f3b3;border-radius:var(--r-sm);scrollbar-width:thin}.ai-error{border:1px solid rgba(165,64,45,.3);border-radius:var(--r-md);padding:var(--sp-3);background:#f3e3dc4d}.shichen-chip{display:inline-flex;align-items:center;justify-content:center;padding:.4rem .9rem;flex-shrink:0;white-space:nowrap;background:var(--celadon-tint);border:1px solid var(--celadon);border-radius:var(--r-sm);font-size:var(--fs-xs);color:var(--celadon-deep);letter-spacing:.15em}.profile-card{display:flex;align-items:center;gap:1rem;cursor:pointer;padding:1rem 1.2rem;background:linear-gradient(175deg,var(--white-paper),var(--paper-deep));border:1px solid var(--line);border-radius:var(--r-md);transition:border-color var(--dur-mid),box-shadow var(--dur-mid),transform var(--dur-mid)}.profile-card:hover{border-color:var(--celadon);box-shadow:var(--shadow-paper);transform:translateY(-2px)}.profile-card.primary{border-left:3px solid var(--cinnabar)}.pc-badge{flex-shrink:0;width:2rem;height:2rem;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--cinnabar);color:var(--paper);font-family:var(--font-kai);font-size:.85rem}.pc-badge.sample{background:var(--celadon-deep)}.pc-body{flex:1;min-width:0}.pc-title{font-family:var(--font-kai);font-size:.98rem;color:var(--ink);letter-spacing:.12em;margin-bottom:.2rem}.pc-info{font-size:var(--fs-xs);color:var(--ink-faint);letter-spacing:.06em;line-height:1.8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.pc-action{flex-shrink:0;font-size:var(--fs-xs);color:var(--celadon-deep);letter-spacing:.2em}.module-hero.compact{padding:var(--sp-3) 0 var(--sp-2)}.mh-row{display:flex;align-items:baseline;gap:1.2rem;flex-wrap:wrap}.mh-row .back-link{margin-bottom:0;flex-shrink:0}.mh-row .module-title{margin-top:0;font-size:var(--fs-xl);display:flex;align-items:baseline;gap:.9rem;letter-spacing:.14em}.mh-row .module-title .cn{font-size:clamp(1.5rem,3vw,2.2rem);display:inline}.mh-row .mh-sub{font-family:var(--font-kai);font-size:var(--fs-sm);color:var(--ink-faint);letter-spacing:.25em;white-space:nowrap}.mh-row .mh-kicker{margin-left:auto;font-size:var(--fs-xs);letter-spacing:.3em;color:var(--ink-faint);white-space:nowrap}@media(max-width:640px){.mh-row .mh-kicker{white-space:normal;letter-spacing:.15em;line-height:1.6;max-width:16em;text-align:right}}.mh-intro{margin-top:.4rem;max-width:72ch}.mh-intro summary{cursor:pointer;font-size:var(--fs-xs);letter-spacing:.25em;color:var(--celadon-deep);list-style:none;display:inline-flex;align-items:center;gap:.4rem;-webkit-user-select:none;-moz-user-select:none;user-select:none}.mh-intro summary:before{content:"▸";font-size:.7rem;transition:transform var(--dur-fast)}.mh-intro[open] summary:before{transform:rotate(90deg)}.mh-intro p{font-size:var(--fs-sm);color:var(--ink-soft);line-height:2;margin-top:.5rem}
@@ -0,0 +1,12 @@
1
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- 墨绿圆底 -->
3
+ <circle cx="16" cy="16" r="16" fill="#2C3A33"/>
4
+ <!-- 纸色外圆 -->
5
+ <circle cx="16" cy="16" r="10" fill="#F5EFE0"/>
6
+ <!-- 太极黑鱼(S 形分割) -->
7
+ <path d="M16 6 A10 10 0 0 1 16 26 A5 5 0 0 1 16 16 A5 5 0 0 0 16 6 Z" fill="#2C3A33"/>
8
+ <!-- 黑鱼之眼(纸色) -->
9
+ <circle cx="16" cy="21" r="1.7" fill="#F5EFE0"/>
10
+ <!-- 白鱼之眼(墨色) -->
11
+ <circle cx="16" cy="11" r="1.7" fill="#2C3A33"/>
12
+ </svg>
@@ -0,0 +1,15 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>观微 · 玄学问道 —— 九术参详,观微知著</title>
8
+ <meta name="description" content="以纸墨留白为骨,融四柱八字、紫微斗数、奇门遁甲、梅花易数、六爻、大六壬、小六壬、星盘、塔罗九术于一体。观微知著,以术问道。" />
9
+ <script type="module" crossorigin src="/assets/index-CsOD5rSo.js"></script>
10
+ <link rel="stylesheet" crossorigin href="/assets/index-DQx62TUr.css">
11
+ </head>
12
+ <body>
13
+ <div id="root"></div>
14
+ </body>
15
+ </html>
@@ -0,0 +1,28 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ export default tseslint.config(
8
+ { ignores: ['dist'] },
9
+ {
10
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
11
+ files: ['**/*.{ts,tsx}'],
12
+ languageOptions: {
13
+ ecmaVersion: 2020,
14
+ globals: globals.browser,
15
+ },
16
+ plugins: {
17
+ 'react-hooks': reactHooks,
18
+ 'react-refresh': reactRefresh,
19
+ },
20
+ rules: {
21
+ ...reactHooks.configs.recommended.rules,
22
+ 'react-refresh/only-export-components': [
23
+ 'warn',
24
+ { allowConstantExport: true },
25
+ ],
26
+ },
27
+ },
28
+ )
package/index.html ADDED
@@ -0,0 +1,25 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>观微 · 玄学问道 —— 九术参详,观微知著</title>
8
+ <meta name="description" content="以纸墨留白为骨,融四柱八字、紫微斗数、奇门遁甲、梅花易数、六爻、大六壬、小六壬、星盘、塔罗九术于一体。观微知著,以术问道。" />
9
+ <script type="module">
10
+ if (import.meta.hot?.on) {
11
+ import.meta.hot.on('vite:error', (error) => {
12
+ if (error.err) {
13
+ console.error(
14
+ [error.err.message, error.err.frame].filter(Boolean).join('\n'),
15
+ )
16
+ }
17
+ })
18
+ }
19
+ </script>
20
+ </head>
21
+ <body>
22
+ <div id="root"></div>
23
+ <script type="module" src="/src/main.tsx"></script>
24
+ </body>
25
+ </html>
package/package.json ADDED
@@ -0,0 +1,96 @@
1
+ {
2
+ "name": "guanwei",
3
+ "private": false,
4
+ "version": "1.1.1",
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "test": "vitest run",
9
+ "build": "tsc -b && vite build",
10
+ "lint": "eslint .",
11
+ "preview": "vite preview",
12
+ "check": "tsc -b --noEmit",
13
+ "cli": "./scripts/guanwei",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "dependencies": {
17
+ "express": "^4.18.2",
18
+ "cors": "^2.8.5",
19
+ "tsx": "^4.7.0",
20
+ "astronomy-engine": "^2.1.19",
21
+ "lunar-typescript": "^1.8.6",
22
+ "iztro": "2.6.0",
23
+ "vite": "^6.3.5",
24
+ "@vitejs/plugin-react": "^4.4.1",
25
+ "vite-tsconfig-paths": "^5.1.4"
26
+ },
27
+ "devDependencies": {
28
+ "@eslint/js": "^9.25.0",
29
+ "@testing-library/dom": "^10.4.1",
30
+ "@testing-library/react": "^16.3.2",
31
+ "@types/express": "^4.17.21",
32
+ "@types/node": "^22.15.30",
33
+ "@types/react": "^18.3.12",
34
+ "@types/react-dom": "^18.3.1",
35
+ "@vitejs/plugin-react": "^4.4.1",
36
+ "autoprefixer": "^10.4.21",
37
+ "clsx": "^2.1.1",
38
+ "eslint": "^9.25.0",
39
+ "eslint-plugin-react-hooks": "^5.2.0",
40
+ "eslint-plugin-react-refresh": "^0.4.19",
41
+ "globals": "^16.0.0",
42
+ "jsdom": "^30.0.1",
43
+ "lucide-react": "^0.511.0",
44
+ "playwright": "^1.62.1",
45
+ "postcss": "^8.5.3",
46
+ "react": "^18.3.1",
47
+ "react-dom": "^18.3.1",
48
+ "react-router-dom": "^7.3.0",
49
+ "tailwind-merge": "^3.0.2",
50
+ "tailwindcss": "^3.4.17",
51
+ "typescript": "~5.8.3",
52
+ "typescript-eslint": "^8.30.1",
53
+ "vite-tsconfig-paths": "^5.1.4",
54
+ "vitest": "^4.1.10",
55
+ "zustand": "^5.0.3"
56
+ },
57
+ "description": "观微 Guanwei · 九术排盘与 AI 深度解读(八字/紫微/星盘/奇门/六爻/六壬/梅花/小六壬/塔罗)",
58
+ "bin": {
59
+ "guanwei": "scripts/guanwei"
60
+ },
61
+ "files": [
62
+ "dist",
63
+ "src",
64
+ "server/src",
65
+ "shared",
66
+ "scripts",
67
+ "public",
68
+ "index.html",
69
+ "vite.config.ts",
70
+ "tsconfig.json",
71
+ "tailwind.config.js",
72
+ "postcss.config.js",
73
+ "eslint.config.js",
74
+ "README.md"
75
+ ],
76
+ "keywords": [
77
+ "chinese-astrology",
78
+ "bazi",
79
+ "ziwei",
80
+ "qimen",
81
+ "tarot",
82
+ "divination",
83
+ "metaphysics",
84
+ "llm",
85
+ "ai",
86
+ "typescript"
87
+ ],
88
+ "license": "MIT",
89
+ "repository": {
90
+ "type": "git",
91
+ "url": "git+https://github.com/RubyCcll/guanwei.git"
92
+ },
93
+ "engines": {
94
+ "node": ">=18"
95
+ }
96
+ }
@@ -0,0 +1,10 @@
1
+ /** WARNING: DON'T EDIT THIS FILE */
2
+ /** WARNING: DON'T EDIT THIS FILE */
3
+ /** WARNING: DON'T EDIT THIS FILE */
4
+
5
+ export default {
6
+ plugins: {
7
+ tailwindcss: {},
8
+ autoprefixer: {},
9
+ },
10
+ };
@@ -0,0 +1,12 @@
1
+ <svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- 墨绿圆底 -->
3
+ <circle cx="16" cy="16" r="16" fill="#2C3A33"/>
4
+ <!-- 纸色外圆 -->
5
+ <circle cx="16" cy="16" r="10" fill="#F5EFE0"/>
6
+ <!-- 太极黑鱼(S 形分割) -->
7
+ <path d="M16 6 A10 10 0 0 1 16 26 A5 5 0 0 1 16 16 A5 5 0 0 0 16 6 Z" fill="#2C3A33"/>
8
+ <!-- 黑鱼之眼(纸色) -->
9
+ <circle cx="16" cy="21" r="1.7" fill="#F5EFE0"/>
10
+ <!-- 白鱼之眼(墨色) -->
11
+ <circle cx="16" cy="11" r="1.7" fill="#2C3A33"/>
12
+ </svg>
@@ -0,0 +1,245 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================
3
+ # 观微 Guanwei CLI(支持:npm 全局安装 / npm link / 本地源码)
4
+ #
5
+ # 用法:
6
+ # guanwei setup [--key xxx] [--provider xxx] 配置 API Key(存 ~/.guanwei/.env,更新不丢)
7
+ # guanwei start [--dev] [--port 5173] 启动并自动打开浏览器
8
+ # guanwei update 更新到最新版(全局:npm 升级;本地:git 增量合并)
9
+ # guanwei check / status 版本检查 / 状态
10
+ # guanwei doctor 环境自检
11
+ # guanwei stop 停止服务
12
+ # guanwei web 仅打开浏览器
13
+ # guanwei --help 帮助
14
+ # ============================================================
15
+ set -uo pipefail
16
+
17
+ # ── 定位项目根(支持 npm link / npm 全局安装的符号链接)──
18
+ SCRIPT_PATH=$(node -e "process.stdout.write(require('fs').realpathSync(process.argv[1]))" "$0" 2>/dev/null || echo "$0")
19
+ ROOT=$(cd "$(dirname "${SCRIPT_PATH}")/.." && pwd)
20
+ GUANWEI_HOME="${GUANWEI_HOME:-${HOME}/.guanwei}"
21
+ ENV_FILE="${GUANWEI_HOME}/.env"
22
+ VERSION=$(node -e "console.log(require('${ROOT}/package.json').version)" 2>/dev/null || echo "?")
23
+ HEAD=$(git -C "${ROOT}" rev-parse --short HEAD 2>/dev/null || echo "pkg")
24
+ if [[ -d "${ROOT}/.git" ]]; then MODE="local"; else MODE="global"; fi
25
+
26
+ remote_head() { git ls-remote origin -h refs/heads/main 2>/dev/null | cut -f1; }
27
+ latest_tag() { git ls-remote --tags origin 'refs/tags/v*' 2>/dev/null | sed 's|.*refs/tags/||' | sort -V | tail -1; }
28
+
29
+ cmd_help() {
30
+ sed -n '3,19p' "$0" | sed 's/^# \{0,1\}//'
31
+ }
32
+
33
+ cmd_check() {
34
+ if [[ "${MODE}" == "global" ]]; then
35
+ local latest
36
+ latest=$(npm view guanwei version 2>/dev/null || echo "?")
37
+ if [[ "${latest}" == "${VERSION}" || "${latest}" == "?" ]]; then
38
+ echo "✅ 已是最新版本(${VERSION})"
39
+ else
40
+ echo "📦 有新版本:本地 ${VERSION} → npm ${latest};运行:guanwei update"
41
+ fi
42
+ else
43
+ local remote remoteTag
44
+ remote=$(remote_head)
45
+ remoteTag=$(latest_tag)
46
+ if [[ -z "${remote}" ]]; then echo "⚠️ 无法连接 GitHub"; return 0; fi
47
+ if [[ "${HEAD}" == "${remote:0:7}" ]]; then
48
+ echo "✅ 已是最新版本(${VERSION} · ${HEAD})"
49
+ else
50
+ echo "📦 有新版本:本地 ${VERSION} · ${HEAD} → 远端 ${remoteTag} · ${remote:0:7};运行:guanwei update"
51
+ fi
52
+ fi
53
+ }
54
+
55
+ cmd_status() {
56
+ echo "观微 Guanwei"
57
+ echo " 版本 : ${VERSION}(${MODE} 模式)"
58
+ [[ "${MODE}" == "local" ]] && echo " 提交 : ${HEAD}"
59
+ echo " 项目目录 : ${ROOT}"
60
+ echo " 配置 : $([ -f "${ENV_FILE}" ] && echo "已配置(${ENV_FILE})" || echo "未配置(运行 guanwei setup)")"
61
+ local be_pid fe_pid
62
+ be_pid=$(lsof -nP -iTCP:3018 -sTCP:LISTEN -t 2>/dev/null | head -1)
63
+ fe_pid=$(lsof -nP -iTCP:${WEB_PORT:-5173} -sTCP:LISTEN -t 2>/dev/null | head -1)
64
+ echo " 服务状态 : 后端 $([ -n "${be_pid}" ] && echo '运行中' || echo '未运行') · 前端 $([ -n "${fe_pid}" ] && echo '运行中' || echo '未运行')"
65
+ cmd_check
66
+ }
67
+
68
+ cmd_setup() {
69
+ local PROVIDER="" KEY="" CHOICE
70
+ while [[ $# -gt 0 ]]; do
71
+ case "$1" in
72
+ --provider) PROVIDER="$2"; shift 2 ;;
73
+ --key) KEY="$2"; shift 2 ;;
74
+ *) echo "未知参数: $1"; exit 1 ;;
75
+ esac
76
+ done
77
+ mkdir -p "${GUANWEI_HOME}"
78
+ if [[ -z "${PROVIDER}" ]]; then
79
+ if [[ -n "${KEY}" ]]; then PROVIDER=deepseek; else
80
+ echo "选择 AI 服务商:"
81
+ echo " 1) DeepSeek(推荐) 2) Gemini 3) Groq 4) 通义千问 5) 自定义端点"
82
+ read -rp "编号 [1]: " CHOICE
83
+ case "${CHOICE:-1}" in
84
+ 1) PROVIDER=deepseek;; 2) PROVIDER=gemini;; 3) PROVIDER=groq;;
85
+ 4) PROVIDER=qwen;; 5) PROVIDER=custom;; *) echo "无效"; exit 1;;
86
+ esac
87
+ fi
88
+ fi
89
+ if [[ -z "${KEY}" ]]; then
90
+ read -rsp "请输入 ${PROVIDER} API Key(不回显): " KEY; echo
91
+ fi
92
+ [[ -z "${KEY}" ]] && { echo "错误:Key 不能为空"; exit 1; }
93
+ {
94
+ echo "# 由 guanwei setup 生成($(date '+%Y-%m-%d %H:%M'))"
95
+ echo "LLM_PROVIDER=${PROVIDER}"
96
+ case "${PROVIDER}" in
97
+ deepseek) echo "LLM_DEEPSEEK_KEY=${KEY}"; echo "LLM_DEEPSEEK_MODEL=deepseek-chat";;
98
+ gemini) echo "LLM_GEMINI_KEY=${KEY}";;
99
+ groq) echo "LLM_GROQ_KEY=${KEY}";;
100
+ qwen) echo "LLM_QWEN_KEY=${KEY}";;
101
+ custom)
102
+ echo "LLM_CUSTOM_KEY=${KEY}"
103
+ read -rp "端点 URL [https://your-endpoint/v1/chat/completions]: " EP
104
+ echo "LLM_CUSTOM_ENDPOINT=${EP:-https://your-endpoint/v1/chat/completions}";;
105
+ esac
106
+ } > "${ENV_FILE}"
107
+ chmod 600 "${ENV_FILE}"
108
+ echo "✅ 配置已保存:${ENV_FILE}(仅本机可读)"
109
+ }
110
+
111
+ cmd_start() {
112
+ local DEV="" web_port="${WEB_PORT:-5173}" api_port="${API_PORT:-3018}"
113
+ while [[ $# -gt 0 ]]; do
114
+ case "$1" in
115
+ --dev) DEV="1"; shift ;;
116
+ --port) web_port="$2"; shift 2 ;;
117
+ --api) api_port="$2"; shift 2 ;;
118
+ *) echo "未知参数: $1"; exit 1 ;;
119
+ esac
120
+ done
121
+ if [[ ! -f "${ENV_FILE}" ]]; then
122
+ echo "⚠️ 尚未配置,先运行:guanwei setup --key 你的APIKey"; exit 1
123
+ fi
124
+ set -a; source "${ENV_FILE}"; set +a
125
+ export PORT="${api_port}" # 后端实际监听端口
126
+
127
+ local mode_label="生产"
128
+ [[ -n "${DEV}" ]] && mode_label="开发"
129
+ echo "🚀 启动观微(${mode_label}模式)"
130
+ echo " 前端 : http://localhost:${web_port} 后端 : http://localhost:${api_port}"
131
+
132
+ if [[ -n "${DEV}" ]]; then
133
+ (cd "${ROOT}/server" && "${ROOT}/node_modules/.bin/tsx" watch src/index.ts >/dev/null 2>&1) &
134
+ (cd "${ROOT}" && "${ROOT}/node_modules/.bin/vite" --port "${web_port}" >/dev/null 2>&1) &
135
+ else
136
+ (cd "${ROOT}/server" && "${ROOT}/node_modules/.bin/tsx" src/index.ts >/dev/null 2>&1) &
137
+ (cd "${ROOT}" && "${ROOT}/node_modules/.bin/vite" preview --port "${web_port}" --strictPort --host 127.0.0.1 >/dev/null 2>&1) &
138
+ fi
139
+
140
+ for i in $(seq 1 30); do
141
+ curl -sf "http://127.0.0.1:${api_port}/api/health" >/dev/null 2>&1 && break
142
+ sleep 1
143
+ done
144
+ sleep 2
145
+ if curl -sf -o /dev/null "http://127.0.0.1:${web_port}/" 2>/dev/null; then
146
+ echo "✅ 已就绪,正在打开浏览器..."
147
+ cmd_web "${web_port}" || true
148
+ else
149
+ echo "⚠️ 前端暂未就绪,稍后访问 http://localhost:${web_port}"
150
+ fi
151
+ echo " Ctrl+C 停止"
152
+ wait
153
+ }
154
+
155
+ cmd_web() {
156
+ local port="${1:-${WEB_PORT:-5173}}"
157
+ local url="http://localhost:${port}"
158
+ case "$(uname -s)" in
159
+ Darwin) open "${url}" ;;
160
+ Linux) xdg-open "${url}" 2>/dev/null || echo "请打开浏览器访问: ${url}" ;;
161
+ MINGW*|MSYS*|CYGWIN*) start "${url}" ;;
162
+ *) echo "请打开浏览器访问: ${url}" ;;
163
+ esac
164
+ }
165
+
166
+ cmd_update() {
167
+ if [[ "${MODE}" == "global" ]]; then
168
+ echo "📦 全局安装模式——通过 npm 升级..."
169
+ npm i -g guanwei@latest 2>&1 | tail -2
170
+ echo "✅ 升级完成,重启生效:guanwei start"
171
+ else
172
+ echo "🔍 检查远端更新..."
173
+ git -C "${ROOT}" fetch origin 2>&1 | tail -1
174
+ local remote; remote=$(remote_head)
175
+ if [[ -z "${remote}" ]]; then echo "⚠️ 无法连接 GitHub"; exit 1; fi
176
+ if [[ "${HEAD}" == "${remote:0:7}" ]]; then echo "✅ 已是最新"; return 0; fi
177
+ local dirty
178
+ dirty=$(git -C "${ROOT}" status --porcelain --untracked-files=no 2>/dev/null | grep -v '^??' | head -5)
179
+ if [[ -n "${dirty}" ]]; then
180
+ echo "⚠️ 本地未提交修改(不会覆盖):"; echo "${dirty}" | sed 's/^/ /'
181
+ read -rp " 继续?[y/N] " ans
182
+ [[ "${ans,,}" == "y" ]] || { echo "已取消"; exit 1; }
183
+ git -C "${ROOT}" stash push -m "guanwei-update-auto" >/dev/null 2>&1 && STASHED=1
184
+ fi
185
+ echo "📦 增量更新..."
186
+ git -C "${ROOT}" merge --ff-only origin/main 2>&1 | tail -2 || git -C "${ROOT}" pull --rebase origin main 2>&1 | tail -2
187
+ echo "📦 安装依赖..."
188
+ (cd "${ROOT}" && npm install --no-fund --no-audit 2>&1 | tail -1)
189
+ (cd "${ROOT}/server" && npm install --no-fund --no-audit 2>&1 | tail -1)
190
+ [[ -n "${STASHED:-}" ]] && git -C "${ROOT}" stash pop >/dev/null 2>&1 && echo "✅ 本地修改已恢复"
191
+ echo "✅ 更新完成:$(node -e "console.log(require('${ROOT}/package.json').version)") · $(git -C "${ROOT}" rev-parse --short HEAD)"
192
+ fi
193
+ }
194
+
195
+ cmd_doctor() {
196
+ local ok=1
197
+ echo "🩺 观微环境自检(${MODE} 模式)"
198
+ echo "──────────────────────────────"
199
+ if command -v node >/dev/null 2>&1; then
200
+ echo "✅ Node.js $(node -v | sed 's/v//')"
201
+ else
202
+ echo "❌ 未检测到 Node.js"; ok=0
203
+ fi
204
+ if [[ -s "${ENV_FILE}" ]]; then
205
+ if grep -qE "sk-你的|sk-test|your-" "${ENV_FILE}" 2>/dev/null; then
206
+ echo "❌ 配置含占位/测试密钥——重新运行 guanwei setup"; ok=0
207
+ else
208
+ echo "✅ 已配置(LLM_PROVIDER=$(grep '^LLM_PROVIDER=' "${ENV_FILE}" | cut -d= -f2))"
209
+ fi
210
+ else
211
+ echo "❌ 未配置——运行 guanwei setup --key 你的APIKey"; ok=0
212
+ fi
213
+ echo "✅ 项目目录: ${ROOT}"
214
+ local be fe
215
+ be=$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 http://localhost:3018/api/health 2>/dev/null || echo 0)
216
+ fe=$(curl -s -o /dev/null -w '%{http_code}' --max-time 2 "http://localhost:${WEB_PORT:-5173}/" 2>/dev/null || echo 0)
217
+ [[ "${be}" == "200" ]] && echo "✅ 后端 3018 运行中" || echo "ℹ️ 后端 3018 未运行"
218
+ [[ "${fe}" == "200" ]] && echo "✅ 前端 ${WEB_PORT:-5173} 运行中" || echo "ℹ️ 前端 ${WEB_PORT:-5173} 未运行"
219
+ cmd_check
220
+ echo "──────────────────────────────"
221
+ [[ ${ok} -eq 1 ]] && echo "🎉 自检通过" || echo "🛠️ 按提示修复后重试:guanwei doctor"
222
+ }
223
+
224
+ cmd_stop() {
225
+ for port in 3018 ${WEB_PORT:-5173}; do
226
+ local pid
227
+ pid=$(lsof -nP -iTCP:${port} -sTCP:LISTEN -t 2>/dev/null | head -1)
228
+ [[ -n "${pid}" ]] && kill "${pid}" 2>/dev/null && echo "已停止端口 ${port}(pid ${pid})"
229
+ done
230
+ echo "✅ 停止命令已执行"
231
+ }
232
+
233
+ CMD="${1:-help}"
234
+ case "${CMD}" in
235
+ setup) shift; cmd_setup "$@" ;;
236
+ start) shift; cmd_start "$@" ;;
237
+ web) shift; cmd_web "$@" ;;
238
+ update) cmd_update ;;
239
+ check) cmd_check ;;
240
+ status) cmd_status ;;
241
+ doctor) cmd_doctor ;;
242
+ stop) cmd_stop ;;
243
+ help|-h|--help) cmd_help ;;
244
+ *) echo "未知命令: ${CMD}"; cmd_help; exit 1 ;;
245
+ esac