/**
 * HR 관리 공통 데이터 + 컴포넌트
 *   - HR_DATA: 직원 / 출퇴근 / 급여 / 알림 / 스케줄 / 히트맵
 *   - 컴포넌트: Avatar, DeptBadge, BranchFilter, AttendHeatmap, OrgMini, WeekStrip 등
 *   - 끼니저 컬러 토큰(C) + 기존 dashboard-shared의 KpiCard/LineChart/Sparkline/TrendBadge 재사용
 */

// 부서별 컬러 (이니셜 아바타 + 배지)
const DEPT = {
  '조리':   { color:'#ea580c', bg:'#fff7ed', label:'조리·주방' },
  '사무':   { color:'#2563eb', bg:'#eff6ff', label:'본사 사무' },
  '배송':   { color:'#16a34a', bg:'#f0fdf4', label:'배송 기사' },
  '파트':   { color:'#8b5cf6', bg:'#f5f3ff', label:'파트타임' },
};

// 본사 + 5지점
const BRANCHES = [
  { key:'all',  label:'전체',   count:24 },
  { key:'hq',   label:'본사',   count:6 },
  { key:'gw',   label:'강서점', count:4 },
  { key:'gb',   label:'강북점', count:3 },
  { key:'mp',   label:'마포점', count:4 },
  { key:'gn',   label:'강남점', count:4 },
  { key:'sd',   label:'성동점', count:3 },
];

// 직원 24명
const EMPLOYEES = [
  // 본사 사무
  { id:1,  name:'김매니저', dept:'사무', branch:'hq', role:'운영팀장',  joinDate:'2021-03-15', salary:4200000, status:'근무중',  today:'출근',  arrival:'08:42' },
  { id:2,  name:'이대표',   dept:'사무', branch:'hq', role:'대표이사',  joinDate:'2019-01-01', salary:6500000, status:'근무중',  today:'출근',  arrival:'08:30' },
  { id:3,  name:'박재무',   dept:'사무', branch:'hq', role:'재무팀',    joinDate:'2022-06-01', salary:3800000, status:'근무중',  today:'출근',  arrival:'09:02' },
  { id:4,  name:'정총무',   dept:'사무', branch:'hq', role:'총무',      joinDate:'2023-02-10', salary:3200000, status:'근무중',  today:'지각',  arrival:'09:18' },
  { id:5,  name:'최인사',   dept:'사무', branch:'hq', role:'HR',        joinDate:'2022-09-01', salary:3600000, status:'근무중',  today:'출근',  arrival:'08:55' },
  { id:6,  name:'서비서',   dept:'사무', branch:'hq', role:'비서',      joinDate:'2024-01-15', salary:2900000, status:'근무중',  today:'연차',  arrival:null  },

  // 강서점 (조리 중심)
  { id:7,  name:'한주방',   dept:'조리', branch:'gw', role:'주방장',    joinDate:'2020-05-10', salary:4500000, status:'근무중',  today:'출근',  arrival:'06:00' },
  { id:8,  name:'문조리',   dept:'조리', branch:'gw', role:'조리사',    joinDate:'2022-03-15', salary:3400000, status:'근무중',  today:'출근',  arrival:'06:05' },
  { id:9,  name:'안조리',   dept:'조리', branch:'gw', role:'조리사',    joinDate:'2023-08-20', salary:3100000, status:'근무중',  today:'결근',  arrival:null  },
  { id:10, name:'오파트',   dept:'파트', branch:'gw', role:'홀서빙',    joinDate:'2024-04-01', salary:1900000, status:'근무중',  today:'출근',  arrival:'10:00' },

  // 강북점
  { id:11, name:'장주방',   dept:'조리', branch:'gb', role:'주방장',    joinDate:'2021-09-05', salary:4300000, status:'근무중',  today:'출근',  arrival:'06:00' },
  { id:12, name:'윤조리',   dept:'조리', branch:'gb', role:'조리사',    joinDate:'2023-11-01', salary:3000000, status:'근무중',  today:'지각',  arrival:'06:32' },
  { id:13, name:'신파트',   dept:'파트', branch:'gb', role:'설거지',    joinDate:'2024-09-15', salary:1700000, status:'수습',    today:'출근',  arrival:'09:00' },

  // 마포점
  { id:14, name:'권주방',   dept:'조리', branch:'mp', role:'주방장',    joinDate:'2020-11-20', salary:4400000, status:'근무중',  today:'출근',  arrival:'05:55' },
  { id:15, name:'배조리',   dept:'조리', branch:'mp', role:'조리사',    joinDate:'2022-12-10', salary:3300000, status:'근무중',  today:'출근',  arrival:'06:08' },
  { id:16, name:'송파트',   dept:'파트', branch:'mp', role:'홀서빙',    joinDate:'2025-04-20', salary:1800000, status:'수습',    today:'출근',  arrival:'10:00' },
  { id:17, name:'홍파트',   dept:'파트', branch:'mp', role:'설거지',    joinDate:'2024-07-05', salary:1700000, status:'근무중',  today:'결근',  arrival:null  },

  // 강남점
  { id:18, name:'고주방',   dept:'조리', branch:'gn', role:'주방장',    joinDate:'2021-02-01', salary:4500000, status:'근무중',  today:'출근',  arrival:'05:50' },
  { id:19, name:'노조리',   dept:'조리', branch:'gn', role:'조리사',    joinDate:'2023-05-15', salary:3200000, status:'근무중',  today:'출근',  arrival:'06:00' },
  { id:20, name:'유파트',   dept:'파트', branch:'gn', role:'홀서빙',    joinDate:'2024-11-01', salary:1900000, status:'근무중',  today:'출근',  arrival:'10:05' },

  // 성동점
  { id:21, name:'임주방',   dept:'조리', branch:'sd', role:'주방장',    joinDate:'2022-01-10', salary:4200000, status:'근무중',  today:'출근',  arrival:'06:02' },
  { id:22, name:'전조리',   dept:'조리', branch:'sd', role:'조리사',    joinDate:'2024-02-20', salary:3000000, status:'근무중',  today:'출근',  arrival:'06:10' },

  // 배송 기사 (전사)
  { id:23, name:'김기사',   dept:'배송', branch:'hq', role:'정규',      joinDate:'2022-08-01', salary:3500000, status:'근무중',  today:'출근',  arrival:'05:30' },
  { id:24, name:'이기사',   dept:'배송', branch:'hq', role:'정규',      joinDate:'2023-04-15', salary:3300000, status:'근무중',  today:'출근',  arrival:'05:35' },
];

// 출근율 히트맵 (8주 × 7일, 0~1)
const HEATMAP = (() => {
  const seed = [
    [0.95,0.92,0.96,0.92,0.88,0.50,0.30],
    [0.96,0.94,0.95,0.90,0.92,0.55,0.30],
    [0.93,0.91,0.92,0.88,0.85,0.45,0.25],
    [0.97,0.95,0.96,0.93,0.94,0.60,0.35],
    [0.92,0.88,0.84,0.78,0.86,0.50,0.30],  // 결근 많은 주
    [0.96,0.94,0.95,0.95,0.92,0.55,0.30],
    [0.95,0.94,0.93,0.90,0.91,0.58,0.32],
    [0.96,0.95,0.92,0.88,null,null,null],   // 이번주 (목요일까지)
  ];
  return seed;
})();

const HR_DATA = {
  asOf: '2025-05-15',
  total: 24,
  todayPresent: 19,   // 출근
  todayLate: 2,       // 지각
  todayAbsent: 2,     // 결근
  todayLeave: 1,      // 연차
  todayRate: 79,      // 출근율 % (지각 포함, 결근 제외)
  newJoinThisMonth: 2,
  leaveThisMonth: 0,
  pendingApprovals: 7,
  monthLateAbsent: 14,

  // 이번달 인건비 (직원수 × 일별 비례)
  payroll: {
    thisMonth: 78_400_000,
    lastMonth: 72_300_000,
    diff: 8.4,
    daily: [2.4,2.5,2.5,2.6,2.5,2.6,2.7,2.7,2.6,2.7,2.8,2.7,2.8,2.9,2.8],
    byDept: [
      { name:'조리·주방', value:42_500_000, ratio:54, color:DEPT['조리'].color },
      { name:'본사 사무', value:24_200_000, ratio:31, color:DEPT['사무'].color },
      { name:'배송 기사', value:6_800_000,  ratio:9,  color:DEPT['배송'].color },
      { name:'파트타임',   value:4_900_000,  ratio:6,  color:DEPT['파트'].color },
    ],
  },

  // 알림 (5종)
  alerts: {
    absentLate: [   // 결근/지각
      { name:'안조리', dept:'조리', branch:'강서점',   text:'무단 결근 · 9:30 연락 두절',   age:'오늘',  level:'high' },
      { name:'홍파트', dept:'파트', branch:'마포점',   text:'결근 · 가족 사정 (대체 필요)', age:'오늘',  level:'high' },
      { name:'정총무', dept:'사무', branch:'본사',     text:'9:18 출근 · 18분 지각',        age:'오늘',  level:'mid'  },
      { name:'윤조리', dept:'조리', branch:'강북점',   text:'6:32 출근 · 32분 지각',        age:'오늘',  level:'mid'  },
    ],
    payrollMissing: [ // 급여 누락/오류
      { name:'송파트', dept:'파트', branch:'마포점',   text:'4월 정산 누락 · 12시간',       age:'1주 전', level:'high' },
      { name:'신파트', dept:'파트', branch:'강북점',   text:'시급 인상 미반영',             age:'3일 전', level:'mid'  },
    ],
    scheduleEdit: [ // 근무 수정 요청 (직원이 신청)
      { name:'문조리', dept:'조리', branch:'강서점',   text:'5/18 휴무 → 5/19 변경 요청',    age:'오늘',   level:'mid'  },
      { name:'장주방', dept:'조리', branch:'강북점',   text:'5/20 연차 신청',                age:'어제',   level:'mid'  },
      { name:'오파트', dept:'파트', branch:'강서점',   text:'5/22 조퇴 신청',                age:'2일 전', level:'low'  },
    ],
    notice: [       // 공지사항
      { name:'전 직원',   dept:null, branch:'전사', text:'5월 회식 공지 · 5/30 19:00',           age:'어제',   level:'low'  },
      { name:'조리팀',     dept:'조리', branch:'전사', text:'주방 위생 점검 일정 (6/2)',           age:'2일 전', level:'low'  },
    ],
    supplyRequest: [ // 비품 구매 요청
      { name:'한주방', dept:'조리', branch:'강서점', text:'식칼 5개 + 도마 3개 (₩145,000)',     age:'오늘',   level:'mid'  },
      { name:'권주방', dept:'조리', branch:'마포점', text:'주방세제 박스 단위 (₩58,000)',         age:'어제',   level:'low'  },
      { name:'정총무', dept:'사무', branch:'본사',   text:'A4 용지 + 토너 (₩78,000)',             age:'3일 전', level:'low'  },
    ],
  },

  // 주간 스케줄 (이번주 6명 샘플 — 5/12 월 ~ 5/18 일)
  weekSchedule: [
    { id:7,  name:'한주방',  dept:'조리', shifts:['주','주','주','주','주','휴','휴'] },
    { id:8,  name:'문조리',  dept:'조리', shifts:['주','주','휴','주','주','주','휴'] },
    { id:11, name:'장주방',  dept:'조리', shifts:['주','주','주','주','주','휴','휴'] },
    { id:14, name:'권주방',  dept:'조리', shifts:['주','주','주','주','주','주','휴'] },
    { id:23, name:'김기사',  dept:'배송', shifts:['새','새','새','새','새','휴','휴'] },
    { id:24, name:'이기사',  dept:'배송', shifts:['새','새','새','새','새','새','휴'] },
  ],

  // 조직도 (간단 트리)
  org: {
    name:'이대표', role:'대표이사', dept:'사무',
    children:[
      { name:'김매니저', role:'운영팀장', dept:'사무', children:[
        { name:'한주방', role:'강서점 주방장', dept:'조리', count:4 },
        { name:'장주방', role:'강북점 주방장', dept:'조리', count:3 },
        { name:'권주방', role:'마포점 주방장', dept:'조리', count:4 },
        { name:'고주방', role:'강남점 주방장', dept:'조리', count:3 },
        { name:'임주방', role:'성동점 주방장', dept:'조리', count:2 },
      ]},
      { name:'박재무', role:'재무팀',   dept:'사무', count:0 },
      { name:'최인사', role:'HR',       dept:'사무', count:1 },
      { name:'김기사', role:'배송팀',   dept:'배송', count:2 },
    ]
  },

  // 금일 출근 스케줄 — 직원별 (AttendanceList / ScheduleCalendar 참고)
  // status: in(출근중) / done(퇴근) / late(지각) / absent(결근) / leave(연차) / off(휴무) / wait(예정)
  todayAttendance: [
    { name:'한주방', dept:'조리', branch:'강서점', shift:'06:00 – 15:00', clockIn:'05:54', clockOut:null,    status:'in',    note:'주방장' },
    { name:'문조리', dept:'조리', branch:'강서점', shift:'06:00 – 15:00', clockIn:'05:58', clockOut:null,    status:'in',    note:'' },
    { name:'안조리', dept:'조리', branch:'강서점', shift:'06:00 – 15:00', clockIn:null,    clockOut:null,    status:'absent',note:'무단 · 9:30 연락두절' },
    { name:'장주방', dept:'조리', branch:'강북점', shift:'06:00 – 15:00', clockIn:'06:02', clockOut:null,    status:'in',    note:'' },
    { name:'윤조리', dept:'조리', branch:'강북점', shift:'06:00 – 15:00', clockIn:'06:32', clockOut:null,    status:'late',  note:'32분 지각' },
    { name:'권주방', dept:'조리', branch:'마포점', shift:'06:00 – 15:00', clockIn:'05:51', clockOut:null,    status:'in',    note:'' },
    { name:'고주방', dept:'조리', branch:'강남점', shift:'06:00 – 15:00', clockIn:'05:55', clockOut:null,    status:'in',    note:'' },
    { name:'임주방', dept:'조리', branch:'성동점', shift:'06:00 – 15:00', clockIn:'06:00', clockOut:null,    status:'in',    note:'' },
    { name:'김기사', dept:'배송', branch:'강서점', shift:'05:30 – 11:30', clockIn:'05:25', clockOut:'11:18', status:'done',  note:'A노선 14곳 완료' },
    { name:'이기사', dept:'배송', branch:'강북점', shift:'05:45 – 12:00', clockIn:'05:40', clockOut:null,    status:'in',    note:'B노선 9/12 진행중' },
    { name:'박기사', dept:'배송', branch:'마포점', shift:'05:30 – 12:00', clockIn:'05:33', clockOut:null,    status:'in',    note:'C노선 7/11' },
    { name:'홍파트', dept:'파트', branch:'마포점', shift:'09:00 – 14:00', clockIn:null,    clockOut:null,    status:'absent',note:'결근 · 가족 사정' },
    { name:'오파트', dept:'파트', branch:'강서점', shift:'09:00 – 14:00', clockIn:'08:56', clockOut:null,    status:'in',    note:'' },
    { name:'송파트', dept:'파트', branch:'마포점', shift:'09:00 – 14:00', clockIn:'08:58', clockOut:null,    status:'in',    note:'' },
    { name:'정총무', dept:'사무', branch:'본사',   shift:'09:00 – 18:00', clockIn:'09:18', clockOut:null,    status:'late',  note:'18분 지각' },
    { name:'박재무', dept:'사무', branch:'본사',   shift:'09:00 – 18:00', clockIn:'08:52', clockOut:null,    status:'in',    note:'' },
    { name:'최인사', dept:'사무', branch:'본사',   shift:'연차',           clockIn:null,    clockOut:null,    status:'leave', note:'5/15 연차 승인' },
    { name:'김매니저',dept:'사무', branch:'본사',   shift:'09:00 – 18:00', clockIn:'08:45', clockOut:null,    status:'in',    note:'' },
  ],

  // (legacy) 금일 배송 스케줄 — 보존
  todayDelivery: [
    { route:'A노선 · 강남권',  driver:'김기사', branch:'강서점', startAt:'05:30', total:14, done:14, etaLast:'09:40', status:'done',
      stops:[{name:'네이버 1784',hh:'07:10'},{name:'토스 강남센터',hh:'07:35'},{name:'쏘카',hh:'08:05'},{name:'외 11곳',hh:'~09:40'}] },
    { route:'B노선 · 강북·종로', driver:'이기사', branch:'강북점', startAt:'05:45', total:12, done:9,  etaLast:'10:15', status:'live',
      stops:[{name:'카카오 광화문',hh:'08:42'},{name:'NHN 종로',hh:'09:05'},{name:'세브란스 별관',hh:'09:30'},{name:'외 9곳',hh:'~10:15'}] },
    { route:'C노선 · 마포·여의도', driver:'박기사', branch:'마포점', startAt:'05:30', total:11, done:7,  etaLast:'10:30', status:'live',
      stops:[{name:'당근마켓',hh:'08:55'},{name:'CJ ENM',hh:'09:20'},{name:'더현대 사무동',hh:'09:50'},{name:'외 8곳',hh:'~10:30'}] },
    { route:'D노선 · 성동·왕십리', driver:'최기사', branch:'본사',   startAt:'06:00', total:9,  done:0,  etaLast:'10:50', status:'wait',
      stops:[{name:'쿠팡 성수',hh:'08:15'},{name:'무신사',hh:'08:35'},{name:'젠틀몬스터',hh:'09:00'},{name:'외 6곳',hh:'~10:50'}] },
    { route:'E노선 · 잠실·송파', driver:'정기사', branch:'강남점', startAt:'05:30', total:10, done:10, etaLast:'09:25', status:'done',
      stops:[{name:'롯데월드타워',hh:'07:40'},{name:'SK 올림픽',hh:'08:10'},{name:'장은성HQ',hh:'08:35'},{name:'외 7곳',hh:'~09:25'}] },
  ],

  // 최근 공지사항 (목록 + 등록 폼에서 사용)
  notices: [
    { title:'5월 회식 공지 · 5/30 19:00 신라각',         target:'전사',    by:'이대표', at:'어제 17:20', views:21, type:'공지' },
    { title:'주방 위생 점검 일정 · 6/2(월) 14:00',       target:'조리팀',  by:'김매니저', at:'2일 전',     views:11, type:'공지' },
    { title:'급여 명세서 발송 안내 · 5/25(일) 자동발송',  target:'전사',    by:'박재무', at:'4일 전',     views:24, type:'안내' },
  ],

  // 근속연수 분포
  tenure: [
    { range:'1년 미만', count:5,  color:'#e0e7ff' },
    { range:'1~2년',   count:8,  color:'#a5b4fc' },
    { range:'2~3년',   count:6,  color:'#6366f1' },
    { range:'3~5년',   count:3,  color:'#4338ca' },
    { range:'5년+',     count:2,  color:'#312e81' },
  ],
};

// ── 알림 카테고리 메타 ──
const ALERT_META = {
  absentLate:     { title:'결근·지각',     color:'#dc2626', icon:'⚠' },
  payrollMissing: { title:'급여 정산 이슈', color:'#ea580c', icon:'₩' },
  scheduleEdit:   { title:'근무 수정 요청', color:'#0ea5e9', icon:'📝' },
  notice:         { title:'공지사항',       color:'#8b5cf6', icon:'📢' },
  supplyRequest:  { title:'비품 구매 요청', color:'#0d9488', icon:'📦' },
};

// ─────────────── 컴포넌트 ───────────────

/* 이니셜 + 부서별 컬러 아바타 */
const Avatar = ({ name, dept, size=32, ring=false }) => {
  const d = DEPT[dept] || { color:'#64748b', bg:'#f1f5f9' };
  const initial = (name||'?').slice(-2);
  return (
    <div title={name} style={{
      width:size, height:size, borderRadius:'50%',
      background:d.bg, color:d.color,
      display:'flex', alignItems:'center', justifyContent:'center',
      fontSize: size*0.42, fontWeight:800, letterSpacing:'-0.5px',
      flexShrink:0, border: ring ? `2px solid ${d.color}` : `1.5px solid ${d.color}33`,
    }}>{initial}</div>
  );
};

/* 부서 배지 */
const DeptBadge = ({ dept, size='md' }) => {
  if (!dept) return null;
  const d = DEPT[dept] || { color:'#64748b', bg:'#f1f5f9', label:dept };
  const small = size==='sm';
  return (
    <span style={{
      display:'inline-flex', alignItems:'center', gap:4,
      padding: small ? '1px 6px' : '2px 8px',
      fontSize: small ? 9 : 10,
      fontWeight:700,
      color:d.color, background:d.bg,
      border:`1px solid ${d.color}33`,
      borderRadius:3, whiteSpace:'nowrap',
    }}>
      <span style={{ width:5, height:5, borderRadius:'50%', background:d.color }}/>
      {d.label.replace('·주방','').replace('본사 ','').replace(' 기사','')}
    </span>
  );
};

/* 지점 필터 — 칩 가로 스크롤 */
const BranchFilter = ({ value='all', onChange=()=>{}, accent='#16a34a' }) => (
  <div style={{ display:'flex', gap:6, overflowX:'auto', paddingBottom:2 }}>
    {BRANCHES.map(b => {
      const on = value === b.key;
      return (
        <button key={b.key} onClick={()=>onChange(b.key)} style={{
          padding:'5px 12px', fontSize:11, fontWeight:on?700:600,
          color: on ? '#fff' : '#475569',
          background: on ? accent : '#fff',
          border: `1px solid ${on?accent:'#e2e8f0'}`,
          borderRadius:14, cursor:'pointer', whiteSpace:'nowrap',
          display:'flex', alignItems:'center', gap:5,
        }}>
          {b.label}
          <span style={{ fontSize:9, fontWeight:700, opacity:on?0.85:0.6 }}>{b.count}</span>
        </button>
      );
    })}
  </div>
);

/* 출근율 히트맵 — 8주 × 7일 */
const AttendHeatmap = ({ data=HEATMAP }) => {
  const days = ['월','화','수','목','금','토','일'];
  const cell = 14;
  const colorFor = (v) => {
    if (v === null) return '#f8fafc';
    if (v >= 0.95) return '#15803d';
    if (v >= 0.90) return '#22c55e';
    if (v >= 0.80) return '#86efac';
    if (v >= 0.50) return '#fde68a';
    if (v > 0)     return '#f87171';
    return '#f1f5f9';
  };
  return (
    <div style={{ display:'flex', gap:8 }}>
      <div style={{ display:'flex', flexDirection:'column', justifyContent:'space-around', fontSize:9, color:'#94a3b8', paddingTop:1 }}>
        {days.map(d => <div key={d} style={{ height:cell, lineHeight:`${cell}px` }}>{d}</div>)}
      </div>
      <div style={{ display:'grid', gridTemplateColumns:`repeat(${data.length}, ${cell}px)`, gap:3 }}>
        {data.map((week, wi) => (
          <div key={wi} style={{ display:'grid', gridTemplateRows:`repeat(7, ${cell}px)`, gap:3 }}>
            {week.map((v, di) => (
              <div key={di} title={v===null?'-':`${Math.round(v*100)}%`}
                style={{ width:cell, height:cell, borderRadius:2, background:colorFor(v) }}/>
            ))}
          </div>
        ))}
      </div>
      <div style={{ display:'flex', flexDirection:'column', gap:3, justifyContent:'flex-end', fontSize:9, color:'#94a3b8' }}>
        <div style={{ display:'flex', alignItems:'center', gap:3 }}><span style={{ width:9, height:9, background:'#15803d', borderRadius:1 }}/>95+</div>
        <div style={{ display:'flex', alignItems:'center', gap:3 }}><span style={{ width:9, height:9, background:'#86efac', borderRadius:1 }}/>80+</div>
        <div style={{ display:'flex', alignItems:'center', gap:3 }}><span style={{ width:9, height:9, background:'#fde68a', borderRadius:1 }}/>50+</div>
        <div style={{ display:'flex', alignItems:'center', gap:3 }}><span style={{ width:9, height:9, background:'#f87171', borderRadius:1 }}/>저조</div>
      </div>
    </div>
  );
};

/* 조직도 미니 */
const OrgMini = ({ data=HR_DATA.org, compact=false }) => {
  const Node = ({ n, top=false }) => {
    const d = DEPT[n.dept] || { color:'#64748b' };
    return (
      <div style={{
        background: top ? '#0f172a' : '#fff',
        color: top ? '#fff' : '#0f172a',
        border: top ? 'none' : `1px solid ${d.color}33`,
        borderLeft: top ? 'none' : `3px solid ${d.color}`,
        borderRadius:6, padding: compact?'6px 10px':'8px 12px',
        display:'flex', alignItems:'center', gap:8, minWidth:0,
      }}>
        <Avatar name={n.name} dept={n.dept} size={compact?22:26}/>
        <div style={{ minWidth:0, flex:1 }}>
          <div style={{ fontSize: compact?11:12, fontWeight:700, whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{n.name}</div>
          <div style={{ fontSize: compact?9:10, color: top?'#cbd5e1':'#94a3b8', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{n.role}</div>
        </div>
        {n.count > 0 && <span style={{ fontSize:9, fontWeight:700, color:top?'#cbd5e1':'#64748b', background:top?'rgba(255,255,255,0.1)':'#f1f5f9', padding:'2px 6px', borderRadius:8 }}>+{n.count}</span>}
      </div>
    );
  };
  return (
    <div style={{ display:'flex', flexDirection:'column', gap:8 }}>
      <Node n={data} top={true}/>
      <div style={{ paddingLeft:18, borderLeft:'1.5px dashed #cbd5e1', marginLeft:14, display:'flex', flexDirection:'column', gap:6 }}>
        {data.children.map((c,i) => (
          <div key={i}>
            <Node n={c}/>
            {c.children && (
              <div style={{ paddingLeft:18, borderLeft:'1.5px dashed #e2e8f0', marginLeft:14, marginTop:6, display:'flex', flexDirection:'column', gap:5 }}>
                {c.children.map((cc,j) => <Node key={j} n={cc} compact/>)}
              </div>
            )}
          </div>
        ))}
      </div>
    </div>
  );
};

/* 주간 스케줄 스트립 */
const WeekStrip = ({ data=HR_DATA.weekSchedule, days=['12월','13화','14수','15목','16금','17토','18일'] }) => {
  const SHIFT = {
    '주': { bg:'#dcfce7', fg:'#166534', label:'주간' },
    '오': { bg:'#fef3c7', fg:'#a16207', label:'오후' },
    '새': { bg:'#dbeafe', fg:'#1e40af', label:'새벽' },
    '야': { bg:'#ede9fe', fg:'#6d28d9', label:'야간' },
    '휴': { bg:'#f1f5f9', fg:'#94a3b8', label:'휴무' },
  };
  return (
    <div style={{ background:'#fff', borderRadius:8, border:'1px solid #e2e8f0', overflow:'hidden' }}>
      <div style={{ display:'grid', gridTemplateColumns:'120px repeat(7, 1fr)', background:'#f8fafc', borderBottom:'1px solid #e2e8f0', fontSize:10, color:'#64748b', fontWeight:700 }}>
        <div style={{ padding:'8px 12px' }}>이름</div>
        {days.map((d,i) => (
          <div key={i} style={{ padding:'8px 0', textAlign:'center', color: i===3 ? '#16a34a' : (i>=5?'#dc2626':'#64748b') }}>{d}</div>
        ))}
      </div>
      {data.map(emp => (
        <div key={emp.id} style={{ display:'grid', gridTemplateColumns:'120px repeat(7, 1fr)', borderBottom:'1px solid #f1f5f9', alignItems:'center' }}>
          <div style={{ padding:'8px 12px', display:'flex', alignItems:'center', gap:6 }}>
            <Avatar name={emp.name} dept={emp.dept} size={22}/>
            <span style={{ fontSize:11, fontWeight:600, color:'#0f172a' }}>{emp.name}</span>
          </div>
          {emp.shifts.map((s,i) => {
            const sh = SHIFT[s] || SHIFT['휴'];
            return (
              <div key={i} style={{ padding:'5px 6px', textAlign:'center', borderLeft:'1px solid #f8fafc', background: i===3 ? '#f0fdf4' : 'transparent' }}>
                <span style={{ display:'inline-block', padding:'2px 8px', fontSize:10, fontWeight:700, background:sh.bg, color:sh.fg, borderRadius:3, minWidth:32 }}>{sh.label}</span>
              </div>
            );
          })}
        </div>
      ))}
    </div>
  );
};

/* 도넛 차트 — 부서별 인건비 */
const DonutChart = ({ data, size=140, thickness=22 }) => {
  const total = data.reduce((s,d)=>s+d.value, 0);
  const r = (size - thickness) / 2;
  const c = 2 * Math.PI * r;
  let acc = 0;
  return (
    <svg width={size} height={size} viewBox={`0 0 ${size} ${size}`}>
      <circle cx={size/2} cy={size/2} r={r} fill="none" stroke="#f1f5f9" strokeWidth={thickness}/>
      {data.map((d,i) => {
        const len = (d.value / total) * c;
        const dash = `${len} ${c-len}`;
        const offset = c * 0.25 - acc;
        acc += len;
        return <circle key={i} cx={size/2} cy={size/2} r={r} fill="none" stroke={d.color} strokeWidth={thickness} strokeDasharray={dash} strokeDashoffset={offset} transform={`rotate(0 ${size/2} ${size/2})`}/>;
      })}
    </svg>
  );
};

/* HR 알림 행 — 부서/지점 칩 + 레벨 컬러바 */
const HrAlertItem = ({ alert, alertKey }) => {
  const meta = ALERT_META[alertKey];
  const lvlBg = alert.level==='high' ? '#fef2f2' : alert.level==='mid' ? '#fffbeb' : '#f8fafc';
  const lvlBar = alert.level==='high' ? '#dc2626' : alert.level==='mid' ? '#f59e0b' : '#cbd5e1';
  return (
    <div style={{ display:'flex', alignItems:'center', gap:10, padding:'10px 14px', borderBottom:'1px solid #f1f5f9', cursor:'pointer', position:'relative' }}
      onMouseEnter={e=>e.currentTarget.style.background=lvlBg}
      onMouseLeave={e=>e.currentTarget.style.background='transparent'}>
      <div style={{ position:'absolute', left:0, top:0, bottom:0, width:3, background:lvlBar }}/>
      {alert.dept ? <Avatar name={alert.name} dept={alert.dept} size={26}/> : <div style={{ width:26, height:26, borderRadius:'50%', background:'#f1f5f9', display:'flex', alignItems:'center', justifyContent:'center', fontSize:13 }}>{meta?.icon||'•'}</div>}
      <div style={{ flex:1, minWidth:0 }}>
        <div style={{ display:'flex', alignItems:'center', gap:6, marginBottom:2 }}>
          <span style={{ fontSize:12, fontWeight:700, color:'#0f172a' }}>{alert.name}</span>
          {alert.dept && <DeptBadge dept={alert.dept} size="sm"/>}
          <span style={{ fontSize:9, color:'#94a3b8' }}>{alert.branch}</span>
        </div>
        <div style={{ fontSize:11, color:'#475569', whiteSpace:'nowrap', overflow:'hidden', textOverflow:'ellipsis' }}>{alert.text}</div>
      </div>
      <span style={{ fontSize:10, color:'#94a3b8', fontWeight:600, flexShrink:0 }}>{alert.age}</span>
    </div>
  );
};

/* 알림 카테고리 박스 (HR) */
const HrAlertStack = ({ alertKey, items }) => {
  const meta = ALERT_META[alertKey];
  return (
    <div style={{ background:'#fff', border:'1px solid #e2e8f0', borderRadius:10, overflow:'hidden', display:'flex', flexDirection:'column', minHeight:0 }}>
      <div style={{ padding:'12px 16px', display:'flex', alignItems:'center', justifyContent:'space-between', background:'#fafbfc', borderBottom:'1px solid #e2e8f0' }}>
        <div style={{ display:'flex', alignItems:'center', gap:8 }}>
          <div style={{ width:24, height:24, borderRadius:5, background:`${meta.color}1a`, color:meta.color, display:'flex', alignItems:'center', justifyContent:'center', fontSize:12, fontWeight:700 }}>{meta.icon}</div>
          <span style={{ fontSize:13, fontWeight:800, color:'#0f172a' }}>{meta.title}</span>
        </div>
        <span style={{ fontSize:11, fontWeight:800, color:meta.color, background:'#fff', border:`1px solid ${meta.color}33`, padding:'2px 9px', borderRadius:11 }}>{items.length}건</span>
      </div>
      <div style={{ flex:1, overflow:'auto', minHeight:0 }}>
        {items.length===0 ? (
          <div style={{ padding:'20px', textAlign:'center', color:'#94a3b8', fontSize:11 }}>해당 항목이 없습니다</div>
        ) : items.map((it,i) => <HrAlertItem key={i} alert={it} alertKey={alertKey}/>)}
      </div>
    </div>
  );
};

/* HR 페이지 셸 — 헤더 + 지점 필터 */
const HrShell = ({ children, headline, subtitle, branch, setBranch, accent='#16a34a', actions }) => (
  <div style={{ height:'100%', background:'#eef2f7', display:'flex', flexDirection:'column', overflow:'hidden' }}>
    <div style={{ padding:'18px 28px 12px', background:'#fff', borderBottom:'1px solid #e2e8f0' }}>
      <div style={{ display:'flex', alignItems:'flex-start', justifyContent:'space-between', marginBottom:12 }}>
        <div>
          <div style={{ fontSize:11, color:'#94a3b8', fontWeight:600, marginBottom:2 }}>끼니저 · HR 관리</div>
          <div style={{ fontSize:20, fontWeight:900, color:'#0f172a', letterSpacing:'-0.4px' }}>{headline}</div>
          {subtitle && <div style={{ fontSize:12, color:'#64748b', marginTop:3, fontWeight:500 }}>{subtitle}</div>}
        </div>
        <div style={{ display:'flex', alignItems:'center', gap:8 }}>
          {actions}
          <button style={{ height:34, padding:'0 12px', borderRadius:6, border:'1px solid #e2e8f0', background:'#fff', color:'#475569', fontSize:12, fontWeight:600, cursor:'pointer' }}>📥 내보내기</button>
          <button style={{ height:34, padding:'0 14px', borderRadius:6, border:'none', background:accent, color:'#fff', fontSize:12, fontWeight:700, cursor:'pointer' }}>+ 직원 등록</button>
        </div>
      </div>
      <BranchFilter value={branch} onChange={setBranch} accent={accent}/>
    </div>
    <div style={{ flex:1, overflow:'auto' }}>{children}</div>
  </div>
);

Object.assign(window, {
  DEPT, BRANCHES, EMPLOYEES, HR_DATA, HEATMAP, ALERT_META,
  Avatar, DeptBadge, BranchFilter, AttendHeatmap, OrgMini, WeekStrip, DonutChart,
  HrAlertItem, HrAlertStack, HrShell,
});
