// ╔══════════════════════════════════════════════════════════╗ // ║ 六命解析 — LIFE CODE ENGINE v2 ║ // ╚══════════════════════════════════════════════════════════╝ // ═══ SHICHUSUIMEI (四柱推命) ═══ const STEMS = ['甲','乙','丙','丁','戊','己','庚','辛','壬','癸']; const STEM_EL = ['木','木','火','火','土','土','金','金','水','水']; const BRANCHES = ['子','丑','寅','卯','辰','巳','午','未','申','酉','戌','亥']; const BRANCH_ANIMALS = ['鼠','牛','虎','兔','龍','蛇','馬','羊','猿','鶏','犬','猪']; const BRANCH_EL = ['水','土','木','木','土','火','火','土','金','金','土','水']; const ELEMENTS = ['木','火','土','金','水']; const ELEMENT_DATA = { '木': { title:'木気', nature:'成長・発展・柔軟性', season:'春', dir:'東', desc:'生命の芽吹きを司る。常に上へ向かう成長のエネルギー。', color:'#5a9e8a' }, '火': { title:'火気', nature:'情熱・変革・輝き', season:'夏', dir:'南', desc:'炎のように周囲を照らし、変化をもたらす強烈なエネルギー。', color:'#c06060' }, '土': { title:'土気', nature:'安定・包容・中庸', season:'土用',dir:'中央',desc:'万物を育む大地の力。バランスを保ち全てを包み込む。', color:'#c8a84b' }, '金': { title:'金気', nature:'収穫・精錬・完璧', season:'秋', dir:'西', desc:'純粋さを追求し、不要なものを削ぎ落とす刃のエネルギー。', color:'#9090b0' }, '水': { title:'水気', nature:'智慧・流動・深淵', season:'冬', dir:'北', desc:'深い知性と適応力。あらゆる形に変化する無限の可能性。', color:'#5080a0' }, }; function getYearPillar(year) { const si = ((year - 4) % 10 + 10) % 10; const bi = ((year - 4) % 12 + 12) % 12; return { stem: STEMS[si], branch: BRANCHES[bi], el: STEM_EL[si], animal: BRANCH_ANIMALS[bi] }; } function getMonthPillar(year, month) { const yearStemIdx = ((year - 4) % 10 + 10) % 10; const monthBase = Math.floor(yearStemIdx / 2) * 2; const si = (monthBase + (month - 1)) % 10; const bi = (month + 1) % 12; return { stem: STEMS[si], branch: BRANCHES[bi], el: STEM_EL[si] }; } function getDayPillar(year, month, day) { const a = Math.floor((14 - month) / 12); const y = year - a, m2 = month + 12 * a - 2; const jd = day + Math.floor((153 * m2 + 2) / 5) + 365 * y + Math.floor(y/4) - Math.floor(y/100) + Math.floor(y/400) - 32045; const si = ((jd + 9) % 10 + 10) % 10; const bi = ((jd + 1) % 12 + 12) % 12; return { stem: STEMS[si], branch: BRANCHES[bi], el: STEM_EL[si] }; } function getHourPillar(hour) { // 子時=23-1, 丑時=1-3, ... const branchIdx = Math.floor(((hour + 1) % 24) / 2) % 12; return { branch: BRANCHES[branchIdx], el: BRANCH_EL[branchIdx], animal: BRANCH_ANIMALS[branchIdx] }; } function getDominantEl(year, month, day, hour) { const yp = getYearPillar(year), mp = getMonthPillar(year,month), dp = getDayPillar(year,month,day); const hp = hour !== null ? getHourPillar(hour) : null; const counts = {}; [yp.el, mp.el, dp.el, hp?.el].filter(Boolean).forEach(e => { counts[e] = (counts[e]||0)+1; }); return Object.entries(counts).sort((a,b)=>b[1]-a[1])[0][0]; } function formatHourInputValue(value) { if (!value) return ''; const match = String(value).match(/^(\d{1,2})/); if (!match) return ''; const hour = parseInt(match[1], 10); if (Number.isNaN(hour)) return ''; return String(hour); } function formatBirthTimeForStorage(hourValue) { const hour = parseInt(String(hourValue ?? '').trim(), 10); if (Number.isNaN(hour) || hour < 0 || hour > 23) { return null; } return `${String(hour).padStart(2, '0')}:00:00`; } function normalizeOptionalDecimalString(value, min, max) { if (value === null || typeof value === 'undefined') { return null; } const trimmed = String(value).trim(); if (trimmed === '') { return null; } const num = parseFloat(trimmed); if (Number.isNaN(num) || num < min || num > max) { return null; } return trimmed; } function formatBirthLatLngValue(lat, lng) { const latStr = lat != null && String(lat).trim() !== '' ? String(lat).trim() : ''; const lngStr = lng != null && String(lng).trim() !== '' ? String(lng).trim() : ''; if (latStr && lngStr) { return `${latStr}, ${lngStr}`; } return latStr || lngStr || ''; } function parseBirthLatLngValue(rawValue) { const raw = String(rawValue ?? '').trim(); if (raw === '') { return { birthLatitude: null, birthLongitude: null }; } const matches = raw.replace(/[,]/g, ',').match(/-?\d+(?:\.\d+)?/g); if (!matches || matches.length < 2) { return { birthLatitude: null, birthLongitude: null }; } return { birthLatitude: normalizeOptionalDecimalString(matches[0], -90, 90), birthLongitude: normalizeOptionalDecimalString(matches[1], -180, 180), }; } function adjustDateTimeByLongitude(birthDateValue, timeValue, longitude, referenceLongitude = 135) { const dateStr = String(birthDateValue ?? '').trim(); const timeStr = String(timeValue ?? '').trim(); const lon = parseFloat(String(longitude ?? '').trim()); if (!dateStr || !timeStr || Number.isNaN(lon)) { return null; } const dateMatch = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/); const timeMatch = timeStr.match(/^(\d{1,2})(?::(\d{2})(?::(\d{2}))?)?$/); if (!dateMatch || !timeMatch) { return null; } const year = parseInt(dateMatch[1], 10); const month = parseInt(dateMatch[2], 10); const day = parseInt(dateMatch[3], 10); const hour = parseInt(timeMatch[1], 10); const minute = timeMatch[2] ? parseInt(timeMatch[2], 10) : 0; const second = timeMatch[3] ? parseInt(timeMatch[3], 10) : 0; if ([year, month, day, hour, minute, second].some(Number.isNaN)) { return null; } const offsetMinutes = Math.round((lon - referenceLongitude) * 4); const baseUtc = Date.UTC(year, month - 1, day, hour, minute, second); const adjusted = new Date(baseUtc + offsetMinutes * 60 * 1000); if (Number.isNaN(adjusted.getTime())) { return null; } const adjustedDate = `${adjusted.getUTCFullYear()}-${String(adjusted.getUTCMonth() + 1).padStart(2, '0')}-${String(adjusted.getUTCDate()).padStart(2, '0')}`; const adjustedTime = `${String(adjusted.getUTCHours()).padStart(2, '0')}:${String(adjusted.getUTCMinutes()).padStart(2, '0')}:${String(adjusted.getUTCSeconds()).padStart(2, '0')}`; return { birthDate: adjustedDate, birthTime: adjustedTime, offsetMinutes }; } // ═══ SUUHI (数秘術) ═══ function calcLifePath(year, month, day) { const digits = `${year}${String(month).padStart(2,'0')}${String(day).padStart(2,'0')}`; let s = digits.split('').reduce((a,b)=>a+parseInt(b),0); while (s > 9 && s !== 11 && s !== 22 && s !== 33) { s = s.toString().split('').reduce((a,b)=>a+parseInt(b),0); } return s; } const LIFEPATH_DATA = { 1: { title:'先駆者', keywords:['独立','創造','主導'] }, 2: { title:'調和者', keywords:['協力','直感','調和'] }, 3: { title:'表現者', keywords:['創造','表現','喜び'] }, 4: { title:'建設者', keywords:['安定','規律','実直'] }, 5: { title:'探求者', keywords:['自由','変化','冒険'] }, 6: { title:'保護者', keywords:['愛情','責任','奉仕'] }, 7: { title:'探偵者', keywords:['探求','直観','神秘'] }, 8: { title:'支配者', keywords:['成功','野心','豊かさ'] }, 9: { title:'完成者', keywords:['完成','奉仕','智慧'] }, 11: { title:'照明者', keywords:['啓示','直観','霊性'] }, 22: { title:'建築者', keywords:['実現','偉業','建設'] }, 33: { title:'教師', keywords:['愛','癒し','教育'] }, }; // ═══ KYUSEI (九星気学) ═══ const KYUSEI_NAMES = ['一白水星','二黒土星','三碧木星','四緑木星','五黄土星','六白金星','七赤金星','八白土星','九紫火星']; const KYUSEI_DIRS = ['北','南西','東','東南','中央','北西','西','北東','南']; const KYUSEI_COLORS = ['#4a7fa0','#604030','#4a7a60','#5a8060','#b8963c','#8090a0','#c04850','#8090a0','#a040a0']; function calcKyusei(year, month) { const base = ((year - 1984) % 9 + 9) % 9; const adj = [0,8,7,6,5,4,3,2,1,0,8,7,6][month] || 0; return ((base + adj) % 9) + 1; } // ═══ ZODIAC (12星座) ═══ function getZodiac(month, day) { const tbl = [ [1,20,'水瓶座','♒'],[2,19,'魚座','♓'],[3,21,'牡羊座','♈'], [4,20,'牡牛座','♉'],[5,21,'双子座','♊'],[6,21,'蟹座','♋'], [7,23,'獅子座','♌'],[8,23,'乙女座','♍'],[9,23,'天秤座','♎'], [10,23,'蠍座','♏'],[11,22,'射手座','♐'],[12,22,'山羊座','♑'], ]; for (let i=0;i=d) { const n=tbl[(i+1)%12]; return {name:n[2],symbol:n[3]}; } } return {name:'山羊座',symbol:'♑'}; } // ═══ BIORHYTHM ═══ function calcBio(by,bm,bd,ty,tm,td) { const days = Math.floor((new Date(ty,tm-1,td)-new Date(by,bm-1,bd))/(86400000)); return { physical: Math.sin(2*Math.PI*days/23), emotional: Math.sin(2*Math.PI*days/28), intellectual:Math.sin(2*Math.PI*days/33), days, }; } // ═══ MAYA ═══ const MAYA_KINS = ['イミシュ','イク','アクバル','カン','チクチャン','キミ','マニク','ラマット','ムルク','オク','チュエン','エブ','ベン','イシュ','メン','キブ','カバン','エツナブ','カワク','アハウ']; const MAYA_TONES = ['磁気','月','電気','自己存在','倍音','韻律','共鳴','銀河','太陽','惑星','スペクトル','水晶','宇宙']; function calcMaya(y,m,d) { const diff = Math.floor((new Date(y,m-1,d)-new Date(2013,0,1))/86400000); const kin = ((diff%20)+20)%20; const tone = ((diff%13)+13)%13; return { kin:MAYA_KINS[kin], tone:MAYA_TONES[tone], kinNum:kin+1, toneNum:tone+1 }; } function getTransitionBucketMeta(evt) { const bucket = evt?.transition_bucket || ''; switch (bucket) { case 'event_type_event_text_transition': return { label: '転機候補', tone: 'high', note: '動きが大きいイベントとして優先表示' }; case 'event_type_event_short_description': return { label: '要確認', tone: 'medium', note: '説明が短いため、参考として表示' }; case 'event_type_event_other': return { label: '参考', tone: 'low', note: '通常のイベントとして控えめに表示' }; case 'event_type_event_date_only': return { label: '除外', tone: 'exclude', note: '日付見出しのみのため表示対象外' }; default: return null; } } // ═══════════════════════════════════════════════════════ // OS SYSTEM — Drive・Care・Vision・Logic・Quest・Balance // ═══════════════════════════════════════════════════════ // 各占術から 6つのOS点(0-20)を返すマッピング const ELEMENT_OS = { '木': { Drive:8, Care:12, Vision:18, Logic:8, Quest:16, Balance:14 }, '火': { Drive:18, Care:12, Vision:14, Logic:8, Quest:14, Balance:10 }, '土': { Drive:8, Care:18, Vision:12, Logic:14, Quest:6, Balance:18 }, '金': { Drive:12, Care:10, Vision:8, Logic:20, Quest:12, Balance:14 }, '水': { Drive:10, Care:14, Vision:16, Logic:16, Quest:12, Balance:8 }, }; const LIFEPATH_OS = { 1:{Drive:20,Care:6, Vision:14,Logic:12,Quest:16,Balance:8 }, 2:{Drive:6, Care:20,Vision:10,Logic:12,Quest:6, Balance:18}, 3:{Drive:14,Care:12,Vision:20,Logic:8, Quest:14,Balance:12}, 4:{Drive:10,Care:14,Vision:8, Logic:20,Quest:8, Balance:16}, 5:{Drive:16,Care:8, Vision:14,Logic:10,Quest:20,Balance:10}, 6:{Drive:8, Care:18,Vision:14,Logic:12,Quest:8, Balance:18}, 7:{Drive:6, Care:10,Vision:18,Logic:20,Quest:12,Balance:12}, 8:{Drive:18,Care:10,Vision:10,Logic:18,Quest:12,Balance:10}, 9:{Drive:12,Care:18,Vision:16,Logic:10,Quest:14,Balance:14}, 11:{Drive:8,Care:12,Vision:20,Logic:14,Quest:12,Balance:16}, 22:{Drive:16,Care:12,Vision:14,Logic:18,Quest:10,Balance:14}, 33:{Drive:8,Care:20,Vision:16,Logic:10,Quest:8, Balance:18}, }; const KYUSEI_OS = { 1:{Drive:8, Care:12,Vision:16,Logic:18,Quest:10,Balance:12}, 2:{Drive:8, Care:18,Vision:10,Logic:12,Quest:6, Balance:20}, 3:{Drive:16,Care:10,Vision:18,Logic:8, Quest:18,Balance:12}, 4:{Drive:14,Care:12,Vision:20,Logic:10,Quest:16,Balance:14}, 5:{Drive:20,Care:12,Vision:12,Logic:16,Quest:12,Balance:14}, 6:{Drive:12,Care:10,Vision:10,Logic:20,Quest:12,Balance:18}, 7:{Drive:10,Care:12,Vision:18,Logic:14,Quest:12,Balance:14}, 8:{Drive:10,Care:14,Vision:8, Logic:18,Quest:10,Balance:20}, 9:{Drive:18,Care:12,Vision:16,Logic:10,Quest:14,Balance:12}, }; const ZODIAC_OS = { '牡羊座':{Drive:20,Care:8, Vision:14,Logic:8, Quest:16,Balance:10}, '牡牛座':{Drive:8, Care:16,Vision:10,Logic:14,Quest:8, Balance:20}, '双子座':{Drive:14,Care:10,Vision:18,Logic:12,Quest:18,Balance:12}, '蟹座': {Drive:6, Care:20,Vision:12,Logic:10,Quest:6, Balance:18}, '獅子座':{Drive:18,Care:12,Vision:18,Logic:8, Quest:14,Balance:10}, '乙女座':{Drive:8, Care:14,Vision:10,Logic:20,Quest:10,Balance:16}, '天秤座':{Drive:12,Care:14,Vision:14,Logic:12,Quest:12,Balance:20}, '蠍座': {Drive:12,Care:16,Vision:16,Logic:18,Quest:14,Balance:8 }, '射手座':{Drive:16,Care:10,Vision:14,Logic:10,Quest:20,Balance:12}, '山羊座':{Drive:14,Care:12,Vision:8, Logic:18,Quest:10,Balance:18}, '水瓶座':{Drive:12,Care:8, Vision:20,Logic:14,Quest:18,Balance:14}, '魚座': {Drive:6, Care:18,Vision:18,Logic:8, Quest:12,Balance:16}, }; function getMayaOS(kinNum) { const g = Math.ceil(kinNum / 5); return [ {Drive:16,Care:14,Vision:12,Logic:12,Quest:14,Balance:14}, {Drive:14,Care:14,Vision:16,Logic:12,Quest:14,Balance:12}, {Drive:12,Care:14,Vision:14,Logic:16,Quest:16,Balance:12}, {Drive:12,Care:16,Vision:14,Logic:14,Quest:12,Balance:16}, ][g - 1] || {Drive:13,Care:13,Vision:13,Logic:13,Quest:13,Balance:13}; } const OS_TYPES = ['Drive','Care','Vision','Logic','Quest','Balance']; const OS_META = { Drive: { label:'🔥 点火者', jpName:'点火者', detailType:'先行突破型', emoji:'🔥', color:'#e07040', tagline:'行動・推進・決断' }, Care: { label:'🌙 包容者', jpName:'包容者', detailType:'信頼構築型', emoji:'🌙', color:'#5080a0', tagline:'共感・支援・調和' }, Vision: { label:'🎨 表現者', jpName:'表現者', detailType:'世界観創造型', emoji:'🎨', color:'#a050a0', tagline:'創造・直感・洞察' }, Logic: { label:'💎 洞察者', jpName:'洞察者', detailType:'本質解析型', emoji:'💎', color:'#6090c0', tagline:'分析・計画・精緻' }, Quest: { label:'🪽 解放者', jpName:'解放者', detailType:'変化適応型', emoji:'🪽', color:'#508060', tagline:'探求・冒険・自由' }, Balance: { label:'🌿 調律者', jpName:'調律者', detailType:'調和設計型', emoji:'🌿', color:'#c8a84b', tagline:'統合・調和・柔軟' }, }; // OSタイプ別の職業適性と成功確率(バックテストデータに基づく) const OS_CAREER_MAP = { Drive: { primary: { career: '起業家・経営者', successRate: 100 }, subModifiers: { Care: '思いやりのある', Vision: 'クリエイティブな', Logic: '戦略的な', Quest: '変革的な', Balance: 'バランス感覚のある', } }, Vision: { primary: { career: 'ミュージシャン・アーティスト', successRate: 87.5 }, subModifiers: { Drive: 'ビジネスセンスのある', Care: '人の心に寄り添う', Logic: '意図的な', Quest: '多様な表現の', Balance: 'バランス感覚のある', } }, Quest: { primary: { career: '起業家・営業・フリーランス', successRate: 100 }, subModifiers: { Drive: '推進力のある', Care: '信頼を築く', Vision: 'クリエイティブな', Logic: '分析眼のある', Balance: 'バランス感覚のある', } }, Balance: { primary: { career: 'マネージャー・コーディネーター', successRate: 69.5 }, subModifiers: { Drive: '決断力のある', Care: '人を育てる', Vision: 'クリエイティブな', Logic: '分析的な', Quest: '適応力のある', } }, Care: { primary: { career: 'カウンセラー・教育者・福祉職', successRate: 69.5 }, subModifiers: { Drive: 'リーダーシップのある', Vision: 'クリエイティブな', Logic: '分析的な', Quest: '開放的な', Balance: 'バランス感覚のある', } }, Logic: { primary: { career: '研究者・分析家・戦略家', successRate: 69.5 }, subModifiers: { Drive: 'ビジネスセンスのある', Care: '共感力のある', Vision: 'クリエイティブな', Quest: '探究心の強い', Balance: 'バランス感覚のある', } }, }; // OSタイプの組み合わせから職業と成功確率を取得 const getOSCareerInfo = (mainOS, subOS) => { const mainCareer = OS_CAREER_MAP[mainOS]; if (!mainCareer) return null; const modifier = mainCareer.subModifiers[subOS] || ''; const careerLabel = modifier ? `${modifier}${mainCareer.primary.career}` : mainCareer.primary.career; return { career: careerLabel, successRate: mainCareer.primary.successRate, mainCareerType: mainCareer.primary.career, }; }; // 層1:OSスコア計算(仕様の重み付け) function calcOSScores(lifePath, el, kyusei, zodiac, maya) { const s = Object.fromEntries(OS_TYPES.map(k=>[k,0])); const add = (map, w) => map && OS_TYPES.forEach(k => { s[k] += (map[k]||0) * w; }); add(ELEMENT_OS[el], 0.35); add(LIFEPATH_OS[lifePath], 0.25); add(KYUSEI_OS[kyusei], 0.15); add(ZODIAC_OS[zodiac.name], 0.10); add(getMayaOS(maya.kinNum), 0.15); const sorted = Object.entries(s).sort((a,b)=>b[1]-a[1]); return { scores: s, mainOS: sorted[0][0], subOS: sorted[1][0] }; } // ═══════════════════════════════════════════════════════ // 6 TYPE SYSTEM — 点火者・包容者・表現者・洞察者・解放者・調律者(互換性用、将来削除予定) // ═══════════════════════════════════════════════════════ const SIX_TYPES = { '点火者': { label:'🚀 先行突破型', color:'#e07040', tagline:'誰よりも早く、誰よりも遠くへ', desc:'物事を一から動かす強烈な推進力を持ちます。停滞を嫌い、常に新しいフロンティアへ向かう。リスクを恐れず、失敗から学ぶ速度が異常に速い。', personality:'強い意志と独立心の持ち主。周囲のペースに合わせることが苦手だが、その突破力は他の追随を許さない。感情より理性を優先し、目標達成のためには孤独も厭わない。自分の信念に忠実で、曲げることを嫌う。', love:'恋愛においても積極的で、好きになったら一直線。しかし自立心が強く、束縛を嫌うため、対等なパートナーシップが理想。相手の自由も尊重できる人と深く結ばれる。', work:'起業家・経営者・プロジェクトリーダーに適性。新規事業の立ち上げや、前例のない挑戦に真価を発揮。金運は行動に比例して高まる。リスクを取った分だけ報われる星の下に生まれている。', advice:'「完璧を待つより、今すぐ動く」があなたのテーゼ。ただし、仲間を置き去りにしないよう意識的に振り返る習慣を。', compat: ['調律者', '解放者'], }, '包容者': { label:'💝 信頼構築型', color:'#c06080', tagline:'すべてを受け入れ、すべてを育む', desc:'深い愛情と受容力で、周囲に安心の場を作ります。人の痛みを自分のこととして感じ取る共感力の持ち主。', personality:'繊細で感受性が豊か。表には出さないが、内側では常に深く考えている。人間関係を大切にし、誰かの役に立つことで自分の存在意義を感じる。ただし自己犠牲が過ぎると燃え尽きやすい。', love:'深く、真剣に愛する。一度結ばれると長く続く傾向。相手の感情を敏感に察知するため、言葉がなくても通じ合える関係を築く。嫉妬心は強いが、それは愛の深さの裏返し。', work:'カウンセラー・医療・教育・福祉の分野で才能を発揮。人を育てる仕事に天職を見出す。金運は「与えること」から循環する。惜しまず与える姿勢が、やがて豊かさとして返ってくる。', advice:'「No」と言う練習をしてください。あなたの優しさを守るために、境界線を引くことは愛の行為です。', compat: ['洞察者', '表現者'], }, '表現者': { label:'🎨 世界観創造型', color:'#8060c0', tagline:'言葉と色彩で、世界を塗り替える', desc:'豊かな創造性と独自の世界観を持ち、表現することで魂が輝きます。芸術・言語・音楽などあらゆる媒体を通じて、見えない何かを可視化する力。', personality:'個性的で独創的。マイペースで、流行より自分のスタイルを貫く。気分の波が激しいことがあるが、その振れ幅が創造力の源泉。孤独な時間が必要で、それが才能の充電になる。', love:'恋愛は情熱的かつ詩的。理想が高く、ありきたりな関係には満足しない。精神的な結びつきを最重視し、魂の共鳴を感じる人との縁が深まる。', work:'アーティスト・作家・デザイナー・ミュージシャンなど創造的職業に天賦の才。また、マーケティングやブランディングでも独自センスが光る。金運は「好きなことをする」ことで引き寄せられる。', advice:'完璧主義を手放すと、作品がさらに自由になります。未完成のままでも、世界に出す勇気を持ってください。', compat: ['包容者', '調律者'], }, '洞察者': { label:'🧠 本質解析型', color:'#4070a0', tagline:'見えないものを見る、静かな哲人', desc:'深い分析力と直観を持ち、物事の本質を瞬時に見抜きます。表面的な情報に惑わされず、構造と真実を追求する。', personality:'内向的で思慮深い。会話より観察を好み、言葉を大切に使う。少数精鋭の深い関係を好み、広く浅い付き合いには消耗する。知的探求が生きがいで、学ぶことをやめない。', love:'恋愛は慎重で、相手を深く理解してから動く。表面的な魅力より、知性と誠実さに惹かれる。一度心を開いた相手には深い信頼を寄せるが、裏切りには敏感。', work:'研究者・分析家・戦略家・医師・哲学者など。データと洞察を組み合わせる仕事で真価を発揮。金運は「専門性を極める」ことで安定する。浅く広くより、深く狭く。', advice:'知識を行動に変える練習を。分析が終わったら、まず一歩だけ動いてみてください。思考と行動のバランスが運を開きます。', compat: ['包容者', '点火者'], }, '解放者': { label:'🌍 変化適応型', color:'#50a070', tagline:'枠を超え、風のように自由に', desc:'変化と自由を愛し、あらゆる環境に適応する柔軟性を持ちます。固定観念を打ち破り、新しいパラダイムをもたらす変革者。', personality:'好奇心旺盛で飽きやすい面もあるが、その多様な経験が独自の視点を生む。ルールより自由、安定より変化。旅・異文化・新しい体験が魂の栄養となる。人脈が広く、縁の引力が強い。', love:'恋愛は自由で軽やか。重い関係より、互いに成長し合えるパートナーシップを好む。単調さを嫌うため、常に新鮮さを保つ工夫が必要。長期の関係では、意図的に変化を作ることが鍵。', work:'トラベラー・フリーランス・営業・起業家・外交官など多様な環境で活躍。変化の多い職場環境が合っている。金運は「動くほど」増える。じっとしていると停滞する。', advice:'「一つのことを続ける」練習が、あなたの可能性を10倍にします。自由と責任はセットです。', compat: ['点火者', '洞察者'], }, '調律者': { label:'✨ 調和設計型', color:'#a09030', tagline:'すべての音を、美しい和音に変える', desc:'バランス感覚と審美眼を持ち、対立を調和へと導く力があります。場の空気を読み、最適なタイミングで最適な言葉を選ぶ。', personality:'穏やかで公平。どんな立場の人とも良好な関係を築き、コンフリクトを避けながら解決に向かう。完璧主義的な側面もあり、細部へのこだわりが強い。美しいものに敏感で、環境が心に影響しやすい。', love:'恋愛は穏やかで安定している。刺激より安心を重視し、信頼関係をゆっくり築く。パートナーへの思いやりが深く、長続きする関係を作る。ただし、自分の気持ちを後回しにしすぎる傾向がある。', work:'調停者・外交官・デザイナー・音楽家・マネージャーなど。チームの調和を保ちながら成果を出す役割で光る。金運は「環境の美しさ」に比例する。整えられた空間が豊かさを呼ぶ。', advice:'「曖昧な返事」をやめることが、あなたの運気を開きます。自分の意見をはっきり伝えることで、本来の力が解放されます。', compat: ['表現者', '点火者'], }, }; function determineType(lifePath, el, kyusei, zodiac) { // Multi-factor scoring const scores = { '点火者':0, '包容者':0, '表現者':0, '洞察者':0, '解放者':0, '調律者':0 }; // LifePath contribution const lpMap = {1:'点火者',2:'調律者',3:'表現者',4:'調律者',5:'解放者',6:'包容者',7:'洞察者',8:'点火者',9:'包容者',11:'洞察者',22:'点火者',33:'包容者'}; if (lpMap[lifePath]) scores[lpMap[lifePath]] += 3; // Element contribution const elMap = {'木':'解放者','火':'点火者','土':'包容者','金':'洞察者','水':'洞察者'}; if (elMap[el]) scores[elMap[el]] += 2; // Kyusei contribution const kyMap = {1:'洞察者',2:'包容者',3:'解放者',4:'表現者',5:'点火者',6:'調律者',7:'表現者',8:'調律者',9:'点火者'}; if (kyMap[kyusei]) scores[kyMap[kyusei]] += 2; // Zodiac contribution const zodMap = { '牡羊座':'点火者','牡牛座':'調律者','双子座':'解放者','蟹座':'包容者', '獅子座':'表現者','乙女座':'洞察者','天秤座':'調律者','蠍座':'洞察者', '射手座':'解放者','山羊座':'点火者','水瓶座':'表現者','魚座':'包容者', }; if (zodMap[zodiac.name]) scores[zodMap[zodiac.name]] += 2; return Object.entries(scores).sort((a,b)=>b[1]-a[1])[0][0]; } // ═══ FORTUNE PHASES ═══ // スコアマッピングテーブル(2026-05-08 最適化) const ELEMENT_SCORE = { '木':16, '火':18, '土':10, '金':12, '水':6 }; const KYUSEI_SCORE = { 1:10, 2:4, 3:16, 4:14, 5:20, 6:16, 7:12, 8:6, 9:18 }; const PERSONAL_MONTH_SCORE = { 1:18, 2:6, 3:16, 4:8, 5:14, 6:12, 7:4, 8:18, 9:14 }; const PERSONAL_YEAR_SCORE = { 1:20, 2:6, 3:16, 4:8, 5:14, 6:12, 7:4, 8:18, 9:14, 11:16, 22:18, 33:16 }; const MAYA_TONE_SCORE = { 1:18, 2:8, 3:14, 4:8, 5:16, 6:12, 7:18, 8:12, 9:18, 10:10, 11:6, 12:14, 13:16 }; const ZODIAC_SCORE = { '牡羊座':18,'獅子座':16,'射手座':14,'牡牛座':12,'乙女座':10,'山羊座':10, '双子座':16,'天秤座':14,'水瓶座':14,'蟹座':8,'蠍座':10,'魚座':8 }; // 数秘術:個人年数を計算 function calcPersonalYear(m, d, y) { const s = `${m}${d}${y}`.split('').reduce((a,b)=>a+parseInt(b),0); while (s > 9 && s !== 11 && s !== 22 && s !== 33) { return s.toString().split('').reduce((a,b)=>a+parseInt(b),0); } return s; } // スコアをフェーズに変換 function scoreToPhase(score) { if (score >= 16) return { phase:'春-始動期', emoji:'🌱', desc:'新しいことを始めるのに最適な時期。エネルギーが高く、チャンスが訪れやすい。積極的な行動が運を広げます。' }; if (score >= 11) return { phase:'夏-拡大期', emoji:'🌿', desc:'活動や影響力を広げるのに良い時期。周囲との協力も進みやすく、成長が加速します。' }; if (score >= 6) return { phase:'秋-収穫期', emoji:'🍂', desc:'これまでの努力が実を結ぶ時期。成果を受け取り、整理することが大切です。' }; return { phase:'冬-調整期', emoji:'🌙', desc:'内面を整える大切な時期。焦らず、自分のペースで準備を進めることが運を開きます。' }; } // 層2-1:年運計算(最適化版:スコアマッピング使用) function calcYearFortune(ty, by, bm, bd, el, lifePath, kyusei, zodiac, maya) { const fourPillarsScore = ELEMENT_SCORE[el] || 12; const personalYear = calcPersonalYear(bm, bd, ty); const numerologyScore = PERSONAL_YEAR_SCORE[personalYear] || 12; const kyuseiYearScore = KYUSEI_SCORE[kyusei] || 12; const mayaScore = MAYA_TONE_SCORE[maya.toneNum] || 12; const zodiacScore = ZODIAC_SCORE[zodiac.name] || 12; const bioScore = 12; // より大きな分散を出すために、スコア範囲を拡大(0-20→0-24)してから正規化 const expandedScore = Math.round( fourPillarsScore * 0.35 + numerologyScore * 0.22 + kyuseiYearScore * 0.27 + mayaScore * 0.10 + zodiacScore * 0.04 + bioScore * 0.02 ); // スコアを非線形に変換して分散を拡大 const normalized = Math.max(0, Math.min(20, expandedScore)); const dispersed = Math.round(((normalized - 12) * 1.8 + 12)); const final = Math.max(0, Math.min(20, dispersed)); return { score: final, ...scoreToPhase(final) }; } // 月初日のバイオリズムスコア計算(0-20) function getCalcBioMonthScore(by, bm, bd, ty, tm) { // 対象月の初日のバイオリズム(身体23日、感情28日、知性33日周期)を計算 const bio = calcBio(by, bm, bd, ty, tm, 1); // 月初日(1日) const avg = (bio.physical + bio.emotional + bio.intellectual) / 3; // バイオリズム平均値 (-1.0〜+1.0) を スコア (0〜20) に正規化 return Math.max(0, Math.min(20, Math.round(avg * 10 + 10))); } // 層2-2:月運計算(最適化版:スコアマッピング使用) function calcMonthFortune(ty, tm, by, bm, bd, el, lifePath, kyusei, zodiac, maya) { const personalMonth = ((tm + calcPersonalYear(bm, bd, ty)) % 9 || 9); const fourPillarsScore = ELEMENT_SCORE[el] || 12; const numerologyScore = PERSONAL_MONTH_SCORE[personalMonth] || 12; const kyuseiMonthScore = calcKyusei(ty, tm); const kyuseiScore = KYUSEI_SCORE[kyuseiMonthScore] || 12; const mayaScore = MAYA_TONE_SCORE[maya.toneNum] || 12; const bioMonthScore = getCalcBioMonthScore(by, bm, bd, ty, tm); const zodiacScore = ZODIAC_SCORE[zodiac.name] || 12; // より大きな分散を出すために、スコア範囲を拡大(0-20→0-24)してから正規化 const expandedScore = Math.round( fourPillarsScore * 0.28 + kyuseiScore * 0.28 + numerologyScore * 0.18 + mayaScore * 0.12 + bioMonthScore * 0.10 + zodiacScore * 0.04 ); // スコアを非線形に変換して分散を拡大 const normalized = Math.max(0, Math.min(20, expandedScore)); const dispersed = Math.round(((normalized - 12) * 1.8 + 12)); const final = Math.max(0, Math.min(20, dispersed)); return { score: final, ...scoreToPhase(final) }; } // 層3:今日の占い - 仕様の重み付け function calcDayFortune(y, m, d, ty, tm, td, el, lifePath, kyusei, zodiac, maya) { const bio = calcBio(y, m, d, ty, tm, td); const bioScore = Math.round((bio.physical + bio.emotional + bio.intellectual) / 3 * 10 + 10); const normalizedBioScore = Math.max(0, Math.min(20, bioScore)); const kyuseiDayScore = 12; const fourPillarsDayScore = 12; const numerologyDayScore = 11; const mayaDayScore = 12; const zodiacDayScore = 10; const score = Math.round( normalizedBioScore * 0.30 + kyuseiDayScore * 0.20 + fourPillarsDayScore * 0.20 + numerologyDayScore * 0.15 + mayaDayScore * 0.10 + zodiacDayScore * 0.05 ); const normalizedScore = Math.max(0, Math.min(20, score)); const lucky = ((td + lifePath + kyusei) % 5 + 5) % 5; const luckyItems = [ {color:'白', item:'白いハンカチ', number:'3', direction:'北'}, {color:'金', item:'ゴールドのアクセサリー', number:'8', direction:'南'}, {color:'緑', item:'植物・観葉植物', number:'4', direction:'東'}, {color:'青', item:'水を入れたクリスタルグラス', number:'7', direction:'西'}, {color:'紫', item:'アメジスト', number:'9', direction:'南東'}, ][lucky]; const lifePathHour = ((lifePath - 1) % 24); // lifePath を 0-23 の時刻に変換 const actions = [ `${ELEMENT_DATA[el]?.dir || '東'}の方角を向いて深呼吸を三回。${el}の気があなたを満たす。`, `${lifePath}という数字に縁のある時刻(${String(lifePathHour).padStart(2, '0')}時)に重要な決断を。`, `${KYUSEI_DIRS[kyusei-1]}方向にある場所で、大切な人と食事をすることで運気が上昇。`, `${zodiac.name}の特質を活かした行動が吉。今日は協調を心がけて。`, `今日の幸運色「${luckyItems.color}」を身につけることで、見えない加護を受け取る。`, ]; const action = actions[(td + lifePath) % actions.length]; return { score: normalizedScore, luckyItems, action, bio }; } // 旧6タイプシステム用の相性計算(互換性維持) function calcCompat(myType) { const typeData = SIX_TYPES[myType]; if (!typeData) return []; return typeData.compat.map(t => ({ type: t, ...SIX_TYPES[t] })); } // ═══════════════════════════════════════════════════════ // UI PRIMITIVES // ═══════════════════════════════════════════════════════ const HexSymbol = ({ size=60, color='#c8a84b', animate=false }) => { const pts = Array.from({length:6},(_,i)=>{ const a=(i*60-30)*Math.PI/180; return `${size/2+(size/2-4)*Math.cos(a)},${size/2+(size/2-4)*Math.sin(a)}`; }).join(' '); return ( ); }; const OrbitalRing = ({ size=120 }) => ( ); const GoldDivider = ({delay=0}) => (
); const ScoreDots = ({ score, max=10, color='#c8a84b' }) => (
{Array.from({length:max},(_,i)=>(
))}
); const BioChart = ({ data, delay=0 }) => { const W=280,H=80,mid=H/2,pts=40; const makePath=(period,color)=>{ const d=Array.from({length:pts},(_,i)=>{ const x=(i/(pts-1))*W; const dy=data.days-20+(i/(pts-1))*40; const yy=mid-Math.sin(2*Math.PI*dy/period)*(mid-8); return `${i===0?'M':'L'} ${x.toFixed(1)} ${yy.toFixed(1)}`; }).join(' '); return ; }; return (
{makePath(23,'#c8a84b')}{makePath(28,'#5a9e8a')}{makePath(33,'#8080c0')} TODAY
{[{label:'肉体',v:data.physical,color:'#c8a84b'},{label:'感情',v:data.emotional,color:'#5a9e8a'},{label:'知性',v:data.intellectual,color:'#8080c0'}].map(({label,v,color})=>(
{label}
0?1:0.4}}/>
{v>0.3?'↑高':v<-0.3?'↓低':'→中'}
))}
); }; // ═══ EXPANDABLE CARD ═══ const ReadingCard = ({ index, title, subtitle, system, icon, children, delay=0, accentColor='var(--gold)', defaultOpen=false }) => { const [open,setOpen] = React.useState(defaultOpen); return (
setOpen(!open)} style={{ background:'linear-gradient(135deg,rgba(16,16,28,0.95),rgba(10,10,18,0.98))', border:`1px solid ${open ? accentColor+'44':'var(--border)'}`, borderRadius:'2px',padding:'20px 24px',cursor:'pointer', transition:'all 0.4s ease', opacity:0,animation:`reveal-card 0.7s ease ${delay}s forwards`, boxShadow:open?`0 0 40px ${accentColor}18,inset 0 0 60px rgba(0,0,0,0.3)`:'0 4px 24px rgba(0,0,0,0.4)', position:'relative',overflow:'hidden', }}>
{icon}
命 {index} {system}

{title}

{subtitle&&{subtitle}}
{open&&(
{children}
)}
); }; // ═══ SECTION HEADER ═══ const SectionHeader = ({ num, label, sub, delay=0 }) => (
{num}
{label}
{sub&&
{sub}
}
); // ═══ LOADING ═══ const LoadingOracle = ({ onComplete }) => { const [step,setStep] = React.useState(0); const steps=['命盤を展開中…','星図を照合中…','運気波動を計算中…','六タイプを判定中…','マヤ暦と同調中…','六命を統合中…']; React.useEffect(()=>{ const timers=steps.map((_,i)=>setTimeout(()=>setStep(i+1),i*550+350)); const done=setTimeout(onComplete,steps.length*550+700); return()=>{timers.forEach(clearTimeout);clearTimeout(done);}; },[]); return (
{steps.map((s,i)=>(
i?1:0.2,transition:'opacity 0.4s ease'}}>
i?'var(--gold)':'var(--border)', flexShrink:0,transition:'background 0.3s ease',boxShadow:step>i?'0 0 8px var(--gold)':'none'}}/> {s}
))}
); }; // ═══ HERO SCREEN ═══ const HeroScreen = ({ onSubmit, isLoggedIn, userBirth, userTime, userBirthLatitude, userBirthLongitude, userName, onLoginClick, onRegisterClick, onLogoutClick, onMyPageClick, preselectedTargetDate, onSelectCelebrity }) => { const today = new Date(); const todayStr = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2,'0')}-${String(today.getDate()).padStart(2,'0')}`; const [birth, setBirth] = React.useState(isLoggedIn && userBirth ? userBirth : ''); const [time, setTime] = React.useState(isLoggedIn && userTime ? formatHourInputValue(userTime) : ''); const [birthplace, setBirthplace] = React.useState(isLoggedIn ? formatBirthLatLngValue(userBirthLatitude, userBirthLongitude) : ''); const [targetDate, setTargetDate] = React.useState(todayStr); const [err, setErr] = React.useState(''); const [focusedField, setFocused] = React.useState(null); const [featuredCelebrities, setFeaturedCelebrities] = React.useState([]); const [celebritySamples, setCelebritySamples] = React.useState([]); const formRef = React.useRef(null); React.useEffect(() => { if (isLoggedIn) { setBirth(userBirth || ''); setTime(formatHourInputValue(userTime || '')); setBirthplace(formatBirthLatLngValue(userBirthLatitude, userBirthLongitude)); } else { setBirth(''); setTime(''); setBirthplace(''); setTargetDate(todayStr); } }, [isLoggedIn, userBirth, userTime, userBirthLatitude, userBirthLongitude, todayStr]); React.useEffect(() => { if (isLoggedIn && preselectedTargetDate) { setTargetDate(preselectedTargetDate); } }, [isLoggedIn, preselectedTargetDate]); React.useEffect(() => { fetch('api/get_featured_celebrities.php?limit=4') .then(r => r.json()) .then(data => { if (data.status === 'success' && data.data) { setFeaturedCelebrities(data.data); } }) .catch(err => console.error('Featured celebrities API error:', err)); }, []); React.useEffect(() => { console.log('Fetching celebrity samples...'); fetch('./api/fortune.php?action=get_celebrity_samples') .then(r => r.json()) .then(data => { console.log('Celebrity samples response:', data); if (data.success && data.samples) { console.log('Setting celebrity samples:', data.samples); setCelebritySamples(data.samples); } else { console.warn('Invalid response structure:', data); } }) .catch(err => console.error('Celebrity samples API error:', err)); }, []); const handleSubmit = (e) => { e.preventDefault(); if (!birth) { setErr('生年月日を入力してください'); return; } setErr(''); const parsedBirthplace = parseBirthLatLngValue(birthplace); onSubmit(birth, time, isTimeMachine ? targetDate : null, { birthLatitude: parsedBirthplace.birthLatitude, birthLongitude: parsedBirthplace.birthLongitude, }); }; const isTimeMachine = targetDate && targetDate !== todayStr; const formatTargetDate = (str) => { if (!str) return ''; const d = new Date(str); return `${d.getFullYear()}年${d.getMonth()+1}月${d.getDate()}日`; }; const applySampleBirthDate = (sampleBirthDate) => { if (!sampleBirthDate) return; setBirth(sampleBirthDate); setErr(''); formRef.current?.scrollIntoView({ behavior:'smooth', block:'center' }); }; const applySampleEventDate = (sampleBirthDate, eventDate) => { if (sampleBirthDate) { setBirth(sampleBirthDate); } if (eventDate) { setTargetDate(eventDate); } setErr(''); formRef.current?.scrollIntoView({ behavior:'smooth', block:'center' }); }; const linkButtonStyle = { background:'none', border:'none', padding:0, color:'var(--gold)', textDecoration:'none', cursor:'pointer', font:'inherit', fontFamily:'inherit', }; const inputStyle = (id) => ({ width:'100%',maxWidth:'100%',padding:'12px 14px', background:'rgba(255,255,255,0.03)', border:`1px solid ${focusedField===id?'rgba(200,168,75,0.5)':'var(--border)'}`, color:'var(--text)',fontSize:'0.95rem',fontFamily:'var(--serif-en)', letterSpacing:'0.1em',outline:'none',textAlign:'center', transition:'border-color 0.3s ease',colorScheme:'dark',borderRadius:'2px', boxSizing:'border-box',minWidth:0, }); const [isSmallScreen, setIsSmallScreen] = React.useState(window.innerWidth <= 768); React.useEffect(() => { const handleResize = () => setIsSmallScreen(window.innerWidth <= 768); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); return (
{/* Header */} {isSmallScreen && (
{/* Auth buttons - ログイン時のみ表示 */} {isLoggedIn && (
)} {/* ユーザー名 - 2番目に表示 */} {isLoggedIn && (
{userName || 'ユーザー'} 様
)} {/* Brand - スマホはセンター寄せ、一番下 */}
Life Code Reading
六命解析
)} {/* PC Header */} {!isSmallScreen && (
{/* Auth buttons - ログイン時のみ表示 */} {isLoggedIn && (
)} {/* ユーザー名 - 下に表示 */} {isLoggedIn && (
{userName || 'ユーザー'} 様
)}
)}
Life Code Reading

六命解析

生年月日に刻まれた六つの命の傾向を
古典占術と宇宙のリズムで解き明かす

「あの人の転機」は、運気の流れと重なっていたのか?
六命解析では、著名人の人生イベントをバックテスト。起業、転職、受賞、記録達成などの出来事を、過去の運気スコアと照合しました。

{featuredCelebrities.length > 0 && (

占い精度を検証した著名人たち

{featuredCelebrities.map((celeb, idx) => (
onSelectCelebrity && onSelectCelebrity(celeb)} onMouseEnter={e => {e.currentTarget.style.borderColor='var(--gold)';e.currentTarget.style.background='rgba(200,168,75,0.05)';}} onMouseLeave={e => {e.currentTarget.style.borderColor='var(--border)';e.currentTarget.style.background='rgba(255,255,255,0.01)';}}>
{celeb.name} = 80 ? '#c8a84b' : '#5a9e8a', fontWeight:600 }}> {Math.round(celeb.accuracy)}%
{celeb.occupation_display || celeb.occupation || '肩書き情報なし'} • {celeb.matched_count}/{celeb.total_tests}件一致
{celeb.sample_events.length > 0 && (
例:{celeb.sample_events[0].title} {getTransitionBucketMeta(celeb.sample_events[0]) && ( {getTransitionBucketMeta(celeb.sample_events[0]).label} )}
)}
))}
)} {/* 有名人診断サンプル(複数著名人対応) */} {celebritySamples.length > 0 && (

有名人の診断例

{celebritySamples.map((sample, idx) => { return (

{sample.name}

生年月日:
{sample.nine_star_name || '星不明'}
的中率
{sample.accuracy || 0}%
テスト
{sample.event_count || 0}件
{sample.events && sample.events.length > 0 && (

検証イベント({sample.events.length}件表示)

{sample.events.map((evt, i) => (
{evt.title} {evt.matched}
{evt.polarity === 'positive' ? ' (好調)' : ' (低迷)'}
))}
)}
); })}
)}
{['四柱推命','数秘術','九星気学','12星座','バイオリズム','マヤ暦'].map(s=>( {s} ))}
setBirth(e.target.value)} onFocus={()=>setFocused('date')} onBlur={()=>setFocused(null)} min="1900-01-01" max="2025-12-31" style={inputStyle('date')}/>
setTime(e.target.value)} onFocus={()=>setFocused('time')} onBlur={()=>setFocused(null)} style={inputStyle('time')} placeholder="12" />
分は不要です。保存時は 00 分 00 秒として扱います
{isLoggedIn ? `ログイン中の生年月日: ${userBirth || '未登録'}` : 'ログイン中でなくても入力できます'}
setBirthplace(e.target.value)} onFocus={()=>setFocused('birthplace')} onBlur={()=>setFocused(null)} style={inputStyle('birthplace')} placeholder="35.681236, 139.767125" />
緯度と経度を 1 行で入力できます。Google の座標をそのまま貼り付けても構いません。
{/* ⏳ タイムマシン */}
{isTimeMachine && ( )}
setTargetDate(e.target.value)} onFocus={()=>setFocused('target')} onBlur={()=>setFocused(null)} min="1900-01-01" max="2099-12-31" style={{ ...inputStyle('target'), border:`1px solid ${isTimeMachine?'rgba(200,168,75,0.5)':focusedField==='target'?'rgba(200,168,75,0.4)':'var(--border)'}`, background: isTimeMachine?'rgba(200,168,75,0.06)':'rgba(255,255,255,0.03)', color: isTimeMachine?'var(--gold-light)':'var(--text)', }}/> {isTimeMachine && (
{formatTargetDate(targetDate)} の運勢を解析します
)} {!isTimeMachine && (
日付を変えると過去・未来の運勢を確認できます
)}
{err&&

{err}

} {/* ログイン・会員登録 */} {!isLoggedIn && (
)}
{['一','二','三','四','五','六'].map((n,i)=>( {n}命 ))}
{/* Footer */}
); }; // ═══════════════════════════════════════════════════════ // CELEBRITY DETAIL SCREEN // ═══════════════════════════════════════════════════════ const CelebrityDetailScreen = ({ celebrity, onBack }) => { const [expandedEvent, setExpandedEvent] = React.useState(0); const getMonthPhase = (score) => { const val = score * 5; if (val >= 85) return { label: '勢いの月', desc: 'パワー全開。積極的に行動すれば大きな成果が期待できる月。新しいことに挑戦するのに最適です。' }; if (val >= 70) return { label: '広がる月', desc: '活動や人間関係が広がる月。新しい出会いやチャンスが増え、視野も広がります。' }; if (val >= 50) return { label: '整える月', desc: '現在の状況を整理し、基盤を強化する月。細部に注意を払い、質を高める工夫が効果的です。' }; if (val >= 30) return { label: '見直す月', desc: '立ち止まって考える月。これまでのやり方を見直し、必要な調整を行うのに適しています。' }; return { label: '休める月', desc: '心身を休める月。疲れを癒し、内省の時間を大切にしましょう。無理は避けて。' }; }; const getYearPhase = (score) => { const val = score * 5; if (val >= 85) return { label: '飛躍期', desc: '大きく動く・挑戦する年。新しいプロジェクトや目標達成に最適な時期。果敢に行動することで大きな成果が期待できます。' }; if (val >= 70) return { label: '拡大期', desc: '活動や影響力を広げるのに良い時期。周囲との協力も進みやすく、成長が加速します。新しい出会いやチャンスに恵まれやすい。' }; if (val >= 50) return { label: '安定期', desc: '基盤を固める時期。急激な変化よりも、現在の資産や関係を大切にしながら、着実に前に進むことが重要。質を高める工夫や改善に注力すると効果的。' }; if (val >= 30) return { label: '調整期', desc: '見直し・整理・休息が必要な年。無理に進めるより、これまでの成果を整理し、次のステップへの準備を進めるべき時期。' }; return { label: '準備期', desc: '無理に広げず、内側を整える年。基礎を強化し、自分自身と向き合う時間を大切に。次のステップへのエネルギー蓄積期。' }; }; const calcForEvent = (eventDateStr) => { const dt = new Date(eventDateStr); const y = dt.getFullYear(); const m = dt.getMonth() + 1; const d = dt.getDate(); const yp = getYearPillar(y); const mp = getMonthPillar(y, m); const dp = getDayPillar(y, m, d); const dominant = getDominantEl(y, m, d, null); const kyusei = calcKyusei(y, m); const zodiac = getZodiac(m, d); return { yp, mp, dp, dominant, kyusei, zodiac, y, m, d }; }; const getMatchReason = (evt, monthScore) => { const monthPhase = getMonthPhase(monthScore); const score100 = Math.round(monthScore * 5); const bucketMeta = getTransitionBucketMeta(evt); if (bucketMeta?.tone === 'exclude') { return bucketMeta.note; } if (bucketMeta?.tone === 'high') { return `補助判定: ${bucketMeta.label}(${bucketMeta.note})`; } const polarity = evt?.polarity; const eventType = evt?.event_type; if (polarity === 'positive' && eventType === 'career') { if (monthScore >= 14) return `${monthPhase.label}(${score100}点)で始動期にマッチ:キャリアの新展開に適した時期`; return `点数が低く、キャリア開始には不向きな時期でした`; } else if (polarity === 'positive' && eventType === 'achievement') { if (monthScore >= 9) return `${monthPhase.label}(${score100}点)で成就期にマッチ:成果確定に適した時期`; return `点数が低く、成果確定には不向きな時期でした`; } else if (polarity === 'negative') { if (monthScore >= 5 && monthScore < 12) return `${monthPhase.label}(${score100}点)で調整期にマッチ:変化・調整に適した時期`; return `調整期(25-60点)から外れており、ネガティブなイベントの時期としては不向きでした`; } if (bucketMeta?.tone === 'medium') { return `補助判定: ${bucketMeta.label}(${bucketMeta.note})`; } if (bucketMeta?.tone === 'low') { return `補助判定: ${bucketMeta.label}(${bucketMeta.note})`; } return `マッチ理由を分析中...`; }; return (

{celebrity.name}

生年月日
{celebrity.birth_date}
職業
{celebrity.occupation_display || celebrity.occupation || '肩書き情報なし'}
バックテスト精度
{Math.round(celebrity.accuracy)}%
マッチ実績
{celebrity.matched_count}/{celebrity.total_tests}件

一致イベント解析

{celebrity.sample_events.map((evt, idx) => { const analysis = calcForEvent(evt.date); const isOpen = expandedEvent === idx; return (
{isOpen && (
年柱: {analysis.yp.stem}{analysis.yp.branch} ({analysis.yp.el})
月柱: {analysis.mp.stem}{analysis.mp.branch} ({analysis.mp.el})
日柱: {analysis.dp.stem}{analysis.dp.branch} ({analysis.dp.el})
主要五行: {analysis.dominant}
星: {KYUSEI_NAMES[analysis.kyusei - 1]}
方位: {KYUSEI_DIRS[analysis.kyusei - 1]}
月運
{getMonthPhase(evt.monthly_score).label}
{getMonthPhase(evt.monthly_score).desc}
年運
{getYearPhase(evt.yearly_score).label}
{getYearPhase(evt.yearly_score).desc}
{getMatchReason(evt, evt.monthly_score)}
)}
); })}
著名人バックテストデータ: {celebrity.total_tests}件
); }; // ═══════════════════════════════════════════════════════ // RESULTS SCREEN // ═══════════════════════════════════════════════════════ const ResultsScreen = ({ birthStr, timeStr, birthLatitude, birthLongitude, targetDateStr, onReset, tweaks, isLoggedIn, userName, onLoginClick, onRegisterClick, onLogoutClick, onMyPageClick, diagnosisId, typePatterns, userStatus = '有料会員' }) => { console.log('ResultsScreen userStatus:', userStatus); const locationCorrection = adjustDateTimeByLongitude(birthStr, timeStr, birthLongitude); const dt = new Date(birthStr); const y=dt.getFullYear(), m=dt.getMonth()+1, d=dt.getDate(); const hourSource = timeStr; const hour = hourSource ? parseInt(String(hourSource).split(':')[0]) : null; // タイムマシン対応: targetDateStrがあればそちらを使う const targetDt = targetDateStr ? new Date(targetDateStr) : new Date(); const ty=targetDt.getFullYear(), tm=targetDt.getMonth()+1, td=targetDt.getDate(); const isTimeMachine = targetDateStr && targetDateStr !== (() => { const t=new Date(); return `${t.getFullYear()}-${String(t.getMonth()+1).padStart(2,'0')}-${String(t.getDate()).padStart(2,'0')}`; })(); // All calculations const yp = getYearPillar(y); const mp = getMonthPillar(y,m); const dp = getDayPillar(y,m,d); const hp = hour !== null ? getHourPillar(hour) : null; const domEl = getDominantEl(y,m,d,hour); const lifePath = calcLifePath(y,m,d); const lpData = LIFEPATH_DATA[lifePath] || LIFEPATH_DATA[1]; const kyusei = calcKyusei(y,m); const zodiac = getZodiac(m,d); const bio = calcBio(y,m,d,ty,tm,td); const maya = calcMaya(y,m,d); const myType = determineType(lifePath, domEl, kyusei, zodiac); const typeInfo = SIX_TYPES[myType]; const typePattern = typePatterns && typePatterns.types ? typePatterns.types.find(t => t.type_name === myType) : null; // 層1:OS診断(新仕様) const osResult = calcOSScores(lifePath, domEl, kyusei, zodiac, maya); const mainOSMeta = OS_META[osResult.mainOS]; const subOSMeta = OS_META[osResult.subOS]; // 層2・層3:運気診断 const yearF = calcYearFortune(ty, y, m, d, domEl, lifePath, kyusei, zodiac, maya); const monthF = calcMonthFortune(ty, tm, y, m, d, domEl, lifePath, kyusei, zodiac, maya); const dayF = calcDayFortune(y, m, d, ty, tm, td, domEl, lifePath, kyusei, zodiac, maya); const compat = calcCompat(myType); const age = ty-y-(tm= 0 ? '+' : ''}${locationCorrection.offsetMinutes}分` : null; // OSスコアバーグラフコンポーネント const OSScoreBar = ({ osName, score, max=20 }) => (
{osName} {Math.round(score)}/20
); const accentColors = ['#c8a84b','#5a9e8a','#8080c0','#c06080','#5080a0','#a06030']; const fsScale = tweaks&&tweaks.fontSize==='large'?1.15:tweaks&&tweaks.fontSize==='small'?0.88:1; // Responsive detection const [isSmallScreen, setIsSmallScreen] = React.useState(window.innerWidth < 768); React.useEffect(() => { const handleResize = () => setIsSmallScreen(window.innerWidth < 768); window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, []); // Tabs for result sections const [activeTab, setActiveTab] = React.useState('综合'); const tabs = ['综合','運勢','今日','相性']; const [recommendation, setRecommendation] = React.useState(''); const [shareMessage, setShareMessage] = React.useState(''); // 年運フェーズ(0-20スコア → 0-100に換算) const getYearPhase = (score) => { const val = score * 5; if (val >= 85) return { label: '飛躍期', desc: '大きく動く・挑戦する年。新しいプロジェクトや目標達成に最適な時期。果敢に行動することで大きな成果が期待できます。' }; if (val >= 70) return { label: '拡大期', desc: '活動や影響力を広げるのに良い時期。周囲との協力も進みやすく、成長が加速します。新しい出会いやチャンスに恵まれやすい。' }; if (val >= 50) return { label: '安定期', desc: '基盤を固める時期。急激な変化よりも、現在の資産や関係を大切にしながら、着実に前に進むことが重要。質を高める工夫や改善に注力すると効果的。' }; if (val >= 30) return { label: '調整期', desc: '見直し・整理・休息が必要な年。無理に進めるより、これまでの成果を整理し、次のステップへの準備を進めるべき時期。' }; return { label: '準備期', desc: '無理に広げず、内側を整える年。基礎を強化し、自分自身と向き合う時間を大切に。次のステップへのエネルギー蓄積期。' }; }; // 月運フェーズ(0-20スコア → 0-100に換算) const getMonthPhase = (score) => { const val = score * 5; if (val >= 85) return { label: '勢いの月', desc: 'パワー全開。積極的に行動すれば大きな成果が期待できる月。新しいことに挑戦するのに最適です。' }; if (val >= 70) return { label: '広がる月', desc: '活動や人間関係が広がる月。新しい出会いやチャンスが増え、視野も広がります。' }; if (val >= 50) return { label: '整える月', desc: '現在の状況を整理し、基盤を強化する月。細部に注意を払い、質を高める工夫が効果的です。' }; if (val >= 30) return { label: '見直す月', desc: '立ち止まって考える月。これまでのやり方を見直し、必要な調整を行うのに適しています。' }; return { label: '休める月', desc: '心身を休める月。疲れを癒し、内省の時間を大切にしましょう。無理は避けて。' }; }; // 今日のフェーズ(0-20スコア → 0-100に換算) const getDayPhase = (score) => { const val = score * 5; if (val >= 85) return { label: '攻める日', desc: 'エネルギー満タンの日。重要な決断や大きなチャレンジに挑戦するのに最適。積極的に行動を。' }; if (val >= 70) return { label: '動く日', desc: '行動力が高い日。新しいことを始めたり、前に進めたい案件に取り組むのに向いています。' }; if (val >= 50) return { label: '整える日', desc: '細かな調整や整理整頓に向いた日。細部に目を配り、質を高める作業が効果的。' }; if (val >= 30) return { label: '慎重な日', desc: '慎重さが必要な日。大きな決断は避け、冷静に判断することが重要。見直しや準備作業に最適。' }; return { label: '休む日', desc: '休息が必要な日。無理をせず、ゆっくりと過ごしましょう。心身のリセットが大切。' }; }; // キャリア指針(月運と年運から人生決断への適性を判定) const getCareerAdvice = (monthScore, yearScore) => { const combined = Math.round(monthScore * 0.6 + yearScore * 0.4); const signal = combined >= 15 ? { label: '追い風', tone: 'high', note: '強く出た結果です。積極的に進めやすい時期です。' } : combined >= 11 ? { label: 'やや出ています', tone: 'middle', note: '弱いシグナルはあります。強くは出ていないので参考扱いです。' } : combined >= 6 ? { label: '見直し', tone: 'warn', note: '今は大きく動くより、見直して整える時期です。' } : { label: '保留', tone: 'none', note: 'この指標では強い判断を出していません。' }; if (combined >= 15) return { level: '最高機', indicator: '🚀', headline: '人生の決断に最適なタイミング', signal, advice: '転職・昇進交渉・起業・重要プロジェクト開始などを前向きに検討しやすい月です。バックテストでは、こうしたテーマと重なりやすい傾向が見られました。大きな決断を進める際の参考として使ってください。', actions: ['転職・求人応募', '昇進交渉・キャリア相談', '起業・副業スタート', '重要なプロジェクト立案', '独立・新事業開始'], caution: null, }; if (combined >= 11) return { level: '好機', indicator: '⬆️', headline: 'キャリアを展開させるのに向いた時期', signal, advice: '新スキル習得・ネットワーク拡大・新プロジェクト参加など、キャリアを段階的に広げやすい月です。動きを小さく始めると、その後の広がりにつながりやすい傾向があります。', actions: ['新スキル習得・資格取得', 'キャリア面談・相談', 'ネットワーキング活動', '新プロジェクト参加', '業務範囲の拡大提案'], caution: null, }; if (combined >= 6) return { level: '調整', indicator: '→', headline: '現状を深め、次に備える時期', signal, advice: '大きく動くより、現在の職務を深めながら準備を整えたい時期です。見直しや改善を進めることで、次の動きにつながりやすくなります。', actions: ['現在のスキルをより深める', '業務の質を高める工夫', '長期キャリアプランの再確認', '人脈の整理・関係構築', '自分の市場価値の分析'], caution: '大きな決断は次の好機まで待つのが賢明', }; return { level: '休養', indicator: '⏸', headline: '充電と内省の時期', signal, advice: '無理に動くより、休息や整理を優先したい時期です。今は強い判断を置かず、次の動きに向けて整えることを重視してください。', actions: ['自己分析・強み再発見', '学習・資格取得の準備', '体調管理・ストレス軽減', '人間関係の整理・修復', '将来のキャリアについて考える時間'], caution: '転職・起業・大きなキャリア決断は、今は急がない方がよい時期です', }; }; const shareResult = async (channel) => { if (!diagnosisId) { setShareMessage('共有URLを作るために、先に診断を保存してください'); return; } if (!recommendation) { setShareMessage('先に当たり度を選択してください'); return; } try { const res = await fetch('./api/diagnoses.php?action=create_share_link', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ diagnosis_id: diagnosisId, recommendation, channel }), }); const data = await res.json(); if (!data.success || !data.share_url) { setShareMessage(data.error || '共有URLの作成に失敗しました'); return; } const url = data.share_url; if (channel === 'sns') { const text = encodeURIComponent('この診断、かなり面白かった!あなたもやってみて'); window.open(`https://twitter.com/intent/tweet?text=${text}&url=${encodeURIComponent(url)}`, '_blank'); } else if (channel === 'email') { location.href = `mailto:?subject=${encodeURIComponent('この診断をシェアします')}&body=${encodeURIComponent(url)}`; } else { await navigator.clipboard.writeText(url); } setShareMessage('共有URLを作成しました'); } catch { setShareMessage('通信エラーで共有に失敗しました'); } }; return (
{/* ── Top nav: ブランド + ログイン/会員登録 ── */}
{/* ユーザー名 - ログイン時のみ表示 */} {isLoggedIn && (
{/* マイページ・ログアウト */}
{/* ユーザー名 */} {userName || 'ユーザー'} 様
)} {/* Brand - センター寄せ */}
Life Code Reading
六命解析
{/* ── Header ── */}
LIFE CODE READING
{y}年 {m}月 {d}日{hour!==null?` ${hour}時台`:''}生まれ
診断時の年齢 {age} 歳 · {ty}年{tm}月{td}日 解析 {isTimeMachine && ⏳ タイムマシン}
{/* ══════════════════════════════════════════ 1. 総合診断 - 6つの六命タイプ + 性格詳細 ══════════════════════════════════════════ */} {/* 六命タイプ表示 */}
{/* 上位3つのタイプ:主命・副命・内なる傾向(横並び) */} {(() => { const sorted = Object.entries(osResult.scores).sort((a,b)=>b[1]-a[1]).slice(0,3); const getTypeName = (osName) => OS_META[osName]?.jpName?.split(' ')[1] || ''; const getTypeData = (osName) => typePatterns?.types?.find(t => t.type_name === getTypeName(osName)); const main = { osName: sorted[0][0], label: '主命タイプ', score: sorted[0][1] }; const sub = { osName: sorted[1][0], label: '副命タイプ', score: sorted[1][1] }; const inner = { osName: sorted[2][0], label: '内なる傾向', score: sorted[2][1] }; const mainData = getTypeData(main.osName); return ( <> {/* 3つのタイプ(PCは横並び、スマホは縦) */}
{[main, sub, inner].map((item, idx) => { const typeData = getTypeData(item.osName); return (
{item.label}
{OS_META[item.osName]?.emoji} {OS_META[item.osName]?.jpName}
{typeData?.catchphrase && (
{typeData.catchphrase}
)}
); })}
{/* 詳細分類と解説 */}
🎯 詳細分類:{OS_META[main.osName]?.detailType}
{main.osName === 'Drive' && '先行突破型とは、誰よりも早く行動し、新しい可能性を切り開くタイプです。物事を0から1に変える能力があります。'} {main.osName === 'Care' && '信頼構築型とは、深い共感力で人間関係を築き、相手の心に寄り添うタイプです。人を育てることを通じて自分の価値を感じます。'} {main.osName === 'Vision' && '世界観創造型とは、独自の視点で新しい世界を表現するタイプです。芸術、言語、音楽などを通じて、見えない何かを形にします。'} {main.osName === 'Logic' && '本質解析型とは、表面的な情報を見抜き、物事の構造や真実を深く理解するタイプです。知的探求が生きがいです。'} {main.osName === 'Quest' && '変化適応型とは、あらゆる環境に適応し、変化を楽しむタイプです。固定観念を打ち破り、新しいパラダイムをもたらします。'} {main.osName === 'Balance' && '調和設計型とは、異なるものを統合し、全体のバランスを整えるタイプです。相反する要素を組み合わせ、新しい調和を生み出します。'}
{/* 総合解釈 */}

あなたは、{getTypeData(main.osName)?.catchphrase?.slice(0,-2)}、その場全体がうまく回るように自然と調整していくタイプです。

ただ優しいだけではなく、相手との距離感、言葉の温度、空気の変化を細かく感じ取る力があります。

さらに、{OS_META[inner.osName]?.jpName}の傾向もあるため、人を支えるだけでなく、自分の感性や考えを形にしたい気持ちも持っています。

); })()}
{/* 性格詳細セクション */}
{/* 詳細情報グリッド */}
{/* 性格の詳しい側面 */}

💎 あなたの特性

✨ 強み
{typePattern?.strength}
💫 注意点
{typePattern?.pitfall}
👥 周囲からの見え方
{typePattern?.seen_by_others}
❤️ 本当の内面
{typePattern?.true_inner}
{/* その他のセクション */}

🧭 人生テーマ

{typePattern?.life_pattern}

💕 恋愛・結婚

{typePattern?.love || typeInfo?.love}

💼 仕事・金運

{typePattern?.work || typeInfo?.work}

🤝 人間関係

{typePattern?.human_relationship}

{locationCorrectionLabel && (
{locationCorrectionLabel} {locationCorrection.day_shift ? ` / 日付補正: ${locationCorrection.day_shift > 0 ? '+' : ''}${locationCorrection.day_shift}日` : ''}
)} {/* ══════════════════════════════════════════ 2. 運気診断 (年運・月運) ══════════════════════════════════════════ */}
{/* 年運 */}
{isTimeMachine ? 'その年' : '今年'}の年運
{yearF.emoji}
{yearF.phase}
{yearF.desc}
{getYearPhase(yearF.score).label}
{getYearPhase(yearF.score).desc}
{/* 月運 */}
{isTimeMachine ? 'その月' : '今月'}の月運
{monthF.emoji}
{monthF.phase}
{monthF.desc}
{getMonthPhase(monthF.score).label}
{getMonthPhase(monthF.score).desc}
{/* ══════════════════════════════════════════ 3. キャリア指針 ══════════════════════════════════════════ */}
{(() => { const ca = getCareerAdvice(monthF.score, yearF.score); const combined = Math.round(monthF.score * 0.6 + yearF.score * 0.4); const levelColor = combined >= 15 ? '#c8a84b' : combined >= 11 ? '#5a9e8a' : combined >= 6 ? 'var(--text-dim)' : '#8a8a9e'; return (
{/* ヘッダー: インジケーター + 月フェーズラベル + ヘッドライン */}
{ca.indicator}
{getMonthPhase(monthF.score).label}
{ca.headline}
{ca.signal && (
{ca.signal.label} {ca.signal.note}
)} {/* アドバイス本文 */}
{ca.advice}
{/* 推奨アクション */}
推奨アクション
{ca.actions.map((action, i) => ( {action} ))}
{/* 注意事項(低スコア時のみ) */} {ca.caution && (
⚠️ {ca.caution}
)} {/* 出典 */}
※ 運気分析(バックテストデータ)とは、有名人の生年月日とイベント発生日をもとに、六命解析の結果との関係を見た参考指標です。座標がある場合は経度補正を反映しますが、個別の結果を断定するものではありません。
{/* 向いている職業と解説 */} {(() => { const sorted = Object.entries(osResult.scores).sort((a,b)=>b[1]-a[1]).slice(0,2); const mainOSName = sorted[0]?.[0]; const subOSName = sorted[1]?.[0]; const careerInfo = mainOSName ? getOSCareerInfo(mainOSName, subOSName) : null; let careerReason = ''; if (mainOSName === 'Drive') careerReason = '先行突破の推進力と'; else if (mainOSName === 'Care') careerReason = '共感力と受容力の'; else if (mainOSName === 'Vision') careerReason = 'クリエイティブな表現力と'; else if (mainOSName === 'Logic') careerReason = '分析力と洞察力の'; else if (mainOSName === 'Quest') careerReason = '適応力と自由さの'; else if (mainOSName === 'Balance') careerReason = '調和とバランス感覚の'; let subTypeDesc = ''; if (subOSName === 'Drive') subTypeDesc = 'リーダーシップの側面'; else if (subOSName === 'Care') subTypeDesc = '人を育てる側面'; else if (subOSName === 'Vision') subTypeDesc = 'クリエイティブな側面'; else if (subOSName === 'Logic') subTypeDesc = '分析的な側面'; else if (subOSName === 'Quest') subTypeDesc = '自由さと適応力'; else if (subOSName === 'Balance') subTypeDesc = 'バランス感覚'; return careerInfo ? (
💼 向いている職業
{careerInfo.career}
成功確率 {careerInfo.successRate}%
主命タイプ「{OS_META[mainOSName]?.jpName}」の{careerReason}副命タイプ「{OS_META[subOSName]?.jpName}」の{subTypeDesc}が組み合わさったあなたには、この職業が向いています。バックテストデータから、この職種でのキャリア転換の成功確率は{careerInfo.successRate}%です。
) : null; })()}
); })()}
{/* ══════════════════════════════════════════ 4. 今日の占い ══════════════════════════════════════════ */}
{/* Day score - 2行レイアウト */}
{/* 1行目:フェーズ説明 */}
{getDayPhase(dayF.score).label}
{getDayPhase(dayF.score).desc}
{/* 2行目:ラッキーアイテム */}
ラッキーカラー
{dayF.luckyItems.color}
ラッキーナンバー
{dayF.luckyItems.number}
吉方位
{dayF.luckyItems.direction}
開運アイテム
{dayF.luckyItems.item}
{/* Action banner */}
開運アクション

✦ {dayF.action}

{/* Biorhythm mini */}
今日のバイオリズム
📊 グラフの見方:
💪 身体リズム — エネルギー、体力、活動性
❤️ 感情リズム — 気分、感性、対人関係
🧠 知性リズム — 集中力、判断力、学習力
↑ 上向き = 上昇期(好調)
↓ 下向き = 下降期(調整が必要)
{/* ══════════════════════════════════════════ 会員登録のすすめ ══════════════════════════════════════════ */} {!isLoggedIn && (
会員登録のすすめ
明日の運勢も受け取るために「会員登録」しませんか?
{[ {icon:'⏳',text:'タイムマシン機能で「あの日の運勢」を振り返る'}, {icon:'🔍',text:'過去の選択と運気の流れを見る'}, {icon:'📅',text:'大事な日のライフコードを確認する'}, {icon:'💫',text:'誕生日・転職日・告白日・別れた日・入社日を振り返る'}, ].map((item, idx) => (
{item.icon} {item.text}
))}
)} {/* ══════════════════════════════════════════ 5. 六命解析について ══════════════════════════════════════════ */}
{/* 六命解析の説明 */}

六命解析では、生年月日から6つの傾向スコアを算出し、
最も強く出たものを「主命タイプ」、
次に強く出たものを「副命タイプ」として読み解きます。

主命タイプは、あなたの中心にある性質。
副命タイプは、あなたの中に混ざるもうひとつの傾向です。

内なる傾向は、ふだんは目立ちにくいけれど、
状況によって表れやすい深い部分の性質です。

この2つを組み合わせることで、
単なる6分類ではなく、より立体的にあなたの本質を読み解きます。

{/* 6つの六命タイプ説明 */}
6つの六命タイプとは
{typePatterns?.types?.slice(0,6).map((type, idx) => (
{type.type_emoji} {type.type_name}
{type.catchphrase}
))}
{/* ══════════════════════════════════════════ 6. 個別占い (6システム詳細) - 有料会員のみ表示 ══════════════════════════════════════════ */} {userStatus === '有料会員' && ( <>
{/* 四柱推命 */}

{ELEMENT_DATA[domEl]?.desc}あなたの本質的なエネルギーは{domEl}気に支配されています。

{[ {label:'年柱',stem:yp.stem,branch:yp.branch,sub:yp.animal+'年'}, {label:'月柱',stem:mp.stem,branch:mp.branch,sub:mp.el+'気'}, {label:'日柱',stem:dp.stem,branch:dp.branch,sub:dp.el+'気'}, ...(hp?[{label:'時柱',stem:'',branch:hp.branch,sub:hp.el+'気'}]:[]), ].map(({label,stem,branch,sub})=>(
{label}
{stem&&
{stem}
}
{branch}
{sub}
))}
{/* 数秘術 */}
{lifePath}
{lpData.title}
{lpData.keywords.map(k=>( {k} ))}
{/* 九星気学 */}
{kyusei}

{KYUSEI_NAMES[kyusei-1]}の気質。恵方{KYUSEI_DIRS[kyusei-1]}を意識することで運気が整います。

{/* 12星座 */}

{zodiac.name}に生まれたあなたは、星々の配置が織りなす独自の運命の糸を持ちます。 本来の自分を信じて行動することで、隠れた才能が開花します。

{/* バイオリズム */}

誕生から {bio.days.toLocaleString()} 日。 肉体{bio.physical>0?'上昇':'調整'}期 · 感情{bio.emotional>0?'高揚':'内省'}期 · 知性{bio.intellectual>0?'活性':'蓄積'}期。

{/* マヤ暦 */}
KIN {maya.kinNum} /260

マヤ暦KIN {maya.kinNum}「{maya.kin}」。 {maya.tone}の波動があなたの使命を定めます。

)} {isLoggedIn && (
この診断、誰かに教えたいくらい当たっていましたか?
{[ ['かなり当たっている','spark'], ['一部当たっている','partial'], ['まだわからない','unknown'], ['あまり当たっていない','low'], ].map(([label, value]) => ( ))}
{shareMessage}
)} {/* Reset */} {!isLoggedIn && (
)} {isLoggedIn && (
)} {/* Footer */}
); }; // ═══ TWEAKS ═══ // ═══ LOGIN MODAL ═══ const LoginModal = ({ isOpen, onClose, initialTab='login', onLoginSuccess }) => { const [tab, setTab] = React.useState(initialTab); const [email, setEmail] = React.useState(''); const [password, setPassword] = React.useState(''); const [username, setUsername] = React.useState(''); const [birthDate, setBirthDate] = React.useState(''); const [birthTime, setBirthTime] = React.useState(''); const [birthplace, setBirthplace] = React.useState(''); const [gender, setGender] = React.useState(''); const [region, setRegion] = React.useState(''); const [occupationCategory, setOccupationCategory] = React.useState(''); const [interestCategories, setInterestCategories] = React.useState([]); const [error, setError] = React.useState(''); const [pendingVerificationEmail, setPendingVerificationEmail] = React.useState(''); const [loading, setLoading] = React.useState(false); const [resetEmail, setResetEmail] = React.useState(''); const [resetStep, setResetStep] = React.useState('email'); const [resetCode, setResetCode] = React.useState(''); const [newPassword, setNewPassword] = React.useState(''); React.useEffect(() => { if (isOpen) { setTab(initialTab); setError(''); } }, [isOpen, initialTab]); if (!isOpen) return null; const INTEREST_OPTIONS = [ '恋愛','結婚・パートナー','仕事・キャリア','お金','人間関係','家族', '自己理解','将来・人生','健康・コンディション','学業・試験','引っ越し・環境変化','特にない','その他' ]; const OCCUPATION_OPTIONS = [ '会社員・公務員','経営者・役員','個人事業主・フリーランス','専門職','クリエイター', '販売・接客・サービス','営業・マーケティング','IT・エンジニア','教育・福祉','学生', '主婦・主夫','無職・休職中','その他','回答しない' ]; const inputSt = { width:'100%', padding:'11px 14px', background:'rgba(255,255,255,0.04)', border:'1px solid rgba(200,168,75,0.25)', color:'var(--text)', fontSize:'0.85rem', fontFamily:'var(--serif-jp)', outline:'none', borderRadius:'2px', boxSizing:'border-box', colorScheme:'dark', }; const handleLogin = async (e) => { e.preventDefault(); if (!email || !password) { setError('メールアドレスとパスワードを入力してください'); return; } setLoading(true); setError(''); setPendingVerificationEmail(''); try { const fd = new FormData(); fd.append('action','login'); fd.append('email',email); fd.append('password',password); const res = await fetch('./api/auth.php', { method:'POST', body:fd, credentials:'include' }); const raw = await res.text(); let data = null; try { data = JSON.parse(raw); } catch { data = { error: raw || `HTTP ${res.status}` }; } if (data.success) { onLoginSuccess(data.user); onClose(); } else { setError(data.error || 'ログインに失敗しました'); if (data.code === 'EMAIL_NOT_VERIFIED') { setPendingVerificationEmail(data.email || email); } } } catch (err) { setError('接続エラー: ' + (err?.message || 'unknown')); } finally { setLoading(false); } }; const handleRegister = async (e) => { e.preventDefault(); if (!email || !password || !username || !birthDate) { setError('全ての必須項目を入力してください'); return; } if (interestCategories.length === 0) { setError('今知りたいテーマを1つ以上選択してください'); return; } if (password.length < 6) { setError('パスワードは6文字以上必要です'); return; } setLoading(true); setError(''); setPendingVerificationEmail(''); try { const fd = new FormData(); fd.append('action','register'); fd.append('email',email); fd.append('password',password); fd.append('username',username); fd.append('birth_date',birthDate); fd.append('birth_time', birthTime); const parsedBirthplace = parseBirthLatLngValue(birthplace); fd.append('birth_latitude', parsedBirthplace.birthLatitude ?? ''); fd.append('birth_longitude', parsedBirthplace.birthLongitude ?? ''); fd.append('gender', gender); fd.append('region', region); fd.append('occupation_category', occupationCategory); fd.append('interest_categories', JSON.stringify(interestCategories)); const res = await fetch('./api/auth.php', { method:'POST', body:fd, credentials:'include' }); const raw = await res.text(); let data = null; try { data = JSON.parse(raw); } catch { data = { error: raw || `HTTP ${res.status}` }; } if (data.success) { if (data.requires_email_verification) { const localHint = data.verification_url ? `(ローカル確認URL: ${data.verification_url})` : ''; setError(`登録完了: 認証メールを送信しました。メール内のURLを開いてからログインしてください。${localHint}`); setPendingVerificationEmail(email); setTab('login'); setPassword(''); return; } onLoginSuccess(data.user); onClose(); } else { setError(data.error || '登録に失敗しました'); } } catch (err) { setError('接続エラー: ' + (err?.message || 'unknown')); } finally { setLoading(false); } }; const handleResendVerification = async () => { if (!pendingVerificationEmail) { setError('再送対象のメールアドレスがありません'); return; } setLoading(true); setError(''); try { const fd = new FormData(); fd.append('action', 'resend_verification'); fd.append('email', pendingVerificationEmail); const res = await fetch('./api/auth.php', { method: 'POST', body: fd, credentials: 'include' }); const raw = await res.text(); let data = null; try { data = JSON.parse(raw); } catch { data = { error: raw || `HTTP ${res.status}` }; } if (data.success) { const localHint = data.verification_url ? ` ローカル確認URL: ${data.verification_url}` : ''; setError(`認証メールを再送しました。受信ボックスをご確認ください。${localHint}`); } else { setError(data.error || '認証メールの再送に失敗しました'); } } catch (err) { setError('接続エラー: ' + (err?.message || 'unknown')); } finally { setLoading(false); } }; const handleForgot = async (e) => { e.preventDefault(); if (!resetEmail) { setError('メールアドレスを入力してください'); return; } setLoading(true); setError(''); try { const fd = new FormData(); fd.append('action','forgot_password'); fd.append('email',resetEmail); const res = await fetch('./api/auth.php', { method:'POST', body:fd, credentials:'include' }); const data = await res.json(); if (data.success) { if (data.reset_code) alert(`リセットコード: ${data.reset_code}`); setResetStep('code'); setError(''); } else { setError(data.error || '送信に失敗しました'); } } catch { setError('送信に失敗しました'); } finally { setLoading(false); } }; const handleReset = async (e) => { e.preventDefault(); setLoading(true); setError(''); try { const fd = new FormData(); fd.append('action','reset_password'); fd.append('email',resetEmail); fd.append('reset_code',resetCode); fd.append('new_password',newPassword); const res = await fetch('./api/auth.php', { method:'POST', body:fd, credentials:'include' }); const data = await res.json(); if (data.success) { alert('パスワードをリセットしました。再度ログインしてください。'); setTab('login'); setResetStep('email'); } else { setError(data.error || 'リセットに失敗しました'); } } catch { setError('リセットに失敗しました'); } finally { setLoading(false); } }; return (
e.stopPropagation()} style={{ background:'linear-gradient(160deg,#0e0e1a,#080810)', border:'1px solid rgba(200,168,75,0.3)', borderRadius:'2px',padding:'36px 32px',width:'100%',maxWidth:'380px', boxShadow:'0 0 80px rgba(0,0,0,0.8), 0 0 40px rgba(200,168,75,0.05)', position:'relative', }}> {/* Close */} {/* Header */}
LIFE CODE
{tab !== 'forgot' && (
{['login','register'].map(t=>( ))}
)} {tab === 'forgot' && (
パスワードをお忘れの方
)}
{/* Login form */} {tab === 'login' && (
setEmail(e.target.value)} style={inputSt} autoComplete="email"/> setPassword(e.target.value)} style={inputSt} autoComplete="current-password"/> {error &&
{error}
} {pendingVerificationEmail && ( )}
)} {/* Register form */} {tab === 'register' && (
setUsername(e.target.value)} style={inputSt} autoComplete="username"/> setEmail(e.target.value)} style={inputSt} autoComplete="email"/>
setBirthDate(e.target.value)} style={inputSt} min="1900-01-01" max="2025-12-31" autoComplete="bday"/>
setBirthTime(e.target.value)} style={inputSt} placeholder="12" />
setBirthplace(e.target.value)} style={inputSt} placeholder="35.681236, 139.767125" />
setRegion(e.target.value)} style={inputSt}/>
今、特に知りたいテーマ *(複数選択可)
{INTEREST_OPTIONS.map(opt => ( ))}
setPassword(e.target.value)} style={inputSt} autoComplete="new-password"/>
{error &&
{error}
}
)} {/* Forgot password */} {tab === 'forgot' && resetStep === 'email' && (

登録済みのメールアドレスを入力してください。

setResetEmail(e.target.value)} style={inputSt} autoComplete="email"/> {error &&
{error}
}
)} {tab === 'forgot' && resetStep === 'code' && (
setResetCode(e.target.value)} style={inputSt}/> setNewPassword(e.target.value)} style={inputSt} autoComplete="new-password"/> {error &&
{error}
}
)}
); }; const MyPageScreen = ({ user, onBack, onLogout, onUserUpdated, onUseEventDate }) => { const [username, setUsername] = React.useState(user?.username || ''); const [email, setEmail] = React.useState(user?.email || ''); const [birthDate, setBirthDate] = React.useState(user?.birth_date || ''); const [birthTime, setBirthTime] = React.useState(formatHourInputValue(user?.birth_time || '')); const [birthplace, setBirthplace] = React.useState(formatBirthLatLngValue(user?.birth_latitude, user?.birth_longitude)); const [profileMessage, setProfileMessage] = React.useState(''); const [currentPassword, setCurrentPassword] = React.useState(''); const [newPassword, setNewPassword] = React.useState(''); const [passwordMessage, setPasswordMessage] = React.useState(''); const [eventDate, setEventDate] = React.useState(''); const [eventType, setEventType] = React.useState(''); const [eventMemo, setEventMemo] = React.useState(''); const [eventMessage, setEventMessage] = React.useState(''); const [events, setEvents] = React.useState([]); const [editingEventId, setEditingEventId] = React.useState(null); const [editEventDate, setEditEventDate] = React.useState(''); const [editEventType, setEditEventType] = React.useState(''); const [editEventMemo, setEditEventMemo] = React.useState(''); React.useEffect(() => { setUsername(user?.username || ''); setEmail(user?.email || ''); setBirthDate(user?.birth_date || ''); setBirthTime(formatHourInputValue(user?.birth_time || '')); setBirthplace(formatBirthLatLngValue(user?.birth_latitude, user?.birth_longitude)); }, [user]); const boxInput = { width:'100%',marginBottom:'8px',background:'rgba(255,255,255,0.04)', color:'var(--text)',border:'1px solid var(--border)',padding:'8px' }; const loadEvents = async () => { try { const res = await fetch('./api/diagnoses.php?action=get_user_events&limit=50', { credentials:'include' }); const data = await res.json(); if (data.success && Array.isArray(data.events)) { setEvents(data.events); } } catch {} }; React.useEffect(() => { loadEvents(); }, []); const deleteEvent = async (eventId) => { try { const fd = new FormData(); fd.append('event_id', String(eventId)); const res = await fetch('./api/diagnoses.php?action=delete_user_event', { method:'POST', credentials:'include', body: fd, }); const data = await res.json(); if (data.success) { setEventMessage('イベントを削除しました'); loadEvents(); } else { setEventMessage(data.error || '削除に失敗しました'); } } catch { setEventMessage('通信エラーで削除に失敗しました'); } }; const startEditEvent = (ev) => { setEditingEventId(ev.id); setEditEventDate(ev.event_date || ''); setEditEventType(ev.event_type || ''); setEditEventMemo(ev.memo || ''); }; const cancelEditEvent = () => { setEditingEventId(null); setEditEventDate(''); setEditEventType(''); setEditEventMemo(''); }; const updateEvent = async () => { if (!editingEventId || !editEventDate || !editEventType) { setEventMessage('更新には日付とイベント種別が必要です'); return; } try { const fd = new FormData(); fd.append('event_id', String(editingEventId)); fd.append('event_date', editEventDate); fd.append('event_type', editEventType); fd.append('memo', editEventMemo); const res = await fetch('./api/diagnoses.php?action=update_user_event', { method:'POST', credentials:'include', body: fd, }); const data = await res.json(); if (data.success) { setEventMessage('イベントを更新しました'); cancelEditEvent(); loadEvents(); } else { setEventMessage(data.error || '更新に失敗しました'); } } catch { setEventMessage('通信エラーで更新に失敗しました'); } }; const updateProfile = async () => { if (!username || !email || !birthDate) { setProfileMessage('ユーザー名・メール・生年月日は必須です'); return; } try { const fd = new FormData(); fd.append('action', 'update_profile'); fd.append('username', username); fd.append('email', email); fd.append('birth_date', birthDate); fd.append('birth_time', birthTime); const parsedBirthplace = parseBirthLatLngValue(birthplace); fd.append('birth_latitude', parsedBirthplace.birthLatitude ?? ''); fd.append('birth_longitude', parsedBirthplace.birthLongitude ?? ''); const res = await fetch('./api/auth.php', { method:'POST', credentials:'include', body: fd }); const data = await res.json(); if (data.success) { setProfileMessage('プロフィールを更新しました'); onUserUpdated && onUserUpdated({ ...user, ...data.user }); } else { setProfileMessage(data.error || '更新に失敗しました'); } } catch { setProfileMessage('通信エラーで更新に失敗しました'); } }; const updatePassword = async () => { if (!currentPassword || !newPassword) { setPasswordMessage('現在のパスワードと新しいパスワードを入力してください'); return; } try { const fd = new FormData(); fd.append('action', 'change_password'); fd.append('current_password', currentPassword); fd.append('new_password', newPassword); const res = await fetch('./api/auth.php', { method:'POST', credentials:'include', body: fd }); const data = await res.json(); if (data.success) { setPasswordMessage('パスワードを変更しました'); setCurrentPassword(''); setNewPassword(''); } else { setPasswordMessage(data.error || '変更に失敗しました'); } } catch { setPasswordMessage('通信エラーで変更に失敗しました'); } }; const saveEvent = async () => { if (!eventDate || !eventType) { setEventMessage('日付とイベント種別を入力してください'); return; } try { const res = await fetch('./api/diagnoses.php?action=save_user_event', { method: 'POST', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ event_date: eventDate, event_type: eventType, memo: eventMemo }), }); const data = await res.json(); setEventMessage(data.success ? 'イベントを保存しました' : (data.error || '保存に失敗しました')); if (data.success) { setEventDate(''); setEventType(''); setEventMemo(''); loadEvents(); } } catch { setEventMessage('通信エラーで保存に失敗しました'); } }; return (
マイページ
プロフィール編集
setUsername(e.target.value)} style={boxInput} /> setEmail(e.target.value)} style={boxInput} /> setBirthDate(e.target.value)} style={boxInput} /> setBirthTime(e.target.value)} style={boxInput} placeholder="12" /> setBirthplace(e.target.value)} style={boxInput} placeholder="35.681236, 139.767125" /> {profileMessage}
パスワード変更
setCurrentPassword(e.target.value)} style={boxInput} /> setNewPassword(e.target.value)} style={boxInput} /> {passwordMessage}
タイムマシン用イベント登録
setEventDate(e.target.value)} style={boxInput} /> setEventType(e.target.value)} style={boxInput} />