(function () {
  window.__mhLedgerEnhancementLoaded = false;
  if (typeof S === "undefined" || typeof E === "undefined" || typeof render !== "function") return;
  window.__mhLedgerEnhancementLoaded = true;
  if (!S.dashboard) S.dashboard = { year: "", section: "active" };
  if (!S.collapsedGroups) S.collapsedGroups = {};

  function bgToday() {
    var now = new Date();
    return new Date(now.getFullYear(), now.getMonth(), now.getDate());
  }

  function bgParseDate(value) {
    var text = String(value || "").trim();
    if (!text) return null;
    var match = text.match(/(20\d{2})\D?(\d{1,2})\D?(\d{1,2})/);
    if (match) {
      var parsed = new Date(Number(match[1]), Number(match[2]) - 1, Number(match[3]));
      return isNaN(parsed.getTime()) ? null : parsed;
    }
    var fallback = new Date(text);
    if (isNaN(fallback.getTime())) return null;
    return new Date(fallback.getFullYear(), fallback.getMonth(), fallback.getDate());
  }

  function bgYearFromText(value) {
    var match = String(value || "").trim().match(/(20\d{2})/);
    return match ? match[1] : "";
  }

  function bgStartYear(row) {
    return bgYearFromText(row && row.sDate);
  }

  function bgEndYear(row) {
    return bgYearFromText(row && row.eDate);
  }

  function bgDisplayYear(row) {
    var start = bgStartYear(row);
    if (start) return start;
    var contractMatch = String((row && row.cDate) || "").trim().match(/(20\d{2})/);
    if (contractMatch) return contractMatch[1];
    var nameMatch = String((row && row.name) || "").trim().match(/^(20\d{2})/);
    if (nameMatch) return nameMatch[1];
    return bgEndYear(row) || "미지정";
  }

  function bgCompletionYear(row) {
    return bgEndYear(row) || bgDisplayYear(row);
  }

  function bgDateOrYearStart(row) {
    var yearText = bgDisplayYear(row);
    return bgParseDate(row && row.sDate) || bgParseDate(row && row.cDate) || (/^20\d{2}$/.test(yearText) ? new Date(Number(yearText), 0, 1) : null);
  }

  function bgDateOrYearEnd(row) {
    var completionYear = bgCompletionYear(row);
    return bgParseDate(row && row.eDate) || (/^20\d{2}$/.test(completionYear) ? new Date(Number(completionYear), 11, 31) : null);
  }

  function bgYearCutoff(year) {
    var targetYear = Number(year || 0);
    if (!targetYear) return null;
    var today = bgToday();
    if (targetYear < today.getFullYear()) return new Date(targetYear, 11, 31);
    if (targetYear === today.getFullYear()) return today;
    return null;
  }

  function bgYearStartDate(year) {
    var targetYear = Number(year || 0);
    return targetYear ? new Date(targetYear, 0, 1) : null;
  }

  function bgActiveInYear(row, year) {
    var cutoff = bgYearCutoff(year);
    var yearStart = bgYearStartDate(year);
    var startDate = bgDateOrYearStart(row);
    var endDate = bgDateOrYearEnd(row);
    if (!(cutoff && yearStart && startDate)) return false;
    if (startDate > cutoff) return false;
    if (endDate && endDate < yearStart) return false;
    return !(endDate && endDate <= cutoff);
  }

  function bgStartedInYear(row, year) {
    var cutoff = bgYearCutoff(year);
    var startDate = bgDateOrYearStart(row);
    if (!(cutoff && startDate)) return false;
    return startDate.getFullYear() === Number(year || 0) && startDate <= cutoff;
  }

  function bgCompletedInYear(row, year) {
    var cutoff = bgYearCutoff(year);
    var endDate = bgDateOrYearEnd(row);
    if (!(cutoff && endDate)) return false;
    return endDate.getFullYear() === Number(year || 0) && endDate <= cutoff;
  }

  function bgYearRange(row) {
    var years = [];
    var startYear = Number(bgDisplayYear(row) || 0);
    var endYear = Number(bgCompletionYear(row) || 0);
    if (startYear && endYear && endYear >= startYear) {
      for (var year = startYear; year <= endYear; year += 1) years.push(String(year));
    } else if (startYear) {
      years.push(String(startYear));
    }
    return years;
  }

  function bgYears(rows) {
    var currentYear = new Date().getFullYear();
    var years = Array.from(new Set((Array.isArray(rows) ? rows : []).flatMap(bgYearRange).filter(function (year) {
      return /^20\d{2}$/.test(year);
    }))).sort(function (a, b) {
      return Number(b) - Number(a);
    });
    years = years.filter(function (year) {
      var numericYear = Number(year);
      return numericYear >= 2018 && numericYear <= currentYear;
    });
    return years.length ? years : [String(currentYear)];
  }

  function bgEnsureYear(rows) {
    var years = bgYears(rows);
    if (!years.includes(S.dashboard.year)) S.dashboard.year = years[0];
    return years;
  }

  function bgTotals(targetRows) {
    return (Array.isArray(targetRows) ? targetRows : []).reduce(function (acc, row) {
      acc.c += Number((row && row.cSup) || 0);
      acc.col += Number((row && row.col) || 0);
      acc.recv += Number((row && row.recv) || 0);
      return acc;
    }, { c: 0, col: 0, recv: 0 });
  }

  function isSupportServiceRow(row) {
    var category = String((row && row.cat) || "").trim();
    return category.indexOf("경영지원") >= 0 || category.indexOf("서비스") >= 0;
  }

  function isBaronProjectRow(row) {
    var category = String((row && row.cat) || "").trim();
    if (category.indexOf("바론") < 0) return false;
    if (isSupportServiceRow(row)) return false;
    return true;
  }

  function bgSummarize(rows, selectedYear) {
    var items = Array.isArray(rows) ? rows : [];
    var targetYear = selectedYear || bgEnsureYear(items)[0];
    var activeRows = items.filter(function (row) { return bgActiveInYear(row, targetYear); });
    var newProjectRows = items.filter(function (row) { return bgStartedInYear(row, targetYear); });
    var completedRows = items.filter(function (row) { return bgCompletedInYear(row, targetYear); });
    var managementRows = newProjectRows.filter(isSupportServiceRow);
    return {
      targetYear: targetYear,
      activeRows: activeRows,
      newProjectRows: newProjectRows,
      completedRows: completedRows,
      managementRows: managementRows,
      managementTotals: bgTotals(managementRows)
    };
  }

  function bgMatches(row) {
    var section = S.dashboard.section || "active";
    var selectedYear = S.dashboard.year || bgEnsureYear(S.all)[0];
    if (section === "new") return bgStartedInYear(row, selectedYear);
    if (section === "completed") return bgCompletedInYear(row, selectedYear);
    return bgActiveInYear(row, selectedYear);
  }

  function normalizeStatusLabel(status) {
    var value = String(status || "").trim();
    if (!value) return "-";
    if (value.indexOf("진행") >= 0) return "과업 진행중";
    return value;
  }

  function formatSplitPercent(split) {
    var numeric = parseFloat(String(split || "").replace(/[^0-9.\-]/g, ""));
    if (!Number.isFinite(numeric) || numeric === 0) return "분담율 -%";
    return "분담율 " + numeric.toFixed(2) + "%";
  }

  function projectYear(row) {
    var start = String((row && row.sDate) || "").trim();
    var startMatch = start.match(/(20\d{2})/);
    if (startMatch) return startMatch[1];
    var name = String((row && row.name) || "").trim();
    var nameMatch = name.match(/^(20\d{2})/);
    if (nameMatch) return nameMatch[1];
    var end = String((row && row.eDate) || "").trim();
    var endMatch = end.match(/(20\d{2})/);
    if (endMatch) return endMatch[1];
    return "미지정";
  }

  function groupSortRank(row) {
    var selectedYear = Number((S.dashboard && S.dashboard.year) || projectYear(row) || 0);
    var startYear = Number(projectYear(row) || 0);
    if (typeof bgCompletedInYear === "function" && bgCompletedInYear(row, String(selectedYear))) return 9999;
    if (!startYear) return 9998;
    return startYear;
  }

  function tableGroupLabel(row) {
    var startYear = projectYear(row);
    if (/^20\d{2}$/.test(startYear)) return startYear + "년";
    return "미지정";
  }

  function renderLedgerTable() {
    var table = document.querySelector(".panel table");
    if (!table || !E.tbody) return;
    var thead = table.querySelector("thead");
    if (thead) {
      thead.innerHTML = '<tr>'
        + '<th><div class="th-head"><button type="button" class="th-trigger" data-filter="cat" data-label="구분"><span class="th-title">구분</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterCatMenu" class="th-menu" data-filter="cat"></div></div></th>'
        + '<th><div class="th-head"><button type="button" class="th-trigger" data-filter="code" data-label="사업코드"><span class="th-title">사업코드</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterCodeMenu" class="th-menu" data-filter="code"></div></div></th>'
        + '<th><div class="th-head"><button type="button" class="th-trigger" data-filter="name" data-label="사업명(계약명)"><span class="th-title">사업명(계약명)</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterNameMenu" class="th-menu" data-filter="name"></div></div></th>'
        + '<th><div class="th-head"><button type="button" class="th-trigger" data-filter="client" data-label="발주처(계약처)"><span class="th-title">발주처(계약처)</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterClientMenu" class="th-menu" data-filter="client"></div></div></th>'
        + '<th><div class="th-head"><button type="button" class="th-trigger" data-filter="order" data-label="발주방법"><span class="th-title">발주방법</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterOrderMenu" class="th-menu" data-filter="order"></div></div></th>'
        + '<th><div class="th-head"><button type="button" class="th-trigger" data-filter="status" data-label="진행상태"><span class="th-title">진행상태</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterStatusMenu" class="th-menu" data-filter="status"></div></div></th>'
        + '<th class="num"><div class="th-head end"><button type="button" class="th-trigger" data-filter="amount" data-label="계약금"><span class="th-title">계약금</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterAmountMenu" class="th-menu" data-filter="amount"></div></div></th>'
        + '<th class="num"><div class="th-head end"><button type="button" class="th-trigger" data-filter="outsource" data-label="외주비"><span class="th-title">외주비</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterOutsourceMenu" class="th-menu" data-filter="outsource"></div></div></th>'
        + '<th class="num"><div class="th-head end"><button type="button" class="th-trigger" data-filter="receivable" data-label="미수금"><span class="th-title">미수금</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterReceivableMenu" class="th-menu" data-filter="receivable"></div></div></th>'
        + '<th class="num"><div class="th-head end"><button type="button" class="th-trigger" data-filter="collected" data-label="수금액"><span class="th-title">수금액</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterCollectedMenu" class="th-menu" data-filter="collected"></div></div></th>'
        + '<th class="num"><div class="th-head end"><button type="button" class="th-trigger" data-filter="rate" data-label="수금률"><span class="th-title">수금률</span><span class="th-mark"></span><span class="th-caret">▼</span></button><div id="filterRateMenu" class="th-menu" data-filter="rate"></div></div></th>'
        + "</tr>";
    }
    var rows = (Array.isArray(S.viewRows) ? S.viewRows : []).slice().sort(function (a, b) {
      var ar = groupSortRank(a);
      var br = groupSortRank(b);
      if (ar !== br) return ar - br;
      return Number(b.recv || 0) - Number(a.recv || 0);
    });
    S.viewRows = rows;
    var lastGroupLabel = "";
    E.tbody.innerHTML = rows.map(function (r) {
      var groupLabel = tableGroupLabel(r);
      var isCollapsed = !!S.collapsedGroups[groupLabel];
      var groupRow = "";
      if (groupLabel !== lastGroupLabel) {
        groupRow = '<tr class="group-row"><td colspan="11"><button type="button" class="group-chip" data-group-label="' + escAttr(groupLabel) + '"><span>' + esc(groupLabel) + '</span><span class="group-toggle" aria-hidden="true">' + (isCollapsed ? "＋" : "－") + "</span></button></td></tr>";
        lastGroupLabel = groupLabel;
      }
      if (isCollapsed) return groupRow;
      return groupRow + '<tr class="' + (isSettledRow(r) ? 'settled' : '') + '">'
        + '<td><div class="badge ' + esc(String(r.cat || "").indexOf("바론") >= 0 ? 'badge-baron' : 'badge-family') + '">' + esc(r.cat || "-") + '</div></td>'
        + '<td><div class="subline" style="margin-top:0;font-size:12px;color:#66756d">' + esc(r.code || "-") + '</div></td>'
        + '<td><button type="button" class="project-link" data-project-key="' + escAttr(String(r.code || "") + "|" + String(r.name || "")) + '">' + esc(r.name || "-") + '</button><div class="subline">' + esc(r.periodText || "-") + '</div></td>'
        + '<td><div class="client-main">' + esc((r.client || "").trim() || "-") + '</div><div class="subline">' + esc(formatSplitPercent(r.split)) + '</div></td>'
        + '<td><div>' + esc(r.order || "-") + '</div></td>'
        + '<td><div class="badge ' + (String(r.status || "").indexOf("완료") >= 0 ? 'ok' : '') + '">' + esc(normalizeStatusLabel(r.status)) + '</div></td>'
        + '<td class="num"><strong>' + esc(won(r.cSup || 0)) + '</strong></td>'
        + '<td class="num"><strong>' + esc(r.outsourceCost ? won(r.outsourceCost) : "-") + '</strong></td>'
        + '<td class="num"><strong>' + esc(won(r.recv || 0)) + '</strong></td>'
        + '<td class="num"><strong>' + esc(won(r.col || 0)) + '</strong></td>'
        + '<td class="num"><strong style="color:' + (isSettledRow(r) ? '#b7aa93' : '#1a5645') + '">' + esc((Number(r.rate || 0)).toFixed(2) + "%") + '</strong></td>'
        + '</tr>';
    }).join("");
  }

  function renderCollectionBoard(r) {
    var payments = Array.isArray(r.payments) && r.payments.length ? r.payments : [{
      pay: r.pay || "-",
      issueDate: r.issueDate || "",
      collectDate: r.collectDateSummary || r.colDate || "",
      collected: r.col || 0,
      receivable: r.recv || Math.max(0, Number(r.sTot || 0) - Number(r.col || 0)),
      note: r.note || "",
      status: r.status || ""
    }];
    return '<div class="ledger-block collect"><div class="ledger-head"><div class="ledger-head-left"><div class="ledger-icon">C</div><div><div class="ledger-name">수금 및 기성 현황</div><div class="ledger-sub">기성 차수별 세금계산서 발행 및 수금 내역</div></div></div><div class="ledger-pill">총 수금 ' + esc(won(r.col || 0)) + '</div></div><div class="ledger-table-wrap"><table class="ledger-table"><thead><tr><th>기성 차수</th><th>세금계산서 발행일</th><th>수금일</th><th style="text-align:right">수금금액</th><th style="text-align:right">미수금액</th><th>비고</th></tr></thead><tbody>'
      + payments.map(function (payment, index) {
        var noteParts = [];
        if (payment.status) noteParts.push(payment.status);
        if (payment.note) noteParts.push(payment.note);
        return '<tr><td><span class="ledger-main">' + esc((index + 1) + "차") + '</span><span class="ledger-muted">' + esc(payment.pay || "-") + '</span></td><td><span class="ledger-main">' + esc(payment.issueDate ? d(payment.issueDate) : "-") + '</span></td><td><span class="ledger-main">' + esc(payment.collectDate ? d(payment.collectDate) : "-") + '</span></td><td class="ledger-amount">' + esc(won(payment.collected || 0)) + '</td><td class="ledger-amount" style="color:#a94832">' + esc(won(payment.receivable || 0)) + '</td><td><span class="ledger-note">' + esc(noteParts.join(" / ") || "-") + '</span></td></tr>';
      }).join("")
      + "</tbody></table></div></div>";
  }

  function renderContactCard(label, name, company, department, phone, email) {
    var hasValue = [name, company, department, phone, email].some(function (value) {
      return String(value || "").trim() !== "";
    });
    if (!hasValue) {
      return '<div class="inline-card"><div class="kvk">' + esc(label) + '</div><div class="summary-note">등록된 담당자 정보가 없습니다.</div></div>';
    }
    return '<div class="inline-card"><div class="kvk">' + esc(label) + '</div><div class="project-meta-grid">'
      + '<div class="kv"><div class="kvk">이름</div><div class="kvv">' + esc(name || "-") + '</div></div>'
      + '<div class="kv"><div class="kvk">소속</div><div class="kvv">' + esc(company || "-") + '</div><div class="summary-note">' + esc(department || "-") + '</div></div>'
      + '<div class="kv"><div class="kvk">연락처</div><div class="kvv">' + esc(phone || "-") + '</div></div>'
      + '<div class="kv"><div class="kvk">이메일</div><div class="kvv">' + esc(email || "-") + '</div></div>'
      + "</div></div>";
  }

  function renderProjectInline(r) {
    var payments = Array.isArray(r.payments) ? r.payments : [];
    var latestCollect = d(r.collectDateSummary || r.colDate);
    var hasOutsource = (Array.isArray(r.outsourceItems) && r.outsourceItems.length > 0) || Number(r.outsourceCost || 0) > 0 || Number(r.outsourcePaid || 0) > 0 || Number(r.outsourceRemaining || 0) > 0;
    var clientDisplay = typeof normalizeClientDisplay === "function" ? normalizeClientDisplay(r.client) : (String(r.client || "").trim() || "-");
    var splitDisplay = typeof formatSplitDisplay === "function" ? formatSplitDisplay(r.split) : formatSplitPercent(r.split).replace("분담율 ", "");
    var summaryCards = [
      '<div class="summary-card"><div class="summary-label">계약금</div><div class="summary-value">' + esc(won(r.cSup || 0)) + '</div><div class="summary-note"></div></div>',
      '<div class="summary-card"><div class="summary-label">수금액</div><div class="summary-value">' + esc(won(r.col || 0)) + '</div><div class="summary-note">' + esc(latestCollect === "-" ? "수금일 없음" : "최종 수금일 " + latestCollect) + '</div></div>',
      '<div class="summary-card"><div class="summary-label">수금률</div><div class="summary-value">' + esc((Number(r.rate || 0)).toFixed(2) + "%") + '</div><div class="summary-note">' + esc(payments.length ? "기성 " + payments.length + "차까지 반영" : "차수 정보 없음") + '</div></div>',
      '<div class="summary-card receivable"><div class="summary-label">미수금액</div><div class="summary-value">' + esc(won(r.recv || 0)) + '</div><div class="summary-note">잔여 수금 필요 금액</div></div>'
    ].join("");
    var boards = [
      hasOutsource && typeof renderOutsourceBoard === "function" ? renderOutsourceBoard(r) : "",
      renderCollectionBoard(r)
    ].filter(Boolean).join("");
    return '<div class="inline-panel"><div class="project-head project-head-grid"><div class="project-head-main"><div class="inline-card"><div class="project-meta-grid"><div class="kv"><div class="kvk">계약법인</div><div class="kvv">' + esc(r.corp || "-") + '</div></div><div class="kv"><div class="kvk">발주처</div><div class="kvv">' + esc(clientDisplay) + '</div><div class="summary-note">' + esc(splitDisplay ? "분담율 " + splitDisplay : "분담율 -") + '</div></div><div class="kv"><div class="kvk">발주방법</div><div class="kvv">' + esc(r.order || "-") + '</div></div><div class="kv"><div class="kvk">PM</div><div class="kvv">' + esc(r.pm || "-") + '</div></div></div></div><div class="inline-card"><div class="summary-grid">' + summaryCards + '</div><div class="project-progress progress"><div class="bar" style="width:' + esc(String(Math.max(0, Math.min(100, Number(r.rate || 0))))) + '%"></div></div></div></div><div class="project-contact-stack">' + renderContactCard("계약 / 청구 담당자", r.cmNm, r.cmCo, r.cmDp, r.cmPh, r.cmEm) + renderContactCard("부서 담당자", r.dmNm, r.dmCo, r.dmDp, r.dmPh, r.dmEm) + '</div></div><div class="ledger-stack">' + boards + '</div></div>';
  }

  function openProjectWindow(r) {
    var popupKey = typeof rowKey === "function"
      ? rowKey(r).replace(/[^0-9a-zA-Z]/g, "_")
      : String((r.code || "project") + "_" + (r.name || "")).replace(/[^0-9a-zA-Z_]/g, "_");
    var popup = window.open("", "business_project_" + popupKey, "width=1600,height=980,resizable=yes,scrollbars=yes");
    if (!popup) return;
    var styleText = Array.from(document.querySelectorAll("style")).map(function (el) {
      return el.textContent || "";
    }).join("\n");
    var detailHtml = renderProjectInline(r);
    var pageHtml = '<!DOCTYPE html><html lang="ko"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>'
      + esc(r.name || "사업 상세")
      + '</title><link rel="stylesheet" href="/design-tokens.css?v=20260401-01"><link rel="stylesheet" href="/design-patterns.css?v=20260401-01"><style>' + styleText
      + 'body{margin:0;background:#f1eadf;color:#10251d;font-family:"Pretendard","Noto Sans KR","Malgun Gothic",sans-serif;}'
      + '.popup-wrap{max-width:1680px;margin:0 auto;padding:20px;}'
      + '@media (max-width: 1180px){.project-head-grid{grid-template-columns:1fr;}.summary-grid{grid-template-columns:repeat(2,minmax(0,1fr));}.project-meta-grid{grid-template-columns:1fr;}}'
      + '@media (max-width: 760px){.popup-wrap{padding:14px;}.summary-grid{grid-template-columns:1fr;}.ledger-head{flex-direction:column;align-items:flex-start;}.ledger-pill{white-space:normal;}.ledger-table-wrap{padding:0 10px 12px;overflow-x:auto;}}'
      + '</style></head><body><div class="popup-wrap"><div class="popup-head"><div class="popup-title">' + esc(r.name || "-") + '</div><div class="popup-sub">사업코드 ' + esc(r.code || "-") + ' · 계약법인 ' + esc(r.corp || "-") + '</div></div>' + detailHtml + "</div></body></html>";
    popup.document.open();
    popup.document.write(pageHtml);
    popup.document.close();
    popup.focus();
  }

  async function tryLoadDbDefaultBusinessLedger() {
    if (window.__mhBusinessDefaultLoaded) return;
    window.__mhBusinessDefaultLoaded = true;
    try {
      var response = await fetch("/api/integration/business-ledger-default");
      if (!response.ok) throw new Error("기본 사업관리대장 원본을 불러오지 못했습니다.");
      var fileName = response.headers.get("x-source-filename") || "사업관리대장-1.xlsx";
      var buffer = await response.arrayBuffer();
      if (!buffer || !buffer.byteLength) throw new Error("기본 사업관리대장 원본 데이터가 비어 있습니다.");
      await loadLedgerFile(buffer, fileName);
    } catch (error) {
      console.error(error);
    }
  }

  function applyDashboardChrome() {
    if (!E.cards) return;
    document.body.setAttribute("data-mh-ledger-enhanced", "true");
    var wrap = document.querySelector(".wrap");
    var panel = document.querySelector(".panel");
    if (wrap && panel) {
      var shell = wrap.querySelector(".business-shell");
      if (!shell) {
        shell = document.createElement("div");
        shell.className = "business-shell";
        wrap.insertBefore(shell, E.cards);
      }
      if (E.cards.parentNode !== shell) shell.appendChild(E.cards);
      if (panel.parentNode !== shell) shell.appendChild(panel);
    }
    var years = bgEnsureYear(S.all);
    var summary = bgSummarize(S.all, S.dashboard.year);
    var rows = Array.isArray(S.rows) ? S.rows : [];
    var visibleBaronProjectRows = rows.filter(isBaronProjectRow);
    var totals = bgTotals(visibleBaronProjectRows);
    var totalRate = typeof rate === "function" ? rate("", totals.col, totals.col + totals.recv) : 0;
    var toolbarHtml = '<div class="cards-toolbar">'
      + '<div class="cards-toolbar-row">'
      + years.map(function (year) {
        return '<button type="button" class="summary-year-chip ' + (S.dashboard.year === year ? "active" : "") + '" data-dashboard-year="' + escAttr(year) + '">' + esc(year) + "</button>";
      }).join("")
      + '<div class="cards-toolbar-search"></div>'
      + "</div>"
      + '<div class="cards-toolbar-metrics">'
      + '<button type="button" class="summary-filter-chip ' + (S.dashboard.section === "active" ? "active" : "") + '" data-dashboard-section="active"><span class="label">' + esc(summary.targetYear) + '년 진행과업</span><span class="count">' + summary.activeRows.length.toLocaleString("ko-KR") + '건</span><span class="meta">전년도 이월 사업 포함</span></button>'
      + '<button type="button" class="summary-filter-chip ' + (S.dashboard.section === "new" ? "active" : "") + '" data-dashboard-section="new"><span class="label">' + esc(summary.targetYear) + '년 신규프로젝트</span><span class="count">' + summary.newProjectRows.length.toLocaleString("ko-KR") + '건</span><span class="meta">계약기간 시작년도 기준</span></button>'
      + '<button type="button" class="summary-filter-chip ' + (S.dashboard.section === "completed" ? "active" : "") + '" data-dashboard-section="completed"><span class="label">' + esc(summary.targetYear) + '년 완료과업</span><span class="count">' + summary.completedRows.length.toLocaleString("ko-KR") + '건</span><span class="meta">해당년도 종료 사업 기준</span></button>'
      + "</div></div>";
    var cards = [
      { label: summary.targetYear + "년 프로젝트", value: visibleBaronProjectRows.length.toLocaleString("ko-KR") + " 건", note: "" },
      { label: "계약금", value: won(totals.c), note: "" },
      { label: "수금액", value: won(totals.col), note: "" },
      { label: "미수금", value: won(totals.recv), note: "" },
      { label: "수금률(%)", value: totalRate.toFixed(2) + "%", note: "" },
      { label: "경영지원서비스 금액", value: won(summary.managementTotals.c), note: "", className: "management" }
    ];
    E.cards.innerHTML = toolbarHtml + cards.map(function (card) {
      return '<div class="card ' + esc(card.className || "") + '"><div class="k">' + esc(card.label) + '</div><div class="v">' + esc(card.value) + '</div><div class="n">' + esc(card.note || "") + "</div></div>";
    }).join("");
    var searchWrap = E.cards.querySelector(".cards-toolbar-search");
    if (searchWrap && E.search) {
      searchWrap.appendChild(E.search);
      E.search.placeholder = "전체 검색";
    }
  }

  var originalRender = render;
  render = function () {
    originalRender();
    applyDashboardChrome();
    renderLedgerTable();
  };

  filter = function () {
    bgEnsureYear(S.all);
    var q = String(E.search.value || "").trim().toLowerCase();
    var searched = !q ? S.all.slice() : S.all.filter(function (r) {
      return [r.code, r.name, r.client, r.pm, r.status, r.cat, r.corp, r.pay, (r.payments || []).map(function (p) { return p.pay; }).join(" "), r.periodText].join(" ").toLowerCase().includes(q);
    });
    S.rows = searched.filter(function (r) {
      return bgMatches(r) && matchesColumnFilters(r);
    });
    render();
  };

  if (E.cards && !E.cards.dataset.dashboardBound) {
    E.cards.dataset.dashboardBound = "true";
    E.cards.addEventListener("click", function (event) {
      var yearButton = event.target && event.target.closest ? event.target.closest("[data-dashboard-year]") : null;
      if (yearButton) {
        S.dashboard.year = yearButton.getAttribute("data-dashboard-year") || S.dashboard.year;
        filter();
        return;
      }
      var sectionButton = event.target && event.target.closest ? event.target.closest("[data-dashboard-section]") : null;
      if (sectionButton) {
        S.dashboard.section = sectionButton.getAttribute("data-dashboard-section") || "active";
        filter();
      }
    });
  }

  if (E.tbody && !E.tbody.dataset.projectBound) {
    E.tbody.dataset.projectBound = "true";
    E.tbody.addEventListener("click", function (event) {
      var groupButton = event.target && event.target.closest ? event.target.closest("[data-group-label]") : null;
      if (groupButton) {
        var label = groupButton.getAttribute("data-group-label") || "";
        if (label) {
          S.collapsedGroups[label] = !S.collapsedGroups[label];
          render();
        }
        return;
      }
      var trigger = event.target && event.target.closest ? event.target.closest(".project-link") : null;
      if (!trigger) return;
      var key = trigger.getAttribute("data-project-key") || "";
      var rows = Array.isArray(S.viewRows) ? S.viewRows : [];
      var row = rows.find(function (item) {
        return (String(item.code || "") + "|" + String(item.name || "")) === key;
      });
      if (row) openProjectWindow(row);
    });
  }

  setTimeout(function () {
    try {
      filter();
      if (typeof loadLedgerFile === "function") {
        tryLoadDbDefaultBusinessLedger();
      }
    } catch (error) {
      console.error(error);
    }
  }, 0);

  window.addEventListener("message", function (event) {
    var data = event.data || {};
    if (data.source !== "total-upload" || data.type !== "business") return;
    setTimeout(function () {
      try {
        applyDashboardChrome();
        renderLedgerTable();
      } catch (error) {
        console.error(error);
      }
    }, 50);
  });
})();
