"use client";

import { useEffect, useMemo, useState } from "react";
import * as XLSX from "xlsx";
import "./stage3.css";
import "./stage4.css";
import "./stage5.css";
import "./stage6.css";
import "./stage7.css";
import "./stage8.css";
import "./stage9.css";
import "./stage10.css";
import "./stage11.css";
import "./stage12.css";
import "./stage13.css";
import "./stage14.css";
import "./stage15.css";
import "./stage16.css";
import "./stage17.css";
import "./stage18.css";
import "./stage19.css";
import "./stage20.css";
import SolverLab from "./SolverLab";

type Module = "lift" | "escalator" | "pump" | "hydrant" | "plumbing" | "electrical" | "lighting" | "fireSafety" | "communications" | "genset" | "ac";
type Project = { id: string; name: string; module: Module; data: Record<string, number | string>; result: Record<string, number | string>; createdAt: string };
type ChatMessage = { role:"user"|"agent"; text:string };
type BriefQuestion={id:string;category:string;question:string;required?:boolean;options?:string[];placeholder?:string;modules?:Module[]};
type AuditItem={area:string;criterion:string;observed:string;pass:boolean;action:string};
type StandardRef={modules:Module[];code:string;title:string;role:"Wajib/utama"|"Regulasi"|"Acuan kontrak";use:string;source:string};
type RoomRecord={id:string;name:string;function:string;area:number;height:number;occupants:number;lightingWm2:number;equipmentW:number;outlets:number;targetLux:number};
type FloorRecord={id:string;name:string;elevation:number;multiplier:number;rooms:RoomRecord[]};
type TechnicalSubmodule={id:string;code:string;title:string;scope:string;deliverables:string[];step:number};
type DEDRegisterItem={id:string;category:string;deliverable:string;owner:string;status:"Belum Mulai"|"Berjalan"|"Siap Review"|"Disetujui";evidence:string};

const engineeringGates=["Design Brief dan data sumber terverifikasi","Perhitungan serta satuan telah diperiksa","Kriteria SNI/PUIL/regulasi dicatat","Koordinasi arsitektur, struktur dan disiplin lain selesai","Data/kurva vendor dan approved equal tersedia","Gambar, schedule, BOQ dan approval engineer lengkap"];
const dedFlow=["Design Brief Owner","Survei, gambar & utilitas","Room Data Sheet","Design Criteria & Code Register","Kalkulasi ruang–zona–lantai","Seleksi sistem & alternatif","Koordinasi multidisiplin","Gambar, schedule & spesifikasi","BOQ, audit & approval","Tender/IFC & commissioning"];
const chapterDEDRequirements:Record<Module,string[]>={
  lift:["Traffic study per kelompok pengguna dan peak period","Hoistway, pit, overhead dan builder-work schedule","Fire recall, emergency power, BMS dan access interface","Vendor traffic simulation dan lift schedule final"],
  escalator:["Arus dua arah dan strategi operasi","Geometri rise–angle–pit–support terkoordinasi","Brake, emergency stop dan fire interface","Escalator schedule serta technical submittal"],
  pump:["Model node–pipe dan system curve","Duty–standby–assist sequence","NPSH, surge dan transient screening","Kurva vendor, valve dan instrument schedule"],
  hydrant:["Hydraulic node calculation titik terjauh","Pressure zoning, PRV, FDC dan valve schedule","Fire-water balance dan fire-pump curve","Riser, test header dan acceptance criteria"],
  plumbing:["Fixture-unit dan water balance per ruang","Domestic water zoning dan pipe sizing","Air panas, wastewater, vent, stormwater dan sump","Riser/isometric, valve dan equipment schedule"],
  electrical:["Load schedule per panel dan lantai","SLD, panel schedule dan circuit numbering","Short circuit, selectivity dan voltage drop setiap feeder","Grounding, LPS, SPD dan cable schedule"],
  lighting:["Kriteria lux/UGR/CRI/CCT per ruang","IES/LDT dan simulasi point-by-point","Emergency lighting dan exit-sign schedule","Control zoning, LPD dan commissioning lux"],
  fireSafety:["Fire strategy dan klasifikasi risiko ruang","Loop/address, NAC dan cause-effect matrix","Sprinkler hydraulic calculation","Special suppression dan integrated testing"],
  communications:["Outlet schedule dan MDF/IDF zoning","Fiber/copper backbone serta pathway sizing","Rack elevation, PoE dan UPS autonomy","CCTV/access/PA/Wi-Fi interface dan certification test"],
  genset:["Emergency load dan step-load study","ATS/AMF, synchronizing dan load shedding","Fuel, ventilation, exhaust dan acoustic calculation","Grounding-neutral, room layout dan load-bank test"],
  ac:["Cooling load per ruang dan peak hour","Psychrometric serta ventilation balance","Duct/piping pressure loss dan equipment curve","Control diagram, BMS points dan TAB schedule"]
};
const initialDEDRegisters:DEDRegisterItem[]=[
  ["criteria","Design Criteria Register"],["utility","Utility & Existing Capacity"],["equipment","Equipment Schedule Terpadu"],["coordination","Coordination / Clash Register"],["builder","Builder’s Work Schedule"],["drawing","Drawing Register"],["specification","Technical Specification"],["boq","BOQ & Engineer’s Estimate"],["compliance","Code Compliance Matrix"],["risk","Design Risk Register"],["revision","Revision & Approval Log"],["commissioning","Testing & Commissioning Matrix"],["datasheet","Room Data Sheet"],["control","Control Narrative & Points List"],["deliverable","DED Deliverable Tracker"]
].map(([id,deliverable])=>({id,category:"Project Delivery",deliverable,owner:"MEP Engineer",status:"Belum Mulai",evidence:""}));

const newRoom=(name="Ruang Baru"):RoomRecord=>({id:`room-${Date.now()}-${Math.random()}`,name,function:"Kantor",area:48,height:3.2,occupants:6,lightingWm2:8,equipmentW:1200,outlets:4,targetLux:350});
const initialFloors:FloorRecord[]=[
  {id:"floor-ground",name:"Lantai Dasar",elevation:0,multiplier:1,rooms:[{...newRoom("Lobby"),id:"room-lobby",function:"Lobby",area:120,occupants:20,lightingWm2:10,equipmentW:1500,outlets:6,targetLux:200},{...newRoom("Ruang Kantor"),id:"room-office-g",area:240,occupants:30,equipmentW:5000,outlets:24}]},
  {id:"floor-typical",name:"Lantai Tipikal 2-8",elevation:4,multiplier:7,rooms:[{...newRoom("Open Office"),id:"room-open",area:520,occupants:65,equipmentW:13000,outlets:52},{...newRoom("Ruang Rapat"),id:"room-meeting",function:"Ruang Rapat",area:72,occupants:20,lightingWm2:9,equipmentW:2500,outlets:12,targetLux:300},{...newRoom("Toilet"),id:"room-toilet",function:"Toilet",area:36,occupants:4,lightingWm2:6,equipmentW:300,outlets:1,targetLux:150}]},
];

const modules = [
  { id: "lift" as Module, no: "01A", title: "Lift", hint: "Traffic, zoning, kabin" },
  { id: "escalator" as Module, no: "01B", title: "Eskalator", hint: "Kapasitas, daya, kontrol" },
  { id: "pump" as Module, no: "02", title: "Pompa", hint: "Head, daya, NPSH" },
  { id: "hydrant" as Module, no: "03", title: "Sistem Hidran", hint: "Debit, reservoir, pompa" },
  { id: "plumbing" as Module, no: "04", title: "Plumbing", hint: "Air bersih & distribusi" },
  { id: "electrical" as Module, no: "V", title: "Instalasi Listrik", hint: "Beban, kabel, proteksi, grounding & petir" },
  { id: "lighting" as Module, no: "VI", title: "Pencahayaan", hint: "Lux, armatur, LPD, energi & kontrol" },
  { id: "fireSafety" as Module, no: "VII", title: "Pengaman Kebakaran", hint: "Deteksi, alarm, sprinkler & suppression" },
  { id: "communications" as Module, no: "VIII", title: "Sistem Komunikasi", hint: "Telepon, PABX, interkom, HT & cabling" },
  { id: "genset" as Module, no: "06", title: "Genset", hint: "Beban darurat & kapasitas" },
  { id: "ac" as Module, no: "07", title: "Air Conditioning", hint: "Beban pendinginan" },
];

const submoduleCatalog:Partial<Record<Module,TechnicalSubmodule[]>>={
  lift:[{id:"traffic",code:"I-A",title:"Traffic & Jumlah Lift",scope:"Populasi, peak handling, interval, waiting time dan zoning.",deliverables:["Traffic calculation","Lift zoning","Unit schedule"],step:0},{id:"shaft",code:"I-B",title:"Lift, Shaft & Interface",scope:"Kabin, hoistway, pit, overhead, daya, fire recall dan aksesibilitas.",deliverables:["Lift schedule","Shaft criteria","Interface matrix"],step:3}],
  escalator:[{id:"capacity",code:"I-C",title:"Kapasitas Eskalator",scope:"Arus naik/turun, kapasitas praktis, arah operasi dan redundansi.",deliverables:["Capacity calculation","Unit schedule","Operating strategy"],step:0},{id:"coordination",code:"I-D",title:"Geometri & Koordinasi",scope:"Rise, sudut, pit, support, clear height, daya dan safety interface.",deliverables:["Escalator schedule","Builder-work criteria","Safety checklist"],step:2}],
  pump:[{id:"hydraulic",code:"II-A",title:"Hidraulik Pompa",scope:"Debit, TDH, losses, NPSH, surge dan operating point.",deliverables:["Hydraulic calculation","System curve","Duty point"],step:0},{id:"selection",code:"II-B",title:"Seleksi & Kontrol Pompa",scope:"Konfigurasi duty/standby, motor, VFD, material, seal dan kurva vendor.",deliverables:["Pump schedule","Control narrative","Vendor comparison"],step:5}],
  hydrant:[{id:"coverage",code:"III-A",title:"Coverage Hidran",scope:"Klasifikasi bahaya, titik hidran, jangkauan, zona dan FDC.",deliverables:["Coverage schedule","Zoning diagram","FDC criteria"],step:0},{id:"hydraulic",code:"III-B",title:"Hidraulik & Fire Pump",scope:"Fire flow, reservoir, TDH, jaringan, fire pump dan acceptance criteria.",deliverables:["Hydraulic basis","Reservoir sizing","Fire-pump schedule"],step:2}],
  plumbing:[{id:"demand",code:"IV-A",title:"Kebutuhan & Tampungan Air",scope:"Populasi, kebutuhan harian, peak factor, reservoir dan roof tank.",deliverables:["Water balance","Tank schedule","Demand calculation"],step:0},{id:"distribution",code:"IV-B",title:"Distribusi Air Bersih",scope:"Debit puncak, tekanan, zoning, pipa dan pompa transfer/booster.",deliverables:["Pipe sizing basis","Pump duty","Water riser basis"],step:0}],
  electrical:[{id:"power",code:"V-A",title:"Daya & Load Schedule",scope:"Connected load, demand, diversity, power factor, cadangan dan sumber normal/darurat.",deliverables:["Load schedule","Demand summary","Transformer/genset interface"],step:0},{id:"panel",code:"V-B",title:"Panel, SLD & Proteksi",scope:"MDB/SDP/DB, feeder, breaker, short circuit, selectivity dan metering.",deliverables:["Panel schedule","Single-line diagram","Protection schedule"],step:3},{id:"cable",code:"V-C",title:"Kabel & Voltage Drop",scope:"KHA terkoreksi, ukuran penghantar, instalasi, voltage drop dan short-circuit withstand.",deliverables:["Cable schedule","Voltage-drop calculation","Cable sizing sheet"],step:2},{id:"grounding",code:"V-D",title:"Grounding & Bonding",scope:"Sistem pembumian, elektroda, PE, equipotential bonding dan target tahanan.",deliverables:["Grounding calculation","Earthing layout basis","Bonding schedule"],step:4},{id:"lightning",code:"V-E",title:"Penangkal Petir & SPD",scope:"Risk screening, LPS eksternal, down conductor, earth termination dan koordinasi SPD.",deliverables:["Lightning-risk sheet","LPS criteria","SPD schedule"],step:5}],
  lighting:[{id:"photometry",code:"VI-A",title:"Lux & Fotometri",scope:"Target lux, room index, UF/MF, uniformity, glare, CRI dan CCT.",deliverables:["Lux calculation","Room lighting criteria","Photometric brief"],step:0},{id:"layout",code:"VI-B",title:"Layout & Luminaire Schedule",scope:"Jumlah, spacing, mounting, tipe armatur dan koordinasi plafon.",deliverables:["Lighting layout basis","Luminaire schedule","Mounting detail list"],step:3},{id:"control",code:"VI-C",title:"Kontrol & Energi",scope:"LPD, zoning, occupancy sensor, daylight control dan energy estimate.",deliverables:["Control zoning","LPD compliance","Energy schedule"],step:5},{id:"emergency",code:"VI-D",title:"Emergency Lighting",scope:"Jalur evakuasi, exit sign, autonomy, emergency circuit dan testing.",deliverables:["Emergency-light schedule","Exit-sign basis","Test criteria"],step:6}],
  fireSafety:[{id:"detection",code:"VII-A",title:"Deteksi Kebakaran",scope:"Jenis detector, coverage, zoning, loop dan interface.",deliverables:["Detector schedule","Zone/loop schedule","Fire alarm layout basis"],step:0},{id:"alarm",code:"VII-B",title:"Alarm, Panel & Cause–Effect",scope:"FACP, NAC, notification, battery, annunciation dan matriks kendali.",deliverables:["Panel schedule","Cause-effect matrix","Battery calculation"],step:3},{id:"sprinkler",code:"VII-C",title:"Sprinkler",scope:"Hazard class, spacing, density-area, remote area dan jaringan.",deliverables:["Sprinkler criteria","Hydraulic basis","Sprinkler schedule"],step:5},{id:"suppression",code:"VII-D",title:"Special Suppression",scope:"Kebutuhan clean agent/kitchen/ruang khusus, enclosure dan interlock.",deliverables:["Suppression brief","Room integrity criteria","Interface matrix"],step:6},{id:"water",code:"VII-E",title:"Water Supply & Fire Pump",scope:"Combined demand, storage, pump duty, zoning, controller dan testing.",deliverables:["Water-supply calculation","Fire-pump schedule","Riser basis"],step:7}],
  communications:[{id:"telephony",code:"VIII-A",title:"Telephony & PABX",scope:"Extension, traffic, trunk, numbering, licensing dan operator interface.",deliverables:["PABX schedule","Numbering plan","Trunk calculation"],step:0},{id:"cabling",code:"VIII-B",title:"Structured Cabling",scope:"Outlet, horizontal channel, backbone, pathway, labeling dan testing.",deliverables:["Outlet schedule","Cable schedule","Telecom riser"],step:3},{id:"intercom",code:"VIII-C",title:"Interkom",scope:"Master/station, duplex mode, zoning dan integrasi lift/security.",deliverables:["Intercom schedule","Point matrix","Interface list"],step:5},{id:"radio",code:"VIII-D",title:"HT / Radio",scope:"User, channel, talk group, repeater, coverage dan perizinan.",deliverables:["Radio schedule","Coverage brief","License checklist"],step:6},{id:"rack",code:"VIII-E",title:"Rack, UPS & Ruang ICT",scope:"MDF/IDF, rack U, PDU, UPS, cooling, grounding, access dan firestop.",deliverables:["Rack elevation basis","UPS calculation","ICT room criteria"],step:7}],
  genset:[{id:"capacity",code:"IX-A",title:"Kapasitas & Starting",scope:"Load schedule darurat, starting sequence, derating, redundansi dan rating.",deliverables:["Genset calculation","Emergency load schedule","Starting sequence"],step:0},{id:"fuel",code:"IX-B",title:"Fuel System",scope:"Autonomy, day tank, bulk tank, bundwall, transfer dan safety.",deliverables:["Fuel calculation","Tank schedule","Fuel schematic basis"],step:4},{id:"room",code:"IX-C",title:"Ruang, Ventilasi & Exhaust",scope:"Airflow, louver, radiator discharge, exhaust back-pressure dan akses maintenance.",deliverables:["Room criteria","Ventilation calculation","Exhaust schedule"],step:5},{id:"control",code:"IX-D",title:"ATS/AMF, Kontrol & Akustik",scope:"Transfer, synchronizing, load shedding, grounding, noise dan vibration.",deliverables:["Control narrative","Interface matrix","Acoustic criteria"],step:7}],
  ac:[{id:"load",code:"X-A",title:"Cooling Load",scope:"Selubung, solar, people, lighting, equipment, infiltration dan schedule.",deliverables:["Room load calculation","Zone load summary","Peak-load schedule"],step:0},{id:"ventilation",code:"X-B",title:"Ventilasi & Psikrometrik",scope:"Outdoor air, humidity, sensible/latent balance dan supply-air condition.",deliverables:["Ventilation calculation","Psychrometric basis","Air balance"],step:4},{id:"equipment",code:"X-C",title:"Seleksi Sistem & Peralatan",scope:"VRF/chiller/split/AHU/FCU, capacity, efficiency, redundancy dan vendor data.",deliverables:["Equipment schedule","System comparison","Selection criteria"],step:7},{id:"distribution",code:"X-D",title:"Duct, Pipa & Kontrol",scope:"Air/water distribution, pressure loss, insulation, drain, zoning, BMS dan energy control.",deliverables:["Duct/pipe sizing basis","Control narrative","Riser/schematic basis"],step:8}]
};

const standards:StandardRef[]=[
  {modules:["lift","escalator"],code:"Permenaker No. 6 Tahun 2017",title:"K3 Elevator dan Eskalator",role:"Regulasi",use:"Perencanaan, konstruksi, pemeriksaan, pengujian dan pengoperasian",source:"JDIH Kemnaker"},
  {modules:["lift"],code:"SNI EN 81-20:2020",title:"Persyaratan keselamatan lift penumpang dan barang",role:"Wajib/utama",use:"Konstruksi, ruang, pintu, kabin dan keselamatan instalasi",source:"Katalog BSN"},
  {modules:["pump"],code:"ISO 9906:2012",title:"Rotodynamic pumps — Hydraulic performance acceptance tests",role:"Acuan kontrak",use:"Duty point, toleransi performa dan acceptance test pompa",source:"ISO"},
  {modules:["hydrant"],code:"SNI 03-1745-2000",title:"Sistem pipa tegak dan slang kebakaran",role:"Wajib/utama",use:"Debit, tekanan, jaringan, outlet dan instalasi hidran",source:"Katalog BSN"},
  {modules:["hydrant"],code:"Permen PU No. 26/PRT/M/2008",title:"Persyaratan teknis sistem proteksi kebakaran bangunan dan lingkungan",role:"Regulasi",use:"Strategi dan kelengkapan proteksi kebakaran gedung",source:"JDIH PUPR"},
  {modules:["hydrant"],code:"NFPA 14 / 20 / 22 / 25",title:"Standpipe, fire pump, water tank dan inspeksi",role:"Acuan kontrak",use:"Dipakai bila disebut kontrak/AHJ; edisi harus ditetapkan dalam design criteria",source:"NFPA"},
  {modules:["plumbing"],code:"SNI 8153:2025",title:"Sistem plambing pada bangunan gedung",role:"Wajib/utama",use:"Kebutuhan air, distribusi, fixture, drainase dan vent",source:"Katalog BSN"},
  {modules:["electrical","genset"],code:"SNI 0225 seri / PUIL 2020",title:"Persyaratan Umum Instalasi Listrik",role:"Wajib/utama",use:"Proteksi, penghantar, pembumian, pemilihan dan pemasangan perlengkapan",source:"Katalog BSN"},
  {modules:["electrical"],code:"SNI 0225-4-43:2020",title:"Proteksi terhadap arus lebih",role:"Wajib/utama",use:"Koordinasi penghantar dan gawai proteksi arus lebih",source:"Katalog BSN"},
  {modules:["electrical"],code:"SNI 0225-4-44:2020",title:"Proteksi gangguan voltase dan elektromagnetik",role:"Wajib/utama",use:"Surge, gangguan voltase dan koordinasi SPD",source:"Katalog BSN"},
  {modules:["electrical"],code:"SNI 0225-5-54:2020",title:"Susunan pembumian dan konduktor proteksi",role:"Wajib/utama",use:"Elektroda, bonding, PE dan sistem pembumian",source:"Katalog BSN"},
  {modules:["electrical"],code:"SNI/IEC seri proteksi petir",title:"Sistem dan komponen proteksi petir bangunan",role:"Wajib/utama",use:"Risk assessment, LPS eksternal, bonding dan komponen",source:"Katalog BSN"},
  {modules:["lighting"],code:"SNI 6197:2020",title:"Konservasi energi pada sistem pencahayaan",role:"Wajib/utama",use:"Kriteria daya pencahayaan, efisiensi, zonasi dan kontrol energi",source:"Katalog BSN"},
  {modules:["lighting"],code:"SNI 03-6575-2001",title:"Tata cara perancangan sistem pencahayaan buatan pada bangunan gedung",role:"Wajib/utama",use:"Tingkat pencahayaan, kualitas, pemilihan dan perancangan sistem",source:"Katalog BSN"},
  {modules:["lighting"],code:"SNI 03-2396-2001",title:"Tata cara perancangan sistem pencahayaan alami pada bangunan gedung",role:"Wajib/utama",use:"Integrasi pencahayaan alami, bukaan dan pengendalian cahaya siang",source:"Katalog BSN"},
  {modules:["fireSafety"],code:"Permen PU No. 26/PRT/M/2008",title:"Persyaratan teknis sistem proteksi kebakaran pada bangunan gedung dan lingkungan",role:"Regulasi",use:"Strategi proteksi aktif/pasif, akses, sarana dan pengelolaan keselamatan kebakaran",source:"JDIH PUPR"},
  {modules:["fireSafety"],code:"SNI 03-3985-2000",title:"Tata cara perencanaan, pemasangan dan pengujian sistem deteksi dan alarm kebakaran",role:"Wajib/utama",use:"Zoning, detector, manual call point, notification, panel dan pengujian",source:"Katalog BSN"},
  {modules:["fireSafety"],code:"SNI 03-3989-2000",title:"Tata cara perencanaan dan pemasangan sistem sprinkler otomatis",role:"Wajib/utama",use:"Klasifikasi bahaya, density-area, sprinkler, jaringan dan water supply",source:"Katalog BSN"},
  {modules:["fireSafety","hydrant"],code:"SNI 03-1745-2000",title:"Tata cara perencanaan dan pemasangan sistem pipa tegak dan slang",role:"Wajib/utama",use:"Standpipe, hose connection, debit, tekanan dan water supply",source:"Katalog BSN"},
  {modules:["communications"],code:"Permenkominfo No. 5 Tahun 2021",title:"Penyelenggaraan Telekomunikasi",role:"Regulasi",use:"Penyelenggaraan jaringan, jasa, interkoneksi, penomoran dan perangkat telekomunikasi",source:"JDIH Komdigi"},
  {modules:["communications"],code:"Permenkominfo No. 12 Tahun 2018",title:"Penyelenggaraan telekomunikasi khusus untuk keperluan sendiri",role:"Regulasi",use:"Jaringan dalam gedung, sertifikasi perangkat dan penggunaan spektrum/radio",source:"JDIH Komdigi"},
  {modules:["communications"],code:"ISO/IEC 11801 seri",title:"Generic cabling for customer premises",role:"Acuan kontrak",use:"Topologi, kelas performa, channel, backbone dan administration cabling",source:"ISO/IEC"},
  {modules:["communications"],code:"ANSI/TIA-568 / 569 / 606 / 607",title:"Cabling, pathways, administration dan bonding",role:"Acuan kontrak",use:"Structured cabling, ruang/jalur, labeling, grounding dan acceptance test",source:"TIA"},
  {modules:["genset"],code:"ISO 8528 seri",title:"RIC engine driven AC generating sets",role:"Acuan kontrak",use:"Klasifikasi rating, performa, kontrol, pengujian dan keselamatan genset",source:"ISO"},
  {modules:["ac"],code:"SNI 6390:2020",title:"Konservasi energi sistem tata udara bangunan gedung",role:"Wajib/utama",use:"Efisiensi sistem, kontrol dan kinerja energi tata udara",source:"Katalog BSN"},
  {modules:["ac"],code:"ASHRAE 55 / 62.1 / 90.1",title:"Kenyamanan, ventilasi dan efisiensi energi",role:"Acuan kontrak",use:"Kriteria indoor, udara luar dan energi bila disyaratkan kontrak",source:"ASHRAE"},
];

const designNarratives:Record<Module,{purpose:string;basis:string;criteria:string;method:string;conclusion:string}>={
  lift:{purpose:"Perencanaan lift ditujukan untuk menyediakan transportasi vertikal yang aman, nyaman, dan mampu melayani arus penumpang pada periode puncak tanpa waktu tunggu berlebihan.",basis:"Analisis mengacu pada data fungsi bangunan, populasi, jumlah lantai, zoning, kapasitas kabin, kecepatan dan jumlah unit; pemeriksaan keselamatan mengikuti Permenaker No. 6 Tahun 2017 dan SNI EN 81-20:2020.",criteria:"Kapasitas angkut, interval dan waktu tunggu harus memenuhi target layanan proyek, sedangkan hoistway, pit, overhead, daya dan interface kebakaran dikonfirmasi terhadap gambar serta data vendor.",method:"Program memperkirakan round trip time, handling capacity, interval, waktu tunggu, kebutuhan unit, kebutuhan ruang dan konsumsi energi sebagai dasar seleksi awal.",conclusion:"Keluaran digunakan untuk menetapkan konfigurasi awal lift dan menjadi dasar traffic study rinci serta technical submittal pemasok."},
  escalator:{purpose:"Perencanaan eskalator ditujukan untuk melayani perpindahan penumpang antarlantai secara kontinu sesuai arus naik dan turun pada kondisi puncak.",basis:"Basis desain meliputi arus penumpang, lebar step, kecepatan, pitch, okupansi efektif, rise, sudut dan unit cadangan; aspek K3 mengikuti Permenaker No. 6 Tahun 2017.",criteria:"Jumlah unit harus memenuhi kapasitas praktis dan strategi redundansi, sementara geometri, support, clear height, brake, emergency stop dan fire interface dikonfirmasi dengan vendor.",method:"Kapasitas teoritis dikoreksi dengan faktor penggunaan praktis, kemudian dibandingkan dengan arus puncak untuk menentukan unit aktif dan cadangan.",conclusion:"Hasil menjadi basis konfigurasi arah operasi, jumlah unit, kebutuhan daya dan koordinasi ruang eskalator."},
  pump:{purpose:"Perencanaan pompa ditujukan untuk memenuhi debit dan tekanan sistem pada titik layanan terjauh dengan operasi yang stabil, efisien dan bebas kavitasi.",basis:"Data desain terdiri dari debit, head statis, tekanan layanan, panjang dan diameter pipa, losses, efisiensi serta NPSH; acceptance test dapat mengacu ISO 9906 bila ditetapkan kontrak.",criteria:"Operating point harus berada dekat daerah efisiensi terbaik, NPSHa memiliki margin terhadap NPSHr, kecepatan aliran terkendali dan pressure class aman terhadap shut-off serta surge.",method:"Total dynamic head dihitung dari head statis, beda tekanan, kerugian mayor dan minor; daya poros, daya motor, Reynolds, NPSH dan tekanan transien kemudian diperiksa.",conclusion:"Hasil dipakai untuk menyusun duty point, konfigurasi duty/standby, motor dan persyaratan kurva pompa yang diminta kepada vendor."},
  hydrant:{purpose:"Sistem hidran direncanakan untuk menyediakan debit dan tekanan pemadaman yang memadai pada kondisi kebakaran desain dan titik hidraulik terjauh.",basis:"Perhitungan memakai jumlah outlet simultan, debit per outlet, durasi, elevasi, residual pressure dan losses; acuan utama SNI 03-1745-2000 dan regulasi proteksi kebakaran PUPR.",criteria:"Reservoir harus mencukupi durasi operasi, tekanan residual terpenuhi, kecepatan pipa terkendali, serta konfigurasi main, standby dan jockey pump sesuai fire strategy dan AHJ.",method:"Program menghitung fire flow, volume reservoir, TDH, daya pompa, diameter awal, jangkauan dan kecukupan node terjauh.",conclusion:"Keluaran menjadi dasar hydraulic calculation rinci, zoning, riser diagram, pump schedule, FDC dan koordinasi dengan dinas pemadam/AHJ."},
  plumbing:{purpose:"Sistem plambing direncanakan untuk menjamin ketersediaan air bersih dan distribusi yang higienis, andal serta sesuai pola pemakaian bangunan.",basis:"Kebutuhan dihitung dari populasi, konsumsi per orang, jam operasi, faktor puncak, cadangan dan konfigurasi tangki dengan acuan SNI 8153:2025.",criteria:"Kapasitas reservoir, roof tank, pompa transfer, tekanan dan kecepatan pipa harus mencukupi kebutuhan harian serta jam puncak tanpa menimbulkan kebisingan atau water hammer.",method:"Program menghitung kebutuhan harian, debit rata-rata dan puncak, volume penyimpanan, pembagian tangki, head serta daya pompa transfer.",conclusion:"Hasil menjadi dasar water balance, sizing awal tangki/pompa dan pengembangan jaringan plambing terkoordinasi."},
  electrical:{purpose:"Sistem elektrikal direncanakan untuk menyalurkan daya secara aman, andal dan selektif kepada seluruh beban normal maupun kritis bangunan.",basis:"Basis perhitungan berasal dari load schedule, faktor kebutuhan, tegangan, fasa, faktor daya, cadangan dan panjang feeder dengan acuan PUIL 2020/SNI 0225 seri.",criteria:"Penghantar dan pengaman harus memenuhi KHA terkoreksi, jatuh tegangan, hubung singkat, selektivitas, pembumian dan kondisi pemasangan aktual.",method:"Program menjumlahkan beban, menerapkan demand dan spare capacity, lalu menghitung arus desain, rating pengaman, luas penghantar awal dan estimasi voltage drop.",conclusion:"Keluaran merupakan basis single-line diagram, panel schedule, cable schedule dan studi proteksi detail."},
  lighting:{purpose:"Sistem pencahayaan direncanakan untuk memberikan tingkat terang, kenyamanan visual, warna dan keselamatan yang sesuai fungsi ruang dengan konsumsi energi terkendali.",basis:"Basis desain meliputi geometri ruang, bidang kerja, target iluminansi, lumen armatur, faktor utilisasi, faktor pemeliharaan, reflektansi, jam operasi dan kontrol; acuan utama SNI 6197:2020, SNI 03-6575-2001 dan SNI 03-2396-2001.",criteria:"Iluminansi rata-rata dan minimum, uniformity, CRI, temperatur warna, glare, LPD, pencahayaan darurat serta integrasi daylight harus ditetapkan per fungsi ruang.",method:"Program menerapkan lumen method, menghitung room index, kebutuhan dan layout armatur, iluminansi terpasang, LPD, energi, biaya serta jumlah armatur darurat awal.",conclusion:"Keluaran menjadi basis lighting layout, luminaire schedule, control zoning, emergency lighting dan simulasi fotometrik per ruang sebelum penerbitan DED."},
  fireSafety:{purpose:"Sistem pengaman kebakaran direncanakan untuk mendeteksi kejadian sedini mungkin, memberikan peringatan, mengendalikan api dan mendukung evakuasi serta pemadaman secara andal.",basis:"Basis desain meliputi fungsi dan luas bangunan, klasifikasi bahaya, zoning, cakupan detector, kapasitas loop, notification appliance, sprinkler density-area, hose demand dan ketersediaan air; acuan nasional utama Permen PU 26/2008 serta SNI 03-3985-2000, SNI 03-3989-2000 dan SNI 03-1745-2000.",criteria:"Seluruh area harus tercakup detector yang sesuai risiko, alarm dapat didengar/dilihat, panel dan baterai memiliki kapasitas, sprinkler memenuhi density-area, serta water supply mencukupi durasi desain.",method:"Program menghitung jumlah detector/sprinkler, zona dan loop, kebutuhan NAC dan baterai, debit suppression gabungan, volume air, diameter awal serta daya pompa sebagai screening engineering.",conclusion:"Keluaran menjadi dasar cause-and-effect matrix, fire alarm layout, sprinkler hydraulic calculation, riser diagram, equipment schedule, BOQ dan pengembangan DED bersama fire engineer/AHJ."},
  communications:{purpose:"Sistem komunikasi gedung direncanakan untuk menyediakan layanan suara, data operasional, interkom dan komunikasi radio yang andal, mudah dikembangkan serta terkoordinasi dengan infrastruktur bangunan.",basis:"Basis desain meliputi jumlah pengguna, extension, tingkat panggilan simultan, trunk, outlet, lantai, jarak kabel, interkom, pengguna HT, perangkat rack, daya dan autonomy UPS; regulasi nasional dipadukan dengan standar structured cabling yang ditetapkan kontrak.",criteria:"Kapasitas PABX dan trunk harus menutup trafik serta pertumbuhan, panjang horizontal tidak melampaui batas channel, backbone memiliki cadangan, rack dan UPS mencukupi, serta perangkat radio menggunakan sertifikasi dan frekuensi yang sah.",method:"Program menghitung extension, concurrent call, SIP/PRI trunk, outlet, panjang kabel, backbone pair/core, rack space, kebutuhan UPS, interkom dan kanal HT sebagai basis desain awal.",conclusion:"Keluaran menjadi dasar telecom riser, outlet layout, rack schedule, PABX/IP-PBX schedule, numbering plan, cable schedule, BOQ, testing plan dan pengembangan DED."},
  genset:{purpose:"Genset direncanakan sebagai sumber daya darurat untuk mempertahankan pelayanan beban keselamatan dan operasional yang telah diprioritaskan owner.",basis:"Perhitungan menggunakan beban darurat, demand, cadangan, starting motor, power factor, derating, autonomy dan konsumsi BBM; PUIL berlaku dan ISO 8528 digunakan bila ditetapkan kontrak.",criteria:"Kapasitas harus mencukupi kondisi steady-state dan transien, dengan utilisasi wajar, voltage dip terkendali, fuel autonomy, ventilasi, exhaust, kebisingan dan proteksi yang memadai.",method:"Program membandingkan kebutuhan steady dan starting, menerapkan faktor derating, memilih kapasitas standar per unit serta menghitung tangki, airflow, louver, exhaust dan noise screening.",conclusion:"Hasil menjadi basis generator schedule, ATS/AMF, sequence of operation, ruang genset dan permintaan data performa integrator."},
  ac:{purpose:"Sistem tata udara direncanakan untuk mempertahankan kondisi termal, kelembapan, kualitas udara dan kenyamanan sesuai fungsi serta jadwal ruang.",basis:"Beban dihitung dari kondisi luar-dalam, selubung, kaca, radiasi matahari, penghuni, lampu, peralatan, udara luar dan infiltrasi; efisiensi mengacu SNI 6390:2020.",criteria:"Kapasitas sensibel dan laten, fresh air, zoning, konsumsi energi, kebisingan, drain kondensat dan akses pemeliharaan harus memenuhi design criteria proyek.",method:"Program menyusun heat balance, menentukan beban pendinginan desain, SHR, supply air, kapasitas TR, daya input dan sizing awal duct/pipa.",conclusion:"Keluaran menjadi basis pemilihan sistem dan kapasitas awal; finalisasi memerlukan load calculation per ruang serta data performa vendor pada kondisi desain."}
};

const defaults: Record<Module, Record<string, number | string>> = {
  lift: { floors: 30, zones: 2, floorHeight: 3.6, speed: 3, capacity: 20, units: 4, area: 1200, occupancyArea: 4, peak: 4, shaftArea: 7, cabinArea: 4.2, motorEfficiency: 82, counterweightFactor: 45, dutyFactor: 35, operatingHours: 12, operatingDays: 300, regenSaving: 25, idlePower: 1.2, energyTariff: 1700, installationCost: 6000000000, maintenanceAnnual: 180000000, analysisYears: 20, discountRate: 8, escalatorUnits: 2, escalatorSpeed: .5, escalatorRise: 5, stepPitch: .4, stepOccupancy: 1.5, escalatorEfficiency: 70 },
  escalator: { peakFlowUp:4500, peakFlowDown:2500, capacityFactor:80, reserveUnits:1, reversibleUnits:0, units:3, rise:5, angle:30, speed:.5, stepWidth:1000, stepPitch:.4, stepOccupancy:1.5, efficiency:70, demandFactor:70, operatingHours:12, operatingDays:300, standbyPower:.4, energyTariff:1700, installationCost:2500000000, maintenanceAnnual:90000000, analysisYears:20, discountRate:8 },
  pump: { flow: 45, staticHead: 28, pressureHead: 0, length: 120, diameter: 100, friction: 0.025, minorK: 8, efficiency: 72, npsha: 6.5, npshr: 3.2, density:1000, viscosity:1.004, operatingHours:10, operatingDays:300, energyTariff:1700, serviceFactor:15, dutyPumps:1, standbyPumps:1, seriesPumps:1, minimumFlow:20, rpm:1450, waveSpeed:1000, closureTime:10, designPressureMargin:10, shutoffFactor:120, runoutFactor:120, bepTolerance:10 },
  hydrant: { buildingArea:6000, floors:8, coverageRadius:30, hydrants:2, flowPerHydrant:6.3, duration:45, staticHead:24, residualPressure:45, pipeLoss:12, efficiency:70, tankReserve:15, availableTank:50, maxVelocity:3, serviceFactor:15, fdcCount:1, zones:1, jockeyPercent:1, networkLength:180, hazenC:120, selectedDiameter:100, minorLossPercent:20, hoseLength:30, nozzleThrow:5, maxTravelDistance:30, floorControlValves:1 },
  plumbing: { occupants: 450, demandPerPerson: 100, operationHours: 10, peakFactor: 2, storageHours: 8, roofTankShare: 35, pipeDiameter: 80, pumpEfficiency: 70, staticHead: 32 },
  electrical: { lightingLoad: 28, socketLoad: 42, motorLoad: 55, acLoad: 68, otherLoad: 12, demandFactor: 0.8, voltage: 400, powerFactor: 0.85, phases: 3, spare: 20, cableLength: 65, temperatureFactor:.91, groupingFactor:.8, installationFactor:.95, allowableDrop:3, faultCurrent:18, clearingTime:.2, kFactor:143, earthFaultCurrent:500, allowableTouchVoltage:50, soilResistivity:100, earthRodLength:3, earthRodDiameter:16, earthRodCount:4, buildingLength:45, buildingWidth:25, buildingHeight:30, lightningProtectionLevel:3, lightningDownConductors:4 },
  lighting: { roomLength:12, roomWidth:8, roomHeight:3.2, workPlane:0.8, targetLux:350, minimumLux:280, maximumLux:420, lumensPerFixture:3600, fixturePower:32, utilizationFactor:.65, maintenanceFactor:.8, reflectanceCeiling:70, reflectanceWall:50, reflectanceFloor:20, spacingHeightRatio:1.2, operatingHours:10, operatingDays:300, controlSaving:25, emergencyShare:10, colorTemperature:4000, cri:80, energyTariff:1700 },
  fireSafety: { floorArea:1200, floors:8, ceilingHeight:3.2, smokeCoverage:80, heatCoverage:50, flameCoverage:250, smokeShare:80, heatShare:18, flameShare:2, manualCallPointsPerFloor:2, soundersPerFloor:4, strobesPerFloor:2, floorsPerZone:1, loopCapacity:200, standbyCurrent:1.2, alarmCurrent:4.5, standbyHours:24, alarmHours:.5, batteryMargin:25, sprinklerCoverage:12, sprinklerDensity:5, designArea:140, hoseDemand:12.6, suppressionDuration:60, waterReserve:15, pipeVelocity:3, pumpEfficiency:70, serviceFactor:15, availableWater:100 },
  communications: { occupants:450, workstations:300, extensionsPerWorkstation:1, commonExtensions:30, extensionGrowth:20, concurrentCallPercent:15, trunkUtilization:80, channelsPerPri:30, floors:8, floorArea:1200, outletsPer100sqm:4, spareOutlets:20, averageHorizontalCable:45, cablePairsPerExtension:2, backboneReserve:25, intercomStations:24, intercomMasters:2, htUsers:20, htChannels:8, rackDevices:12, rackUnitsPerDevice:2, rackReserve:25, devicePower:120, upsRuntime:60, upsEfficiency:90, powerFactor:.8 },
  genset: { connectedLoad: 200.2, emergencyShare: 75, demandFactor: 0.8, future: 15, powerFactor: 0.8, largestMotor: 45, motorStartMultiplier: 3, startVoltageDip: 15, altitude: 750, ambientTemp: 32, altitudeDerate: 1, tempDerate: 0, autonomy: 8, fuelRate: 0.24, tankReserve: 15, roomLength: 8, roomWidth: 5, roomHeight: 3.5, airPerKw: 0.09, maxAirVelocity: 3, exhaustLength: 18, exhaustBackPressurePerM: 0.35, silencerLoss: 4, soundSource: 105, targetNoise: 70, distance: 7, wallAttenuation: 18, units: 1, standbyUnits: 0, operatingHours: 250, energyTariff: 1700 },
  ac: { area: 120, height: 3.2, people: 18, outdoorDb: 33, outdoorRh: 65, indoorDb: 24, indoorRh: 55, wallU: 2.1, wallArea: 150, roofU: 1.5, roofArea: 120, glassU: 5.7, windowArea: 18, solarGlass: 180, shadingFactor: .65, lighting: 12, equipment: 3500, sensiblePerson: 75, latentPerson: 55, freshAirPerson: 10, ach: .5, infiltrationAch: .25, safety: 10, supplyDeltaT: 10, ductVelocity: 6, pipeVelocity: 1.5, condensateSlope: 1, cop: 3.5, diversity: .9, operatingHours: 10, operatingDays: 300, energyTariff: 1700 },
};

const fieldSets: Record<Module, { key: string; label: string; unit: string; min?: number }[]> = {
  lift: [
    { key: "floors", label: "Jumlah lantai total", unit: "lantai", min: 2 }, { key: "zones", label: "Jumlah zona layanan", unit: "zona", min: 1 }, { key: "floorHeight", label: "Tinggi antar lantai", unit: "m" },
    { key: "speed", label: "Kecepatan rata-rata lift", unit: "m/s" }, { key: "capacity", label: "Kapasitas kabin", unit: "orang" },
    { key: "units", label: "Jumlah lift per zona", unit: "unit" }, { key: "area", label: "Luas kotor per lantai", unit: "m²" },
    { key: "occupancyArea", label: "Luas netto per orang", unit: "m²/orang" }, { key: "peak", label: "Beban puncak", unit: "%" },
    { key: "shaftArea", label: "Luas hoistway per lift", unit: "m²" }, { key: "motorEfficiency", label: "Efisiensi motor lift", unit: "%" },
    { key: "cabinArea", label: "Luas bersih kabin", unit: "m²" },
    { key: "counterweightFactor", label: "Kompensasi counterweight", unit: "%" }, { key: "dutyFactor", label: "Faktor operasi simultan", unit: "%" },
    { key: "operatingHours", label: "Jam operasi per hari", unit: "jam" }, { key: "operatingDays", label: "Hari operasi per tahun", unit: "hari" },
    { key: "regenSaving", label: "Penghematan regenerative drive", unit: "%" }, { key: "idlePower", label: "Daya standby per lift", unit: "kW" },
    { key: "energyTariff", label: "Tarif energi", unit: "Rp/kWh" }, { key: "installationCost", label: "Biaya investasi sistem", unit: "Rp" },
    { key: "maintenanceAnnual", label: "Biaya pemeliharaan tahunan", unit: "Rp/tahun" }, { key: "analysisYears", label: "Periode analisis", unit: "tahun" },
    { key: "discountRate", label: "Tingkat diskonto", unit: "%" },
    { key: "escalatorUnits", label: "Jumlah eskalator", unit: "unit" }, { key: "escalatorSpeed", label: "Kecepatan eskalator", unit: "m/s" },
    { key: "escalatorRise", label: "Ketinggian eskalator", unit: "m" }, { key: "stepPitch", label: "Pitch anak tangga", unit: "m" },
    { key: "stepOccupancy", label: "Okupansi tiap step", unit: "orang" }, { key: "escalatorEfficiency", label: "Efisiensi penggerak eskalator", unit: "%" },
  ],
  escalator: [
    {key:"peakFlowUp",label:"Arus penumpang puncak naik",unit:"orang/jam",min:0},{key:"peakFlowDown",label:"Arus penumpang puncak turun",unit:"orang/jam",min:0},
    {key:"capacityFactor",label:"Faktor kapasitas praktis",unit:"%",min:1},{key:"reserveUnits",label:"Unit cadangan",unit:"unit",min:0},
    {key:"reversibleUnits",label:"Unit reversible yang direncanakan",unit:"unit",min:0},{key:"units",label:"Jumlah unit rencana/tersedia",unit:"unit",min:1},{key:"rise",label:"Tinggi angkat vertikal",unit:"m",min:1},
    {key:"angle",label:"Sudut kemiringan",unit:"derajat"},{key:"speed",label:"Kecepatan nominal",unit:"m/s"},
    {key:"stepWidth",label:"Lebar anak tangga",unit:"mm"},{key:"stepPitch",label:"Pitch anak tangga",unit:"m"},
    {key:"stepOccupancy",label:"Okupansi efektif per step",unit:"orang"},{key:"efficiency",label:"Efisiensi penggerak",unit:"%"},
    {key:"demandFactor",label:"Faktor beban operasi",unit:"%"},{key:"operatingHours",label:"Jam operasi per hari",unit:"jam"},
    {key:"operatingDays",label:"Hari operasi per tahun",unit:"hari"},{key:"standbyPower",label:"Daya standby per unit",unit:"kW"},
    {key:"energyTariff",label:"Tarif energi",unit:"Rp/kWh"},{key:"installationCost",label:"Biaya investasi",unit:"Rp"},
    {key:"maintenanceAnnual",label:"Pemeliharaan tahunan",unit:"Rp/tahun"},{key:"analysisYears",label:"Periode analisis",unit:"tahun"},
    {key:"discountRate",label:"Tingkat diskonto",unit:"%"},
  ],
  pump: [
    { key: "flow", label: "Debit desain", unit: "m³/jam" }, { key: "staticHead", label: "Head statis", unit: "m" },
    { key: "pressureHead", label: "Beda head tekanan", unit: "m" }, { key: "length", label: "Panjang pipa ekuivalen", unit: "m" },
    { key: "diameter", label: "Diameter dalam pipa", unit: "mm" }, { key: "friction", label: "Faktor gesek Darcy", unit: "-" },
    { key: "minorK", label: "Jumlah koefisien minor (ΣK)", unit: "-" }, { key: "efficiency", label: "Efisiensi pompa", unit: "%" },
    { key: "npsha", label: "NPSH tersedia", unit: "m" }, { key: "npshr", label: "NPSH dibutuhkan pabrik", unit: "m" },
    { key: "density", label: "Massa jenis fluida", unit: "kg/m³" }, {key:"viscosity",label:"Viskositas dinamis",unit:"mPa·s"},
    {key:"operatingHours",label:"Jam operasi per hari",unit:"jam"},{key:"operatingDays",label:"Hari operasi per tahun",unit:"hari"},
    {key:"energyTariff",label:"Tarif energi",unit:"Rp/kWh"},{key:"serviceFactor",label:"Cadangan daya motor",unit:"%"},
    {key:"dutyPumps",label:"Jumlah pompa duty paralel",unit:"unit",min:1},{key:"standbyPumps",label:"Jumlah pompa standby",unit:"unit",min:0},
    {key:"seriesPumps",label:"Jumlah pompa seri/tingkat tekanan",unit:"unit",min:1},{key:"minimumFlow",label:"Minimum continuous flow",unit:"%",min:1},
    {key:"rpm",label:"Putaran motor/pompa",unit:"rpm",min:1},{key:"waveSpeed",label:"Kecepatan gelombang pipa",unit:"m/s",min:1},
    {key:"closureTime",label:"Waktu penutupan valve",unit:"detik",min:.1},{key:"designPressureMargin",label:"Margin tekanan desain",unit:"%",min:0},
    {key:"shutoffFactor",label:"Head shut-off kurva pompa",unit:"% duty head",min:101},{key:"runoutFactor",label:"Batas run-out flow",unit:"% duty flow",min:101},{key:"bepTolerance",label:"Toleransi operating point dari duty/BEP",unit:"%",min:1},
  ],
  hydrant: [
    { key: "buildingArea", label: "Luas total bangunan terlindungi", unit: "m²" }, {key:"floors",label:"Jumlah lantai dilayani",unit:"lantai",min:1},
    {key:"coverageRadius",label:"Radius jangkauan efektif",unit:"m",min:1},{ key: "hydrants", label: "Hidran beroperasi simultan", unit: "unit" }, { key: "flowPerHydrant", label: "Debit setiap hidran", unit: "L/s" },
    { key: "duration", label: "Durasi operasi minimum", unit: "menit" }, { key: "staticHead", label: "Head statis tertinggi", unit: "m" },
    { key: "residualPressure", label: "Tekanan residual outlet", unit: "mH₂O" }, { key: "pipeLoss", label: "Kerugian jaringan estimasi", unit: "m" },
    { key: "efficiency", label: "Efisiensi pompa", unit: "%" }, { key: "tankReserve", label: "Cadangan volume reservoir", unit: "%" },
    {key:"availableTank",label:"Volume reservoir tersedia",unit:"m³",min:0},{key:"maxVelocity",label:"Batas kecepatan pipa utama",unit:"m/s",min:.5},
    {key:"serviceFactor",label:"Cadangan daya motor",unit:"%",min:0},{key:"fdcCount",label:"Jumlah FDC/siamese connection",unit:"unit",min:1},
    {key:"zones",label:"Jumlah zona tekanan",unit:"zona",min:1},{key:"jockeyPercent",label:"Debit jockey pump",unit:"% fire flow",min:.5},
    {key:"networkLength",label:"Panjang jalur node terjauh",unit:"m",min:1},{key:"hazenC",label:"Koefisien Hazen-Williams C",unit:"-",min:1},
    {key:"selectedDiameter",label:"Diameter dalam pipa terpilih",unit:"mm",min:25},{key:"minorLossPercent",label:"Tambahan minor losses",unit:"% friction",min:0},
    {key:"hoseLength",label:"Panjang selang efektif",unit:"m",min:1},{key:"nozzleThrow",label:"Jangkauan pancaran nozzle",unit:"m",min:0},
    {key:"maxTravelDistance",label:"Jarak tempuh maksimum ke area",unit:"m",min:1},{key:"floorControlValves",label:"Control valve set per lantai",unit:"set",min:1},
  ],
  plumbing: [
    { key: "occupants", label: "Jumlah penghuni desain", unit: "orang" }, { key: "demandPerPerson", label: "Kebutuhan air per orang", unit: "L/orang/hari" },
    { key: "operationHours", label: "Jam operasi bangunan", unit: "jam/hari" }, { key: "peakFactor", label: "Faktor jam puncak", unit: "-" },
    { key: "storageHours", label: "Durasi cadangan air", unit: "jam" }, { key: "roofTankShare", label: "Porsi tangki atap", unit: "%" },
    { key: "pipeDiameter", label: "Diameter pipa utama awal", unit: "mm" }, { key: "staticHead", label: "Head statis pompa transfer", unit: "m" },
    { key: "pumpEfficiency", label: "Efisiensi pompa", unit: "%" },
  ],
  electrical: [
    { key: "lightingLoad", label: "Beban pencahayaan", unit: "kW" }, { key: "socketLoad", label: "Beban stop kontak", unit: "kW" },
    { key: "motorLoad", label: "Beban motor/pompa", unit: "kW" }, { key: "acLoad", label: "Beban AC/ventilasi", unit: "kW" },
    { key: "otherLoad", label: "Beban lainnya", unit: "kW" }, { key: "demandFactor", label: "Faktor kebutuhan", unit: "0–1" },
    { key: "voltage", label: "Tegangan sistem", unit: "V" }, { key: "powerFactor", label: "Faktor daya", unit: "cos φ" },
    { key: "phases", label: "Jumlah fasa", unit: "fasa" }, { key: "spare", label: "Cadangan panel", unit: "%" },
    { key: "cableLength", label: "Panjang feeder", unit: "m" },
    {key:"temperatureFactor",label:"Faktor koreksi temperatur",unit:"-",min:.1},{key:"groupingFactor",label:"Faktor koreksi pengelompokan",unit:"-",min:.1},{key:"installationFactor",label:"Faktor metode instalasi",unit:"-",min:.1},{key:"allowableDrop",label:"Batas jatuh tegangan",unit:"%",min:.1},
    {key:"faultCurrent",label:"Arus hubung singkat prospektif",unit:"kA",min:.1},{key:"clearingTime",label:"Waktu pemutusan proteksi",unit:"detik",min:.01},{key:"kFactor",label:"Faktor material/isolasi k",unit:"A√s/mm²",min:1},
    {key:"earthFaultCurrent",label:"Arus gangguan ke tanah",unit:"A",min:1},{key:"allowableTouchVoltage",label:"Tegangan sentuh diizinkan",unit:"V",min:1},{key:"soilResistivity",label:"Tahanan jenis tanah",unit:"Ωm",min:1},{key:"earthRodLength",label:"Panjang elektroda batang",unit:"m",min:.5},{key:"earthRodDiameter",label:"Diameter elektroda batang",unit:"mm",min:8},{key:"earthRodCount",label:"Jumlah elektroda",unit:"batang",min:1},
    {key:"buildingLength",label:"Panjang bangunan",unit:"m",min:1},{key:"buildingWidth",label:"Lebar bangunan",unit:"m",min:1},{key:"buildingHeight",label:"Tinggi bangunan",unit:"m",min:1},{key:"lightningProtectionLevel",label:"Kelas proteksi petir (I-IV)",unit:"kelas",min:1},{key:"lightningDownConductors",label:"Jumlah down conductor",unit:"jalur",min:2},
  ],
  genset: [
    { key: "connectedLoad", label: "Total beban terpasang", unit: "kW", min:1 }, {key:"emergencyShare",label:"Beban yang wajib dibackup",unit:"%",min:1},{ key: "demandFactor", label: "Faktor kebutuhan", unit: "0–1",min:.1 },
    { key: "future", label: "Cadangan pengembangan", unit: "%",min:0 }, { key: "powerFactor", label: "Faktor daya genset", unit: "cos φ",min:.1 },
    {key:"largestMotor",label:"Motor terbesar yang start",unit:"kW",min:0},{key:"motorStartMultiplier",label:"Pengali arus/daya starting",unit:"× FLA",min:1},{key:"startVoltageDip",label:"Batas voltage dip",unit:"%",min:1},
    {key:"altitude",label:"Elevasi lokasi",unit:"m dpl",min:0},{key:"ambientTemp",label:"Suhu udara ruang/masuk",unit:"°C",min:0},{key:"altitudeDerate",label:"Derating per 100 m di atas 1000 m",unit:"%",min:0},{key:"tempDerate",label:"Derating per °C di atas 40°C",unit:"%",min:0},
    {key:"autonomy",label:"Durasi operasi mandiri",unit:"jam",min:.5},{key:"fuelRate",label:"Konsumsi spesifik bahan bakar",unit:"L/kWh",min:.01},{key:"tankReserve",label:"Cadangan tangki bahan bakar",unit:"%",min:0},
    {key:"roomLength",label:"Panjang ruang genset",unit:"m",min:1},{key:"roomWidth",label:"Lebar ruang genset",unit:"m",min:1},{key:"roomHeight",label:"Tinggi bersih ruang",unit:"m",min:1},{key:"airPerKw",label:"Kebutuhan udara ventilasi",unit:"m³/s per kW",min:.001},{key:"maxAirVelocity",label:"Kecepatan maksimum louver",unit:"m/s",min:.5},
    {key:"exhaustLength",label:"Panjang ekuivalen exhaust",unit:"m",min:0},{key:"exhaustBackPressurePerM",label:"Back pressure pipa per meter",unit:"mbar/m",min:0},{key:"silencerLoss",label:"Pressure loss silencer",unit:"mbar",min:0},
    {key:"soundSource",label:"Sound pressure sumber",unit:"dB(A)",min:1},{key:"targetNoise",label:"Target kebisingan batas ruang",unit:"dB(A)",min:1},{key:"distance",label:"Jarak titik penerima",unit:"m",min:1},{key:"wallAttenuation",label:"Reduksi enclosure/dinding",unit:"dB",min:0},
    {key:"units",label:"Jumlah genset duty",unit:"unit",min:1},{key:"standbyUnits",label:"Jumlah genset cadangan",unit:"unit",min:0},{key:"operatingHours",label:"Estimasi operasi tahunan",unit:"jam/tahun",min:0},{key:"energyTariff",label:"Nilai energi pembanding",unit:"Rp/kWh",min:0},
  ],
  ac: [
    {key:"area",label:"Luas ruang terkondisi",unit:"m²",min:1},{key:"height",label:"Tinggi bersih ruang",unit:"m",min:1},{key:"people",label:"Jumlah penghuni desain",unit:"orang",min:0},
    {key:"outdoorDb",label:"Suhu bola kering luar",unit:"°C",min:0},{key:"outdoorRh",label:"Kelembapan relatif luar",unit:"%RH",min:1},{key:"indoorDb",label:"Suhu dalam rencana",unit:"°C",min:16},{key:"indoorRh",label:"Kelembapan relatif dalam",unit:"%RH",min:30},
    {key:"wallU",label:"U-value dinding",unit:"W/m²K",min:.1},{key:"wallArea",label:"Luas dinding efektif",unit:"m²",min:0},{key:"roofU",label:"U-value atap",unit:"W/m²K",min:.1},{key:"roofArea",label:"Luas atap/ceiling terpapar",unit:"m²",min:0},{key:"glassU",label:"U-value kaca",unit:"W/m²K",min:.1},{key:"windowArea",label:"Luas kaca",unit:"m²",min:0},{key:"solarGlass",label:"Solar heat gain kaca",unit:"W/m²",min:0},{key:"shadingFactor",label:"Faktor shading/SC",unit:"0–1",min:0},
    {key:"lighting",label:"Daya pencahayaan",unit:"W/m²",min:0},{key:"equipment",label:"Beban peralatan",unit:"W",min:0},{key:"sensiblePerson",label:"Panas sensibel per orang",unit:"W/orang",min:0},{key:"latentPerson",label:"Panas laten per orang",unit:"W/orang",min:0},
    {key:"freshAirPerson",label:"Udara luar per orang",unit:"L/s.orang",min:0},{key:"ach",label:"Ventilasi tambahan",unit:"ACH",min:0},{key:"infiltrationAch",label:"Infiltrasi",unit:"ACH",min:0},
    {key:"safety",label:"Faktor keamanan",unit:"%",min:0},{key:"diversity",label:"Faktor keragaman sistem",unit:"0–1",min:.1},{key:"supplyDeltaT",label:"Beda suhu supply-room",unit:"K",min:4},{key:"ductVelocity",label:"Kecepatan duct utama",unit:"m/s",min:1},{key:"pipeVelocity",label:"Kecepatan chilled-water",unit:"m/s",min:.3},{key:"condensateSlope",label:"Kemiringan drain kondensat",unit:"%",min:.5},
    {key:"cop",label:"COP sistem",unit:"-",min:1},{key:"operatingHours",label:"Jam operasi per hari",unit:"jam",min:0},{key:"operatingDays",label:"Hari operasi per tahun",unit:"hari",min:0},{key:"energyTariff",label:"Tarif energi",unit:"Rp/kWh",min:0},
  ],
  lighting: [
    {key:"roomLength",label:"Panjang ruang",unit:"m",min:.1},{key:"roomWidth",label:"Lebar ruang",unit:"m",min:.1},{key:"roomHeight",label:"Tinggi ruang",unit:"m",min:.1},{key:"workPlane",label:"Tinggi bidang kerja",unit:"m",min:0},
    {key:"targetLux",label:"Target iluminansi",unit:"lux",min:1},{key:"minimumLux",label:"Iluminansi minimum desain",unit:"lux",min:0},{key:"maximumLux",label:"Iluminansi maksimum desain",unit:"lux",min:1},{key:"colorTemperature",label:"Temperatur warna",unit:"K",min:1000},{key:"cri",label:"Color Rendering Index",unit:"Ra",min:1},
    {key:"lumensPerFixture",label:"Lumen per armatur",unit:"lm",min:1},{key:"fixturePower",label:"Daya per armatur",unit:"W",min:.1},{key:"utilizationFactor",label:"Faktor utilisasi (UF)",unit:"-",min:.01},{key:"maintenanceFactor",label:"Faktor pemeliharaan (MF)",unit:"-",min:.01},
    {key:"reflectanceCeiling",label:"Reflektansi plafon",unit:"%",min:0},{key:"reflectanceWall",label:"Reflektansi dinding",unit:"%",min:0},{key:"reflectanceFloor",label:"Reflektansi lantai",unit:"%",min:0},{key:"spacingHeightRatio",label:"Spacing-to-height ratio",unit:"-",min:.1},
    {key:"operatingHours",label:"Jam operasi per hari",unit:"jam",min:0},{key:"operatingDays",label:"Hari operasi per tahun",unit:"hari",min:0},{key:"controlSaving",label:"Penghematan kontrol/daylight",unit:"%",min:0},{key:"emergencyShare",label:"Armatur darurat awal",unit:"%",min:0},{key:"energyTariff",label:"Tarif energi",unit:"Rp/kWh",min:0},
  ],
  fireSafety: [
    {key:"floorArea",label:"Luas per lantai",unit:"m²",min:1},{key:"floors",label:"Jumlah lantai",unit:"lantai",min:1},{key:"ceilingHeight",label:"Tinggi plafon",unit:"m",min:1},
    {key:"smokeCoverage",label:"Cakupan smoke detector",unit:"m²/unit",min:1},{key:"heatCoverage",label:"Cakupan heat detector",unit:"m²/unit",min:1},{key:"flameCoverage",label:"Cakupan flame detector",unit:"m²/unit",min:1},{key:"smokeShare",label:"Area smoke detector",unit:"%",min:0},{key:"heatShare",label:"Area heat detector",unit:"%",min:0},{key:"flameShare",label:"Area flame detector",unit:"%",min:0},
    {key:"manualCallPointsPerFloor",label:"Manual call point per lantai",unit:"unit",min:1},{key:"soundersPerFloor",label:"Sounder per lantai",unit:"unit",min:1},{key:"strobesPerFloor",label:"Strobe per lantai",unit:"unit",min:0},{key:"floorsPerZone",label:"Lantai per zona alarm",unit:"lantai",min:1},{key:"loopCapacity",label:"Kapasitas device per loop",unit:"device",min:1},
    {key:"standbyCurrent",label:"Arus panel kondisi standby",unit:"A",min:0},{key:"alarmCurrent",label:"Arus panel kondisi alarm",unit:"A",min:0},{key:"standbyHours",label:"Durasi standby baterai",unit:"jam",min:1},{key:"alarmHours",label:"Durasi alarm baterai",unit:"jam",min:.1},{key:"batteryMargin",label:"Margin baterai",unit:"%",min:0},
    {key:"sprinklerCoverage",label:"Cakupan sprinkler",unit:"m²/head",min:1},{key:"sprinklerDensity",label:"Density sprinkler",unit:"L/menit/m²",min:.1},{key:"designArea",label:"Remote design area",unit:"m²",min:1},{key:"hoseDemand",label:"Debit hose/standpipe simultan",unit:"L/s",min:0},{key:"suppressionDuration",label:"Durasi water supply",unit:"menit",min:1},{key:"waterReserve",label:"Cadangan volume air",unit:"%",min:0},{key:"availableWater",label:"Volume air tersedia",unit:"m³",min:0},
    {key:"pipeVelocity",label:"Batas kecepatan pipa awal",unit:"m/s",min:.1},{key:"pumpEfficiency",label:"Efisiensi pompa",unit:"%",min:1},{key:"serviceFactor",label:"Service factor motor",unit:"%",min:0},
  ],
  communications: [
    {key:"occupants",label:"Jumlah pengguna gedung",unit:"orang",min:1},{key:"workstations",label:"Jumlah workstation",unit:"titik",min:1},{key:"extensionsPerWorkstation",label:"Extension per workstation",unit:"ext/titik",min:0},{key:"commonExtensions",label:"Extension area umum",unit:"ext",min:0},{key:"extensionGrowth",label:"Cadangan pertumbuhan extension",unit:"%",min:0},
    {key:"concurrentCallPercent",label:"Panggilan simultan",unit:"%",min:1},{key:"trunkUtilization",label:"Batas utilisasi trunk",unit:"%",min:1},{key:"channelsPerPri",label:"Kanal per PRI/E1",unit:"kanal",min:1},
    {key:"floors",label:"Jumlah lantai",unit:"lantai",min:1},{key:"floorArea",label:"Luas per lantai",unit:"m²",min:1},{key:"outletsPer100sqm",label:"Outlet telekomunikasi per 100 m²",unit:"titik",min:1},{key:"spareOutlets",label:"Cadangan outlet",unit:"%",min:0},{key:"averageHorizontalCable",label:"Panjang kabel horizontal rata-rata",unit:"m",min:1},{key:"cablePairsPerExtension",label:"Pair per extension",unit:"pair",min:1},{key:"backboneReserve",label:"Cadangan backbone",unit:"%",min:0},
    {key:"intercomStations",label:"Interkom station",unit:"unit",min:0},{key:"intercomMasters",label:"Interkom master",unit:"unit",min:1},{key:"htUsers",label:"Pengguna HT/radio",unit:"orang",min:0},{key:"htChannels",label:"Kanal HT tersedia",unit:"kanal",min:1},
    {key:"rackDevices",label:"Perangkat aktif dalam rack",unit:"unit",min:1},{key:"rackUnitsPerDevice",label:"Rack unit rata-rata per perangkat",unit:"U",min:1},{key:"rackReserve",label:"Cadangan ruang rack",unit:"%",min:0},{key:"devicePower",label:"Daya rata-rata per perangkat",unit:"W",min:1},{key:"upsRuntime",label:"Autonomy UPS",unit:"menit",min:1},{key:"upsEfficiency",label:"Efisiensi UPS",unit:"%",min:1},{key:"powerFactor",label:"Faktor daya beban ICT",unit:"-",min:.1},
  ],
};

const liftFlowSteps = [
  {title:"Data gedung", hint:"Geometri dan populasi", keys:["floors","floorHeight","area","occupancyArea"]},
  {title:"Target layanan", hint:"Traffic dan peak", keys:["peak","capacity","speed","units"]},
  {title:"Zoning & kabin", hint:"Zona, core, ruang kabin", keys:["zones","shaftArea","cabinArea"]},
  {title:"Energi & biaya", hint:"Operasi dan life-cycle", keys:["motorEfficiency","counterweightFactor","dutyFactor","operatingHours","operatingDays","regenSaving","idlePower","energyTariff","installationCost","maintenanceAnnual","analysisYears","discountRate"]},
  {title:"Eskalator", hint:"Kapasitas dan daya", keys:["escalatorUnits","escalatorSpeed","escalatorRise","stepPitch","stepOccupancy","escalatorEfficiency"]},
  {title:"Hasil & vendor", hint:"Review dan pembanding", keys:[]},
];

const liftVendors = [
  {id:"kone-monospace", brand:"KONE", model:"MonoSpace DX", application:"Low-mid rise, MRL", maxTravel:120, maxSpeed:3, maxLoad:"33 orang", capacity:20, speed:2.5, note:"Katalog KONE Indonesia; konfigurasi final by quotation."},
  {id:"kone-minispace", brand:"KONE", model:"MiniSpace DX", application:"Mid-high rise, compact MR", maxTravel:210, maxSpeed:4, maxLoad:"26 orang", capacity:20, speed:4, note:"Katalog KONE Indonesia; periksa ruang mesin dan duty."},
  {id:"mitsubishi-nexiez", brand:"Mitsubishi Electric", model:"NEXIEZ-MRL V2", application:"Low-mid rise, MRL", maxTravel:80, maxSpeed:2.5, maxLoad:"450-1.800 kg*", capacity:17, speed:1.75, note:"Rentang tergantung pasar/konfigurasi; wajib konfirmasi agen lokal."},
  {id:"schindler-3000", brand:"Schindler", model:"3000", application:"Residential, office, hotel", maxTravel:75, maxSpeed:1.75, maxLoad:"400-1.150 kg", capacity:13, speed:1.75, note:"MR/MRL, sampai 25 stops dan grup hingga 8 unit."},
  {id:"schindler-5000", brand:"Schindler", model:"5000 / 5000 MR", application:"Mid-rise premium", maxTravel:125, maxSpeed:2.5, maxLoad:"630-1.600 kg", capacity:17, speed:2.5, note:"Travel maksimum berbeda antara MRL dan MR."},
  {id:"otis-gen3", brand:"Otis", model:"Gen3 family", application:"Low-high traffic", maxTravel:150, maxSpeed:3.5, maxLoad:"hingga ±2.268 kg*", capacity:20, speed:3.5, note:"Core/Edge/Peak berbeda per pasar; verifikasi produk yang tersedia di Indonesia."},
];
const escalatorVendors = [
  {id:"kone-tm110",brand:"KONE",model:"TravelMaster 110",use:"Komersial indoor/outdoor",maxRise:13,speeds:"0,5",angles:"30/35",widths:"600/800/1000"},
  {id:"kone-tm120",brand:"KONE",model:"TransitMaster 120",use:"Public transport/heavy duty",maxRise:20,speeds:"0,5/0,65/0,75",angles:"27,3/30/35",widths:"800/1000"},
  {id:"kone-tm140",brand:"KONE",model:"TransitMaster 140",use:"Heavy duty high rise",maxRise:35,speeds:"0,5/0,65/0,75",angles:"27,3-30",widths:"1000"},
  {id:"mitsubishi-u",brand:"Mitsubishi Electric",model:"u series",use:"Komersial, energy-saving",maxRise:13,speeds:"0,5 (standby 0,2)",angles:"30",widths:"800/1000"},
  {id:"mitsubishi-z",brand:"Mitsubishi Electric",model:"Z series",use:"Indoor/semi-outdoor/outdoor",maxRise:13,speeds:"0,5",angles:"30/35",widths:"600/1000"},
  {id:"otis-link",brand:"Otis",model:"Link",use:"Komersial indoor/outdoor",maxRise:13,speeds:"0,5",angles:"30/35",widths:"600/800/1000"},
];
const pumpFlowSteps=[
  {title:"Duty point",hint:"Debit dan head statis",keys:["flow","staticHead","pressureHead"]},
  {title:"Jaringan",hint:"Pipa dan fitting",keys:["length","diameter","friction","minorK"]},
  {title:"Fluida",hint:"Densitas dan viskositas",keys:["density","viscosity"]},
  {title:"NPSH",hint:"Cek kavitasi",keys:["npsha","npshr"]},
  {title:"Energi",hint:"Efisiensi dan operasi",keys:["efficiency","serviceFactor","operatingHours","operatingDays","energyTariff"]},
  {title:"Konfigurasi",hint:"Duty, standby, seri dan surge",keys:["dutyPumps","standbyPumps","seriesPumps","minimumFlow","rpm","waveSpeed","closureTime","designPressureMargin"]},
  {title:"Kurva sistem",hint:"Operating point dan BEP",keys:["shutoffFactor","runoutFactor","bepTolerance"]},
  {title:"Seleksi",hint:"Jenis, material dan vendor",keys:[]},
];
const pumpVendors=[
  {id:"grundfos-nb",brand:"Grundfos",series:"NB/NBE",type:"End suction EN 733",application:"Distribusi air, HVAC, industri"},
  {id:"grundfos-cr",brand:"Grundfos",series:"CR/CRE",type:"Vertical multistage",application:"Booster, tekanan tinggi, proses"},
  {id:"ebara-3",brand:"EBARA Indonesia",series:"3/3L Series",type:"End suction SS304/316L",application:"Air bersih, industri, HVAC"},
  {id:"ebara-lpd",brand:"EBARA Indonesia",series:"LPD/LPDA",type:"Vertical in-line",application:"Booster gedung, sirkulasi HVAC"},
  {id:"ksb-etanorm",brand:"KSB Indonesia",series:"Etanorm",type:"End suction EN 733",application:"Water supply, HVAC, fire/irigasi"},
  {id:"torishima",brand:"Torishima Indonesia",series:"Project selection",type:"End suction/multistage/split case",application:"Infrastruktur dan industri"},
];
const hydrantFlowSteps=[
  {title:"Kriteria",hint:"Luas, lantai, risiko",keys:["buildingArea","floors","coverageRadius"]},
  {title:"Debit",hint:"Outlet simultan dan flow",keys:["hydrants","flowPerHydrant","duration"]},
  {title:"Reservoir",hint:"Volume dan cadangan",keys:["tankReserve","availableTank"]},
  {title:"Jaringan",hint:"Node terjauh dan diameter",keys:["staticHead","residualPressure","networkLength","hazenC","selectedDiameter","minorLossPercent","maxVelocity","zones"]},
  {title:"Fire pump",hint:"Daya dan konfigurasi",keys:["efficiency","serviceFactor","jockeyPercent"]},
  {title:"Layout & FDC",hint:"Jangkauan dan koneksi",keys:["hoseLength","nozzleThrow","maxTravelDistance","fdcCount","floorControlValves"]},
  {title:"Vendor & audit",hint:"Schedule dan review",keys:[]},
];
const gensetFlowSteps=[
  {title:"Load schedule",hint:"Beban darurat & demand",keys:["connectedLoad","emergencyShare","demandFactor","future","powerFactor"]},
  {title:"Motor starting",hint:"Transien & voltage dip",keys:["largestMotor","motorStartMultiplier","startVoltageDip"]},
  {title:"Derating",hint:"Elevasi dan temperatur",keys:["altitude","ambientTemp","altitudeDerate","tempDerate"]},
  {title:"Bahan bakar",hint:"Autonomy dan tangki",keys:["autonomy","fuelRate","tankReserve","operatingHours","energyTariff"]},
  {title:"Ruang & udara",hint:"Layout dan ventilasi",keys:["roomLength","roomWidth","roomHeight","airPerKw","maxAirVelocity"]},
  {title:"Exhaust",hint:"Back pressure",keys:["exhaustLength","exhaustBackPressurePerM","silencerLoss"]},
  {title:"Akustik",hint:"Noise & vibration",keys:["soundSource","targetNoise","distance","wallAttenuation"]},
  {title:"Konfigurasi",hint:"Duty, standby & vendor",keys:["units","standbyUnits"]},
  {title:"Audit",hint:"Instalasi dan kerusakan",keys:[]},
];
const gensetVendors=[
  {id:"cummins",brand:"Cummins",series:"C Series / Power Generation",note:"Diesel generator set; kapasitas dan rating mengikuti data sheet resmi proyek."},
  {id:"caterpillar",brand:"Caterpillar",series:"Diesel Generator Sets",note:"Standby/prime rating; verifikasi alternator, controller, emisi dan dukungan lokal."},
  {id:"perkins",brand:"Perkins",series:"Powered Generating Sets",note:"Engine basis; integrator lokal wajib menyerahkan paket alternator dan controller."},
  {id:"mtu",brand:"Rolls-Royce mtu",series:"Series 1600/2000",note:"Paket emergency/standby; konfigurasi aktual mengikuti technical submittal."},
  {id:"fgwilson",brand:"FG Wilson",series:"Industrial Range",note:"Rating 50/60 Hz berbeda; cek ketersediaan dan layanan Indonesia."},
  {id:"mitsubishi",brand:"Mitsubishi",series:"MGS / approved local package",note:"Verifikasi integrator, panel sinkron, alternator, fuel system dan sertifikasi."},
];
const electricalFlowSteps=[
  {title:"Load schedule",hint:"Beban dan demand",keys:["lightingLoad","socketLoad","motorLoad","acLoad","otherLoad","demandFactor","spare"]},
  {title:"Sistem",hint:"Tegangan dan faktor daya",keys:["voltage","powerFactor","phases"]},
  {title:"Kabel",hint:"KHA dan koreksi",keys:["cableLength","temperatureFactor","groupingFactor","installationFactor","allowableDrop"]},
  {title:"Proteksi",hint:"Hubung singkat & pemutusan",keys:["faultCurrent","clearingTime","kFactor"]},
  {title:"Grounding",hint:"Elektroda & sentuh",keys:["earthFaultCurrent","allowableTouchVoltage","soilResistivity","earthRodLength","earthRodDiameter","earthRodCount"]},
  {title:"Proteksi petir",hint:"Geometri & down conductor",keys:["buildingLength","buildingWidth","buildingHeight","lightningProtectionLevel","lightningDownConductors"]},
  {title:"Audit DED",hint:"SLD, schedule dan BOQ",keys:[]},
];
const lightingFlowSteps=[
  {title:"Geometri",hint:"Ruang dan bidang kerja",keys:["roomLength","roomWidth","roomHeight","workPlane"]},
  {title:"Kriteria visual",hint:"Lux, warna dan CRI",keys:["targetLux","minimumLux","maximumLux","colorTemperature","cri"]},
  {title:"Armatur",hint:"Lumen, daya, UF dan MF",keys:["lumensPerFixture","fixturePower","utilizationFactor","maintenanceFactor"]},
  {title:"Reflektansi",hint:"Plafon, dinding dan lantai",keys:["reflectanceCeiling","reflectanceWall","reflectanceFloor"]},
  {title:"Layout",hint:"Jarak dan pola armatur",keys:["spacingHeightRatio"]},
  {title:"Energi & kontrol",hint:"LPD, jadwal dan daylight",keys:["operatingHours","operatingDays","controlSaving","energyTariff"]},
  {title:"Darurat & DED",hint:"Emergency, audit dan laporan",keys:["emergencyShare"]},
];
const fireSafetyFlowSteps=[
  {title:"Klasifikasi",hint:"Bangunan dan bahaya",keys:["floorArea","floors","ceilingHeight"]},
  {title:"Deteksi",hint:"Smoke, heat dan flame",keys:["smokeCoverage","heatCoverage","flameCoverage","smokeShare","heatShare","flameShare"]},
  {title:"Alarm",hint:"MCP, sounder dan strobe",keys:["manualCallPointsPerFloor","soundersPerFloor","strobesPerFloor"]},
  {title:"Panel & loop",hint:"Zoning dan kapasitas",keys:["floorsPerZone","loopCapacity"]},
  {title:"Baterai",hint:"Standby dan alarm",keys:["standbyCurrent","alarmCurrent","standbyHours","alarmHours","batteryMargin"]},
  {title:"Sprinkler",hint:"Coverage dan density",keys:["sprinklerCoverage","sprinklerDensity","designArea"]},
  {title:"Water supply",hint:"Debit, durasi dan reserve",keys:["hoseDemand","suppressionDuration","waterReserve","availableWater"]},
  {title:"Pompa & jaringan",hint:"Diameter dan daya",keys:["pipeVelocity","pumpEfficiency","serviceFactor"]},
  {title:"DED & audit",hint:"Cause-effect dan dokumen",keys:[]},
];
const fireSafetyVendors=[
  {brand:"Honeywell Notifier",system:"Addressable fire alarm",note:"Panel, detector, module dan network; seri final mengikuti approved submittal Indonesia."},
  {brand:"Siemens Cerberus PRO",system:"Fire detection & voice alarm",note:"Verifikasi kompatibilitas, listing, integrasi dan layanan lokal."},
  {brand:"Bosch AVENAR",system:"Addressable fire alarm",note:"Panel jaringan, detector dan interface; kapasitas aktual mengikuti datasheet."},
  {brand:"Hochiki / Nohmi",system:"Detector & fire alarm",note:"Gunakan sebagai pembanding atau setara; cek sertifikasi dan dukungan integrator."},
  {brand:"Tyco / Viking / HD Fire",system:"Sprinkler, valve & suppression",note:"K-factor, temperature rating, hazard dan listing harus sesuai hydraulic calculation."},
];
const communicationsFlowSteps=[
  {title:"Pengguna",hint:"Populasi dan workstation",keys:["occupants","workstations","extensionsPerWorkstation","commonExtensions","extensionGrowth"]},
  {title:"PABX & trunk",hint:"Traffic dan kanal",keys:["concurrentCallPercent","trunkUtilization","channelsPerPri"]},
  {title:"Outlet",hint:"Titik per lantai",keys:["floors","floorArea","outletsPer100sqm","spareOutlets"]},
  {title:"Horizontal cabling",hint:"Panjang dan channel",keys:["averageHorizontalCable"]},
  {title:"Backbone",hint:"Pair, core dan reserve",keys:["cablePairsPerExtension","backboneReserve"]},
  {title:"Interkom",hint:"Master dan station",keys:["intercomStations","intercomMasters"]},
  {title:"HT & radio",hint:"Pengguna dan kanal",keys:["htUsers","htChannels"]},
  {title:"Rack & UPS",hint:"Space, daya dan autonomy",keys:["rackDevices","rackUnitsPerDevice","rackReserve","devicePower","upsRuntime","upsEfficiency","powerFactor"]},
  {title:"DED & audit",hint:"Riser, schedule dan test",keys:[]},
];
const communicationsVendors=[
  {brand:"Panasonic",system:"IP-PBX / hybrid PABX",note:"Kapasitas extension, SIP trunk, license dan dukungan lokal diverifikasi pada submittal."},
  {brand:"NEC",system:"UNIVERGE communications",note:"IP telephony, attendant dan redundancy mengikuti kebutuhan proyek."},
  {brand:"Cisco / Grandstream",system:"IP telephony & unified communications",note:"License, PoE, QoS, security dan interoperability wajib dikunci."},
  {brand:"CommScope / Panduit / Schneider",system:"Structured cabling",note:"Copper/fiber channel harus diuji dengan permanent-link certification."},
  {brand:"TOA / Aiphone",system:"Interkom dan communication station",note:"Interface, coverage, power supply dan environmental rating dikonfirmasi."},
  {brand:"Motorola Solutions / Hytera",system:"HT / digital radio",note:"Frekuensi, izin, repeater, coverage dan sertifikasi perangkat harus sah di Indonesia."},
];
const acFlowSteps=[
  {title:"Ruang",hint:"Luas, tinggi, penghuni",keys:["area","height","people"]},
  {title:"Kondisi desain",hint:"Outdoor & indoor",keys:["outdoorDb","outdoorRh","indoorDb","indoorRh"]},
  {title:"Selubung",hint:"Dinding, atap dan kaca",keys:["wallU","wallArea","roofU","roofArea","glassU","windowArea","solarGlass","shadingFactor"]},
  {title:"Internal load",hint:"Lampu, alat, penghuni",keys:["lighting","equipment","sensiblePerson","latentPerson"]},
  {title:"Udara luar",hint:"Ventilasi & infiltrasi",keys:["freshAirPerson","ach","infiltrationAch"]},
  {title:"Sistem",hint:"Diversity, supply & COP",keys:["safety","diversity","supplyDeltaT","cop"]},
  {title:"Distribusi",hint:"Duct, pipe dan drain",keys:["ductVelocity","pipeVelocity","condensateSlope"]},
  {title:"Energi",hint:"Operasi tahunan",keys:["operatingHours","operatingDays","energyTariff"]},
  {title:"Seleksi",hint:"Jenis sistem & vendor",keys:[]},
  {title:"Audit",hint:"DED dan commissioning",keys:[]},
];
const acVendors=[
  {id:"daikin-vrv",brand:"Daikin",series:"VRV / SkyAir",type:"VRF/DX",use:"Gedung komersial, kantor, hotel; zoning fleksibel"},
  {id:"mitsubishi-vrf",brand:"Mitsubishi Electric",series:"CITY MULTI",type:"VRF/DX",use:"Multi-zone dengan kontrol terpusat"},
  {id:"panasonic-vrf",brand:"Panasonic",series:"ECOi",type:"VRF/DX",use:"Komersial multi-zone dan heat recovery tertentu"},
  {id:"lg-vrf",brand:"LG",series:"MULTI V",type:"VRF/DX",use:"Komersial dengan pilihan indoor unit beragam"},
  {id:"carrier-chiller",brand:"Carrier",series:"AquaEdge / AquaForce",type:"Water/Air Cooled Chiller",use:"Sistem sentral kapasitas menengah-besar"},
  {id:"trane-chiller",brand:"Trane",series:"Sintesis / CenTraVac",type:"Water Cooled Chiller",use:"Sistem sentral efisiensi tinggi"},
  {id:"york-chiller",brand:"YORK",series:"YVAA / YK",type:"Air/Water Cooled Chiller",use:"Sistem sentral gedung komersial"},
];
const firePumpVendors=[
  {id:"grundfos-nk",brand:"Grundfos",series:"NK/NB Fire System",type:"End suction",note:"Konfigurasi final mengikuti listing, kurva dan paket controller lokal."},
  {id:"ebara-fire",brand:"EBARA Indonesia",series:"Fire Fighting Pump Package",type:"End suction / split case",note:"Verifikasi kapasitas, controller, diesel driver dan sertifikasi proyek."},
  {id:"ksb-fire",brand:"KSB Indonesia",series:"Etanorm / Omega Fire",type:"End suction / split case",note:"Pilih dari kurva resmi serta persyaratan fire pump set."},
  {id:"torishima-fire",brand:"Torishima Indonesia",series:"Fire Pump Project Selection",type:"Split case / end suction",note:"Untuk infrastruktur dan gedung; final berdasarkan technical submittal."},
  {id:"patterson",brand:"Patterson",series:"Horizontal Split Case",type:"Listed fire pump",note:"Ketersediaan dan dukungan lokal wajib dikonfirmasi."},
];
const chapterManualGates:Record<"lift"|"escalator",string[]>={
  lift:["Data populasi tiap lantai disetujui owner","Target waiting time dan handling capacity disetujui","Dimensi shaft, pit, overhead dan pintu diverifikasi","Reaksi struktur dan bukaan core terkoordinasi","Daya, kualitas listrik, genset dan rescue operation terverifikasi","Fireman lift, aksesibilitas dan komunikasi darurat terkoordinasi","Kurva traffic dan group control dikonfirmasi vendor","Shop drawing serta technical submittal disetujui engineer"],
  escalator:["Arus penumpang puncak dan arah perjalanan disetujui","Rise, sudut, panjang horizontal dan clear height terverifikasi","Pit, landing dan support reaction terkoordinasi struktur","Lebar step, speed dan kapasitas dikonfirmasi vendor","Brake, emergency stop dan anti-reversal terverifikasi","Fire alarm interface serta skenario penghentian disetujui","Drainage dan weatherproofing terverifikasi bila outdoor","Shop drawing serta technical submittal disetujui engineer"]
};
const briefQuestions:BriefQuestion[]=[
  {id:"project",category:"Owner",question:"Nama dan kode proyek",required:true,placeholder:"Nama proyek / kode"},{id:"function",category:"Owner",question:"Fungsi utama bangunan",required:true,options:["Kantor","Hunian","Hotel","Rumah sakit","Pusat belanja","Kampus","Mixed-use","Lainnya"]},{id:"location",category:"Owner",question:"Lokasi proyek",required:true},{id:"floors",category:"Owner",question:"Jumlah lantai dan basement",required:true},{id:"population",category:"Owner",question:"Populasi normal dan maksimum",required:true},{id:"hours",category:"Owner",question:"Jam dan hari operasional",required:true},{id:"peak",category:"Owner",question:"Periode lalu lintas tertinggi",required:true,options:["Pagi","Siang","Sore","Acara khusus","Beragam"]},{id:"wait",category:"Owner",question:"Target waktu tunggu lift",required:true,options:["≤25 detik","26-35 detik","36-45 detik","Ditentukan engineer"]},{id:"specialLift",category:"Owner",question:"Jenis lift khusus yang dibutuhkan",options:["Tidak ada","Service/barang","Bed lift","VIP","Fireman","Beberapa jenis"]},{id:"budget",category:"Owner",question:"Prioritas biaya",required:true,options:["Investasi awal","Biaya operasi","Life-cycle cost seimbang"]},{id:"documents",category:"Owner",question:"Tahap dokumen yang diperlukan",required:true,options:["Konsep","Preliminary","DED","Tender","IFC"]},
  {id:"floorHeight",category:"Arsitektur",question:"Tinggi lantai ke lantai",required:true},{id:"floorArea",category:"Arsitektur",question:"Luas setiap lantai",required:true},{id:"core",category:"Arsitektur",question:"Posisi dan luas core/lobby tersedia",required:true},{id:"shaft",category:"Arsitektur",question:"Ukuran shaft tersedia"},{id:"pit",category:"Arsitektur",question:"Kedalaman pit tersedia"},{id:"overhead",category:"Arsitektur",question:"Overhead lantai teratas"},{id:"machineRoom",category:"Arsitektur",question:"Ketersediaan ruang mesin",options:["Tersedia","Tidak tersedia","Belum ditentukan"]},{id:"door",category:"Arsitektur",question:"Lebar, tinggi dan sisi bukaan pintu"},{id:"skyLobby",category:"Arsitektur",question:"Kebutuhan skylobby",options:["Tidak","Ya","Perlu dievaluasi"]},{id:"escSpace",category:"Arsitektur",question:"Rise dan ruang horizontal eskalator"},{id:"outdoor",category:"Arsitektur",question:"Lingkungan eskalator",options:["Indoor","Semi-outdoor","Outdoor","Belum ditentukan"]},
  {id:"populationFloor",category:"Traffic",question:"Populasi setiap lantai",required:true},{id:"trafficDirection",category:"Traffic",question:"Arah traffic dominan",required:true,options:["Up-peak","Down-peak","Dua arah","Beragam"]},{id:"publicFloor",category:"Traffic",question:"Lantai publik khusus/restoran/ballroom"},{id:"maxCore",category:"Traffic",question:"Jumlah lift maksimum yang dapat ditampung core"},{id:"destination",category:"Traffic",question:"Destination control",options:["Tidak","Ya","Perlu dievaluasi"]},{id:"escPeak",category:"Traffic",question:"Arus penumpang puncak eskalator (orang/jam)",required:true},{id:"escDirection",category:"Traffic",question:"Distribusi arah eskalator",required:true},{id:"escReserve",category:"Traffic",question:"Kebutuhan unit cadangan/reversible",options:["Tidak","Cadangan","Reversible","Keduanya"]},
  {id:"structure",category:"Struktur",question:"Sistem struktur core dan kapasitas support",required:true},{id:"waterproof",category:"Struktur",question:"Waterproofing pit dan sump pump",options:["Tersedia","Tidak","Perlu direncanakan"]},{id:"seismic",category:"Struktur",question:"Seismic provision",options:["Diperlukan","Tidak","Belum diperiksa"]},{id:"reaction",category:"Struktur",question:"Reaksi vendor dan bukaan struktur sudah dikoordinasikan",options:["Sudah","Belum","Menunggu vendor"]},
  {id:"power",category:"Kelistrikan",question:"Tegangan, frekuensi dan jumlah fasa",required:true},{id:"emergency",category:"Kelistrikan",question:"Unit yang dilayani genset",required:true},{id:"ats",category:"Kelistrikan",question:"ATS/AMF dan automatic rescue",options:["Tersedia","Direncanakan","Belum ditentukan"]},{id:"bms",category:"Kelistrikan",question:"Interface BMS, fire alarm, access control dan CCTV"},{id:"communication",category:"Kelistrikan",question:"Komunikasi darurat ke ruang kontrol",options:["Diperlukan","Tidak","Belum ditentukan"]},
  {id:"brands",category:"Vendor",question:"Merek yang diterima atau approved equal",required:true},{id:"warranty",category:"Vendor",question:"Garansi dan free maintenance"},{id:"afterSales",category:"Vendor",question:"Target layanan purnajual dan ketersediaan suku cadang"},{id:"tkdn",category:"Vendor",question:"Persyaratan TKDN",options:["Diperlukan","Tidak","Belum ditentukan"]},{id:"schedule",category:"Vendor",question:"Target produksi, pemasangan dan commissioning",required:true},{id:"submittal",category:"Vendor",question:"Kurva, shop drawing, daya dan sertifikasi wajib diserahkan",options:["Ya","Tidak","Belum ditentukan"]},
  {id:"pump_source",category:"Sumber Air",question:"Sumber dan kontinuitas air",required:true,modules:["pump"]},{id:"pump_flow",category:"Duty Point",question:"Debit desain total (m³/jam)",required:true,modules:["pump"]},{id:"pump_static",category:"Duty Point",question:"Head statis (m)",required:true,modules:["pump"]},{id:"pump_pressure",category:"Duty Point",question:"Tambahan head tekanan (m)",required:true,modules:["pump"]},{id:"pump_length",category:"Jaringan",question:"Panjang pipa ekuivalen (m)",required:true,modules:["pump"]},{id:"pump_diameter",category:"Jaringan",question:"Diameter dalam pipa awal (mm)",required:true,modules:["pump"]},{id:"pump_fluid",category:"Fluida",question:"Jenis, densitas, suhu dan kualitas fluida",required:true,modules:["pump"]},{id:"pump_suction",category:"Suction",question:"Kondisi suction, elevasi dan tekanan tersedia",required:true,modules:["pump"]},{id:"pump_npsha",category:"Suction",question:"NPSH tersedia/NPSHa (m)",required:true,modules:["pump"]},{id:"pump_config",category:"Konfigurasi",question:"Konfigurasi duty/standby/seri/paralel",required:true,modules:["pump"],options:["1 duty + 1 standby","2 duty + 1 standby","Duty paralel","Pompa seri/multistage","Ditentukan engineer"]},{id:"pump_control",category:"Konfigurasi",question:"Metode kontrol",required:true,modules:["pump"],options:["VFD + pressure sensor","Constant speed","Level control","Duty alternation"]},{id:"pump_room",category:"Koordinasi",question:"Ruang pompa, akses maintenance dan drainage",required:true,modules:["pump"]},
  {id:"hyd_hazard",category:"Fire Strategy",question:"Klasifikasi bahaya dan fungsi bangunan",required:true,modules:["hydrant"],options:["Light hazard","Ordinary hazard / gedung umum","High hazard","Special hazard"]},{id:"hyd_system",category:"Fire Strategy",question:"Jenis sistem hidran",required:true,modules:["hydrant"],options:["Indoor hydrant + hose reel","Indoor standpipe/hydrant","Outdoor yard hydrant","Combined indoor + outdoor"]},{id:"hyd_area",category:"Bangunan",question:"Luas total yang dilindungi (m²)",required:true,modules:["hydrant"]},{id:"hyd_floors",category:"Bangunan",question:"Jumlah lantai dilayani",required:true,modules:["hydrant"]},{id:"hyd_flow",category:"Fire Flow",question:"Debit setiap outlet (L/s)",required:true,modules:["hydrant"]},{id:"hyd_simultaneous",category:"Fire Flow",question:"Jumlah outlet simultan",required:true,modules:["hydrant"]},{id:"hyd_duration",category:"Fire Flow",question:"Durasi operasi (menit)",required:true,modules:["hydrant"]},{id:"hyd_tank",category:"Reservoir",question:"Volume fire-water tank tersedia (m³)",required:true,modules:["hydrant"]},{id:"hyd_static",category:"Jaringan",question:"Head statis node tertinggi (m)",required:true,modules:["hydrant"]},{id:"hyd_residual",category:"Jaringan",question:"Target tekanan residual (mH₂O)",required:true,modules:["hydrant"]},{id:"hyd_pipe",category:"Jaringan",question:"Panjang, material dan diameter jalur terjauh",required:true,modules:["hydrant"]},{id:"hyd_zones",category:"Zoning & FDC",question:"Jumlah pressure zone",required:true,modules:["hydrant"]},{id:"hyd_fdc",category:"Zoning & FDC",question:"Jumlah/lokasi FDC dan akses mobil pemadam",required:true,modules:["hydrant"]},{id:"hyd_room",category:"Koordinasi",question:"Fire-pump room, sumber listrik darurat dan drainage",required:true,modules:["hydrant"]},
  {id:"plumb_source",category:"Plumbing",question:"Sumber air, kualitas dan kapasitas sambungan",required:true,modules:["plumbing"]},{id:"plumb_demand",category:"Plumbing",question:"Populasi dan kebutuhan air per orang",required:true,modules:["plumbing"]},{id:"plumb_storage",category:"Plumbing",question:"Target durasi cadangan dan lokasi tangki",required:true,modules:["plumbing"]},
  {id:"elec_supply",category:"Kelistrikan",question:"Sumber, tegangan, fasa dan kapasitas utilitas",required:true,modules:["electrical"]},{id:"elec_loads",category:"Kelistrikan",question:"Daftar beban utama dan faktor kebutuhan",required:true,modules:["electrical"]},{id:"elec_room",category:"Kelistrikan",question:"Ruang panel, jalur feeder dan sistem pembumian",required:true,modules:["electrical"]},
  {id:"gen_connected",category:"Load Schedule",question:"Total beban terpasang gedung (kW)",required:true,modules:["genset"]},{id:"gen_emergency",category:"Load Schedule",question:"Persentase beban prioritas/darurat yang wajib dibackup (%)",required:true,modules:["genset"]},{id:"gen_demand",category:"Load Schedule",question:"Faktor kebutuhan/diversity beban darurat",required:true,modules:["genset"]},{id:"gen_priority",category:"Load Schedule",question:"Daftar beban life-safety, essential dan optional beserta urutan pelepasan",required:true,modules:["genset"]},
  {id:"gen_motor",category:"Starting",question:"Motor terbesar yang start dan metode starting (kW)",required:true,modules:["genset"]},{id:"gen_voltage",category:"Starting",question:"Batas voltage dip yang diterima peralatan (%)",required:true,modules:["genset"]},{id:"gen_sequence",category:"Starting",question:"Urutan starting motor, lift, pompa, HVAC dan UPS",required:true,modules:["genset"]},
  {id:"gen_location",category:"Site",question:"Elevasi lokasi (m dpl) dan suhu udara desain (°C)",required:true,modules:["genset"]},{id:"gen_fuel",category:"Fuel",question:"Target autonomy operasi (jam), jenis bahan bakar dan metode pengisian",required:true,modules:["genset"]},{id:"gen_tank",category:"Fuel",question:"Lokasi day tank/bulk tank, bundwall dan akses pengisian",required:true,modules:["genset"]},
  {id:"gen_room",category:"Ruang Genset",question:"Dimensi ruang bersih, akses pemasukan unit dan jalur maintenance",required:true,modules:["genset"]},{id:"gen_air",category:"Ruang Genset",question:"Posisi intake/discharge louver dan jalur udara pendingin",required:true,modules:["genset"]},{id:"gen_exhaust",category:"Ruang Genset",question:"Rute exhaust, panjang ekuivalen, silencer dan lokasi outlet",required:true,modules:["genset"]},{id:"gen_noise",category:"Akustik",question:"Target kebisingan batas ruang dan titik sensitif terdekat",required:true,modules:["genset"]},{id:"gen_vibration",category:"Akustik",question:"Sistem inertia block/foundation dan vibration isolator",required:true,modules:["genset"]},
  {id:"gen_system",category:"Kelistrikan",question:"Sistem ATS/AMF, sinkronisasi, netral dan pembumian",required:true,modules:["genset"]},{id:"gen_rating",category:"Kelistrikan",question:"Rating operasi yang dibutuhkan",required:true,modules:["genset"],options:["Emergency standby","Standby","Prime","Continuous"]},{id:"gen_redundancy",category:"Kelistrikan",question:"Konfigurasi redundansi",required:true,modules:["genset"],options:["1 × 100%","2 × 50% paralel","2 × 100% (N+1)","Multi-set sinkron","Ditentukan engineer"]},{id:"gen_vendor",category:"Vendor",question:"Merek/integrator yang diterima atau approved equal",required:true,modules:["genset"]},{id:"gen_service",category:"Vendor",question:"SLA layanan, spare part, load bank test dan commissioning",required:true,modules:["genset"]},
  {id:"ac_rooms",category:"Ruang",question:"Daftar ruang, fungsi, luas, tinggi dan jam operasional",required:true,modules:["ac"]},{id:"ac_people",category:"Ruang",question:"Jumlah penghuni desain dan pola okupansi",required:true,modules:["ac"]},{id:"ac_zoning",category:"Ruang",question:"Zoning berdasarkan orientasi, jadwal dan fungsi ruang",required:true,modules:["ac"]},
  {id:"ac_outdoor",category:"Kondisi Desain",question:"Suhu DB/WB atau DB/RH luar desain lokasi",required:true,modules:["ac"]},{id:"ac_indoor",category:"Kondisi Desain",question:"Target suhu dan kelembapan dalam",required:true,modules:["ac"]},{id:"ac_pressurization",category:"Kondisi Desain",question:"Kebutuhan tekanan positif/negatif ruang khusus",modules:["ac"]},
  {id:"ac_envelope",category:"Selubung",question:"Orientasi, luas dan U-value dinding/atap/lantai",required:true,modules:["ac"]},{id:"ac_glass",category:"Selubung",question:"Luas, jenis, U-value/SHGC kaca dan shading",required:true,modules:["ac"]},{id:"ac_internal",category:"Beban Internal",question:"Daya lampu, peralatan dan beban proses per ruang",required:true,modules:["ac"]},
  {id:"ac_fresh",category:"Udara Luar",question:"Kebutuhan udara segar berdasarkan orang dan fungsi ruang",required:true,modules:["ac"]},{id:"ac_infiltration",category:"Udara Luar",question:"Infiltrasi dari pintu, facade dan tekanan angin",required:true,modules:["ac"]},{id:"ac_exhaust",category:"Udara Luar",question:"Exhaust toilet, pantry, parkir dan ruang khusus",required:true,modules:["ac"]},
  {id:"ac_system",category:"Sistem",question:"Preferensi sistem atau batasan owner",required:true,modules:["ac"],options:["Split/DX","Multi-split","VRF/VRV","Chilled water","Package/AHU","Evaluasi engineer"]},{id:"ac_redundancy",category:"Sistem",question:"Kebutuhan redundansi untuk ruang kritis",required:true,modules:["ac"]},{id:"ac_energy",category:"Sistem",question:"Target efisiensi energi, COP/IPLV dan BMS",required:true,modules:["ac"]},
  {id:"ac_ceiling",category:"Koordinasi",question:"Ruang ceiling, shaft duct/pipa dan lokasi equipment",required:true,modules:["ac"]},{id:"ac_outdoorunit",category:"Koordinasi",question:"Lokasi outdoor unit/cooling tower/chiller dan akses maintenance",required:true,modules:["ac"]},{id:"ac_drain",category:"Koordinasi",question:"Jalur drain kondensat, trap, slope dan titik pembuangan",required:true,modules:["ac"]},{id:"ac_noise",category:"Koordinasi",question:"Target NC/kebisingan dan ruang sensitif",required:true,modules:["ac"]},{id:"ac_vendor",category:"Vendor",question:"Merek yang diterima atau approved equal",required:true,modules:["ac"]},{id:"ac_submittal",category:"Vendor",question:"Data kapasitas pada kondisi desain, sound, power, control dan warranty",required:true,modules:["ac"]},
  {id:"light_rooms",category:"Ruang",question:"Daftar ruang, fungsi, dimensi, tinggi plafon dan bidang kerja",required:true,modules:["lighting"]},{id:"light_lux",category:"Kriteria Visual",question:"Target lux, uniformity, CRI dan temperatur warna per ruang",required:true,modules:["lighting"]},{id:"light_ceiling",category:"Arsitektur",question:"Jenis plafon, modul ceiling dan reflektansi permukaan",required:true,modules:["lighting"]},{id:"light_fixture",category:"Armatur",question:"Jenis armatur, lumen, watt, photometric file dan approved equal",required:true,modules:["lighting"]},{id:"light_control",category:"Kontrol",question:"Zonasi sakelar, occupancy sensor, dimming, daylight dan BMS",required:true,modules:["lighting"]},{id:"light_emergency",category:"Keselamatan",question:"Rute evakuasi, emergency lighting, exit sign dan sumber darurat",required:true,modules:["lighting"]},{id:"light_schedule",category:"Energi",question:"Jam operasi, target LPD dan sasaran penghematan energi",required:true,modules:["lighting"]},{id:"light_coordination",category:"Koordinasi",question:"Koordinasi sprinkler, diffuser, detector, signage dan akses pemeliharaan",required:true,modules:["lighting"]},
  {id:"fire_occupancy",category:"Fire Strategy",question:"Fungsi, klasifikasi hunian, jumlah lantai, luas dan tingkat risiko kebakaran",required:true,modules:["fireSafety"]},{id:"fire_ahj",category:"Fire Strategy",question:"Ketentuan dinas pemadam/AHJ, regulasi daerah dan standar kontrak",required:true,modules:["fireSafety"]},{id:"fire_compartment",category:"Fire Strategy",question:"Kompartemen, fire rating, shaft, atrium dan area khusus",required:true,modules:["fireSafety"]},{id:"fire_detection",category:"Deteksi",question:"Jenis detector per ruang: smoke, heat, flame, aspirating atau gas",required:true,modules:["fireSafety"]},{id:"fire_alarm",category:"Alarm",question:"Zoning alarm, voice evacuation, strobe dan target audibility",required:true,modules:["fireSafety"]},{id:"fire_cause",category:"Integrasi",question:"Cause-and-effect untuk lift, HVAC, smoke control, door release, genset dan BMS",required:true,modules:["fireSafety"]},{id:"fire_sprinkler",category:"Suppression",question:"Hazard classification, density-area dan jenis sprinkler",required:true,modules:["fireSafety"]},{id:"fire_special",category:"Suppression",question:"Area yang memerlukan clean agent, kitchen hood, foam atau water mist",modules:["fireSafety"]},{id:"fire_water",category:"Water Supply",question:"Sumber air, kapasitas reservoir, durasi dan hasil flow test",required:true,modules:["fireSafety"]},{id:"fire_pump",category:"Water Supply",question:"Konfigurasi main, standby dan jockey pump serta sumber daya",required:true,modules:["fireSafety"]},{id:"fire_document",category:"DED",question:"Riser, layout, hydraulic calculation, matrix I/O, BOQ dan test plan yang diwajibkan",required:true,modules:["fireSafety"]},{id:"fire_vendor",category:"Vendor",question:"Approved equal, listing/sertifikasi, layanan dan spare part Indonesia",required:true,modules:["fireSafety"]},
  {id:"comm_users",category:"Pengguna",question:"Jumlah pengguna, workstation, area umum dan proyeksi pertumbuhan",required:true,modules:["communications"]},{id:"comm_service",category:"Layanan",question:"Layanan yang dibutuhkan: voice, SIP, hotline, operator, voicemail, recording dan fax",required:true,modules:["communications"]},{id:"comm_provider",category:"Operator",question:"Operator telekomunikasi, jenis trunk, demarcation point dan SLA",required:true,modules:["communications"]},{id:"comm_numbering",category:"PABX",question:"Numbering plan, concurrent call, call routing dan redundancy PABX",required:true,modules:["communications"]},{id:"comm_outlet",category:"Cabling",question:"Jumlah outlet telepon/data per ruang dan kategori kabel",required:true,modules:["communications"]},{id:"comm_backbone",category:"Cabling",question:"Topologi backbone, pair/core, jalur riser dan cadangan",required:true,modules:["communications"]},{id:"comm_room",category:"Ruang ICT",question:"MDF/IDF, rack, cooling, grounding, akses dan fire protection",required:true,modules:["communications"]},{id:"comm_intercom",category:"Interkom",question:"Lokasi master/station, mode duplex dan integrasi security/lift",required:true,modules:["communications"]},{id:"comm_radio",category:"Radio",question:"Jumlah pengguna HT, kanal, repeater, coverage dan izin frekuensi",required:true,modules:["communications"]},{id:"comm_ups",category:"Daya",question:"Daya perangkat, autonomy UPS, sumber darurat dan monitoring",required:true,modules:["communications"]},{id:"comm_security",category:"Keamanan",question:"QoS, VLAN, cybersecurity, recording retention dan akses administrasi",required:true,modules:["communications"]},{id:"comm_vendor",category:"Vendor",question:"Approved equal, sertifikasi installer, warranty dan spare part Indonesia",required:true,modules:["communications"]},
];

function n(v: number | string) { return Number(v) || 0; }
function fmt(v: number, d = 2) { return new Intl.NumberFormat("id-ID", { maximumFractionDigits: d }).format(v); }
function rupiah(v: number) { return new Intl.NumberFormat("id-ID", { style:"currency", currency:"IDR", maximumFractionDigits:0 }).format(v); }

function calculate(module: Module, d: Record<string, number | string>) {
  if (module === "lift") {
    const zones=Math.max(1,n(d.zones)), floorsPerZone=Math.ceil(n(d.floors)/zones), speed=Math.max(.1,n(d.speed));
    const travel=(floorsPerZone-1)*n(d.floorHeight); const T=((2*n(d.floorHeight)+4*speed)*(floorsPerZone-1)+speed*(3*n(d.capacity)+4))/speed;
    const totalLifts=zones*n(d.units), wait=T/n(d.units), handling=300*n(d.capacity)*n(d.units)/T;
    const population=n(d.area)*n(d.floors)/Math.max(.1,n(d.occupancyArea)), peakLoad=population*n(d.peak)/100/zones;
    const coreArea=totalLifts*n(d.shaftArea), buildingEfficiency=Math.max(0,(n(d.area)-coreArea)/Math.max(1,n(d.area))*100);
    const recommendedSpeed=travel<=20?1:travel<=30?1.5:travel<=45?2.5:travel<=60?3.5:travel<=120?5:6;
    const ratedLoad=n(d.capacity)*75, motorPower=ratedLoad*9.81*speed*(1-n(d.counterweightFactor)/100)/(Math.max(1,n(d.motorEfficiency))/100)/1000;
    const operatingDemand=motorPower*totalLifts*n(d.dutyFactor)/100;
    const cabinDensity=ratedLoad/Math.max(.1,n(d.cabinArea));
    const activeEnergy=operatingDemand*n(d.operatingHours)*n(d.operatingDays)*(1-n(d.regenSaving)/100);
    const standbyEnergy=n(d.idlePower)*totalLifts*24*n(d.operatingDays); const annualEnergy=activeEnergy+standbyEnergy;
    const annualEnergyCost=annualEnergy*n(d.energyTariff); const annualOpex=annualEnergyCost+n(d.maintenanceAnnual);
    const rate=n(d.discountRate)/100, years=n(d.analysisYears); const pvFactor=rate>0?(1-Math.pow(1+rate,-years))/rate:years;
    const lifeCycleCost=n(d.installationCost)+annualOpex*pvFactor;
    return { "Lantai per zona":floorsPerZone, "Tinggi perjalanan zona":travel, "Jumlah lift total":totalLifts, "Round trip time":T, "Waktu tunggu":wait, "Daya angkut 5 menit/zona":handling, "Beban puncak/zona":peakLoad, "Rasio layanan":handling/Math.max(.1,peakLoad)*100, "Kecepatan rekomendasi":recommendedSpeed, "Luas core hoistway":coreArea, "Efisiensi lantai":buildingEfficiency, "Daya motor per lift":motorPower, "Demand operasi lift":operatingDemand, "Kepadatan beban kabin":cabinDensity, "Energi tahunan":annualEnergy, "Biaya energi tahunan":annualEnergyCost, "Life cycle cost":lifeCycleCost };
  }
  if(module==="escalator"){
    const units=Math.max(1,n(d.units)), angleRad=n(d.angle)*Math.PI/180, slopeLength=n(d.rise)/Math.max(.1,Math.sin(angleRad));
    const theoreticalPerUnit=3600*n(d.speed)/Math.max(.05,n(d.stepPitch))*n(d.stepOccupancy);
    const practicalPerUnit=theoreticalPerUnit*Math.min(1,Math.max(.01,n(d.capacityFactor)/100));
    const requiredUp=Math.ceil(n(d.peakFlowUp)/Math.max(1,practicalPerUnit)), requiredDown=Math.ceil(n(d.peakFlowDown)/Math.max(1,practicalPerUnit));
    const activeRequired=requiredUp+requiredDown, totalRequired=activeRequired+Math.max(0,Math.ceil(n(d.reserveUnits)));
    const theoretical=theoreticalPerUnit*units, practical=practicalPerUnit*units, massFlow=practical/3600*75, fullPower=massFlow*9.81*n(d.rise)/(Math.max(1,n(d.efficiency))/100)/1000*1.15;
    const demandPower=fullPower*n(d.demandFactor)/100, activeEnergy=demandPower*n(d.operatingHours)*n(d.operatingDays), standby=n(d.standbyPower)*units*(24-n(d.operatingHours))*n(d.operatingDays);
    const annualEnergy=activeEnergy+standby, annualEnergyCost=annualEnergy*n(d.energyTariff), annualOpex=annualEnergyCost+n(d.maintenanceAnnual);
    const rate=n(d.discountRate)/100, years=n(d.analysisYears), pv=rate>0?(1-Math.pow(1+rate,-years))/rate:years, lcc=n(d.installationCost)+annualOpex*pv;
    return {"Panjang miring":slopeLength,"Kapasitas teoritis per unit":theoreticalPerUnit,"Kapasitas praktis per unit":practicalPerUnit,"Kebutuhan arah naik":requiredUp,"Kebutuhan arah turun":requiredDown,"Unit aktif minimum":activeRequired,"Jumlah unit minimum":totalRequired,"Rasio kecukupan unit":units/Math.max(1,totalRequired)*100,"Kapasitas praktis total":practical,"Daya beban penuh":fullPower,"Demand operasi":demandPower,"Energi tahunan":annualEnergy,"Biaya energi tahunan":annualEnergyCost,"Life cycle cost":lcc};
  }
  if (module === "pump") {
    const q = n(d.flow)/3600, D=n(d.diameter)/1000, velocity=q/(Math.PI*D*D/4);
    const reynolds=n(d.density)*velocity*D/(Math.max(.001,n(d.viscosity))/1000);
    const major=n(d.friction)*(n(d.length)/D)*(velocity*velocity/(2*9.81)); const minor=n(d.minorK)*(velocity*velocity/(2*9.81));
    const head=n(d.staticHead)+n(d.pressureHead)+major+minor, duty=Math.max(1,Math.ceil(n(d.dutyPumps))), series=Math.max(1,Math.ceil(n(d.seriesPumps)));
    const flowPerPump=n(d.flow)/duty, headPerPump=head/series, qPerPump=flowPerPump/3600;
    const hydraulic=n(d.density)*9.81*qPerPump*headPerPump/1000, power=hydraulic/(n(d.efficiency)/100), motor=power*(1+n(d.serviceFactor)/100);
    const minFlow=flowPerPump*n(d.minimumFlow)/100, energy=power*duty*series*n(d.operatingHours)*n(d.operatingDays), energyCost=energy*n(d.energyTariff);
    const criticalClosure=2*n(d.length)/Math.max(1,n(d.waveSpeed));
    const surgeHead=n(d.closureTime)<=criticalClosure?n(d.waveSpeed)*velocity/9.81:2*n(d.length)*velocity/(9.81*Math.max(.1,n(d.closureTime)));
    const maxHead=(head+surgeHead)*(1+n(d.designPressureMargin)/100), maxPressure=maxHead*n(d.density)*9.81/100000;
    const specificSpeed=n(d.rpm)*Math.sqrt(Math.max(.0001,qPerPump))/Math.pow(Math.max(.1,headPerPump),.75);
    const staticPerStage=(n(d.staticHead)+n(d.pressureHead))/series, frictionPerStage=(major+minor)/series;
    const shutoffHead=headPerPump*n(d.shutoffFactor)/100, pumpCurveK=(shutoffHead-headPerPump)/Math.max(.0001,flowPerPump*flowPerPump), systemCurveK=frictionPerStage/Math.max(.0001,flowPerPump*flowPerPump);
    const operatingFlow=Math.sqrt(Math.max(0,(shutoffHead-staticPerStage)/Math.max(.000001,pumpCurveK+systemCurveK)));
    const operatingHead=staticPerStage+systemCurveK*operatingFlow*operatingFlow, dutyDeviation=Math.abs(operatingFlow-flowPerPump)/Math.max(.1,flowPerPump)*100;
    return { "Kecepatan aliran": velocity,"Bilangan Reynolds":reynolds, "Kerugian mayor": major, "Kerugian minor": minor, "Total dynamic head": head,"Debit per pompa duty":flowPerPump,"Head per pompa seri":headPerPump,"Minimum flow per pompa":minFlow,"Operating flow kurva":operatingFlow,"Operating head kurva":operatingHead,"Deviasi operating point":dutyDeviation,"Head shut-off":shutoffHead,"Daya hidraulik per pompa":hydraulic, "Daya poros per pompa": power,"Daya motor desain per pompa":motor,"Jumlah pompa terpasang":duty*series+Math.max(0,Math.ceil(n(d.standbyPumps))),"Margin NPSH": n(d.npsha)-n(d.npshr),"Waktu penutupan kritis":criticalClosure,"Surge head estimasi":surgeHead,"Tekanan desain maksimum":maxPressure,"Specific speed indeks":specificSpeed,"Energi tahunan":energy,"Biaya energi tahunan":energyCost };
  }
  if (module === "hydrant") {
    const flow=n(d.hydrants)*n(d.flowPerHydrant); const volume=flow*n(d.duration)*60/1000*(1+n(d.tankReserve)/100);
    const q=flow/1000, diameter=Math.max(.025,n(d.selectedDiameter)/1000), frictionLoss=10.67*n(d.networkLength)*Math.pow(q,1.852)/(Math.pow(Math.max(1,n(d.hazenC)),1.852)*Math.pow(diameter,4.87));
    const minorLoss=frictionLoss*n(d.minorLossPercent)/100, head=n(d.staticHead)+n(d.residualPressure)+frictionLoss+minorLoss; const power=1000*9.81*q*head/(n(d.efficiency)/100)/1000, motor=power*(1+n(d.serviceFactor)/100);
    const mainDiameter=Math.sqrt(4*(flow/1000)/(Math.PI*Math.max(.5,n(d.maxVelocity))))*1000;
    const coverageArea=Math.PI*n(d.coverageRadius)*n(d.coverageRadius), pointsPerFloor=Math.ceil((n(d.buildingArea)/Math.max(1,n(d.floors)))/Math.max(1,coverageArea)), totalPoints=pointsPerFloor*Math.max(1,n(d.floors));
    const tankRatio=n(d.availableTank)/Math.max(.1,volume)*100, jockey=flow*n(d.jockeyPercent)/100, zoneHead=head/Math.max(1,n(d.zones)), velocity=q/(Math.PI*diameter*diameter/4);
    const reach=n(d.hoseLength)+n(d.nozzleThrow), reachRatio=reach/Math.max(1,n(d.maxTravelDistance))*100, remotePressure=head-n(d.staticHead)-frictionLoss-minorLoss;
    return { "Debit kebakaran":flow, "Volume reservoir minimum":volume,"Rasio kecukupan reservoir":tankRatio,"Titik hidran per lantai":pointsPerFloor,"Jumlah titik hidran":totalPoints,"Luas cakupan per titik":coverageArea,"Jangkauan selang + nozzle":reach,"Rasio kecukupan jangkauan":reachRatio,"Kerugian Hazen-Williams":frictionLoss,"Kerugian minor":minorLoss,"Kecepatan pipa terpilih":velocity,"Tekanan node terjauh":remotePressure, "Total dynamic head":head,"Head per zona":zoneHead,"Daya pompa minimum":power,"Daya motor fire pump":motor,"Diameter hidraulik minimum":mainDiameter,"Jockey pump":jockey,"Jumlah main fire pump":2,"Jumlah jockey pump":1,"FDC minimum":Math.max(1,n(d.fdcCount)),"Floor control valve set":Math.max(1,n(d.floorControlValves))*Math.max(1,n(d.floors)),"Zone isolation valve":Math.max(1,n(d.zones)) };
  }
  if (module === "plumbing") {
    const daily=n(d.occupants)*n(d.demandPerPerson)/1000; const average=daily/n(d.operationHours); const peak=average*n(d.peakFactor);
    const storage=daily*n(d.storageHours)/24; const roof=storage*n(d.roofTankShare)/100; const ground=storage-roof;
    const q=peak/3600, D=n(d.pipeDiameter)/1000, velocity=q/(Math.PI*D*D/4); const power=1000*9.81*q*n(d.staticHead)/(n(d.pumpEfficiency)/100)/1000;
    return { "Kebutuhan air harian":daily, "Debit rata-rata":average, "Debit jam puncak":peak, "Reservoir bawah":ground, "Tangki atap":roof, "Kecepatan pipa utama":velocity, "Daya pompa transfer":power };
  }
  if (module === "electrical") {
    const connected=n(d.lightingLoad)+n(d.socketLoad)+n(d.motorLoad)+n(d.acLoad)+n(d.otherLoad);
    const demand=connected*n(d.demandFactor), design=demand*(1+n(d.spare)/100), kva=design/n(d.powerFactor);
    const current=n(d.phases)===3 ? design*1000/(Math.sqrt(3)*n(d.voltage)*n(d.powerFactor)) : design*1000/(n(d.voltage)*n(d.powerFactor));
    const totalCorrection=Math.max(.1,n(d.temperatureFactor)*n(d.groupingFactor)*n(d.installationFactor)), correctedCurrent=current/totalCorrection;
    const breakers=[6,10,16,20,25,32,40,50,63,80,100,125,160,200,250,320,400,500,630,800,1000,1250,1600];
    const breaker=breakers.find(x=>x>=current*1.25) || Math.ceil(current*1.25/100)*100;
    const cableSizes=[1.5,2.5,4,6,10,16,25,35,50,70,95,120,150,185,240,300,400,500,630];
    const thermalArea=n(d.faultCurrent)*1000*Math.sqrt(n(d.clearingTime))/Math.max(1,n(d.kFactor));
    const ampacityArea=correctedCurrent/2.4, requiredArea=Math.max(ampacityArea,thermalArea), cable=cableSizes.find(x=>x>=requiredArea) || 630;
    const drop=(n(d.phases)===3?Math.sqrt(3):2)*current*n(d.cableLength)*0.0225/(cable*n(d.voltage))*100;
    const maxEarthResistance=n(d.allowableTouchVoltage)/Math.max(1,n(d.earthFaultCurrent));
    const rodD=n(d.earthRodDiameter)/1000,rodL=Math.max(.5,n(d.earthRodLength)),singleRod=n(d.soilResistivity)/(2*Math.PI*rodL)*(Math.log(4*rodL/Math.max(.001,rodD))-1);
    const earthResistance=singleRod/Math.max(1,n(d.earthRodCount))*1.3,earthAdequacy=maxEarthResistance/Math.max(.001,earthResistance)*100;
    const L=n(d.buildingLength),W=n(d.buildingWidth),H=n(d.buildingHeight),collectionArea=L*W+6*H*(L+W)+9*Math.PI*H*H;
    const perimeter=2*(L+W),downSpacing=perimeter/Math.max(2,n(d.lightningDownConductors));
    return { "Beban terpasang":connected, "Beban kebutuhan":demand, "Daya desain + cadangan":design, "Daya semu":kva, "Arus desain":current,"Faktor koreksi total":totalCorrection,"KHA minimum terkoreksi":correctedCurrent, "Rating pengaman awal":breaker,"Luas minimum termal hubung singkat":thermalArea,"Luas minimum berbasis KHA":ampacityArea, "Luas kabel Cu terpilih":cable, "Jatuh tegangan estimasi":drop,"Batas jatuh tegangan":n(d.allowableDrop),"Tahanan grounding maksimum":maxEarthResistance,"Tahanan 1 elektroda":singleRod,"Tahanan grounding estimasi":earthResistance,"Rasio kecukupan grounding":earthAdequacy,"Equivalent collection area petir":collectionArea,"Jarak down conductor":downSpacing,"Kelas proteksi petir":Math.min(4,Math.max(1,n(d.lightningProtectionLevel))) };
  }
  if(module==="lighting"){
    const length=Math.max(.1,n(d.roomLength)),width=Math.max(.1,n(d.roomWidth)),area=length*width;
    const mountingHeight=Math.max(.1,n(d.roomHeight)-n(d.workPlane));
    const roomIndex=area/(mountingHeight*(length+width));
    const uf=Math.max(.01,n(d.utilizationFactor)),mf=Math.max(.01,n(d.maintenanceFactor));
    const requiredLumens=n(d.targetLux)*area/(uf*mf);
    const countByLux=Math.ceil(requiredLumens/Math.max(1,n(d.lumensPerFixture)));
    const maxSpacing=mountingHeight*Math.max(.1,n(d.spacingHeightRatio));
    const rows=Math.max(1,Math.ceil(width/maxSpacing)),columns=Math.max(1,Math.ceil(length/maxSpacing));
    const selected=Math.max(countByLux,rows*columns);
    const installedLux=selected*n(d.lumensPerFixture)*uf*mf/area;
    const uniformity=n(d.minimumLux)/Math.max(1,installedLux);
    const minMax=n(d.minimumLux)/Math.max(1,n(d.maximumLux));
    const installedPower=selected*n(d.fixturePower)/1000, lpd=installedPower*1000/area;
    const annualEnergy=installedPower*n(d.operatingHours)*n(d.operatingDays)*(1-Math.min(100,Math.max(0,n(d.controlSaving)))/100);
    const annualCost=annualEnergy*n(d.energyTariff), emergency=Math.ceil(selected*Math.max(0,n(d.emergencyShare))/100);
    return {"Luas ruang":area,"Tinggi pemasangan efektif":mountingHeight,"Room index":roomIndex,"Lumen total diperlukan":requiredLumens,"Armatur berdasarkan lux":countByLux,"Baris armatur":rows,"Kolom armatur":columns,"Jumlah armatur terpilih":selected,"Jarak maksimum awal":maxSpacing,"Iluminansi terpasang":installedLux,"Rasio uniformity Emin/Eavg":uniformity,"Rasio uniformity Emin/Emax":minMax,"Daya pencahayaan terpasang":installedPower,"Lighting power density":lpd,"Armatur darurat awal":emergency,"Energi tahunan":annualEnergy,"Biaya energi tahunan":annualCost,"Penghematan energi kontrol":installedPower*n(d.operatingHours)*n(d.operatingDays)-annualEnergy};
  }
  if(module==="fireSafety"){
    const totalArea=n(d.floorArea)*Math.max(1,n(d.floors));
    const smoke=Math.ceil(totalArea*n(d.smokeShare)/100/Math.max(1,n(d.smokeCoverage)));
    const heat=Math.ceil(totalArea*n(d.heatShare)/100/Math.max(1,n(d.heatCoverage)));
    const flame=Math.ceil(totalArea*n(d.flameShare)/100/Math.max(1,n(d.flameCoverage)));
    const mcp=Math.ceil(n(d.manualCallPointsPerFloor)*n(d.floors)),sounders=Math.ceil(n(d.soundersPerFloor)*n(d.floors)),strobes=Math.ceil(n(d.strobesPerFloor)*n(d.floors));
    const initiating=smoke+heat+flame+mcp,totalDevices=initiating+sounders+strobes;
    const zones=Math.ceil(n(d.floors)/Math.max(1,n(d.floorsPerZone))),loops=Math.ceil(initiating/Math.max(1,n(d.loopCapacity)));
    const battery=(n(d.standbyCurrent)*n(d.standbyHours)+n(d.alarmCurrent)*n(d.alarmHours))*(1+n(d.batteryMargin)/100);
    const sprinklers=Math.ceil(totalArea/Math.max(1,n(d.sprinklerCoverage)));
    const remoteHeads=Math.ceil(n(d.designArea)/Math.max(1,n(d.sprinklerCoverage)));
    const sprinklerFlow=n(d.sprinklerDensity)*n(d.designArea)/60;
    const totalFlow=sprinklerFlow+n(d.hoseDemand),volume=totalFlow*n(d.suppressionDuration)*60/1000*(1+n(d.waterReserve)/100);
    const adequacy=n(d.availableWater)/Math.max(.1,volume)*100;
    const diameter=Math.sqrt(4*(totalFlow/1000)/(Math.PI*Math.max(.1,n(d.pipeVelocity))))*1000;
    const assumedHead=70,pumpPower=1000*9.81*(totalFlow/1000)*assumedHead/(Math.max(1,n(d.pumpEfficiency))/100)/1000;
    const motor=pumpPower*(1+n(d.serviceFactor)/100);
    return {"Luas proteksi total":totalArea,"Smoke detector":smoke,"Heat detector":heat,"Flame detector":flame,"Manual call point":mcp,"Sounder":sounders,"Strobe":strobes,"Initiating devices":initiating,"Total addressable devices":totalDevices,"Zona alarm minimum":zones,"Loop minimum":loops,"Kapasitas baterai minimum":battery,"Jumlah sprinkler total":sprinklers,"Sprinkler remote area":remoteHeads,"Debit sprinkler remote":sprinklerFlow,"Debit suppression total":totalFlow,"Volume water supply minimum":volume,"Rasio kecukupan air":adequacy,"Diameter pipa utama awal":diameter,"Daya pompa awal":pumpPower,"Daya motor pompa awal":motor};
  }
  if(module==="communications"){
    const baseExtensions=n(d.workstations)*n(d.extensionsPerWorkstation)+n(d.commonExtensions);
    const extensions=Math.ceil(baseExtensions*(1+n(d.extensionGrowth)/100));
    const concurrentCalls=Math.ceil(extensions*n(d.concurrentCallPercent)/100);
    const trunkChannels=Math.ceil(concurrentCalls/Math.max(.01,n(d.trunkUtilization)/100));
    const priCircuits=Math.ceil(trunkChannels/Math.max(1,n(d.channelsPerPri)));
    const pbxSizes=[16,32,64,128,256,512,1024,2048];
    const pbxCapacity=pbxSizes.find(x=>x>=extensions)||Math.ceil(extensions/512)*512;
    const totalArea=n(d.floorArea)*Math.max(1,n(d.floors));
    const baseOutlets=Math.ceil(totalArea/100*n(d.outletsPer100sqm));
    const outlets=Math.ceil(baseOutlets*(1+n(d.spareOutlets)/100));
    const horizontalCable=outlets*n(d.averageHorizontalCable)*1.1;
    const maxHorizontal=n(d.averageHorizontalCable)*1.1;
    const backbonePairs=Math.ceil(extensions*n(d.cablePairsPerExtension)*(1+n(d.backboneReserve)/100));
    const pairCables=Math.ceil(backbonePairs/100);
    const intercomPerMaster=Math.ceil(n(d.intercomStations)/Math.max(1,n(d.intercomMasters)));
    const usersPerChannel=n(d.htUsers)/Math.max(1,n(d.htChannels));
    const rackU=Math.ceil(n(d.rackDevices)*n(d.rackUnitsPerDevice)*(1+n(d.rackReserve)/100));
    const rackSizes=[12,20,27,42,45]; const racks=Math.max(1,Math.ceil(rackU/45));
    const selectedRack=racks===1?(rackSizes.find(x=>x>=rackU)||45):45;
    const loadW=n(d.rackDevices)*n(d.devicePower),upsVA=loadW/Math.max(.1,n(d.powerFactor))/Math.max(.01,n(d.upsEfficiency)/100)*1.25;
    const batteryWh=loadW*n(d.upsRuntime)/60/Math.max(.01,n(d.upsEfficiency)/100)*1.25;
    return {"Extension dasar":baseExtensions,"Extension desain":extensions,"Concurrent calls":concurrentCalls,"SIP/trunk channels minimum":trunkChannels,"PRI/E1 circuits":priCircuits,"Kapasitas PABX terpilih":pbxCapacity,"Total area layanan":totalArea,"Outlet telekomunikasi":outlets,"Panjang kabel horizontal":horizontalCable,"Channel horizontal maksimum":maxHorizontal,"Backbone copper pairs":backbonePairs,"Kabel backbone 100-pair":pairCables,"Interkom per master":intercomPerMaster,"Pengguna HT per kanal":usersPerChannel,"Kebutuhan rack total":rackU,"Jumlah rack":racks,"Ukuran rack terpilih":selectedRack,"Beban perangkat ICT":loadW,"Kapasitas UPS minimum":upsVA,"Energi baterai minimum":batteryWh};
  }
  if (module === "genset") {
    const emergency=n(d.connectedLoad)*n(d.emergencyShare)/100, demand=emergency*n(d.demandFactor), expanded=demand*(1+n(d.future)/100);
    const motorRunning=Math.min(expanded,n(d.largestMotor)), motorExtra=motorRunning*Math.max(0,n(d.motorStartMultiplier)-1), transientKw=expanded+motorExtra;
    const baseKva=expanded/Math.max(.1,n(d.powerFactor)), transientKva=transientKw/Math.max(.1,n(d.powerFactor));
    const altitudeDerate=Math.max(0,(n(d.altitude)-1000)/100*n(d.altitudeDerate)), tempDerate=Math.max(0,(n(d.ambientTemp)-40)*n(d.tempDerate)), derate=Math.min(35,altitudeDerate+tempDerate), required=Math.max(baseKva,transientKva)*(1+derate/100);
    const standards=[20,30,40,50,60,80,100,125,150,200,250,300,350,400,500,625,750,1000,1250,1500,2000];
    const selected=standards.find(x=>x>=required/Math.max(1,n(d.units))) || Math.ceil(required/Math.max(1,n(d.units))/250)*250;
    const installed=selected*Math.max(1,n(d.units)), utilization=baseKva/installed*100;
    const fuelHourly=expanded*n(d.fuelRate), fuelTank=fuelHourly*n(d.autonomy)*(1+n(d.tankReserve)/100);
    const ventilation=selected*Math.max(1,n(d.units))*n(d.airPerKw), louver=ventilation/Math.max(.5,n(d.maxAirVelocity));
    const roomVolume=n(d.roomLength)*n(d.roomWidth)*n(d.roomHeight), exhaustBackPressure=n(d.exhaustLength)*n(d.exhaustBackPressurePerM)+n(d.silencerLoss);
    const distanceLoss=20*Math.log10(Math.max(1,n(d.distance))), predictedNoise=n(d.soundSource)-distanceLoss-n(d.wallAttenuation), acousticGap=predictedNoise-n(d.targetNoise);
    const annualFuel=fuelHourly*n(d.operatingHours), annualGenerated=expanded*n(d.operatingHours), avoidedValue=annualGenerated*n(d.energyTariff);
    return {"Beban darurat terpasang":emergency,"Beban kebutuhan":demand,"Beban + pengembangan":expanded,"Kebutuhan steady-state":baseKva,"Kebutuhan saat starting":transientKva,"Faktor derating":derate,"Kebutuhan genset terkoreksi":required,"Kapasitas per unit":selected,"Kapasitas total terpasang":installed,"Utilisasi steady-state":utilization,"Konsumsi BBM per jam":fuelHourly,"Tangki BBM minimum":fuelTank,"Debit ventilasi":ventilation,"Luas efektif louver":louver,"Volume ruang genset":roomVolume,"Back pressure exhaust":exhaustBackPressure,"Kebisingan titik penerima":predictedNoise,"Selisih target kebisingan":acousticGap,"Konsumsi BBM tahunan":annualFuel,"Energi darurat tahunan":annualGenerated,"Nilai energi tahunan":avoidedValue};
  }
  const volume=n(d.area)*n(d.height), deltaT=Math.max(0,n(d.outdoorDb)-n(d.indoorDb));
  const pws=(t:number)=>.61078*Math.exp(17.2694*t/(t+237.29)), humidity=(t:number,rh:number)=>.62198*(rh/100*pws(t))/(101.325-rh/100*pws(t));
  const wOut=humidity(n(d.outdoorDb),n(d.outdoorRh)),wIn=humidity(n(d.indoorDb),n(d.indoorRh)),hOut=1.006*n(d.outdoorDb)+wOut*(2501+1.86*n(d.outdoorDb)),hIn=1.006*n(d.indoorDb)+wIn*(2501+1.86*n(d.indoorDb));
  const wall=n(d.wallU)*n(d.wallArea)*deltaT, roof=n(d.roofU)*n(d.roofArea)*deltaT, glassConduction=n(d.glassU)*n(d.windowArea)*deltaT, solar=n(d.solarGlass)*n(d.windowArea)*n(d.shadingFactor);
  const peopleSensible=n(d.people)*n(d.sensiblePerson),peopleLatent=n(d.people)*n(d.latentPerson),lighting=n(d.area)*n(d.lighting),equipment=n(d.equipment);
  const freshFlow=n(d.people)*n(d.freshAirPerson)/1000+volume*n(d.ach)/3600, infiltration=volume*n(d.infiltrationAch)/3600,totalOutside=freshFlow+infiltration;
  const outsideTotal=1.2*totalOutside*Math.max(0,hOut-hIn)*1000, outsideSensible=1.2*1.006*totalOutside*deltaT*1000, outsideLatent=Math.max(0,outsideTotal-outsideSensible);
  const sensible=wall+roof+glassConduction+solar+peopleSensible+lighting+equipment+outsideSensible,latent=peopleLatent+outsideLatent,total=(sensible+latent)*(1+n(d.safety)/100)*n(d.diversity);
  const shr=sensible/Math.max(1,sensible+latent),tr=total/3516.85,airflow=sensible/(1.2*1006*Math.max(1,n(d.supplyDeltaT))),ductArea=airflow/Math.max(1,n(d.ductVelocity)),chilledFlow=total/(4186*5),pipeDiameter=Math.sqrt(4*chilledFlow/(Math.PI*Math.max(.3,n(d.pipeVelocity))))*1000;
  const inputPower=total/1000/Math.max(1,n(d.cop)),annualEnergy=inputPower*n(d.operatingHours)*n(d.operatingDays),annualCost=annualEnergy*n(d.energyTariff),condensate=Math.max(0,1.2*totalOutside*(wOut-wIn)*3600);
  return {"Beban dinding":wall,"Beban atap":roof,"Konduksi kaca":glassConduction,"Radiasi matahari kaca":solar,"Beban penghuni sensibel":peopleSensible,"Beban penghuni laten":peopleLatent,"Beban pencahayaan":lighting,"Beban peralatan":equipment,"Udara luar sensibel":outsideSensible,"Udara luar laten":outsideLatent,"Beban sensibel ruang":sensible,"Beban laten ruang":latent,"Beban pendinginan desain":total,"Kapasitas pendinginan":total*3.412,"Ton refrigerasi":tr,"Sensible heat ratio":shr,"Debit supply air":airflow,"Luas duct utama awal":ductArea,"Debit chilled water awal":chilledFlow*1000,"Diameter pipa awal":pipeDiameter,"Kondensat estimasi":condensate,"Humidity ratio luar":wOut,"Humidity ratio dalam":wIn,"Enthalpy luar":hOut,"Enthalpy dalam":hIn,"Daya input sistem":inputPower,"Energi tahunan":annualEnergy,"Biaya energi tahunan":annualCost};
}

function units(module: Module, key: string) {
  const maps: Record<Module, Record<string,string>> = {
    lift: { "Lantai per zona":"lantai", "Tinggi perjalanan zona":"m", "Jumlah lift total":"unit", "Round trip time":"detik", "Waktu tunggu":"detik", "Daya angkut 5 menit/zona":"orang/5 menit", "Beban puncak/zona":"orang", "Rasio layanan":"%", "Kecepatan rekomendasi":"m/s", "Luas core hoistway":"m²", "Efisiensi lantai":"%", "Daya motor per lift":"kW", "Demand operasi lift":"kW", "Kepadatan beban kabin":"kg/m²", "Energi tahunan":"kWh/tahun", "Biaya energi tahunan":"Rp/tahun", "Life cycle cost":"Rp" },
    escalator:{"Panjang miring":"m","Kapasitas teoritis per unit":"orang/jam/unit","Kapasitas praktis per unit":"orang/jam/unit","Kebutuhan arah naik":"unit","Kebutuhan arah turun":"unit","Unit aktif minimum":"unit","Jumlah unit minimum":"unit","Rasio kecukupan unit":"%","Kapasitas praktis total":"orang/jam","Daya beban penuh":"kW","Demand operasi":"kW","Energi tahunan":"kWh/tahun","Biaya energi tahunan":"Rp/tahun","Life cycle cost":"Rp"},
    pump: { "Kecepatan aliran":"m/s","Bilangan Reynolds":"-", "Kerugian mayor":"m", "Kerugian minor":"m", "Total dynamic head":"m","Debit per pompa duty":"m³/jam","Head per pompa seri":"m","Minimum flow per pompa":"m³/jam","Operating flow kurva":"m³/jam","Operating head kurva":"m","Deviasi operating point":"%","Head shut-off":"m","Daya hidraulik per pompa":"kW", "Daya poros per pompa":"kW","Daya motor desain per pompa":"kW","Jumlah pompa terpasang":"unit", "Margin NPSH":"m","Waktu penutupan kritis":"detik","Surge head estimasi":"m","Tekanan desain maksimum":"bar","Specific speed indeks":"-","Energi tahunan":"kWh/tahun","Biaya energi tahunan":"Rp/tahun" },
    hydrant: { "Debit kebakaran":"L/s", "Volume reservoir minimum":"m³","Rasio kecukupan reservoir":"%","Titik hidran per lantai":"unit","Jumlah titik hidran":"unit","Luas cakupan per titik":"m²","Jangkauan selang + nozzle":"m","Rasio kecukupan jangkauan":"%","Kerugian Hazen-Williams":"m","Kerugian minor":"m","Kecepatan pipa terpilih":"m/s","Tekanan node terjauh":"mH₂O", "Total dynamic head":"m","Head per zona":"m", "Daya pompa minimum":"kW","Daya motor fire pump":"kW", "Diameter hidraulik minimum":"mm", "Jockey pump":"L/s","Jumlah main fire pump":"unit","Jumlah jockey pump":"unit","FDC minimum":"unit","Floor control valve set":"set","Zone isolation valve":"unit" },
    plumbing: { "Kebutuhan air harian":"m³/hari", "Debit rata-rata":"m³/jam", "Debit jam puncak":"m³/jam", "Reservoir bawah":"m³", "Tangki atap":"m³", "Kecepatan pipa utama":"m/s", "Daya pompa transfer":"kW" },
    electrical: { "Beban terpasang":"kW", "Beban kebutuhan":"kW", "Daya desain + cadangan":"kW", "Daya semu":"kVA", "Arus desain":"A","Faktor koreksi total":"-","KHA minimum terkoreksi":"A", "Rating pengaman awal":"A","Luas minimum termal hubung singkat":"mm²","Luas minimum berbasis KHA":"mm²", "Luas kabel Cu terpilih":"mm²", "Jatuh tegangan estimasi":"%","Batas jatuh tegangan":"%","Tahanan grounding maksimum":"Ω","Tahanan 1 elektroda":"Ω","Tahanan grounding estimasi":"Ω","Rasio kecukupan grounding":"%","Equivalent collection area petir":"m²","Jarak down conductor":"m","Kelas proteksi petir":"kelas" },
    lighting: {"Luas ruang":"m²","Tinggi pemasangan efektif":"m","Room index":"-","Lumen total diperlukan":"lm","Armatur berdasarkan lux":"unit","Baris armatur":"baris","Kolom armatur":"kolom","Jumlah armatur terpilih":"unit","Jarak maksimum awal":"m","Iluminansi terpasang":"lux","Rasio uniformity Emin/Eavg":"-","Rasio uniformity Emin/Emax":"-","Daya pencahayaan terpasang":"kW","Lighting power density":"W/m²","Armatur darurat awal":"unit","Energi tahunan":"kWh/tahun","Biaya energi tahunan":"Rp/tahun","Penghematan energi kontrol":"kWh/tahun"},
    fireSafety: {"Luas proteksi total":"m²","Smoke detector":"unit","Heat detector":"unit","Flame detector":"unit","Manual call point":"unit","Sounder":"unit","Strobe":"unit","Initiating devices":"device","Total addressable devices":"device","Zona alarm minimum":"zona","Loop minimum":"loop","Kapasitas baterai minimum":"Ah","Jumlah sprinkler total":"head","Sprinkler remote area":"head","Debit sprinkler remote":"L/s","Debit suppression total":"L/s","Volume water supply minimum":"m³","Rasio kecukupan air":"%","Diameter pipa utama awal":"mm","Daya pompa awal":"kW","Daya motor pompa awal":"kW"},
    communications: {"Extension dasar":"ext","Extension desain":"ext","Concurrent calls":"call","SIP/trunk channels minimum":"kanal","PRI/E1 circuits":"circuit","Kapasitas PABX terpilih":"port","Total area layanan":"m²","Outlet telekomunikasi":"titik","Panjang kabel horizontal":"m","Channel horizontal maksimum":"m","Backbone copper pairs":"pair","Kabel backbone 100-pair":"run","Interkom per master":"station/master","Pengguna HT per kanal":"orang/kanal","Kebutuhan rack total":"U","Jumlah rack":"rack","Ukuran rack terpilih":"U","Beban perangkat ICT":"W","Kapasitas UPS minimum":"VA","Energi baterai minimum":"Wh"},
    genset: {"Beban darurat terpasang":"kW","Beban kebutuhan":"kW","Beban + pengembangan":"kW","Kebutuhan steady-state":"kVA","Kebutuhan saat starting":"kVA","Faktor derating":"%","Kebutuhan genset terkoreksi":"kVA","Kapasitas per unit":"kVA","Kapasitas total terpasang":"kVA","Utilisasi steady-state":"%","Konsumsi BBM per jam":"L/jam","Tangki BBM minimum":"L","Debit ventilasi":"m³/s","Luas efektif louver":"m²","Volume ruang genset":"m³","Back pressure exhaust":"mbar","Kebisingan titik penerima":"dB(A)","Selisih target kebisingan":"dB","Konsumsi BBM tahunan":"L/tahun","Energi darurat tahunan":"kWh/tahun","Nilai energi tahunan":"Rp/tahun"},
    ac: {"Beban dinding":"W","Beban atap":"W","Konduksi kaca":"W","Radiasi matahari kaca":"W","Beban penghuni sensibel":"W","Beban penghuni laten":"W","Beban pencahayaan":"W","Beban peralatan":"W","Udara luar sensibel":"W","Udara luar laten":"W","Beban sensibel ruang":"W","Beban laten ruang":"W","Beban pendinginan desain":"W","Kapasitas pendinginan":"BTU/jam","Ton refrigerasi":"TR","Sensible heat ratio":"-","Debit supply air":"m³/s","Luas duct utama awal":"m²","Debit chilled water awal":"L/s","Diameter pipa awal":"mm","Kondensat estimasi":"kg/jam","Humidity ratio luar":"kg/kg","Humidity ratio dalam":"kg/kg","Enthalpy luar":"kJ/kg","Enthalpy dalam":"kJ/kg","Daya input sistem":"kW","Energi tahunan":"kWh/tahun","Biaya energi tahunan":"Rp/tahun"},
  }; return maps[module][key] || "";
}

function boq(module: Module, d: Record<string, number | string>, r: Record<string, number>) {
  if(module==="escalator") {const qty=Math.max(n(d.units),n(r["Jumlah unit minimum"]));return [[`Eskalator ${fmt(n(d.stepWidth),0)} mm, rise ${fmt(n(d.rise))} m`,qty,"unit"],["Panel kontrol VVVF dan proteksi",qty,"set"],["Balustrade, landing plate dan interface struktur",qty,"set"],["Testing, commissioning dan sertifikasi",1,"lot"]]};
  if(module==="pump") {const qty=n(r["Jumlah pompa terpasang"]);return [[`Pompa ${fmt(n(r["Debit per pompa duty"]))} m³/jam @ ${fmt(n(r["Head per pompa seri"]))} m`,qty,"unit"],[`Motor ${fmt(n(r["Daya motor desain per pompa"]))} kW`,qty,"unit"],["Panel kontrol/VFD dan proteksi",Math.max(1,n(d.dutyPumps)),"set"],["Valve, flexible joint, baseplate dan instrumen",qty,"set"],["Testing, commissioning dan performance test",1,"lot"]]};
  if(module==="electrical") return [["Panel distribusi utama lengkap metering dan proteksi",1,"set"],[`MCCB ${fmt(r["Rating pengaman awal"],0)} A, breaking capacity ≥ ${fmt(n(d.faultCurrent))} kA`,1,"unit"],[`Kabel Cu ${fmt(r["Luas kabel Cu terpilih"],0)} mm²`,n(d.cableLength),"m"],[`Elektroda grounding Ø${fmt(n(d.earthRodDiameter),0)} mm × ${fmt(n(d.earthRodLength))} m`,n(d.earthRodCount),"batang"],["Earth bar, bonding conductor, test link dan terminasi",1,"lot"],[`Air termination, down conductor ${fmt(n(d.lightningDownConductors),0)} jalur dan SPD`,1,"sistem"],["Pengujian insulation, continuity, earth resistance dan proteksi",1,"lot"]];
  if(module==="lighting") return [[`Armatur LED ${fmt(n(d.lumensPerFixture),0)} lm / ${fmt(n(d.fixturePower),0)} W`,n(r["Jumlah armatur terpilih"]),"unit"],["Emergency lighting lengkap battery/central system",n(r["Armatur darurat awal"]),"unit"],["Exit sign dan directional sign",1,"lot"],["Sakelar, dimmer, occupancy/daylight sensor dan control wiring",1,"lot"],["Kabel, conduit, junction box, support dan terminasi",1,"lot"],["Testing lux, functional test kontrol dan commissioning",1,"lot"]];
  if(module==="fireSafety") return [[`Addressable fire alarm control panel, minimum ${fmt(n(r["Loop minimum"]),0)} loop`,1,"set"],["Smoke detector addressable",n(r["Smoke detector"]),"unit"],["Heat detector addressable",n(r["Heat detector"]),"unit"],["Flame/special detector",n(r["Flame detector"]),"unit"],["Manual call point",n(r["Manual call point"]),"unit"],["Sounder dan strobe notification appliance",n(r["Sounder"])+n(r["Strobe"]),"unit"],[`Sprinkler head termasuk fitting`,n(r["Jumlah sprinkler total"]),"head"],[`Fire pump package ${fmt(n(r["Debit suppression total"]))} L/s`,1,"set"],[`Fire water storage minimum ${fmt(n(r["Volume water supply minimum"]))} m³`,1,"set"],["Cable, conduit, module, isolator, interface dan accessories",1,"lot"],["Programming, integrated testing, commissioning dan training",1,"lot"]];
  if(module==="communications") return [[`IP-PBX/PABX minimum ${fmt(n(r["Kapasitas PABX terpilih"]),0)} port`,1,"set"],[`SIP trunk ${fmt(n(r["SIP/trunk channels minimum"]),0)} kanal / PRI ${fmt(n(r["PRI/E1 circuits"]),0)} circuit`,1,"lot"],["Telephone/IP handset dan lisensi extension",n(r["Extension desain"]),"unit"],["Outlet telekomunikasi lengkap faceplate dan module",n(r["Outlet telekomunikasi"]),"titik"],["Horizontal UTP/FTP cable",n(r["Panjang kabel horizontal"]),"m"],[`Backbone copper 100-pair`,n(r["Kabel backbone 100-pair"]),"run"],[`Rack ${fmt(n(r["Ukuran rack terpilih"]),0)}U lengkap PDU, organizer dan grounding`,n(r["Jumlah rack"]),"unit"],[`UPS online minimum ${fmt(n(r["Kapasitas UPS minimum"]),0)} VA`,1,"set"],["Intercom master dan station",n(d.intercomMasters)+n(d.intercomStations),"unit"],["HT/radio handset, charger dan coverage accessories",n(d.htUsers),"unit"],["Labeling, certification test, programming, training dan as-built",1,"lot"]];
  if(module==="hydrant") return [[`Main fire pump ${fmt(r["Debit kebakaran"])} L/s @ ${fmt(r["Total dynamic head"])} m`,1,"unit"],["Standby fire pump set",1,"unit"],[`Jockey pump ${fmt(r["Jockey pump"])} L/s`,1,"unit"],[`Hydrant cabinet/outlet`,n(r["Jumlah titik hidran"]),"titik"],[`FDC / siamese connection`,n(r["FDC minimum"]),"unit"],[`Reservoir kebakaran ${fmt(r["Volume reservoir minimum"])} m³`,1,"unit"],["Panel, valve, instrument, testing & commissioning",1,"lot"]];
  if(module==="plumbing") return [[`Ground water tank ${fmt(r["Reservoir bawah"])} m³`,1,"unit"],[`Roof tank ${fmt(r["Tangki atap"])} m³`,1,"unit"],["Pompa transfer duty",1,"unit"],["Pompa transfer standby",1,"unit"]];
  if(module==="genset") return [[`Diesel genset ${fmt(r["Kapasitas per unit"],0)} kVA`,Math.max(1,n(d.units))+Math.max(0,n(d.standbyUnits)),"unit"],["Panel kontrol digital, AMF/ATS dan proteksi",1,"set"],["Panel sinkron & load sharing (bila multi-set)",Math.max(0,n(d.units)-1),"set"],[`Tangki BBM minimum ${fmt(r["Tangki BBM minimum"],0)} L`,1,"set"],["Fuel transfer, piping, bundwall dan instrumen",1,"lot"],["Radiator duct, louver dan ventilation fan",1,"lot"],["Exhaust, flexible joint, silencer dan insulation",1,"lot"],["Acoustic enclosure/room treatment",1,"lot"],["Inertia block dan vibration isolator",Math.max(1,n(d.units)),"set"],["Load bank test, commissioning dan training",1,"lot"]];
  if(module==="ac") return [[`Sistem AC ${fmt(r["Beban pendinginan desain"]/1000)} kW / ${fmt(r["Ton refrigerasi"])} TR`,1,"system"],["Indoor unit/AHU/FCU sesuai zoning",1,"lot"],["Outdoor unit/chiller package",1,"lot"],[`Duct utama area awal ${fmt(r["Luas duct utama awal"])} m²`,1,"lot"],[`Pipa refrigeran/chilled water awal ${fmt(r["Diameter pipa awal"],0)} mm`,1,"lot"],["Fresh air, exhaust dan filtration package",1,"lot"],["Drain kondensat, trap, insulation dan support",1,"lot"],["Control panel, thermostat dan BMS interface",1,"lot"],["Testing, adjusting, balancing & commissioning",1,"lot"]];
  return [[`Peralatan utama ${modules.find(x=>x.id===module)?.title}`,1,"set"],["Panel kontrol dan proteksi",1,"set"],["Instalasi, testing & commissioning",1,"lot"]];
}

function agentReply(question:string,module:Module,result:Record<string,number>,warnings:string[],projects:Project[],checks:boolean[],grandTotal:number,docs:{name:string;text:string}[]) {
  const q=question.toLowerCase(); const title=modules.find(x=>x.id===module)?.title;
  const values=Object.entries(result).map(([k,v])=>`${k}: ${fmt(v)} ${units(module,k)}`).join("; ");
  const base=`Konteks aktif: ${title}. ${values}.`;
  if(q.includes("risiko")||q.includes("bahaya")) return `${base}\n\nRisiko utama yang perlu ditutup:\n1. ${warnings[0]||"Belum ditemukan pelanggaran rentang awal."}\n2. Pastikan asumsi desain sesuai kondisi lapangan dan fungsi bangunan.\n3. Verifikasi kapasitas, kurva pabrikan, redundansi, akses pemeliharaan, serta skenario kegagalan.\n4. Jangan menerbitkan IFC sebelum review engineer berwenang.`;
  if(q.includes("standar")||q.includes("sni")||q.includes("puil")||q.includes("nfpa")) return `${base}\n\nMatriks standar yang perlu dikonfirmasi: regulasi nasional dan daerah yang berlaku; PUIL untuk kelistrikan; SNI proteksi kebakaran/plumbing terkait; serta NFPA/ASHRAE bila dipersyaratkan kontrak. Buku 2005 hanya menjadi dasar awal—edisi dan pasal terbaru wajib diverifikasi sebelum keputusan desain.`;
  if(q.includes("biaya")||q.includes("rab")||q.includes("boq")) return `${base}\n\nRAB modul aktif saat ini ${rupiah(grandTotal)}. Nilai ini hanya mencakup item yang harga satuannya sudah diisi. Periksa kembali volume dari gambar, AHSP/HSPK lokasi, spesifikasi, waste, mobilisasi, testing-commissioning, pajak, dan ketentuan kontrak.`;
  if(q.includes("laporan")||q.includes("kesimpulan")) return `${base}\n\nRingkasan laporan: tersedia ${projects.length} rekaman desain dari ${new Set(projects.map(p=>p.module)).size}/${modules.length} modul. Gate review terpenuhi ${checks.filter(Boolean).length}/6. ${checks.every(Boolean)?"Dokumen dapat diajukan untuk review formal.":"Dokumen belum layak disetujui; lengkapi gate review terlebih dahulu."}`;
  if(q.includes("dokumen")||q.includes("file")) return docs.length?`Dokumen yang terdaftar: ${docs.map(d=>d.name).join(", ")}. ${docs.some(d=>d.text)?`Konteks teks terbaca: ${docs.filter(d=>d.text).map(d=>d.text.slice(0,350)).join(" | ")}`:"PDF/DOCX dicatat sebagai referensi; validasi isi tetap dilakukan engineer."}`:"Belum ada dokumen yang dilampirkan. Tambahkan TXT/CSV untuk konteks teks atau PDF/DOCX sebagai daftar dokumen proyek.";
  const docContext=docs.filter(d=>d.text).map(d=>d.text.slice(0,250)).join(" | ");
  return `${base}\n\nReview agen: ${warnings.join(" ")||"Nilai berada dalam rentang pemeriksaan awal."}${docContext?`\n\nKonteks dokumen: ${docContext}`:""}\n\nTindakan berikutnya: verifikasi data masukan, cek standar terbaru, konfirmasi spesifikasi pabrikan, selesaikan koordinasi arsitektur-struktur, lalu dokumentasikan keputusan pada riwayat proyek.`;
}

export default function Home() {
  const [view, setView] = useState<"brief"|"building"|"design"|"solver"|"ded"|"dashboard"|"report">("brief");
  const [module, setModule] = useState<Module>("pump");
  const [data, setData] = useState<Record<string, number | string>>(defaults.pump);
  const [projectName, setProjectName] = useState("Gedung Kantor - Pompa Utama");
  const [projects, setProjects] = useState<Project[]>([]);
  const [saving, setSaving] = useState(false);
  const [unitPrices, setUnitPrices] = useState<Record<number,number>>({});
  const [overhead, setOverhead] = useState(10);
  const [tax, setTax] = useState(11);
  const [preparedBy, setPreparedBy] = useState("Engineer MEP");
  const [checkedBy, setCheckedBy] = useState("Koordinator MEP");
  const [checks, setChecks] = useState<boolean[]>(Array(6).fill(false));
  const [clientName, setClientName] = useState("Pemilik Proyek");
  const [projectLocation, setProjectLocation] = useState("Bandung");
  const [projectCode, setProjectCode] = useState("MEP-001");
  const [agentOpen, setAgentOpen] = useState(true);
  const [agentQuestion, setAgentQuestion] = useState("");
  const [documents, setDocuments] = useState<{name:string;text:string}[]>([]);
  const [liftStep, setLiftStep] = useState(0);
  const [vendorId, setVendorId] = useState(liftVendors[0].id);
  const [escalatorVendorId, setEscalatorVendorId] = useState(escalatorVendors[0].id);
  const [pumpStep,setPumpStep]=useState(0);
  const [pumpVendorId,setPumpVendorId]=useState(pumpVendors[0].id);
  const [pumpImpeller,setPumpImpeller]=useState("Closed impeller");
  const [pumpMaterial,setPumpMaterial]=useState("Cast iron casing / SS shaft");
  const [pumpSeal,setPumpSeal]=useState("Mechanical seal");
  const [pumpControl,setPumpControl]=useState("VFD + pressure sensor");
  const [hydrantStep,setHydrantStep]=useState(0);
  const [firePumpVendorId,setFirePumpVendorId]=useState(firePumpVendors[0].id);
  const [hazardClass,setHazardClass]=useState("Ordinary hazard / gedung umum");
  const [hydrantType,setHydrantType]=useState("Indoor hydrant + hose reel");
  const [gensetStep,setGensetStep]=useState(0);
  const [gensetVendorId,setGensetVendorId]=useState(gensetVendors[0].id);
  const [gensetRating,setGensetRating]=useState("Emergency standby");
  const [gensetControl,setGensetControl]=useState("AMF + ATS automatic transfer");
  const [acStep,setAcStep]=useState(0);
  const [electricalStep,setElectricalStep]=useState(0);
  const [lightingStep,setLightingStep]=useState(0);
  const [fireSafetyStep,setFireSafetyStep]=useState(0);
  const [communicationsStep,setCommunicationsStep]=useState(0);
  const [activeSubmodule,setActiveSubmodule]=useState("traffic");
  const [submoduleChecks,setSubmoduleChecks]=useState<Record<string,boolean[]>>({});
  const [packageMessage,setPackageMessage]=useState("");
  const [dedRegisters,setDEDRegisters]=useState<DEDRegisterItem[]>(initialDEDRegisters);
  const [dedChecks,setDEDChecks]=useState<Record<string,boolean>>({});
  const [dedModule,setDEDModule]=useState<Module>("electrical");
  const [acVendorId,setAcVendorId]=useState(acVendors[0].id);
  const [acSystem,setAcSystem]=useState("VRF/VRV");
  const [exampleLoaded,setExampleLoaded]=useState(false);
  const [reportRevision,setReportRevision]=useState("00");
  const [reportStatus,setReportStatus]=useState("Design Review");
  const [ownerRepresentative,setOwnerRepresentative]=useState("Perwakilan Pemilik Proyek");
  const [briefAnswers,setBriefAnswers]=useState<Record<string,string>>({});
  const [briefCategory,setBriefCategory]=useState("Owner");
  const [briefModule,setBriefModule]=useState<Module>("lift");
  const [chapterGateChecks,setChapterGateChecks]=useState<Record<"lift"|"escalator",boolean[]>>({lift:Array(8).fill(false),escalator:Array(8).fill(false)});
  const [buildingName,setBuildingName]=useState("Gedung Kantor Nusantara");
  const [buildingFunction,setBuildingFunction]=useState("Perkantoran");
  const [floors,setFloors]=useState<FloorRecord[]>(initialFloors);
  const [activeFloorId,setActiveFloorId]=useState(initialFloors[0].id);
  const [hierarchyLoaded,setHierarchyLoaded]=useState(false);
  const [importMessage,setImportMessage]=useState("");
  const [messages, setMessages] = useState<ChatMessage[]>([{role:"agent",text:"Saya siap meninjau modul aktif. Tanyakan risiko, standar, RAB, laporan, atau tindakan desain berikutnya."}]);
  const result = useMemo(()=>calculate(module,data),[module,data]);
  const current = modules.find(x=>x.id===module)!;
  const technicalSubmodules=submoduleCatalog[module]||[];
  const selectedSubmodule=technicalSubmodules.find(x=>x.id===activeSubmodule)||technicalSubmodules[0];
  const allTechnicalSubmodules=(Object.entries(submoduleCatalog) as [Module,TechnicalSubmodule[]][]).flatMap(([owner,items])=>items.map(sm=>({owner,sm})));
  const technicalSubmoduleCount=allTechnicalSubmodules.length;
  const selectedSubmoduleKey=`${module}:${selectedSubmodule?.id||"general"}`;
  const selectedGateChecks=submoduleChecks[selectedSubmoduleKey]||Array(engineeringGates.length).fill(false);
  const selectedGateScore=Math.round(selectedGateChecks.filter(Boolean).length/engineeringGates.length*100);
  const completedTechnicalSubmodules=allTechnicalSubmodules.filter(({owner,sm})=>(submoduleChecks[`${owner}:${sm.id}`]||[]).every(Boolean)&&(submoduleChecks[`${owner}:${sm.id}`]||[]).length===engineeringGates.length).length;
  const dedRegisterDone=dedRegisters.filter(x=>x.status==="Disetujui").length;
  const dedChapterDone=Object.entries(chapterDEDRequirements).reduce((sum,[m,items])=>sum+items.filter((_,i)=>dedChecks[`chapter:${m}:${i}`]).length,0);
  const dedChapterTotal=Object.values(chapterDEDRequirements).reduce((sum,items)=>sum+items.length,0);
  const dedFlowDone=dedFlow.filter((_,i)=>dedChecks[`flow:${i}`]).length;
  const dedReadiness=Math.round((dedRegisterDone/dedRegisters.length+dedChapterDone/dedChapterTotal+dedFlowDone/dedFlow.length)*100/3);
  const boqRows = useMemo(()=>boq(module,data,result),[module,data,result]);
  const directCost = boqRows.reduce((sum,row,i)=>sum+n(row[1])*n(unitPrices[i]||0),0);
  const overheadCost = directCost*overhead/100;
  const beforeTax = directCost+overheadCost;
  const taxCost = beforeTax*tax/100;
  const grandTotal = beforeTax+taxCost;
  const completedModules = new Set(projects.map(p=>p.module)).size;
  const coverage = Math.round(completedModules/modules.length*100);
  const integratedResults=useMemo(()=>modules.map(m=>({module:m,...calculate(m.id,defaults[m.id])})),[]);
  const exampleTests=useMemo(()=>[
    {name:"Lift",pass:n(calculate("lift",defaults.lift)["Jumlah lift total"])>0,note:`${fmt(n(calculate("lift",defaults.lift)["Jumlah lift total"]),0)} unit`},
    {name:"Eskalator",pass:n(calculate("escalator",defaults.escalator)["Jumlah unit minimum"])>0,note:`${fmt(n(calculate("escalator",defaults.escalator)["Jumlah unit minimum"]),0)} unit minimum`},
    {name:"Pompa",pass:n(calculate("pump",defaults.pump)["Total dynamic head"])>0,note:`${fmt(n(calculate("pump",defaults.pump)["Total dynamic head"]))} m TDH`},
    {name:"Hidran",pass:n(calculate("hydrant",defaults.hydrant)["Volume reservoir minimum"])>0,note:`${fmt(n(calculate("hydrant",defaults.hydrant)["Volume reservoir minimum"]))} m³`},
    {name:"Plumbing",pass:n(calculate("plumbing",defaults.plumbing)["Kebutuhan air harian"])>0,note:`${fmt(n(calculate("plumbing",defaults.plumbing)["Kebutuhan air harian"]))} m³/hari`},
    {name:"Elektrikal",pass:n(calculate("electrical",defaults.electrical)["Daya desain + cadangan"])>0,note:`${fmt(n(calculate("electrical",defaults.electrical)["Daya desain + cadangan"]))} kW`},
    {name:"Pencahayaan",pass:n(calculate("lighting",defaults.lighting)["Jumlah armatur terpilih"])>0,note:`${fmt(n(calculate("lighting",defaults.lighting)["Jumlah armatur terpilih"]),0)} armatur`},
    {name:"Pengaman Kebakaran",pass:n(calculate("fireSafety",defaults.fireSafety)["Total addressable devices"])>0,note:`${fmt(n(calculate("fireSafety",defaults.fireSafety)["Total addressable devices"]),0)} device`},
    {name:"Sistem Komunikasi",pass:n(calculate("communications",defaults.communications)["Extension desain"])>0,note:`${fmt(n(calculate("communications",defaults.communications)["Extension desain"]),0)} extension`},
    {name:"Genset",pass:n(calculate("genset",defaults.genset)["Kapasitas per unit"])>0,note:`${fmt(n(calculate("genset",defaults.genset)["Kapasitas per unit"]),0)} kVA`},
    {name:"AC",pass:n(calculate("ac",defaults.ac)["Beban pendinginan desain"])>0,note:`${fmt(n(calculate("ac",defaults.ac)["Ton refrigerasi"]))} TR`},
  ],[]);
  const examplePass=exampleTests.filter(x=>x.pass).length;
  const roomCalc=(r:RoomRecord)=>{const lighting=r.area*r.lightingWm2/1000,coolingW=r.area*120+r.occupants*130+r.equipmentW*.15,coolingTR=coolingW/3516.85,acInput=coolingW/1000/3.5;return {area:r.area,occupants:r.occupants,lighting,coolingTR,power:lighting+r.equipmentW/1000+acInput,water:r.occupants*.1,freshAir:r.occupants*10,detectors:Math.ceil(r.area/80),sprinklers:Math.ceil(r.area/12),outlets:r.outlets}};
  const floorSummaries=useMemo(()=>floors.map(f=>{const raw=f.rooms.reduce((a,r)=>{const x=roomCalc(r);Object.keys(a).forEach(k=>a[k as keyof typeof a]+=x[k as keyof typeof x]);return a},{area:0,occupants:0,lighting:0,coolingTR:0,power:0,water:0,freshAir:0,detectors:0,sprinklers:0,outlets:0});return {...f,raw,weighted:Object.fromEntries(Object.entries(raw).map(([k,v])=>[k,v*f.multiplier])) as typeof raw}}),[floors]);
  const buildingSummary=useMemo(()=>floorSummaries.reduce((a,f)=>{Object.keys(a).forEach(k=>a[k as keyof typeof a]+=f.weighted[k as keyof typeof f.weighted]);return a},{area:0,occupants:0,lighting:0,coolingTR:0,power:0,water:0,freshAir:0,detectors:0,sprinklers:0,outlets:0}),[floorSummaries]);
  const logicalFloors=floors.reduce((s,f)=>s+f.multiplier,0),totalRooms=floors.reduce((s,f)=>s+f.rooms.length*f.multiplier,0),validRooms=floors.reduce((s,f)=>s+f.rooms.filter(r=>r.name&&r.function&&r.area>0&&r.height>0).length*f.multiplier,0),hierarchyProgress=Math.round(validRooms/Math.max(1,totalRooms)*100);
  const activeFloor=floors.find(f=>f.id===activeFloorId)||floors[0];
  const hierarchyAudit=useMemo(()=>floors.flatMap(f=>f.rooms.flatMap(r=>{const issues:string[]=[];if(!r.name.trim())issues.push("Nama ruang kosong");if(!r.function.trim())issues.push("Fungsi ruang kosong");if(r.area<=0)issues.push("Luas harus > 0 m²");if(r.height<=0)issues.push("Tinggi harus > 0 m");if(r.occupants<0)issues.push("Penghuni tidak boleh negatif");if(r.targetLux<=0)issues.push("Target lux belum ditetapkan");if(r.lightingWm2<=0)issues.push("LPD belum ditetapkan");return issues.map(issue=>({floor:f.name,room:r.name||"(tanpa nama)",issue}))})),[floors]);
  const activeLiftFields = module==="lift" ? fieldSets.lift.filter(f=>liftFlowSteps[liftStep].keys.includes(f.key)) : module==="pump"?fieldSets.pump.filter(f=>pumpFlowSteps[pumpStep].keys.includes(f.key)):module==="hydrant"?fieldSets.hydrant.filter(f=>hydrantFlowSteps[hydrantStep].keys.includes(f.key)):module==="genset"?fieldSets.genset.filter(f=>gensetFlowSteps[gensetStep].keys.includes(f.key)):module==="ac"?fieldSets.ac.filter(f=>acFlowSteps[acStep].keys.includes(f.key)):module==="electrical"?fieldSets.electrical.filter(f=>electricalFlowSteps[electricalStep].keys.includes(f.key)):module==="lighting"?fieldSets.lighting.filter(f=>lightingFlowSteps[lightingStep].keys.includes(f.key)):module==="fireSafety"?fieldSets.fireSafety.filter(f=>fireSafetyFlowSteps[fireSafetyStep].keys.includes(f.key)):module==="communications"?fieldSets.communications.filter(f=>communicationsFlowSteps[communicationsStep].keys.includes(f.key)):fieldSets[module];
  const selectedVendor = liftVendors.find(v=>v.id===vendorId)!;
  const selectedEscalatorVendor = escalatorVendors.find(v=>v.id===escalatorVendorId)!;
  const selectedPumpVendor=pumpVendors.find(v=>v.id===pumpVendorId)!;
  const selectedFirePumpVendor=firePumpVendors.find(v=>v.id===firePumpVendorId)!;
  const selectedGensetVendor=gensetVendors.find(v=>v.id===gensetVendorId)!;
  const selectedAcVendor=acVendors.find(v=>v.id===acVendorId)!;
  const recommendedPumpType=n(data.flow)>300?"Split case":n(result["Total dynamic head"])>100?"Vertical multistage":n(data.flow)<15?"Vertical in-line / multistage":"End suction centrifugal";
  const pumpChart=useMemo(()=>{
    const W=600,H=300,pad=42, qRated=Math.max(.1,n(result["Debit per pompa duty"])), hRated=Math.max(.1,n(result["Head per pompa seri"]));
    const qMax=qRated*Math.max(1.05,n(data.runoutFactor)/100), hStatic=(n(data.staticHead)+n(data.pressureHead))/Math.max(1,n(data.seriesPumps));
    const hShut=Math.max(hRated,n(result["Head shut-off"])), kPump=(hShut-hRated)/(qRated*qRated), kSystem=Math.max(0,(hRated-hStatic)/(qRated*qRated));
    const hMax=Math.max(hShut,hStatic+kSystem*qMax*qMax)*1.12, sx=(q:number)=>pad+q/qMax*(W-pad*2), sy=(h:number)=>H-pad-h/hMax*(H-pad*2);
    const qs=Array.from({length:31},(_,i)=>qMax*i/30), pump=qs.map(q=>`${sx(q)},${sy(Math.max(0,hShut-kPump*q*q))}`).join(" "), system=qs.map(q=>`${sx(q)},${sy(hStatic+kSystem*q*q)}`).join(" ");
    return {W,H,pad,qMax,hMax,pump,system,opX:sx(n(result["Operating flow kurva"])),opY:sy(n(result["Operating head kurva"]))};
  },[data,result]);
  const commonBriefIds=new Set(["project","function","location","floors","population","hours","budget","documents","power","emergency","bms","brands","schedule","tkdn","submittal"]);
  const activeBriefQuestions=briefQuestions.filter(q=>q.modules?.includes(briefModule)||(!q.modules&&((briefModule==="lift"||briefModule==="escalator")||commonBriefIds.has(q.id))));
  const briefCategories=Array.from(new Set(activeBriefQuestions.map(q=>q.category)));
  const requiredBrief=activeBriefQuestions.filter(q=>q.required), answeredRequired=requiredBrief.filter(q=>briefAnswers[q.id]?.trim()).length;
  const briefProgress=Math.round(answeredRequired/requiredBrief.length*100), missingBrief=requiredBrief.filter(q=>!briefAnswers[q.id]?.trim());
  const chapterAuditItems=useMemo<AuditItem[]>(()=>{
    if(module==="lift") return [
      {area:"Data",criterion:"Jumlah lantai ≥ 2",observed:`${fmt(n(data.floors),0)} lantai`,pass:n(data.floors)>=2,action:"Koreksi jumlah lantai layanan."},
      {area:"Zoning",criterion:"Zona tidak melebihi jumlah lantai",observed:`${fmt(n(data.zones),0)} zona`,pass:n(data.zones)>=1&&n(data.zones)<=n(data.floors),action:"Tinjau pembagian zona dan skylobby."},
      {area:"Traffic",criterion:"Waktu tunggu ≤ 45 detik",observed:`${fmt(n(result["Waktu tunggu"]))} detik`,pass:n(result["Waktu tunggu"])<=45,action:"Tambah unit, kapasitas, kecepatan, atau revisi zoning."},
      {area:"Traffic",criterion:"Handling capacity memenuhi peak load",observed:`${fmt(n(result["Rasio layanan"]))}%`,pass:n(result["Rasio layanan"])>=100,action:"Naikkan daya angkut 5 menit atau revisi asumsi peak."},
      {area:"Kinerja",criterion:"Kecepatan ≥ rekomendasi awal",observed:`${fmt(n(data.speed))} / ${fmt(n(result["Kecepatan rekomendasi"]))} m/s`,pass:n(data.speed)>=n(result["Kecepatan rekomendasi"]),action:"Evaluasi kecepatan dan tipe drive."},
      {area:"Arsitektur",criterion:"Efisiensi lantai ≥ 85%",observed:`${fmt(n(result["Efisiensi lantai"]))}%`,pass:n(result["Efisiensi lantai"])>=85,action:"Optimalkan core, jumlah unit, dan luas hoistway."},
      {area:"Kabin",criterion:"Kepadatan kabin ≤ 350 kg/m²",observed:`${fmt(n(result["Kepadatan beban kabin"]))} kg/m²`,pass:n(result["Kepadatan beban kabin"])<=350,action:"Perbesar kabin atau turunkan kapasitas nominal."},
      {area:"Energi",criterion:"Klaim regenerative saving ≤ 40%",observed:`${fmt(n(data.regenSaving))}%`,pass:n(data.regenSaving)<=40,action:"Lampirkan simulasi profil operasi dan bukti vendor."}
    ];
    return [
      {area:"Geometri",criterion:"Sudut berada pada 27–35°",observed:`${fmt(n(data.angle))}°`,pass:n(data.angle)>=27&&n(data.angle)<=35,action:"Revisi geometri atau konfirmasi produk khusus."},
      {area:"Geometri",criterion:"Rise bernilai positif",observed:`${fmt(n(data.rise))} m`,pass:n(data.rise)>0,action:"Masukkan tinggi angkat aktual."},
      {area:"Transportasi",criterion:"Lebar step ukuran umum",observed:`${fmt(n(data.stepWidth),0)} mm`,pass:[600,800,1000].includes(n(data.stepWidth)),action:"Gunakan 600/800/1000 mm atau buktikan ketersediaan."},
      {area:"Transportasi",criterion:"Kecepatan ≤ 0,75 m/s",observed:`${fmt(n(data.speed))} m/s`,pass:n(data.speed)>0&&n(data.speed)<=.75,action:"Turunkan speed atau lakukan kajian keselamatan khusus."},
      {area:"Kapasitas",criterion:"Arus naik dan turun terlayani",observed:`${fmt(n(data.peakFlowUp),0)} naik; ${fmt(n(data.peakFlowDown),0)} turun orang/jam`,pass:n(result["Kapasitas praktis per unit"])>0&&n(data.peakFlowUp)>=0&&n(data.peakFlowDown)>=0,action:"Periksa arus puncak, speed, pitch, okupansi dan faktor kapasitas."},
      {area:"Daya",criterion:"Demand operasi terhitung",observed:`${fmt(n(result["Demand operasi"]))} kW`,pass:n(result["Demand operasi"])>0,action:"Periksa efisiensi dan faktor beban."},
      {area:"Operasi",criterion:"Hari dan jam operasi valid",observed:`${fmt(n(data.operatingHours))} jam × ${fmt(n(data.operatingDays),0)} hari`,pass:n(data.operatingHours)>0&&n(data.operatingHours)<=24&&n(data.operatingDays)>0&&n(data.operatingDays)<=366,action:"Koreksi jadwal operasi tahunan."},
      {area:"Kecukupan",criterion:"Unit rencana ≥ kebutuhan minimum",observed:`${fmt(n(data.units),0)} tersedia / ${fmt(n(result["Jumlah unit minimum"]),0)} dibutuhkan`,pass:n(result["Rasio kecukupan unit"])>=100,action:"Tambahkan unit atau evaluasi ulang arus puncak dan konfigurasi arah."}
    ];
  },[module,data,result]);
  const chapterKey: "lift"|"escalator"=module==="escalator"?"escalator":"lift";
  const autoPassed=chapterAuditItems.filter(x=>x.pass).length;
  const manualPassed=chapterGateChecks[chapterKey].filter(Boolean).length;
  const auditScore=Math.round((autoPassed+manualPassed)/(chapterAuditItems.length+chapterManualGates[chapterKey].length)*100);
  const auditStatus=autoPassed<chapterAuditItems.length?"PERLU REVISI":manualPassed<chapterManualGates[chapterKey].length?"MENUNGGU VERIFIKASI":"SIAP REVIEW FINAL";

  useEffect(()=>{ fetch("/api/projects").then(r=>r.ok?r.json():[]).then(setProjects).catch(()=>{});try{const saved=localStorage.getItem("mep-building-hierarchy-v1");if(saved){const h=JSON.parse(saved);setBuildingName(h.buildingName||buildingName);setBuildingFunction(h.buildingFunction||buildingFunction);if(h.floors?.length){setFloors(h.floors);setActiveFloorId(h.floors[0].id)}}const gates=localStorage.getItem("mep-submodule-gates-v1");if(gates)setSubmoduleChecks(JSON.parse(gates));const delivery=localStorage.getItem("mep-ded-delivery-v1");if(delivery){const d=JSON.parse(delivery);if(d.registers?.length)setDEDRegisters(d.registers);if(d.checks)setDEDChecks(d.checks)}}catch{}setHierarchyLoaded(true); },[]);
  useEffect(()=>{if(hierarchyLoaded)localStorage.setItem("mep-building-hierarchy-v1",JSON.stringify({buildingName,buildingFunction,floors}))},[buildingName,buildingFunction,floors,hierarchyLoaded]);
  useEffect(()=>{if(hierarchyLoaded)localStorage.setItem("mep-submodule-gates-v1",JSON.stringify(submoduleChecks))},[submoduleChecks,hierarchyLoaded]);
  useEffect(()=>{if(hierarchyLoaded)localStorage.setItem("mep-ded-delivery-v1",JSON.stringify({registers:dedRegisters,checks:dedChecks}))},[dedRegisters,dedChecks,hierarchyLoaded]);
  function updateFloor(id:string,key:keyof FloorRecord,value:string|number){setFloors(fs=>fs.map(f=>f.id===id?{...f,[key]:value}:f))}
  function updateRoom(fid:string,rid:string,key:keyof RoomRecord,value:string|number){setFloors(fs=>fs.map(f=>f.id===fid?{...f,rooms:f.rooms.map(r=>r.id===rid?{...r,[key]:value}:r)}:f))}
  function addFloor(){const f={id:`floor-${Date.now()}`,name:`Lantai ${floors.length+1}`,elevation:logicalFloors*4,multiplier:1,rooms:[newRoom()]};setFloors([...floors,f]);setActiveFloorId(f.id)}
  function duplicateFloor(){if(!activeFloor)return;const f={...activeFloor,id:`floor-${Date.now()}`,name:`${activeFloor.name} Salinan`,multiplier:1,rooms:activeFloor.rooms.map(r=>({...r,id:`room-${Date.now()}-${Math.random()}`}))};setFloors([...floors,f]);setActiveFloorId(f.id)}
  function removeFloor(){if(floors.length<2||!activeFloor)return;const next=floors.filter(f=>f.id!==activeFloor.id);setFloors(next);setActiveFloorId(next[0].id)}
  function addRoom(){if(!activeFloor)return;setFloors(fs=>fs.map(f=>f.id===activeFloor.id?{...f,rooms:[...f.rooms,newRoom()]}:f))}
  function duplicateRoom(r:RoomRecord){if(!activeFloor)return;setFloors(fs=>fs.map(f=>f.id===activeFloor.id?{...f,rooms:[...f.rooms,{...r,id:`room-${Date.now()}-${Math.random()}`,name:`${r.name} Salinan`}]}:f))}
  function removeRoom(rid:string){if(!activeFloor||activeFloor.rooms.length<2)return;setFloors(fs=>fs.map(f=>f.id===activeFloor.id?{...f,rooms:f.rooms.filter(r=>r.id!==rid)}:f))}
  function syncHierarchy(m:Module){const r=activeFloor?.rooms[0];let next={...defaults[m]};if(m==="plumbing")next.occupants=buildingSummary.occupants;if(m==="electrical")next={...next,lightingLoad:buildingSummary.lighting,socketLoad:buildingSummary.outlets*.2,acLoad:buildingSummary.coolingTR*1.05};if(m==="fireSafety")next={...next,floors:logicalFloors,floorArea:buildingSummary.area/Math.max(1,logicalFloors)};if(m==="communications")next={...next,users:buildingSummary.occupants,outlets:buildingSummary.outlets,floors:logicalFloors};if(m==="ac"&&r)next={...next,area:r.area,people:r.occupants,lighting:r.area*r.lightingWm2,equipment:r.equipmentW};if(m==="lighting"&&r){const side=Math.sqrt(r.area);next={...next,roomLength:side,roomWidth:side,roomHeight:r.height,targetLux:r.targetLux}}setModule(m);setData(next);setProjectName(`${buildingName} - ${modules.find(x=>x.id===m)?.title}`);setView("design")}
  function openTechnicalSubmodule(sm:TechnicalSubmodule){setActiveSubmodule(sm.id);if(module==="lift")setLiftStep(sm.step);if(module==="pump")setPumpStep(sm.step);if(module==="hydrant")setHydrantStep(sm.step);if(module==="genset")setGensetStep(sm.step);if(module==="ac")setAcStep(sm.step);if(module==="electrical")setElectricalStep(sm.step);if(module==="lighting")setLightingStep(sm.step);if(module==="fireSafety")setFireSafetyStep(sm.step);if(module==="communications")setCommunicationsStep(sm.step)}
  function toggleSubmoduleGate(index:number){const next=[...selectedGateChecks];next[index]=!next[index];setSubmoduleChecks({...submoduleChecks,[selectedSubmoduleKey]:next})}
  function exportProjectPackage(){const payload={schema:"MEP-ENGINEER-AI-PROJECT-1.1",exportedAt:new Date().toISOString(),identity:{projectName,projectCode,clientName,projectLocation,buildingName,buildingFunction,reportRevision,reportStatus,preparedBy,checkedBy,ownerRepresentative},floors,briefAnswers,submoduleChecks,projects,dedRegisters,dedChecks};const blob=new Blob([JSON.stringify(payload,null,2)],{type:"application/json"});const a=document.createElement("a");a.href=URL.createObjectURL(blob);a.download=`${projectCode||"MEP_PROJECT"}_Master_Project.json`;a.click();URL.revokeObjectURL(a.href);setPackageMessage("Paket master proyek dan DED Center berhasil diekspor.")}
  async function importProjectPackage(file:File){try{const payload=JSON.parse(await file.text());if(!["MEP-ENGINEER-AI-PROJECT-1.0","MEP-ENGINEER-AI-PROJECT-1.1"].includes(payload.schema))throw new Error("Format paket tidak dikenali");const i=payload.identity||{};setProjectName(i.projectName||projectName);setProjectCode(i.projectCode||projectCode);setClientName(i.clientName||clientName);setProjectLocation(i.projectLocation||projectLocation);setBuildingName(i.buildingName||buildingName);setBuildingFunction(i.buildingFunction||buildingFunction);setReportRevision(i.reportRevision||"00");setReportStatus(i.reportStatus||"Design Review");setPreparedBy(i.preparedBy||preparedBy);setCheckedBy(i.checkedBy||checkedBy);setOwnerRepresentative(i.ownerRepresentative||ownerRepresentative);if(payload.floors?.length){setFloors(payload.floors);setActiveFloorId(payload.floors[0].id)}setBriefAnswers(payload.briefAnswers||{});setSubmoduleChecks(payload.submoduleChecks||{});if(payload.projects?.length)setProjects(payload.projects);if(payload.dedRegisters?.length)setDEDRegisters(payload.dedRegisters);if(payload.dedChecks)setDEDChecks(payload.dedChecks);setPackageMessage("Paket proyek dan DED Center berhasil dipulihkan.")}catch(e){setPackageMessage(`Impor gagal: ${e instanceof Error?e.message:"file rusak"}`)}}
  function updateDEDRegister(id:string,key:keyof DEDRegisterItem,value:string){setDEDRegisters(rows=>rows.map(row=>row.id===id?{...row,[key]:value}:row))}
  function addDEDRegister(){setDEDRegisters(rows=>[...rows,{id:`custom-${Date.now()}`,category:"Tambahan",deliverable:"Deliverable baru",owner:"MEP Engineer",status:"Belum Mulai",evidence:""}])}
  function removeDEDRegister(id:string){setDEDRegisters(rows=>rows.filter(row=>row.id!==id))}
  function toggleDEDCheck(key:string){setDEDChecks(x=>({...x,[key]:!x[key]}))}
  function roomRows(){return floors.flatMap(f=>f.rooms.map(r=>{const x=roomCalc(r);return {Bangunan:buildingName,Lantai:f.name,"Pengali Lantai":f.multiplier,Ruangan:r.name,"Fungsi Ruang":r.function,"Luas m2":r.area,"Tinggi m":r.height,"Penghuni orang":r.occupants,"LPD Wm2":r.lightingWm2,"Peralatan W":r.equipmentW,"Outlet titik":r.outlets,"Target Lux":r.targetLux,"Beban Lampu kW":x.lighting,"Beban Pendinginan TR":x.coolingTR,"Estimasi Daya kW":x.power,"Fresh Air Ls":x.freshAir,"Detector unit":x.detectors,"Sprinkler unit":x.sprinklers,"Air Bersih m3hari":x.water}}))}
  function downloadHierarchyWorkbook(template=false){const wb=XLSX.utils.book_new();const rows=template?[{Lantai:"Lantai Dasar",Pengali:1,Elevasi_m:0,Ruangan:"Lobby",Fungsi:"Lobby",Luas_m2:120,Tinggi_m:3.5,Penghuni:20,LPD_Wm2:10,Peralatan_W:1500,Outlet:6,Target_Lux:200}]:floors.flatMap(f=>f.rooms.map(r=>({Lantai:f.name,Pengali:f.multiplier,Elevasi_m:f.elevation,Ruangan:r.name,Fungsi:r.function,Luas_m2:r.area,Tinggi_m:r.height,Penghuni:r.occupants,LPD_Wm2:r.lightingWm2,Peralatan_W:r.equipmentW,Outlet:r.outlets,Target_Lux:r.targetLux})));XLSX.utils.book_append_sheet(wb,XLSX.utils.json_to_sheet(rows),"Input Ruang");if(!template){XLSX.utils.book_append_sheet(wb,XLSX.utils.json_to_sheet(roomRows()),"Room Data Sheet");XLSX.utils.book_append_sheet(wb,XLSX.utils.json_to_sheet(floorSummaries.map(f=>({Lantai:f.name,Pengali:f.multiplier,"Luas m2":f.weighted.area,Penghuni:f.weighted.occupants,"Lampu kW":f.weighted.lighting,"Cooling TR":f.weighted.coolingTR,"Daya kW":f.weighted.power,Detector:f.weighted.detectors,Sprinkler:f.weighted.sprinklers}))),"Floor Schedule");XLSX.utils.book_append_sheet(wb,XLSX.utils.json_to_sheet([{Bangunan:buildingName,Fungsi:buildingFunction,"Lantai Logis":logicalFloors,"Ruang Ekuivalen":totalRooms,"Luas m2":buildingSummary.area,Penghuni:buildingSummary.occupants,"Lampu kW":buildingSummary.lighting,"Cooling TR":buildingSummary.coolingTR,"Daya kW":buildingSummary.power,"Outlet titik":buildingSummary.outlets,Detector:buildingSummary.detectors,Sprinkler:buildingSummary.sprinklers}]),"Building Summary")}XLSX.writeFile(wb,template?"Template_Import_Ruangan_MEP.xlsx":`${buildingName.replace(/[^a-z0-9]+/gi,"_")}_MEP_Schedules.xlsx`)}
  async function importHierarchyExcel(file:File){try{const wb=XLSX.read(await file.arrayBuffer());const rows=XLSX.utils.sheet_to_json<Record<string,string|number>>(wb.Sheets[wb.SheetNames[0]],{defval:""});if(!rows.length)throw new Error("Sheet pertama kosong");const grouped=new Map<string,FloorRecord>();rows.forEach((row,i)=>{const floorName=String(row.Lantai||"").trim();const roomName=String(row.Ruangan||"").trim();if(!floorName||!roomName)throw new Error(`Baris ${i+2}: Lantai dan Ruangan wajib diisi`);if(!grouped.has(floorName))grouped.set(floorName,{id:`floor-import-${Date.now()}-${i}`,name:floorName,elevation:Number(row.Elevasi_m)||0,multiplier:Math.max(1,Number(row.Pengali)||1),rooms:[]});grouped.get(floorName)!.rooms.push({id:`room-import-${Date.now()}-${i}`,name:roomName,function:String(row.Fungsi||"Belum ditetapkan"),area:Number(row.Luas_m2)||0,height:Number(row.Tinggi_m)||0,occupants:Number(row.Penghuni)||0,lightingWm2:Number(row.LPD_Wm2)||0,equipmentW:Number(row.Peralatan_W)||0,outlets:Number(row.Outlet)||0,targetLux:Number(row.Target_Lux)||0})});const next=[...grouped.values()];setFloors(next);setActiveFloorId(next[0].id);setImportMessage(`${rows.length} ruang pada ${next.length} kelompok lantai berhasil diimpor dan diaudit.`)}catch(e){setImportMessage(`Impor gagal: ${e instanceof Error?e.message:"format tidak dikenali"}`)}}
  function moduleBriefReady(m:Module){const qs=briefQuestions.filter(q=>q.modules?.includes(m)||(!q.modules&&((m==="lift"||m==="escalator")||commonBriefIds.has(q.id)))).filter(q=>q.required);return qs.every(q=>briefAnswers[q.id]?.trim())}
  function select(m: Module) { setModule(m); setData(defaults[m]); setLiftStep(0); setPumpStep(0); setHydrantStep(0);setGensetStep(0);setAcStep(0);setElectricalStep(0);setLightingStep(0);setFireSafetyStep(0);setCommunicationsStep(0); setUnitPrices({}); setChecks(Array(6).fill(false)); setProjectName(`Proyek Baru - ${modules.find(x=>x.id===m)?.title}`);setView("design"); }
  function openBriefModule(m:Module){setBriefModule(m);const first=briefQuestions.find(q=>q.modules?.includes(m)||(!q.modules&&((m==="lift"||m==="escalator")||commonBriefIds.has(q.id))));setBriefCategory(first?.category||"Owner")}
  function briefNumber(id:string){const raw=(briefAnswers[id]||"").replace(/\s/g,"").replace(",",".").match(/-?\d+(\.\d+)?/);return raw?Number(raw[0]):undefined}
  function applyBriefToWorkspace(){
    const next={...defaults[briefModule]}; const put=(key:string,id:string)=>{const value=briefNumber(id);if(value!==undefined)next[key]=value};
    if(briefModule==="pump"){put("flow","pump_flow");put("staticHead","pump_static");put("pressureHead","pump_pressure");put("length","pump_length");put("diameter","pump_diameter");put("npsha","pump_npsha")}
    if(briefModule==="hydrant"){put("buildingArea","hyd_area");put("floors","hyd_floors");put("flowPerHydrant","hyd_flow");put("hydrants","hyd_simultaneous");put("duration","hyd_duration");put("availableTank","hyd_tank");put("staticHead","hyd_static");put("residualPressure","hyd_residual");put("zones","hyd_zones");put("fdcCount","hyd_fdc")}
    if(briefModule==="lift"){put("floors","floors");put("floorHeight","floorHeight");put("area","floorArea")}
    if(briefModule==="escalator"){put("rise","escSpace");put("peakFlowUp","escPeak")}
    if(briefModule==="genset"){put("connectedLoad","gen_connected");put("emergencyShare","gen_emergency");put("demandFactor","gen_demand");put("largestMotor","gen_motor");put("startVoltageDip","gen_voltage");put("altitude","gen_location");put("autonomy","gen_fuel")}
    if(briefModule==="ac"){put("area","ac_rooms");put("people","ac_people");put("outdoorDb","ac_outdoor");put("indoorDb","ac_indoor")}
    setModule(briefModule);setData(next);setProjectName(briefAnswers.project||`Proyek Baru - ${modules.find(x=>x.id===briefModule)?.title}`);setView("design");setLiftStep(0);setPumpStep(0);setHydrantStep(0);setGensetStep(0);setAcStep(0);setElectricalStep(0);setLightingStep(0);setFireSafetyStep(0);setCommunicationsStep(0)
  }
  function applyVendor(){ setData({...data,capacity:selectedVendor.capacity,speed:selectedVendor.speed}); }
  function loadExampleProject(){
    const answers=briefQuestions.reduce<Record<string,string>>((a,q)=>({...a,[q.id]:q.options?.[0]||"Sudah dikonfirmasi untuk proyek contoh"}),{});
    Object.assign(answers,{project:"Gedung Kantor Nusantara",function:"Perkantoran",location:"Bandung, Jawa Barat",floors:"12",population:"850",hours:"10",budget:"Design to budget",documents:"DED",power:"400 V, 3 fasa, 50 Hz",emergency:"Life safety dan beban esensial",bms:"BACnet/IP",brands:"Approved equal",schedule:"12 bulan",tkdn:"Diperlukan",submittal:"Ya",pump_flow:"45",pump_static:"28",pump_pressure:"0",pump_length:"120",pump_diameter:"100",pump_npsha:"6.5",hyd_area:"6000",hyd_floors:"8",hyd_flow:"6.3",hyd_simultaneous:"2",hyd_duration:"45",hyd_tank:"50",hyd_static:"24",hyd_residual:"45",hyd_zones:"1",hyd_fdc:"1",gen_connected:"200.2",gen_emergency:"75",gen_demand:"0.8",gen_motor:"45",gen_voltage:"15",gen_location:"750",gen_fuel:"8",ac_rooms:"120",ac_people:"18",ac_outdoor:"33",ac_indoor:"24"});
    setBriefAnswers(answers);setClientName("PT Nusantara Properti");setProjectLocation("Bandung, Jawa Barat");setProjectCode("MEP-GKN-001");setProjectName("Gedung Kantor Nusantara");setPreparedBy("Engineer MEP");setCheckedBy("Lead MEP Engineer");setOwnerRepresentative("Project Manager / Owner Representative");setExampleLoaded(true);
    const now=new Date().toISOString();setProjects(modules.map((m,i)=>({id:`example-${i}`,name:`Gedung Kantor Nusantara · ${m.title}`,module:m.id,data:{...defaults[m.id]},result:calculate(m.id,defaults[m.id]),createdAt:now})));
    setModule("lift");setData({...defaults.lift});setView("dashboard");
  }
  async function saveProject() {
    setSaving(true);
    const res=await fetch("/api/projects",{method:"POST",headers:{"content-type":"application/json"},body:JSON.stringify({name:projectName,module,data,result})});
    if(res.ok){ const p=await res.json(); setProjects(x=>[p,...x]); } setSaving(false);
  }
  function askAgent(question=agentQuestion) {
    const clean=question.trim(); if(!clean)return;
    const reply=agentReply(clean,module,result,warnings,projects,checks,grandTotal,documents);
    setMessages(x=>[...x,{role:"user",text:clean},{role:"agent",text:reply}]); setAgentQuestion("");
  }
  async function addDocuments(files:FileList|null) {
    if(!files)return; const added:{name:string;text:string}[]=[];
    for(const file of Array.from(files).slice(0,8)){const readable=/\.(txt|csv|md)$/i.test(file.name);added.push({name:file.name,text:readable?(await file.text()).slice(0,12000):""});}
    setDocuments(x=>[...x,...added]);
    setMessages(x=>[...x,{role:"agent",text:`${added.length} dokumen ditambahkan: ${added.map(d=>d.name).join(", ")}. ${added.some(d=>d.text)?"Konteks teks siap digunakan.":"PDF/DOCX dicatat sebagai referensi; isi wajib diverifikasi engineer."}`}]);
  }
  const warnings = (()=>{
    const w:string[]=[];
    if(module==="pump" && n(result["Kecepatan aliran"])>3) w.push("Kecepatan aliran tinggi; evaluasi diameter pipa dan water hammer.");
    if(module==="pump" && n(result["Kecepatan aliran"])<.6) w.push("Kecepatan aliran rendah; evaluasi sedimentasi, ukuran pipa, biaya dan pola operasi.");
    if(module==="pump" && n(result["Bilangan Reynolds"])<4000) w.push("Aliran belum turbulen penuh; faktor gesek Darcy perlu dihitung sesuai regime aliran.");
    if(module==="pump" && n(result["Margin NPSH"])<1) w.push("Margin NPSH kurang dari 1 m; risiko kavitasi perlu ditinjau.");
    if(module==="pump" && n(result["Tekanan desain maksimum"])>10) w.push("Tekanan maksimum termasuk surge melebihi 10 bar; verifikasi pressure class pipa, fitting, valve, dan proteksi water hammer.");
    if(module==="pump" && n(data.minimumFlow)<10) w.push("Minimum continuous flow di bawah 10%; wajib dikonfirmasi dari kurva dan batas termal vendor.");
    if(module==="pump" && n(result["Deviasi operating point"])>n(data.bepTolerance)) w.push(`Operating point menyimpang ${fmt(n(result["Deviasi operating point"]))}% dari duty point; pilih ulang kurva, diameter impeller, rpm, atau strategi VFD.`);
    if(module==="hydrant" && n(result["Diameter hidraulik minimum"])>150) w.push("Debit membutuhkan diameter besar pada batas kecepatan 3 m/s; evaluasi ukuran pipa standar dan analisis jaringan.");
    if(module==="hydrant" && n(result["Rasio kecukupan reservoir"])<100) w.push(`Reservoir tersedia belum mencukupi; kebutuhan minimum ${fmt(n(result["Volume reservoir minimum"]))} m³.`);
    if(module==="hydrant" && n(data.fdcCount)<n(data.zones)) w.push("Jumlah FDC lebih sedikit daripada zona tekanan; periksa strategi fire department connection dan manifold.");
    if(module==="hydrant" && n(result["Kecepatan pipa terpilih"])>n(data.maxVelocity)) w.push(`Kecepatan pada diameter terpilih ${fmt(n(result["Kecepatan pipa terpilih"]))} m/s melampaui batas ${fmt(n(data.maxVelocity))} m/s.`);
    if(module==="hydrant" && n(result["Rasio kecukupan jangkauan"])<100) w.push("Panjang selang dan jangkauan nozzle belum menutup jarak tempuh maksimum; tambahkan titik atau revisi layout.");
    if(module==="hydrant" && n(result["Tekanan node terjauh"])<n(data.residualPressure)) w.push("Tekanan node terjauh di bawah target residual; evaluasi TDH, diameter, losses dan pressure zoning.");
    if(module==="hydrant") w.push("Verifikasi jumlah hidran simultan, durasi, tekanan residual, sambungan dinas kebakaran, dan konfigurasi pompa terhadap SNI/NFPA proyek.");
    if(module==="plumbing" && n(result["Kecepatan pipa utama"])>2.5) w.push("Kecepatan pipa utama tinggi; perbesar diameter untuk membatasi kebisingan dan water hammer.");
    if(module==="plumbing") w.push("Kebutuhan air dan cadangan harus disesuaikan dengan fungsi bangunan, jadwal operasi, sumber air, serta ketentuan daerah.");
    if(module==="electrical" && n(result["Jatuh tegangan estimasi"])>n(data.allowableDrop)) w.push(`Jatuh tegangan feeder ${fmt(n(result["Jatuh tegangan estimasi"]))}% melampaui batas ${fmt(n(data.allowableDrop))}%; perbesar kabel atau revisi distribusi.`);
    if(module==="electrical" && n(result["Tahanan grounding estimasi"])>n(result["Tahanan grounding maksimum"])) w.push("Tahanan grounding estimasi belum memenuhi batas tegangan sentuh; tambah/perdalam elektroda dan verifikasi pengukuran tanah.");
    if(module==="electrical" && n(result["Luas kabel Cu terpilih"])>=630) w.push("Kebutuhan penghantar mencapai batas tabel awal; evaluasi kabel paralel, busduct, metode pemasangan dan short-circuit withstand.");
    if(module==="electrical" && n(result["Jarak down conductor"])>20) w.push("Jarak antar down conductor proteksi petir relatif besar; tinjau kelas LPS, geometri, bonding dan standar proyek.");
    if(module==="electrical") w.push("Seleksi final wajib diverifikasi terhadap KHA tabel PUIL, harmonisa, netral, hubung singkat, koordinasi/selektivitas, SPD, pembumian, proteksi petir, gambar SLD dan data utilitas.");
    if(module==="lighting" && n(result["Iluminansi terpasang"])<n(data.targetLux)*.95) w.push("Iluminansi terpasang kurang dari 95% target; tambah armatur atau perbaiki UF, MF dan distribusi.");
    if(module==="lighting" && n(result["Iluminansi terpasang"])>n(data.targetLux)*1.2) w.push("Iluminansi terpasang lebih dari 120% target; evaluasi over-lighting, dimming dan efisiensi energi.");
    if(module==="lighting" && n(result["Rasio uniformity Emin/Eavg"])<.6) w.push("Uniformity Emin/Eavg di bawah 0,60; optimalkan spacing, distribusi fotometrik dan tata letak.");
    if(module==="lighting" && n(data.cri)<80) w.push("CRI di bawah 80; pastikan sesuai fungsi ruang dan kebutuhan identifikasi warna.");
    if(module==="lighting" && n(result["Lighting power density"])>10) w.push("LPD di atas screening 10 W/m²; bandingkan dengan batas fungsi ruang pada standar dan tingkatkan efikasi/kontrol.");
    if(module==="lighting") w.push("Finalisasi wajib memakai file fotometrik armatur, simulasi per ruang, evaluasi glare, daylight, emergency lighting, koordinasi ceiling dan pengukuran lux saat commissioning.");
    if(module==="fireSafety" && n(data.smokeShare)+n(data.heatShare)+n(data.flameShare)!==100) w.push("Pembagian area smoke, heat dan flame detector tidak berjumlah 100%; koreksi basis cakupan.");
    if(module==="fireSafety" && n(result["Rasio kecukupan air"])<100) w.push(`Water supply belum cukup; kebutuhan minimum ${fmt(n(result["Volume water supply minimum"]))} m³.`);
    if(module==="fireSafety" && n(result["Kapasitas baterai minimum"])>100) w.push("Kebutuhan baterai panel tinggi; bagi panel/loop atau verifikasi arus aktual setiap perangkat dan power supply booster.");
    if(module==="fireSafety" && n(data.ceilingHeight)>6) w.push("Plafon tinggi dapat memengaruhi tipe, spacing dan respons detector/sprinkler; lakukan evaluasi khusus sesuai listing dan standar.");
    if(module==="fireSafety" && n(result["Diameter pipa utama awal"])>200) w.push("Diameter pipa utama screening melebihi 200 mm; hydraulic calculation jaringan dan pembagian riser/zona wajib dilakukan.");
    if(module==="fireSafety") w.push("Jumlah perangkat adalah screening berbasis luas; finalisasi harus memakai layout aktual, obstruction, ceiling geometry, listing perangkat, hydraulic node calculation, cause-and-effect matrix dan persetujuan AHJ/fire engineer.");
    if(module==="communications" && n(result["Channel horizontal maksimum"])>100) w.push("Estimasi channel horizontal melampaui 100 m; tambah IDF atau revisi zoning dan jalur kabel.");
    if(module==="communications" && n(result["Kapasitas PABX terpilih"])<n(result["Extension desain"])) w.push("Kapasitas PABX belum menutup extension desain dan pertumbuhan.");
    if(module==="communications" && n(result["Pengguna HT per kanal"])>8) w.push("Rasio pengguna HT per kanal tinggi; evaluasi talk group, trafik puncak, repeater dan kanal tambahan.");
    if(module==="communications" && n(data.upsRuntime)<30) w.push("Autonomy UPS kurang dari 30 menit; sesuaikan dengan kebutuhan kontinuitas, genset dan SLA owner.");
    if(module==="communications" && n(result["Kebutuhan rack total"])>n(result["Jumlah rack"])*45) w.push("Kebutuhan rack melebihi kapasitas 45U terpasang; tambah rack atau kurangi kepadatan perangkat.");
    if(module==="communications") w.push("Finalisasi wajib memakai outlet schedule per ruang, route survey, loss/link budget bila fiber/radio, license matrix, QoS/cybersecurity, sertifikasi permanent-link, izin frekuensi dan technical submittal operator/vendor.");
    if(module==="lift" && n(result["Waktu tunggu"])>45) w.push("Waktu tunggu melampaui acuan perkantoran 25–45 detik pada buku.");
    if(module==="lift" && n(result["Rasio layanan"])<100) w.push("Daya angkut belum memenuhi estimasi beban puncak.");
    if(module==="lift" && n(data.speed)<n(result["Kecepatan rekomendasi"])) w.push("Kecepatan lift di bawah rekomendasi awal berdasarkan tinggi perjalanan zona.");
    if(module==="lift" && n(result["Efisiensi lantai"])<85) w.push("Core hoistway menyisakan efisiensi lantai di bawah 85%; koordinasikan ulang zoning dan arsitektur.");
    if(module==="lift" && n(result["Kepadatan beban kabin"])>350) w.push("Kepadatan beban kabin tinggi; verifikasi luas kabin efektif, kapasitas nominal, aksesibilitas, dan data vendor.");
    if(module==="lift" && n(data.regenSaving)>40) w.push("Penghematan regenerative drive di atas 40% harus dibuktikan dengan profil operasi dan data pabrikan.");
    if(module==="lift") w.push("Dimensi hoistway, pit, overhead, reaksi struktur, kontrol grup, proteksi kebakaran, dan daya final wajib dikonfirmasi dengan vendor lift/eskalator.");
    if(module==="escalator" && (n(data.angle)<27||n(data.angle)>35)) w.push("Sudut eskalator di luar rentang umum 27-35°; verifikasi geometri dan katalog vendor.");
    if(module==="escalator" && ![600,800,1000].includes(n(data.stepWidth))) w.push("Lebar step bukan ukuran umum 600/800/1000 mm; pastikan ketersediaan produk.");
    if(module==="escalator" && n(data.speed)>.75) w.push("Kecepatan di atas 0,75 m/s memerlukan evaluasi keselamatan, fungsi, dan regulasi proyek.");
    if(module==="escalator" && n(result["Rasio kecukupan unit"])<100) w.push(`Jumlah unit rencana belum cukup; minimum ${fmt(n(result["Jumlah unit minimum"]),0)} unit untuk arus puncak dan cadangan yang dimasukkan.`);
    if(module==="escalator" && n(data.reversibleUnits)>n(result["Unit aktif minimum"])) w.push("Jumlah unit reversible melebihi kebutuhan unit aktif; periksa kembali konfigurasi operasi.");
    if(module==="escalator") w.push("Verifikasi support structure, pit/landing, clear height, balustrade, brake, emergency stop, fire interface, kontrol VVVF, dan technical submittal vendor.");
    if(module==="genset" && n(result["Utilisasi steady-state"])>85) w.push("Utilisasi steady-state di atas 85%; margin ekspansi dan respons transien terbatas.");
    if(module==="genset" && n(result["Utilisasi steady-state"])<30) w.push("Utilisasi steady-state di bawah 30%; evaluasi oversizing, wet stacking, pembagian multi-set, dan load bank berkala.");
    if(module==="genset" && n(result["Kebutuhan saat starting"])>n(result["Kapasitas total terpasang"])) w.push("Kapasitas terpasang belum menutup kebutuhan starting motor; terapkan sequential start, soft starter/VFD, atau tambah kapasitas.");
    if(module==="genset" && n(result["Back pressure exhaust"])>10) w.push("Screening back pressure exhaust melebihi 10 mbar; batas final wajib mengikuti data engine vendor.");
    if(module==="genset" && n(result["Selisih target kebisingan"])>0) w.push(`Prediksi kebisingan melampaui target ${fmt(n(result["Selisih target kebisingan"]))} dB; tambah attenuator, enclosure atau treatment ruang.`);
    if(module==="genset" && n(data.standbyUnits)===0) w.push("Tidak ada genset cadangan; verifikasi konsekuensi kegagalan dan kebutuhan N+1 untuk beban life-safety/kritis.");
    if(module==="genset") w.push("Rating, derating, step-load, voltage dip, emisi, exhaust back pressure, fuel system, ventilasi dan akustik wajib dikonfirmasi dari technical submittal integrator.");
    if(module==="ac" && n(result["Sensible heat ratio"])<.65) w.push("Beban laten dominan; evaluasi dehumidification, supply air condition dan reheat bila diperlukan.");
    if(module==="ac" && n(result["Sensible heat ratio"])>.95) w.push("Beban laten sangat rendah; periksa kembali data kelembapan dan udara luar.");
    if(module==="ac" && n(data.indoorRh)>60) w.push("Target kelembapan dalam di atas 60% dapat meningkatkan risiko kondensasi dan kenyamanan yang buruk.");
    if(module==="ac" && n(data.freshAirPerson)<5) w.push("Udara luar per orang rendah; verifikasi standar ventilasi sesuai fungsi ruang.");
    if(module==="ac" && n(data.ductVelocity)>8) w.push("Kecepatan duct utama tinggi; tinjau noise, pressure loss dan ukuran duct.");
    if(module==="ac" && n(data.condensateSlope)<1) w.push("Slope drain kondensat di bawah 1%; tingkatkan kemiringan atau buktikan dengan koordinasi elevasi.");
    if(module==="ac") w.push("Hasil adalah desain konseptual; verifikasi dengan metode CLTD/RTS/heat-balance, data cuaca, orientasi, schedule, psychrometric process, pressure loss, kurva fan/pump dan data pabrikan.");
    return w;
  })();

  return <main>
    <header className="topbar"><div className="brand"><span className="brandMark">M</span><div><b>MASTER PROGRAM MEP</b><small>ENGINEER AI · VERSION 4.0</small></div></div><div className="viewSwitch"><button className={view==="brief"?"active":""} onClick={()=>setView("brief")}>0. Design Brief</button><button className={view==="building"?"active":""} onClick={()=>setView("building")}>1. Bangunan–Lantai–Ruang</button><button className={view==="design"?"active":""} onClick={() => setView("design")}>Workspace</button><button className={view==="solver"?"active":""} onClick={()=>setView("solver")}>Solver Lab</button><button className={view==="ded"?"active":""} onClick={()=>setView("ded")}>DED Center</button><button className={view==="dashboard"?"active":""} onClick={()=>setView("dashboard")}>Dashboard</button><button className={view==="report"?"active":""} onClick={()=>setView("report")}>Laporan Akhir</button></div><a className="masterDownload" href="/MEP_Engineer_AI_Master_Standalone_v3.0.html" target="_blank">Master HTML</a><div className="status"><span></span> MASTER ACTIVE</div></header>
    <section className="hero"><div><p className="eyebrow">MASTER PROGRAM · MECHANICAL · ELECTRICAL · PLUMBING</p><h1>Desain MEP yang<br/><em>terukur, terlacak, berulang.</em></h1><p className="lead">Acuan induk pengembangan MEP Engineer AI berbasis buku <i>Mekanikal Elektrikal</i> dan <i>Mekanikal Elektrikal Lanjutan</i> Ir. Sunarno, M.Eng., Ph.D. Kini dilengkapi solver deterministik serta pusat delivery DED untuk koordinasi, compliance, gambar, BOQ, testing dan approval proyek nyata.</p></div><div className="heroStat"><b>4.0</b><span>MASTER VERSION</span><small>{technicalSubmoduleCount} submodul · 5 engineering solver</small></div></section>
    <section className="exampleLauncher"><div><span>PROJECT VALIDATION PACK</span><h2>Proyek Contoh Gedung Kantor Nusantara</h2><p>Mengisi Design Brief, data proyek dan sebelas modul sekaligus untuk menguji alur input, kalkulasi, audit, dashboard dan laporan akhir.</p></div><div className="exampleScore"><b>{examplePass}/11</b><span>mesin perhitungan lulus</span></div><button onClick={loadExampleProject}>{exampleLoaded?"Muat Ulang Proyek Contoh":"Jalankan Proyek Contoh"}</button></section>

    {view==="brief"&&<section className="briefSection"><div className="briefHead"><div><p>TAHAP 0 · DATA PROYEK BERSAMA + BRIEF MODUL</p><h2>Design Brief {modules.find(m=>m.id===briefModule)?.title}</h2><span>Data umum dipakai bersama; data teknis disesuaikan dengan Workspace terpilih.</span></div><div className="briefScore"><b>{briefProgress}%</b><span>{answeredRequired}/{requiredBrief.length} data wajib modul</span></div></div><div className="briefModulePicker">{modules.map(m=>{const qs=briefQuestions.filter(q=>q.modules?.includes(m.id)||(!q.modules&&((m.id==="lift"||m.id==="escalator")||commonBriefIds.has(q.id)))),req=qs.filter(q=>q.required),done=req.filter(q=>briefAnswers[q.id]?.trim()).length,pct=Math.round(done/Math.max(1,req.length)*100);return <button key={m.id} className={briefModule===m.id?"active":""} onClick={()=>openBriefModule(m.id)}><span>{m.no}</span><b>{m.title}</b><small>{pct}% lengkap</small></button>})}</div><div className="briefArchitecture"><span>01 · Data Proyek Bersama</span><i>→</i><span>02 · Brief {modules.find(m=>m.id===briefModule)?.title}</span><i>→</i><span>03 · Workspace & Audit</span></div><div className="briefProgress"><i style={{width:`${briefProgress}%`}}></i></div><div className="briefTabs">{briefCategories.map(c=>{const qs=activeBriefQuestions.filter(q=>q.category===c),done=qs.filter(q=>briefAnswers[q.id]?.trim()).length;return <button key={c} className={briefCategory===c?"active":""} onClick={()=>setBriefCategory(c)}><b>{c}</b><span>{done}/{qs.length}</span></button>})}</div><div className="briefGrid">{activeBriefQuestions.filter(q=>q.category===briefCategory).map((q,i)=><label key={q.id}><span>{String(i+1).padStart(2,"0")} · {q.question}{q.required&&<b>*</b>}</span>{q.options?<select value={briefAnswers[q.id]||""} onChange={e=>setBriefAnswers({...briefAnswers,[q.id]:e.target.value})}><option value="">Pilih jawaban…</option>{q.options.map(x=><option key={x}>{x}</option>)}</select>:<input value={briefAnswers[q.id]||""} placeholder={q.placeholder||"Masukkan data/catatan"} onChange={e=>setBriefAnswers({...briefAnswers,[q.id]:e.target.value})}/>}</label>)}</div><div className="briefSummary"><div><h3>Gate Design Brief {modules.find(m=>m.id===briefModule)?.title}</h3><b className={briefProgress===100?"ready":"hold"}>{briefProgress===100?"SIAP DAN TERSINKRON KE WORKSPACE":"DATA WAJIB MODUL BELUM LENGKAP"}</b><p>{missingBrief.length?`${missingBrief.length} pertanyaan wajib masih kosong. Workspace tetap dapat dibuka sebagai draft; finalisasi menunggu kelengkapan brief.`:"Seluruh data wajib terisi. Nilai numerik yang sesuai akan dipindahkan otomatis ke Workspace; engineer tetap memverifikasi bukti pendukung."}</p><button onClick={applyBriefToWorkspace}>Buka Draft Workspace {modules.find(m=>m.id===briefModule)?.title} →</button></div><div><h3>Data request tersisa</h3>{missingBrief.length?<ul>{missingBrief.slice(0,12).map(q=><li key={q.id}><b>{q.category}</b>{q.question}</li>)}</ul>:<p>Tidak ada data wajib yang kosong. Cetak halaman ini sebagai lampiran design criteria modul.</p>}<button onClick={()=>window.print()}>Cetak Design Brief Modul</button></div></div></section>}

    {view==="building"&&<section className="hierarchySection"><div className="hierarchyHead"><div><p>TAHAP 1 · INPUT SUMBER TERSTRUKTUR</p><h2>Bangunan → Lantai → Ruangan</h2><span>Satu sumber data untuk kalkulasi per ruang, rekap lantai, rekap bangunan, dan sinkronisasi modul MEP.</span></div><div className="briefScore"><b>{hierarchyProgress}%</b><span>{validRooms}/{totalRooms} ruang ekuivalen valid</span></div></div><div className="hierarchyFlow"><b>01 · Identitas bangunan</b><i>→</i><b>02 · Susunan lantai</b><i>→</i><b>03 · Data ruangan</b><i>→</i><b>04 · Rekap & sinkronisasi</b></div><div className="buildingMeta"><label>Nama bangunan<input value={buildingName} onChange={e=>setBuildingName(e.target.value)}/></label><label>Fungsi utama<input value={buildingFunction} onChange={e=>setBuildingFunction(e.target.value)}/></label></div><div className="hierarchyKpis"><article><small>LANTAI LOGIS</small><b>{logicalFloors}</b><span>{floors.length} kelompok lantai</span></article><article><small>RUANG EKUIVALEN</small><b>{totalRooms}</b><span>termasuk lantai tipikal</span></article><article><small>LUAS BANGUNAN</small><b>{fmt(buildingSummary.area,0)} <i>m²</i></b><span>luas MEP terdata</span></article><article><small>POPULASI DESAIN</small><b>{fmt(buildingSummary.occupants,0)}</b><span>orang</span></article></div><div className="hierarchyBuilder"><aside><div className="floorActions"><button onClick={addFloor}>+ Lantai</button><button onClick={duplicateFloor}>Duplikat</button></div>{floors.map((f,i)=><button key={f.id} className={activeFloor?.id===f.id?"active":""} onClick={()=>setActiveFloorId(f.id)}><span>{String(i+1).padStart(2,"0")}</span><div><b>{f.name}</b><small>{f.rooms.length} tipe ruang × {f.multiplier} lantai</small></div></button>)}</aside>{activeFloor&&<div className="floorEditor"><div className="floorTitle"><div><small>KELOMPOK LANTAI AKTIF</small><h3>{activeFloor.name}</h3></div><button onClick={removeFloor} disabled={floors.length<2}>Hapus lantai</button></div><div className="floorFields"><label>Nama / rentang lantai<input value={activeFloor.name} onChange={e=>updateFloor(activeFloor.id,"name",e.target.value)}/></label><label>Elevasi awal (m)<input type="number" value={activeFloor.elevation} onChange={e=>updateFloor(activeFloor.id,"elevation",+e.target.value)}/></label><label>Jumlah lantai tipikal<input type="number" min="1" value={activeFloor.multiplier} onChange={e=>updateFloor(activeFloor.id,"multiplier",Math.max(1,+e.target.value))}/><small>Contoh Lantai 2–8 = 7</small></label></div><div className="roomHeader"><div><h3>Daftar ruangan</h3><p>Setiap perubahan langsung memperbarui hasil ruang, lantai, dan bangunan.</p></div><button onClick={addRoom}>+ Tambah Ruang</button></div><div className="roomGrid">{activeFloor.rooms.map((r,i)=>{const x=roomCalc(r);return <article className="roomCard" key={r.id}><div className="roomCardHead"><span>{String(i+1).padStart(2,"0")}</span><input value={r.name} onChange={e=>updateRoom(activeFloor.id,r.id,"name",e.target.value)}/><button onClick={()=>duplicateRoom(r)}>Duplikat</button><button onClick={()=>removeRoom(r.id)} disabled={activeFloor.rooms.length<2}>Hapus</button></div><div className="roomFields">{[["function","Fungsi ruang",r.function],["area","Luas (m²)",r.area],["height","Tinggi (m)",r.height],["occupants","Penghuni",r.occupants],["lightingWm2","LPD (W/m²)",r.lightingWm2],["equipmentW","Peralatan (W)",r.equipmentW],["outlets","Outlet",r.outlets],["targetLux","Target lux",r.targetLux]].map(([k,l,v])=><label key={String(k)}>{l}<input type={k==="function"?"text":"number"} value={v} onChange={e=>updateRoom(activeFloor.id,r.id,k as keyof RoomRecord,k==="function"?e.target.value:+e.target.value)}/></label>)}</div><div className="roomResults"><span><b>{fmt(x.lighting)}</b> kW lampu</span><span><b>{fmt(x.coolingTR)}</b> TR</span><span><b>{fmt(x.power)}</b> kW listrik</span><span><b>{x.detectors}</b> detector</span><span><b>{x.sprinklers}</b> sprinkler</span><span><b>{fmt(x.freshAir,0)}</b> L/s udara luar</span></div></article>})}</div></div>}</div><div className="hierarchySummary"><h3>Rekap per lantai dan bangunan</h3><table><thead><tr><th>Kelompok lantai</th><th>Pengali</th><th>Luas (m²)</th><th>Orang</th><th>Lampu (kW)</th><th>Pendinginan (TR)</th><th>Listrik (kW)</th><th>Detector</th><th>Sprinkler</th></tr></thead><tbody>{floorSummaries.map(f=><tr key={f.id}><td><b>{f.name}</b></td><td>× {f.multiplier}</td><td>{fmt(f.weighted.area)}</td><td>{fmt(f.weighted.occupants,0)}</td><td>{fmt(f.weighted.lighting)}</td><td>{fmt(f.weighted.coolingTR)}</td><td>{fmt(f.weighted.power)}</td><td>{fmt(f.weighted.detectors,0)}</td><td>{fmt(f.weighted.sprinklers,0)}</td></tr>)}<tr className="total"><td colSpan={2}>TOTAL BANGUNAN</td><td>{fmt(buildingSummary.area)}</td><td>{fmt(buildingSummary.occupants,0)}</td><td>{fmt(buildingSummary.lighting)}</td><td>{fmt(buildingSummary.coolingTR)}</td><td>{fmt(buildingSummary.power)}</td><td>{fmt(buildingSummary.detectors,0)}</td><td>{fmt(buildingSummary.sprinklers,0)}</td></tr></tbody></table><div className="syncGrid"><div><b>Sinkronkan data agregat ke Workspace</b><p>Modul menerima parameter yang relevan; nilai tetap dapat disunting engineer.</p></div>{(["lighting","ac","electrical","plumbing","fireSafety","communications"] as Module[]).map(m=><button key={m} onClick={()=>syncHierarchy(m)}>→ {modules.find(x=>x.id===m)?.title}</button>)}</div></div></section>}

    {view==="building"&&<section className="dedReadiness"><div className="hierarchyTools"><div><b>DATA EXCHANGE & DELIVERABLES</b><p>Impor daftar ruang, ekspor schedule, dan cetak lembar data proyek.</p></div><button onClick={()=>downloadHierarchyWorkbook(true)}>Unduh Template Excel</button><label>Impor Excel<input type="file" accept=".xlsx,.xls,.csv" onChange={e=>e.target.files?.[0]&&importHierarchyExcel(e.target.files[0])}/></label><button onClick={()=>downloadHierarchyWorkbook(false)}>Ekspor Semua Schedule</button><button onClick={()=>window.print()}>Cetak Data Sheet</button>{importMessage&&<p className={importMessage.startsWith("Impor gagal")?"importError":"importOk"}>{importMessage}</p>}</div><div className="dataSheet"><div className="dataSheetHead"><div><small>MEP-RDS-001</small><h3>ROOM DATA SHEET</h3><p>{buildingName} · {buildingFunction}</p></div><b>{hierarchyProgress}% COMPLETE</b></div><table><thead><tr><th>Lantai</th><th>Ruang/Fungsi</th><th>Luas</th><th>Orang</th><th>Lux/LPD</th><th>Lampu</th><th>Cooling</th><th>Daya</th><th>Fire</th></tr></thead><tbody>{floors.flatMap(f=>f.rooms.map(r=>{const x=roomCalc(r);return <tr key={r.id}><td>{f.name}<small>×{f.multiplier}</small></td><td><b>{r.name}</b><small>{r.function}</small></td><td>{fmt(r.area)} m²</td><td>{r.occupants}</td><td>{r.targetLux} lx<small>{r.lightingWm2} W/m²</small></td><td>{fmt(x.lighting)} kW</td><td>{fmt(x.coolingTR)} TR</td><td>{fmt(x.power)} kW</td><td>{x.detectors} D / {x.sprinklers} S</td></tr>}))}</tbody></table></div><div className="dataSheet"><div className="dataSheetHead"><div><small>MEP-FS-001</small><h3>FLOOR SCHEDULE</h3><p>Rekap beban terkoordinasi per kelompok lantai</p></div><b>{logicalFloors} LANTAI LOGIS</b></div><table><thead><tr><th>Lantai</th><th>Pengali</th><th>Luas</th><th>Populasi</th><th>Lampu</th><th>Cooling</th><th>Daya</th><th>Outlet</th><th>Detector/Sprinkler</th></tr></thead><tbody>{floorSummaries.map(f=><tr key={f.id}><td><b>{f.name}</b></td><td>× {f.multiplier}</td><td>{fmt(f.weighted.area)} m²</td><td>{fmt(f.weighted.occupants,0)}</td><td>{fmt(f.weighted.lighting)} kW</td><td>{fmt(f.weighted.coolingTR)} TR</td><td>{fmt(f.weighted.power)} kW</td><td>{fmt(f.weighted.outlets,0)}</td><td>{fmt(f.weighted.detectors,0)} / {fmt(f.weighted.sprinklers,0)}</td></tr>)}</tbody></table></div><div className="buildingSheet"><div className="dataSheetHead"><div><small>MEP-BS-001</small><h3>BUILDING SUMMARY & AI AUDIT</h3><p>Rekap final sumber data DED</p></div><b>{hierarchyAudit.length?"HOLD / INCOMPLETE":"READY FOR REVIEW"}</b></div><div className="buildingSummaryGrid">{[["Luas total",`${fmt(buildingSummary.area)} m²`],["Populasi",`${fmt(buildingSummary.occupants,0)} orang`],["Beban lampu",`${fmt(buildingSummary.lighting)} kW`],["Beban pendinginan",`${fmt(buildingSummary.coolingTR)} TR`],["Estimasi daya",`${fmt(buildingSummary.power)} kW`],["Air bersih",`${fmt(buildingSummary.water)} m³/hari`],["Fresh air",`${fmt(buildingSummary.freshAir)} L/s`],["Outlet",`${fmt(buildingSummary.outlets,0)} titik`],["Detector",`${fmt(buildingSummary.detectors,0)} unit`],["Sprinkler",`${fmt(buildingSummary.sprinklers,0)} unit`]].map(([k,v])=><article key={k}><small>{k}</small><b>{v}</b></article>)}</div><div className="aiAudit"><h3>AI Agent · Pemeriksaan Kelengkapan</h3>{hierarchyAudit.length?<><p>Ditemukan <b>{hierarchyAudit.length} isu</b>. Data belum boleh dilepas ke DED final.</p><table><thead><tr><th>Lantai</th><th>Ruang</th><th>Temuan</th><th>Tindakan</th></tr></thead><tbody>{hierarchyAudit.map((a,i)=><tr key={i}><td>{a.floor}</td><td>{a.room}</td><td>{a.issue}</td><td>Lengkapi pada kartu ruang</td></tr>)}</tbody></table></>:<p className="auditPass">Seluruh ruang memenuhi pemeriksaan kelengkapan minimum. Lanjutkan engineering review, compliance check, dan approval.</p>}</div></div><div className="drawingSheet"><div className="dataSheetHead"><div><small>MEP-SCH-001</small><h3>SINKRONISASI DED</h3><p>Jejak Room Data hingga laporan akhir</p></div><b>TRACEABLE</b></div><div className="dedFlow"><article><b>ROOM DATA</b><span>Fungsi · luas · orang · lux</span></article><i>→</i><article><b>FLOOR SCHEDULE</b><span>Beban · zoning · riser</span></article><i>→</i><article><b>LOAD SCHEDULE</b><span>MEP per disiplin</span></article><i>→</i><article><b>BOQ & GAMBAR</b><span>Equipment · layout · SLD</span></article><i>→</i><article><b>LAPORAN</b><span>Calculation · audit · approval</span></article></div><div className="syncMatrix">{[["Load Schedule","Beban ruang/lantai → elektrikal, AC, plumbing, fire, ICT"],["BOQ","Kuantitas hasil kalkulasi → equipment dan quantity schedule"],["Gambar","Ruang/lantai/zoning → layout, riser, schematic dan SLD"],["Laporan Akhir","Room/Floor/Building schedule → calculation report"]].map(([a,b])=><div key={a}><b>{a}</b><span>{b}</span><strong>TERHUBUNG</strong></div>)}</div></div></section>}

    {view==="ded"&&<section className="dedCenter"><div className="dedHero"><div><small>PROJECT DELIVERY · DED / TENDER / IFC</small><h2>DED Delivery Center</h2><p>Menghubungkan perhitungan seluruh bab dengan koordinasi, gambar, schedule, spesifikasi, BOQ, compliance, risiko, testing dan approval.</p></div><div className="dedScore"><b>{dedReadiness}%</b><span>{dedReadiness===100?"READY FOR ENGINEER RELEASE":"HOLD · LENGKAPI BUKTI"}</span></div></div><div className="dedKpis"><article><small>FLOW SELESAI</small><b>{dedFlowDone}/{dedFlow.length}</b><span>Tahapan proyek</span></article><article><small>KEBUTUHAN BAB</small><b>{dedChapterDone}/{dedChapterTotal}</b><span>Perhitungan dan deliverable khusus</span></article><article><small>REGISTER DISETUJUI</small><b>{dedRegisterDone}/{dedRegisters.length}</b><span>Dokumen delivery proyek</span></article><article><small>STATUS RELEASE</small><b>{dedReadiness===100?"READY":"HOLD"}</b><span>Persetujuan engineer tetap wajib</span></article></div><div className="dedBlock"><div className="dedTitle"><div><small>FLOW IDEAL PROYEK NYATA</small><h3>Design Brief hingga commissioning</h3></div><span>Klik tahap setelah bukti tersedia</span></div><div className="dedFlowGrid">{dedFlow.map((step,i)=><label key={step} className={dedChecks[`flow:${i}`]?"done":""}><input type="checkbox" checked={!!dedChecks[`flow:${i}`]} onChange={()=>toggleDEDCheck(`flow:${i}`)}/><span>{String(i+1).padStart(2,"0")}</span><b>{step}</b>{i<dedFlow.length-1&&<i>→</i>}</label>)}</div></div><div className="dedBlock"><div className="dedTitle"><div><small>CHAPTER ENGINEERING COMPLETENESS</small><h3>Kekurangan khusus setiap bab → gate fungsional</h3></div><span>{modules.length} disiplin</span></div><div className="dedModuleTabs">{modules.map(m=>{const req=chapterDEDRequirements[m.id],done=req.filter((_,i)=>dedChecks[`chapter:${m.id}:${i}`]).length;return <button key={m.id} className={dedModule===m.id?"active":""} onClick={()=>setDEDModule(m.id)}><span>{m.no}</span><b>{m.title}</b><small>{done}/{req.length}</small></button>})}</div><div className="dedChapter"><div><small>BAB AKTIF</small><h3>{modules.find(m=>m.id===dedModule)?.title}</h3><p>Gate ini menutup kebutuhan engineering yang belum cukup diwakili oleh kalkulasi dasar. Centang hanya setelah perhitungan/gambar/bukti benar-benar tersedia.</p><button onClick={()=>{setModule(dedModule);setData(defaults[dedModule]);setView("design")}}>Buka Workspace Bab →</button></div><div>{chapterDEDRequirements[dedModule].map((req,i)=><label key={req} className={dedChecks[`chapter:${dedModule}:${i}`]?"done":""}><input type="checkbox" checked={!!dedChecks[`chapter:${dedModule}:${i}`]} onChange={()=>toggleDEDCheck(`chapter:${dedModule}:${i}`)}/><span>{String(i+1).padStart(2,"0")}</span><b>{req}</b><em>{dedChecks[`chapter:${dedModule}:${i}`]?"TERVERIFIKASI":"BELUM"}</em></label>)}</div></div></div><div className="dedBlock"><div className="dedTitle"><div><small>15 MODUL DELIVERY PROYEK</small><h3>Register dokumen DED terpadu</h3></div><button onClick={addDEDRegister}>+ Tambah Register</button></div><div className="dedRegister"><table><thead><tr><th>No.</th><th>Kategori</th><th>Deliverable</th><th>PIC</th><th>Status</th><th>Nomor/Bukti Dokumen</th><th></th></tr></thead><tbody>{dedRegisters.map((row,i)=><tr key={row.id}><td>{String(i+1).padStart(2,"0")}</td><td><input value={row.category} onChange={e=>updateDEDRegister(row.id,"category",e.target.value)}/></td><td><input value={row.deliverable} onChange={e=>updateDEDRegister(row.id,"deliverable",e.target.value)}/></td><td><input value={row.owner} onChange={e=>updateDEDRegister(row.id,"owner",e.target.value)}/></td><td><select value={row.status} onChange={e=>updateDEDRegister(row.id,"status",e.target.value)}><option>Belum Mulai</option><option>Berjalan</option><option>Siap Review</option><option>Disetujui</option></select></td><td><input value={row.evidence} placeholder="Nomor gambar, revisi, file, approval" onChange={e=>updateDEDRegister(row.id,"evidence",e.target.value)}/></td><td><button onClick={()=>removeDEDRegister(row.id)}>×</button></td></tr>)}</tbody></table></div></div><div className="dedRelease"><div><small>RELEASE RULE</small><h3>{dedReadiness===100?"Paket memenuhi gate internal program":"Paket belum boleh dinyatakan DED/IFC final"}</h3><p>Nilai 100% menunjukkan seluruh checklist program telah diisi, tetapi tidak menggantikan validasi solver teknik, pemeriksaan standar terbaru, data vendor, koordinasi BIM/CAD, persetujuan owner/AHJ dan tanda tangan engineer berwenang.</p></div><div><b>PRIORITAS BERIKUTNYA</b><ol><li>Solver hidraulik node–pipe Bab II</li><li>Load/panel schedule dan SLD Bab V</li><li>Fotometri point-by-point Bab VI</li><li>Cause-effect dan hydraulic fire Bab VII</li><li>Outlet/rack/backbone schedule Bab VIII</li><li>Generator gambar dan spesifikasi</li><li>Benchmark serta regression test proyek</li></ol></div></div></section>}

    {view==="dashboard"&&<section className="dashboard">
      <div className="dashHead"><div><p>DASHBOARD PROYEK TERPADU</p><h2>{projectName}</h2><span>{projectCode} · {clientName} · {projectLocation}</span></div><button onClick={()=>window.print()}>Cetak Paket Laporan</button></div>
      <div className="projectIdentity"><label>Kode proyek<input value={projectCode} onChange={e=>setProjectCode(e.target.value)}/></label><label>Pemberi tugas<input value={clientName} onChange={e=>setClientName(e.target.value)}/></label><label>Lokasi proyek<input value={projectLocation} onChange={e=>setProjectLocation(e.target.value)}/></label><label>Nama paket<input value={projectName} onChange={e=>setProjectName(e.target.value)}/></label></div>
      <div className="projectPackage"><div><small>MASTER PROJECT PACKAGE</small><h3>Pindahkan, cadangkan, atau lanjutkan proyek pada perangkat lain</h3><p>Paket memuat identitas, Design Brief, data bangunan–lantai–ruang, hasil tersimpan, dan status seluruh readiness gate.</p></div><button onClick={exportProjectPackage}>Ekspor Paket Proyek</button><label>Impor Paket Proyek<input type="file" accept=".json,application/json" onChange={e=>e.target.files?.[0]&&importProjectPackage(e.target.files[0])}/></label>{packageMessage&&<span>{packageMessage}</span>}</div>
      <div className="kpiGrid"><article><small>BAB TERDOKUMENTASI</small><b>{completedModules}<i>/11</i></b><span>Cakupan desain tersimpan</span></article><article><small>SUBMODUL SIAP REVIEW</small><b>{completedTechnicalSubmodules}<i>/{technicalSubmoduleCount}</i></b><span>Seluruh enam gate harus terverifikasi</span></article><article><small>REKAMAN PERHITUNGAN</small><b>{projects.length}</b><span>Riwayat desain proyek</span></article><article><small>STATUS PAKET PROYEK</small><b>{Math.round((coverage+completedTechnicalSubmodules/technicalSubmoduleCount*100+hierarchyProgress)/3)}<i>%</i></b><span>Bab, submodul, dan data ruangan</span></article></div>
      <div className="validationPanel"><div><span>AUTOMATED EXAMPLE TEST</span><h3>Validasi sebelas mesin perhitungan</h3><p>Setiap kartu memastikan input contoh menghasilkan nilai numerik positif dan keluaran utama dapat dibaca.</p></div><div className="validationGrid">{exampleTests.map((t,i)=><article key={t.name} className={t.pass?"pass":"fail"}><span>{String(i+1).padStart(2,"0")}</span><div><b>{t.name}</b><small>{t.note}</small></div><strong>{t.pass?"LULUS":"GAGAL"}</strong></article>)}</div><button onClick={()=>setView("report")} disabled={!exampleLoaded}>Buka Laporan Akhir Owner →</button></div>
      <div className="dashGrid"><div className="coverageCard"><h3>Cakupan disiplin MEP</h3>{modules.map(m=>{const count=projects.filter(p=>p.module===m.id).length;return <div className="coverageRow" key={m.id}><span>{m.no}</span><b>{m.title}</b><div><i style={{width:`${count?100:4}%`}}></i></div><small>{count?`${count} desain`:"Belum ada"}</small></div>})}</div>
      <div className="reportCard"><h3>Rekap laporan terbaru</h3>{projects.length?projects.slice(0,7).map(p=><button key={p.id} onClick={()=>{setView("design");setModule(p.module);setData(p.data);setProjectName(p.name)}}><span>{p.module.toUpperCase()}</span><b>{p.name}</b><small>{new Date(p.createdAt).toLocaleDateString("id-ID")}</small></button>):<p>Belum ada hasil tersimpan. Buka Workspace dan simpan setiap modul untuk membentuk paket laporan.</p>}</div></div>
      <div className="reportStructure"><h3>Struktur paket laporan MEP</h3><div>{["Sampul & identitas proyek","Ringkasan eksekutif","Dasar desain & asumsi","Rekap perhitungan sebelas modul","BOQ dan RAB awal","Temuan agen review","Checklist koordinasi","Kesimpulan & persetujuan"].map((x,i)=><span key={x}><b>{String(i+1).padStart(2,"0")}</b>{x}</span>)}</div></div>
    </section>}

    {view==="report"&&<section className="ownerReport">
      <div className="reportToolbar"><div><b>FINAL CALCULATION REPORT</b><span>{exampleLoaded?"Proyek contoh terisi · siap diperiksa":"Isi data proyek atau jalankan proyek contoh terlebih dahulu"}</span></div><button onClick={()=>window.print()}>Cetak / Simpan PDF</button></div>
      <article className="reportCover"><div className="coverBrand"><span>MEP</span><b>ENGINEER AI</b></div><div className="coverTitle"><small>MECHANICAL · ELECTRICAL · PLUMBING</small><h1>LAPORAN PERHITUNGAN<br/>DESAIN MEP</h1><p>Calculation, Design Criteria, Equipment Schedule & Engineering Review</p></div><div className="coverProject"><span>Nama Proyek</span><b>{projectName}</b><span>Pemberi Tugas</span><b>{clientName}</b><span>Lokasi</span><b>{projectLocation}</b></div><div className="coverMeta"><div><span>Nomor Dokumen</span><b>{projectCode}-CALC-MEP</b></div><div><span>Revisi</span><b>{reportRevision}</b></div><div><span>Status</span><b>{reportStatus}</b></div><div><span>Tanggal</span><b>{new Date().toLocaleDateString("id-ID",{day:"2-digit",month:"long",year:"numeric"})}</b></div></div><p className="confidential">DOKUMEN TEKNIS · UNTUK REVIEW DAN PERSETUJUAN PEMILIK PROYEK</p></article>
      <article className="reportPage reportControl"><h2>LEMBAR KENDALI DOKUMEN</h2><div className="reportEditGrid"><label>Nomor revisi<input value={reportRevision} onChange={e=>setReportRevision(e.target.value)}/></label><label>Status dokumen<select value={reportStatus} onChange={e=>setReportStatus(e.target.value)}><option>Draft</option><option>Design Review</option><option>Issued for Approval</option><option>Approved for Tender</option></select></label><label>Disusun oleh<input value={preparedBy} onChange={e=>setPreparedBy(e.target.value)}/></label><label>Diperiksa oleh<input value={checkedBy} onChange={e=>setCheckedBy(e.target.value)}/></label><label>Perwakilan owner<input value={ownerRepresentative} onChange={e=>setOwnerRepresentative(e.target.value)}/></label></div><table><thead><tr><th>Rev.</th><th>Tanggal</th><th>Uraian penerbitan</th><th>Disusun</th><th>Diperiksa</th><th>Disetujui</th></tr></thead><tbody><tr><td>{reportRevision}</td><td>{new Date().toLocaleDateString("id-ID")}</td><td>{reportStatus}</td><td>{preparedBy}</td><td>{checkedBy}</td><td>{ownerRepresentative}</td></tr></tbody></table><div className="reportNotice"><b>STATUS OTOMATIS</b><span>{exampleLoaded&&examplePass===exampleTests.length?"SELURUH MESIN KALKULASI CONTOH LULUS":"BELUM LENGKAP"}</span><p>Kelulusan mesin menunjukkan fungsi program berjalan pada data contoh. Kelayakan proyek nyata tetap bergantung pada kebenaran data lapangan, standar, koordinasi, technical submittal, serta pengesahan engineer.</p></div></article>
      <article className="reportPage"><h2>1. RINGKASAN EKSEKUTIF</h2><p>Laporan ini menyajikan basis perencanaan dan hasil perhitungan awal sistem mekanikal, elektrikal, pencahayaan, komunikasi, plumbing, transportasi vertikal, proteksi kebakaran, pembangkit darurat, dan tata udara untuk <b>{projectName}</b>. Perhitungan disusun sebagai dasar pengambilan keputusan owner, koordinasi multidisiplin, pengembangan DED, penyusunan spesifikasi dan evaluasi penawaran.</p><div className="executiveGrid">{exampleTests.map(t=><div key={t.name}><span>{t.name}</span><b>{t.note}</b><small>{t.pass?"Perhitungan terbentuk":"Perlu koreksi"}</small></div>)}</div><h3>Kesimpulan awal</h3><p>Sebelas modul menghasilkan keluaran terukur dengan jejak input, formula, satuan dan pemeriksaan rentang. Status laporan tetap <b>{reportStatus}</b>; nilai final peralatan dan konstruksi harus dikonfirmasi melalui gambar terkoordinasi, standar terbaru, survei, data utilitas dan submittal vendor.</p></article>
      <article className="reportPage"><h2>2. DASAR DESAIN DAN ASUMSI</h2><table className="basisTable"><tbody>{[["Sumber dasar","Buku Mekanikal Elektrikal, Ir. Sunarno, M.Eng., Ph.D. (2005) sebagai acuan awal struktur materi"],["Tahap desain",reportStatus],["Fungsi contoh","Gedung kantor bertingkat"],["Lokasi",projectLocation],["Tegangan basis","400/230 V, 3 fasa, 50 Hz"],["Kriteria mutu","Traceable, repeatable, reviewable, extensible"],["Referensi final","SNI, PUIL, ketentuan PUPR/otoritas, NFPA/ASHRAE dan data pabrikan yang berlaku untuk kontrak"],["Batas penggunaan","Perhitungan konseptual/preliminary; bukan pengganti verifikasi dan tanda tangan engineer berwenang"]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table><h3>Daftar formula utama</h3><div className="formulaRegister">{[["Lift","W = T/N; handling = 300 × kapasitas × unit / RTT"],["Eskalator","Q = 3600 × v/pitch × okupansi × faktor kapasitas"],["Pompa","Hₜ = Hstatis + ΔHp + hf mayor + hf minor; P = ρgQH/η"],["Hidran","Qfire = n × q; V = Q × durasi × cadangan"],["Plumbing","Qhari = populasi × kebutuhan; Qpeak = Qavg × Fp"],["Elektrikal","I₃φ = P/(√3 × V × cos φ)"],["Genset","Sgen = max(Ssteady, Sstarting) × faktor derating"],["AC","Qtotal = Qselubung + Qinternal + Qudara luar sensibel + laten"]].map((x,i)=><div key={x[0]}><span>{String(i+1).padStart(2,"0")}</span><b>{x[0]}</b><code>{x[1]}</code></div>)}</div></article>
      <article className="reportPage"><h2>3. REKAP HIERARKI RUANG–LANTAI–BANGUNAN</h2><p>Basis beban disusun dari setiap ruangan, direkap pada kelompok lantai, lalu dikalikan menurut jumlah lantai tipikal. Struktur ini menjaga keterlacakan perubahan desain dari sumber input hingga total bangunan.</p><table className="basisTable"><tbody><tr><th>Bangunan</th><td>{buildingName}</td></tr><tr><th>Fungsi</th><td>{buildingFunction}</td></tr><tr><th>Lantai logis</th><td>{logicalFloors}</td></tr><tr><th>Ruang ekuivalen</th><td>{totalRooms}</td></tr><tr><th>Kelengkapan input</th><td>{hierarchyProgress}%</td></tr></tbody></table><table><thead><tr><th>Lantai</th><th>Pengali</th><th>Luas</th><th>Populasi</th><th>Lampu</th><th>Cooling</th><th>Daya</th></tr></thead><tbody>{floorSummaries.map(f=><tr key={f.id}><td>{f.name}</td><td>× {f.multiplier}</td><td>{fmt(f.weighted.area)} m²</td><td>{fmt(f.weighted.occupants,0)}</td><td>{fmt(f.weighted.lighting)} kW</td><td>{fmt(f.weighted.coolingTR)} TR</td><td>{fmt(f.weighted.power)} kW</td></tr>)}<tr><th colSpan={2}>TOTAL</th><th>{fmt(buildingSummary.area)} m²</th><th>{fmt(buildingSummary.occupants,0)}</th><th>{fmt(buildingSummary.lighting)} kW</th><th>{fmt(buildingSummary.coolingTR)} TR</th><th>{fmt(buildingSummary.power)} kW</th></tr></tbody></table><p><b>Catatan engineering:</b> Estimasi otomatis per ruang merupakan screening awal untuk koordinasi dan pembentukan brief. Perhitungan DED tetap memerlukan kriteria proyek, diversity, zoning, kondisi desain, analisis hidraulik/elektrik, data vendor, dan review engineer berwenang.</p></article>
      {modules.map((m,idx)=>{const rr=calculate(m.id,defaults[m.id]),nar=designNarratives[m.id],refs=standards.filter(s=>s.modules.includes(m.id));return <article className="reportPage moduleCalc" key={m.id}><div className="moduleReportHead"><span>{String(idx+3).padStart(2,"0")}</span><div><small>CALCULATION SHEET · MODUL {m.no}</small><h2>{m.title.toUpperCase()}</h2></div><strong>ENGINEERING REVIEW</strong></div><section className="designNarrative"><h3>Narasi dan tujuan desain</h3><p>{nar.purpose}</p><h3>Dasar dan acuan desain</h3><p>{nar.basis}</p><table><thead><tr><th>Acuan</th><th>Status</th><th>Penerapan</th></tr></thead><tbody>{refs.map(s=><tr key={s.code}><td><b>{s.code}</b><br/><small>{s.title}</small></td><td>{s.role}</td><td>{s.use}</td></tr>)}</tbody></table><h3>Kriteria dan metode</h3><p><b>Kriteria:</b> {nar.criteria}</p><p><b>Metode:</b> {nar.method}</p></section><div className="calcColumns"><div><h3>Data masukan utama</h3><table><tbody>{fieldSets[m.id].slice(0,12).map(f=><tr key={f.key}><th>{f.label}</th><td>{fmt(n(defaults[m.id][f.key]))} {f.unit}</td></tr>)}</tbody></table></div><div><h3>Hasil perhitungan utama</h3><table><tbody>{Object.entries(rr).slice(0,12).map(([k,v])=><tr key={k}><th>{k}</th><td>{fmt(n(v))} {units(m.id,k)}</td></tr>)}</tbody></table></div></div><div className="moduleInterpret"><b>Interpretasi engineering</b><p>{nar.conclusion} Hasil tetap harus dibandingkan dengan kondisi lapangan, analisis detail, kurva pabrikan dan acceptance criteria proyek.</p></div></article>})}
      <article className="reportPage"><h2>11. EQUIPMENT, BOQ DAN ESTIMASI BIAYA</h2><p>BOQ berikut merupakan struktur awal. Volume final mengikuti gambar DED terkoordinasi; harga satuan mengikuti penawaran, HSPK/AHSP yang relevan, lokasi, spesifikasi, pajak dan ketentuan kontrak.</p>{modules.map(m=>{const rr=calculate(m.id,defaults[m.id]);return <div className="reportBoq" key={m.id}><h3>{m.no} · {m.title}</h3><table><thead><tr><th>Uraian</th><th>Volume</th><th>Satuan</th><th>Harga Satuan</th><th>Jumlah</th></tr></thead><tbody>{boq(m.id,defaults[m.id],rr).map((row,i)=><tr key={i}><td>{row[0]}</td><td>{row[1]}</td><td>{row[2]}</td><td>Diisi QS/Estimator</td><td>—</td></tr>)}</tbody></table></div>})}</article>
      <article className="reportPage"><h2>12. AUDIT, RISIKO DAN REKOMENDASI</h2><div className="riskMatrix">{[["Data proyek","Brief, survei dan as-built belum terverifikasi","Tinggi","Validasi data dan approval design criteria"],["Standar","Edisi/pasal dapat berubah","Tinggi","Buat compliance matrix standar terbaru"],["Vendor","Kapasitas/kurva contoh bukan selection final","Tinggi","Minta technical submittal dan deviation list"],["Koordinasi","Potensi clash ruang dan jalur utilitas","Sedang","Lakukan coordination drawing/BIM review"],["Biaya","Harga dan volume masih allowance","Sedang","Sinkronkan DED, BOQ dan quotation"],["Commissioning","Kinerja aktual belum dibuktikan","Tinggi","Siapkan method statement, TAB dan acceptance test"]].map((x,i)=><div key={x[0]}><span>{String(i+1).padStart(2,"0")}</span><b>{x[0]}</b><p>{x[1]}</p><strong>{x[2]}</strong><em>{x[3]}</em></div>)}</div><h3>Rekomendasi penerbitan</h3><p>Laporan dapat diajukan kepada owner sebagai dokumen <b>{reportStatus}</b>. Peningkatan ke Approved for Tender/IFC dilakukan setelah seluruh data wajib, koordinasi, compliance matrix, equipment selection, gambar, BOQ dan bukti review engineer dinyatakan lengkap.</p></article>
      <article className="reportPage"><h2>13. STATUS DELIVERY DED</h2><div className="dedReportKpi"><div><small>FLOW</small><b>{dedFlowDone}/{dedFlow.length}</b></div><div><small>KEBUTUHAN BAB</small><b>{dedChapterDone}/{dedChapterTotal}</b></div><div><small>REGISTER DISETUJUI</small><b>{dedRegisterDone}/{dedRegisters.length}</b></div><div><small>READINESS</small><b>{dedReadiness}%</b></div></div><table><thead><tr><th>Deliverable</th><th>PIC</th><th>Status</th><th>Bukti</th></tr></thead><tbody>{dedRegisters.map(row=><tr key={row.id}><td>{row.deliverable}</td><td>{row.owner}</td><td>{row.status}</td><td>{row.evidence||"Belum dilampirkan"}</td></tr>)}</tbody></table><p><b>Release decision:</b> {dedReadiness===100?"seluruh gate internal program telah dipenuhi dan paket dapat diajukan kepada engineer berwenang untuk keputusan penerbitan.":"paket masih berstatus HOLD; lengkapi flow, kebutuhan khusus bab dan register beserta bukti dokumen."}</p></article>\n<article className="reportPage approvalPage"><h2>14. LEMBAR PERSETUJUAN</h2><p>Dengan menandatangani lembar ini, para pihak menyatakan telah menerima dokumen untuk status yang tercantum. Persetujuan tidak menghapus kewajiban perencana, kontraktor, vendor dan engineer untuk memenuhi standar, spesifikasi, keselamatan dan ketentuan kontrak.</p><div className="signatureGrid"><div><b>DISUSUN OLEH</b><span className="signSpace"></span><strong>{preparedBy}</strong><small>MEP Engineer</small><em>Tanggal: __________________</em></div><div><b>DIPERIKSA OLEH</b><span className="signSpace"></span><strong>{checkedBy}</strong><small>Lead/Coordinator MEP</small><em>Tanggal: __________________</em></div><div><b>DISETUJUI OWNER</b><span className="signSpace"></span><strong>{ownerRepresentative}</strong><small>Owner Representative</small><em>Tanggal: __________________</em></div></div><div className="approvalDecision"><label><input type="checkbox"/> Disetujui</label><label><input type="checkbox"/> Disetujui dengan catatan</label><label><input type="checkbox"/> Revisi dan ajukan kembali</label><p>Catatan owner: ____________________________________________________________________________________</p></div></article>
    </section>}

    {view==="report"&&<article className="reportPage submoduleReport"><h2>LAMPIRAN A · STRUKTUR MODUL TEKNIS</h2><p>Setiap bab dipecah menjadi workspace kerja engineer. Semua submodul memakai identitas proyek, Design Brief, sumber data bangunan–lantai–ruang, mesin hitung, audit dan laporan yang sama agar tidak terjadi input ganda.</p>{modules.map(m=><section key={m.id}><h3>BAB {m.no} · {m.title}</h3><table><thead><tr><th>Kode</th><th>Modul teknis</th><th>Ruang lingkup</th><th>Keluaran DED</th><th>Status gate</th></tr></thead><tbody>{(submoduleCatalog[m.id]||[]).map(sm=><tr key={sm.id}><td><b>{sm.code}</b></td><td>{sm.title}</td><td>{sm.scope}</td><td>{sm.deliverables.join(" · ")}</td><td><b>{(submoduleChecks[`${m.id}:${sm.id}`]||[]).filter(Boolean).length}/{engineeringGates.length}</b><br/><small>{(submoduleChecks[`${m.id}:${sm.id}`]||[]).filter(Boolean).length===engineeringGates.length?"SIAP REVIEW":"BELUM LENGKAP"}</small></td></tr>)}</tbody></table></section>)}</article>}

    {view==="report"&&<article className="reportPage"><h2>LAMPIRAN B · CODE & STANDARDS REGISTER</h2><p>Register ini menetapkan basis pemeriksaan program. Status hukum, edisi, amandemen, pasal, ketentuan daerah dan persyaratan AHJ harus diverifikasi kembali pada tanggal penerbitan dokumen.</p><table><thead><tr><th>Modul</th><th>Kode / standar</th><th>Peran</th><th>Penerapan desain</th><th>Sumber</th></tr></thead><tbody>{standards.map(s=><tr key={s.code}><td>{s.modules.map(m=>modules.find(x=>x.id===m)?.title).join(", ")}</td><td><b>{s.code}</b><br/><small>{s.title}</small></td><td>{s.role}</td><td>{s.use}</td><td>{s.source}</td></tr>)}</tbody></table><div className="reportNotice"><b>HIERARKI DOKUMEN</b><span>REGULASI/AHJ → SNI/PUIL → SPESIFIKASI KONTRAK → STANDAR INTERNASIONAL → DATA PABRIKAN</span><p>Jika terdapat konflik, engineer mencatatnya pada compliance matrix dan deviation register untuk keputusan tertulis owner/AHJ.</p></div></article>}

    <nav className="moduleNav" style={{display:view==="design"?undefined:"none"}}>{modules.map(m=><button key={m.id} onClick={()=>select(m.id)} className={module===m.id?"active":""}><span>{m.no}</span><div><b>{m.title}</b><small>{m.hint}</small></div></button>)}</nav>

    <section className="workspace" style={{display:view==="design"?undefined:"none"}}>
      <div className="calcPanel">
        <div className="sectionTitle"><div><span>MODUL {current.no}</span><h2>{current.title}</h2></div><span className="sourceTag">SUMBER BUKU · 2005</span></div>
        <label className="projectName">Nama proyek<input value={projectName} onChange={e=>setProjectName(e.target.value)}/></label>
        <section className="submoduleHub"><div className="submoduleHead"><div><span>WORKSPACE TEKNIS TERPISAH · BAB {current.no}</span><h3>Pilih bagian desain yang sedang dikerjakan</h3></div><b>{technicalSubmodules.length} MODUL</b></div><div className="submoduleGrid">{technicalSubmodules.map(sm=><button key={sm.id} className={selectedSubmodule?.id===sm.id?"active":""} onClick={()=>openTechnicalSubmodule(sm)}><span>{sm.code}</span><b>{sm.title}</b><small>{sm.scope}</small></button>)}</div>{selectedSubmodule&&<div className="submoduleDetail"><div><small>RUANG LINGKUP AKTIF</small><b>{selectedSubmodule.code} · {selectedSubmodule.title}</b><p>{selectedSubmodule.scope}</p></div><div>{selectedSubmodule.deliverables.map(x=><span key={x}>✓ {x}</span>)}</div></div>}<div className="engineeringGate"><div className="gateScore"><small>READINESS GATE</small><b>{selectedGateScore}%</b><span className={selectedGateScore===100?"ready":"hold"}>{selectedGateScore===100?"SIAP REVIEW ENGINEER":"BELUM SIAP DITERBITKAN"}</span></div><div className="gateChecklist">{engineeringGates.map((gate,i)=><label key={gate} className={selectedGateChecks[i]?"checked":""}><input type="checkbox" checked={selectedGateChecks[i]||false} onChange={()=>toggleSubmoduleGate(i)}/><span>{String(i+1).padStart(2,"0")}</span><b>{gate}</b></label>)}</div></div><p className="gateDisclaimer">Checklist merekam kesiapan dokumen, bukan menggantikan perhitungan, pemeriksaan lapangan, approval owner/AHJ, atau tanda tangan engineer berwenang.</p></section>
        {!moduleBriefReady(module)&&<div className="draftNotice"><b>MODE DRAFT · DATA CONTOH</b><span>Workspace dapat digunakan untuk simulasi. Lengkapi Design Brief {current.title} sebelum status laporan dinaikkan ke tender/final.</span><button onClick={()=>{openBriefModule(module);setView("brief")}}>Lengkapi Design Brief</button></div>}
        {module==="lift"&&<div className="wizardSteps">{liftFlowSteps.map((s,i)=><button key={s.title} className={liftStep===i?"active":liftStep>i?"done":""} onClick={()=>setLiftStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="pump"&&<div className="wizardSteps">{pumpFlowSteps.map((s,i)=><button key={s.title} className={pumpStep===i?"active":pumpStep>i?"done":""} onClick={()=>setPumpStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="hydrant"&&<div className="wizardSteps">{hydrantFlowSteps.map((s,i)=><button key={s.title} className={hydrantStep===i?"active":hydrantStep>i?"done":""} onClick={()=>setHydrantStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="genset"&&<div className="wizardSteps gensetWizard">{gensetFlowSteps.map((s,i)=><button key={s.title} className={gensetStep===i?"active":gensetStep>i?"done":""} onClick={()=>setGensetStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="ac"&&<div className="wizardSteps acWizard">{acFlowSteps.map((s,i)=><button key={s.title} className={acStep===i?"active":acStep>i?"done":""} onClick={()=>setAcStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="electrical"&&<div className="wizardSteps electricalWizard">{electricalFlowSteps.map((s,i)=><button key={s.title} className={electricalStep===i?"active":electricalStep>i?"done":""} onClick={()=>setElectricalStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="lighting"&&<div className="wizardSteps electricalWizard">{lightingFlowSteps.map((s,i)=><button key={s.title} className={lightingStep===i?"active":lightingStep>i?"done":""} onClick={()=>setLightingStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="fireSafety"&&<div className="wizardSteps gensetWizard">{fireSafetyFlowSteps.map((s,i)=><button key={s.title} className={fireSafetyStep===i?"active":fireSafetyStep>i?"done":""} onClick={()=>setFireSafetyStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="communications"&&<div className="wizardSteps gensetWizard">{communicationsFlowSteps.map((s,i)=><button key={s.title} className={communicationsStep===i?"active":communicationsStep>i?"done":""} onClick={()=>setCommunicationsStep(i)}><span>{String(i+1).padStart(2,"0")}</span><b>{s.title}</b><small>{s.hint}</small></button>)}</div>}
        {module==="lift"&&<div className="stepGuide"><b>TAHAP {liftStep+1}: {liftFlowSteps[liftStep].title}</b><span>{liftStep<5?"Isi parameter yang tersedia. Hasil di kanan berubah otomatis.":"Periksa hasil, peringatan, lalu pilih pembanding vendor tanpa mengunci merek."}</span></div>}
        {module==="pump"&&<div className="stepGuide"><b>TAHAP {pumpStep+1}: {pumpFlowSteps[pumpStep].title}</b><span>{pumpStep<7?"Isi parameter hidraulik tahap ini; hasil diperbarui otomatis.":"Pilih jenis, material dan produk pembanding; cocokkan duty point dengan kurva resmi vendor."}</span></div>}
        {module==="hydrant"&&<div className="stepGuide"><b>TAHAP {hydrantStep+1}: {hydrantFlowSteps[hydrantStep].title}</b><span>{hydrantStep<6?"Isi kriteria fire protection tahap ini; hasil diperbarui otomatis.":"Periksa equipment schedule, referensi vendor dan seluruh gate keselamatan."}</span></div>}
        {module==="genset"&&<div className="stepGuide"><b>TAHAP {gensetStep+1}: {gensetFlowSteps[gensetStep].title}</b><span>{gensetStep<8?"Isi data tahap ini. Kapasitas, bahan bakar, ruang, exhaust dan kebisingan diperbarui otomatis.":"Tutup temuan instalasi dan kerusakan sebelum paket desain diterbitkan."}</span></div>}
        {module==="ac"&&<div className="stepGuide"><b>TAHAP {acStep+1}: {acFlowSteps[acStep].title}</b><span>{acStep<8?"Isi parameter ruang dan sistem. Beban sensibel, laten, kapasitas dan distribusi diperbarui otomatis.":acStep===8?"Bandingkan jenis sistem dan vendor tanpa mengunci merek.":"Periksa audit, BOQ dan paket DED sebelum finalisasi."}</span></div>}
        {module==="electrical"&&<div className="stepGuide"><b>TAHAP {electricalStep+1}: {electricalFlowSteps[electricalStep].title}</b><span>{electricalStep<6?"Isi data tahap ini; beban, KHA, short-circuit, grounding dan proteksi petir diperbarui otomatis.":"Periksa SLD, schedule, BOQ, compliance matrix dan bukti pengujian sebelum penerbitan DED."}</span></div>}
        {module==="lighting"&&<div className="stepGuide"><b>TAHAP {lightingStep+1}: {lightingFlowSteps[lightingStep].title}</b><span>{lightingStep<6?"Isi data ruang, kriteria dan armatur; lux, layout, LPD dan energi diperbarui otomatis.":"Periksa simulasi fotometrik, emergency lighting, control zoning, BOQ dan bukti pengukuran sebelum penerbitan DED."}</span></div>}
        {module==="fireSafety"&&<div className="stepGuide"><b>TAHAP {fireSafetyStep+1}: {fireSafetyFlowSteps[fireSafetyStep].title}</b><span>{fireSafetyStep<8?"Isi data proteksi tahap ini; jumlah perangkat, loop, baterai, debit dan water supply diperbarui otomatis.":"Periksa cause-and-effect, riser, hydraulic calculation, schedule, BOQ dan acceptance test sebelum penerbitan DED."}</span></div>}
        {module==="communications"&&<div className="stepGuide"><b>TAHAP {communicationsStep+1}: {communicationsFlowSteps[communicationsStep].title}</b><span>{communicationsStep<8?"Isi kebutuhan komunikasi tahap ini; extension, trunk, outlet, kabel, rack dan UPS diperbarui otomatis.":"Periksa telecom riser, numbering plan, rack/cable schedule, BOQ dan certification test sebelum penerbitan DED."}</span></div>}
        {module==="hydrant"&&hydrantStep===0&&<div className="hydrantSelectors"><label>Klasifikasi bahaya<select value={hazardClass} onChange={e=>setHazardClass(e.target.value)}><option>Light hazard</option><option>Ordinary hazard / gedung umum</option><option>High hazard</option><option>Special hazard - perlu fire engineering</option></select></label><label>Jenis sistem<select value={hydrantType} onChange={e=>setHydrantType(e.target.value)}><option>Indoor hydrant + hose reel</option><option>Indoor standpipe/hydrant</option><option>Outdoor yard hydrant</option><option>Combined indoor + outdoor</option></select></label></div>}
        <div className="fields">{activeLiftFields.map(f=><label key={f.key}><span>{f.label}</span><div><input type="number" step="any" min={f.min} value={data[f.key]} onChange={e=>setData({...data,[f.key]:e.target.value})}/><small>{f.unit}</small></div></label>)}</div>
        {module==="escalator"&&<div className="vendorPicker"><label>Produk pembanding eskalator<select value={escalatorVendorId} onChange={e=>setEscalatorVendorId(e.target.value)}>{escalatorVendors.map(v=><option value={v.id} key={v.id}>{v.brand} — {v.model}</option>)}</select></label><div className="vendorSpec"><b>{selectedEscalatorVendor.brand} · {selectedEscalatorVendor.model}</b><p>{selectedEscalatorVendor.use}</p><span>Rise maks.<strong>{selectedEscalatorVendor.maxRise} m</strong></span><span>Speed<strong>{selectedEscalatorVendor.speeds} m/s</strong></span><span>Step width<strong>{selectedEscalatorVendor.widths} mm</strong></span><small>Sudut katalog: {selectedEscalatorVendor.angles}°. Ketersediaan, motor, truss, pit, support, finishing dan opsi keselamatan wajib dikonfirmasi melalui technical submittal vendor Indonesia.</small></div></div>}
        {module==="lift"&&liftStep===5&&<div className="vendorPicker"><label>Produk pembanding<select value={vendorId} onChange={e=>setVendorId(e.target.value)}>{liftVendors.map(v=><option value={v.id} key={v.id}>{v.brand} — {v.model}</option>)}</select></label><div className="vendorSpec"><b>{selectedVendor.brand} · {selectedVendor.model}</b><p>{selectedVendor.application}</p><span>Travel maks. <strong>{selectedVendor.maxTravel} m</strong></span><span>Speed maks. <strong>{selectedVendor.maxSpeed} m/s</strong></span><span>Load <strong>{selectedVendor.maxLoad}</strong></span><small>{selectedVendor.note}</small></div><button onClick={applyVendor}>Gunakan kapasitas & kecepatan contoh</button></div>}
        {module==="pump"&&pumpStep===5&&<div className="pumpConfiguration"><div><small>KONFIGURASI OTOMATIS</small><b>{fmt(n(data.dutyPumps),0)} duty + {fmt(n(data.standbyPumps),0)} standby</b><p>{fmt(n(data.seriesPumps),0)} pompa seri/tingkat tekanan · {fmt(n(result["Debit per pompa duty"]))} m³/jam per duty · {fmt(n(result["Head per pompa seri"]))} m per pompa seri.</p></div><div><small>SCREENING WATER HAMMER</small><b>{fmt(n(result["Tekanan desain maksimum"]))} bar</b><p>Surge head {fmt(n(result["Surge head estimasi"]))} m; valve closure kritis {fmt(n(result["Waktu penutupan kritis"]))} detik. Lanjutkan transient analysis bila konsekuensi kegagalan tinggi.</p></div></div>}
        {module==="pump"&&pumpStep===6&&<div className="curvePanel"><div className="curveHead"><div><small>KURVA SISTEM × KURVA POMPA</small><h3>Operating point otomatis</h3></div><strong className={n(result["Deviasi operating point"])<=n(data.bepTolerance)?"ok":"warn"}>{n(result["Deviasi operating point"])<=n(data.bepTolerance)?"DALAM TOLERANSI":"PERLU SELEKSI ULANG"}</strong></div><svg viewBox={`0 0 ${pumpChart.W} ${pumpChart.H}`} role="img" aria-label="Grafik kurva sistem dan kurva pompa"><line x1={pumpChart.pad} y1={pumpChart.H-pumpChart.pad} x2={pumpChart.W-pumpChart.pad} y2={pumpChart.H-pumpChart.pad}/><line x1={pumpChart.pad} y1={pumpChart.pad} x2={pumpChart.pad} y2={pumpChart.H-pumpChart.pad}/><polyline className="systemCurve" points={pumpChart.system}/><polyline className="pumpCurve" points={pumpChart.pump}/><circle cx={pumpChart.opX} cy={pumpChart.opY} r="6"/><text x={pumpChart.opX+10} y={pumpChart.opY-8}>Operating point</text><text x={pumpChart.W/2} y={pumpChart.H-8}>Debit Q (m³/jam per pompa)</text><text x="8" y="20">Head H (m)</text></svg><div className="curveLegend"><span><i className="system"></i>Kurva sistem</span><span><i className="pump"></i>Kurva pompa estimasi</span><b>Qop {fmt(n(result["Operating flow kurva"]))} m³/jam · Hop {fmt(n(result["Operating head kurva"]))} m · Deviasi {fmt(n(result["Deviasi operating point"]))}%</b></div><p>Kurva pompa di layar adalah estimasi parabolik dari duty head dan shut-off factor. Ganti/validasi dengan kurva resmi vendor sebelum persetujuan model.</p></div>}
        {module==="pump"&&pumpStep===7&&<div className="vendorPicker"><div className="pumpSelectionGrid"><label>Jenis rekomendasi<input value={recommendedPumpType} readOnly/></label><label>Impeller<select value={pumpImpeller} onChange={e=>setPumpImpeller(e.target.value)}><option>Closed impeller</option><option>Semi-open impeller</option><option>Open impeller</option><option>Vortex / non-clog</option></select></label><label>Material<select value={pumpMaterial} onChange={e=>setPumpMaterial(e.target.value)}><option>Cast iron casing / SS shaft</option><option>Stainless steel 304</option><option>Stainless steel 316</option><option>Bronze fitted</option></select></label><label>Seal<select value={pumpSeal} onChange={e=>setPumpSeal(e.target.value)}><option>Mechanical seal</option><option>Double mechanical seal</option><option>Gland packing</option></select></label><label>Kontrol<select value={pumpControl} onChange={e=>setPumpControl(e.target.value)}><option>VFD + pressure sensor</option><option>Constant speed</option><option>Level control</option><option>Duty alternation</option></select></label><label>Produk pembanding<select value={pumpVendorId} onChange={e=>setPumpVendorId(e.target.value)}>{pumpVendors.map(v=><option value={v.id} key={v.id}>{v.brand} — {v.series}</option>)}</select></label></div><div className="vendorSpec"><b>{selectedPumpVendor.brand} · {selectedPumpVendor.series}</b><p>{selectedPumpVendor.type}</p><span>Duty per pompa<strong>{fmt(n(result["Debit per pompa duty"]))} m³/jam</strong></span><span>Head per seri<strong>{fmt(n(result["Head per pompa seri"]))} m</strong></span><span>Motor/pompa<strong>{fmt(n(result["Daya motor desain per pompa"]))} kW</strong></span><small>Aplikasi: {selectedPumpVendor.application}. Model, diameter impeller, rpm, BEP, efisiensi, NPSHr, material, seal dan motor final wajib dipilih dari kurva serta technical submittal vendor.</small></div></div>}
        {module==="hydrant"&&hydrantStep===5&&<div className="hydrantLayout"><article><small>REKOMENDASI TITIK</small><b>{fmt(n(result["Jumlah titik hidran"]),0)} titik</b><span>{fmt(n(result["Titik hidran per lantai"]),0)} titik/lantai · radius {fmt(n(data.coverageRadius))} m</span></article><article><small>ZONA & FDC</small><b>{fmt(n(data.zones),0)} zona / {fmt(n(result["FDC minimum"]),0)} FDC</b><span>Head rata-rata per zona {fmt(n(result["Head per zona"]))} m</span></article><article><small>RESERVOIR</small><b>{fmt(n(result["Volume reservoir minimum"]))} m³</b><span>Kecukupan tersedia {fmt(n(result["Rasio kecukupan reservoir"]))}%</span></article></div>}
        {module==="hydrant"&&hydrantStep===6&&<div className="vendorPicker"><div className="hydrantSelectors"><label>Fire pump package pembanding<select value={firePumpVendorId} onChange={e=>setFirePumpVendorId(e.target.value)}>{firePumpVendors.map(v=><option key={v.id} value={v.id}>{v.brand} — {v.series}</option>)}</select></label><label>Konfigurasi<input readOnly value="Electric main + standby + jockey / sesuai fire strategy"/></label></div><div className="vendorSpec"><b>{selectedFirePumpVendor.brand} · {selectedFirePumpVendor.series}</b><p>{selectedFirePumpVendor.type}</p><span>Duty<strong>{fmt(n(result["Debit kebakaran"]))} L/s</strong></span><span>TDH<strong>{fmt(n(result["Total dynamic head"]))} m</strong></span><span>Motor awal<strong>{fmt(n(result["Daya motor fire pump"]))} kW</strong></span><small>{selectedFirePumpVendor.note} Merek hanya pembanding dan harus ditulis “atau setara”.</small></div></div>}
        {module==="genset"&&gensetStep===7&&<div className="vendorPicker"><div className="pumpSelectionGrid"><label>Rating operasi<select value={gensetRating} onChange={e=>setGensetRating(e.target.value)}><option>Emergency standby</option><option>Standby</option><option>Prime</option><option>Continuous</option></select></label><label>Kontrol<select value={gensetControl} onChange={e=>setGensetControl(e.target.value)}><option>AMF + ATS automatic transfer</option><option>AMF + ATS + load shedding</option><option>Multi-set synchronizing</option><option>Synchronizing utility + genset</option></select></label><label>Produk pembanding<select value={gensetVendorId} onChange={e=>setGensetVendorId(e.target.value)}>{gensetVendors.map(v=><option value={v.id} key={v.id}>{v.brand} — {v.series}</option>)}</select></label></div><div className="vendorSpec"><b>{selectedGensetVendor.brand} · {selectedGensetVendor.series}</b><p>{gensetRating} · {gensetControl}</p><span>Kapasitas/unit<strong>{fmt(n(result["Kapasitas per unit"]),0)} kVA</strong></span><span>Jumlah<strong>{fmt(n(data.units),0)} duty + {fmt(n(data.standbyUnits),0)} standby</strong></span><span>Utilisasi<strong>{fmt(n(result["Utilisasi steady-state"]))}%</strong></span><small>{selectedGensetVendor.note} Merek hanya pembanding “atau setara”; integrator wajib menyerahkan step-load, voltage dip, fuel curve, heat rejection, airflow, exhaust limit, noise dan emisi.</small></div></div>}
        {module==="genset"&&gensetStep===8&&<div className="gensetFaults"><h3>Diagnostik kerusakan & tindakan desain</h3>{[["Gagal start","Baterai/charger, fuel starvation, starter, interlock, emergency stop"],["Tegangan/frekuensi tidak stabil","AVR, governor, step-load terlalu besar, sensing dan tuning"],["Overheating","Airflow pendek/sirkulasi balik, radiator kotor, ambient tinggi"],["Asap berlebih/wet stacking","Beban rendah lama, injector, kualitas BBM dan pembakaran"],["Getaran/kebisingan","Alignment, flexible coupling, isolator, foundation, exhaust support"],["Trip proteksi","Overcurrent, earth fault, reverse power, over/under V-Hz, coolant/oil"]].map((x,i)=><article key={x[0]}><span>{String(i+1).padStart(2,"0")}</span><div><b>{x[0]}</b><p>{x[1]}</p></div><label><input type="checkbox"/> Ditinjau</label></article>)}</div>}
        {module==="ac"&&acStep===8&&<div className="vendorPicker"><div className="pumpSelectionGrid"><label>Jenis sistem<select value={acSystem} onChange={e=>setAcSystem(e.target.value)}><option>Split/DX</option><option>Multi-split</option><option>VRF/VRV</option><option>Air-cooled chiller</option><option>Water-cooled chiller</option><option>Package/AHU</option></select></label><label>Produk pembanding<select value={acVendorId} onChange={e=>setAcVendorId(e.target.value)}>{acVendors.map(v=><option value={v.id} key={v.id}>{v.brand} — {v.series}</option>)}</select></label></div><div className="vendorSpec"><b>{selectedAcVendor.brand} · {selectedAcVendor.series}</b><p>{selectedAcVendor.type} · {selectedAcVendor.use}</p><span>Beban desain<strong>{fmt(n(result["Beban pendinginan desain"])/1000)} kW</strong></span><span>Kapasitas<strong>{fmt(n(result["Ton refrigerasi"]))} TR</strong></span><span>Daya input awal<strong>{fmt(n(result["Daya input sistem"]))} kW</strong></span><small>Model final wajib dipilih dari kapasitas terkoreksi kondisi luar/dalam, panjang pipa, diversity, fan static, sound, COP/IPLV, refrigerant, kontrol, serviceability dan technical submittal Indonesia.</small></div></div>}
        {module==="ac"&&acStep===9&&<div className="acProcess"><article><small>PSIKROMETRIK</small><b>{fmt(n(result["Enthalpy luar"]))} → {fmt(n(result["Enthalpy dalam"]))} kJ/kg</b><p>Humidity ratio {fmt(n(result["Humidity ratio luar"]),4)} → {fmt(n(result["Humidity ratio dalam"]),4)} kg/kg.</p></article><article><small>BEBAN</small><b>{fmt(n(result["Beban sensibel ruang"])/1000)} + {fmt(n(result["Beban laten ruang"])/1000)} kW</b><p>SHR {fmt(n(result["Sensible heat ratio"]),3)} · total desain {fmt(n(result["Beban pendinginan desain"])/1000)} kW.</p></article><article><small>DISTRIBUSI</small><b>{fmt(n(result["Debit supply air"]))} m³/s</b><p>Duct awal {fmt(n(result["Luas duct utama awal"]))} m² · CHW {fmt(n(result["Debit chilled water awal"]))} L/s.</p></article></div>}
        {module==="lift"&&<div className="wizardNav"><button disabled={liftStep===0} onClick={()=>setLiftStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{liftStep+1} / {liftFlowSteps.length}</span><button disabled={liftStep===liftFlowSteps.length-1} onClick={()=>setLiftStep(x=>Math.min(liftFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="pump"&&<div className="wizardNav"><button disabled={pumpStep===0} onClick={()=>setPumpStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{pumpStep+1} / {pumpFlowSteps.length}</span><button disabled={pumpStep===pumpFlowSteps.length-1} onClick={()=>setPumpStep(x=>Math.min(pumpFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="hydrant"&&<div className="wizardNav"><button disabled={hydrantStep===0} onClick={()=>setHydrantStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{hydrantStep+1} / {hydrantFlowSteps.length}</span><button disabled={hydrantStep===hydrantFlowSteps.length-1} onClick={()=>setHydrantStep(x=>Math.min(hydrantFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="genset"&&<div className="wizardNav"><button disabled={gensetStep===0} onClick={()=>setGensetStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{gensetStep+1} / {gensetFlowSteps.length}</span><button disabled={gensetStep===gensetFlowSteps.length-1} onClick={()=>setGensetStep(x=>Math.min(gensetFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="ac"&&<div className="wizardNav"><button disabled={acStep===0} onClick={()=>setAcStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{acStep+1} / {acFlowSteps.length}</span><button disabled={acStep===acFlowSteps.length-1} onClick={()=>setAcStep(x=>Math.min(acFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="electrical"&&<div className="wizardNav"><button disabled={electricalStep===0} onClick={()=>setElectricalStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{electricalStep+1} / {electricalFlowSteps.length}</span><button disabled={electricalStep===electricalFlowSteps.length-1} onClick={()=>setElectricalStep(x=>Math.min(electricalFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="lighting"&&<div className="wizardNav"><button disabled={lightingStep===0} onClick={()=>setLightingStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{lightingStep+1} / {lightingFlowSteps.length}</span><button disabled={lightingStep===lightingFlowSteps.length-1} onClick={()=>setLightingStep(x=>Math.min(lightingFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="fireSafety"&&<div className="wizardNav"><button disabled={fireSafetyStep===0} onClick={()=>setFireSafetyStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{fireSafetyStep+1} / {fireSafetyFlowSteps.length}</span><button disabled={fireSafetyStep===fireSafetyFlowSteps.length-1} onClick={()=>setFireSafetyStep(x=>Math.min(fireSafetyFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="communications"&&<div className="wizardNav"><button disabled={communicationsStep===0} onClick={()=>setCommunicationsStep(x=>Math.max(0,x-1))}>← Sebelumnya</button><span>{communicationsStep+1} / {communicationsFlowSteps.length}</span><button disabled={communicationsStep===communicationsFlowSteps.length-1} onClick={()=>setCommunicationsStep(x=>Math.min(communicationsFlowSteps.length-1,x+1))}>Selanjutnya →</button></div>}
        {module==="escalator"&&<div className="escalatorRecommendation"><div><small>REKOMENDASI OTOMATIS</small><b>{fmt(n(result["Jumlah unit minimum"]),0)} unit minimum</b><p>{fmt(n(result["Kebutuhan arah naik"]),0)} naik + {fmt(n(result["Kebutuhan arah turun"]),0)} turun + {fmt(n(data.reserveUnits),0)} cadangan. {n(data.reversibleUnits)>0?`${fmt(n(data.reversibleUnits),0)} unit direncanakan reversible untuk penyesuaian arah puncak.`:"Tambahkan mode reversible bila pola traffic berubah signifikan."}</p></div><strong className={n(result["Rasio kecukupan unit"])>=100?"adequate":"shortage"}>{n(result["Rasio kecukupan unit"])>=100?"UNIT MENCUKUPI":`KURANG ${fmt(Math.max(0,n(result["Jumlah unit minimum"])-n(data.units)),0)} UNIT`}</strong></div>}
        <div className="formulaNote"><b>Jejak perhitungan aktif</b><span>{module==="pump"?"Hₜ = Hstatis + ΔHp + f(L/D)(v²/2g) + ΣK(v²/2g)":module==="hydrant"?"Qfire = n × q; V = Q × t × cadangan; P = ρgQH/η":module==="plumbing"?"Qhari = populasi × kebutuhan; Qpuncak = Qrata × Fp; v = Q/A":module==="electrical"?"I₃φ = P/(√3Vcosφ); Iz ≥ Ib/(kT·kG·kI); S ≥ I√t/k; Rₑ ≤ Uₜ/Iₑ":module==="lighting"?"N = E × A / (Φ × UF × MF); LPD = ΣW/A; Eavg = N × Φ × UF × MF/A":module==="fireSafety"?"Ndet = A/Acover; Qsup = density × remote area + hose demand; V = Q × t × reserve":module==="communications"?"Next = (workstation × ext/titik + umum) × growth; Ntrunk = concurrent call/utilisasi; UPS = W/(PF×η)":module==="lift"?"T = [(2h + 4s)(n−1) + s(3m+4)] / s":module==="escalator"?"Qunit = 3600 × v/pitch × okupansi × FK; N = ⌈Qpuncak/Qunit⌉ + unit cadangan":module==="genset"?"Sgen = Pterpasang × FK × cadangan × starting / cos φ":"Qtotal = selubung + penghuni + lampu + alat + udara luar + kaca"}</span></div>
      </div>

      <aside className="resultPanel">
        <div className="resultHead"><span>HASIL DESAIN</span><button onClick={()=>window.print()}>Cetak</button></div>
        <div className="resultGrid">{Object.entries(result).map(([k,v],i)=><article key={k} className={i===Object.keys(result).length-1?"primary":""}><small>{k}</small><b>{fmt(Number(v))}</b><span>{units(module,k)}</span></article>)}</div>
        <button className="save" onClick={saveProject} disabled={saving}>{saving?"Menyimpan…":"Simpan ke Riwayat Proyek"}</button>
      </aside>
    </section>

    {view==="design"&&(module==="lift"||module==="escalator")&&<section className="chapterAudit">
      <div className="auditHead"><div><span>BAB I · AUDIT DESAIN LIFT & ESKALATOR</span><h2>Hasil audit {module==="lift"?"lift":"eskalator"}</h2><p>Cakupan materi program dan kesiapan desain proyek dinilai terpisah.</p></div><div className="auditScores"><b>100%<small>cakupan implementasi</small></b><b className={auditScore===100?"auditPass":"auditHold"}>{auditScore}%<small>kesiapan proyek</small></b><button onClick={()=>window.print()}>Cetak hasil audit</button></div></div>
      <div className="auditBanner"><div><small>STATUS AUDIT AKTIF</small><b>{auditStatus}</b><span>{autoPassed}/{chapterAuditItems.length} pemeriksaan otomatis · {manualPassed}/{chapterManualGates[chapterKey].length} verifikasi engineer</span></div><p><b>Interpretasi:</b> 100% implementasi berarti seluruh lingkup Bab I telah tersedia di program. Desain proyek baru dapat dinyatakan siap setelah pemeriksaan otomatis lulus dan seluruh bukti manual diverifikasi.</p></div>
      <div className="auditFlow">{(module==="lift"?[
        ["01","Traffic","Populasi, beban puncak, RTT, interval dan handling capacity"],
        ["02","Zoning","Pembagian lantai, jumlah lift per zona dan kebutuhan skylobby"],
        ["03","Core","Luas hoistway awal dan efisiensi lantai bangunan"],
        ["04","Energi","Kecepatan, daya motor dan demand operasi simultan"],
        ["05","Vendor","Pit, overhead, pintu, kontrol dan technical submittal"]
      ]:[["01","Geometri","Rise, sudut dan panjang miring"],["02","Transportasi","Step, speed dan kapasitas"],["03","Daya","Motor, efisiensi dan demand"],["04","Operasi","Energi dan biaya siklus hidup"],["05","Safety","Brake, emergency stop, fire interface dan vendor"]]).map(x=><article key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><p>{x[2]}</p></article>)}</div>
      <div className="auditResultTable"><div className="auditTableHead"><h3>A. Pemeriksaan otomatis</h3><span>{autoPassed===chapterAuditItems.length?"SELURUH KRITERIA LULUS":`${chapterAuditItems.length-autoPassed} TEMUAN PERLU DITUTUP`}</span></div><div className="auditRows">{chapterAuditItems.map((item,i)=><article key={item.criterion} className={item.pass?"pass":"review"}><span>{String(i+1).padStart(2,"0")}</span><div><small>{item.area}</small><b>{item.criterion}</b><p>Terbaca: {item.observed}</p></div><strong>{item.pass?"LULUS":"REVIEW"}</strong><p>{item.pass?"Tidak ada tindakan koreksi otomatis.":item.action}</p></article>)}</div></div>
      <div className="manualAudit"><div className="auditTableHead"><h3>B. Verifikasi dokumen & koordinasi</h3><span>{manualPassed}/{chapterManualGates[chapterKey].length} TERKONFIRMASI</span></div><p>Centang hanya setelah bukti tersedia dalam dokumen proyek. Checklist tidak menggantikan tanda tangan engineer.</p><div>{chapterManualGates[chapterKey].map((x,i)=><label key={x} className={chapterGateChecks[chapterKey][i]?"checked":""}><input type="checkbox" checked={chapterGateChecks[chapterKey][i]} onChange={e=>{const next=[...chapterGateChecks[chapterKey]];next[i]=e.target.checked;setChapterGateChecks({...chapterGateChecks,[chapterKey]:next})}}/><span>{String(i+1).padStart(2,"0")}</span><b>{x}</b><em>{chapterGateChecks[chapterKey][i]?"TERVERIFIKASI":"BELUM"}</em></label>)}</div></div>
      {module==="lift"&&<div className="liftDecision"><div><small>REKOMENDASI SISTEM AWAL</small><b>{n(result["Tinggi perjalanan zona"])<=18?"Hydraulic / low-rise traction":n(result["Tinggi perjalanan zona"])<=60?"Geared atau MRL traction":"Gearless high-speed traction"}</b><p>Keputusan final mengikuti travel, kecepatan, frekuensi operasi, ruang mesin, efisiensi energi, akses maintenance, dan penawaran vendor.</p></div><div><small>KELAS KUALITAS LAYANAN</small><b>{n(result["Waktu tunggu"])<=25?"Excellent":n(result["Waktu tunggu"])<=35?"Good":n(result["Waktu tunggu"])<=45?"Acceptable":"Perlu redesign"}</b><p>Penilaian awal berdasarkan waktu tunggu; fungsi bangunan dan target proyek tetap menentukan acceptance criteria.</p></div></div>}
      <div className="auditNotes"><div><h3>Registri perhitungan selesai</h3><p>{module==="lift"?"Traffic, zoning, kabin, core, kualitas layanan, tipe sistem, daya, energi dan life-cycle cost.":"Geometri, kapasitas teoritis/praktis, daya, demand, energi, biaya dan life-cycle cost."}</p></div><div><h3>Gate engineering wajib</h3><p>{module==="lift"?"Pit/overhead, pintu, kontrol grup, fireman lift, reaksi struktur, kualitas daya dan shop drawing vendor.":"Support structure, pit/landing, clear height, balustrade, brake, emergency stop, fire interface dan submittal vendor."}</p></div></div>
      <p className="auditDisclaimer"><b>Kesimpulan audit:</b> {auditStatus==="SIAP REVIEW FINAL"?"seluruh pemeriksaan program dan gate manual telah dipenuhi; paket dapat diajukan untuk review serta persetujuan final engineer.":auditStatus==="PERLU REVISI"?`${chapterAuditItems.length-autoPassed} temuan otomatis harus diperbaiki sebelum proses verifikasi final.`:`perhitungan otomatis telah memenuhi kriteria awal, namun masih terdapat ${chapterManualGates[chapterKey].length-manualPassed} bukti koordinasi/dokumen yang belum dikonfirmasi.`} Hasil tetap berstatus preliminary dan belum boleh diterbitkan sebagai IFC tanpa verifikasi standar terbaru, data pabrikan, dan persetujuan engineer berwenang.</p>
    </section>}

    {view==="design"&&(module==="lift"||module==="escalator")&&<section className="engineerPack">
      <div className="sectionTitle"><div><span>BAB I · ENGINEER PACK</span><h2>Equipment schedule & spesifikasi tender</h2></div><b className="completeBadge">IMPLEMENTASI 100%</b></div>
      <div className="scheduleGrid"><div className="scheduleCard"><h3>Schedule peralatan</h3><table><tbody>{(module==="lift"?[
        ["Sistem",selectedVendor.brand+" "+selectedVendor.model+" / approved equal"],["Jumlah",`${fmt(n(result["Jumlah lift total"]),0)} unit`],["Kapasitas",`${fmt(n(data.capacity),0)} orang / ${fmt(n(data.capacity)*75,0)} kg`],["Kecepatan",`${fmt(n(data.speed))} m/s`],["Travel per zona",`${fmt(n(result["Tinggi perjalanan zona"]))} m`],["Kabin konseptual",`${fmt(n(data.cabinArea))} m²`],["Daya motor/unit",`${fmt(n(result["Daya motor per lift"]))} kW`],["Kontrol","Group control + emergency/fire interface"],["Shaft/pit/OH","Sesuai approved vendor drawing"]
      ]:[["Sistem",selectedEscalatorVendor.brand+" "+selectedEscalatorVendor.model+" / approved equal"],["Arus puncak",`${fmt(n(data.peakFlowUp),0)} naik / ${fmt(n(data.peakFlowDown),0)} turun orang/jam`],["Jumlah minimum",`${fmt(n(result["Jumlah unit minimum"]),0)} unit`],["Konfigurasi",`${fmt(n(result["Kebutuhan arah naik"]),0)} naik + ${fmt(n(result["Kebutuhan arah turun"]),0)} turun + ${fmt(n(data.reserveUnits),0)} cadangan`],["Unit rencana",`${fmt(n(data.units),0)} unit (${fmt(n(result["Rasio kecukupan unit"]),0)}%)`],["Rise",`${fmt(n(data.rise))} m`],["Sudut",`${fmt(n(data.angle))}°`],["Lebar step",`${fmt(n(data.stepWidth),0)} mm`],["Kecepatan",`${fmt(n(data.speed))} m/s`],["Kapasitas/unit",`${fmt(n(result["Kapasitas praktis per unit"]),0)} orang/jam`],["Demand",`${fmt(n(result["Demand operasi"]))} kW`],["Truss/pit/support","Sesuai approved vendor drawing"]]).map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div>
      <div className="specCard"><h3>Persyaratan minimum tender</h3>{(module==="lift"?["Drive VVVF dan regenerative bila layak","Automatic rescue/emergency operation","Fireman operation sesuai fire strategy","Door protection dan overload device","Aksesibilitas kabin, tombol dan indikator","Interface BMS, alarm dan komunikasi darurat","Ride quality, leveling dan noise test","Shop drawing shaft, pit, overhead dan reaksi struktur"]:["VVVF/energy-saving operation","Comb plate dan skirt safety device","Emergency stop di landing","Brake dan anti-reversal protection","Fire alarm interface sesuai fire strategy","Balustrade, handrail dan landing plate","Drainage/weatherproofing untuk outdoor","Shop drawing truss, pit dan support reaction"]).map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div>
      <p className="engineerNote"><b>Aturan penggunaan:</b> merek hanya basis pembanding dan harus ditulis “atau setara”. Nilai final yang memengaruhi arsitektur, struktur, listrik, keselamatan, biaya dan konstruksi harus diambil dari technical submittal yang disetujui engineer.</p>
    </section>}

    {view==="design"&&module==="pump"&&<section className="engineerPack"><div className="sectionTitle"><div><span>BAB II · RELEASE POMPA</span><h2>Calculation sheet, kurva & equipment schedule</h2></div><b className="completeBadge">MODUL POMPA · 100%</b></div><div className="scheduleGrid"><div className="scheduleCard"><h3>Duty schedule</h3><table><tbody>{[["Service",projectName],["Vendor/series",selectedPumpVendor.brand+" "+selectedPumpVendor.series+" / approved equal"],["Jenis rekomendasi",recommendedPumpType],["Konfigurasi",`${fmt(n(data.dutyPumps),0)} duty + ${fmt(n(data.standbyPumps),0)} standby; ${fmt(n(data.seriesPumps),0)} seri`],["Debit total",`${fmt(n(data.flow))} m³/jam`],["Duty per pompa",`${fmt(n(result["Debit per pompa duty"]))} m³/jam @ ${fmt(n(result["Head per pompa seri"]))} m`],["Operating point",`${fmt(n(result["Operating flow kurva"]))} m³/jam @ ${fmt(n(result["Operating head kurva"]))} m`],["Deviasi duty/BEP",`${fmt(n(result["Deviasi operating point"]))}%`],["Minimum flow",`${fmt(n(result["Minimum flow per pompa"]))} m³/jam`],["NPSH margin",`${fmt(n(result["Margin NPSH"]))} m`],["Impeller / material",pumpImpeller+" / "+pumpMaterial],["Seal / kontrol",pumpSeal+" / "+pumpControl],["Motor per pompa",`${fmt(n(result["Daya motor desain per pompa"]))} kW`],["Tekanan desain",`${fmt(n(result["Tekanan desain maksimum"]))} bar`],["Energi tahunan",`${fmt(n(result["Energi tahunan"]),0)} kWh`]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div><div className="specCard"><h3>Gate seleksi kurva vendor</h3>{["Duty point berada dekat BEP","Kurva Q-H menutup duty point","Kurva efisiensi dikonfirmasi","NPSHr vendor di bawah NPSHa","Diameter impeller dan rpm tercatat","Minimum continuous flow dikonfirmasi","Material casing/impeller sesuai fluida","Mechanical seal sesuai tekanan/suhu","Motor, VFD, IP class dan isolasi sesuai","Konfigurasi duty/standby/series ditentukan","Pressure class dan water hammer ditinjau","Baseplate, coupling, vibration isolator dan akses maintenance tersedia"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div><div className="pumpAuditStrip"><article><small>KAVITASI</small><b className={n(result["Margin NPSH"])>=1?"ok":"warn"}>{n(result["Margin NPSH"])>=1?"LULUS AWAL":"REVIEW"}</b><span>Margin NPSH {fmt(n(result["Margin NPSH"]))} m</span></article><article><small>OPERATING POINT</small><b className={n(result["Deviasi operating point"])<=n(data.bepTolerance)?"ok":"warn"}>{fmt(n(result["Deviasi operating point"]))}%</b><span>Toleransi yang diizinkan {fmt(n(data.bepTolerance))}%</span></article><article><small>WATER HAMMER</small><b className={n(result["Tekanan desain maksimum"])<=10?"ok":"warn"}>{fmt(n(result["Tekanan desain maksimum"]))} BAR</b><span>Screening tekanan maksimum</span></article><article><small>SELEKSI</small><b className="ok">{recommendedPumpType}</b><span>Rekomendasi awal; kurva vendor wajib</span></article></div><p className="engineerNote"><b>Penting:</b> program menghitung duty point, kurva sistem estimasi, operating point, konfigurasi, minimum flow dan screening water hammer. Pemilihan model final tidak boleh dilakukan hanya dari merek/seri; wajib menggunakan kurva Q-H, BEP, efisiensi, NPSHr, pressure class dan data motor yang diterbitkan vendor untuk konfigurasi aktual.</p></section>}

    {view==="design"&&module==="hydrant"&&<section className="engineerPack"><div className="sectionTitle"><div><span>BAB II · HIDRAN TAHAP 1</span><h2>Calculation sheet & fire protection schedule</h2></div><b className="completeBadge">IMPLEMENTASI · 75%</b></div><div className="scheduleGrid"><div className="scheduleCard"><h3>Hydrant & fire pump schedule</h3><table><tbody>{[["Service",projectName],["Klasifikasi",hazardClass],["Sistem",hydrantType],["Vendor pembanding",selectedFirePumpVendor.brand+" "+selectedFirePumpVendor.series+" / approved equal"],["Debit fire flow",`${fmt(n(result["Debit kebakaran"]))} L/s`],["TDH",`${fmt(n(result["Total dynamic head"]))} m`],["Main/standby/jockey","1 main + 1 standby + 1 jockey"],["Motor fire pump",`${fmt(n(result["Daya motor fire pump"]))} kW`],["Reservoir minimum",`${fmt(n(result["Volume reservoir minimum"]))} m³`],["Titik hidran",`${fmt(n(result["Jumlah titik hidran"]),0)} titik`],["Diameter awal",`${fmt(n(result["Diameter hidraulik minimum"]),0)} mm`],["FDC",`${fmt(n(result["FDC minimum"]),0)} unit`]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div><div className="specCard"><h3>Gate review Hidran</h3>{["Klasifikasi bahaya dan fire strategy disetujui","Debit simultan dan durasi dikonfirmasi","Reservoir dedicated dan make-up water tersedia","Hydraulic calculation seluruh node diselesaikan","Main, standby dan jockey pump memenuhi kurva","Pressure zoning dan PRV terkoordinasi","Jangkauan selang serta obstruksi diverifikasi di layout","FDC mudah diakses mobil pemadam","Valve, alarm, flow switch dan test header tersedia","Warna pipa, label, signage dan cabinet schedule tersedia","Sumber listrik darurat dan controller terkoordinasi","Testing, commissioning dan acceptance test direncanakan"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div><div className="pumpAuditStrip"><article><small>RESERVOIR</small><b className={n(result["Rasio kecukupan reservoir"])>=100?"ok":"warn"}>{fmt(n(result["Rasio kecukupan reservoir"]),0)}%</b><span>{n(result["Rasio kecukupan reservoir"])>=100?"Mencukupi":"Perlu penambahan volume"}</span></article><article><small>JANGKAUAN</small><b className="ok">{fmt(n(result["Jumlah titik hidran"]),0)} TITIK</b><span>{fmt(n(result["Titik hidran per lantai"]),0)} titik per lantai</span></article><article><small>FIRE PUMP</small><b className="ok">{fmt(n(result["Daya motor fire pump"]))} KW</b><span>Seleksi awal; kurva vendor wajib</span></article><article><small>FDC</small><b className={n(data.fdcCount)>=n(data.zones)?"ok":"warn"}>{fmt(n(result["FDC minimum"]),0)} UNIT</b><span>{fmt(n(data.zones),0)} zona tekanan</span></article></div><p className="engineerNote"><b>Status:</b> flow, debit, reservoir, jaringan awal, fire pump, jangkauan, FDC, vendor, BOQ dan audit telah aktif. Penyelesaian 100% berikutnya memerlukan hydraulic node/loop, layout coverage lebih detail, valve schedule, label/warna, detail DED dan audit final.</p></section>}

    {view==="design"&&module==="hydrant"&&<section className="hydrantFinal"><div className="sectionTitle"><div><span>BAB II · AUDIT FINAL HIDRAN</span><h2>Hydraulic node, riser, valve & DED</h2></div><b className="completeBadge">MODUL HIDRAN · 100%</b></div><div className="hydrantFinalGrid"><div className="riserCard"><h3>Skema riser dan zona</h3><div className="riserDiagram"><span className="tank">FIRE WATER TANK<br/><b>{fmt(n(result["Volume reservoir minimum"]))} m³</b></span><i></i><span>MAIN + STANDBY + JOCKEY<br/><b>{fmt(n(result["Debit kebakaran"]))} L/s @ {fmt(n(result["Total dynamic head"]))} m</b></span><i></i>{Array.from({length:Math.min(6,Math.max(1,Math.ceil(n(data.zones))))},(_,i)=><span key={i}>ZONE {i+1}<br/><b>Head basis {fmt(n(result["Head per zona"]))} m</b></span>)}<i></i><span>REMOTE NODE<br/><b>{fmt(n(result["Tekanan node terjauh"]))} mH₂O</b></span></div><p>Diagram adalah basis skematik. Elevasi, valve, PRV, check valve, drain, test connection, dan diameter setiap segmen harus dicantumkan pada gambar DED.</p></div><div className="valveCard"><h3>Valve & identification schedule</h3><table><tbody>{[["Floor control valve set",`${fmt(n(result["Floor control valve set"]),0)} set`],["Zone isolation valve",`${fmt(n(result["Zone isolation valve"]),0)} unit`],["FDC / siamese",`${fmt(n(result["FDC minimum"]),0)} unit`],["Pipe identification","Merah; flow arrow; service label"],["Valve tag","HV-Zxx-Fxx-xxx"],["Hydrant cabinet","Nomor, zona, lantai dan inspection tag"],["Test & drain","Pada riser/zone sesuai hydraulic strategy"],["Drawing output","Plan, riser, detail, schedule, calculation"]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div></div><div className="hydrantAuditMatrix">{[["Debit & durasi",n(result["Debit kebakaran"])>0&&n(data.duration)>0,`${fmt(n(result["Debit kebakaran"]))} L/s; ${fmt(n(data.duration))} menit`],["Reservoir",n(result["Rasio kecukupan reservoir"])>=100,`${fmt(n(result["Rasio kecukupan reservoir"]))}%`],["Hydraulic node",n(result["Tekanan node terjauh"])>=n(data.residualPressure),`${fmt(n(result["Tekanan node terjauh"]))} mH₂O`],["Kecepatan pipa",n(result["Kecepatan pipa terpilih"])<=n(data.maxVelocity),`${fmt(n(result["Kecepatan pipa terpilih"]))} m/s`],["Jangkauan",n(result["Rasio kecukupan jangkauan"])>=100,`${fmt(n(result["Rasio kecukupan jangkauan"]))}%`],["FDC per zona",n(data.fdcCount)>=n(data.zones),`${fmt(n(data.fdcCount),0)} / ${fmt(n(data.zones),0)}`]].map(([a,pass,value],i)=><article key={String(a)} className={pass?"pass":"review"}><span>{String(i+1).padStart(2,"0")}</span><div><small>{a}</small><b>{value}</b></div><strong>{pass?"LULUS":"REVIEW"}</strong></article>)}</div><div className="dedChecklist"><h3>Paket keluaran DED</h3>{["Design criteria dan fire strategy","Hydraulic calculation node terjauh","Plan coverage setiap lantai","Riser diagram dan pressure zoning","Pump room layout dan equipment schedule","Reservoir, suction, test header dan drainage detail","FDC detail dan akses mobil pemadam","Valve, instrument dan cabinet schedule","Pipe size, warna, label dan flow direction","Power, controller, alarm dan monitoring interface","BOQ/RAB awal","Testing, commissioning dan acceptance procedure"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div><p className="auditDisclaimer"><b>Kesimpulan:</b> seluruh lingkup Modul Hidran pada Bab II telah tersedia sebagai alat desain konseptual/preliminary. Dokumen IFC tetap memerlukan model jaringan lengkap, data elevasi dan routing aktual, kurva fire pump, koordinasi fire strategy, ketentuan otoritas setempat, serta persetujuan engineer berwenang.</p></section>}

    {view==="design"&&module==="genset"&&<section className="engineerPack gensetPack"><div className="sectionTitle"><div><span>BAB III · GENSET</span><h2>Calculation sheet, instalasi & audit desain</h2></div><b className="completeBadge">IMPLEMENTASI BUKU · 100%</b></div><div className="scheduleGrid"><div className="scheduleCard"><h3>Generator set schedule</h3><table><tbody>{[["Service",projectName],["Rating",gensetRating],["Vendor pembanding",selectedGensetVendor.brand+" "+selectedGensetVendor.series+" / approved equal"],["Konfigurasi",`${fmt(n(data.units),0)} duty + ${fmt(n(data.standbyUnits),0)} standby`],["Beban steady-state",`${fmt(n(result["Kebutuhan steady-state"]))} kVA`],["Beban starting",`${fmt(n(result["Kebutuhan saat starting"]))} kVA`],["Derating",`${fmt(n(result["Faktor derating"]))}%`],["Kapasitas/unit",`${fmt(n(result["Kapasitas per unit"]),0)} kVA`],["Kapasitas total",`${fmt(n(result["Kapasitas total terpasang"]),0)} kVA`],["Kontrol",gensetControl],["Autonomy / tangki",`${fmt(n(data.autonomy))} jam / ${fmt(n(result["Tangki BBM minimum"]),0)} L`],["Ventilasi / louver",`${fmt(n(result["Debit ventilasi"]))} m³/s / ${fmt(n(result["Luas efektif louver"]))} m²`],["Exhaust back pressure",`${fmt(n(result["Back pressure exhaust"]))} mbar`],["Noise penerima",`${fmt(n(result["Kebisingan titik penerima"]))} dB(A)`]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div><div className="specCard"><h3>Gate pemilihan & instalasi</h3>{["Load schedule dan prioritas life-safety disetujui","Starting sequence serta voltage dip diverifikasi","Rating standby/prime dan derating vendor sesuai","ATS/AMF, interlock, load shedding dan sinkronisasi diuji","Netral, pembumian dan proteksi terkoordinasi","Tangki, bundwall, ventilasi uap dan fire safety tersedia","Radiator discharge tidak short-circuit ke intake","Back pressure exhaust di bawah batas engine","Silencer, insulation dan outlet aman terhadap bangunan","Inertia block, isolator dan flexible connection tersedia","Akses removal, maintenance dan lifting clear","Load bank, black-start dan commissioning direncanakan"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div><div className="pumpAuditStrip"><article><small>KAPASITAS</small><b className={n(result["Kapasitas total terpasang"])>=n(result["Kebutuhan saat starting"])?"ok":"warn"}>{fmt(n(result["Kapasitas total terpasang"]),0)} KVA</b><span>Kebutuhan transien {fmt(n(result["Kebutuhan saat starting"]),0)} kVA</span></article><article><small>UTILISASI</small><b className={n(result["Utilisasi steady-state"])>=30&&n(result["Utilisasi steady-state"])<=85?"ok":"warn"}>{fmt(n(result["Utilisasi steady-state"]))}%</b><span>Screening operasi 30–85%</span></article><article><small>EXHAUST</small><b className={n(result["Back pressure exhaust"])<=10?"ok":"warn"}>{fmt(n(result["Back pressure exhaust"]))} MBAR</b><span>Batas vendor tetap mengikat</span></article><article><small>AKUSTIK</small><b className={n(result["Selisih target kebisingan"])<=0?"ok":"warn"}>{fmt(n(result["Kebisingan titik penerima"]))} DB(A)</b><span>Target {fmt(n(data.targetNoise))} dB(A)</span></article></div><div className="gensetBookMap"><h3>Pemetaan Bab III buku → program</h3>{[["3.1","Pendahuluan","Design criteria dan peran sumber daya darurat"],["3.2","Kriteria pemilihan","Load schedule, demand, starting, derating dan rating"],["3.2.1","Sesuai kebutuhan","Kapasitas standar, utilisasi, redundansi dan fuel autonomy"],["3.2.2","Tata letak","Ruang, akses, airflow, louver, fuel dan exhaust"],["3.2.3","Peredaman suara","Prediksi noise, target dan attenuation"],["3.2.4","Getaran & suara","Foundation, isolator, flexible joint dan silencer"],["3.2.5","Penyambungan poros","Alignment/coupling sebagai gate vendor-integrator"],["3.3","Kerusakan genset","Diagnostik enam kelompok gangguan"],["3.4","Contoh instalasi","Schedule, skema instalasi dan checklist commissioning"]].map(x=><article key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><p>{x[2]}</p><strong>TERAKOMODASI</strong></article>)}</div><div className="dedChecklist"><h3>Paket keluaran Bab III</h3>{["Design Brief Genset","Load schedule beban darurat dan prioritas","Calculation sheet kapasitas steady/transient","Derating elevasi dan temperatur","Fuel consumption dan tank sizing","Ventilation/louver sizing","Exhaust back-pressure screening","Noise screening dan vibration criteria","Single-line ATS/AMF/synchronizing basis","Equipment dan BOQ schedule","Troubleshooting/maintenance checklist","Testing, commissioning dan load-bank procedure"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div><p className="auditDisclaimer"><b>Kesimpulan:</b> seluruh subbab Bab III telah diubah menjadi alat desain konseptual/preliminary yang dapat dihitung dan diaudit. Kapasitas final, transient response, emisi, fuel curve, heat rejection, airflow, exhaust limit, alternator, proteksi dan akustik wajib dikonfirmasi dari data pabrikan/integrator dan disahkan engineer berwenang sebelum tender/IFC.</p></section>}

    {view==="design"&&module==="ac"&&<section className="engineerPack acPack"><div className="sectionTitle"><div><span>BAB IV · AIR CONDITIONING</span><h2>Cooling load, system selection & audit DED</h2></div><b className="completeBadge">IMPLEMENTASI BUKU · 100%</b></div><div className="scheduleGrid"><div className="scheduleCard"><h3>AC equipment schedule</h3><table><tbody>{[["Service",projectName],["Sistem awal",acSystem],["Vendor pembanding",selectedAcVendor.brand+" "+selectedAcVendor.series+" / approved equal"],["Kondisi luar",`${fmt(n(data.outdoorDb))}°C / ${fmt(n(data.outdoorRh))}%RH`],["Kondisi dalam",`${fmt(n(data.indoorDb))}°C / ${fmt(n(data.indoorRh))}%RH`],["Beban sensibel",`${fmt(n(result["Beban sensibel ruang"])/1000)} kW`],["Beban laten",`${fmt(n(result["Beban laten ruang"])/1000)} kW`],["Beban desain",`${fmt(n(result["Beban pendinginan desain"])/1000)} kW`],["Kapasitas",`${fmt(n(result["Ton refrigerasi"]))} TR / ${fmt(n(result["Kapasitas pendinginan"]),0)} BTU/jam`],["Supply air",`${fmt(n(result["Debit supply air"]))} m³/s`],["Duct utama awal",`${fmt(n(result["Luas duct utama awal"]))} m²`],["Chilled water awal",`${fmt(n(result["Debit chilled water awal"]))} L/s @ ${fmt(n(result["Diameter pipa awal"]),0)} mm`],["Kondensat",`${fmt(n(result["Kondensat estimasi"]))} kg/jam`],["Input / energi",`${fmt(n(result["Daya input sistem"]))} kW / ${fmt(n(result["Energi tahunan"]),0)} kWh/tahun`]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div><div className="specCard"><h3>Gate desain & spesifikasi</h3>{["Room data sheet dan zoning terverifikasi","Outdoor/indoor design condition disetujui","U-value, SHGC, shading dan schedule terbukti","Beban sensibel-laten dan udara luar dihitung","Pemilihan sistem dibandingkan CAPEX/OPEX/ruang","Kapasitas vendor dikoreksi kondisi desain aktual","Duct sizing, pressure loss dan fan ESP diselesaikan","Pipa refrigeran/CHW, pump head dan insulation dihitung","Drain kondensat memiliki slope, trap dan cleanout","Fresh air, exhaust, filtration dan pressurization terkoordinasi","Noise/NC, vibration dan akses maintenance diperiksa","TAB, flushing, pressure test dan commissioning direncanakan"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div><div className="pumpAuditStrip"><article><small>KAPASITAS</small><b className="ok">{fmt(n(result["Ton refrigerasi"]))} TR</b><span>{fmt(n(result["Beban pendinginan desain"])/1000)} kW desain</span></article><article><small>SHR</small><b className={n(result["Sensible heat ratio"])>=.65&&n(result["Sensible heat ratio"])<=.95?"ok":"warn"}>{fmt(n(result["Sensible heat ratio"]),3)}</b><span>Screening sensibel-laten</span></article><article><small>UDARA</small><b className="ok">{fmt(n(result["Debit supply air"]))} M³/S</b><span>ΔT supply {fmt(n(data.supplyDeltaT))} K</span></article><article><small>ENERGI</small><b className="ok">{fmt(n(result["Daya input sistem"]))} KW</b><span>COP input {fmt(n(data.cop))}</span></article></div><div className="acBookMap"><h3>Pemetaan Bab IV buku → program</h3>{[["4.1","Latar belakang","Design Brief, fungsi ruang dan target kenyamanan"],["4.2","Dasar teori","Heat balance dan kondisi psikrometrik"],["4.2.1","Prinsip AC","Suhu, kelembapan, kebersihan dan gerakan udara"],["4.2.2","Siklus pendinginan","Evaporator–compressor–condenser–expansion sebagai basis seleksi"],["4.2.3","Kenyamanan","Indoor DB/RH, fresh air, SHR dan zoning"],["4.3","Komponen utama","Indoor/outdoor, AHU/FCU, duct, pipe, drain dan control"],["4.4","Jenis di pasaran","Split, multi-split, VRF, package dan chiller"],["4.5","Penggolongan sistem","DX, all-air, all-water dan air-water basis"],["4.6","Contoh instalasi","Equipment schedule, BOQ dan checklist DED gedung"]].map(x=><article key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><p>{x[2]}</p><strong>TERAKOMODASI</strong></article>)}</div><div className="dedChecklist"><h3>Paket keluaran Bab IV</h3>{["Design Brief dan room data sheet","Psychrometric design points","Cooling-load calculation sheet","System comparison dan basis of design","Equipment schedule","Duct sizing dan pressure-loss sheet","Pipe/refrigerant distribution basis","Fresh-air/exhaust schedule","Condensate drainage detail","Control/BMS sequence","BOQ/RAB awal","TAB dan commissioning checklist"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div><p className="auditDisclaimer"><b>Kesimpulan:</b> seluruh subbab Bab IV telah diterjemahkan menjadi alat desain konseptual/preliminary. Final tender/IFC tetap memerlukan room-by-room load, weather design resmi, schedule aktual, model duct/piping lengkap, data performa pabrikan pada kondisi desain, koordinasi arsitektur-struktur-listrik, dan persetujuan engineer berwenang.</p></section>}

    <section className="standardsRegister" style={{display:view==="design"?undefined:"none"}}><div className="sectionTitle"><div><span>CODE & STANDARDS REGISTER</span><h2>Referensi aktif — {current.title}</h2></div><b>{standards.filter(s=>s.modules.includes(module)).length} ACUAN</b></div><table><thead><tr><th>Kode / standar</th><th>Status penggunaan</th><th>Lingkup desain</th><th>Sumber verifikasi</th></tr></thead><tbody>{standards.filter(s=>s.modules.includes(module)).map(s=><tr key={s.code}><td><b>{s.code}</b><br/><small>{s.title}</small></td><td>{s.role}</td><td>{s.use}</td><td>{s.source}</td></tr>)}</tbody></table><p className="auditDisclaimer"><b>Aturan pemakaian:</b> regulasi dan SNI yang berlaku menjadi basis nasional; standar internasional hanya mengikat jika ditetapkan kontrak atau AHJ. Engineer wajib mengunci edisi, pasal, status berlaku, ketentuan daerah dan hierarki dokumen pada Design Criteria sebelum tender/IFC.</p></section>

    {view==="design"&&module==="electrical"&&<section className="engineerPack electricalPack"><div className="sectionTitle"><div><span>BAB V · INSTALASI LISTRIK</span><h2>Load, cable, protection, grounding & lightning audit</h2></div><b className="completeBadge">TAHAP DED · MODUL INTI</b></div><div className="pumpAuditStrip"><article><small>DAYA DESAIN</small><b>{fmt(n(result["Daya desain + cadangan"]))} KW</b><span>{fmt(n(result["Daya semu"]))} kVA</span></article><article><small>KABEL</small><b className={n(result["Jatuh tegangan estimasi"])<=n(data.allowableDrop)?"ok":"warn"}>{fmt(n(result["Luas kabel Cu terpilih"]),0)} MM²</b><span>VD {fmt(n(result["Jatuh tegangan estimasi"]))}% / batas {fmt(n(data.allowableDrop))}%</span></article><article><small>GROUNDING</small><b className={n(result["Rasio kecukupan grounding"])>=100?"ok":"warn"}>{fmt(n(result["Tahanan grounding estimasi"]),2)} Ω</b><span>Batas {fmt(n(result["Tahanan grounding maksimum"]),2)} Ω</span></article><article><small>PROTEKSI PETIR</small><b>{fmt(n(result["Kelas proteksi petir"]),0)}</b><span>Spacing {fmt(n(result["Jarak down conductor"]))} m</span></article></div><div className="scheduleGrid"><div className="scheduleCard"><h3>Electrical design schedule</h3><table><tbody>{[["Sistem",`${fmt(n(data.voltage),0)} V · ${fmt(n(data.phases),0)} fasa · cos φ ${fmt(n(data.powerFactor),2)}`],["Connected load",`${fmt(n(result["Beban terpasang"]))} kW`],["Demand load",`${fmt(n(result["Beban kebutuhan"]))} kW`],["Design current",`${fmt(n(result["Arus desain"]))} A`],["Corrected ampacity",`${fmt(n(result["KHA minimum terkoreksi"]))} A`],["Main protection",`${fmt(n(result["Rating pengaman awal"]),0)} A · Icu ≥ ${fmt(n(data.faultCurrent))} kA`],["Selected cable",`${fmt(n(result["Luas kabel Cu terpilih"]),0)} mm² Cu`],["Grounding",`${fmt(n(data.earthRodCount),0)} rod · ${fmt(n(result["Tahanan grounding estimasi"]),2)} Ω`],["Lightning protection",`LPS class ${fmt(n(result["Kelas proteksi petir"]),0)} · ${fmt(n(data.lightningDownConductors),0)} down conductor`]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div><div className="specCard"><h3>Gate DED elektrikal</h3>{["Load schedule per panel dan lantai tersedia","Single-line diagram dan source arrangement disetujui","KHA memakai tabel serta faktor koreksi PUIL aktual","Short-circuit dan breaking capacity terverifikasi","Koordinasi dan selektivitas proteksi diselesaikan","Voltage drop setiap feeder memenuhi design criteria","Netral, harmonisa dan phase balancing ditinjau","Grounding, bonding dan touch voltage terverifikasi","Risk assessment dan layout proteksi petir tersedia","SPD dikoordinasikan pada service entrance dan panel","Panel/cable/breaker schedule sinkron dengan BOQ","Testing, commissioning dan as-built requirement tersedia"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div><div className="gensetBookMap"><h3>Pemetaan Bab I buku lanjutan → Bab V program</h3>{[["5.1","Pendahuluan","Design Brief dan batas pelayanan"],["5.2","Dasar teori","Syarat jaringan, kabel dan grounding"],["5.2.2","Perancangan kabel","KHA, voltage drop dan short-circuit withstand"],["5.2.3","Sistem pentanahan","Rod resistance, touch voltage dan bonding"],["5.3","Peralatan instalasi","Schedule conduit, switchgear dan penghantar"],["5.4","Contoh perancangan","Input hingga calculation report"],["5.5","Penangkal petir","LPS, collection area, down conductor dan SPD"],["5.5.3","Analisis biaya manfaat","BOQ dan evaluasi risiko/biaya"]].map(x=><article key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><p>{x[2]}</p><strong>TERAKOMODASI</strong></article>)}</div><p className="auditDisclaimer"><b>Status:</b> Bab V telah menjadi modul desain rinci tahap pertama. Untuk IFC, ampacity, impedansi loop, fault level, selectivity, grounding grid dan lightning risk assessment tetap harus ditetapkan dari data utilitas, tabel PUIL, studi jaringan dan gambar aktual serta disahkan engineer berwenang.</p></section>}

    {view==="design"&&module==="lighting"&&<section className="engineerPack electricalPack"><div className="sectionTitle"><div><span>BAB VI · PENCAHAYAAN</span><h2>Lux, layout, LPD, energy & control audit</h2></div><b className="completeBadge">TAHAP DED · MODUL PER RUANG</b></div><div className="pumpAuditStrip"><article><small>TARGET / HASIL</small><b>{fmt(n(data.targetLux),0)} / {fmt(n(result["Iluminansi terpasang"]),0)} LUX</b><span>Uniformity {fmt(n(result["Rasio uniformity Emin/Eavg"]),2)}</span></article><article><small>ARMATUR</small><b>{fmt(n(result["Jumlah armatur terpilih"]),0)} UNIT</b><span>{fmt(n(result["Baris armatur"]),0)} × {fmt(n(result["Kolom armatur"]),0)} layout</span></article><article><small>LPD</small><b className={n(result["Lighting power density"])<=10?"ok":"warn"}>{fmt(n(result["Lighting power density"]),2)} W/M²</b><span>Daya {fmt(n(result["Daya pencahayaan terpasang"]),2)} kW</span></article><article><small>ENERGI</small><b>{fmt(n(result["Energi tahunan"]),0)} KWH</b><span>Hemat kontrol {fmt(n(result["Penghematan energi kontrol"]),0)} kWh</span></article></div><div className="scheduleGrid"><div className="scheduleCard"><h3>Luminaire design schedule</h3><table><tbody>{[["Geometri",`${fmt(n(data.roomLength))} × ${fmt(n(data.roomWidth))} × ${fmt(n(data.roomHeight))} m`],["Bidang kerja",`${fmt(n(data.workPlane))} m`],["Target",`${fmt(n(data.targetLux),0)} lux · ${fmt(n(data.colorTemperature),0)} K · CRI ${fmt(n(data.cri),0)}`],["Armatur",`${fmt(n(data.lumensPerFixture),0)} lm · ${fmt(n(data.fixturePower),0)} W`],["UF / MF",`${fmt(n(data.utilizationFactor),2)} / ${fmt(n(data.maintenanceFactor),2)}`],["Jumlah",`${fmt(n(result["Jumlah armatur terpilih"]),0)} unit`],["Spacing awal",`${fmt(n(result["Jarak maksimum awal"]),2)} m`],["Emergency",`${fmt(n(result["Armatur darurat awal"]),0)} unit awal`],["Energi",`${fmt(n(result["Energi tahunan"]),0)} kWh/tahun · ${rupiah(n(result["Biaya energi tahunan"]))}`]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div><div className="specCard"><h3>Gate DED pencahayaan</h3>{["Room data sheet dan fungsi ruang disetujui","Target lux, uniformity, CRI dan CCT terkunci","File fotometrik IES/LDT armatur tersedia","Lighting calculation dan glare review per ruang selesai","Layout terkoordinasi ceiling, sprinkler, diffuser dan detector","LPD dibandingkan dengan batas fungsi ruang","Daylight, occupancy dan dimming zoning terdokumentasi","Emergency lighting dan exit sign terverifikasi","Luminaire schedule sinkron dengan layout dan BOQ","Circuiting, panel load dan control diagram selesai","Mock-up dan acceptance criteria lux disetujui","Testing, commissioning dan as-built requirement tersedia"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div><div className="gensetBookMap"><h3>Pemetaan Bab II buku lanjutan → Bab VI program</h3>{[["6.1","Pendahuluan","Design Brief dan tujuan visual"],["6.2","Dasar teori","Lux, lumen, intensitas dan kualitas cahaya"],["6.2.1","Istilah","Parameter, satuan dan tooltip input"],["6.2.2","Simbol dan standar","Register SNI dan design criteria"],["6.2.3","Katalog","Luminaire schedule dan data fotometrik"],["6.2.4","Cahaya dan warna","CCT, CRI, reflektansi dan glare review"],["6.2.5","Rekomendasi gedung","Target lux dan uniformity per fungsi ruang"],["6.2.6","Konservasi energi","LPD, kontrol, daylight dan energi tahunan"],["6.2.7","Jenis lampu","Seleksi LED/armatur berbasis efikasi"],["6.3","Rumus-rumus","Lumen method, room index dan layout"],["6.4","Penyusunan sistem","Schedule, kontrol, emergency, BOQ dan laporan DED"]].map(x=><article key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><p>{x[2]}</p><strong>TERAKOMODASI</strong></article>)}</div><p className="auditDisclaimer"><b>Status:</b> Bab VI telah diotomasi sebagai desain awal per ruang. Untuk IFC/DED final, model harus dijalankan untuk setiap ruang dan divalidasi dengan file fotometrik produk, evaluasi glare, daylight, emergency route, coordinated ceiling plan serta pengukuran lux; hasil disahkan engineer berwenang.</p></section>}

    {view==="design"&&module==="fireSafety"&&<section className="engineerPack electricalPack"><div className="sectionTitle"><div><span>BAB VII · SISTEM PENGAMAN KEBAKARAN</span><h2>Detection, alarm, sprinkler, suppression & water supply</h2></div><b className="completeBadge">TAHAP DED · LIFE SAFETY</b></div><div className="pumpAuditStrip"><article><small>DETECTION</small><b>{fmt(n(result["Initiating devices"]),0)} DEVICE</b><span>{fmt(n(result["Loop minimum"]),0)} loop · {fmt(n(result["Zona alarm minimum"]),0)} zona</span></article><article><small>NOTIFICATION</small><b>{fmt(n(result["Sounder"])+n(result["Strobe"]),0)} UNIT</b><span>MCP {fmt(n(result["Manual call point"]),0)} unit</span></article><article><small>SUPPRESSION FLOW</small><b>{fmt(n(result["Debit suppression total"]),2)} L/S</b><span>Remote sprinkler {fmt(n(result["Debit sprinkler remote"]),2)} L/s</span></article><article><small>WATER SUPPLY</small><b className={n(result["Rasio kecukupan air"])>=100?"ok":"warn"}>{fmt(n(result["Volume water supply minimum"]),2)} M³</b><span>Kecukupan {fmt(n(result["Rasio kecukupan air"]),0)}%</span></article></div><div className="scheduleGrid"><div className="scheduleCard"><h3>Fire safety design schedule</h3><table><tbody>{[["Protected area",`${fmt(n(result["Luas proteksi total"]),0)} m² · ${fmt(n(data.floors),0)} lantai`],["Detection",`${fmt(n(result["Smoke detector"]),0)} smoke · ${fmt(n(result["Heat detector"]),0)} heat · ${fmt(n(result["Flame detector"]),0)} flame`],["Panel",`${fmt(n(result["Loop minimum"]),0)} loop · battery ${fmt(n(result["Kapasitas baterai minimum"]),1)} Ah`],["Sprinkler",`${fmt(n(result["Jumlah sprinkler total"]),0)} head · remote ${fmt(n(result["Sprinkler remote area"]),0)} head`],["Water demand",`${fmt(n(result["Debit suppression total"]),2)} L/s × ${fmt(n(data.suppressionDuration),0)} menit`],["Main pipe",`${fmt(n(result["Diameter pipa utama awal"]),0)} mm screening`],["Fire pump",`${fmt(n(result["Daya motor pompa awal"]),1)} kW awal`]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div><div className="specCard"><h3>Gate DED fire safety</h3>{["Fire strategy dan klasifikasi hunian disetujui AHJ","Kompartemen dan fire rating terkoordinasi","Detector layout sesuai risiko, ceiling dan obstruction","Alarm zoning, audibility dan visibility terverifikasi","Cause-and-effect matrix seluruh interface selesai","Loop, isolator, power supply dan battery schedule selesai","Sprinkler hazard classification dan remote area terkunci","Hydraulic calculation node terjauh memenuhi","Reservoir, pump duty dan redundancy terverifikasi","Riser, valve, drain, test connection dan FDC selesai","BOQ, datasheet, listing dan approved equal sinkron","Integrated testing dan commissioning scenario disetujui"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div><div className="gensetBookMap"><h3>Pemetaan Bab III buku lanjutan → Bab VII program</h3>{[["7.1","Pendahuluan","Fire strategy dan Design Brief"],["7.2","Dasar teori","Deteksi, alarm dan suppression"],["7.2.1","Sistem pengamanan kebakaran","Proteksi aktif, pasif dan integrasi"],["7.2.2","Sistem pendeteksian","Zoning, loop dan panel"],["7.2.3","Sistem suppression","Sprinkler, hose dan special suppression"],["7.2.4","Jenis detector","Smoke, heat, flame dan spark screening"],["7.2.5","Jenis detector sprinkler","Coverage, density dan remote area"],["7.2.6","Penunjuk dan pendeteksi sprinkler","Flow switch, supervisory dan alarm interface"],["7.2.7","Persediaan air","Debit, durasi, volume dan pump screening"],["7.3","Penyusunan sistem","Flow input dan system architecture"],["7.3.1","Blok diagram","Cause-and-effect dan riser requirement"],["7.3.2","Detail Engineering Design","Schedule, BOQ, audit dan report"],["7.3.3","Contoh desain","Proyek contoh terintegrasi"],["7.4","Kelas, sistem dan bahan pemadam","Hazard, media dan special suppression brief"]].map(x=><article key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><p>{x[2]}</p><strong>TERAKOMODASI</strong></article>)}</div><p className="auditDisclaimer"><b>Status:</b> Bab VII telah diotomasi untuk screening dan penyusunan DED. Jumlah detector/sprinkler berbasis luas bukan pengganti layout aktual. Final design wajib memakai hydraulic calculation node-by-node, listed device data, cause-and-effect, fire strategy, ketentuan AHJ dan pengesahan fire/MEP engineer.</p></section>}

    {view==="design"&&module==="communications"&&<section className="engineerPack electricalPack"><div className="sectionTitle"><div><span>BAB VIII · SISTEM KOMUNIKASI</span><h2>Telephony, PABX, intercom, radio & structured cabling</h2></div><b className="completeBadge">TAHAP DED · ICT/ELV</b></div><div className="pumpAuditStrip"><article><small>EXTENSION</small><b>{fmt(n(result["Extension desain"]),0)} PORT</b><span>PABX {fmt(n(result["Kapasitas PABX terpilih"]),0)} port</span></article><article><small>TRUNK</small><b>{fmt(n(result["SIP/trunk channels minimum"]),0)} KANAL</b><span>{fmt(n(result["PRI/E1 circuits"]),0)} PRI/E1 circuit</span></article><article><small>OUTLET & CABLE</small><b>{fmt(n(result["Outlet telekomunikasi"]),0)} TITIK</b><span>{fmt(n(result["Panjang kabel horizontal"]),0)} m kabel</span></article><article><small>RACK & UPS</small><b>{fmt(n(result["Jumlah rack"]),0)} × {fmt(n(result["Ukuran rack terpilih"]),0)}U</b><span>UPS {fmt(n(result["Kapasitas UPS minimum"]),0)} VA</span></article></div><div className="scheduleGrid"><div className="scheduleCard"><h3>Communication system schedule</h3><table><tbody>{[["Users / workstations",`${fmt(n(data.occupants),0)} / ${fmt(n(data.workstations),0)}`],["Extensions",`${fmt(n(result["Extension desain"]),0)} ext · ${fmt(n(data.extensionGrowth),0)}% growth`],["Concurrent call",`${fmt(n(result["Concurrent calls"]),0)} call`],["Trunk",`${fmt(n(result["SIP/trunk channels minimum"]),0)} SIP channel / ${fmt(n(result["PRI/E1 circuits"]),0)} PRI`],["Outlets",`${fmt(n(result["Outlet telekomunikasi"]),0)} points`],["Backbone",`${fmt(n(result["Backbone copper pairs"]),0)} pairs`],["Intercom",`${fmt(n(data.intercomMasters),0)} master + ${fmt(n(data.intercomStations),0)} station`],["HT",`${fmt(n(data.htUsers),0)} users / ${fmt(n(data.htChannels),0)} channel`],["Rack & UPS",`${fmt(n(result["Kebutuhan rack total"]),0)}U · ${fmt(n(result["Kapasitas UPS minimum"]),0)} VA`]].map(x=><tr key={x[0]}><th>{x[0]}</th><td>{x[1]}</td></tr>)}</tbody></table></div><div className="specCard"><h3>Gate DED komunikasi</h3>{["User requirement dan service matrix disetujui","Operator, demarcation dan SLA terkonfirmasi","Numbering plan serta call routing selesai","PABX port, trunk, license dan redundancy terverifikasi","Outlet schedule per ruang sinkron dengan layout","Horizontal channel dan IDF zoning memenuhi batas","Backbone pair/core dan reserve terdokumentasi","MDF/IDF, rack, cooling, grounding dan firestop selesai","Interkom dan interface security/lift terkoordinasi","Radio coverage dan perizinan frekuensi terverifikasi","UPS, emergency power dan monitoring selesai","Labeling, certification test dan as-built requirement tersedia"].map((x,i)=><label key={x}><input type="checkbox"/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}</div></div><div className="vendorPicker"><div className="pumpSelectionGrid">{communicationsVendors.map(v=><div className="vendorSpec" key={v.brand}><b>{v.brand}</b><p>{v.system}</p><small>{v.note}</small></div>)}</div><small>Merek hanya referensi pasar Indonesia dan harus ditulis “atau setara”; model final ditetapkan melalui technical submittal serta compliance matrix.</small></div><div className="gensetBookMap"><h3>Pemetaan Bab IV buku lanjutan → Bab VIII program</h3>{[["8.1","Pendahuluan","Design Brief, layanan dan batas sistem"],["8.2","Telepon dan sistem PBX","Extension, trunk, numbering dan PABX"],["8.2.1.1","Dasar teori telepon","Traffic, channel dan kapasitas"],["8.2.1.2","Mendesain sistem telepon","Flow pengguna hingga schedule"],["8.2.1.3","Membangun sistem telepon","MDF/IDF, rack dan commissioning"],["8.2.1.4","Sambungan pelanggan","Operator, demarcation dan SLA"],["8.2.1.5","Pengkabelan telepon","Horizontal dan backbone cabling"],["8.2.1.6","Kabel","Pair, reserve, route dan testing"],["8.2.1.7","Outlet telepon","Outlet schedule dan BOQ"],["8.2.1.8","Pesawat telepon","Handset dan license schedule"],["8.2.2","Telepon dan PBX","Kapasitas, trunk dan redundancy"],["8.3","Facsimile","Layanan legacy/analog gateway brief"],["8.4","Interkom","Master, station, mode dan jaringan"],["8.5","Handy Talkie","User, channel, repeater dan coverage"],["8.6","Detail Engineering Design","Riser, schedule, BOQ, audit dan report"]].map(x=><article key={x[0]}><span>{x[0]}</span><b>{x[1]}</b><p>{x[2]}</p><strong>TERAKOMODASI</strong></article>)}</div><p className="auditDisclaimer"><b>Status:</b> Bab VIII telah diotomasi sebagai basis DED komunikasi. Fax diperlakukan sebagai layanan legacy melalui analog gateway bila masih diwajibkan. Final design wajib dikonfirmasi terhadap operator, route aktual, link/coverage survey, sertifikasi kabel, license software, cybersecurity, izin frekuensi dan pengesahan engineer.</p></section>}

    <div style={{display:view==="solver"?undefined:"none"}}><SolverLab/></div>

    <section className="aiSection" style={{display:view==="design"?undefined:"none"}}>
      <div className="aiLabel"><span>AI</span><div><p>AGEN REVIEW MEP</p><h2>Pemeriksaan desain otomatis</h2></div></div>
      <div className="aiBody"><div className="aiIntro"><p>Agen membaca parameter dan hasil aktif, lalu menandai kondisi yang perlu diverifikasi engineer.</p><div className="chips"><span>✓ Unit check</span><span>✓ Range check</span><span>✓ Design trace</span></div></div><div className="aiFindings">{warnings.length?warnings.map((w,i)=><p key={w}><b>{String(i+1).padStart(2,"0")}</b>{w}</p>):<p><b>01</b>Parameter berada dalam rentang pemeriksaan awal. Lanjutkan verifikasi terhadap data lapangan dan standar proyek.</p>}<p><b>{String(warnings.length+(warnings.length?1:2)).padStart(2,"0")}</b>Konfirmasi seluruh asumsi, kurva pabrikan, faktor simultan, serta SNI/PUIL/NFPA/ASHRAE yang berlaku sebelum IFC.</p></div></div>
    </section>

    <section className="copilot" style={{display:view==="design"?undefined:"none"}}>
      <div className="copilotHead"><div><span>AI</span><div><p>MEP PROJECT COPILOT</p><h2>Agen AI kontekstual</h2></div></div><button onClick={()=>setAgentOpen(!agentOpen)}>{agentOpen?"Tutup Panel":"Buka Panel"}</button></div>
      {agentOpen&&<div className="copilotGrid"><aside className="contextPanel"><h3>Konteks yang dibaca agen</h3><div><span>Modul aktif</span><b>{current.title}</b></div><div><span>Rekaman proyek</span><b>{projects.length}</b></div><div><span>Gate review</span><b>{checks.filter(Boolean).length}/6</b></div><div><span>Dokumen</span><b>{documents.length}</b></div><label className="uploadContext">+ Tambahkan dokumen<input type="file" multiple accept=".txt,.csv,.md,.pdf,.docx" onChange={e=>addDocuments(e.target.files)}/></label>{documents.length>0&&<ul>{documents.map((d,i)=><li key={i}>{d.name}<button onClick={()=>setDocuments(documents.filter((_,j)=>j!==i))}>×</button></li>)}</ul>}<small>TXT/CSV/MD dibaca sebagai konteks teks lokal. PDF/DOCX didaftarkan sebagai referensi dokumen dan tetap memerlukan pemeriksaan engineer.</small></aside>
      <div className="chatPanel"><div className="quickPrompts">{["Apa risiko desain ini?","Standar apa yang harus dicek?","Review RAB modul aktif","Buat ringkasan laporan"].map(x=><button key={x} onClick={()=>askAgent(x)}>{x}</button>)}</div><div className="chatStream">{messages.map((m,i)=><article className={m.role} key={i}><b>{m.role==="agent"?"AI MEP":"ENGINEER"}</b><p>{m.text}</p></article>)}</div><div className="chatInput"><textarea value={agentQuestion} onChange={e=>setAgentQuestion(e.target.value)} onKeyDown={e=>{if(e.key==="Enter"&&!e.shiftKey){e.preventDefault();askAgent();}}} placeholder="Tanyakan hasil, risiko, standar, biaya, atau laporan…"/><button onClick={()=>askAgent()}>Analisis</button></div><p className="agentDisclaimer">Agen merupakan decision-support. Keputusan akhir, standar, dan dokumen desain wajib diverifikasi serta disahkan engineer berwenang.</p></div></div>}
    </section>

    <section className="deliverables" style={{display:view==="design"?undefined:"none"}}>
      <div className="sectionTitle"><div><span>OUTPUT OTOMATIS</span><h2>BOQ awal & checklist koordinasi</h2></div><button onClick={()=>window.print()}>Cetak Laporan</button></div>
      <div className="reportMeta"><label>Disusun oleh<input value={preparedBy} onChange={e=>setPreparedBy(e.target.value)}/></label><label>Diperiksa oleh<input value={checkedBy} onChange={e=>setCheckedBy(e.target.value)}/></label><label>Tanggal laporan<input type="date" defaultValue={new Date().toISOString().slice(0,10)}/></label><label>Status dokumen<select defaultValue="Preliminary"><option>Preliminary</option><option>Design Review</option><option>Approved for Tender</option></select></label></div>
      <div className="deliverableGrid"><div className="boqCard"><h3>RAB awal berbasis BOQ</h3><table><thead><tr><th>Uraian</th><th>Vol.</th><th>Sat.</th><th>Harga satuan</th><th>Jumlah</th></tr></thead><tbody>{boqRows.map((x,i)=><tr key={i}><td>{x[0]}</td><td>{x[1]}</td><td>{x[2]}</td><td><input aria-label={`Harga satuan ${x[0]}`} type="number" min="0" value={unitPrices[i]||""} placeholder="Rp" onChange={e=>setUnitPrices({...unitPrices,[i]:Number(e.target.value)})}/></td><td>{rupiah(n(x[1])*n(unitPrices[i]||0))}</td></tr>)}</tbody></table>
      <div className="costFactors"><label>Overhead & profit<input type="number" value={overhead} onChange={e=>setOverhead(Number(e.target.value))}/><span>%</span></label><label>PPN<input type="number" value={tax} onChange={e=>setTax(Number(e.target.value))}/><span>%</span></label></div>
      <div className="costSummary"><p><span>Biaya langsung</span><b>{rupiah(directCost)}</b></p><p><span>Overhead & profit</span><b>{rupiah(overheadCost)}</b></p><p><span>PPN</span><b>{rupiah(taxCost)}</b></p><p className="grand"><span>TOTAL RAB AWAL</span><b>{rupiah(grandTotal)}</b></p></div><small>Harga satuan diisi oleh pengguna. Volume, spesifikasi, pajak, dan faktor biaya wajib dikonfirmasi terhadap gambar, AHSP/HSPK, lokasi, serta ketentuan kontrak.</small></div>
      <div className="checkCard"><div className="checkHead"><h3>Gate review sebelum persetujuan</h3><b>{Math.round(checks.filter(Boolean).length/6*100)}%</b></div>{["Data eksisting dan fungsi ruang sudah diverifikasi","Jalur utilitas bebas konflik arsitektur dan struktur","Akses operasi, inspeksi, dan pemeliharaan tersedia","Kapasitas cadangan dan skenario darurat diperiksa","Perhitungan mengikuti standar serta regulasi terbaru","Spesifikasi dan kurva pabrikan sudah dikonfirmasi"].map((x,i)=><label key={x}><input type="checkbox" checked={checks[i]} onChange={e=>setChecks(checks.map((v,j)=>j===i?e.target.checked:v))}/><span>{String(i+1).padStart(2,"0")}</span>{x}</label>)}<p className={checks.every(Boolean)?"gateReady":"gateHold"}>{checks.every(Boolean)?"SIAP UNTUK REVIEW/PERSETUJUAN":"DOKUMEN BELUM LENGKAP UNTUK DISETUJUI"}</p></div></div>
    </section>

    <section className="history" style={{display:view==="design"?undefined:"none"}}><div className="sectionTitle"><div><span>DATABASE</span><h2>Riwayat desain</h2></div><small>{projects.length} proyek tersimpan</small></div>{projects.length===0?<p className="empty">Belum ada proyek. Simpan hasil pertama untuk membangun jejak desain.</p>:<div className="historyGrid">{projects.slice(0,6).map(p=><button key={p.id} onClick={()=>{setModule(p.module);setData(p.data);setProjectName(p.name)}}><span>{p.module.toUpperCase()}</span><b>{p.name}</b><small>{new Date(p.createdAt).toLocaleString("id-ID")}</small></button>)}</div>}</section>
    <footer><b>MEP ENGINEER AI</b><span>Perhitungan awal untuk engineer · Hasil wajib diverifikasi oleh engineer berwenang dan standar terbaru.</span></footer>
  </main>;
}
