// Portfolio.jsx — 포트폴리오 관리 (트레이딩 데스크 실무). 주식/채권 데스크 분리.
// 화면 순서: 종합 요약 → 보유 현황 → 리스크 요인 분석 → 투자 시뮬레이션 + AI 최적화 → 스트레스 + 시나리오.

// 외부 클릭 닫힘 훅
function useOutside(ref, onOut) {
  React.useEffect(() => {const h = (e) => {if (ref.current && !ref.current.contains(e.target)) onOut();};
    document.addEventListener('mousedown', h);return () => document.removeEventListener('mousedown', h);}, [onOut]);
}

// 담당자 검색 콤보박스 (사번·이름·부서·팀)
function PersonPicker({ value, onChange, people }) {
  const POOL = people || PEOPLE;
  const [open, setOpen] = React.useState(false);
  const [q, setQ] = React.useState('');
  const ref = React.useRef(null);
  useOutside(ref, () => setOpen(false));
  const sel = PMAP[value];
  const list = POOL.filter((p) => {const s = (p.name + p.emp + p.dept + p.team).toLowerCase();return s.includes(q.toLowerCase());});
  return (
    <div ref={ref} style={{ position: 'relative', minWidth: 300 }}>
      <button onClick={() => setOpen((o) => !o)} style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, background: T.white, border: '1px solid ' + (open ? T.blue : T.c250), borderRadius: 10, padding: '8px 12px', cursor: 'pointer', textAlign: 'left' }}>
        <div style={{ width: 30, height: 30, borderRadius: 999, background: 'linear-gradient(135deg,#0A4FA3,#003B7E)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', fontFamily: T.font, fontWeight: 700, fontSize: 12, flex: 'none' }}>{sel.name[0]}</div>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontFamily: T.font, fontWeight: 700, fontSize: 13, color: T.c900 }}>{sel.name} <span style={{ fontWeight: 600, fontSize: 11, color: T.c400 }}>{sel.title}</span></div>
          <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c500 }}>{sel.dept} · {sel.team} · 사번 {sel.emp}</div>
        </div>
        <Icon name="chevDown" size={16} color={T.c400} style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }} />
      </button>
      {open &&
      <div style={{ position: 'absolute', top: 'calc(100% + 6px)', left: 0, right: 0, background: T.white, border: '1px solid ' + T.line, borderRadius: 12, boxShadow: '0 12px 32px rgba(15,23,34,.16)', zIndex: 40, padding: 8 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, background: T.c100, border: '1px solid ' + T.line, borderRadius: 8, padding: '7px 10px', marginBottom: 6 }}>
            <Icon name="search" size={15} color={T.c400} />
            <input autoFocus value={q} onChange={(e) => setQ(e.target.value)} placeholder="사번·이름·부서 검색"
          style={{ border: 0, outline: 'none', flex: 1, background: 'transparent', fontFamily: T.font, fontWeight: 500, fontSize: 12.5, color: T.c800 }} />
          </div>
          <div style={{ maxHeight: 264, overflowY: 'auto' }}>
            {list.map((p) => {const on = p.emp === value;
            return <button key={p.emp} onClick={() => {onChange(p.emp);setOpen(false);setQ('');}} style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '8px 10px', borderRadius: 8, cursor: 'pointer', background: on ? T.blueSurface : 'transparent', textAlign: 'left' }}
            onMouseEnter={(e) => {if (!on) e.currentTarget.style.background = T.c50;}} onMouseLeave={(e) => {if (!on) e.currentTarget.style.background = 'transparent';}}>
                <div style={{ width: 28, height: 28, borderRadius: 999, background: on ? T.blue : T.c150, display: 'flex', alignItems: 'center', justifyContent: 'center', color: on ? '#fff' : T.c600, fontFamily: T.font, fontWeight: 700, fontSize: 12, flex: 'none' }}>{p.name[0]}</div>
                <div style={{ minWidth: 0, flex: 1 }}>
                  <div style={{ fontFamily: T.font, fontWeight: 700, fontSize: 12.5, color: T.c900 }}>{p.name} <span style={{ fontWeight: 500, fontSize: 10.5, color: T.c400 }}>{p.title}</span></div>
                  <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 10.5, color: T.c500 }}>{p.dept} · {p.team}</div>
                </div>
                <span className="num" style={{ fontFamily: T.mono, fontSize: 10.5, color: T.c400 }}>{p.emp}</span>
              </button>;})}
            {list.length === 0 && <div style={{ padding: '22px', textAlign: 'center', fontFamily: T.font, fontWeight: 500, fontSize: 12.5, color: T.c400 }}>검색 결과가 없어요.</div>}
          </div>
        </div>
      }
    </div>);
}

// 조직 선택 드롭다운
function DeptPicker({ value, onChange, depts, asset }) {
  const LIST = depts || DEPT_LIST;
  const [open, setOpen] = React.useState(false);
  const ref = React.useRef(null);
  useOutside(ref, () => setOpen(false));
  const countOf = (d) => PEOPLE.filter((p) => p.dept === d && (!asset || p.asset === asset)).length;
  return (
    <div ref={ref} style={{ position: 'relative', minWidth: 300 }}>
      <button onClick={() => setOpen((o) => !o)} style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, background: T.white, border: '1px solid ' + (open ? T.blue : T.c250), borderRadius: 10, padding: '10px 12px', cursor: 'pointer', textAlign: 'left' }}>
        <div style={{ width: 30, height: 30, borderRadius: 8, background: T.blueSurface, display: 'flex', alignItems: 'center', justifyContent: 'center', color: T.blue, flex: 'none' }}><Icon name="building" size={17} /></div>
        <div style={{ flex: 1 }}>
          <div style={{ fontFamily: T.font, fontWeight: 700, fontSize: 13, color: T.c900 }}>{value}</div>
          <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c500 }}>{asset ? asset + ' 데스크 합산' : '부서 합산 포트폴리오'}</div>
        </div>
        <Icon name="chevDown" size={16} color={T.c400} style={{ transform: open ? 'rotate(180deg)' : 'none', transition: 'transform .15s' }} />
      </button>
      {open &&
      <div style={{ position: 'absolute', top: 'calc(100% + 6px)', left: 0, right: 0, background: T.white, border: '1px solid ' + T.line, borderRadius: 12, boxShadow: '0 12px 32px rgba(15,23,34,.16)', zIndex: 40, padding: 8 }}>
          {LIST.map((d) => {const on = d === value;const n = countOf(d);
          return <button key={d} onClick={() => {onChange(d);setOpen(false);}} style={{ width: '100%', display: 'flex', alignItems: 'center', gap: 10, padding: '9px 10px', borderRadius: 8, cursor: 'pointer', background: on ? T.blueSurface : 'transparent', textAlign: 'left' }}
          onMouseEnter={(e) => {if (!on) e.currentTarget.style.background = T.c50;}} onMouseLeave={(e) => {if (!on) e.currentTarget.style.background = 'transparent';}}>
              <div style={{ width: 28, height: 28, borderRadius: 8, background: on ? T.blue : T.c150, display: 'flex', alignItems: 'center', justifyContent: 'center', color: on ? '#fff' : T.c600, flex: 'none' }}><Icon name="building" size={15} /></div>
              <span style={{ flex: 1, fontFamily: T.font, fontWeight: 700, fontSize: 12.5, color: T.c900 }}>{d}</span>
              <span style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: T.c400 }}>담당자 {n}명</span>
            </button>;})}
        </div>
      }
    </div>);
}

function KPIStat({ label, value, unit, sub, color, subColor }) {
  return (
    <div style={{ flex: 1, minWidth: 0 }}>
      <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.c500, textAlign: 'left', whiteSpace: 'nowrap' }}>{label}</div>
      <div style={{ display: 'flex', alignItems: 'baseline', gap: 4, marginTop: 6 }}>
        <span className="num" style={{ fontFamily: T.font, fontWeight: 800, fontSize: 28, color: color || T.c900, letterSpacing: '-.02em', whiteSpace: 'nowrap' }}>{value}</span>
        {unit && <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 13.5, color: T.c400 }}>{unit}</span>}
      </div>
      {sub && <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11, color: subColor || T.c400, marginTop: 4, textAlign: 'left' }}>{sub}</div>}
    </div>);
}

// 섹션 헤더 (번호 스텝)
function StepHead({ n, title, sub }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10, margin: '6px 2px -4px' }}>
      <span style={{ width: 22, height: 22, borderRadius: 999, background: T.blue, color: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: T.font, fontWeight: 700, fontSize: 11.5, flex: 'none' }}>{n}</span>
      <span style={{ fontFamily: T.font, fontWeight: 800, fontSize: 16.5, color: T.c900, letterSpacing: '-.01em', whiteSpace: 'nowrap' }}>{title}</span>
      {sub && <span style={{ fontFamily: T.font, fontWeight: 500, fontSize: 12.5, color: T.c400 }}>{sub}</span>}
    </div>);
}

// 딜러 한도 현황 — SA(표준방법) 한도 · 투자한도 · 손절매한도 · 비율 한도. 한도 설정은 리스크총괄부, 딜러는 조회.
function DealerLimitsCard({ emp, asset, pf }) {
  const lim = dealerLimit(emp);
  if (!lim) return null;
  const tiles = [
  { label: 'SA 한도', pair: lim.sa, unit: '억', sub: '표준방법(SA) 산출 시장리스크' },
  { label: '투자한도', pair: lim.invest, unit: '억', sub: '배정 투자가능금액 대비 집행액' }];
  const zc = stopCounts(pf);
  const flagged = zc[1] + zc[2] + zc[3] + zc[4];
  const delta = asset === '주식' ? Math.round(kospiDelta(pf)) : null;
  return (
    <Card>
      <CardHead title="딜러 한도 현황" sub="트레이딩 조회용 한도 레이어 — 한도 설정은 리스크총괄부 권한" icon="shield"
      right={<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        {delta != null && <Pill kind="blue">델타(KOSPI) {delta.toLocaleString()}억</Pill>}
        {flagged > 0 && <Pill kind="warn" dot>손절 구간 도달 {flagged}종목</Pill>}
      </div>} />
      <div style={{ display: 'grid', gridTemplateColumns: lim.ratio ? '1fr 1fr 1.3fr 1fr' : '1fr 1fr 1.3fr', gap: 0, padding: '16px 20px 18px' }}>
        {tiles.map((t, i) =>
        <div key={t.label} style={{ paddingRight: 18, marginRight: 18, borderRight: '1px solid ' + T.line, textAlign: 'left' }}>
            <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.c500, whiteSpace: 'nowrap' }}>{t.label}</div>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 5, marginTop: 7 }}>
              <span className="num" style={{ fontFamily: T.font, fontWeight: 800, fontSize: 24, color: limUtil(t.pair) >= 100 ? T.alert : limUtil(t.pair) >= 80 ? T.warn : T.c900, letterSpacing: '-.02em', whiteSpace: 'nowrap' }}>{t.pair[0].toLocaleString()}</span>
              <span className="num" style={{ fontFamily: T.font, fontWeight: 600, fontSize: 13, color: T.c400, whiteSpace: 'nowrap' }}>/ {t.pair[1].toLocaleString()}{t.unit}</span>
            </div>
            <div style={{ margin: '9px 0 7px' }}><LimitMeter pct={limUtil(t.pair)} h={7} /></div>
            <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11.5, color: T.c400, whiteSpace: 'nowrap' }}>{t.sub} · 소진 <b className="num" style={{ color: limUtil(t.pair) >= 100 ? T.alert : limUtil(t.pair) >= 80 ? T.warn : T.c600 }}>{limUtil(t.pair).toFixed(1)}%</b></div>
          </div>
        )}
        <div style={{ paddingRight: lim.ratio ? 18 : 0, marginRight: lim.ratio ? 18 : 0, borderRight: lim.ratio ? '1px solid ' + T.line : 'none', textAlign: 'left' }}>
          <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.c500, whiteSpace: 'nowrap' }}>손절매한도 · 구간 도달 종목 수</div>
          <div style={{ display: 'flex', gap: 6, marginTop: 10, flexWrap: 'wrap' }}>
            {STOP_ZONES.slice(1).map((z) => {const n = zc[z.v];const on = n > 0;
            return <span key={z.v} className="num" style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: T.font, fontWeight: 700, fontSize: 12.5, padding: '4px 10px', borderRadius: 999, whiteSpace: 'nowrap',
              background: on ? STATUS[z.kind].bg : T.c100, color: on ? STATUS[z.kind].fg : T.c400, border: '1px solid ' + (on ? STATUS[z.kind].line : T.c200) }}>{z.l} <b>{n}</b></span>;})}
          </div>
          <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11.5, color: T.c500, marginTop: 10, whiteSpace: 'nowrap' }}>
            정상 <b className="num" style={{ color: T.ok }}>{zc[0]}종목</b> · 종목별 구간은 아래 보유 종목 참조
          </div>
        </div>
        {lim.ratio && <div style={{ textAlign: 'left' }}>
          <div style={{ fontFamily: T.font, fontWeight: 600, fontSize: 12.5, color: T.c500, whiteSpace: 'nowrap' }}>비율 한도 (단일 종목 집중도)</div>
          <React.Fragment>
            <div style={{ display: 'flex', alignItems: 'baseline', gap: 5, marginTop: 7 }}>
              <span className="num" style={{ fontFamily: T.font, fontWeight: 800, fontSize: 24, color: (lim.ratio.max / lim.ratio.cap * 100) >= 100 ? T.alert : (lim.ratio.max / lim.ratio.cap * 100) >= 80 ? T.warn : T.c900, letterSpacing: '-.02em' }}>{lim.ratio.max.toFixed(2)}</span>
              <span className="num" style={{ fontFamily: T.font, fontWeight: 600, fontSize: 13, color: T.c400 }}>/ {lim.ratio.cap.toFixed(1)}%</span>
            </div>
            <div style={{ margin: '9px 0 7px' }}><LimitMeter pct={lim.ratio.max / lim.ratio.cap * 100} h={7} /></div>
            <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 11.5, color: T.c400, whiteSpace: 'nowrap' }}>시가총액 비중 기준 · 최대 <b style={{ color: T.c600 }}>{lim.ratio.n}</b></div>
          </React.Fragment>
        </div>}
      </div>
    </Card>);
}

function Portfolio({ asset, preset }) {
  const people = peopleOf(asset);
  const depts = deptsOf(asset);
  const [mode, setMode] = React.useState(preset && preset.dept ? '조직' : '담당자');
  const [emp, setEmp] = React.useState(preset && preset.emp ? preset.emp : people[0].emp);
  const [dept, setDept] = React.useState(preset && preset.dept ? preset.dept : depts[0]);
  const [tool, setTool] = React.useState('trade');
  const [stress, setStress] = React.useState('single');
  const [comp, setComp] = React.useState('종목');
  const [holds, setHolds] = React.useState(null);
  const [val, setVal] = React.useState(null);

  React.useEffect(() => { if (preset) { if (preset.emp) { setMode('담당자'); setEmp(preset.emp); } else if (preset.dept) { setMode('조직'); setDept(preset.dept); } } }, [preset]);

  // 선택된 소유자 → 기준 포트폴리오
  const ownerKey = mode === '담당자' ? 'p:' + emp : 'd:' + dept;
  const owner = React.useMemo(() => {
    if (mode === '담당자') {const p = PMAP[emp];return { kind: 'person', name: p.name, sub: `${p.dept} · ${p.team} · 사번 ${p.emp}`, title: p.title, base: p.pf };}
    const dp = deptPortfolio(dept, asset);return { kind: 'dept', name: dept, sub: `${asset} 담당 ${people.filter((p) => p.dept === dept).length}명 합산 · 부서 포트폴리오`, base: dp };
  }, [mode, emp, dept]);

  // 소유자 변경 시 working holds·평가액 리셋
  React.useEffect(() => {setHolds(owner.base.holds.map((h) => ({ ...h })));setVal(owner.base.value);setTool('trade');}, [ownerKey]);
  const pf = { holds: holds || owner.base.holds, value: val != null ? val : owner.base.value, limit: owner.base.limit };
  const m = summarize(pf);
  const contribs = riskContribs(pf.holds);
  const cmap = {};contribs.forEach((c) => cmap[c.id] = c.frac);
  const commit = React.useCallback((h, v) => {setHolds(h);if (v != null) setVal(v);}, []);
  const dirty = holds && JSON.stringify(holds) !== JSON.stringify(owner.base.holds.map((h) => ({ ...h }))) || val != null && Math.round(val) !== Math.round(owner.base.value);

  // 구성 데이터
  const compKeys = asset === '주식' ? ['종목', '국가', '섹터'] : ['종목', '국가', '등급'];
  const compData = React.useMemo(() => {
    if (comp === '종목') return pf.holds.slice().sort((a, b) => b.w - a.w).map((h, i) => ({ label: UMAP[h.id].name, value: h.w, color: PAL[i % PAL.length] }));
    const keyOf = comp === '국가' ? (u) => u.country : comp === '섹터' ? (u) => u.sector : (u) => u.rating || '기타';
    const g = {};pf.holds.forEach((h) => {const k = keyOf(UMAP[h.id]);g[k] = (g[k] || 0) + h.w;});
    return Object.entries(g).sort((a, b) => b[1] - a[1]).map(([label, value], i) => ({ label, value: +value.toFixed(1), color: comp === '국가' ? COUNTRY_HUE[label] || PAL[i % PAL.length] : PAL[i % PAL.length] }));
  }, [pf.holds, comp]);

  // AI 컨텍스트 등록
  React.useEffect(() => {
    const top = pf.holds.slice().sort((a, b) => (cmap[b.id] || 0) - (cmap[a.id] || 0))[0];
    window.setAIContext && window.setAIContext({
      screen: `${asset} 포트폴리오 관리 · ${owner.name}`,
      summary: `${owner.sub} · 평가액 ${fmtEok(pf.value)}`,
      facts: [
      `전체 수익률(연) ${m.ret.toFixed(1)}%`,
      `포트폴리오 리스크(CVaR 99%/1일) ${fmtEok(m.risk)}`,
      `리스크 한도 ${fmtEok(pf.limit)} · 잔여 ${fmtEok(m.remain)} · 소진율 ${m.util.toFixed(1)}%`,
      top ? `위험기여 1위 ${UMAP[top.id].name} (${(cmap[top.id] * 100).toFixed(0)}%)` : ''].
      filter(Boolean),
      suggestions: asset === '주식' ?
      ['국가별 리스크 기여가 가장 큰 곳은?', '이 포트폴리오에서 리스크 기여가 큰 종목은?', 'COVID 시나리오에서 예상 손실은?'] :
      ['Net DV01이 큰 포지션은?', '금리 100bp 상승 시 손실은?', '신용 스프레드 노출을 줄이려면?']
    });
  }, [ownerKey, holds, val]);

  const reset = () => {setHolds(owner.base.holds.map((h) => ({ ...h })));setVal(owner.base.value);};

  return (
    <div data-screen-label={asset + ' 포트폴리오 관리'} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
      {/* 선택 바 */}
      <Card pad="14px 18px">
        <div style={{ display: 'flex', alignItems: 'center', gap: 16, flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
            <Icon name={asset === '주식' ? 'trendUp' : 'bond'} size={18} color={T.blue} />
            <span style={{ fontFamily: T.font, fontWeight: 700, fontSize: 14, color: T.c900 }}>{asset} 포트폴리오 불러오기</span>
          </div>
          <Segmented options={['담당자', '조직']} value={mode} onChange={setMode} size="sm" />
          {mode === '담당자' ? <PersonPicker value={emp} onChange={setEmp} people={people} /> : <DeptPicker value={dept} onChange={setDept} depts={depts} asset={asset} />}
          <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 9 }}>
            {dirty && <Pill kind="warn" dot>편집됨</Pill>}
            {dirty && <Btn variant="secondary" size="sm" icon="refresh" onClick={reset}>원장 복원</Btn>}
            <Pill kind="neutral" icon="clock">기준 {FIRM.asOf}</Pill>
          </div>
        </div>
      </Card>

      {/* ① 종합 요약 */}
      <StepHead n={1} title="포트폴리오 종합 요약" sub="손익·리스크·한도 소진을 한눈에" />
      <Card pad="0">
        <div style={{ display: 'flex', alignItems: 'center', gap: 14, padding: '16px 20px', borderBottom: '1px solid ' + T.line }}>
          <div style={{ width: 42, height: 42, borderRadius: owner.kind === 'dept' ? 11 : 999, background: 'linear-gradient(135deg,#0A4FA3,#003B7E)', display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff', flex: 'none', fontFamily: T.font, fontWeight: 700, fontSize: 16 }}>
            {owner.kind === 'dept' ? <Icon name="building" size={21} /> : owner.name[0]}</div>
          <div style={{ textAlign: 'left' }}>
            <div style={{ fontFamily: T.font, fontWeight: 800, fontSize: 19, color: T.c900, letterSpacing: '-.01em', whiteSpace: 'nowrap' }}>{owner.name}{owner.title && <span style={{ fontWeight: 600, fontSize: 13, color: T.c400, marginLeft: 8 }}>{owner.title}</span>}</div>
            <div style={{ fontFamily: T.font, fontWeight: 500, fontSize: 12.5, color: T.c500, marginTop: 2, whiteSpace: 'nowrap' }}>{owner.sub}</div>
          </div>
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 8 }}>
            <Pill kind="blue" small>{asset} 데스크</Pill>
            <Pill kind={m.util >= 100 ? 'alert' : m.util >= 80 ? 'warn' : 'ok'} dot>{m.util >= 100 ? '한도 초과' : m.util >= 80 ? '한도 임박' : '한도 내 정상'}</Pill>
          </div>
        </div>
        <div style={{ display: 'flex', alignItems: 'stretch', padding: '16px 20px', gap: 0 }}>
          <KPIStat label="평가액" value={fmtEok(pf.value)} />
          <div style={{ width: 1, background: T.line, margin: '0 18px' }} />
          <KPIStat label="전체 수익률 (연)" value={(m.ret > 0 ? '+' : '') + m.ret.toFixed(1)} unit="%" color={dirColor(m.ret)} sub="가중평균 기대수익" />
          <div style={{ width: 1, background: T.line, margin: '0 18px' }} />
          <KPIStat label="포트폴리오 리스크" value={Math.round(m.risk).toLocaleString()} unit="억" sub={`CVaR 99% · 변동성 ${m.vol.toFixed(1)}%`} />
          <div style={{ width: 1, background: T.line, margin: '0 18px' }} />
          <KPIStat label="잔여 리스크 한도" value={Math.round(m.remain).toLocaleString()} unit="억" color={m.remain < 0 ? T.alert : utilColor(m.util)} sub={`한도 ${fmtEok(pf.limit)} · 소진 ${m.util.toFixed(1)}%`} subColor={m.util >= 80 ? T.warn : T.c400} />
          {asset === '주식' && <React.Fragment>
            <div style={{ width: 1, background: T.line, margin: '0 18px' }} />
            <KPIStat label="델타 (KOSPI 기준)" value={Math.round(kospiDelta(pf)).toLocaleString()} unit="억" sub="보유주식 베타 가중 노출" />
          </React.Fragment>}
        </div>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, padding: '0 20px 15px' }}>
          <span style={{ fontFamily: T.font, fontWeight: 600, fontSize: 11, color: T.c500, flex: 'none' }}>한도 소진 <b className="num" style={{ color: m.util >= 100 ? T.alert : m.util >= 80 ? T.warn : T.c700 }}>{m.util.toFixed(1)}%</b></span>
          <div style={{ flex: 1 }}><LimitMeter pct={m.util} h={7} /></div>
          <span className="num" style={{ fontFamily: T.font, fontWeight: 500, fontSize: 10.5, color: T.c400, flex: 'none' }}>한도 {Math.round(pf.limit).toLocaleString()}억 · 잔여 {Math.round(m.remain).toLocaleString()}억</span>
        </div>
      </Card>

      {mode === '담당자' && <DealerLimitsCard emp={emp} asset={asset} pf={pf} />}

      {/* ② 포트폴리오(보유 종목) 현황 */}
      <StepHead n={2} title="포트폴리오 현황" sub="보유 종목 비중과 리스크 기여 구성" />
      <div style={{ display: 'grid', gridTemplateColumns: '400px 1fr', gap: 16, alignItems: 'stretch' }}>
        <Card style={{ display: 'flex', flexDirection: 'column' }}>
          <CardHead title="포트폴리오 구성" sub={`보유 ${pf.holds.length}종목`} icon="pie"
            right={<Segmented options={compKeys} value={comp} onChange={setComp} size="sm" />} />
          <div style={{ padding: '20px 22px', flex: 1, display: 'flex', alignItems: 'center' }}>
            <Donut data={compData} size={176} thick={27} centerTop={comp === '종목' ? '종목수' : comp} centerBig={comp === '종목' ? pf.holds.length : compData.length} unit={comp === '종목' ? '' : '개'} />
          </div>
        </Card>
        <Card style={{ display: 'flex', flexDirection: 'column' }}>
          <CardHead title="보유 종목" sub="비중·수익률·리스크 기여" icon="briefcase"
            right={<Pill kind="neutral" small dot>{asset} {pf.holds.length}종목</Pill>} />
          <DataTable dense initialSort={{ k: 'w', dir: 'desc' }} maxH={352}
            rows={pf.holds.map((h) => {const u = UMAP[h.id];return { _key: h.id, id: h.id, name: u.name, country: u.country, sector: u.sector, rating: u.rating, dur: u.dur, w: h.w, ret: u.ret, vol: u.vol, ctr: (cmap[h.id] || 0) * 100, ctrAmt: (cmap[h.id] || 0) * m.risk, dv01: u.type === '채권' ? pf.value * h.w / 100 * u.dur * 0.0001 : null };})}
            cols={[
            { k: 'name', t: '종목', w: '17%', render: (r) => <span style={{ display: 'inline-flex', alignItems: 'center', gap: 7, fontWeight: 700, color: T.c900 }}><span style={{ width: 7, height: 7, borderRadius: 9, background: SECTOR_HUE[r.sector] || T.c400 }} />{r.name}</span> },
            { k: 'country', t: '국가', w: 58, render: (r) => <span style={{ color: T.c500 }}>{r.country}</span> },
            ...(asset === '주식' ? [
              { k: 'sector', t: '섹터', render: (r) => <span style={{ color: T.c500 }}>{r.sector}</span> },
              { k: 'fac', t: '주 노출 요인', render: (r) => <span style={{ display: 'inline-flex', gap: 4 }}>{mainFactorsOf(r.id).map((fid) => {const f = FMAP[fid];
                return <span key={fid} style={{ fontFamily: T.font, fontWeight: 700, fontSize: 10.5, color: f.color, background: f.color + '14', border: '1px solid ' + f.color + '33', borderRadius: 999, padding: '2px 8px', whiteSpace: 'nowrap' }}>{f.name === '변동성·개별' ? '변동성' : f.name}</span>;})}</span>, sortVal: (r) => mainFactorsOf(r.id)[0] },
            ] : [
              { k: 'rating', t: '등급', w: 58, render: (r) => <span style={{ fontWeight: 600, color: T.c600 }}>{r.rating}</span> },
              { k: 'dur', t: '듀레이션', num: true, render: (r) => <span className="num" style={{ color: T.c600 }}>{r.dur.toFixed(1)}Y</span>, sortVal: (r) => r.dur },
              { k: 'dv01', t: 'DV01', num: true, render: (r) => <span className="num" style={{ color: T.c600 }}>{bfMn(r.dv01)}백만</span>, sortVal: (r) => r.dv01 },
            ]),
            { k: 'w', t: '비중', num: true, render: (r) => <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, justifyContent: 'flex-end' }}><MiniBar pct={r.w * 2.8} color={T.blue} w={48} /><b className="num" style={{ color: T.c900, width: 46, textAlign: 'right' }}>{r.w.toFixed(1)}%</b></span>, sortVal: (r) => r.w },
            { k: 'ret', t: '수익률', num: true, render: (r) => <Delta v={r.ret} suffix="%" digits={1} size={12.5} />, sortVal: (r) => r.ret },
            { k: 'stopz', t: '손절 구간', num: true, render: (r) => <StopZoneCell ret={r.ret} />, sortVal: (r) => stopZoneOf(r.ret) * 1000 - r.ret },
            { k: 'ctr', t: '리스크 기여', num: true, render: (r) => <span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, justifyContent: 'flex-end' }}><MiniBar pct={r.ctr * 2.2} color={r.ctr > 20 ? T.alert : r.ctr > 12 ? T.warn : T.c400} w={48} /><b className="num" style={{ color: T.c900, width: 42, textAlign: 'right' }}>{r.ctr.toFixed(1)}%</b></span>, sortVal: (r) => r.ctr },
            { k: 'ctrAmt', t: '기여 위험액', num: true, render: (r) => <span className="num" style={{ color: T.c600 }}>{Math.round(r.ctrAmt).toLocaleString()}억</span>, sortVal: (r) => r.ctrAmt }]
            } />
        </Card>
      </div>

      {/* ③ 리스크 요인 분석 */}
      <StepHead n={3} title="리스크 요인 분석" sub={asset === '주식' ? 'CVaR를 국가·섹터·종목으로 분해해 리스크 집중도와 헤징 포인트를 파악' : '금리·신용·통화 감도를 계량화해 포지션 위험과 헤지 효과를 측정'} />
      {asset === '주식' ? <EquityFactorSection pf={pf} /> : <BondFactorSection pf={pf} />}

      {/* ④ 투자 시뮬레이션 + AI 최적화 */}
      <StepHead n={4} title="투자 시뮬레이션" sub="거래 실행 전 리스크 영향을 시뮬레이션하고 AI로 최적 비중을 산출" />
      <Card>
        <CardHead title={tool === 'trade' ? '모의 매수 · 매도' : 'AI 최적화'} sub={tool === 'trade' ? '가상 매매에 따른 리스크 실시간 재측정' : '목표 기반 권장 비중 산출'} icon="sliders"
          right={<Segmented options={[{ v: 'trade', l: '모의 매수 · 매도' }, { v: 'opt', l: 'AI 최적화' }]} value={tool} onChange={setTool} size="sm" />} />
        <div style={{ padding: '20px 22px 24px' }}>
          {tool === 'trade' && <AddStockPanel pf={pf} onCommit={commit} asset={asset} />}
          {tool === 'opt' && <OptimizePanel pf={pf} onCommit={commit} asset={asset} />}
        </div>
      </Card>

      {/* ⑤ 스트레스 테스트 */}
      <StepHead n={5} title="스트레스 테스트" sub="충격 강도를 직접 설정하거나 과거 위기를 재현해 잠재 손실 규모를 사전에 파악" />
      <Card>
        <CardHead title={stress === 'single' ? '단일 요인 스트레스' : '시나리오 테스트'} sub={stress === 'single' ? '요인별 충격을 조정해 감도를 확인' : '과거 위기 시나리오 기반 복합 충격'} icon="alert"
          right={<Segmented options={[{ v: 'single', l: '단일 요인' }, { v: 'scenario', l: '시나리오 테스트' }]} value={stress} onChange={setStress} size="sm" />} />
        <div style={{ padding: '20px 22px 24px' }}>
          {stress === 'single' && <FactorStress pf={pf} keys={asset === '주식' ? ['eq', 'fx'] : ['rate', 'credit', 'fx']} />}
          {stress === 'scenario' && <SimulatePanel pf={pf} />}
        </div>
      </Card>
    </div>);
}
Object.assign(window, { Portfolio, PersonPicker, DeptPicker, KPIStat, StepHead, DealerLimitsCard });
