// data.jsx — реалістичні дані для ERP «Укрбудпроєкт»

const TEAM = [
  { id: "ok", initials: "ОК", name: "Олена Кравченко", role: "ГІП (адмін)", load: 95, phone: "+380 67 123 45 67", email: "o.kravchenko@ukrbudproiekt.ua",
    employment: "staff", hireDate: "2018-04-02", birthDate: "1985-09-14",
    salaryModel: "fixed_bonus", baseSalary: 65000, bonusRate: 0.04, cashSalary: 0,
    education: "КНУБА, ПЦБ, маг. 2008", certs: ["Сертифікат відповідального виконавця №АА-1247", "ГІП-сертифікат, 2020"],
    docs: ["passport", "id_card", "tax_id"], vacationBalance: 18, sickBalance: 0,
    address: "Київ, вул. Лук'янівська 21, кв. 14", iban: "UA293052990000026003123456789",
    // — кадрові та податкові дані (для довідок про доходи / ДПС / банк)
    positionOfficial: "Директор, головний інженер проєкту",
    taxId: "2891405672",
    idDoc: { kind: "ID-картка", number: "001 245 789", issuedBy: "1234 (ДМС у Шевченківському р-ні м. Києва)", issuedDate: "2019-06-10" },
    hireOrder: "Наказ №1-К від 02.04.2018",
    contractType: "Безстроковий трудовий договір",
    // — КЕП (кваліфікований електронний підпис)
    kep: { type: "Печатка ТОВ + КЕП ФОП", provider: "Дія.Підпис", serial: "5E 9A 1C 04", issuedDate: "2025-03-12", validUntil: "2027-03-12" },
    qualCerts: [
      { kind: "Головний інженер проєкту (ГІП)", number: "ГІП-0571", class: "СС3",
        scope: "Об'єкти зі значними наслідками (СС3)",
        issuedDate: "2008-11-20", lastTraining: "2023-04-15", lastEdessbReport: "2025-06-10",
        scan: { name: "Сертифікат ГІП-0571.pdf", slot: "scan-cert-ok", uploaded: "2023-04-20" } },
    ],
    // — страхування професійної відповідальності (поновлюється щороку)
    insurance: { policyNumber: "ARX-2025-44871", insurer: "СК «ARX»", sumInsured: 2000000, premium: 14000,
      startDate: "2025-09-01", endDate: "2026-09-01",
      scan: { name: "Поліс ARX-2025-44871.pdf", slot: "scan-ins-ok" } },
    // — членство у профспілці / СРО (внесок щороку)
    membership: { org: "Гільдія інженерів-проєктувальників України", memberSince: "2012", feeAmount: 4800,
      lastPaid: "2026-01-15", paidUntil: "2027-01-15",
      scan: { name: "Квитанція про сплату 2026.pdf", slot: "scan-mem-ok" } },
  },
  { id: "ap", initials: "АП", name: "Андрій Петренко", role: "Конструктор", load: 80, phone: "+380 67 234 56 78", email: "a.petrenko@ukrbudproiekt.ua",
    employment: "staff", hireDate: "2020-07-15", birthDate: "1988-03-22",
    salaryModel: "fixed_bonus", baseSalary: 45000, bonusRate: 0.025, cashSalary: 20000,
    education: "КНУБА, ПЦБ, маг. 2011", certs: ["Сертифікат відповідального виконавця №КП-3382"],
    docs: ["passport", "id_card", "tax_id"], vacationBalance: 12, sickBalance: 0,
    address: "Київ, пр. Перемоги 75, кв. 88", iban: "UA293052990000026003234567890",
    positionOfficial: "Інженер-конструктор I категорії",
    taxId: "3012284561",
    idDoc: { kind: "ID-картка", number: "002 318 640", issuedBy: "8021 (ДМС у Святошинському р-ні м. Києва)", issuedDate: "2018-11-03" },
    hireOrder: "Наказ №18-К від 15.07.2020",
    contractType: "Безстроковий трудовий договір",
    kep: { type: "КЕП фахівця (підпис як автор КЖ)", provider: "АЦСК «Україна»", serial: "7B 22 D5 81", issuedDate: "2025-08-15", validUntil: "2026-08-15" },
    qualCerts: [
      { kind: "Відповідальний виконавець окремих видів робіт", number: "КП-3382", class: "СС1",
        scope: "Конструктивні рішення",
        issuedDate: "2016-05-12", lastTraining: "2021-03-20", lastEdessbReport: "2025-09-01",
        scan: { name: "Сертифікат КП-3382.pdf", slot: "scan-cert-ap", uploaded: "2021-03-25" } },
    ],
    insurance: { policyNumber: "ВУСО-7720-118", insurer: "СК «ВУСО»", sumInsured: 1000000, premium: 7200,
      startDate: "2025-06-11", endDate: "2026-06-11",
      scan: { name: "Поліс ВУСО-7720-118.pdf", slot: "scan-ins-ap" } },
    membership: { org: "Спілка інженерів-будівельників України", memberSince: "2017", feeAmount: 3600,
      lastPaid: "2025-04-20", paidUntil: "2026-04-20",
      scan: { name: "Квитанція про сплату 2025.pdf", slot: "scan-mem-ap" } },
  },
  { id: "tm", initials: "ТМ", name: "Тарас Мельник", role: "Архітектор", load: 70, phone: "+380 67 345 67 89", email: "t.melnyk@ukrbudproiekt.ua",
    employment: "staff", hireDate: "2021-02-10", birthDate: "1990-11-08",
    salaryModel: "fixed_bonus", baseSalary: 42000, bonusRate: 0.02, cashSalary: 15000,
    edessbRegistrar: { since: "2023-02-01", scope: "Розділи АР · подача та реєстрація об'єктів у ЄДЕССБ", contractType: "Внутрішнє суміщення (наказ №18)", rate: 450, hours: 22 },
    education: "КНУБА, Архітектура, маг. 2013", certs: ["Сертифікат архітектора №АР-2891"],
    docs: ["passport", "id_card", "tax_id"], vacationBalance: 24, sickBalance: 2,
    address: "Київ, вул. Антоновича 102", iban: "UA293052990000026003345678901",
    positionOfficial: "Архітектор",
    taxId: "3105187423",
    idDoc: { kind: "ID-картка", number: "003 901 226", issuedBy: "8043 (ДМС у Печерському р-ні м. Києва)", issuedDate: "2020-02-18" },
    hireOrder: "Наказ №27-К від 10.02.2021",
    contractType: "Безстроковий трудовий договір",
    kep: { type: "КЕП фахівця (підпис як автор АР)", provider: "АЦСК «Україна»", serial: "3C 90 4F 17", issuedDate: "2025-02-08", validUntil: "2027-02-08" },
    qualCerts: [
      { kind: "Архітектор (відповідальний виконавець)", number: "АР-2891", class: "СС2",
        scope: "Архітектурно-будівельні рішення",
        issuedDate: "2014-07-01", lastTraining: "2022-09-10", lastEdessbReport: "2024-12-15",
        scan: { name: "Сертифікат АР-2891.pdf", slot: "scan-cert-tm", uploaded: "2022-09-15" } },
    ],
    insurance: { policyNumber: "УНІКА-AR-3391", insurer: "СК «УНІКА»", sumInsured: 1500000, premium: 9600,
      startDate: "2025-05-18", endDate: "2026-05-18",
      scan: { name: "Поліс УНІКА-AR-3391.pdf", slot: "scan-ins-tm" } },
    membership: { org: "Національна спілка архітекторів України (НСАУ)", memberSince: "2015", feeAmount: 2400,
      lastPaid: "2026-02-14", paidUntil: "2027-02-14",
      scan: { name: "Квитанція про сплату 2026.pdf", slot: "scan-mem-tm" } },
  },
  { id: "vs", initials: "ВС", name: "Вікторія Савчук", role: "Інженер ОВ-ВК", load: 50, phone: "+380 67 456 78 90", email: "v.savchuk@ukrbudproiekt.ua",
    employment: "part", hireDate: "2022-09-01", birthDate: "1992-06-17",
    salaryModel: "fixed", baseSalary: 22000, partRate: 0.5, cashSalary: 7000,
    edessbRegistrar: { since: "2024-03-15", scope: "Розділи ОВ-ВК · подача та реєстрація об'єктів у ЄДЕССБ", contractType: "Внутрішнє суміщення (наказ №31)", rate: 400, hours: 14 },
    education: "КНУБА, Теплогазопостачання, спец. 2015", certs: ["Сертифікат інженера ОВ-ВК"],
    docs: ["passport", "id_card", "tax_id"], vacationBalance: 12, sickBalance: 0,
    address: "Київ, вул. Сім'ї Сосніних 9", iban: "UA293052990000026003456789012",
    positionOfficial: "Інженер з опалення, вентиляції та водопостачання (0,5 ставки)",
    taxId: "3217064895",
    idDoc: { kind: "ID-картка", number: "004 552 130", issuedBy: "8002 (ДМС у Голосіївському р-ні м. Києва)", issuedDate: "2017-07-22" },
    hireOrder: "Наказ №34-К від 01.09.2022",
    contractType: "Трудовий договір, 0,5 ставки",
    kep: { type: "КЕП фахівця", provider: "АЦСК «Україна»", serial: "9F 18 6B 2A", issuedDate: "2025-11-10", validUntil: "2026-11-10" },
  },
  { id: "dl", initials: "ДЛ", name: "Дмитро Левченко", role: "Електрик", load: 40, phone: "+380 67 567 89 01", email: "d.levchenko@ukrbudproiekt.ua",
    employment: "contractor", hireDate: "2024-03-01", birthDate: "1987-12-04",
    salaryModel: "task_rate", taskRate: 800, // ₴/год за задачу
    education: "НТУУ КПІ, Електропостачання, маг. 2010", certs: ["Сертифікат інженера-електрика"],
    docs: ["contract_cph"], vacationBalance: 0, sickBalance: 0,
    address: "Київ, вул. Богатирська 6", iban: "UA293052990000026003567890123",
    positionOfficial: "Інженер-електрик (за договором ЦПХ)",
    taxId: "2845128370",
    idDoc: { kind: "Паспорт громадянина України", number: "СН 482190", issuedBy: "Деснянський РУ ГУ МВС у м. Києві", issuedDate: "2006-04-19" },
    hireOrder: "Договір ЦПХ №7-ЦПХ від 01.03.2024",
    contractType: "Договір цивільно-правового характеру (ЦПХ)",
    kep: { type: "КЕП ФОП (для ЦПХ)", provider: "Дія.Підпис", serial: "1D 70 A3 55", issuedDate: "2024-12-31", validUntil: "2025-12-31" },
  },
];

// Реквізити компанії — використовуються у довідках про доходи та інших офіційних документах
const COMPANY = {
  shortName: "ТОВ «УКРБУДПРОЄКТ»",
  fullName: "Товариство з обмеженою відповідальністю «УКРБУДПРОЄКТ»",
  fullNameUpper: "ТОВАРИСТВО З ОБМЕЖЕНОЮ ВІДПОВІДАЛЬНІСТЮ «УКРБУДПРОЄКТ»",
  edrpou: "44531964",
  vatId: "445319615512",
  vatPayer: true,
  address: "65062, м. Одеса, Приморський район, вул. Аркадійське плато, буд. 5/2, прим. 13",
  city: "Одеса",
  iban: "UA293220010000026004700004299",
  bank: "АТ «Універсал Банк»",
  mfo: "322001",
  director: "Підкапка М.І.",
  directorFull: "Підкапка Марія Іванівна",
  directorGen: "Підкапки Марії Іванівни",
  directorSign: "Марія ПІДКАПКА",
  directorRole: "Директорка",
  directorBasis: "Статуту",
  accountant: "Підкапка М.І.",
  phone: "+380 (48) 700 16 69",
  email: "info@ukrbudproiekt.ua",
  web: "ukrbudproiekt.ua",
  tmText: "УКРБУДПРОЄКТ® — зареєстрована торговельна марка",
  tmCert: "Свідоцтво № 37394 від 24.12.2025 р.",
};
window.COMPANY = COMPANY;

// ФОП Підкапка М.І. — друга юрособа (неплатник ПДВ), для договорів менших об'єктів
const COMPANY_FOP = {
  shortName: "ФОП Підкапка М.І.",
  fullName: "Фізична особа — підприємець Підкапка Марія Іванівна",
  fullNameUpper: "ФІЗИЧНА ОСОБА — ПІДПРИЄМЕЦЬ ПІДКАПКА МАРІЯ ІВАНІВНА",
  edrpou: "3405603368",
  edrpouLabel: "ІПН (РНОКПП)",
  vatId: null,
  vatPayer: false,
  taxNote: "платник єдиного податку, не є платником ПДВ",
  address: "65123, Одеська обл., м. Одеса, вул. Заболотного Академіка, буд. 57/1, кв. 55",
  city: "Одеса",
  iban: "UA493220010000026005350073107",
  bank: "АТ «Універсал Банк»",
  mfo: "322001",
  director: "Підкапка М.І.",
  directorFull: "Підкапка Марія Іванівна",
  directorGen: "Підкапки Марії Іванівни",
  directorSign: "Марія ПІДКАПКА",
  directorRole: "Фізична особа — підприємець",
  directorBasis: "запису в ЄДР",
  edrRecord: "2010350010001763879",
  edrDate: "2025-07-07",
  phone: "+380 63 051 99 16, +380 50 224 84 49",
  email: "info@ukrbudproiekt.ua",
  web: "ukrbudproiekt.ua",
  tmText: "УКРБУДПРОЄКТ® — зареєстрована торговельна марка",
  tmCert: "Свідоцтво № 37394 від 24.12.2025 р.",
};
window.COMPANY_FOP = COMPANY_FOP;
// Повертає реквізити Виконавця за обраною юрособою
window.execEntity = (kind) => (kind === "fop" ? COMPANY_FOP : COMPANY);

// Поточна дата системи як ISO-рядок (окремо від window.TODAY — той об'єкт для UI)
const SYS_DATE = "2026-05-29";
window.SYS_DATE = SYS_DATE;

// Статус КЕП за датою завершення дії: active / expiring (<= 60 днів) / expired
window.kepStatus = function (validUntil, today) {
  const t = new Date((today || SYS_DATE) + "T00:00:00");
  const v = new Date(validUntil + "T00:00:00");
  const days = Math.round((v - t) / 86400000);
  if (days < 0)  return { state: "expired",  days, label: "Прострочено",        tone: "late" };
  if (days <= 60) return { state: "expiring", days, label: "Завершується",       tone: "warn" };
  return { state: "active", days, label: "Дійсний", tone: "live" };
};

// Помісячний дохід за останні 6 місяців — для довідки про доходи / банку / ДПС.
// Детермінований (без random), повертає брутто, ПДФО (18%), військовий збір (5%), «на руки».
window.incomeRows = function (person, months) {
  const MONTHS_UA = ["січень","лютий","березень","квітень","травень","червень","липень","серпень","вересень","жовтень","листопад","грудень"];
  const n = months || 6;
  // базовий місячний нарахунок
  let base;
  if (person.salaryModel === "task_rate") base = (person.taskRate || 0) * 80; // ~80 год/міс за задачами
  else base = (person.baseSalary || 0) * (person.partRate || 1);
  // невеликі детерміновані коливання (бонуси/години) за номером місяця
  const wobble = [1.0, 1.0, 1.06, 1.0, 1.12, 1.0, 1.0, 1.04, 1.0, 1.08, 1.0, 1.15];
  // 6 останніх повних місяців до травня 2026 → лист.2025 … квіт.2026
  const seq = [
    { y: 2025, m: 10 }, { y: 2025, m: 11 }, { y: 2026, m: 0 },
    { y: 2026, m: 1 },  { y: 2026, m: 2 },  { y: 2026, m: 3 },
  ].slice(-n);
  return seq.map(({ y, m }) => {
    const gross = Math.round(base * (person.salaryModel === "fixed_bonus" ? wobble[m] : 1) / 10) * 10;
    const pdfo = Math.round(gross * 0.18);
    const vz   = Math.round(gross * 0.05);
    return { label: `${MONTHS_UA[m]} ${y}`, gross, pdfo, vz, net: gross - pdfo - vz };
  });
};

// — Кваліфікаційні сертифікати: підвищення кваліфікації (раз на 5 р.) + безперервність стажу в ЄДЕССБ (раз на рік)
function addYearsISO(iso, n) {
  const [y, m, d] = iso.split("-").map(Number);
  return `${y + n}-${String(m).padStart(2, "0")}-${String(d).padStart(2, "0")}`;
}
function daysUntil(iso, today) {
  const t = new Date((today || window.SYS_DATE) + "T00:00:00");
  const v = new Date(iso + "T00:00:00");
  return Math.round((v - t) / 86400000);
}
window.addYearsISO = addYearsISO;

// Статус підвищення кваліфікації: дедлайн = остання атестація (або видача) + 5 років
window.qualTrainingStatus = function (qc, today) {
  const base = qc.lastTraining || qc.issuedDate;
  const dueDate = addYearsISO(base, 5);
  const days = daysUntil(dueDate, today);
  let state = "active", label = "За графіком", tone = "live";
  if (days < 0)      { state = "expired";  label = "Прострочено";  tone = "late"; }
  else if (days <= 90) { state = "expiring"; label = "Скоро дедлайн"; tone = "warn"; }
  return { dueDate, days, state, label, tone };
};

// Статус подання відомостей про безперервність стажу в ЄДЕССБ: дедлайн = останнє подання + 1 рік
window.edessbContinuityStatus = function (qc, today) {
  const base = qc.lastEdessbReport || qc.issuedDate;
  const dueDate = addYearsISO(base, 1);
  const days = daysUntil(dueDate, today);
  let state = "active", label = "Подано", tone = "live";
  if (days < 0)      { state = "expired";  label = "Час подавати"; tone = "late"; }
  else if (days <= 30) { state = "expiring"; label = "Скоро подавати"; tone = "warn"; }
  return { dueDate, days, state, label, tone };
};

// Статус договору страхування професійної відповідальності: дедлайн = дата завершення поліса (щороку)
window.insuranceStatus = function (ins, today) {
  if (!ins) return null;
  const dueDate = ins.endDate;
  const days = daysUntil(dueDate, today);
  let state = "active", label = "Чинний", tone = "live";
  if (days < 0)        { state = "expired";  label = "Прострочено"; tone = "late"; }
  else if (days <= 45) { state = "expiring"; label = "Спливає";     tone = "warn"; }
  return { dueDate, days, state, label, tone };
};

// Статус сплати членських внесків: дедлайн = дата, до якої сплачено (щороку)
window.membershipStatus = function (m, today) {
  if (!m) return null;
  const dueDate = m.paidUntil;
  const days = daysUntil(dueDate, today);
  let state = "active", label = "Сплачено", tone = "live";
  if (days < 0)        { state = "expired";  label = "Час сплатити"; tone = "late"; }
  else if (days <= 45) { state = "expiring"; label = "Скоро внесок"; tone = "warn"; }
  return { dueDate, days, state, label, tone };
};

// Наступний клас наслідків для підвищення (СС1 → СС2 → СС3)
window.qualUpgrade = function (qc) {
  const order = ["СС1", "СС2", "СС3"];
  const i = order.indexOf((qc.class || "").trim());
  if (i < 0 || i >= order.length - 1) return null;
  return { current: order[i], next: order[i + 1] };
};

// Класи наслідків та обсяг експертизи:
//   СС1 (прості умови)                       — expertiseScope: "estimate"  (тільки експертиза кошторису)
//   СС1 (складні техногенні умови, напр. сейсмічність) — expertiseScope: "strength" (МОС — міцність, остійність,
//                                                                  стійкість — РАЗОМ із кошторисом; для бюджету кошторис завжди)
//   СС1 (складні)      — expertiseScope: "full"      (комплексна)
//   СС2                — expertiseScope: "full"
//   СС3                — expertiseScope: "full"+
//
// Кожен етап має масив задач і прогноз скільки задач буде взагалі (`plan`)
// — це дозволяє показувати «зроблено 3 з 8» навіть до повного декомпозу.

const STAGES = [
  { id: "project",     name: "Проєктні роботи",         short: "Проєкт"   },
  { id: "estimate",    name: "Кошторис та аналіз цін",  short: "Кошторис" },
  { id: "expertise",   name: "Експертиза",              short: "Експерт." },
  { id: "edessb",      name: "Реєстрація в ЄДЕССБ",     short: "ЄДЕССБ"   },
  { id: "supervision", name: "Авторський нагляд",       short: "Авт. нагл." },
];

// Коригування — паралельний цикл з тих самих 4 етапів, показуємо тільки якщо є задачі/план
const CORRECTION_STAGES = [
  { id: "c-project",   name: "Коригування: проєкт",     short: "Проєкт"   },
  { id: "c-estimate",  name: "Коригування: кошторис",   short: "Кошторис" },
  { id: "c-expertise", name: "Коригування: експертиза", short: "Експерт." },
  { id: "c-edessb",    name: "Коригування: ЄДЕССБ",     short: "ЄДЕССБ"   },
];

const OBJECTS = [
  {
    id: "ukp-47", code: "УКП-2025-47", name: "ЖК «Подільський»",
    client: "ТОВ «Подільський Девелопмент»", address: "Київ, Подільський р-н",
    workType: "new",
    grade: "СС2", expertiseScope: "full", gradeKind: "calculated",
    gradeHistory: [
      { value: "СС2", kind: "preliminary", date: "2025-03-12", by: "ok", basis: "Завдання на проєктування, погоджене із замовником" },
      { value: "СС2", kind: "calculated", date: "2025-04-20", by: "ap", basis: "Розрахунок класу наслідків за ДБН В.1.2-14: підтверджено СС2 (>300 осіб)" },
    ],
    activeStage: "project",
    revision: "Ред. 2", deadline: "12.06", contract: "8.4 млн ₴",
    team: ["ok","ap","tm","vs"],
    plan: { project: 18, estimate: 5, expertise: 9, edessb: 3, supervision: 0,
            "c-project": 0, "c-estimate": 0, "c-expertise": 0, "c-edessb": 0 },
  },
  {
    id: "ukp-32", code: "УКП-2025-32", name: "Школа №14",
    client: "Управління освіти м. Києва", address: "Святошин",
    grade: "СС2", expertiseScope: "full", gradeKind: "preliminary", workType: "new",
    gradeHistory: [
      { value: "СС2", kind: "preliminary", date: "2025-09-20", by: "ok", basis: "Завдання на проєктування (бюджетний тендер). Розрахунок класу ще не виконано." },
    ],
    activeStage: "project",
    revision: "Ред. 1", deadline: "28.06", contract: "3.1 млн ₴",
    team: ["ok","tm","vs","dl"],
    plan: { project: 22, estimate: 6, expertise: 11, edessb: 3, supervision: 0,
            "c-project": 0, "c-estimate": 0, "c-expertise": 0, "c-edessb": 0 },
  },
  {
    id: "ukp-51", code: "УКП-2025-51", name: "«Соснові гаї»",
    client: "ПП «Лісове»", address: "с. Лісники",
    grade: "СС1", expertiseScope: "estimate", gradeKind: "preliminary", workType: "new",
    gradeHistory: [
      { value: "СС1", kind: "preliminary", date: "2025-12-18", by: "ok", basis: "Завдання на проєктування. Попередньо СС1 — малоповерхова житлова забудова." },
    ],
    activeStage: "project",
    revision: "Ред. 1", deadline: "15.07", contract: "1.7 млн ₴",
    team: ["ok","ap","tm"],
    plan: { project: 14, estimate: 4, expertise: 2, edessb: 2, supervision: 0,
            "c-project": 0, "c-estimate": 0, "c-expertise": 0, "c-edessb": 0 },
  },
  {
    id: "ukp-89", code: "УКП-2024-89", name: "Реконструкція адмінбудівлі",
    client: "Міністерство юстиції", address: "вул. Грушевського 8",
    grade: "СС2", expertiseScope: "full", gradeKind: "calculated", workType: "reconstruction",
    gradeHistory: [
      { value: "СС1", kind: "preliminary", date: "2024-06-01", by: "ok", basis: "Завдання на проєктування — попередньо СС1." },
      { value: "СС2", kind: "calculated", date: "2024-07-10", by: "ap", basis: "За результатами ТО: пам'ятка-складова + понад 300 відвідувачів — підвищено до СС2." },
    ],
    activeStage: "supervision",
    revision: "—", deadline: "31.12", contract: "0.8 млн ₴",
    team: ["ok","ap"],
    plan: { project: 16, estimate: 5, expertise: 8, edessb: 3, supervision: 12,
            "c-project": 6, "c-estimate": 2, "c-expertise": 4, "c-edessb": 2 },
    completedStages: ["project","estimate","expertise","edessb","c-project","c-estimate","c-expertise","c-edessb"],
  },
];

// Сьогодні = вівторок, 26 травня 2026
// Кожна задача має stage (етап) або null для адміністративних
const TASKS = [
  // === ЖК Подільський (УКП-47), проєктні роботи ===
  { id: "t1", title: "Розрахунок плит перекриття 4-9 поверхів", obj: "ukp-47", stage: "project", assignee: "ap", section: "КЖ", day: "26", time: "10:30 — 13:00", est: "2.5 год", status: "live",
    note: "Перерахувати з урахуванням нової специфікації арматури А500С.",
    subtasks: [
      { id: "st-t1-a", title: "Узгодити специфікацію арматури з постачальником", assignee: "ap", status: "done", due: "28 трав" },
      { id: "st-t1-b", title: "Передати розрахункову модель на перевірку ГІПу", assignee: "ok", status: "live", due: "30 трав" },
    ],
    subs: [
      { t: "Збір навантажень", done: true, h: "0.5 год" },
      { t: "Розрахункова модель ЛІРА", done: true, h: "1.0 год" },
      { t: "Армування основних балок", done: false, h: "0.5 год" },
      { t: "Перевірка прогинів", done: false, h: "0.5 год" },
    ]
  },
  { id: "t2", title: "Узгодження фасадних рішень з замовником", obj: "ukp-47", stage: "project", assignee: "tm", section: "АР", day: "26", time: "14:00 — 15:30", est: "1.5 год", status: "live",
    subs: [
      { t: "Підготувати 3 варіанти", done: true, h: "" },
      { t: "Zoom-зустріч з ТОВ", done: false, h: "1.0 год" },
      { t: "Зафіксувати в протоколі", done: false, h: "0.5 год" },
    ]
  },
  { id: "t4", title: "Перевірити креслення розділу ОВ", obj: "ukp-47", stage: "project", assignee: "ok", section: "ОВ", day: "26", time: "09:00 — 10:00", est: "1 год", status: "todo", subs: [] },
  { id: "t8", title: "Зустріч з командою — планерка", obj: "ukp-47", stage: "project", assignee: "ok", section: "ГІП", day: "26", time: "08:00 — 08:30", est: "0.5 год", status: "done", subs: [] },
  { id: "t9", title: "Видача КЖ Розділу 4 (каркас)", obj: "ukp-47", stage: "project", assignee: "ap", section: "КЖ", day: "27", est: "8 год", status: "todo", subs: [] },
  { id: "t13", title: "Узгодження з ДСНС", obj: "ukp-47", stage: "project", assignee: "ok", section: "ГІП", day: "29", est: "2 год", status: "todo", subs: [] },

  // === Школа №14 (УКП-32), проєктні роботи ===
  { id: "t3", title: "Видача КЖ Розділу 3 (фундаменти)", obj: "ukp-32", stage: "project", assignee: "ap", section: "КЖ", day: "25", est: "—", status: "late",
    note: "Прострочено на 1 день. Завершити сьогодні до 18:00.",
    subs: [
      { t: "Перевірка експертом", done: true, h: "" },
      { t: "Підпис ГІП", done: false, h: "" },
      { t: "Відправка замовнику", done: false, h: "" },
    ]
  },
  { id: "t5", title: "Розрахунок вентиляції харчоблоку", obj: "ukp-32", stage: "project", assignee: "vs", section: "ОВ", day: "26", time: "11:00 — 13:00", est: "2 год", status: "todo", subs: [] },
  { id: "t10", title: "Креслення АР спортзалу", obj: "ukp-32", stage: "project", assignee: "tm", section: "АР", day: "27", est: "6 год", status: "todo", subs: [] },
  { id: "t11", title: "Розрахунок ВК (господарські стоки)", obj: "ukp-32", stage: "project", assignee: "vs", section: "ВК", day: "28", est: "3 год", status: "todo", subs: [] },
  { id: "t16", title: "Перевірка кошторису на матеріали", obj: "ukp-32", stage: "estimate", assignee: "ok", section: "ГІП", day: "28", est: "1.5 год", status: "todo", subs: [] },

  // === Соснові гаї (УКП-51), проєктні роботи ===
  { id: "t7", title: "Підготувати ТЗ електропостачання", obj: "ukp-51", stage: "project", assignee: "dl", section: "ЕО", day: "26", est: "1.5 год", status: "todo", subs: [] },
  { id: "t12", title: "Підготовка тендерного пакету «Соснові гаї»", obj: "ukp-51", stage: "estimate", assignee: "ok", section: "ТД", day: "28", est: "4 год", status: "todo", subs: [] },
  { id: "t14", title: "Розділ ЕО — основні споживачі", obj: "ukp-51", stage: "project", assignee: "dl", section: "ЕО", day: "29", est: "5 год", status: "todo", subs: [] },

  // === Адмінбудівля (УКП-89), авторський нагляд ===
  { id: "t6", title: "Виїзд на майданчик — авт. нагляд", obj: "ukp-89", stage: "supervision", assignee: "ok", section: "АН", day: "26", time: "15:00 — 17:30", est: "2.5 год", status: "todo",
    note: "Контрольна перевірка вузлів примикання покрівлі.",
    subs: [
      { t: "Перевірка вузлів покрівлі", done: false, h: "" },
      { t: "Журнал авт. нагляду", done: false, h: "" },
      { t: "Фотофіксація", done: false, h: "" },
    ]
  },
  { id: "t15", title: "Передача архіву по об'єкту УКП-2024-89", obj: "ukp-89", stage: "supervision", assignee: "ap", section: "АР", day: "29", est: "2 год", status: "todo", subs: [] },

  // === ПОЗА ПРОЄКТАМИ — адміністративні ===
  { id: "a1", title: "Оплата оренди офісу за травень", obj: null, stage: "admin", assignee: "ok", section: "АДМ", day: "28", est: "0.5 год", status: "todo", subs: [] },
  { id: "a2", title: "Фотосесія команди для сайту", obj: null, stage: "admin", assignee: "ok", section: "АДМ", day: "29", time: "16:00 — 18:00", est: "2 год", status: "todo",
    note: "Підтвердити локацію та фотографа.",
    subs: [
      { t: "Підтвердити фотографа", done: true, h: "" },
      { t: "Узгодити дрес-код", done: false, h: "" },
      { t: "Бронювати студію", done: false, h: "" },
    ]
  },
  { id: "a3", title: "Купити канцелярію та картриджі", obj: null, stage: "admin", assignee: "ok", section: "АДМ", day: "27", est: "0.5 год", status: "todo", subs: [] },
];

// Нормалізація задачі: гарантуємо поля subtasks (повноцінні міні-задачі)
// та checklists (групи галочок). Старий формат `subs` → перша чекліст-група.
function normalizeTask(t) {
  const out = { ...t };
  if (!Array.isArray(out.subtasks)) out.subtasks = [];
  out.subtasks = out.subtasks.map((s, i) => ({
    id: s.id || `st-${out.id}-${i}`,
    title: s.title || s.t || "",
    assignee: s.assignee || null,
    status: s.status || (s.done ? "done" : "todo"),
    due: s.due || null,
  }));
  if (!Array.isArray(out.checklists)) {
    if (Array.isArray(out.subs) && out.subs.length) {
      out.checklists = [{
        id: `cl-${out.id}`, title: "Чек-ліст",
        items: out.subs.map((s, i) => ({ id: `ci-${out.id}-${i}`, text: s.t || s.text || "", done: !!s.done, hint: s.h || "" })),
      }];
    } else {
      out.checklists = [];
    }
  }
  return out;
}
window.normalizeTask = normalizeTask;

window.DATA = { TEAM, OBJECTS, TASKS: TASKS.map(normalizeTask), STAGES, CORRECTION_STAGES };
window.getTeam = (id) => TEAM.find(t => t.id === id);
window.getObject = (id) => OBJECTS.find(o => o.id === id);
window.getStage = (id) =>
  STAGES.find(s => s.id === id) || CORRECTION_STAGES.find(s => s.id === id);

// ============ HR / SALARY ============

// Тип зайнятості
const EMPLOYMENT_LABELS = {
  staff:      { label: "Штат",          short: "Шт",  color: "blue" },
  part:       { label: "Часткова",      short: "Ч",   color: "green" },
  contractor: { label: "Субпідряд",     short: "СП",  color: "neutral" },
};
window.EMPLOYMENT_LABELS = EMPLOYMENT_LABELS;

// Модель ЗП
const SALARY_MODEL_LABELS = {
  fixed:       "Оклад",
  fixed_bonus: "Оклад + бонус",
  task_rate:   "Гонорар за задачу",
};
window.SALARY_MODEL_LABELS = SALARY_MODEL_LABELS;

// ВІДПУСТКИ та ЛІКАРНЯНІ — заявки
const VACATIONS = [
  { id: "v1", who: "ap", type: "vacation", from: "2026-06-15", to: "2026-06-26", days: 10, status: "approved",  note: "Літня відпустка" },
  { id: "v2", who: "vs", type: "vacation", from: "2026-07-06", to: "2026-07-17", days: 10, status: "pending",   note: "Очікує підтвердження" },
  { id: "v3", who: "tm", type: "sick",     from: "2026-05-12", to: "2026-05-14", days: 3,  status: "approved",  note: "Лікарняний (відкритий)" },
  { id: "v4", who: "ap", type: "vacation", from: "2026-12-23", to: "2026-12-31", days: 6,  status: "pending",   note: "Новорічні свята" },
  { id: "v5", who: "ok", type: "vacation", from: "2026-08-10", to: "2026-08-23", days: 12, status: "approved",  note: "Серпнева відпустка" },
];

// ДОКУМЕНТИ — кадрові накази, договори
const DOCUMENTS = [
  { id: "d1", title: "Наказ №47-К про преміювання за травень", type: "order",    date: "2026-05-31", status: "draft",     who: ["ok","ap","tm","vs"], note: "У підготовці" },
  { id: "d2", title: "Договір №ЦПХ-15 з Левченко Д.",          type: "contract", date: "2024-03-01", status: "signed",    who: ["dl"],                note: "Діючий" },
  { id: "d3", title: "Заява на відпустку (Петренко)",          type: "request",  date: "2026-05-18", status: "approved",  who: ["ap"],                note: "Підписано" },
  { id: "d4", title: "Заява на відпустку (Савчук)",            type: "request",  date: "2026-05-24", status: "pending",   who: ["vs"],                note: "Очікує підпису ГІП" },
  { id: "d5", title: "Трудовий договір (Мельник)",             type: "contract", date: "2021-02-10", status: "signed",    who: ["tm"],                note: "Діючий" },
  { id: "d6", title: "Наказ №46-К про прийняття",              type: "order",    date: "2024-03-01", status: "signed",    who: ["dl"],                note: "Архів" },
];
const DOC_TYPE_LABELS = {
  order:    "Наказ",
  contract: "Договір",
  request:  "Заява",
};
const DOC_STATUS_LABELS = {
  draft:    { label: "Чернетка",         tone: "neutral" },
  pending:  { label: "На підписанні",    tone: "warn" },
  signed:   { label: "Підписано",        tone: "live" },
  approved: { label: "Затверджено",      tone: "live" },
};
window.DOC_TYPE_LABELS = DOC_TYPE_LABELS;
window.DOC_STATUS_LABELS = DOC_STATUS_LABELS;

// TIMESHEET — спрощено: відпрацьовані години на тиждень
// Тижні останніх 4 + поточний, по людях
const TIMESHEET = {
  // тиждень: { who: години_по_днях [пн, вт, ср, чт, пт] }
  "2026-W22": {
    ok: { hours: [8, 7.5, 0, 0, 0],  planned: 40, note: "Сьогодні вівторок, день у процесі" },
    ap: { hours: [8, 8,   0, 0, 0],  planned: 40 },
    tm: { hours: [0, 8,   0, 0, 0],  planned: 40, note: "Понеділок — лікарняний" },
    vs: { hours: [4, 4,   0, 0, 0],  planned: 20 }, // пів-ставки
    dl: { hours: [0, 2,   0, 0, 0],  planned: 0, note: "По задачам — без планового часу" },
  },
  "2026-W21": {
    ok: { hours: [9, 8, 8.5, 8, 7],   planned: 40 },
    ap: { hours: [8, 8, 8, 8, 8],     planned: 40 },
    tm: { hours: [8, 8, 0, 0, 0],     planned: 40, note: "Лікарняний з середи" },
    vs: { hours: [4, 4, 4, 4, 4],     planned: 20 },
    dl: { hours: [0, 0, 6, 0, 4],     planned: 0 },
  },
  "2026-W20": {
    ok: { hours: [8, 8, 9, 8, 8],     planned: 40 },
    ap: { hours: [8, 9, 8, 8, 7],     planned: 40 },
    tm: { hours: [8, 8, 8, 8, 8],     planned: 40 },
    vs: { hours: [4, 4, 4, 4, 4],     planned: 20 },
    dl: { hours: [4, 0, 4, 0, 0],     planned: 0 },
  },
};
const CURRENT_WEEK = "2026-W22";
window.CURRENT_WEEK = CURRENT_WEEK;
window.TIMESHEET = TIMESHEET;

// ЗАРПЛАТА — за квітень (попередній закритий період)
// Кожна позиція збирається з: оклад × коефіцієнт + бонуси − утримання
function computePayroll(personId, periodHours, periodPlan) {
  const p = TEAM.find(t => t.id === personId);
  if (!p) return null;
  let base = 0, bonus = 0, notes = [];
  if (p.salaryModel === "fixed" || p.salaryModel === "fixed_bonus") {
    const part = p.partRate || 1;
    base = p.baseSalary * part;
    if (periodPlan && periodHours && periodPlan > 0 && periodHours < periodPlan) {
      // якщо відпрацював менше плану — пропорційно
      const ratio = periodHours / periodPlan;
      base = base * ratio;
      notes.push(`Відпрацьовано ${periodHours}/${periodPlan} год — оклад × ${ratio.toFixed(2)}`);
    }
  }
  if (p.salaryModel === "fixed_bonus") {
    // бонус: % від обсягу зданих за період договорів
    // тут спрощено — фіксована сума для прикладу
    bonus = Math.round(p.baseSalary * (p.bonusRate || 0));
  }
  if (p.salaryModel === "task_rate") {
    base = Math.round((periodHours || 0) * p.taskRate);
    notes.push(`${periodHours || 0} год × ${p.taskRate} ₴`);
  }
  // Окрема погодинна посада: реєстратор ЄДЕССБ (внутрішнє суміщення)
  let edessb = 0;
  const er = p.edessbRegistrar;
  if (er && er.rate && er.hours) {
    edessb = Math.round(er.hours * er.rate);
    notes.push(`ЄДЕССБ: ${er.hours} год × ${er.rate} ₴ = ${edessb} ₴`);
  }
  // утримання (для штатних)
  let pdfo = 0, vz = 0;
  const gross = base + bonus + edessb;
  if (p.employment !== "contractor") {
    // Ставки — єдине джерело істини: window.TAX_RATES (profit-report.jsx)
    const R = window.TAX_RATES || { pdfo: 0.18, vz: 0.05 };
    pdfo = Math.round(gross * R.pdfo); // ПДФО 18%
    vz   = Math.round(gross * R.vz);   // військовий збір 5%
  }
  const net = gross - pdfo - vz;
  return { person: p, base, bonus, edessb, gross, pdfo, vz, net, notes };
}
window.computePayroll = computePayroll;

// ============ ТАБЕЛЬ ОБЛІКУ РОБОЧОГО ЧАСУ (форма П-5) ============
// Коди обліку використання робочого часу. «Р» зберігається як число годин,
// решта — буквені позначення особливих днів/неявок (за типовою формою П-5).
const TABEL_CODES = {
  Р:  { label: "Відпрацьовано",   full: "Робочі години",               tone: "work",    paid: true,  work: true  },
  ВД: { label: "Вихідний",        full: "Вихідний / святковий день",   tone: "off",     paid: false, work: false },
  В:  { label: "Відпустка",       full: "Щорічна основна відпустка",   tone: "blue",    paid: true,  work: false },
  ТН: { label: "Лікарняний",      full: "Тимчасова непрацездатність",  tone: "amber",   paid: true,  work: false },
  ВП: { label: "Відрядження",     full: "Службове відрядження",        tone: "live",    paid: true,  work: true  },
  НА: { label: "За свій рахунок", full: "Відпустка без збереження ЗП", tone: "neutral", paid: false, work: false },
  ІН: { label: "Інші неявки",     full: "Інші невідпрацьовані причини",tone: "late",    paid: false, work: false },
};
window.TABEL_CODES = TABEL_CODES;

// Святкові/неробочі дні (виробничий календар) — для розрахунку норми годин
const TABEL_HOLIDAYS = {
  "2026-05": [1, 8],   // 1 травня (День праці), 8 травня (День памʼяті та перемоги)
  "2026-04": [],
};

// Метадані місяця: масив днів з ознаками вихідний/свято/робочий
function monthMeta(monthId) {
  const [y, m] = monthId.split("-").map(Number);
  const total = new Date(y, m, 0).getDate();
  const hol = TABEL_HOLIDAYS[monthId] || [];
  const DOW = ["Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"];
  const out = [];
  for (let d = 1; d <= total; d++) {
    const dow = new Date(y, m - 1, d).getDay();
    const weekend = dow === 0 || dow === 6;
    const holiday = hol.includes(d);
    out.push({ day: d, dow, dowName: DOW[dow], weekend, holiday, working: !weekend && !holiday });
  }
  return out;
}
window.monthMeta = monthMeta;

// Норма годин на робочий день для людини (часткова ставка → пропорційно)
function personDayHours(person) {
  return 8 * (person && person.partRate ? person.partRate : 1);
}

// Генератор сидів табеля: заповнює робочі дні базою з урахуванням
// правок (overrides — буквений код) та погодинних значень (byDay).
function seedTabel(monthId, rules) {
  const meta = monthMeta(monthId);
  const entries = {};
  for (const pid in rules) {
    const r = rules[pid];
    const e = {};
    meta.forEach(dm => {
      if (!dm.working) return;
      if (r.overrides && r.overrides[dm.day] != null) { e[dm.day] = r.overrides[dm.day]; return; }
      const v = (r.byDay && r.byDay[dm.day] != null) ? r.byDay[dm.day]
              : (r.base != null ? r.base : 0);
      if (v !== 0) e[dm.day] = v;
    });
    entries[pid] = e;
  }
  return entries;
}

// Сидові дані. Травень — поточний (на затвердженні/до закриття);
// квітень — закритий, його підсумки збігаються з виплаченою ЗП.
const TABEL = {
  "2026-05": {
    status: "submitted", closedAt: null,
    entries: seedTabel("2026-05", {
      ok: { base: 8, byDay: { 18: 9, 26: 9 } },                       // 154 год (2 дні понаднормово)
      ap: { base: 8 },                                                 // 152 год
      tm: { base: 8, overrides: { 12: "ТН", 13: "ТН", 14: "ТН" } },    // 128 год + 3 дні лікарняного
      vs: { base: 4 },                                                 // 76 год (0,5 ставки)
      dl: { base: 0, byDay: { 5: 6, 6: 4, 12: 6, 13: 4, 19: 6, 20: 4, 27: 8 } }, // 38 год за задачами
    }),
  },
  "2026-04": {
    status: "closed", closedAt: "2026-05-04",
    entries: seedTabel("2026-04", {
      ok: { base: 8, byDay: { 6: 9, 20: 9 } },                        // 178 год
      ap: { base: 8 },                                                 // 176 год
      tm: { base: 8, overrides: { 15: "ТН" } },                       // 168 год
      vs: { base: 4 },                                                 // 88 год
      dl: { base: 0, byDay: { 7: 6, 8: 4, 14: 6, 15: 4, 21: 6, 22: 4, 28: 8, 29: 4 } }, // 42 год
    }),
  },
};
window.TABEL = TABEL;
window.TABEL_MONTHS = [
  { id: "2026-05", label: "Травень 2026" },
  { id: "2026-04", label: "Квітень 2026" },
];

// Робочий стан табеля (з правками користувача), зберігається у localStorage
const TABEL_STORE_KEY = "ukrbud_tabel_v1";
function freshTabelState() {
  const s = { __v: 1 };
  for (const mid in TABEL) s[mid] = {
    status: TABEL[mid].status, closedAt: TABEL[mid].closedAt,
    entries: JSON.parse(JSON.stringify(TABEL[mid].entries)),
  };
  return s;
}
window.__tabelState = (function () {
  try {
    const raw = localStorage.getItem(TABEL_STORE_KEY);
    if (raw) { const p = JSON.parse(raw); if (p && p.__v === 1) return p; }
  } catch (e) {}
  return freshTabelState();
})();
window.saveTabelState = function () {
  try { localStorage.setItem(TABEL_STORE_KEY, JSON.stringify(window.__tabelState)); } catch (e) {}
  window.dispatchEvent(new Event("ukrbud-tabel"));
};
window.resetTabelState = function () {
  window.__tabelState = freshTabelState();
  window.saveTabelState();
};
// Хук перерендеру при зміні табеля
window.useTabel = function () {
  const [, force] = React.useState(0);
  React.useEffect(() => {
    const h = () => force(x => x + 1);
    window.addEventListener("ukrbud-tabel", h);
    return () => window.removeEventListener("ukrbud-tabel", h);
  }, []);
  return window.__tabelState;
};

// Підсумок по людині за місяць: відпрацьовані год/дні, норма, коди неявок
window.tabelSummary = function (monthId, personId) {
  const month = window.__tabelState[monthId];
  if (!month) return null;
  const e = month.entries[personId] || {};
  const meta = monthMeta(monthId);
  const person = window.getTeam(personId);
  const dayHours = personDayHours(person);
  let workedHours = 0, workedDays = 0, normHours = 0, normDays = 0;
  const codeDays = {};
  meta.forEach(dm => {
    if (dm.working) { normDays++; normHours += dayHours; }
    const v = e[dm.day];
    if (typeof v === "number") { workedHours += v; workedDays++; }
    else if (typeof v === "string" && TABEL_CODES[v] && TABEL_CODES[v].work) { workedHours += dayHours; workedDays++; }
    else if (typeof v === "string") { codeDays[v] = (codeDays[v] || 0) + 1; }
  });
  return { workedHours, workedDays, normHours, normDays, dayHours, codeDays };
};
// Години для нарахування ЗП (по всіх людях за місяць)
window.tabelHours = function (monthId) {
  const out = {};
  const m = window.__tabelState[monthId];
  if (!m) return out;
  for (const pid in m.entries) out[pid] = window.tabelSummary(monthId, pid).workedHours;
  return out;
};
window.tabelNorm = function (monthId, personId) {
  const s = window.tabelSummary(monthId, personId);
  return s ? s.normHours : 0;
};
window.tabelStatus = function (monthId) {
  const m = window.__tabelState[monthId];
  return m ? m.status : null;
};
window.TABEL_STATUS_LABELS = {
  draft:     { label: "Чернетка",    tone: "neutral" },
  submitted: { label: "На затвердженні", tone: "amber" },
  closed:    { label: "Закрито",     tone: "live" },
};

// Розрахунковий період ЗП — травень 2026; години беруться з табеля
const PAYROLL_PERIOD = {
  id: "2026-05",
  label: "Травень 2026",
  workdays: 19,
  workhours: 152,
};
Object.defineProperty(PAYROLL_PERIOD, "hoursByPerson", {
  enumerable: true, get() { return window.tabelHours("2026-05"); },
});
Object.defineProperty(PAYROLL_PERIOD, "status", {
  enumerable: true, get() { return window.tabelStatus("2026-05") === "closed" ? "closed" : "open"; },
});
Object.defineProperty(PAYROLL_PERIOD, "closedAt", {
  enumerable: true, get() { const m = window.__tabelState["2026-05"]; return m ? m.closedAt : null; },
});
window.PAYROLL_PERIOD = PAYROLL_PERIOD;

window.DATA.VACATIONS = VACATIONS;
window.DATA.DOCUMENTS = DOCUMENTS;

// ============ ФІНАНСИ З ЗАМОВНИКАМИ ============

// КЛІЄНТИ (юр. та фіз. особи)
const CLIENTS = [
  {
    id: "c-pod", name: "ТОВ «Подільський Девелопмент»", short: "Подільський Девелопмент",
    type: "legal", edrpou: "41835672",
    address: "м. Київ, вул. Хорива 12, оф. 305",
    contact: { name: "Олександр Гриценко", role: "Директор з розвитку", phone: "+380 67 412 38 90", email: "o.grytsenko@podildev.ua" },
    rating: 5, since: "2023-01-15",
    note: "Перевірений замовник, стабільні оплати, працюємо з 2023."
  },
  {
    id: "c-osv", name: "Управління освіти м. Києва", short: "Управління освіти",
    type: "state", edrpou: "02147853",
    address: "м. Київ, вул. Богдана Хмельницького 32",
    contact: { name: "Тетяна Сергіївна Заварзіна", role: "Заст. начальника", phone: "+380 44 234 56 78", email: "uo@kyivcity.gov.ua" },
    rating: 3, since: "2024-09-01",
    note: "Бюджетний замовник. Оплати з затримками 30-90 днів — особливість тендерів."
  },
  {
    id: "c-lis", name: "ПП «Лісове»", short: "Лісове",
    type: "legal", edrpou: "39184562",
    address: "Київська обл., с. Лісники, вул. Соснова 1",
    contact: { name: "Ігор Петрук", role: "Власник", phone: "+380 50 887 23 45", email: "lisove.pp@gmail.com" },
    rating: 4, since: "2024-12-10",
    note: "Котеджне містечко, перший спільний проєкт."
  },
  {
    id: "c-mju", name: "Міністерство юстиції України", short: "Мін'юст",
    type: "state", edrpou: "00015622",
    address: "м. Київ, вул. Архітектора Городецького 13",
    contact: { name: "Наталія Корчинська", role: "Гол. спец. відділу буд.", phone: "+380 44 271 17 17", email: "natalia.k@minjust.gov.ua" },
    rating: 4, since: "2022-05-20",
    note: "Реконструкція. Працюємо третій рік."
  },
];
window.DATA.CLIENTS = CLIENTS;
window.getClient = (id) => CLIENTS.find(c => c.id === id);

// ДОГОВОРИ (прив'язані до об'єктів)
// Графік платежів: по етапах + аванс
const CONTRACTS = [
  {
    id: "k-47", number: "47/2025", date: "2025-03-12",
    client: "c-pod", obj: "ukp-47",
    total: 8400000, status: "active",
    schedule: [
      { stage: "Аванс при підписанні",       pct: 20, amount: 1680000, dueDate: "2025-03-15", paidDate: "2025-03-14", invoice: "i-47-01" },
      { stage: "Здача ескізного проєкту",    pct: 15, amount: 1260000, dueDate: "2025-06-30", paidDate: "2025-07-08", invoice: "i-47-02" },
      { stage: "Проміжна виплата — травень", pct: 15, amount: 1260000, dueDate: "2026-05-20", invoice: "i-47-03" }, // прострочено на 6 днів
      { stage: "Здача робочого проєкту",     pct: 35, amount: 2940000, dueDate: "2026-06-12", invoice: "i-47-04" }, // скоро
      { stage: "Реєстрація в ЄДЕССБ",        pct: 10, amount: 840000,  dueDate: "2026-08-30" },
      { stage: "Остаточний розрахунок",      pct: 5,  amount: 420000,  dueDate: "2026-12-31" },
    ]
  },
  {
    id: "k-32", number: "32/2025", date: "2025-09-20",
    client: "c-osv", obj: "ukp-32",
    total: 3100000, status: "active",
    schedule: [
      { stage: "Аванс після тендеру",        pct: 30, amount: 930000,  dueDate: "2025-10-15", paidDate: "2025-11-18", invoice: "i-32-01", lateDays: 34 },
      { stage: "Експертиза кошторису",       pct: 15, amount: 465000,  dueDate: "2026-04-30", invoice: "i-32-02" }, // ДУЖЕ прострочено
      { stage: "Здача робочого проєкту",     pct: 40, amount: 1240000, dueDate: "2026-06-28", invoice: "i-32-03" }, // скоро
      { stage: "Експертиза",                 pct: 10, amount: 310000,  dueDate: "2026-09-30" },
      { stage: "Остаточний",                 pct: 5,  amount: 155000,  dueDate: "2026-12-15" },
    ]
  },
  {
    id: "k-51", number: "51/2025", date: "2025-12-18",
    client: "c-lis", obj: "ukp-51",
    total: 1700000, status: "active",
    schedule: [
      { stage: "Аванс",                      pct: 25, amount: 425000,  dueDate: "2025-12-25", paidDate: "2025-12-23", invoice: "i-51-01" },
      { stage: "Ескізний проєкт",            pct: 25, amount: 425000,  dueDate: "2026-04-15", paidDate: "2026-04-18", invoice: "i-51-02" },
      { stage: "Робочий проєкт",             pct: 35, amount: 595000,  dueDate: "2026-07-15", invoice: "i-51-03" },
      { stage: "Завершальний",               pct: 15, amount: 255000,  dueDate: "2026-10-01" },
    ]
  },
  {
    id: "k-89", number: "89/2024", date: "2024-06-01",
    client: "c-mju", obj: "ukp-89",
    total: 800000, status: "supervision",
    schedule: [
      { stage: "Аванс",                      pct: 30, amount: 240000,  dueDate: "2024-06-10", paidDate: "2024-06-08", invoice: "i-89-01" },
      { stage: "Робочий проєкт",             pct: 40, amount: 320000,  dueDate: "2024-12-30", paidDate: "2025-01-22", invoice: "i-89-02", lateDays: 23 },
      { stage: "Експертиза + ЄДЕССБ",        pct: 20, amount: 160000,  dueDate: "2025-08-15", paidDate: "2025-08-30", invoice: "i-89-03", lateDays: 15 },
      { stage: "Авт. нагляд — травень",      pct: 5,  amount: 40000,   dueDate: "2026-06-05", invoice: "i-89-04" }, // скоро
      { stage: "Авт. нагляд — фінал",        pct: 5,  amount: 40000,   dueDate: "2026-12-31" },
    ]
  },
];
window.DATA.CONTRACTS = CONTRACTS;
window.getContract = (id) => CONTRACTS.find(c => c.id === id);

const CONTRACT_STATUS = {
  draft:       { label: "Чернетка",    tone: "neutral" },
  active:      { label: "Активний",    tone: "live" },
  supervision: { label: "Авт. нагляд", tone: "neutral" },
  completed:   { label: "Виконано",    tone: "live" },
  cancelled:   { label: "Розірвано",   tone: "warn" },
};
window.CONTRACT_STATUS = CONTRACT_STATUS;

// РАХУНКИ (виставлені інвойси)
function buildInvoices() {
  const today = "2026-05-26";
  const result = [];
  CONTRACTS.forEach(k => {
    k.schedule.forEach((s, idx) => {
      if (!s.invoice) return;
      // Виставлення: 14 днів до сплати (для сплачених) або 30 днів до терміну (для невиставлених)
      let issued;
      if (s.paidDate) {
        issued = offsetDate(s.paidDate, -14);
      } else {
        // виставляємо рахунок за 30 днів до dueDate, якщо це у межах сьогодні+45 днів
        const planned = offsetDate(s.dueDate, -30);
        if (planned > today) return; // ще не настав час виставляти
        issued = planned;
      }
      let status, lateDays = 0;
      if (s.paidDate) {
        status = "paid";
      } else if (s.dueDate < today) {
        status = "overdue";
        lateDays = daysBetween(s.dueDate, today);
      } else {
        status = "issued";
      }
      result.push({
        id: s.invoice,
        number: `${k.number.split('/')[0]}/${String(idx+1).padStart(2,'0')}`,
        contract: k.id, client: k.client, obj: k.obj,
        title: s.stage, amount: s.amount,
        issueDate: issued, dueDate: s.dueDate, paidDate: s.paidDate || null,
        status, lateDays
      });
    });
  });
  return result.sort((a,b) => b.issueDate.localeCompare(a.issueDate));
}
function offsetDate(iso, days) {
  const d = new Date(iso);
  d.setDate(d.getDate() + days);
  return d.toISOString().slice(0, 10);
}
function daysBetween(iso1, iso2) {
  return Math.round((new Date(iso2) - new Date(iso1)) / 86400000);
}
const INVOICES = buildInvoices();
window.DATA.INVOICES = INVOICES;
window.daysBetween = daysBetween;

// ============ ВИТРАТИ КОМПАНІЇ ============

// Категорії витрат з типом (АДМ vs ЗВВ) за замовчуванням
const EXPENSE_CATEGORIES = [
  // АДМ — адміністративні (загальні витрати утримання офісу/команди)
  { id: "rent",      label: "Оренда офісу",       type: "adm", icon: "🏢" },
  { id: "utilities", label: "Комунальні послуги", type: "adm", icon: "💡" },
  { id: "software",  label: "ПЗ (підписки)",      type: "adm", icon: "💻" },
  { id: "supplies",  label: "Споживчі матеріали", type: "adm", icon: "📎" },
  { id: "equipment", label: "Обладнання",         type: "adm", icon: "🖥" },
  { id: "comms",     label: "Зв'язок, інтернет",  type: "adm", icon: "📡" },
  { id: "marketing", label: "Реклама / маркетинг",type: "adm", icon: "📢" },
  { id: "bank",      label: "Банківські комісії", type: "adm", icon: "🏦" },
  // ЗВВ — загальновиробничі (прямі виробничі витрати)
  { id: "subcontract", label: "Субпідряд",        type: "zvv", icon: "🤝" },
  { id: "expertise",   label: "Експертиза",       type: "zvv", icon: "📋" },
  { id: "delivery",    label: "Виїзди на об'єкт", type: "zvv", icon: "🚗" },
  { id: "fuel",        label: "Пальне / авто",    type: "zvv", icon: "⛽" },
  { id: "training",    label: "Навчання",         type: "zvv", icon: "🎓" },
];
window.EXPENSE_CATEGORIES = EXPENSE_CATEGORIES;
window.getCategory = (id) => EXPENSE_CATEGORIES.find(c => c.id === id);

const EXPENSE_TYPE_LABELS = {
  adm: { label: "Адміністративні", short: "АДМ" },
  zvv: { label: "Загальновиробничі", short: "ЗВВ" },
};
window.EXPENSE_TYPE_LABELS = EXPENSE_TYPE_LABELS;

const PERIODICITY_LABELS = {
  monthly:   "щомісячно",
  quarterly: "щокварталу",
  yearly:    "щорічно",
  once:      "разово",
};
window.PERIODICITY_LABELS = PERIODICITY_LABELS;

// ВИТРАТИ — реальні і планові
// monthlyAmount — у скільки місячно ця витрата виливається (для річних розраховано)
// vatRate — ставка вхідного ПДВ (20 / 7 / 0). 0 → постачальник не платник ПДВ або послуга звільнена.
// taxInvoice — чи зареєстрована податкова накладна (без неї вхідний ПДВ не йде в кредит).
const EXPENSES = [
  // АДМ — постійні
  { id: "e1",  cat: "rent",      title: "Оренда офісу, Хорива 12",         supplier: "ТОВ «Хорива Дев»",       amount: 28000,  monthlyAmount: 28000, periodicity: "monthly",   vatRate: 20, taxInvoice: true,  nextDate: "2026-06-01", note: "Договір №О-15/2024, до 31.12.2026" },
  { id: "e2",  cat: "utilities", title: "Комунальні (опалення, ел.енергія)", supplier: "Київенерго · Київгаз",  amount: 4200,   monthlyAmount: 4200,  periodicity: "monthly",   vatRate: 20, taxInvoice: true,  nextDate: "2026-06-10" },
  { id: "e3",  cat: "comms",     title: "Інтернет + хостинг",              supplier: "Воля Бізнес + Hetzner",  amount: 2800,   monthlyAmount: 2800,  periodicity: "monthly",   vatRate: 20, taxInvoice: true,  nextDate: "2026-06-05" },
  { id: "e4",  cat: "software",  title: "Autodesk AutoCAD + Revit (5 ліц.)", supplier: "Autodesk",             amount: 145000, monthlyAmount: 12083, periodicity: "yearly",    vatRate: 0,  taxInvoice: false, nextDate: "2026-08-15", note: "Нерезидент · послуги ІТ, вхідного ПДВ немає. 5 ліцензій × $580/міс" },
  { id: "e5",  cat: "software",  title: "ЛІРА-САПР (3 ліцензії)",          supplier: "ЛІРА",                   amount: 36000,  monthlyAmount: 3000,  periodicity: "yearly",    vatRate: 20, taxInvoice: true,  nextDate: "2026-09-01" },
  { id: "e6",  cat: "software",  title: "АВК-5 (кошторисна, 2 ліц.)",      supplier: "АВК-Прайм",              amount: 24000,  monthlyAmount: 2000,  periodicity: "yearly",    vatRate: 20, taxInvoice: true,  nextDate: "2026-10-10" },
  { id: "e7",  cat: "software",  title: "Google Workspace Business (5)",   supplier: "Google",                 amount: 750,    monthlyAmount: 750,   periodicity: "monthly",   vatRate: 0,  taxInvoice: false, nextDate: "2026-06-02", note: "Нерезидент (Google Ireland) — вхідного ПДВ немає" },
  { id: "e8",  cat: "supplies",  title: "Канцелярія, картриджі",           supplier: "Епіцентр",               amount: 1800,   monthlyAmount: 1800,  periodicity: "monthly",   vatRate: 20, taxInvoice: true,  nextDate: "2026-06-20" },
  { id: "e9",  cat: "bank",      title: "Банківські комісії (monobank)",     supplier: "monobank",             amount: 1200,   monthlyAmount: 1200,  periodicity: "monthly",   vatRate: 0,  taxInvoice: false, nextDate: "2026-05-31", note: "Банківські послуги звільнені від ПДВ" },
  { id: "e10", cat: "marketing", title: "Реклама в Google Ads + сайт",     supplier: "Google + UAhost",        amount: 5000,   monthlyAmount: 5000,  periodicity: "monthly",   vatRate: 20, taxInvoice: false, nextDate: "2026-06-15", note: "Накладна від UAhost ще не зареєстрована" },

  // АДМ — разові
  { id: "e11", cat: "equipment", title: "Принтер HP Color LaserJet",       supplier: "Rozetka",                amount: 32000,  monthlyAmount: 0,     periodicity: "once",      vatRate: 20, taxInvoice: true,  nextDate: "2026-04-12", paid: true, paidDate: "2026-04-12", note: "Списано на квітень" },

  // ЗВВ — субпідряд (прив'язано до об'єктів)
  { id: "e12", cat: "subcontract", title: "Геологія по УКП-2025-47",       supplier: "ТОВ «Інженерна геологія»", obj: "ukp-47", amount: 48000, monthlyAmount: 0, periodicity: "once", vatRate: 20, taxInvoice: true,  nextDate: "2026-04-20", paid: true, paidDate: "2026-04-25" },
  { id: "e13", cat: "expertise",   title: "Експертиза УКП-2024-89",        supplier: "Київекспертиза",         obj: "ukp-89", amount: 35000, monthlyAmount: 0, periodicity: "once", vatRate: 20, taxInvoice: true,  nextDate: "2025-08-10", paid: true, paidDate: "2025-08-15" },
  { id: "e14", cat: "subcontract", title: "Геодезичні роботи по УКП-2025-32", supplier: "ФОП Іваненко О.",     obj: "ukp-32", amount: 18000, monthlyAmount: 0, periodicity: "once", vatRate: 0,  taxInvoice: false, nextDate: "2026-06-10", note: "ФОП на єдиному податку — без ПДВ" },

  // ЗВВ — поточні
  { id: "e15", cat: "fuel",     title: "Пальне для виїздів",               supplier: "WOG",                    amount: 6000,   monthlyAmount: 6000, periodicity: "monthly", vatRate: 20, taxInvoice: true,  nextDate: "2026-06-01" },
  { id: "e16", cat: "delivery", title: "Виїзди на об'єкти (відрядження)",  supplier: "—",                      amount: 3500,   monthlyAmount: 3500, periodicity: "monthly", vatRate: 0,  taxInvoice: false, nextDate: "2026-06-01", note: "Добові та компенсації — поза об'єктом оподаткування ПДВ" },
  { id: "e17", cat: "training", title: "Курс «Реновація 2026»",            supplier: "АСЛО",                   amount: 8500,   monthlyAmount: 0,    periodicity: "once",     vatRate: 0,  taxInvoice: false, nextDate: "2026-06-25", note: "Освітні послуги звільнені від ПДВ" },
];

window.DATA.EXPENSES = EXPENSES;

// Розклад витрати на нетто / ПДВ. amount вважаємо сумою З ПДВ (брутто).
window.expenseVat = function (e) {
  const rate = e.vatRate || 0;
  const gross = e.amount || 0;
  const vatable = rate > 0;
  const net = vatable ? Math.round(gross / (1 + rate / 100)) : gross;
  const vat = gross - net;
  return { rate, vatable, hasInvoice: !!e.taxInvoice, gross, net, vat,
    // вхідний ПДВ іде в кредит лише за наявності зареєстрованої податкової накладної
    creditable: vatable && !!e.taxInvoice ? vat : 0 };
};

// Місячна сума вхідного ПДВ (run-rate; разові не входять) — для оглядів і P&L
window.expenseVatMonthly = function (e) {
  const rate = e.vatRate || 0;
  if (!rate || !(e.monthlyAmount > 0)) return 0;
  const net = Math.round(e.monthlyAmount / (1 + rate / 100));
  return { vat: e.monthlyAmount - net, creditable: e.taxInvoice ? e.monthlyAmount - net : 0 };
};

// Річний вхідний ПДВ-кредит із фактичних витрат (а не оцінка база/6):
// регулярні → monthlyAmount×12, разові цього року → повна сума. Лише з податковою накладною.
window.inputVatAnnual = function () {
  return (window.DATA.EXPENSES || []).reduce((s, e) => {
    const v = window.expenseVat(e);
    if (!v.creditable) return s;
    if (e.monthlyAmount > 0) return s + Math.round(v.creditable / e.amount * e.monthlyAmount) * 12;
    return s + v.creditable; // разові
  }, 0);
};

// ============ РОЗПОДІЛ ВИТРАТ НА ОБ'ЄКТИ ============
// Налаштування: коефіцієнти АДМ і ЗВВ як % надбавка до прямих витрат
const OVERHEAD_RATES = {
  admPct: 22,  // 22% — типово для проєктних організацій згідно настанови
  zvvPct: 15,  // 15% — додатково на ЗВВ
  profitPct: 18, // нормативний прибуток
  allocationMethod: "contract_volume", // contract_volume | hours | equal
};
window.OVERHEAD_RATES = OVERHEAD_RATES;

// Загальна сума АДМ-витрат за місяць (для розподілу)
window.totalMonthlyByType = (type) => {
  return EXPENSES
    .filter(e => window.getCategory(e.cat).type === type)
    .reduce((sum, e) => sum + (e.monthlyAmount || 0), 0);
};

// ============ РАХУНКИ І КАСА ============
const CASH_ACCOUNTS = [
  { id: "main",  label: "Основний рахунок",   bank: "monobank (бізнес)", iban: "UA29 3220 0100 0026 0031 2345 6789", balance: 1240000, currency: "UAH", primary: true },
  { id: "usd",   label: "Валютний рахунок",   bank: "monobank",       iban: "UA29 3220 0100 0026 0099 1234 5678", balance: 8400,    currency: "USD" },
  { id: "card",  label: "Корпоративна картка",bank: "monobank",   iban: "UA73 3220 0100 0000 0026 0099 5512", balance: 38000,   currency: "UAH" },
  { id: "cash",  label: "Готівка (каса)",     bank: "—",                iban: "",                                  balance: 12500,   currency: "UAH" },
];
window.DATA.CASH_ACCOUNTS = CASH_ACCOUNTS;

// Останні операції (з рахунку — приклади)
const CASH_OPS = [
  { id: "o1",  date: "2026-05-26", type: "out", category: "salary",     amount: -162524, account: "main", note: "ЗП за квітень · Кравченко О." },
  { id: "o2",  date: "2026-05-25", type: "out", category: "salary",     amount: -135000, account: "main", note: "ЗП за квітень · Петренко А." },
  { id: "o3",  date: "2026-05-24", type: "out", category: "salary",     amount: -126000, account: "main", note: "ЗП за квітень · Мельник Т." },
  { id: "o4",  date: "2026-05-22", type: "in",  category: "client_pay", amount: 425000,  account: "main", note: "Аванс ескізний · ПП Лісове · УКП-2025-51" },
  { id: "o5",  date: "2026-05-20", type: "out", category: "rent",       amount: -28000,  account: "main", note: "Оренда офісу · Хорива 12" },
  { id: "o6",  date: "2026-05-15", type: "out", category: "software",   amount: -750,    account: "card", note: "Google Workspace" },
  { id: "o7",  date: "2026-05-12", type: "in",  category: "client_pay", amount: 1680000, account: "main", note: "Аванс · ТОВ Подільський Девелопмент · УКП-2025-47" },
  { id: "o8",  date: "2026-05-10", type: "out", category: "utilities",  amount: -4200,   account: "main", note: "Комунальні послуги" },
  { id: "o9",  date: "2026-05-08", type: "out", category: "expertise",  amount: -35000,  account: "main", note: "Київекспертиза · УКП-2024-89" },
  { id: "o10", date: "2026-05-05", type: "out", category: "fuel",       amount: -6000,   account: "card", note: "Пальне для виїздів" },
];
window.DATA.CASH_OPS = CASH_OPS;

// ============ ГОТІВКОВА КАСА (неофіційний управлінський контур) ============
// ⚠ Поза офіційним обліком. Частина підрядників/замовників розраховується готівкою;
// з цих коштів виплачується готівкова частина ЗП («у конверті») та дрібні витрати.
// Доступ — лише director / accountant + перемикач видимості готівки. Фіксуємо тут,
// щоб гроші не «губилися» і було видно реальний залишок.
//   dir: "in" (надходження) | "out" (видаток)
//   kind: client | contractor | other  (in) · salary | supplier | cph | other (out)
const CASH_LEDGER = {
  opening: 35000,            // залишок готівки на 01.04.2026
  openingDate: "2026-04-01",
  ops: [
    { id: "c1",  date: "2026-04-03", dir: "in",  kind: "client",     amount: 60000, counterparty: "Замовник котеджу · Лісники", note: "Готівка за робочий проєкт (приватний)" },
    { id: "c2",  date: "2026-04-10", dir: "out", kind: "salary",     amount: 42000, counterparty: "ЗП «у конверті» · березень", note: "Петренко 20 000 · Мельник 15 000 · Савчук 7 000" },
    { id: "c3",  date: "2026-04-18", dir: "in",  kind: "contractor", amount: 25000, counterparty: "Бригада оздоблення", note: "Розрахунок за координацію" },
    { id: "c4",  date: "2026-04-22", dir: "out", kind: "supplier",   amount: 8000,  counterparty: "Матеріали (ринок)", note: "Без документів" },
    { id: "c5",  date: "2026-04-29", dir: "in",  kind: "client",     amount: 40000, counterparty: "Приватний замовник · аванс", note: "Готівковий аванс" },
    { id: "c6",  date: "2026-05-05", dir: "out", kind: "salary",     amount: 42000, counterparty: "ЗП «у конверті» · квітень", note: "Петренко 20 000 · Мельник 15 000 · Савчук 7 000" },
    { id: "c7",  date: "2026-05-12", dir: "in",  kind: "client",     amount: 55000, counterparty: "Замовник реконструкції", note: "Готівка за етап" },
    { id: "c8",  date: "2026-05-15", dir: "out", kind: "cph",        amount: 12000, counterparty: "Електрик (ЦПХ) · готівкою", note: "Поза офіційним договором ЦПХ" },
    { id: "c9",  date: "2026-05-20", dir: "in",  kind: "contractor", amount: 30000, counterparty: "Субпідрядник · повернення", note: "Залишок невикористаного авансу" },
    { id: "c10", date: "2026-05-26", dir: "out", kind: "supplier",   amount: 6000,  counterparty: "Дрібні витрати готівкою", note: "Канцелярія, дрібний інструмент" },
  ],
};
window.DATA.CASH_LEDGER = CASH_LEDGER;

// Довідники готівкової каси
window.CASH_LEDGER_KINDS = {
  client:     { label: "Готівка від замовника",   dir: "in",  icon: "💵" },
  contractor: { label: "Від субпідрядника",        dir: "in",  icon: "🤝" },
  salary:     { label: "ЗП «у конверті»",          dir: "out", icon: "✉️" },
  supplier:   { label: "Постачальнику готівкою",    dir: "out", icon: "📦" },
  cph:        { label: "Субпідряднику (ЦПХ)",       dir: "out", icon: "🔧" },
  other:      { label: "Інше",                      dir: "both", icon: "•" },
};

// Поточний залишок готівки (opening + усі операції)
window.cashLedgerBalance = function () {
  const L = window.DATA.CASH_LEDGER;
  return L.ops.reduce((s, o) => s + (o.dir === "in" ? o.amount : -o.amount), L.opening);
};

// ============ ЛІДИ (потенційні замовники) ============
// Дві колії: direct (воронка продажів, Варіант Б) і tender (дзеркало Prozorro/zakupivli.pro).
// Поле stage — ключ зі LEAD_STAGES; для тендерів додаються externalId/platform/sync/ourBid.
const LEADS = [
  // ——— ПРЯМІ ЛІДИ · Звернення → Прорахунок/КП → Перемовини → Договір ———
  { id: "L1", track: "direct", stage: "negotiation", companyId: "ubp",
    name: "Реконструкція фасаду ЖК «Подільський»", client: "ТОВ «Подільський Девелопмент»",
    contact: "Михайло Тарасенко · +380 67 555 12 34 · m.tarasenko@podilsky.ua",
    type: "private", grade: "СС2",
    estimatedValue: 340000, probability: 75, source: "Повторний замовник",
    nextAction: "Узгодити правки до договору, підписати до 12 червня",
    expectedClose: "2026-06-15", addedDate: "2026-04-22", owner: "ok",
    notes: "Повторний замовник, бюджет підтверджено. Лишилось узгодити аванс."
  },
  { id: "L2", track: "direct", stage: "estimate", companyId: "ubp",
    name: "Котедж «Сосновий», 2 будинки", client: "Іван Левчук (фіз. особа)",
    contact: "+380 50 123 99 88 · ivan.levchuk@gmail.com",
    type: "individual", grade: "СС1",
    estimatedValue: 180000, probability: 55, source: "Рекомендація",
    nextAction: "Надіслати КП за результатом виїзду на ділянку",
    expectedClose: "2026-07-01", addedDate: "2026-05-15", owner: "tm",
    notes: "Прийшов за порадою попереднього замовника. Чекає прорахунок."
  },
  { id: "L3", track: "direct", stage: "inquiry", companyId: "ubp",
    name: "Адмінбудівля, 2-й корпус", client: "Міністерство юстиції України",
    contact: "Наталія Корчинська · natalia.k@minjust.gov.ua",
    type: "state", grade: "СС2",
    estimatedValue: 260000, probability: 45, source: "Повторний замовник",
    nextAction: "Зустріч 11 червня — уточнити обсяг розділів",
    expectedClose: "2026-07-10", addedDate: "2026-05-28", owner: "ok",
    notes: "Розширення співпраці. Замовник перевірений."
  },
  { id: "L4", track: "direct", stage: "estimate", companyId: "ubp",
    name: "Розділ КМ для виробничого корпусу", client: "БК «Альтіс-Буд» (субпідряд)",
    contact: "Сергій Іваненко · +380 63 240 77 10",
    type: "private", grade: "СС2",
    estimatedValue: 210000, probability: 60, source: "Партнер (будівельна компанія)",
    nextAction: "Узгодити обсяг розділу й строки",
    expectedClose: "2026-06-25", addedDate: "2026-05-20", owner: "tm",
    notes: "Партнерська будкомпанія віддає суміжний розділ КМ на субпідряд."
  },
  { id: "L5", track: "direct", stage: "contract", companyId: "ubp",
    name: "Складський комплекс, стадія П", client: "ТОВ «Логістик Плюс»",
    contact: "Олег Гнатюк · +380 67 110 22 33",
    type: "private", grade: "СС2",
    estimatedValue: 300000, probability: 95, source: "Партнер (будівельна компанія)",
    nextAction: "Підписання договору призначено на 9 червня",
    expectedClose: "2026-06-09", addedDate: "2026-04-30", owner: "ok",
    notes: "Договір узгоджено, чекаємо підпис і аванс."
  },
  { id: "L6", track: "direct", stage: "declined", companyId: "ubp",
    name: "Інтер'єр салону краси", client: "ФОП Кравчук О.",
    contact: "+380 50 909 11 22",
    type: "individual", grade: "СС1",
    estimatedValue: 90000, probability: 0, source: "Рекомендація",
    nextAction: "—", expectedClose: "2026-05-20", addedDate: "2026-04-18", owner: "tm",
    closedDate: "2026-05-22",
    closeReason: "Обрали дешевшого виконавця (фрилансер). Бюджет не зійшовся.",
    notes: ""
  },

  // ——— ТЕНДЕРИ · дзеркало статусу Prozorro через zakupivli.pro (синхронізується) ———
  { id: "T1", track: "tender", stage: "auction", companyId: "ubp",
    name: "Реконструкція ДНЗ №47 (ПКД)", client: "Управління освіти м. Київ",
    contact: "Кабінет zakupivli.pro · UA-2026-05-21-007842",
    type: "state_tender", grade: "СС2",
    estimatedValue: 480000, probability: 40, source: "Prozorro",
    platform: "zakupivli.pro", externalId: "UA-2026-05-21-007842", synced: "2026-06-07 09:12",
    ourBid: 455000, competitorsCount: 5, priceRank: 2, awaitingReview: true,
    nextAction: "Аукціон завершено. Дешевший учасник на розгляді — чекаємо рішення замовника",
    expectedClose: "2026-06-18", addedDate: "2026-05-21", owner: "ok",
    notes: "Ми другі за ціною. Якщо першого відхилять за невідповідністю — піднімаємось на кваліфікацію."
  },
  { id: "T2", track: "tender", stage: "monitoring", companyId: "ubp",
    name: "Капремонт покрівлі ЗОШ №112", client: "Управління освіти м. Київ",
    contact: "Кабінет zakupivli.pro · UA-2026-06-02-004417",
    type: "state_tender", grade: "СС1",
    estimatedValue: 160000, probability: 20, source: "Prozorro",
    platform: "zakupivli.pro", externalId: "UA-2026-06-02-004417", synced: "2026-06-07 09:12",
    nextAction: "Оцінити доцільність участі до 10 червня",
    expectedClose: "2026-06-28", addedDate: "2026-06-02", owner: "ok",
    notes: "З'явилось у підписці майданчика. Вирішуємо, чи беремось."
  },
  { id: "T3", track: "tender", stage: "lost", companyId: "ubp",
    name: "Капремонт спортзалу школи №87", client: "Управління освіти м. Київ",
    contact: "Кабінет zakupivli.pro · UA-2026-04-15-000234",
    type: "state_tender", grade: "СС1",
    estimatedValue: 140000, probability: 0, source: "Prozorro",
    platform: "zakupivli.pro", externalId: "UA-2026-04-15-000234", synced: "2026-05-02 14:30",
    ourBid: 140000, competitorsCount: 6, priceRank: 3,
    nextAction: "—", expectedClose: "2026-05-01", addedDate: "2026-04-15", owner: "ok",
    closedDate: "2026-05-02",
    closeReason: "Переможець визначений — конкурент на 18% дешевший пройшов кваліфікацію.",
    notes: ""
  },
];
window.DATA.LEADS = LEADS;

// Колії воронки
const LEAD_TRACKS = {
  direct: { label: "Прямі ліди", sub: "Воронка продажів", icon: "handshake" },
  tender: { label: "Тендери", sub: "Prozorro · zakupivli.pro", icon: "target" },
};
window.LEAD_TRACKS = LEAD_TRACKS;

// Стадії. won — цільова (договір); terminal+lost — закрито без угоди.
const LEAD_STAGES = {
  // прямі (Варіант Б)
  inquiry:       { track: "direct", order: 1, label: "Звернення",             tone: "neutral" },
  estimate:      { track: "direct", order: 2, label: "Прорахунок / КП",       tone: "blue"    },
  negotiation:   { track: "direct", order: 3, label: "Перемовини",            tone: "amber"   },
  contract:      { track: "direct", order: 4, label: "Договір",               tone: "live", won: true },
  declined:      { track: "direct", order: 5, label: "Відмова",               tone: "warn", terminal: true, lost: true },
  // тендери (дзеркало Prozorro)
  monitoring:    { track: "tender", order: 1, label: "Моніторинг",            tone: "neutral" },
  preparing:     { track: "tender", order: 2, label: "Підготовка пропозиції", tone: "blue"    },
  auction:       { track: "tender", order: 3, label: "Участь в аукціоні",     tone: "amber"   },
  qualification: { track: "tender", order: 4, label: "Кваліфікація",          tone: "blue"    },
  awarded:       { track: "tender", order: 5, label: "Договір",               tone: "live", won: true },
  lost:          { track: "tender", order: 6, label: "Програли",              tone: "warn", terminal: true, lost: true },
  rejected:      { track: "tender", order: 7, label: "Відхилили",             tone: "warn", terminal: true, lost: true },
};
window.LEAD_STAGES = LEAD_STAGES;

window.leadStage = (l) => {
  const s = LEAD_STAGES[l.stage] || { label: l.stage, track: l.track, tone: "neutral", order: 99 };
  return { key: l.stage, ...s };
};
window.leadStagesFor = (track) =>
  Object.keys(LEAD_STAGES).filter(k => LEAD_STAGES[k].track === track)
    .map(k => ({ key: k, ...LEAD_STAGES[k] })).sort((a, b) => a.order - b.order);

// Джерела під реальність компанії (сарафан/повторні/партнери; Prozorro рідко)
const LEAD_SOURCES = ["Рекомендація", "Повторний замовник", "Партнер (будівельна компанія)", "Prozorro", "Інше"];
window.LEAD_SOURCES = LEAD_SOURCES;

const LEAD_TYPE = {
  private:      "Приватний замовник",
  state:        "Державний",
  state_tender: "Тендер",
  individual:   "Фіз. особа",
};
window.LEAD_TYPE = LEAD_TYPE;

// ============ КОМПАНІЇ (шов під майбутній другий напрям) ============
// Поки компанія одна. companyId проставляється фінансовим/об'єктним записам, щоб
// згодом (будівельний напрям з партнером) розділити контури без переробки структури.
const COMPANIES = [
  { id: "ubp", name: "ТОВ «УКРБУДПРОЄКТ»", short: "Проєктування", kind: "design",
    taxSystem: "Загальна · платник ПДВ", active: true },
  { id: "bud", name: "Будівельний напрям", short: "Будівництво", kind: "construction",
    taxSystem: "—", active: false, planned: true,
    note: "Спільний проєкт із партнером. Переважно готівка. Активуємо після оформлення юрособи.",
    // Орієнтовний штат напряму (50+ людей) — ролі на майбутнє, поки не реалізуємо
    futureRoles: [
      "Головний інженер",
      "Менеджер із закупівель (будівельні матеріали)",
      "Виконавець робіт (прораб)",
      "Інженер виробничо-технічного відділу (ВТВ)",
      "Інженер з охорони праці (+ служба ОП)",
      "Відділ кадрів (імовірно)",
    ] },
];
window.DATA.COMPANIES = COMPANIES;
window.DEFAULT_COMPANY = "ubp";
window.getCompany = (id) => COMPANIES.find(c => c.id === id) || COMPANIES[0];

// Проставляємо companyId за замовчуванням усім записам, де його ще немає (шов).
["OBJECTS", "LEADS", "CONTRACTS", "INVOICES", "EXPENSES", "CASH_ACCOUNTS", "CASH_OPS"].forEach(k => {
  (window.DATA[k] || []).forEach(r => { if (!r.companyId) r.companyId = window.DEFAULT_COMPANY; });
});

// ============ РОЛІ ТА ПРАВА ============
const ROLES = [
  { id: "director", label: "Директор", icon: "👔", color: "var(--accent)",
    description: "Повний доступ до всіх модулів і даних",
    members: ["ok"],
    permissions: { read: "*", write: "*", delete: "*", export: "*" },
  },
  { id: "accountant", label: "Бухгалтер", icon: "🧮", color: "var(--c-blue)",
    description: "Фінанси, зарплата, документообіг",
    members: [],
    permissions: {
      read: ["finance", "salary", "hr_documents", "reports"],
      write: ["finance", "salary", "hr_documents"],
      delete: ["finance"],
      export: ["finance", "salary", "reports"],
    },
  },
  { id: "gip", label: "Головний інженер проєкту", icon: "👷", color: "var(--c-green-deep)",
    description: "Управління командою, об'єктами, задачами; ЗП тільки своя",
    members: [],
    permissions: {
      read: ["work", "objects", "hr_team", "own_salary"],
      write: ["work", "objects", "hr_team"],
      delete: ["work_tasks"],
      export: ["work", "objects"],
    },
  },
  { id: "deputy", label: "Заступник директора", icon: "🎖", color: "var(--c-green-deep)",
    description: "Повноваження як у ГІП: команда, об'єкти, задачі; ЗП тільки своя",
    members: [],
    permissions: {
      read: ["work", "objects", "hr_team", "own_salary"],
      write: ["work", "objects", "hr_team"],
      delete: ["work_tasks"],
      export: ["work", "objects"],
    },
  },
  { id: "engineer", label: "Інженер / Архітектор", icon: "📐", color: "var(--c-mint)",
    description: "Свої задачі і об'єкти, обмежений доступ",
    members: ["ap", "tm", "vs"],
    permissions: {
      read: ["own_tasks", "own_objects", "own_salary"],
      write: ["own_tasks"],
      delete: [],
      export: [],
    },
  },
  { id: "contractor", label: "Субпідрядник", icon: "🤝", color: "var(--ink-3)",
    description: "Тільки свої задачі і виплати",
    members: ["dl"],
    permissions: {
      read: ["own_tasks"],
      write: ["own_tasks"],
      delete: [],
      export: [],
    },
  },
];
window.DATA.ROLES = ROLES;

// Каталог дозволів для відображення
const PERMISSIONS_CATALOG = [
  { group: "Робота", id: "work", items: [
    { id: "today", label: "Сьогодні / Тиждень" },
    { id: "objects", label: "Об'єкти" },
    { id: "admin", label: "Адміністрація" },
  ]},
  { group: "Продажі і клієнти", id: "sales", items: [
    { id: "leads", label: "Ліди і тендери" },
    { id: "clients", label: "Клієнти" },
    { id: "contracts", label: "Договори" },
  ]},
  { group: "Фінанси", id: "finance", items: [
    { id: "receivables", label: "Рахунки і дебіторка" },
    { id: "expenses", label: "Витрати" },
    { id: "budgets", label: "Бюджети об'єктів" },
    { id: "cashflow", label: "Cash flow" },
    { id: "estimates", label: "Кошториси" },
  ]},
  { group: "Персонал", id: "hr", items: [
    { id: "profiles", label: "Профілі команди" },
    { id: "time", label: "Табель" },
    { id: "vacation", label: "Відпустки" },
    { id: "documents", label: "Документи" },
    { id: "orders", label: "Накази" },
  ]},
  { group: "Зарплата", id: "salary", items: [
    { id: "payroll", label: "Розрахунок" },
    { id: "registry", label: "Реєстр виплат" },
    { id: "all_salary", label: "Зарплати всіх" },
    { id: "own_salary", label: "Тільки своя ЗП" },
  ]},
  { group: "Звіти", id: "reports", items: [
    { id: "internal_reports", label: "Внутрішні" },
  ]},
  { group: "Адмін", id: "admin", items: [
    { id: "roles", label: "Ролі і права" },
    { id: "company", label: "Налаштування компанії" },
    { id: "audit", label: "Журнал аудиту" },
  ]},
];
window.PERMISSIONS_CATALOG = PERMISSIONS_CATALOG;

// ============ ПІДРЯДНИКИ ============
const SUPPLIERS = [
  { id: "s1", name: "ТОВ «Інженерна геологія»", category: "geology",
    contact: "Сергій Бондаренко · +380 67 555 23 45 · s.bondarenko@geology.ua",
    address: "Київ, вул. Кіото 15", edrpou: "32147589",
    rating: 5, since: "2023-04-12",
    completedProjects: 8, activeProjects: 1,
    totalPaid: 384000, avgCost: 48000,
    specialties: ["Інженерно-геологічні вишукування", "Звіти ІГВ", "Гідрогеологія"],
    note: "Перевірений підрядник, працюємо з 2023. Завжди в дедлайні."
  },
  { id: "s2", name: "Київекспертиза", category: "expertise",
    contact: "Тетяна Швед · +380 44 222 11 33 · contact@kyivexp.com.ua",
    address: "Київ, бул. Шевченка 50", edrpou: "00012543",
    rating: 4, since: "2022-01-15",
    completedProjects: 12, activeProjects: 2,
    totalPaid: 540000, avgCost: 45000,
    specialties: ["Комплексна експертиза", "Експертиза кошторисної частини", "СС2 СС3"],
    note: "Бюрократія, але якість висока. Затримки на тиждень-два."
  },
  { id: "s3", name: "ФОП Іваненко О.І.", category: "geodesy",
    contact: "+380 50 887 11 22 · ivanenko.geo@gmail.com",
    address: "Київська обл., с. Чубинське", edrpou: "2987654321",
    rating: 5, since: "2024-02-20",
    completedProjects: 6, activeProjects: 1,
    totalPaid: 108000, avgCost: 18000,
    specialties: ["Геодезичні роботи", "Топозйомка", "Виставлення осей"],
    note: "Швидкий, точний, без проблем з документацією."
  },
  { id: "s4", name: "ТОВ «Архітектурні рішення»", category: "subcontractor",
    contact: "Михайло Климчук · +380 67 333 44 55 · arh.solutions@gmail.com",
    address: "Київ, вул. Драгомирова 22", edrpou: "41789632",
    rating: 4, since: "2023-09-01",
    completedProjects: 4, activeProjects: 1,
    totalPaid: 280000, avgCost: 70000,
    specialties: ["Архітектурні рішення", "АР для тендерів", "Допомога з 3D"],
    note: "Допомагають коли наші архітектори перевантажені."
  },
  { id: "s5", name: "ТОВ «ЕнергоПроект»", category: "subcontractor",
    contact: "Андрій Левчук · +380 67 666 77 88 · contact@energo-project.ua",
    address: "Київ, пр. Перемоги 95", edrpou: "39875421",
    rating: 3, since: "2024-08-10",
    completedProjects: 2, activeProjects: 0,
    totalPaid: 95000, avgCost: 47500,
    specialties: ["Розділ ЕО", "Зовнішнє електропостачання", "Системи безпеки"],
    note: "Якість середня, ціна нижче ринку. Беремо коли термінових витрат немає."
  },
  { id: "s6", name: "ФОП Левченко Д.І.", category: "individual",
    contact: "+380 67 567 89 01 · d.levchenko@ukrbudproiekt.ua",
    address: "Київ, вул. Богатирська 6", edrpou: "3456789012",
    rating: 5, since: "2024-03-01",
    completedProjects: 5, activeProjects: 1,
    totalPaid: 168000, avgCost: 33600,
    specialties: ["Електрика", "Розділ ЕО", "Слабкострумові"],
    note: "Наш постійний субпідрядник. Реєстрований у системі як ЦПХ."
  },
];
window.DATA.SUPPLIERS = SUPPLIERS;

const SUPPLIER_CATEGORIES = {
  geology:        { label: "Геологія",        icon: "🪨", color: "#8b6f47" },
  geodesy:        { label: "Геодезія",        icon: "📐", color: "#3c6287" },
  expertise:      { label: "Експертиза",      icon: "📋", color: "#a87038" },
  subcontractor:  { label: "Субпідряд",       icon: "🤝", color: "#068b49" },
  individual:     { label: "ФОП-фахівець",    icon: "👤", color: "#6b7c93" },
  edessb:         { label: "ЄДЕССБ-реєстр.",  icon: "🏛", color: "#5a8aa8" },
};
window.SUPPLIER_CATEGORIES = SUPPLIER_CATEGORIES;

// ============ KPI ТА 1:1 ============
// Кожен співробітник має KPI цілі на квартал
const KPI_GOALS = {
  ok: [
    { id: "k1", title: "Виторг компанії за Q2", target: 12000000, current: 5440000, unit: "₴", trend: "on_track" },
    { id: "k2", title: "Маржа об'єктів", target: 50, current: 59, unit: "%", trend: "exceeding" },
    { id: "k3", title: "Закрито лідів", target: 8, current: 3, unit: "", trend: "behind" },
    { id: "k4", title: "Команда без перевантажень", target: 90, current: 85, unit: "%", trend: "at_risk" },
  ],
  ap: [
    { id: "k1", title: "Видано розділів КЖ", target: 6, current: 5, unit: "", trend: "on_track" },
    { id: "k2", title: "Своєчасність здачі", target: 95, current: 87, unit: "%", trend: "at_risk" },
    { id: "k3", title: "Внутрішні зауваження", target: 5, current: 8, unit: " на проєкт", trend: "behind", inverse: true },
  ],
  tm: [
    { id: "k1", title: "Архітектурних рішень", target: 8, current: 6, unit: "", trend: "on_track" },
    { id: "k2", title: "Узгоджень із замовником", target: 100, current: 100, unit: "%", trend: "exceeding" },
  ],
  vs: [
    { id: "k1", title: "Розділів ОВ/ВК", target: 4, current: 3, unit: "", trend: "on_track" },
    { id: "k2", title: "Своєчасність", target: 90, current: 92, unit: "%", trend: "exceeding" },
  ],
  dl: [
    { id: "k1", title: "Розділів ЕО", target: 3, current: 2, unit: "", trend: "on_track" },
  ],
};
window.KPI_GOALS = KPI_GOALS;

const KPI_TREND_LABELS = {
  exceeding: { label: "Перевиконує",  tone: "live" },
  on_track:  { label: "У графіку",    tone: "live" },
  at_risk:   { label: "Ризик",        tone: "warn" },
  behind:    { label: "Відстає",      tone: "warn" },
};
window.KPI_TREND_LABELS = KPI_TREND_LABELS;

// 1:1 РОЗМОВИ
const ONE_ON_ONES = [
  { id: "o1", who: "ap", date: "2026-05-20", duration: 45, status: "done",
    topics: ["Q2 KPIs review", "Проблема зі своєчасністю", "Запит на навчання"],
    notes: "Андрій просив додатковий курс ЛІРА. План — записати на липень. Своєчасність обговорили — дамо +20% буфер у плануванні.",
    nextSteps: ["Записати на курс ЛІРА (до 15.06)", "Обговорити з командою бажане навантаження"] },
  { id: "o2", who: "tm", date: "2026-05-15", duration: 30, status: "done",
    topics: ["Завантаження", "Кар'єрні цілі"],
    notes: "Тарас задоволений, хоче більше зацікавлених архітектурних проєктів. Розглянемо передати йому ЖК «Подільський» фасадну частину повністю.",
    nextSteps: ["Передати фасадну частину УКП-47 повністю"] },
  { id: "o3", who: "vs", date: "2026-06-02", duration: 30, status: "scheduled",
    topics: ["Перехід на повну ставку?", "Q2 results"],
    notes: "",
    nextSteps: [] },
  { id: "o4", who: "ap", date: "2026-06-15", duration: 30, status: "scheduled",
    topics: ["Прогрес з KPI", "Курс ЛІРА — статус"],
    notes: "",
    nextSteps: [] },
];
window.ONE_ON_ONES = ONE_ON_ONES;

// ============ ШАБЛОНИ ЗАДАЧ ============
const TASK_TEMPLATES = [
  {
    id: "tt1", name: "Школа (новобудова, СС2)", icon: "🏫",
    category: "object_type",
    description: "Стандартний набір задач на проєктування школи з нуля",
    objectGrade: "СС2", expertiseScope: "full",
    estimatedDays: 90,
    stages: [
      { stage: "project", tasks: [
        { title: "Геодезичні вишукування", est: "5 дн", section: "ВД" },
        { title: "Геологічні вишукування", est: "10 дн", section: "ВД" },
        { title: "Архітектурне рішення (АР)", est: "20 дн", section: "АР" },
        { title: "Конструктивне рішення (КЖ)", est: "25 дн", section: "КЖ" },
        { title: "Розділ ОВ", est: "10 дн", section: "ОВ" },
        { title: "Розділ ВК", est: "8 дн", section: "ВК" },
        { title: "Розділ ЕО", est: "10 дн", section: "ЕО" },
        { title: "Слабкострумові", est: "5 дн", section: "СС" },
        { title: "Розрахунок вентиляції харчоблоку", est: "3 дн", section: "ОВ" },
        { title: "Пожежна безпека", est: "5 дн", section: "ПБ" },
      ]},
      { stage: "estimate", tasks: [
        { title: "Збір цін на матеріали", est: "5 дн", section: "ТД" },
        { title: "Кошторисна частина", est: "10 дн", section: "ТД" },
        { title: "Зведений кошторис", est: "3 дн", section: "ТД" },
      ]},
      { stage: "expertise", tasks: [
        { title: "Підготовка пакету до експертизи", est: "5 дн", section: "ГІП" },
        { title: "Перша подача експертизи", est: "0 дн", section: "ГІП" },
        { title: "Виправлення зауважень", est: "10 дн", section: "ГІП" },
        { title: "Отримання експертного висновку", est: "0 дн", section: "ГІП" },
      ]},
      { stage: "edessb", tasks: [
        { title: "Підготовка пакету до ЄДЕССБ", est: "2 дн", section: "ГІП" },
        { title: "Подача в ЄДЕССБ", est: "0 дн", section: "ГІП" },
        { title: "Отримання реєстраційної квитанції", est: "0 дн", section: "ГІП" },
      ]},
    ]
  },
  {
    id: "tt2", name: "Житловий комплекс (5-9 поверхів, СС2)", icon: "🏢",
    category: "object_type",
    description: "ЖК середньої поверховості",
    objectGrade: "СС2", expertiseScope: "full",
    estimatedDays: 120,
    stages: [
      { stage: "project", tasks: [
        { title: "Геологічні вишукування", est: "12 дн", section: "ВД" },
        { title: "Концепція ЖК", est: "10 дн", section: "АР" },
        { title: "АР всіх блоків", est: "30 дн", section: "АР" },
        { title: "КЖ всіх блоків", est: "35 дн", section: "КЖ" },
        { title: "Інженерні мережі", est: "20 дн", section: "ОВ/ВК/ЕО" },
        { title: "Благоустрій території", est: "10 дн", section: "АР" },
        { title: "Дитячі майданчики", est: "5 дн", section: "АР" },
        { title: "Паркінг", est: "10 дн", section: "АР" },
      ]},
    ]
  },
  {
    id: "tt3", name: "Котедж (СС1, прості умови)", icon: "🏡",
    category: "object_type",
    description: "Приватний будинок, спрощена процедура (тільки експертиза кошторису)",
    objectGrade: "СС1", expertiseScope: "estimate",
    estimatedDays: 45,
    stages: [
      { stage: "project", tasks: [
        { title: "Архітектурне рішення", est: "8 дн", section: "АР" },
        { title: "Конструктивні рішення", est: "10 дн", section: "КЖ" },
        { title: "Інженерні мережі (ОВ/ВК)", est: "5 дн", section: "ОВ/ВК" },
        { title: "Електрика і слаботструмові", est: "5 дн", section: "ЕО" },
      ]},
    ]
  },
  {
    id: "tt4", name: "Реконструкція адмінбудівлі", icon: "🏛",
    category: "object_type",
    description: "Реконструкція з посиленням, без зміни функції",
    objectGrade: "СС2", expertiseScope: "full",
    estimatedDays: 75,
    stages: [
      { stage: "project", tasks: [
        { title: "Технічне обстеження конструкцій", est: "15 дн", section: "ТО" },
        { title: "Архітектурно-планувальне рішення", est: "12 дн", section: "АР" },
        { title: "Конструктивне підсилення", est: "20 дн", section: "КЖ" },
        { title: "Заміна інженерних мереж", est: "15 дн", section: "ОВ/ВК/ЕО" },
      ]},
    ]
  },
  {
    id: "tt5", name: "Авторський нагляд (щомісячний)", icon: "👷",
    category: "process",
    description: "Регулярний цикл задач АН для активного будівництва",
    estimatedDays: 30,
    stages: [
      { stage: "supervision", tasks: [
        { title: "Виїзд на майданчик", est: "0.5 дн", section: "АН" },
        { title: "Перевірка вузлів виконання", est: "0.5 дн", section: "АН" },
        { title: "Журнал АН", est: "0.3 дн", section: "АН" },
        { title: "Фотофіксація", est: "0.2 дн", section: "АН" },
        { title: "Звіт замовнику", est: "0.5 дн", section: "АН" },
      ]},
    ]
  },
  {
    id: "tt6", name: "Подача в ЄДЕССБ", icon: "🏛",
    category: "process",
    description: "Підготовка і подача документації",
    estimatedDays: 7,
    stages: [
      { stage: "edessb", tasks: [
        { title: "Перевірка комплектності пакету", est: "1 дн", section: "ГІП" },
        { title: "Підпис КЕП всіх авторів", est: "1 дн", section: "ГІП" },
        { title: "Подача через кабінет ЄДЕССБ", est: "0.3 дн", section: "ГІП" },
        { title: "Сплата збору", est: "0.2 дн", section: "ГІП" },
        { title: "Отримання реєстраційного номера", est: "5 дн", section: "ГІП" },
      ]},
    ]
  },
];
window.DATA.TASK_TEMPLATES = TASK_TEMPLATES;

const TEMPLATE_CATEGORIES = {
  object_type: "Тип об'єкта",
  process:     "Процес",
};
window.TEMPLATE_CATEGORIES = TEMPLATE_CATEGORIES;

// ============ ВИПЛАТИ ПІДРЯДНИКАМ ============
const SUB_PAYMENTS = [
  { id: "sp1", supplier: "s1", obj: "ukp-47", amount: 48000,
    invoiceNumber: "ІГ-2026-104", invoiceDate: "2026-04-18", dueDate: "2026-05-02",
    paidDate: "2026-04-25", status: "paid",
    description: "Інженерно-геологічні вишукування · ЖК «Подільський»",
    contractType: "act_pdv", pdvIncluded: 8000,
  },
  { id: "sp2", supplier: "s2", obj: "ukp-89", amount: 35000,
    invoiceNumber: "КЕ-2025-2241", invoiceDate: "2025-08-05", dueDate: "2025-08-19",
    paidDate: "2025-08-30", status: "paid", lateDays: 11,
    description: "Експертний висновок · УКП-2024-89 · комплексна",
    contractType: "act_pdv", pdvIncluded: 5833,
  },
  { id: "sp3", supplier: "s6", obj: "ukp-51", amount: 33600,
    invoiceNumber: "ФОП-2026-12", invoiceDate: "2026-05-15", dueDate: "2026-05-29",
    status: "issued",
    description: "Розділ ЕО · УКП-2025-51 · «Соснові гаї»",
    contractType: "cph", pdvIncluded: 0,
  },
  { id: "sp4", supplier: "s3", obj: "ukp-32", amount: 18000,
    invoiceNumber: "ГД-2026-67", invoiceDate: "2026-05-20", dueDate: "2026-06-03",
    status: "issued",
    description: "Геодезичні роботи · Школа №14",
    contractType: "act_no_pdv", pdvIncluded: 0,
  },
  { id: "sp5", supplier: "s4", obj: "ukp-47", amount: 70000,
    invoiceNumber: "АР-2026-08", invoiceDate: "2026-05-08", dueDate: "2026-05-22",
    status: "overdue", lateDays: 4,
    description: "АР додаткові варіанти фасадів · УКП-2025-47",
    contractType: "act_pdv", pdvIncluded: 11667,
  },
  { id: "sp6", supplier: "s1", obj: "ukp-32", amount: 52000,
    invoiceNumber: "ІГ-2025-098", invoiceDate: "2025-09-10", dueDate: "2025-09-24",
    paidDate: "2025-09-20", status: "paid",
    description: "Інженерно-геологічні вишукування · Школа №14",
    contractType: "act_pdv", pdvIncluded: 8667,
  },
];
window.DATA.SUB_PAYMENTS = SUB_PAYMENTS;

const CONTRACT_TYPE_LABELS = {
  act_pdv:    { label: "Акт + ПДВ",  short: "АКТ+ПДВ" },
  act_no_pdv: { label: "Акт",        short: "АКТ" },
  cph:        { label: "ЦПХ",        short: "ЦПХ" },
};
window.CONTRACT_TYPE_LABELS = CONTRACT_TYPE_LABELS;

// ============ АВАНСОВІ ЗВІТИ ============
const ADVANCE_REPORTS = [
  { id: "a1", who: "ok", date: "2026-05-15", amount: 4200, status: "approved", approvedDate: "2026-05-16",
    title: "Відрядження в Одесу · виїзд на майданчик",
    items: [
      { what: "Квитки потяг Київ-Одеса-Київ", amount: 1800 },
      { what: "Готель «Чорне море» (2 ночі)", amount: 2000 },
      { what: "Таксі від вокзалу", amount: 400 },
    ]
  },
  { id: "a2", who: "ap", date: "2026-05-22", amount: 1450, status: "pending",
    title: "Канцелярія + картриджі для офісу",
    items: [
      { what: "Тонер HP CF410X", amount: 980 },
      { what: "Папір А4 (10 пачок)", amount: 470 },
    ]
  },
  { id: "a3", who: "tm", date: "2026-05-08", amount: 850, status: "approved", approvedDate: "2026-05-09",
    title: "Виїзд УКП-2024-89 авт.нагляд",
    items: [
      { what: "Пальне (250 км)", amount: 650 },
      { what: "Платна дорога", amount: 200 },
    ]
  },
  { id: "a4", who: "vs", date: "2026-05-25", amount: 320, status: "rejected", approvedDate: "2026-05-26",
    title: "Обід під час зустрічі з замовником",
    rejectReason: "Не передбачено політикою компанії — потрібно узгодити заздалегідь",
    items: [
      { what: "Ресторан «Веранда»", amount: 320 },
    ]
  },
  { id: "a5", who: "dl", date: "2026-04-30", amount: 1200, status: "approved", approvedDate: "2026-05-02",
    title: "Інструменти для електромонтажних розрахунків",
    items: [
      { what: "Мультиметр Fluke", amount: 1200 },
    ]
  },
];
window.DATA.ADVANCE_REPORTS = ADVANCE_REPORTS;

const ADVANCE_STATUS = {
  pending:  { label: "На розгляді", tone: "neutral" },
  approved: { label: "Затверджено", tone: "live" },
  rejected: { label: "Відхилено",   tone: "warn" },
};
window.ADVANCE_STATUS = ADVANCE_STATUS;

// ============ ВАЛЮТНІ КУРСИ ============
const FX_RATES = {
  USD: 41.50,
  EUR: 44.80,
  asOf: "2026-05-26",
  source: "НБУ",
};
window.FX_RATES = FX_RATES;

// ============ ОНБОРДИНГ ============
const ONBOARDING_STEPS = [
  { id: "ob1", category: "office",   label: "Видати ключі від офісу", required: true },
  { id: "ob2", category: "office",   label: "Виділити робоче місце", required: true },
  { id: "ob3", category: "office",   label: "Видати ноутбук + монітор", required: true },
  { id: "ob4", category: "office",   label: "Налаштувати робочу пошту", required: true },
  { id: "ob5", category: "office",   label: "Видати корпоративний телефон", required: false },

  { id: "ob6", category: "docs",     label: "Оформити трудовий договір / ЦПХ", required: true },
  { id: "ob7", category: "docs",     label: "Підписати NDA", required: true },
  { id: "ob8", category: "docs",     label: "Зібрати копії паспорта + ІПН", required: true },
  { id: "ob9", category: "docs",     label: "Інструктаж з безпеки праці", required: true },

  { id: "ob10", category: "access",  label: "Видати доступ до ERP (роль + права)", required: true },
  { id: "ob11", category: "access",  label: "Підключити до Google Workspace", required: true },
  { id: "ob12", category: "access",  label: "Додати в Telegram-чат команди", required: true },
  { id: "ob13", category: "access",  label: "Видати ліцензії: Autodesk / ЛІРА / АВК", required: true },

  { id: "ob14", category: "people",  label: "Знайомство з командою (вступна зустріч)", required: true },
  { id: "ob15", category: "people",  label: "Призначити ментора на перший місяць", required: false },
  { id: "ob16", category: "people",  label: "1:1 з ГІП — обговорити ролі та очікування", required: true },
  { id: "ob17", category: "people",  label: "Дати огляд активних об'єктів", required: true },

  { id: "ob18", category: "review",  label: "Зустріч за тиждень — як влаштувалось", required: true },
  { id: "ob19", category: "review",  label: "Перший 1:1 за місяць — оцінка прогресу", required: true },
  { id: "ob20", category: "review",  label: "Випробувальний термін — фінальна оцінка (3 міс)", required: true },
];
window.ONBOARDING_STEPS = ONBOARDING_STEPS;

const ONBOARDING_CATEGORIES = {
  office:  { label: "Офіс і обладнання", icon: "🖥" },
  docs:    { label: "Документи",          icon: "📄" },
  access:  { label: "Доступи і ліцензії", icon: "🔑" },
  people:  { label: "Команда",            icon: "👥" },
  review:  { label: "Перевірки",          icon: "✅" },
};
window.ONBOARDING_CATEGORIES = ONBOARDING_CATEGORIES;

// Активні онбординги
const ACTIVE_ONBOARDINGS = [
  { id: "on1", who: "Сергій Гончаренко", role: "Інженер-конструктор", startDate: "2026-05-19", mentor: "ap",
    progress: { ob1: true, ob2: true, ob3: true, ob4: true, ob6: true, ob7: true, ob8: true, ob9: false,
                ob10: true, ob11: true, ob12: true, ob14: true, ob15: true, ob17: false } },
  { id: "on2", who: "Марія Іщенко", role: "Архітектор (інтерн)", startDate: "2026-05-26", mentor: "tm",
    progress: { ob1: true, ob2: true, ob4: true, ob6: true, ob7: true, ob8: true } },
];
window.ACTIVE_ONBOARDINGS = ACTIVE_ONBOARDINGS;

// ============ НАВЧАННЯ ============
const TRAININGS = [
  { id: "tr1", title: "ЛІРА-САПР · поглиблений курс",      provider: "ЛІРА Education", duration: "40 год / 5 днів",
    cost: 12000, who: "ap", date: "2026-07-15", status: "scheduled",
    type: "course", note: "Запит Андрія на 1:1 від 20.05" },
  { id: "tr2", title: "ДБН 2024: Зміни і застосування",     provider: "АСЛО",           duration: "8 год / 1 день",
    cost: 2500, who: "ok", date: "2026-06-12", status: "scheduled", type: "seminar" },
  { id: "tr3", title: "BIM-моделювання Revit",              provider: "Autodesk",       duration: "60 год / 6 тижнів онлайн",
    cost: 18000, who: "tm", date: "2026-04-10", status: "in_progress", type: "online", progress: 65 },
  { id: "tr4", title: "Сертифікат відповідального виконавця", provider: "АСЛО",         duration: "—",
    cost: 4800, who: "ap", date: "2026-09-01", status: "planned", type: "certification" },
  { id: "tr5", title: "Кошторисна справа: АВК-5 для практиків", provider: "АВК-Прайм", duration: "24 год / 3 дні",
    cost: 6500, who: "ok", date: "2025-11-20", status: "completed", type: "course",
    completedDate: "2025-11-22", certificateUrl: "drive.google.com/cert" },
  { id: "tr6", title: "Прозоро для проєктантів",            provider: "Prozorro",       duration: "4 год онлайн",
    cost: 0, who: "ok", date: "2025-09-10", status: "completed", type: "seminar",
    completedDate: "2025-09-10" },
];
window.DATA.TRAININGS = TRAININGS;

const TRAINING_STATUS = {
  planned:     { label: "Заплановано",  tone: "neutral" },
  scheduled:   { label: "Підтверджено", tone: "neutral" },
  in_progress: { label: "Триває",       tone: "live"    },
  completed:   { label: "Завершено",    tone: "live"    },
  cancelled:   { label: "Скасовано",    tone: "warn"    },
};
window.TRAINING_STATUS = TRAINING_STATUS;

const TRAINING_TYPE_LABELS = {
  course:        { label: "Курс",          icon: "📚" },
  seminar:       { label: "Семінар",        icon: "🎤" },
  online:        { label: "Онлайн",         icon: "💻" },
  certification: { label: "Сертифікація",   icon: "📜" },
};
window.TRAINING_TYPE_LABELS = TRAINING_TYPE_LABELS;

// ============ АУДИТ ============
const AUDIT_LOG = [
  { id: "au1", date: "2026-05-26T14:32", who: "ok", action: "edit",   entity: "task",      entityName: "Розрахунок плит перекриття 4-9 поверхів", details: "статус: todo → live" },
  { id: "au2", date: "2026-05-26T14:18", who: "ap", action: "create", entity: "task",      entityName: "Перевірка прогинів плит",                 details: "об'єкт: УКП-2025-47, призначено АП" },
  { id: "au3", date: "2026-05-26T13:45", who: "ok", action: "approve",entity: "invoice",   entityName: "Рахунок №47/03",                          details: "виставлено замовнику на 1 260 000 ₴" },
  { id: "au4", date: "2026-05-26T11:20", who: "tm", action: "edit",   entity: "object",    entityName: "УКП-2025-32 Школа №14",                   details: "оновлено опис конструктивної частини" },
  { id: "au5", date: "2026-05-26T10:05", who: "ok", action: "login",  entity: "system",    entityName: "Вхід у систему",                          details: "IP 178.137.42.18, Chrome 138, Київ" },
  { id: "au6", date: "2026-05-25T18:42", who: "ok", action: "create", entity: "payment",   entityName: "ЗП за квітень · Петренко А.",             details: "135 000 ₴, виплачено через Приват24" },
  { id: "au7", date: "2026-05-25T16:30", who: "ok", action: "edit",   entity: "lead",      entityName: "Реконструкція ДНЗ №47",                   details: "ймовірність: 30% → 35%" },
  { id: "au8", date: "2026-05-25T14:15", who: "ap", action: "complete", entity: "task",    entityName: "Видача КЖ Розділу 3",                     details: "статус: live → late (пропустив дедлайн)" },
  { id: "au9", date: "2026-05-25T11:00", who: "ok", action: "delete", entity: "expense",   entityName: "Витрата на каву для офісу",               details: "сума: 850 ₴, причина: не передбачено політикою" },
  { id: "au10", date: "2026-05-24T17:22", who: "vs", action: "create", entity: "vacation_request", entityName: "Заявка на відпустку 6-17 липня",  details: "10 днів, очікує затвердження" },
  { id: "au11", date: "2026-05-24T15:10", who: "ok", action: "approve", entity: "advance",  entityName: "Авансовий звіт Кравченко О. · Одеса",    details: "затверджено 4 200 ₴" },
  { id: "au12", date: "2026-05-24T09:30", who: "ok", action: "edit",   entity: "settings",  entityName: "Налаштування ролей",                     details: "Інженер: додано доступ до Cash flow (read-only)" },
  { id: "au13", date: "2026-05-23T16:45", who: "tm", action: "upload", entity: "file",      entityName: "Фасади_v3.dwg",                          details: "завантажено в Drive, папка УКП-47 / 03-Проєкт" },
  { id: "au14", date: "2026-05-23T11:18", who: "ok", action: "create", entity: "contract",  entityName: "Договір №51/2025",                       details: "сума: 1 700 000 ₴, ПП «Лісове»" },
  { id: "au15", date: "2026-05-22T18:00", who: "system", action: "auto", entity: "backup", entityName: "Автоматичне резервне копіювання",        details: "успішно, розмір: 1.2 GB" },
];
window.AUDIT_LOG = AUDIT_LOG;

const AUDIT_ACTIONS = {
  create:   { label: "Створено",  color: "var(--c-green-deep)", icon: "+" },
  edit:     { label: "Редагуван.", color: "var(--c-blue)",       icon: "✎" },
  delete:   { label: "Видалено",  color: "var(--late)",         icon: "−" },
  approve:  { label: "Затверд.",  color: "var(--c-green-deep)", icon: "✓" },
  complete: { label: "Завершено", color: "var(--c-green-deep)", icon: "✓" },
  login:    { label: "Вхід",      color: "var(--ink-3)",        icon: "→" },
  upload:   { label: "Завантаж.", color: "var(--c-blue)",       icon: "↑" },
  auto:     { label: "Авто",      color: "var(--ink-3)",        icon: "⚙" },
};
window.AUDIT_ACTIONS = AUDIT_ACTIONS;

const AUDIT_ENTITY_LABELS = {
  task: "Задача", object: "Об'єкт", invoice: "Рахунок", payment: "Виплата",
  lead: "Лід", contract: "Договір", file: "Файл", expense: "Витрата",
  vacation_request: "Відпустка", advance: "Авансовий звіт", settings: "Налаштування",
  system: "Система", backup: "Резерв",
};
window.AUDIT_ENTITY_LABELS = AUDIT_ENTITY_LABELS;

// ============ СПОВІЩЕННЯ ============
const NOTIFICATIONS = [
  { id: "n1", time: "2026-05-26T14:32", kind: "late_task",   important: true,
    title: "Прострочена задача",
    text: "«Видача КЖ Розділу 3 (фундаменти)» — на 1 день після дедлайну",
    actor: "system", link: "task:t3", read: false },
  { id: "n2", time: "2026-05-26T14:00", kind: "overdue_invoice", important: true,
    title: "Прострочений рахунок",
    text: "Управління освіти м. Києва — рахунок №32/02 на 465 000 ₴, +26 днів",
    actor: "system", link: "invoice:i-32-02", read: false },
  { id: "n3", time: "2026-05-26T13:18", kind: "vacation_request", important: false,
    title: "Нова заявка на відпустку",
    text: "Вікторія Савчук подала заявку 6–17 липня",
    actor: "vs", link: "vacation:v2", read: false },
  { id: "n4", time: "2026-05-26T11:00", kind: "advance_request", important: false,
    title: "Авансовий звіт на затвердження",
    text: "Петренко А. — канцелярія + картриджі, 1 450 ₴",
    actor: "ap", link: "advance:a2", read: true },
  { id: "n5", time: "2026-05-26T09:15", kind: "incoming_payment", important: false,
    title: "Надходження коштів",
    text: "+425 000 ₴ від ПП «Лісове» · аванс ескізного проєкту",
    actor: "system", link: "invoice:i-51-02", read: true },
  { id: "n6", time: "2026-05-25T17:45", kind: "birthday", important: false,
    title: "Завтра день народження",
    text: "Тарас Мельник — 8 листопада (приклад: за тиждень нагадаємо)",
    actor: "system", link: "profile:tm", read: true },
  { id: "n7", time: "2026-05-25T16:30", kind: "lead_followup", important: false,
    title: "Час повернутися до ліда",
    text: "ТРЦ «Лук'янівська-Плаза» — 18 днів без контакту",
    actor: "system", link: "lead:L5", read: true },
  { id: "n8", time: "2026-05-25T10:00", kind: "expert_due", important: false,
    title: "Експертиза наближається",
    text: "УКП-2025-47 — здача експертизи через 17 днів, статус «у роботі»",
    actor: "system", link: "object:ukp-47", read: true },
];
window.NOTIFICATIONS = NOTIFICATIONS;

const NOTIF_KINDS = {
  late_task:        { icon: "⚠️", label: "Прострочення" },
  overdue_invoice:  { icon: "💰", label: "Дебіторка" },
  vacation_request: { icon: "🏖", label: "Відпустки" },
  advance_request:  { icon: "📋", label: "Аванс. звіти" },
  incoming_payment: { icon: "💵", label: "Надходження" },
  birthday:         { icon: "🎂", label: "Дні народження" },
  lead_followup:    { icon: "🎯", label: "Ліди" },
  expert_due:       { icon: "📅", label: "Дедлайни" },
};
window.NOTIF_KINDS = NOTIF_KINDS;

// Розподіл місячних АДМ/ЗВВ між активними об'єктами пропорційно сумі договорів
window.allocateOverheadToObjects = () => {
  const activeContracts = window.DATA.CONTRACTS.filter(c => c.status === "active" || c.status === "supervision");
  const totalContractsSum = activeContracts.reduce((s, c) => s + c.total, 0);
  const monthlyAdm = window.totalMonthlyByType("adm");
  const monthlyZvv = window.totalMonthlyByType("zvv");

  return activeContracts.map(c => {
    const share = c.total / totalContractsSum;
    return {
      contract: c,
      shareRatio: share,
      monthlyAdm: Math.round(monthlyAdm * share),
      monthlyZvv: Math.round(monthlyZvv * share),
      monthlyTotal: Math.round((monthlyAdm + monthlyZvv) * share),
    };
  });
};

// Поточна дата для UI
window.TODAY = { day: "26", dayName: "Вівторок", date: "26 травня 2026" };

window.WEEK_DAYS = [
  { day: "25", short: "ПН", name: "Понеділок", isPast: true,  isToday: false },
  { day: "26", short: "ВТ", name: "Вівторок",  isPast: false, isToday: true  },
  { day: "27", short: "СР", name: "Середа",    isPast: false, isToday: false },
  { day: "28", short: "ЧТ", name: "Четвер",    isPast: false, isToday: false },
  { day: "29", short: "ПТ", name: "П'ятниця",  isPast: false, isToday: false },
];

// Helper: яка стадія активна для об'єкта (повертає id)
window.stagesForObject = (obj) => {
  // СС1 + спрощені форми експертизи (кошторис / МОС+кошторис) — пропускаємо повний експертний цикл
  if (obj.expertiseScope === "estimate" || obj.expertiseScope === "strength") {
    return ["project", "estimate", "expertise", "edessb", "correction", "supervision"];
  }
  return ["project", "estimate", "expertise", "edessb", "correction", "supervision"];
};

// Helper: рахує задачі по етапу для об'єкта
window.stageStats = (objId, stageId, tasks) => {
  const obj = window.getObject(objId);
  const list = tasks.filter(t => t.obj === objId && t.stage === stageId);
  const done = list.filter(t => t.status === "done").length;
  const total = list.length;
  const planned = obj?.plan?.[stageId] || total;
  return { done, total, planned };
};
