@extends('layouts.app')

@section('title', '업무 Workflow')

@section('content')
<div id="root"></div>
@endsection

@push('scripts')
<script src="https://cdn.tailwindcss.com"></script>
@include('partials.react-cdn')
<script type="text/babel">
@verbatim
const { useState, useRef, useEffect } = React;

// --- 업무 프로세스 데이터 (상세 서브플로우 포함) ---
const processes = [
    {
        id: 1, phase: 'sales', name: '영업', icon: '📋', dept: '영업팀',
        color: '#3B82F6', bgColor: '#EFF6FF',
        description: '건설사/시행사로부터 프로젝트 정보를 수집하고 영업 활동을 수행한다.',
        documents: ['입찰공고문', '현장조사서', '고객 요구사항서'],
        samLink: null, samMenu: null,
        subSteps: [
            {
                id: 'S1-1', name: '정보 수집', icon: '🔎',
                description: '나라장터, 건설사 공고, 인맥 등을 통해 프로젝트 정보를 수집한다.',
                input: ['나라장터 공고', '건설사 입찰 안내', '업계 네트워크 정보'],
                output: ['프로젝트 리스트 (엑셀)'],
                responsible: '영업 담당자',
                tips: ['주 2회 이상 나라장터/건설사 사이트 모니터링', '기존 거래처 정기 연락 유지'],
                duration: '수시',
            },
            {
                id: 'S1-2', name: '현장 실측', icon: '📐',
                description: '프로젝트 대상 현장을 방문하여 실측하고 요구사항을 파악한다.',
                input: ['도면 (건설사 제공)', '현장 위치 정보'],
                output: ['현장조사서', '실측 데이터', '현장 사진'],
                responsible: '영업 담당자 + 기술팀',
                tips: ['도면과 현장 차이 반드시 확인', '사진은 전경/상세 모두 촬영'],
                duration: '1~2일',
            },
            {
                id: 'S1-3', name: '고객 미팅', icon: '🤝',
                description: '건설사 담당자와 미팅하여 요구사항, 예산, 일정 등을 협의한다.',
                input: ['현장조사서', '회사 소개서', '시공 포트폴리오'],
                output: ['미팅 기록', '고객 요구사항서'],
                responsible: '영업 담당자',
                tips: ['고객 예산 범위를 사전에 파악', '경쟁사 동향 정보 수집'],
                duration: '1~3시간',
            },
            {
                id: 'S1-4', name: '프로젝트 검토', icon: '📊',
                description: '수집된 정보를 바탕으로 수익성을 평가하여 진행 여부를 결정한다.',
                input: ['고객 요구사항서', '예상 공사비', '경쟁 현황'],
                output: ['프로젝트 검토서 (Go/No-Go 결정)'],
                responsible: '영업팀장',
                tips: ['수익률 10% 미만 프로젝트는 팀장 승인 필요', '리스크 요소 반드시 기재'],
                duration: '1일',
            },
        ],
    },
    {
        id: 2, phase: 'estimate', name: '견적서 작성', icon: '🧮', dept: '견적팀',
        color: '#8B5CF6', bgColor: '#F5F3FF',
        description: '자재/인건비/경비를 산출하여 견적서를 작성한다. 입찰 프로젝트는 입찰가 산정에, 수의계약은 고객 제출용으로 활용한다.',
        documents: ['견적서', '단가산출서', '자재목록'],
        samLink: '/juil/estimate', samMenu: '견적/입찰/공사관리',
        subSteps: [
            {
                id: 'S2-1', name: '물량 산출', icon: '📏',
                description: '도면과 실측 데이터를 기반으로 자재별 물량(수량)을 산출한다.',
                input: ['도면', '실측 데이터', '현장조사서'],
                output: ['물량산출서 (자재별 수량)'],
                responsible: '견적 담당자',
                tips: ['로스율(5~10%) 반드시 반영', '현장 여건에 따른 추가 물량 고려'],
                duration: '2~3일',
            },
            {
                id: 'S2-2', name: '단가 산정', icon: '💵',
                description: '자재 단가, 인건비, 장비비, 경비 등 각 항목의 단가를 산정한다.',
                input: ['물량산출서', '최근 자재 시세', '노무단가표'],
                output: ['단가산출서'],
                responsible: '견적 담당자',
                tips: ['자재 시세는 최근 3개월 평균 적용', '노무단가는 대한건설협회 기준 참조'],
                duration: '1~2일',
            },
            {
                id: 'S2-3', name: '견적가 산출', icon: '🧮',
                description: '물량 x 단가를 계산하고, 이윤율/관리비를 적용하여 최종 견적가를 산출한다.',
                input: ['물량산출서', '단가산출서', '이윤율 기준'],
                output: ['원가계산서', '내부 견적서'],
                responsible: '견적팀장',
                tips: ['이윤율은 프로젝트 규모별 차등 적용', '원가 대비 견적가 검증 필수'],
                duration: '1일',
            },
            {
                id: 'S2-4', name: '견적서 작성/검토', icon: '📋',
                description: '고객 제출용 견적서를 작성하고, 팀장 검토 후 최종 확정한다.',
                input: ['원가계산서', '고객 요구 양식'],
                output: ['공식 견적서 (PDF)'],
                responsible: '견적팀장 → 대표이사 승인',
                tips: ['고객사 양식이 별도로 있는지 확인', '견적 유효기간 명시 (보통 30일)'],
                duration: '1일',
            },
        ],
    },
    {
        id: 3, phase: 'bid', name: '입찰 참여', icon: '🏷️', dept: '영업팀',
        color: '#EC4899', bgColor: '#FDF2F8',
        description: '공공/대형 프로젝트의 경우 견적서를 기반으로 입찰에 참여한다. 소규모 업체나 수의계약은 이 단계를 생략한다.',
        documents: ['입찰서', '기술제안서', '사업자등록증 사본'],
        samLink: '/juil/estimate', samMenu: '견적/입찰/공사관리',
        subSteps: [
            {
                id: 'S3-1', name: '입찰서류 준비', icon: '📂',
                description: '입찰 참여에 필요한 서류(사업자등록증, 실적증명, 재무제표 등)를 준비한다.',
                input: ['입찰 공고문 (필요서류 목록)', '회사 기본서류'],
                output: ['입찰서류 패키지'],
                responsible: '영업 담당자 + 경영지원팀',
                tips: ['서류 유효기간 확인 (인감증명 등 3개월)', '전자입찰 시 공인인증서 사전 확인'],
                duration: '2~3일',
            },
            {
                id: 'S2-2', name: '기술제안서 작성', icon: '📝',
                description: '시공 방법, 품질관리 계획, 안전관리 계획 등을 포함한 기술제안서를 작성한다.',
                input: ['현장조사서', '시공 실적', '품질/안전 매뉴얼'],
                output: ['기술제안서'],
                responsible: '기술팀 + 영업팀',
                tips: ['유사 프로젝트 실적 강조', '차별화 포인트 명확히 기술'],
                duration: '3~5일',
            },
            {
                id: 'S2-3', name: '입찰가 결정', icon: '🎯',
                description: '견적가를 기반으로 경쟁 상황을 고려한 최종 입찰가를 결정한다.',
                input: ['내부 견적서', '경쟁사 동향', '예정가 분석'],
                output: ['최종 입찰가 결정서'],
                responsible: '대표이사 승인',
                tips: ['예정가 대비 적정 투찰률 분석', '최저가 낙찰 vs 종합심사 방식 확인'],
                duration: '당일',
            },
            {
                id: 'S2-4', name: '입찰서 제출', icon: '📮',
                description: '준비된 서류와 입찰가를 포함하여 입찰서를 제출한다.',
                input: ['입찰서류 패키지', '최종 입찰가', '입찰보증금'],
                output: ['입찰 접수증', '입찰 결과 대기'],
                responsible: '영업 담당자',
                tips: ['마감시간 최소 2시간 전 제출', '전자입찰 시스템 사전 테스트'],
                duration: '당일',
            },
        ],
    },
    {
        id: 4, phase: 'contract', name: '수주/계약', icon: '📝', dept: '영업팀',
        color: '#10B981', bgColor: '#ECFDF5',
        description: '견적 승인 또는 낙찰 후 공사 계약을 체결하고 프로젝트를 등록한다.',
        documents: ['공사계약서', '착공계', '공정표'],
        samLink: '/juil/project', samMenu: '프로젝트관리/기성청구',
        subSteps: [
            {
                id: 'S4-1', name: '수주 확정', icon: '🏆',
                description: '입찰 낙찰 통보 또는 고객 견적 승인을 확인하고 수주를 확정한다.',
                input: ['낙찰 통보서 또는 견적 승인 회신'],
                output: ['수주 확정 (사내 공유)'],
                responsible: '영업 담당자',
                tips: ['입찰: 낙찰 후 계약 기한 확인 (보통 7~10일)', '수의계약: 고객 승인 서면 확보 필수'],
                duration: '당일',
            },
            {
                id: 'S4-2', name: '계약서 검토/체결', icon: '🖊️',
                description: '계약 조건(금액, 공기, 하자보증 등)을 검토하고 계약서에 서명한다.',
                input: ['계약서 초안 (발주처 제공)', '견적서/낙찰 내역서'],
                output: ['공사계약서 (날인본)', '계약보증금 납부 영수증'],
                responsible: '대표이사 + 경영지원팀',
                tips: ['지체상금 조항 반드시 확인', '하자보증기간/비율 확인', '대금지급 조건 확인'],
                duration: '3~5일',
            },
            {
                id: 'S4-3', name: '프로젝트 등록', icon: '💻',
                description: 'SAM 시스템에 프로젝트를 등록하고 담당자를 배정한다.',
                input: ['공사계약서', '공정표'],
                output: ['프로젝트 등록 완료 (SAM)', '담당자 배정표'],
                responsible: '영업팀장 → PM',
                tips: ['계약 금액/공기 정확히 입력', '하도급 계획이 있으면 동시 등록'],
                duration: '1일',
            },
            {
                id: 'S4-4', name: '착공 준비', icon: '🚧',
                description: '착공계를 제출하고, 현장 착공에 필요한 사전 준비를 완료한다.',
                input: ['공사계약서', '현장 배치도'],
                output: ['착공계', '현장 투입 계획서', '안전관리 계획서'],
                responsible: 'PM + 안전관리자',
                tips: ['착공 전 현장 안전교육 필수', '인근 주민 민원 사전 확인'],
                duration: '2~3일',
            },
        ],
    },
    {
        id: 5, phase: 'order', name: '자재 발주', icon: '📦', dept: '구매팀',
        color: '#F59E0B', bgColor: '#FFFBEB',
        description: '시공에 필요한 자재를 발주',
        documents: ['발주서', 'BOM', '납품요청서'],
        samLink: null, samMenu: null,
        subSteps: [
            {
                id: 'S5-1', name: 'BOM 작성', icon: '📋',
                description: '프로젝트에 필요한 자재 명세서(BOM)를 작성한다.',
                input: ['도면', '물량산출서', '시방서'],
                output: ['BOM (자재명세서)'],
                responsible: '기술팀 + 구매 담당자',
                tips: ['규격/색상/사양 정확히 기재', '예비 자재(로스분) 포함'],
                duration: '2~3일',
            },
            {
                id: 'S5-2', name: '공급사 선정/견적 비교', icon: '🔄',
                description: '2개 이상 공급사로부터 견적을 받아 비교하고 공급사를 선정한다.',
                input: ['BOM', '기존 거래처 목록', '시세 정보'],
                output: ['공급사 비교표', '공급사 선정 품의서'],
                responsible: '구매 담당자 → 구매팀장 승인',
                tips: ['기존 거래처 우선 검토', '납기 준수율 이력 확인'],
                duration: '2~3일',
            },
            {
                id: 'S5-3', name: '발주서 발행', icon: '📤',
                description: '선정된 공급사에 발주서를 발행하고, 납기일을 확정한다.',
                input: ['공급사 선정 결과', 'BOM'],
                output: ['발주서', '발주 확인서 (공급사)'],
                responsible: '구매 담당자',
                tips: ['시공일 기준 최소 1주 전 입고되도록 납기 설정', '분할 납품 시 일정 명확히'],
                duration: '1일',
            },
            {
                id: 'S5-4', name: '납기 관리', icon: '📅',
                description: '발주 후 공급사의 생산/배송 진행 상황을 추적한다.',
                input: ['발주서', '납기 일정'],
                output: ['납기 진행현황표'],
                responsible: '구매 담당자',
                tips: ['납기 3일 전 공급사에 확인 연락', '지연 시 즉시 PM에게 보고'],
                duration: '발주~입고 기간 중 수시',
            },
        ],
    },
    {
        id: 6, phase: 'receive', name: '자재 입고/검수', icon: '🔍', dept: '자재팀',
        color: '#06B6D4', bgColor: '#ECFEFF',
        description: '입고된 자재의 수량/품질 검수',
        documents: ['입고검수서', '거래명세서', '반품요청서'],
        samLink: null, samMenu: null,
        subSteps: [
            {
                id: 'S6-1', name: '입고 접수', icon: '🚛',
                description: '공급사로부터 자재가 도착하면 거래명세서와 대조하여 접수한다.',
                input: ['발주서', '거래명세서 (공급사)', '납품 자재'],
                output: ['입고 접수 기록'],
                responsible: '자재 담당자',
                tips: ['거래명세서와 발주서 수량 대조 필수', '포장 상태 외관 확인'],
                duration: '당일',
            },
            {
                id: 'S6-2', name: '수량 검수', icon: '🔢',
                description: '입고된 자재의 수량이 발주 수량과 일치하는지 전수 검사한다.',
                input: ['발주서', '입고 자재'],
                output: ['수량 검수 체크리스트'],
                responsible: '자재 담당자',
                tips: ['부족분 즉시 공급사에 연락', '과다 입고 시에도 기록 후 보고'],
                duration: '당일',
            },
            {
                id: 'S6-3', name: '품질 검사', icon: '🔬',
                description: '자재의 규격, 색상, 기능 등 품질 기준에 부합하는지 검사한다.',
                input: ['자재 시방서 (규격 기준)', '샘플'],
                output: ['품질검사 성적서'],
                responsible: '품질 담당자',
                tips: ['블라인드: 색상/슬랫 간격 확인', '셔터: 개폐 동작 테스트 필수'],
                duration: '1~2일',
            },
            {
                id: 'S6-4', name: '입고 확정/불량 처리', icon: '✅',
                description: '검수 합격 자재는 입고 확정, 불량 자재는 반품 처리한다.',
                input: ['수량 검수 결과', '품질검사 결과'],
                output: ['입고검수서 (확정)', '반품요청서 (불량 시)'],
                responsible: '자재팀장',
                tips: ['불량률 3% 이상 시 공급사 품질 회의 요청', '입고 확정 후 재고에 즉시 반영'],
                duration: '당일',
            },
        ],
    },
    {
        id: 7, phase: 'construct', name: '현장 시공', icon: '🔧', dept: '시공팀',
        color: '#EF4444', bgColor: '#FEF2F2',
        description: '현장에서 블라인드/스크린/셔터 설치 시공',
        documents: ['시공계획서', '작업일보', '시공사진'],
        samLink: '/juil/construction-photos', samMenu: '공사현장 사진대지',
        subSteps: [
            {
                id: 'S7-1', name: '시공 계획 수립', icon: '📅',
                description: '건설사 공정표에 맞춰 시공 일정, 인력, 장비 계획을 수립한다.',
                input: ['건설사 공정표', '자재 입고 일정', '인력 가용 현황'],
                output: ['시공계획서', '인력/장비 투입 계획'],
                responsible: 'PM + 현장소장',
                tips: ['건설사 타 공종과 간섭 구간 사전 확인', '우천 시 대체 일정 수립'],
                duration: '2~3일',
            },
            {
                id: 'S7-2', name: '자재 반출/현장 배송', icon: '🚚',
                description: '창고에서 필요 자재를 반출하여 현장에 배송한다.',
                input: ['시공계획서', '자재 재고 현황'],
                output: ['자재 반출 전표', '현장 입고 확인서'],
                responsible: '자재 담당자 + 운송팀',
                tips: ['현장 반입 가능 시간 확인 (아파트: 주로 오전)', '양중 장비 필요 여부 사전 확인'],
                duration: '시공일 1~2일 전',
            },
            {
                id: 'S7-3', name: '설치 시공', icon: '🔧',
                description: '현장에서 블라인드/스크린/셔터를 설치한다.',
                input: ['도면', '시공계획서', '자재'],
                output: ['작업일보', '시공 진행률'],
                responsible: '현장소장 + 시공기사',
                tips: ['안전장구 착용 필수 (안전모, 안전화, 안전벨트)', '일일 작업일보 당일 작성'],
                duration: '프로젝트 규모에 따라 상이',
            },
            {
                id: 'S7-4', name: '시공 사진 촬영/기록', icon: '📸',
                description: '시공 전/중/후 사진을 촬영하고, SAM 시스템에 등록한다.',
                input: ['시공 현장'],
                output: ['시공사진 (SAM 등록)', '시공 진행 보고서'],
                responsible: '현장소장',
                tips: ['공정별 Before/After 반드시 촬영', 'SAM 공사현장 사진대지에 당일 업로드'],
                duration: '시공 기간 중 매일',
            },
        ],
    },
    {
        id: 8, phase: 'inspect', name: '시공 검수', icon: '✅', dept: '현장팀',
        color: '#14B8A6', bgColor: '#F0FDFA',
        description: '시공 완료 후 품질 검수 및 하자 보수',
        documents: ['검수확인서', '하자보수보고서', '준공사진'],
        samLink: '/juil/construction-photos', samMenu: '공사현장 사진대지',
        subSteps: [
            {
                id: 'S8-1', name: '자체 검수', icon: '🔎',
                description: '건설사 합동검수 전, 시공팀 자체적으로 시공 품질을 점검한다.',
                input: ['시공 도면', '시방서', '시공 체크리스트'],
                output: ['자체 검수 보고서', '보수 필요 항목 리스트'],
                responsible: '현장소장',
                tips: ['체크리스트 기반 전수 검사', '사소한 하자도 미리 보수'],
                duration: '1~2일',
            },
            {
                id: 'S8-2', name: '하자 보수', icon: '🔨',
                description: '자체 검수에서 발견된 하자 사항을 보수한다.',
                input: ['보수 필요 항목 리스트', '보수 자재'],
                output: ['하자보수 완료 보고서'],
                responsible: '시공기사',
                tips: ['보수 전/후 사진 촬영 필수', '동일 하자 재발 방지 대책 수립'],
                duration: '1~3일',
            },
            {
                id: 'S8-3', name: '합동 검수', icon: '👥',
                description: '건설사(감리) 담당자와 합동으로 시공 품질을 검수한다.',
                input: ['시공 완료 현장', '검수 체크리스트', '시공사진'],
                output: ['합동검수 확인서', '추가 보수 요청 (있을 경우)'],
                responsible: '현장소장 + 건설사 감리',
                tips: ['검수 일정 최소 3일 전 건설사와 협의', '추가 보수 요청 시 기한/범위 명확히 합의'],
                duration: '당일',
            },
            {
                id: 'S8-4', name: '준공 확인', icon: '🏁',
                description: '모든 검수를 완료하고 준공 확인서를 수령한다.',
                input: ['합동검수 확인서', '준공사진'],
                output: ['준공확인서', '준공도서'],
                responsible: 'PM',
                tips: ['준공확인서 수령 후 기성청구 가능', '준공도서(도면, 매뉴얼) 제출 확인'],
                duration: '1~2일',
            },
        ],
    },
    {
        id: 9, phase: 'billing', name: '기성 청구', icon: '💰', dept: '경리팀',
        color: '#7C3AED', bgColor: '#F5F3FF',
        description: '시공 진행률에 따른 기성금 청구',
        documents: ['기성내역서', '세금계산서', '기성청구서'],
        samLink: '/juil/project', samMenu: '프로젝트관리/기성청구',
        subSteps: [
            {
                id: 'S9-1', name: '기성 내역서 작성', icon: '📊',
                description: '시공 진행률에 따라 청구할 기성금 내역을 작성한다.',
                input: ['계약서 (계약금액)', '시공 진행률', '기 청구 내역'],
                output: ['기성내역서'],
                responsible: '경리 담당자 + PM',
                tips: ['계약 조건의 기성 산정 기준 확인 (월별/단계별)', '기 청구분 공제 정확히'],
                duration: '1~2일',
            },
            {
                id: 'S9-2', name: '세금계산서 발행', icon: '🧾',
                description: '기성금에 대한 세금계산서를 발행한다.',
                input: ['기성내역서', '사업자등록 정보'],
                output: ['전자세금계산서'],
                responsible: '경리 담당자',
                tips: ['발행일과 기성 승인일 맞추기', '수정 세금계산서 발행 시 사유 기재'],
                duration: '당일',
            },
            {
                id: 'S9-3', name: '청구서 제출', icon: '📮',
                description: '기성내역서, 세금계산서 등 청구 서류를 건설사에 제출한다.',
                input: ['기성내역서', '세금계산서', '시공사진', '검수확인서'],
                output: ['기성청구서 (접수 확인)'],
                responsible: '경리 담당자',
                tips: ['건설사별 청구 마감일 확인', '첨부서류 누락 없이 제출'],
                duration: '당일',
            },
            {
                id: 'S9-4', name: '입금 추적', icon: '🔄',
                description: '청구 후 입금 일정을 추적하고, 지연 시 독촉한다.',
                input: ['기성청구서 제출 내역', '계약상 지급 기한'],
                output: ['입금 예정일 관리표'],
                responsible: '경리 담당자',
                tips: ['지급 기한 초과 시 3일 내 독촉', '장기 미수금은 월간 보고에 포함'],
                duration: '청구 후 지속',
            },
        ],
    },
    {
        id: 10, phase: 'payment', name: '입금 확인', icon: '🏦', dept: '경리팀',
        color: '#059669', bgColor: '#ECFDF5',
        description: '기성금 입금 확인 및 매출 처리',
        documents: ['입금확인서', '매출대장'],
        samLink: null, samMenu: null,
        subSteps: [
            {
                id: 'S10-1', name: '입금 확인', icon: '💳',
                description: '은행 계좌에 기성금이 입금되었는지 확인한다.',
                input: ['은행 거래내역', '기성청구 내역'],
                output: ['입금 확인 기록'],
                responsible: '경리 담당자',
                tips: ['청구 금액과 입금액 일치 여부 확인', '부분 입금 시 잔액 별도 관리'],
                duration: '당일',
            },
            {
                id: 'S10-2', name: '매출 전표 처리', icon: '📑',
                description: '입금 확인 후 회계 시스템에 매출 전표를 기록한다.',
                input: ['입금 확인 기록', '세금계산서'],
                output: ['매출 전표', '매출대장 업데이트'],
                responsible: '경리 담당자',
                tips: ['매출 인식 시점 확인 (발생주의/현금주의)', '부가세 신고 일정과 연계'],
                duration: '당일',
            },
            {
                id: 'S10-3', name: '미수금 관리', icon: '📊',
                description: '미입금 건을 관리하고, 장기 미수금은 별도 조치한다.',
                input: ['기성청구 내역', '입금 내역'],
                output: ['미수금 현황표', '독촉장 (필요 시)'],
                responsible: '경리팀장',
                tips: ['30일 이상 미수금은 주간 보고', '60일 이상 시 법적 조치 검토'],
                duration: '월간',
            },
            {
                id: 'S10-4', name: '프로젝트 수익 정산', icon: '📈',
                description: '프로젝트 완료 시 총 매출/비용을 정산하여 최종 수익을 산출한다.',
                input: ['총 매출 내역', '총 비용 내역 (자재+인건비+경비)'],
                output: ['프로젝트 수익 정산서'],
                responsible: '경리팀장 → 대표이사 보고',
                tips: ['예정 이익률 대비 실제 이익률 비교 분석', '손실 프로젝트 원인 분석 필수'],
                duration: '프로젝트 완료 후 1주일',
            },
        ],
    },
    {
        id: 11, phase: 'as', name: 'A/S 관리', icon: '🛠️', dept: '시공팀',
        color: '#6B7280', bgColor: '#F9FAFB',
        description: '하자보수기간 내 A/S 대응',
        documents: ['A/S 접수대장', '하자보수 보고서'],
        samLink: null, samMenu: null,
        subSteps: [
            {
                id: 'S11-1', name: 'A/S 접수', icon: '📞',
                description: '건설사/입주자로부터 A/S 요청을 접수한다.',
                input: ['A/S 요청 (전화/메일/문서)'],
                output: ['A/S 접수 기록'],
                responsible: '영업 담당자 → 시공팀',
                tips: ['접수 즉시 SAM에 등록', '긴급 A/S(누수 등)는 24시간 내 대응'],
                duration: '당일',
            },
            {
                id: 'S11-2', name: '현장 확인/진단', icon: '🔍',
                description: 'A/S 현장을 방문하여 하자 원인을 진단한다.',
                input: ['A/S 접수 기록', '시공 이력'],
                output: ['하자 진단 보고서'],
                responsible: '시공기사',
                tips: ['시공 하자 vs 사용자 과실 판단 명확히', '사진/영상 촬영 필수'],
                duration: '접수 후 2~3일 이내',
            },
            {
                id: 'S11-3', name: '보수 작업', icon: '🔧',
                description: '진단 결과에 따라 하자 보수를 실시한다.',
                input: ['하자 진단 보고서', '보수 자재'],
                output: ['보수 완료 보고서', '보수 전/후 사진'],
                responsible: '시공기사',
                tips: ['보수 완료 후 고객 확인 서명 수령', '동일 부위 재발 방지 조치'],
                duration: '1~3일',
            },
            {
                id: 'S11-4', name: '이력 관리/보증기간', icon: '📂',
                description: 'A/S 이력을 관리하고, 하자보증기간 만료를 추적한다.',
                input: ['A/S 완료 기록', '계약서 (보증기간)'],
                output: ['A/S 이력대장', '보증기간 만료 알림'],
                responsible: '영업팀 + 경영지원팀',
                tips: ['하자보증기간: 보통 1~3년 (항목별 상이)', '만료 1개월 전 최종 점검 실시'],
                duration: '보증기간 종료까지',
            },
        ],
    },
];

// --- 화살표 컴포넌트 ---
function Arrow() {
    return (
        <div style={{
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            minWidth: '40px', position: 'relative',
        }}>
            <div style={{ width: '100%', height: '2px', backgroundColor: '#9CA3AF' }}></div>
            <div style={{
                position: 'absolute', right: 0, width: 0, height: 0,
                borderTop: '6px solid transparent', borderBottom: '6px solid transparent',
                borderLeft: '8px solid #9CA3AF',
            }}></div>
        </div>
    );
}

// --- 미니 화살표 (모달 서브플로우용) ---
function MiniArrow() {
    return (
        <div style={{
            display: 'flex', alignItems: 'center', justifyContent: 'center',
            width: '32px', flexShrink: 0,
        }}>
            <div style={{ width: '100%', height: '2px', backgroundColor: '#D1D5DB' }}></div>
            <div style={{
                position: 'relative', right: 0, width: 0, height: 0,
                borderTop: '4px solid transparent', borderBottom: '4px solid transparent',
                borderLeft: '6px solid #D1D5DB',
            }}></div>
        </div>
    );
}

function DownArrow() {
    return (
        <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', padding: '2px 0' }}>
            <div style={{ width: '2px', height: '12px', backgroundColor: '#D1D5DB' }}></div>
            <div style={{
                width: 0, height: 0,
                borderLeft: '5px solid transparent', borderRight: '5px solid transparent',
                borderTop: '6px solid #D1D5DB',
            }}></div>
        </div>
    );
}

// --- 프로세스 노드 컴포넌트 ---
function ProcessNode({ process, isActive, onClick }) {
    return (
        <div
            onClick={() => onClick(process)}
            style={{
                minWidth: '140px', maxWidth: '160px', padding: '12px',
                borderRadius: '12px',
                border: `2px solid ${isActive ? process.color : '#E5E7EB'}`,
                backgroundColor: isActive ? process.bgColor : '#FFFFFF',
                cursor: 'pointer', transition: 'all 0.2s ease',
                boxShadow: isActive ? `0 4px 12px ${process.color}33` : '0 1px 3px rgba(0,0,0,0.1)',
                textAlign: 'center', position: 'relative',
            }}
        >
            <div style={{ fontSize: '24px', marginBottom: '6px' }}>{process.icon}</div>
            <div style={{ fontSize: '13px', fontWeight: 700, color: '#1F2937', marginBottom: '4px', whiteSpace: 'nowrap' }}>
                {process.name}
            </div>
            <div style={{
                fontSize: '11px', color: '#FFF', backgroundColor: process.color,
                borderRadius: '10px', padding: '1px 8px', display: 'inline-block',
            }}>{process.dept}</div>
            {process.phase === 'bid' && (
                <div style={{
                    position: 'absolute', top: '-8px', right: '-8px',
                    width: '20px', height: '20px', borderRadius: '50%',
                    backgroundColor: '#FCE7F3', border: '2px solid #EC4899',
                    display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '10px',
                }}>A</div>
            )}
        </div>
    );
}

// --- 서브스텝 상세 카드 ---
function SubStepDetail({ sub, index, color, bgColor, isExpanded, onToggle }) {
    return (
        <div style={{
            backgroundColor: '#FFFFFF', borderRadius: '12px',
            border: isExpanded ? `2px solid ${color}` : '1px solid #E5E7EB',
            overflow: 'hidden', transition: 'all 0.2s ease',
            boxShadow: isExpanded ? `0 4px 12px ${color}20` : 'none',
        }}>
            {/* 헤더 (클릭 영역) */}
            <div
                onClick={onToggle}
                style={{
                    display: 'flex', alignItems: 'center', gap: '10px',
                    padding: '12px 16px', cursor: 'pointer',
                    backgroundColor: isExpanded ? bgColor : '#FAFBFC',
                }}
            >
                <div style={{
                    width: '28px', height: '28px', borderRadius: '50%',
                    backgroundColor: isExpanded ? color : '#E5E7EB',
                    color: isExpanded ? '#FFF' : '#6B7280',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    fontSize: '12px', fontWeight: 700, flexShrink: 0,
                }}>{index + 1}</div>
                <span style={{ fontSize: '18px' }}>{sub.icon}</span>
                <div style={{ flex: 1 }}>
                    <div style={{ fontSize: '14px', fontWeight: 700, color: '#1F2937' }}>{sub.name}</div>
                    <div style={{ fontSize: '12px', color: '#6B7280', marginTop: '1px' }}>{sub.responsible}</div>
                </div>
                <div style={{
                    fontSize: '11px', padding: '2px 8px', borderRadius: '8px',
                    backgroundColor: '#F3F4F6', color: '#6B7280', whiteSpace: 'nowrap',
                }}>{sub.duration}</div>
                <span style={{
                    fontSize: '16px', color: '#9CA3AF', transition: 'transform 0.2s',
                    transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)',
                }}>▾</span>
            </div>

            {/* 상세 내용 (펼침) */}
            {isExpanded && (
                <div style={{ padding: '0 16px 16px' }}>
                    {/* 설명 */}
                    <div style={{
                        fontSize: '13px', color: '#374151', padding: '12px',
                        backgroundColor: bgColor, borderRadius: '8px',
                        borderLeft: `3px solid ${color}`, margin: '4px 0 12px',
                    }}>{sub.description}</div>

                    {/* Input → Output */}
                    <div style={{ display: 'flex', gap: '12px', flexWrap: 'wrap', marginBottom: '12px' }}>
                        <div style={{ flex: '1 1 180px' }}>
                            <div style={{ fontSize: '11px', fontWeight: 700, color: '#EF4444', marginBottom: '6px' }}>
                                INPUT (필요 자료)
                            </div>
                            {sub.input.map((item, i) => (
                                <div key={i} style={{
                                    fontSize: '12px', color: '#4B5563', padding: '3px 0',
                                    display: 'flex', alignItems: 'flex-start', gap: '6px',
                                }}>
                                    <span style={{ color: '#EF4444', fontSize: '8px', marginTop: '4px', flexShrink: 0 }}>●</span>
                                    {item}
                                </div>
                            ))}
                        </div>
                        <div style={{
                            display: 'flex', alignItems: 'center', justifyContent: 'center',
                            fontSize: '20px', color: '#D1D5DB', flexShrink: 0, padding: '0 4px',
                        }}>→</div>
                        <div style={{ flex: '1 1 180px' }}>
                            <div style={{ fontSize: '11px', fontWeight: 700, color: '#10B981', marginBottom: '6px' }}>
                                OUTPUT (산출물)
                            </div>
                            {sub.output.map((item, i) => (
                                <div key={i} style={{
                                    fontSize: '12px', color: '#4B5563', padding: '3px 0',
                                    display: 'flex', alignItems: 'flex-start', gap: '6px',
                                }}>
                                    <span style={{ color: '#10B981', fontSize: '8px', marginTop: '4px', flexShrink: 0 }}>●</span>
                                    {item}
                                </div>
                            ))}
                        </div>
                    </div>

                    {/* 팁/주의사항 */}
                    <div style={{
                        backgroundColor: '#FFFBEB', borderRadius: '8px', padding: '10px 12px',
                        border: '1px solid #FDE68A',
                    }}>
                        <div style={{ fontSize: '11px', fontWeight: 700, color: '#92400E', marginBottom: '4px' }}>
                            TIP / 주의사항
                        </div>
                        {sub.tips.map((tip, i) => (
                            <div key={i} style={{
                                fontSize: '12px', color: '#78350F', padding: '2px 0',
                                display: 'flex', alignItems: 'flex-start', gap: '6px',
                            }}>
                                <span style={{ flexShrink: 0 }}>💡</span>
                                {tip}
                            </div>
                        ))}
                    </div>
                </div>
            )}
        </div>
    );
}

// --- 상세 모달 컴포넌트 ---
function DetailModal({ process, onClose }) {
    const [expandedSub, setExpandedSub] = useState(0); // 첫번째 펼침
    const modalRef = useRef(null);

    useEffect(() => {
        const handleEsc = (e) => { if (e.key === 'Escape') onClose(); };
        document.addEventListener('keydown', handleEsc);
        return () => document.removeEventListener('keydown', handleEsc);
    }, [onClose]);

    if (!process) return null;

    return (
        <div
            onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}
            style={{
                position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
                backgroundColor: 'rgba(0,0,0,0.5)', zIndex: 9999,
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                padding: '20px',
            }}
        >
            <div
                ref={modalRef}
                style={{
                    backgroundColor: '#FFFFFF', borderRadius: '20px',
                    width: '100%', maxWidth: '800px', maxHeight: '90vh',
                    overflow: 'hidden', display: 'flex', flexDirection: 'column',
                    boxShadow: '0 25px 60px rgba(0,0,0,0.3)',
                }}
            >
                {/* 모달 헤더 */}
                <div style={{
                    padding: '20px 24px', borderBottom: '1px solid #E5E7EB',
                    backgroundColor: process.bgColor, flexShrink: 0,
                }}>
                    <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
                            <div style={{
                                width: '48px', height: '48px', borderRadius: '14px',
                                backgroundColor: '#FFFFFF', display: 'flex',
                                alignItems: 'center', justifyContent: 'center', fontSize: '28px',
                                boxShadow: '0 2px 8px rgba(0,0,0,0.1)',
                            }}>{process.icon}</div>
                            <div>
                                <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
                                    <span style={{
                                        width: '24px', height: '24px', borderRadius: '50%',
                                        backgroundColor: process.color, color: '#FFF',
                                        fontSize: '12px', fontWeight: 700, display: 'inline-flex',
                                        alignItems: 'center', justifyContent: 'center',
                                    }}>{process.id}</span>
                                    <span style={{ fontSize: '20px', fontWeight: 800, color: '#111827' }}>
                                        {process.name}
                                    </span>
                                </div>
                                <div style={{ fontSize: '13px', color: '#6B7280', marginTop: '2px' }}>
                                    담당: <span style={{ color: process.color, fontWeight: 600 }}>{process.dept}</span>
                                    {' · '}상세 업무 {process.subSteps.length}단계
                                </div>
                            </div>
                        </div>
                        <button onClick={onClose} style={{
                            width: '36px', height: '36px', borderRadius: '50%',
                            border: '1px solid #E5E7EB', backgroundColor: '#FFFFFF',
                            cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
                            fontSize: '18px', color: '#6B7280',
                        }}>&times;</button>
                    </div>

                    {/* 프로세스 설명 */}
                    <div style={{
                        fontSize: '13px', color: '#374151', marginTop: '12px',
                        padding: '10px 12px', backgroundColor: '#FFFFFF', borderRadius: '8px',
                        borderLeft: `4px solid ${process.color}`,
                    }}>{process.description}</div>

                    {/* 미니 서브플로우 다이어그램 */}
                    <div style={{
                        display: 'flex', alignItems: 'center', justifyContent: 'center',
                        gap: '2px', marginTop: '16px', overflowX: 'auto', padding: '4px 0',
                    }}>
                        {process.subSteps.map((sub, i) => (
                            <React.Fragment key={sub.id}>
                                <div
                                    onClick={() => setExpandedSub(i)}
                                    style={{
                                        padding: '4px 10px', borderRadius: '8px', cursor: 'pointer',
                                        backgroundColor: expandedSub === i ? process.color : '#FFFFFF',
                                        color: expandedSub === i ? '#FFFFFF' : '#374151',
                                        fontSize: '11px', fontWeight: 600, whiteSpace: 'nowrap',
                                        border: `1px solid ${expandedSub === i ? process.color : '#D1D5DB'}`,
                                        transition: 'all 0.15s ease',
                                    }}
                                >{sub.name}</div>
                                {i < process.subSteps.length - 1 && <MiniArrow />}
                            </React.Fragment>
                        ))}
                    </div>
                </div>

                {/* 모달 본문 (스크롤) */}
                <div style={{ overflowY: 'auto', padding: '16px 24px 24px', flex: 1 }}>
                    {/* 서브스텝 목록 */}
                    <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
                        {process.subSteps.map((sub, i) => (
                            <React.Fragment key={sub.id}>
                                <SubStepDetail
                                    sub={sub}
                                    index={i}
                                    color={process.color}
                                    bgColor={process.bgColor}
                                    isExpanded={expandedSub === i}
                                    onToggle={() => setExpandedSub(expandedSub === i ? -1 : i)}
                                />
                                {i < process.subSteps.length - 1 && <DownArrow />}
                            </React.Fragment>
                        ))}
                    </div>

                    {/* 하단 - 필요서류 + SAM 연동 */}
                    <div style={{
                        display: 'flex', gap: '16px', flexWrap: 'wrap',
                        marginTop: '20px', padding: '16px',
                        backgroundColor: '#F9FAFB', borderRadius: '12px', border: '1px solid #E5E7EB',
                    }}>
                        <div style={{ flex: '1 1 250px' }}>
                            <div style={{ fontSize: '12px', fontWeight: 700, color: '#374151', marginBottom: '8px' }}>
                                주요 서류
                            </div>
                            <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px' }}>
                                {process.documents.map((doc, i) => (
                                    <span key={i} style={{
                                        fontSize: '11px', padding: '3px 10px',
                                        backgroundColor: '#FFFFFF', borderRadius: '12px',
                                        color: '#374151', border: '1px solid #E5E7EB',
                                    }}>{doc}</span>
                                ))}
                            </div>
                        </div>
                        <div style={{ flex: '0 0 auto' }}>
                            <div style={{ fontSize: '12px', fontWeight: 700, color: '#374151', marginBottom: '8px' }}>
                                SAM 연동
                            </div>
                            {process.samLink ? (
                                <a href={process.samLink} style={{
                                    display: 'inline-flex', alignItems: 'center', gap: '6px',
                                    fontSize: '12px', color: '#FFFFFF', fontWeight: 600,
                                    padding: '8px 16px', borderRadius: '8px',
                                    backgroundColor: process.color, textDecoration: 'none',
                                }}>↗ {process.samMenu}</a>
                            ) : (
                                <span style={{ fontSize: '12px', color: '#9CA3AF', fontStyle: 'italic' }}>
                                    추후 연동 예정
                                </span>
                            )}
                        </div>
                    </div>
                </div>
            </div>
        </div>
    );
}

// --- 메인 앱 ---
function App() {
    const [modalProcess, setModalProcess] = useState(null);

    // 프로세스 참조
    const p = {};
    processes.forEach(proc => { p[proc.phase] = proc; });

    // 공통 후반 프로세스 (수주/계약 이후)
    const postContract = [p.order, p.receive, p.construct, p.inspect, p.billing, p.payment, p.as].filter(Boolean);

    return (
        <div style={{ padding: '20px' }}>
            {/* 헤더 */}
            <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '24px' }}>
                <div>
                    <h1 style={{ fontSize: '22px', fontWeight: 800, color: '#111827', margin: 0 }}>
                        업무 Workflow
                    </h1>
                    <p style={{ fontSize: '13px', color: '#6B7280', margin: '4px 0 0' }}>
                        주일기업 업무처리과정 플로우차트 — 각 단계를 클릭하면 상세 업무 흐름을 확인할 수 있습니다
                    </p>
                </div>
            </div>

            {/* 플로우차트 영역 */}
            <div style={{
                backgroundColor: '#FAFBFC', borderRadius: '16px', border: '1px solid #E5E7EB',
                padding: '32px 24px', marginBottom: '24px', overflowX: 'auto',
            }}>
                {/* === 1단계: 영업 (공통 시작점) === */}
                <div style={{ display: 'flex', justifyContent: 'center', marginBottom: '8px' }}>
                    <ProcessNode process={p.sales} isActive={false} onClick={setModalProcess} />
                </div>

                {/* 분기 화살표 */}
                <div style={{ display: 'flex', justifyContent: 'center', position: 'relative', height: '60px' }}>
                    {/* 중앙 세로선 */}
                    <div style={{ position: 'absolute', top: 0, width: '2px', height: '20px', backgroundColor: '#9CA3AF' }}></div>
                    {/* 가로 분기선 */}
                    <div style={{ position: 'absolute', top: '20px', width: '400px', height: '2px', backgroundColor: '#9CA3AF', left: '50%', transform: 'translateX(-50%)' }}></div>
                    {/* 왼쪽 세로선 + 화살표 */}
                    <div style={{ position: 'absolute', top: '20px', left: '50%', transform: 'translateX(calc(-200px))', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
                        <div style={{ width: '2px', height: '20px', backgroundColor: '#9CA3AF' }}></div>
                        <div style={{ width: 0, height: 0, borderLeft: '6px solid transparent', borderRight: '6px solid transparent', borderTop: '8px solid #9CA3AF' }}></div>
                    </div>
                    {/* 오른쪽 세로선 + 화살표 */}
                    <div style={{ position: 'absolute', top: '20px', left: '50%', transform: 'translateX(calc(200px - 2px))', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
                        <div style={{ width: '2px', height: '20px', backgroundColor: '#9CA3AF' }}></div>
                        <div style={{ width: 0, height: 0, borderLeft: '6px solid transparent', borderRight: '6px solid transparent', borderTop: '8px solid #9CA3AF' }}></div>
                    </div>
                </div>

                {/* === 2단계: 두 경로 분기 === */}
                <div style={{ display: 'flex', justifyContent: 'center', gap: '24px', marginBottom: '8px' }}>
                    {/* A 경로: 입찰 기업 */}
                    <div style={{
                        flex: '0 0 auto', padding: '16px', borderRadius: '16px',
                        border: '2px dashed #EC4899', backgroundColor: '#FDF2F820',
                        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px',
                        minWidth: '360px',
                    }}>
                        <div style={{
                            fontSize: '12px', fontWeight: 700, color: '#BE185D',
                            padding: '3px 14px', borderRadius: '12px', backgroundColor: '#FCE7F3',
                            marginBottom: '8px', display: 'flex', alignItems: 'center', gap: '6px',
                        }}>
                            <span style={{ fontSize: '14px' }}>🏢</span>
                            A. 입찰 참여 기업 (대형/공공 프로젝트)
                        </div>
                        <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
                            <ProcessNode process={p.estimate} isActive={false} onClick={setModalProcess} />
                            <Arrow />
                            <ProcessNode process={p.bid} isActive={false} onClick={setModalProcess} />
                        </div>
                        <div style={{ fontSize: '10px', color: '#9CA3AF', marginTop: '4px' }}>
                            견적서 작성 → 입찰 참여 → 낙찰 시 수주
                        </div>
                    </div>

                    {/* B 경로: 수의계약 */}
                    <div style={{
                        flex: '0 0 auto', padding: '16px', borderRadius: '16px',
                        border: '2px dashed #3B82F6', backgroundColor: '#EFF6FF20',
                        display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px',
                        minWidth: '200px',
                    }}>
                        <div style={{
                            fontSize: '12px', fontWeight: 700, color: '#1D4ED8',
                            padding: '3px 14px', borderRadius: '12px', backgroundColor: '#DBEAFE',
                            marginBottom: '8px', display: 'flex', alignItems: 'center', gap: '6px',
                        }}>
                            <span style={{ fontSize: '14px' }}>🏠</span>
                            B. 수의계약 (소규모/직접 의뢰)
                        </div>
                        <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
                            <ProcessNode process={p.estimate} isActive={false} onClick={setModalProcess} />
                        </div>
                        <div style={{ fontSize: '10px', color: '#9CA3AF', marginTop: '4px' }}>
                            견적서 작성 → 고객 승인 시 수주
                        </div>
                    </div>
                </div>

                {/* 합류 화살표 */}
                <div style={{ display: 'flex', justifyContent: 'center', position: 'relative', height: '60px' }}>
                    {/* 왼쪽 세로선 */}
                    <div style={{ position: 'absolute', top: 0, left: '50%', transform: 'translateX(calc(-200px))', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
                        <div style={{ width: '2px', height: '20px', backgroundColor: '#9CA3AF' }}></div>
                    </div>
                    {/* 오른쪽 세로선 */}
                    <div style={{ position: 'absolute', top: 0, left: '50%', transform: 'translateX(calc(200px - 2px))', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
                        <div style={{ width: '2px', height: '20px', backgroundColor: '#9CA3AF' }}></div>
                    </div>
                    {/* 가로 합류선 */}
                    <div style={{ position: 'absolute', top: '20px', width: '400px', height: '2px', backgroundColor: '#9CA3AF', left: '50%', transform: 'translateX(-50%)' }}></div>
                    {/* 중앙 세로선 + 화살표 */}
                    <div style={{ position: 'absolute', top: '20px', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
                        <div style={{ width: '2px', height: '20px', backgroundColor: '#9CA3AF' }}></div>
                        <div style={{ width: 0, height: 0, borderLeft: '6px solid transparent', borderRight: '6px solid transparent', borderTop: '8px solid #9CA3AF' }}></div>
                    </div>
                </div>

                {/* === 3단계: 수주/계약 (합류 지점) === */}
                <div style={{ display: 'flex', justifyContent: 'center', marginBottom: '8px' }}>
                    <ProcessNode process={p.contract} isActive={false} onClick={setModalProcess} />
                </div>

                {/* 아래 화살표 */}
                <div style={{ display: 'flex', justifyContent: 'center', padding: '4px 0' }}>
                    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
                        <div style={{ width: '2px', height: '12px', backgroundColor: '#9CA3AF' }}></div>
                        <div style={{ width: 0, height: 0, borderLeft: '6px solid transparent', borderRight: '6px solid transparent', borderTop: '8px solid #9CA3AF' }}></div>
                    </div>
                </div>

                {/* === 4단계: 공통 후반 프로세스 === */}
                <div style={{
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    gap: '4px', minWidth: 'fit-content', margin: '0 auto',
                }}>
                    {postContract.map((proc, i) => (
                        <React.Fragment key={proc.id}>
                            <ProcessNode process={proc} isActive={false} onClick={setModalProcess} />
                            {i < postContract.length - 1 && <Arrow />}
                        </React.Fragment>
                    ))}
                </div>
            </div>

            {/* 범례 */}
            <div style={{
                display: 'flex', gap: '16px', flexWrap: 'wrap',
                padding: '12px 16px', backgroundColor: '#F9FAFB',
                borderRadius: '8px', border: '1px solid #E5E7EB',
                fontSize: '12px', color: '#6B7280', alignItems: 'center',
            }}>
                <span><strong>범례:</strong></span>
                <span style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
                    <span style={{ display: 'inline-block', width: '16px', height: '3px', borderTop: '2px dashed #EC4899' }}></span>
                    입찰 참여 경로
                </span>
                <span style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
                    <span style={{ display: 'inline-block', width: '16px', height: '3px', borderTop: '2px dashed #3B82F6' }}></span>
                    수의계약 경로
                </span>
                <span>클릭 → 상세 업무 모달</span>
                <span>↗ SAM 메뉴 바로가기</span>
                <span>ESC 키로 모달 닫기</span>
            </div>

            {/* 상세 모달 */}
            {modalProcess && (
                <DetailModal process={modalProcess} onClose={() => setModalProcess(null)} />
            )}
        </div>
    );
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
@endverbatim
</script>
@endpush
