import { t, getLocale, setLocale, localePath, type Locale } from './i18n/core';
import './i18n/ui';
import { createConversionTracker } from './analytics/conversion';
import { applySiteMeta } from './site-meta';
import { useEffect, useRef, useState } from 'react';
import { chartSummary, cycleSummary, cycleStatus } from './components/chart-summary';
import { shareSite, getPublicSiteUrl } from './export/share-site';
import { loadSavedBirth, saveSavedBirth, clearSavedBirth } from './storage';
import PersonSelector from './components/PersonSelector';
import { getMethodologies, defaultMethodologies as defaultPresets } from './export/methodologies';
import { getDefaultAnalysisPrompt, getQuestionTopics, translateAnalysisPrompt, appendQuestion, toggleQuestion } from './export/analysis-prompts';
import type { BirthInput, ChartResult, Pillar } from './core/types';
import { calculateInWorker } from './core/client';
import { cities, getCityGroups, getCityLabel, getDefaultCity } from './data/cities';
import { copyLedger, exportLedgers, downloadMarkdown, labels, readable, systems, type System } from './export/ledger';
const initial:BirthInput={nickname:'',birth_date:'',calendar:'solar',leap_month:false,birth_time:null,gender:'unspecified',place:{name:'서울',lat:37.5665,lon:126.978,timezone:'Asia/Seoul'},time_correction:'longitude',late_zi:'next_day',western_houses:'placidus'};
function Data({value}:{value:unknown}) {if(value===null||typeof value!=='object')return <span>{readable(value)}</span>;if(Array.isArray(value)&&value.length&&value.every(v=>v!==null&&typeof v==='object'&&!Array.isArray(v)&&Object.values(v).every(x=>x===null||typeof x!=='object'))){const keys=[...new Set(value.flatMap(v=>Object.keys(v)))];return <div className="table-scroll"><table><thead><tr>{keys.map(k=><th key={k}>{t(labels[k]??k)}</th>)}</tr></thead><tbody>{value.map((v,i)=><tr key={i}>{keys.map(k=><td key={k}>{readable(v[k])}</td>)}</tr>)}</tbody></table></div>;}if(Array.isArray(value))return value.length?<ol className="data-list">{value.map((v,i)=><li key={i}><Data value={v}/></li>)}</ol>:<span className="muted">{t("없음")}</span>;return <dl className="data-grid">{Object.entries(value).map(([k,v])=><div key={k}><dt>{t(labels[k]??k)}</dt><dd><Data value={v}/></dd></div>)}</dl>;}
const hanjaStems='甲乙丙丁戊己庚辛壬癸';const hanjaBranches='子丑寅卯辰巳午未申酉戌亥';
function Pillars({result,hanja}:{result:NonNullable<ChartResult['saju']>;hanja:boolean}){const columns:[string,Pillar|null][]=[[t("년주"),result.year],[t("월주"),result.month],[t("일주"),result.day],[t("시주"),result.hour]];return <><p className="muted pillar-scroll-hint">{t("표를 좌우로 넘기면 네 기둥을 모두 볼 수 있습니다.")}</p><div className="table-scroll"><div className="pillars">{[t("기둥"),t("천간"),t("지지"),t("십신 · 일간 기준"),t("지장간")].map((row,r)=><div className="pillar-row" key={row}>{columns.map(([name,p])=><div key={name} className={r===1||r===2?'pillar-symbol':''}>{r===0?<strong>{t(name)}</strong>:!p?<span className="muted">{t("미확정")}</span>:r===1?<>{readable(p.stem)}{hanja&&<small>{hanjaStems[p.stem_index]}</small>}</>:r===2?<>{readable(p.branch)}{hanja&&<small>{hanjaBranches[p.branch_index]}</small>}</>:r===3?<><small className="row-caption">{t(row)}</small>{readable(p.ten_god)}</>:<><small className="row-caption">{t(row)}</small>{p.hidden_stems.map(s=>`${readable(s.stem)} (${readable(s.ten_god)})`).join(' · ')}</>}</div>)}</div>)}</div></div><details><summary>{t("절입 근거와 적용 규칙")}</summary><Data value={{solar_boundaries:result.solar_boundaries,rule:result.rule}}/></details>{'details' in result&&result.details!=null&&<details className="supplemental-data"><summary>{t("합충·오행·12운성 등 보충 자료")}</summary><Data value={result.details}/></details>}</>;}
export default function App(){const [conversion]=useState(()=>createConversionTracker());const [locale, updateLocale]=useState(getLocale); const methodologies=getMethodologies(locale); const questionTopics=getQuestionTopics(locale); const defaultAnalysisPrompt=getDefaultAnalysisPrompt(locale); function changeLanguage(next:Locale){setPrompt(current=>translateAnalysisPrompt(current,locale,next));setLocale(next);updateLocale(next);history.replaceState(null,'',localePath(next));} useEffect(()=>{applySiteMeta(locale);},[locale]);const [saved]=useState(()=>loadSavedBirth());const [saveEnabled,setSaveEnabled]=useState(saved.enabled);const [storageNotice,setStorageNotice]=useState('');const [inputs,setInputs]=useState<BirthInput[]>(()=>[saved.input??{...initial,place:{...getDefaultCity(locale)}},{...initial,place:{...getDefaultCity(locale)}}]);const [person,setPerson]=useState(0);const [twoPeople,setTwoPeople]=useState(false);const input=inputs[person];const [results,setResults]=useState<(ChartResult|null)[]>([null,null]);const result=results[person];const [cycleReference,setCycleReference]=useState(()=>new Date().toISOString().slice(0,16));const [busy,setBusy]=useState(false);const [error,setError]=useState('');const [stale,setStale]=useState(false);const [tab,setTab]=useState<System>('saju');const [selected,setSelected]=useState<System[]>(['saju']);const [presets,setPresets]=useState(defaultPresets);const [prompt,setPrompt]=useState(()=>appendQuestion(defaultAnalysisPrompt,'comprehensive'));const [topics,setTopics]=useState<string[]>(['comprehensive']);const [hanja,setHanja]=useState(true);const [customPlaces,setCustomPlaces]=useState([saved.custom,false]);const custom=customPlaces[person];const setCustom=(value:boolean)=>setCustomPlaces(current=>current.map((item,index)=>index===person?value:item));const [fallback,setFallback]=useState('');const [notice,setNotice]=useState('');const [shareNotice,setShareNotice]=useState('');const [shareFallback,setShareFallback]=useState(false);const [theme,setTheme]=useState(()=>{try{return localStorage.getItem('cheongirok-theme')??'system';}catch{return 'system';}});const revision=useRef(0);const fallbackRef=useRef<HTMLTextAreaElement>(null);
useEffect(()=>{document.documentElement.dataset.theme=theme;try{localStorage.setItem('cheongirok-theme',theme);}catch{/* Theme remains usable without storage. */}},[theme]);
useEffect(()=>{const status=saveSavedBirth(inputs[0],customPlaces[0],saveEnabled);setStorageNotice(current=>status.ok?(saveEnabled?'':current):status.error??"내 기기에 정보를 저장하지 못했습니다.");},[inputs[0],customPlaces[0],saveEnabled]);function handleTabKey(event:React.KeyboardEvent<HTMLButtonElement>,system:System){const order=Object.keys(systems) as System[];const index=order.indexOf(system);const target=event.key==='ArrowRight'?(index+1)%order.length:event.key==='ArrowLeft'?(index+order.length-1)%order.length:event.key==='Home'?0:event.key==='End'?order.length-1:-1;if(target<0)return;event.preventDefault();setTab(order[target]);document.getElementById(`tab-${order[target]}`)?.focus();}function invalidate(){conversion.invalidateCalculation();revision.current++;if(results.some(Boolean))setStale(true);setFallback('');setNotice('');setError('');}
function update(patch:Partial<BirthInput>){invalidate();setInputs(current=>current.map((value,index)=>index===person?{...value,...patch}:value));}
function changeMode(enabled:boolean){invalidate();setTwoPeople(enabled);setPerson(0);if(enabled){let next={prompt,selected:topics};if(next.selected.includes('comprehensive'))next=toggleQuestion(next.prompt,next.selected,'comprehensive');for(const id of ['love','compatibility'])if(!next.selected.includes(id))next=toggleQuestion(next.prompt,next.selected,id);setPrompt(next.prompt);setTopics(next.selected);}}
async function calculate(event:React.FormEvent){event.preventDefault();if(busy)return;conversion.invalidateCalculation();const version=revision.current;setBusy(true);setError('');setFallback('');try{const request={reference_utc:new Date(cycleReference+'Z').toISOString()};const next:(ChartResult|null)[]=[null,null];for(let i=0;i<(twoPeople?2:1);i++){try{next[i]=await calculateInWorker(inputs[i],request);}catch(error){throw new Error(`${i+1} ${t('번 사람')}: ${error instanceof Error?t(error.message):String(error)}`);}}if(version===revision.current){conversion.completeCalculation(locale);setResults(next);setStale(false);setNotice(twoPeople?"두 사람의 명식과 주기를 산출했습니다.":"명식과 주기를 산출했습니다.");}}catch(e){if(version===revision.current)setError(e instanceof Error?e.message:String(e));}finally{setBusy(false);}}
const chosenMethod=methodologies[tab].find(method=>method.prompt===presets[tab])??methodologies[tab][0];const cycleState=result?cycleStatus(result,tab):{unavailable:[],errors:[]};const exportResults=results.slice(0,twoPeople?2:1).filter((value):value is ChartResult=>value!==null);
const canExport=!stale&&!busy&&selected.length>0&&exportResults.length===(twoPeople?2:1);
function exportText(withPrompt:boolean){return exportLedgers(exportResults,selected,presets,withPrompt?prompt:undefined);}async function copy(withPrompt:boolean){if(!canExport)return;const token=conversion.captureResult();const text=exportText(withPrompt);const ok=await copyLedger(text,t=>navigator.clipboard.writeText(t));if(ok)conversion.exportSucceeded('copy_success',token);setNotice(ok?"클립보드에 복사했습니다.":"클립보드를 사용할 수 없습니다. 아래 텍스트를 선택해 복사하세요.");setFallback(ok?'':text);if(!ok)setTimeout(()=>{fallbackRef.current?.focus();fallbackRef.current?.select();},0);}
const today=new Date();const maxDate=[today.getFullYear(),String(today.getMonth()+1).padStart(2,'0'),String(today.getDate()).padStart(2,'0')].join('-');
return <div className="app"><header className="masthead"><a className="brand" href="#"><span className="brand-mark" aria-hidden="true">✧</span><span>천기록<small>Sky Ledger</small></span></a><p>{t("하늘의 기록, 해석은 당신에게.")}</p><div className="header-actions"><label className="language-label">{t('언어')}<select id="site-language" aria-label={t('언어')} value={locale} onChange={e=>changeLanguage(e.target.value as Locale)}><option value="ko">한국어</option><option value="zh-CN">中文</option><option value="ja">日本語</option></select></label><button type="button" className="share-button" aria-label={t("사이트 공유하기")} title={t("사이트 공유하기")} onClick={async()=>{setShareNotice('');setShareFallback(false);const status=await shareSite(navigator.share?data=>navigator.share(data):undefined,text=>navigator.clipboard.writeText(text));setShareNotice(status==='shared'?"공유했습니다.":status==='copied'?"사이트 주소를 복사했습니다.":status==='fallback'?"아래 사이트 주소를 직접 복사하세요.":'');setShareFallback(status==='fallback');}}><svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" aria-hidden="true"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><path d="m8.6 10.5 6.8-4M8.6 13.5l6.8 4"/></svg></button><label className="theme-label">{t("화면")}<select aria-label={t("화면 테마")} value={theme} onChange={e=>setTheme(e.target.value)}><option value="system">{t("시스템")}</option><option value="light">{t("라이트")}</option><option value="dark">{t("다크")}</option></select></label></div>{shareNotice&&<div className="share-status" role="status">{t(shareNotice)}{shareFallback&&<input aria-label={t("공유할 사이트 주소")} readOnly value={getPublicSiteUrl(locale)} onFocus={e=>e.target.select()}/>}</div>}</header><main className="main-grid"><aside><section className="panel input-panel"><div className="section-head"><h1>{t("출생 정보 입력")}</h1><label className="check mode-toggle"><input type="checkbox" checked={twoPeople} onChange={e=>changeMode(e.target.checked)}/>{t("2인 모드")}</label></div><form onSubmit={calculate}><div className="personal-storage"><div className="storage-toolbar">{person===0?<><label className="storage-toggle"><input type="checkbox" role="switch" aria-label={t("내 기기에 내 정보 저장")} checked={saveEnabled} onChange={e=>{setStorageNotice('');setSaveEnabled(e.target.checked);}}/><span>{t("자동저장")}</span><small>{saveEnabled?(storageNotice?t("확인 필요"):t("켜짐")):t("꺼짐")}</small></label><button type="button" className="storage-delete" aria-label={t("저장 정보 삭제")} title={t("저장된 정보만 삭제하고 현재 입력은 유지합니다")} onClick={()=>{const status=clearSavedBirth();setSaveEnabled(false);setStorageNotice(status.ok?"저장된 정보 삭제됨 · 현재 입력 유지":status.error??"저장 정보를 삭제하지 못했습니다.");}}>{t("삭제")}</button></>:<span className="storage-secondary">{t("2번 정보는 저장하지 않아요")}</span>}</div><p className="storage-scope">{person===0?t("1번만 내 기기에 저장 · 2번은 저장 안 함"):t("현재 화면의 메모리에만 보관")}</p>{person===0&&storageNotice&&<p role="status" className="storage-notice">{t(storageNotice)}</p>}</div>{twoPeople&&<PersonSelector names={inputs.map(value=>value.nickname??'')} active={person} kind="입력" onSelect={index=>{setPerson(index);setFallback('');}}/>}<div className="form-grid"><label>{t("닉네임")}<span className="muted">{t("선택")}</span><input maxLength={20} value={input.nickname} onChange={e=>update({nickname:e.target.value})} placeholder={t("최대 20자")} autoComplete="off"/></label><label>{t("생년월일")}<input type={input.calendar==='lunar'?'text':'date'} inputMode={input.calendar==='lunar'?'numeric':undefined} pattern={input.calendar==='lunar'?'[0-9]{4}-[0-9]{2}-[0-9]{2}':undefined} placeholder="YYYY-MM-DD" required min={input.calendar==='solar'?'1900-01-01':undefined} max={input.calendar==='solar'?maxDate:undefined} value={input.birth_date} onChange={e=>update({birth_date:e.target.value})}/></label><label>{t("달력 기준")}<select value={input.calendar} onChange={e=>update({calendar:e.target.value as BirthInput['calendar'],leap_month:false})}><option value="solar">{t("양력 (태양력)")}</option><option value="lunar">{t("한국 음력")}</option></select></label>{input.calendar==='lunar'&&<label className="check"><input type="checkbox" checked={input.leap_month} onChange={e=>update({leap_month:e.target.checked})}/>{t("윤달")}</label>}<label>{t("출생시각")}<input type="time" value={input.birth_time??''} disabled={input.birth_time===null} onChange={e=>update({birth_time:e.target.value})}/></label><label className="check"><input type="checkbox" checked={input.birth_time===null} onChange={e=>update({birth_time:e.target.checked?null:'12:00'})}/>{t("시각 모름")}</label>{input.birth_time===null&&<p className="muted">{t("시각을 모르면 날짜 내 변하지 않는 값만 표시합니다.")}</p>}<label>{t("성별")}<select value={input.gender} onChange={e=>update({gender:e.target.value as BirthInput['gender']})}><option value="unspecified">{t("표시 안 함")}</option><option value="female">{t("여성")}</option><option value="male">{t("남성")}</option></select></label><label>{t("출생지")}<select value={custom?'custom':input.place.name} onChange={e=>{const isCustom=e.target.value==='custom';setCustom(isCustom);if(!isCustom){const city=cities.find(c=>c.name===e.target.value);if(city)update({place:city});}else update({place:{...input.place,name:'직접 입력'}});}}>{getCityGroups(locale).map(group=><optgroup key={group.id} label={group.label}>{group.cities.map(city=><option key={city.name} value={city.name}>{getCityLabel(city,locale)}</option>)}</optgroup>)}<option value="custom">{t("직접 좌표·시간대 입력")}</option></select></label>{custom&&<><label>{t("지명")}<input required maxLength={80} value={input.place.name} onChange={e=>update({place:{...input.place,name:e.target.value}})}/></label><label>{t("위도")}<input type="number" required min={-90} max={90} step="any" value={input.place.lat} onChange={e=>update({place:{...input.place,lat:Number(e.target.value)}})}/></label><label>{t("경도")}<input type="number" required min={-180} max={180} step="any" value={input.place.lon} onChange={e=>update({place:{...input.place,lon:Number(e.target.value)}})}/></label><label>{t("IANA 시간대")}<input required value={input.place.timezone} placeholder="Asia/Seoul" onChange={e=>update({place:{...input.place,timezone:e.target.value}})}/></label></>}</div><details className="assumption-controls"><summary>{t("계산 전제 설정")}</summary><label>{t("사주·자미두수 시각")}<select value={input.time_correction} onChange={e=>update({time_correction:e.target.value as BirthInput['time_correction']})}><option value="longitude">{t("경도 보정 적용")}</option><option value="none">{t("경도 보정 없음")}</option></select></label><p className="muted">{t("날짜 경계: 23시부터 다음 날. 천문 계산은 원래 출생 순간을 사용합니다.")}</p><label>{t("서양 하우스")}<select value={input.western_houses} onChange={e=>update({western_houses:e.target.value as BirthInput['western_houses']})}><option value="placidus">{t("플라시두스")}</option><option value="whole_sign">{t("홀사인")}</option></select></label><label>{t("시간대 중복 시각")}<select value={input.timezone_disambiguation??''} onChange={e=>update({timezone_disambiguation:(e.target.value||undefined) as BirthInput['timezone_disambiguation']})}><option value="">{t("중복 시각이면 오류로 확인")}</option><option value="earlier">{t("먼저 발생한 시각")}</option><option value="later">{t("나중에 발생한 시각")}</option></select></label><label>{t("수동 UTC 오프셋 (분, 선택)")}<input type="number" min={-840} max={840} value={input.timezone_offset_minutes??''} onChange={e=>update({timezone_offset_minutes:e.target.value===''?undefined:Number(e.target.value)})}/></label></details><label className="cycle-reference">{t("주기 기준시각 (UTC)")}<input type="datetime-local" required value={cycleReference} onChange={e=>{invalidate();setCycleReference(e.target.value);}}/></label><p className="muted">{t('입력값을 UTC로 사용합니다.')}{twoPeople?t(" 두 사람에게 같은 기준시각을 적용합니다."):''}</p><p className="muted local-calculation">{t("브라우저에서 계산")}</p><button className="primary calculate" disabled={busy}>{busy?t("명식 산출 중…"):twoPeople?t("두 사람 명식 산출 →"):t("명식 산출 →")}</button>{error&&<p role="alert" className="error">{t(error)}</p>}</form></section><section className="panel assumptions"><h2>{t("계산 가정 및 기준")}</h2><p>{input.place.timezone}</p><p>{Math.abs(input.place.lat)}° {input.place.lat<0?'S':'N'} · {Math.abs(input.place.lon)}° {input.place.lon<0?'W':'E'}</p><p>{t("사주: 12절 기준 / 자시 23시 경계")}<br/>{t("자미두수: 한국 음력·본명 배치")}<br/>{t("베딕: 라히리·평균 노드·홀사인")}<br/>{t("서양: 열대황도·트루 노드")}</p><p className="muted">{t("1번 정보는 저장 설정에 따라 내 기기에 보관합니다. 2번 정보는 저장하지 않으며 서버로 전송하지 않습니다.")}</p></section></aside><div className="right-column"><section className="panel result-panel">{twoPeople&&<PersonSelector names={inputs.map(value=>value.nickname??'')} active={person} kind="명식" result onSelect={index=>{setPerson(index);setFallback('');}}/>}<div className="tabs" role="tablist" aria-label={t("명식 체계")}>{(Object.keys(systems) as System[]).map(s=><button role="tab" id={`tab-${s}`} aria-selected={tab===s} tabIndex={tab===s?0:-1} onKeyDown={event=>handleTabKey(event,s)} aria-controls="chart-panel" key={s} onClick={()=>setTab(s)}>{t(systems[s])}</button>)}</div><div className="result-body" role="tabpanel" id="chart-panel" aria-labelledby={`tab-${tab}`}><div className="section-head"><h2>{t(systems[tab])} {tab==='saju'?t("명식"):t("기록")}</h2>{tab==='saju'&&<label className="check"><input type="checkbox" checked={hanja} onChange={e=>setHanja(e.target.checked)}/>{t("한자 병기")}</label>}</div><div className="chart-methodology"><label>{t('적용 방법론 ·')} {t(systems[tab])}<select aria-label={`${t(systems[tab])} ${t('방법론')}`} value={presets[tab]} onChange={e=>setPresets({...presets,[tab]:e.target.value})}>{methodologies[tab].map(method=><option key={method.id} value={method.prompt}>{method.label} : {method.description}</option>)}</select></label><dl className="method-guide"><div><dt>{t("장점")}</dt><dd>{chosenMethod.strength}</dd></div><div><dt>{t("한계")}</dt><dd>{chosenMethod.limitation}</dd></div></dl></div>{!result?<div className="empty"><span aria-hidden="true">✧</span><h3>{t("첫 기록을 기다립니다")}</h3><p>{t("출생 정보를 입력하고 명식을 산출하세요.")}<br/>{t("확인할 수 없는 값은 미확정으로 남깁니다.")}</p></div>:<>{stale&&<p className="warning" role="status">{t("입력이 변경되었습니다. 이전 결과입니다. 다시 산출해야 복사할 수 있습니다.")}</p>}<p className="result-caption"><span className="caption-name" title={result.input.nickname||t("이름 없는 기록")}>{twoPeople?`${person+1} · `:''}{result.input.nickname||t("이름 없는 기록")}</span><span className="caption-facts">· {result.normalized.solar_date} · {result.input.birth_time??t("시각 미상")} · {customPlaces[person]?result.input.place.name:getCityLabel(result.input.place,locale)}</span></p>{result[tab]===null?<p className="empty-small">{t("이 체계의 명식을 확정할 수 없습니다. 아래 사유를 확인하세요.")}</p>:tab==='saju'?<Pillars result={result.saju!} hanja={hanja}/>:<><dl className={tab==="ziwei"?"chart-summary ziwei-summary":"chart-summary"}>{chartSummary(result,tab).map(item=><div key={item.label}><dt>{item.label}</dt><dd>{item.value}</dd></div>)}</dl><details key={`${person}-${tab}`} className="full-chart-data"><summary>{t("전체 명식·보충 데이터")}</summary><Data value={result[tab]}/></details></>}{result.cycles&&<details key={`${person}-${tab}-cycles`} className="cycle-data"><summary>{t("운세 주기")}<span className="cycle-preview">{cycleSummary(result,tab)}</span>{(cycleState.unavailable.length>0||cycleState.errors.length>0)&&<span className="cycle-preview cycle-warning">{t(systems[tab])} {t('운세 주기')}: {t('미확정')} {cycleState.unavailable.length} · {t('오류')} {cycleState.errors.length} · {t('상세 확인')}</span>}</summary><Data value={{reference_utc:result.cycles.reference_utc,[tab]:result.cycles[tab],assumptions:result.cycles.assumptions,unavailable:cycleState.unavailable,errors:cycleState.errors}}/></details>}{(result.unavailable.length>0||result.errors.length>0)&&<p className="warning" role="status">{t('미확정')} {result.unavailable.length} · {t('오류')} {result.errors.length}. {t('아래 계산 전제·미확정·오류를 펼쳐 사유를 확인하세요.')}</p>}<details className="metadata"><summary>{t("계산 전제 · 미확정 · 오류 · 엔진 버전")}</summary><Data value={{meta:result.meta,normalized:result.normalized,assumptions:result.assumptions,unavailable:result.unavailable,errors:result.errors}}/></details></>}</div></section><section className="panel export-panel"><h2>{t("AI에 가져가기")}</h2><p className="muted">{t("명식 데이터와 궁금한 주제를 골라, 자신의 AI에 붙여넣으세요.")}</p><div className="export-grid"><fieldset><legend>{t("포함할 명식 데이터")}</legend>{(Object.keys(systems) as System[]).map(s=><label className="check" key={s}><input type="checkbox" checked={selected.includes(s)} onChange={e=>setSelected(e.target.checked?[...selected,s]:selected.filter(v=>v!==s))}/>{t(systems[s])}</label>)}</fieldset><div><fieldset className="topic-options"><legend>{t("궁금한 주제")}</legend><div className="topic-comprehensive"><label className="check"><input type="checkbox" checked={topics.includes('comprehensive')} onChange={()=>{const next=toggleQuestion(prompt,topics,'comprehensive');setPrompt(next.prompt);setTopics(next.selected);}}/>{t("종합")}</label></div><div className="topic-grid">{questionTopics.filter(item=>item.id!=='comprehensive').map(item=><label className="check" key={item.id}><input type="checkbox" checked={topics.includes(item.id)} onChange={()=>{const next=toggleQuestion(prompt,topics,item.id);setPrompt(next.prompt);setTopics(next.selected);}}/>{item.label}</label>)}</div></fieldset>{questionTopics.filter(item=>topics.includes(item.id)&&item.note).map(item=><p className="muted" key={item.id}>{item.note}</p>)}<label>{t("분석 프롬프트")}<textarea rows={5} value={prompt} onChange={e=>setPrompt(e.target.value)}/></label></div><div className="copy-actions"><button className="primary" disabled={!canExport} onClick={()=>void copy(true)}>{t("프롬프트와 함께 복사")}</button><button disabled={!canExport} onClick={()=>void copy(false)}>{t("데이터만 복사")}</button><button disabled={!canExport} onClick={()=>{if(canExport)downloadMarkdown(exportText(true),maxDate);}} title={t("선택한 명식·방법론·현재 분석 프롬프트 전체를 저장합니다")}>{t("Markdown 다운로드")}</button><p className="muted">{t("메타데이터와 미확정 사유를 항상 포함합니다. 해석이나 AI 호출은 제공하지 않습니다.")}</p></div></div><p role="status" className="copy-status">{t(notice)}</p>{fallback&&<label>{t("직접 선택해 복사")}<textarea ref={fallbackRef} className="fallback" readOnly value={fallback} rows={12} onFocus={e=>e.target.select()}/></label>}</section></div></main><footer><span>{t("천기록 · Sky Ledger · 브라우저 계산 · 입력·명식 전송 없음 · 익명 이용 집계")}</span><nav aria-label={t("소스와 라이선스")}><a href={`${localePath(locale)}guide/`}>{t('사용 안내')}</a><a href={`${import.meta.env.BASE_URL}source/index.html`}>{t("소스 코드")}</a><a href={`${import.meta.env.BASE_URL}source/LICENSE`}>{t("AGPL 라이선스")}</a><a href={`${import.meta.env.BASE_URL}source/docs/THIRD_PARTY_NOTICES.md`}>{t("제3자 고지")}</a></nav></footer></div>;
}































