İSG Risk Analizi – HAZOP

Bir kimyasal reaktörde basıncın beklenenden fazla yükselmesi, bir boru hattında akışın tersine dönmesi veya depolama tankındaki seviyenin kontrolsüz artması... Bu olayların her biri üretimi aksatabilir, ekipmanlara zarar verebilir, çevresel salımlara ve ciddi iş kazalarına yol açabilir. Peki, bu tür sapmalar henüz gerçekleşmeden sistematik olarak nasıl incelenebilir?

HAZOP (Hazard and Operability Study – Tehlike ve İşletilebilirlik Analizi), proseslerin tasarlanan çalışma koşullarından hangi şekillerde sapabileceğini araştıran sistematik bir inceleme yöntemidir.

Bu ihtiyaca yönelik hazırlanan İSG Risk Analizi – HAZOP masaüstü HTML uygulaması, proses sapmalarını tanımlamak, nedenlerini ve sonuçlarını değerlendirmek, mevcut koruyucu önlemleri kaydetmek ve iyileştirme çalışmalarını takip etmek amacıyla geliştirilmiş çevrimdışı bir dijital çalışma aracıdır.

HAZOP Nedir, Neden Kullanılır?

HAZOP; kimya, petrokimya, enerji, petrol, doğal gaz, ilaç ve benzeri proses endüstrilerinde kullanılan yapılandırılmış bir tehlike inceleme yöntemidir.

Temel yaklaşımı, bir prosesin normalde nasıl çalışması gerektiğini belirlemek ve ardından bu çalışma düzeninden olası sapmaları araştırmaktır.

Örneğin bir boru hattında akışın belirli bir yönde ve debide gerçekleşmesi bekleniyorsa HAZOP incelemesi şu sorularla ilerler:

  • Akış tamamen durursa ne olur?
  • Akış beklenenden fazla veya az olursa hangi sorunlar ortaya çıkar?
  • Akış ters yönde gerçekleşebilir mi?
  • Bu sapmalara hangi ekipman arızaları veya işletme koşulları neden olabilir?
  • Mevcut koruyucular yeterli mi?
  • İlave önlem alınması gerekiyor mu?

Bu yaklaşım sayesinde yalnızca mevcut tehlikeler değil, belirli koşullar altında ortaya çıkabilecek istenmeyen proses durumları da sistematik olarak incelenir.

HAZOP'un önemli bir özelliği, tek kişinin yaptığı basit bir kontrol listesi çalışması olmamasıdır. Yöntem, proses tasarımını ve işletmeyi bilen farklı disiplinlerden uzmanların katıldığı ekip çalışmasıyla yürütülür.

HAZOP HTML Uygulaması Hangi Amaçla Hazırlandı?

Geleneksel HAZOP çalışmalarında proses sapmaları çoğunlukla Excel tablolarına veya özel yazılımlara kaydedilir. Ancak kayıtların düzenlenmesi, sapmaların sınıflandırılması, aksiyonların izlenmesi ve raporların hazırlanması zaman alabilir.

HAZOP masaüstü HTML uygulaması, bu işlemleri tek bir arayüzde toplamak amacıyla hazırlanmıştır.

Uygulama sayesinde proses incelemeleri kayıt altına alınabilir, sapmalara ilişkin değerlendirmeler tutarlı biçimde tutulabilir, mevcut ve hedef riskler karşılaştırılabilir ve alınması gereken önlemler izlenebilir.

Ayrıca bilgisayara ayrı bir program kurulmasını gerektirmeden, internet bağlantısı olmadan kullanılabilir.

HAZOP Analizinin Temel Mantığı

HAZOP yönteminde değerlendirme, proses parametresi ve kılavuz kelime birleşimiyle başlar.

Bir proses parametresi, işletme sırasında kontrol edilmesi veya belirli koşullarda tutulması gereken değişkendir. Basınç, sıcaklık, seviye, akış, pH ve derişim bunlara örnektir.

Kılavuz kelime ise parametrenin normal çalışma koşulundan hangi yönde sapabileceğini tanımlar.

Örneğin:

  • Basınç + Fazla = Beklenenden yüksek basınç
  • Akış + Yok = Akışın tamamen durması
  • Akış + Tersine = Akışın ters yönde gerçekleşmesi
  • Sıcaklık + Az = Beklenenden düşük sıcaklık

Bu birleşimler, HAZOP ekibine incelenecek senaryoları sistematik olarak oluşturma imkânı verir.

Uygulamadaki HAZOP kılavuz kelimeleri

Kılavuz kelimeİncelenen sapma
Yok / Hiç (NO)Beklenen durumun hiç gerçekleşmemesi
Fazla (MORE)Parametrenin beklenen değerin üzerine çıkması
Az (LESS)Parametrenin beklenenden düşük olması
Tersine (REVERSE)Akışın veya işlemin ters yönde gerçekleşmesi
Yanı Sıra (AS WELL AS)Beklenen işleme ilave bir durumun ortaya çıkması
Bir Bölümü (PART OF)İşlemin yalnızca bir kısmının gerçekleşmesi
Başka / Farklı (OTHER THAN)Beklenenin yerine farklı bir durum oluşması
Erken (EARLY)İşlemin beklenen zamandan önce gerçekleşmesi
Geç (LATE)İşlemin beklenen zamandan sonra gerçekleşmesi
Önce (BEFORE)İşlem sırasının değişmesi
Sonra (AFTER)Bir işlemin sıralamada farklı konumda gerçekleşmesi

Her kılavuz kelimenin her proses parametresi için anlamlı olmayabileceği unutulmamalıdır. Uygun kombinasyonlar, tasarım amacı ve gerçek işletme koşulları dikkate alınarak değerlendirilmelidir.

HAZOP HTML Formu Nasıl Doldurulur?

Uygulama üç ana bölümden oluşur: 

  • Çalışma ve Proses Bilgileri, 
  • Yeni HAZOP Sapma Kaydı 
  • HAZOP Sapma Kayıtları ve Aksiyon Takibi.
Sapma kayıt formunun içinde ise dört ayrı değerlendirme aşaması bulunmaktadır.

1. Çalışma ve Proses Bilgileri

İlk bölüm, HAZOP incelemesinin hangi işletmede, hangi proses için ve hangi koşullar altında yapıldığını tanımlar.

İşyeri / Firma: İncelemenin gerçekleştirildiği kuruluşun adı yazılır.

Tesis / İşletme: Çalışmanın yapıldığı tesis belirtilir. Örneğin kimyasal üretim tesisi, doğal gaz basınç düşürme istasyonu veya enerji üretim tesisi.

Proses / Ünite: İncelenecek proses ünitesi tanımlanır. Örneğin Reaktör Ünitesi R-100 veya Buhar Üretim Ünitesi.

P&ID / Doküman No.: Proses borulama ve enstrümantasyon diyagramının numarası yazılır. P&ID, Piping and Instrumentation Diagram ifadesinin kısaltmasıdır. HAZOP değerlendirmesinde başlıca teknik referanslardan biridir.

Değerlendirme Tarihi: Çalışmanın gerçekleştirildiği tarih girilir.

HAZOP Lideri: İncelemeyi yöneten, HAZOP konusunda yetkin kişinin adı belirtilir.

Çalışma Ekibi / Disiplinler: Proses, işletme, bakım, mekanik, elektrik, enstrümantasyon ve iş güvenliği gibi çalışmaya katılan disiplinler kaydedilir.

Revizyon / Rapor No.: Çalışmanın belge numarası ve revizyon bilgisi girilir.

İşletme Aşaması: Normal işletme, devreye alma, durdurma, bakım/müdahale veya acil durum gibi incelenecek aşama seçilir.

Kapsam / Hariç Tutulanlar / Referanslar: İncelemenin hangi ekipmanları, hatları ve işletme koşullarını kapsadığı; hangi bölümlerin kapsam dışında bırakıldığı ve hangi teknik dokümanların kullanıldığı belirtilir.

Bu bölümün doğru doldurulması, daha sonra raporları inceleyecek kişilerin değerlendirmenin sınırlarını anlamasını sağlar.


2. Düğüm, Tasarım Amacı ve Sapma Tanımı

HAZOP çalışmasının en önemli aşamalarından biri, incelenecek prosesin uygun parçalara ayrılmasıdır. Bu parçalara HAZOP düğümü (node) denir. Bir düğüm; belirli bir boru hattı, reaktör bölgesi, pompa grubu, tank bağlantısı veya kontrol sistemi olabilir.

* Düğüm / Node Adı
   İncelenen bölüm açıkça yazılır.
   Örnek: Reaktör R-101 giriş hattı.

* Ekipman / Hat / Enstrüman Tag
   İlgili ekipmanın veya enstrümanın teknik tanımlama numarası girilir.
   Örnek: R-101 / PT-205.

Normal Çalışma Değeri / Birim
İncelenen parametrenin normal çalışma koşulu belirtilir.
Örnek: 4,5 bar veya 90 °C.

* Tasarım Amacı / Beklenen İşleyiş
   Düğümün normal koşullarda ne yapması gerektiği açıklanır.
   Örnek: Reaktöre beslenen akışkanın belirlenen debi ve basınç sınırları içinde iletilmesi ve      prosesin güvenli işletme koşullarında sürdürülmesi.

Bu bilgi oldukça önemlidir. Çünkü tasarım amacı bilinmeden hangi durumun sapma olduğu doğru belirlenemez.

Proses Parametresi
İncelenecek parametre seçilir. Uygulamada akış, basınç, sıcaklık, seviye, kompozisyon/derişim, pH, debi/hız, reaksiyon, soğutma/ısıtma, elektrik/enstrüman havası ve zaman/sıralama gibi seçenekler bulunur.

HAZOP Kılavuz Kelimesi
Parametrenin normal çalışma koşullarından hangi yönde sapabileceğini belirlemek için uygun kılavuz kelime seçilir.

Oluşabilecek Sapma
Seçilen parametre ve kılavuz kelimeye göre oluşabilecek proses sapması yazılır. Uygulama, bazı kombinasyonlarda örnek sapma önerisi gösterir. Bu öneri doğrudan kabul edilmek zorunda değildir; gerçek proses koşullarına göre düzenlenmelidir.


3. Sapma Nedenleri, Sonuçları ve Mevcut Koruyucular

Sapma tanımlandıktan sonra üç temel konu araştırılır: Sapma neden oluşabilir, gerçekleşirse neye yol açar ve hangi koruyucular bu sonucu önleyebilir veya sınırlandırabilir?

Olası Sapma Nedenleri
Sapmaya neden olabilecek ekipman, kontrol sistemi veya işletme kaynaklı durumlar yazılır. Örneğin yüksek basınç sapmasında çıkış hattının tıkanması, kontrol vanasının arızalanması veya basınç kontrol sisteminin işlevini yerine getirememesi olası nedenler olabilir.

Olası Sonuçlar / Etkiler
Sapmanın gerçekleşmesi halinde ortaya çıkabilecek sonuçlar açıklanır. Aşırı basınç, ekipman hasarı, tehlikeli madde salımı, yangın, çalışanların maruz kalması, çevresel etkiler ve üretim kaybı gibi sonuçlar değerlendirilebilir.

Sonuçlar yazılırken yalnızca ekipmanın zarar görmesi değil, olayın insan, çevre ve işletme üzerindeki etkileri de düşünülmelidir.

Mevcut Bağımsız / Diğer Koruyucular
Proseste hâlihazırda bulunan koruyucu önlemler kaydedilir. Emniyet ventilleri, alarmlar, otomatik durdurma sistemleri, bağımsız emniyet fonksiyonları, operatör kontrolleri veya prosedürler buna örnek olabilir.

Ancak burada önemli bir ayrım vardır: Bir önlemin mevcut olması, mutlaka bağımsız ve yeterli koruma sağladığı anlamına gelmez. Bağımsızlık, güvenilirlik ve etkinlik ayrıca değerlendirilmelidir.

Koruma Kanıtı / Güvenilirlik / Test Referansı
Koruyucu önlemlerin dayandığı teknik kanıtlar bu alanda belirtilir. Örneğin test raporları, emniyet ventili kontrol kayıtları, alarm set değerleri veya emniyet sistemi doğrulama belgeleri yazılabilir.

İşletilebilirlik / İnsan / Çevre Etkisi
Sapmanın operasyonel süreklilik, personel güvenliği ve çevre üzerindeki diğer etkileri açıklanır. Bu alan, yalnızca büyük kazalara yol açabilecek durumları değil, prosesin güvenilir ve verimli işletilmesini etkileyen sorunları da incelemeye yardımcı olur.


4. Yardımcı Risk Önceliklendirmesi: 5×5 Matris

HAZOP HTML uygulamasında, sapmaların değerlendirilmesini kolaylaştırmak amacıyla 5×5 risk matrisi bulunmaktadır. Bu matriste iki faktör kullanılır:

Olasılık: Sapmanın ve ilgili istenmeyen sonucun değerlendirme koşulları altında meydana gelme olasılığı.

Şiddet: İstenmeyen sonucun ciddiyeti. (Her iki faktör de 1 ile 5 arasında puanlanır.)
Risk Puanı = Olasılık × Şiddet

Örneğin, olasılık 4 ve şiddet 5 olarak seçildiğinde: (Risk Puanı = 4 × 5 = 20) Uygulama sonucu otomatik hesaplar ve risk matrisinde gösterir.

Risk öncelik seviyeleri
PuanUygulamadaki sınıflandırma
1–4Kabul Edilebilir
5–9Kontrol Gerekli
10–15Yüksek
16–25Çok Yüksek

Bu aralıklar, uygulamadaki yardımcı sınıflamadır. HAZOP yönteminin zorunlu veya evrensel risk eşikleri değildir. HAZOP'un asıl amacı, kılavuz kelimeler aracılığıyla proses sapmalarını ve bunların olası sonuçlarını incelemektir. Matris yalnızca kararların önceliklendirilmesine yardımcı olur.


5. Öneriler, Aksiyonlar ve Doğrulama

Risk değerlendirmesinden sonra ilave önlem gerektiren konular için aksiyonlar oluşturulur.

Önerilen İlave Önlem / Aksiyon: Gerçekleştirilmesi gereken teknik veya idari iyileştirme açık biçimde yazılır.

Aksiyon Sorumlusu: Çalışmayı gerçekleştirecek kişi veya birim belirlenir.

Hedef Tamamlanma Tarihi: Önlemin tamamlanması için hedeflenen tarih girilir.

Aksiyon Durumu: Açık, Devam Ediyor veya Tamamlandı seçeneklerinden uygun olanı belirlenir.

Uygulama / Etkinlik Kanıtı ve Ek Not: Uygulanan önlemlerin test, kontrol ve doğrulama bilgileri kaydedilir.

Uygulamada ayrıca önlemin gerçekleştirildiğini ve etkinliğinin doğrulandığını belirten bir onay kutusu bulunmaktadır. Bu kutu yalnızca aksiyon tamamlandıktan ve hedef risk değerlendirmesi yapıldıktan sonra kullanılmalıdır.

Örnek Bir HAZOP Değerlendirmesi
Bir kimyasal tesiste bulunan R-101 reaktörünün basınç kontrolü incelensin.

Değerlendirme alanı Örnek giriş
Proses düğümüR-101 reaktör basınç hattı
Proses parametresiBasınç
Kılavuz kelimeFazla (MORE)
SapmaReaktör basıncının tasarımda öngörülen işletme sınırlarını aşması
Olası nedenÇıkış hattının tıkanması veya basınç kontrol vanasının arızalanması
Olası sonuçAşırı basınç, ekipman bütünlüğünün kaybı ve tehlikeli madde salımı
Mevcut koruyucularYüksek basınç alarmı ve uygunluğu doğrulanması gereken emniyet ventili
Mevcut olasılık4
Mevcut şiddet5
Mevcut risk puanı20
Önerilen aksiyonBasınç koruma tasarımının, emniyet ventili kapasitesinin ve bağımsız durdurma gereksiniminin mühendislik incelemesi
Hedef olasılık2
Hedef şiddet5
Hedef risk puanı10

Bu örnekte mevcut risk puanı 20, hedef risk puanı ise 10 olarak hesaplanmaktadır.

Şiddet puanının aynı kalmasının nedeni, olay gerçekleşirse olası sonucun hâlâ ciddi olabilmesidir. İyileştirme ile hedeflenen, bu sonuca yol açabilecek olayın gerçekleşme olasılığını azaltmaktır.

Bununla birlikte, bu örnekte verilen hedef puanlar yalnızca açıklama amaçlıdır. Gerçek risk azaltımının kabulü, mühendislik hesapları ve uygulama sonrası doğrulama gerektirir.


6. HAZOP Kayıtları ve Aksiyon Takibi

  • Form doldurulduktan sonra Sapma Kaydını Ekle butonuyla değerlendirme kayıt tablosuna aktarılır.
  • Bu bölümde kayıtlı sapmalar; proses düğümü, parametre, kılavuz kelime, neden, sonuç, mevcut risk, hedef risk ve aksiyon bilgileriyle birlikte görüntülenir.
  • Kayıtlar üzerinde düzenleme, kopyalama ve silme işlemleri yapılabilir.
  • Arama alanı kullanılarak belirli bir düğüme, sapmaya veya aksiyona ait kayıtlar bulunabilir. Durum filtresiyle de takip edilmesi gereken çalışmalar ayrıştırılabilir.
  • HAZOP çalışmalarında her sapmanın kendine özgü bir kayıt olarak değerlendirilmesi, sorumlulukların ve alınan kararların izlenmesini kolaylaştırır.


7. Risk Matrisi ve Genel Dağılım Grafikleri

Uygulamada iki grafik görünümü bulunur.

5 × 5 Matris, mevcut ve hedef risk puanlarını görsel olarak karşılaştırır. Mevcut risk mavi, hedef risk ise mor çerçeveyle işaretlenir.

Genel Dağılım, oluşturulan HAZOP sapma kayıtlarının risk öncelik seviyelerine göre dağılımını gösterir.

Bu grafikler hangi sapmaların öncelikli olarak ele alınması gerektiğini anlamaya yardımcı olur. Ancak grafiklerdeki sonuçlar, koruyucu tedbirlerin teknik olarak yeterli olduğunu tek başına kanıtlamaz.


8. Excel İçeri ve Dışarı Aktarma

HAZOP uygulamasında verilerin farklı çalışmalar arasında aktarılması için Excel desteği bulunmaktadır.

Excel İçeri Aktar seçeneği, uygulamanın HAZOP şablonuna uygun .xlsx dosyalarını yükler. İçeri aktarılan dosya, proses ve değerlendirme bilgileriyle birlikte HAZOP kayıtlarını uygulamaya taşır.

Excel Dışarı Aktar seçeneği ise oluşturulan kayıtları yeni bir Excel dosyasına kaydeder. Bu işlem özellikle uzun HAZOP toplantılarında, farklı zamanlarda gerçekleştirilen değerlendirmelerin arşivlenmesinde ve kayıtların güncellenmesinde yararlıdır.


9. PDF Raporlama ve Yazdırma

  • PDF / Yazdır seçeneğiyle HAZOP değerlendirmeleri raporlanabilir.
  • Raporda çalışma ve proses bilgileri, kayıtlı sapmalar, değerlendirme puanları, aksiyonlar ve risk grafikleri yer alır.
  • Kurum logosu ve başlık alanı da raporda görüntülenir.
  • Bu sayede HAZOP çalışmalarının çıktıları toplantılarda, teknik incelemelerde ve kurumsal kayıt süreçlerinde kullanılabilir.


10. Kurum Logosu ve Başlık Düzenleme

  • Uygulama başlığında kurum logosu, değerlendirme başlığı, açıklama ve işletme adı bulunmaktadır.
  • HTML dosyasıyla aynı klasörde logo.jpg dosyası bulunursa logo otomatik görüntülenebilir.
  • Logo alanına art arda beş kez tıklanarak farklı bir logo yüklenebilir.
  • Başlık ve kurum adı da aynı yöntemle düzenlenebilir.
  • Bu bilgiler tarayıcının yerel kayıt alanında saklanabilir. Böylece uygulama kurumsal kullanıma uygun hâle getirilebilir.


11. Çevrimdışı Kullanım ve Veri Kaybını Önleme

  • HAZOP masaüstü uygulaması bağımsız bir HTML dosyasıdır. Chrome veya Edge gibi güncel bir tarayıcıda açılır ve normal kullanım için internet bağlantısına ihtiyaç duymaz.
  • Uygulamanın önemli bir özelliği, yeni açılışta form ve risk kayıtlarının boş başlamasıdır. Bu nedenle çalışma boyunca oluşturulan kayıtlar, sayfa yenilenmeden veya uygulama kapatılmadan önce Excel olarak dışarı aktarılmalıdır.
  • Excel Dışarı Aktar işlemi, çalışmanın yedeğini almak için temel adımdır. Daha sonra aynı Excel dosyası kullanılarak çalışma yeniden başlatılabilir.

HAZOP HTML Uygulaması Kimler İçin Uygundur?

Uygulama özellikle; proses güvenliği çalışmalarında görev alan proses mühendisleri, işletme mühendisleri, bakım ve enstrümantasyon uzmanları, iş güvenliği uzmanları ve HAZOP ekipleri için hazırlanmıştır.

Kimya tesisleri, petrol rafinerileri, doğal gaz tesisleri, enerji santralleri, basınçlı proses sistemleri, reaktörler, depolama ve transfer hatları uygulamanın kullanılabileceği alanlara örnektir.

Uygulama ayrıca HAZOP yönteminin öğretilmesinde, örnek senaryolar hazırlanmasında ve ekip içi eğitim çalışmalarında yardımcı olabilir.

HAZOP Çalışmalarında Dikkat Edilmesi Gerekenler

  • Bir HAZOP değerlendirmesinin kalitesi, yalnızca formun bütün alanlarının doldurulmasına bağlı değildir.
  • Doğru ve güncel proses dokümanlarının kullanılması, tasarım amacının anlaşılması, gerçekçi sapma senaryolarının oluşturulması, koruyucuların güvenilirliğinin sorgulanması ve alınan kararların izlenmesi gerekir.
  • Bu nedenle uygulama, teknik değerlendirmeyi kolaylaştıran bir kayıt ve raporlama aracıdır; kendi başına proses güvenliğini onaylayan bir sistem değildir.
  • Özellikle yüksek tehlikeli proseslerde gerekli görüldüğünde ilave mühendislik değerlendirmeleri, bağımsız koruma katmanı analizleri ve ilgili güvenlik doğrulamaları gerçekleştirilmelidir.


Sonuç: HAZOP Formunu Doldurmak Değil, Proses Sapmalarını Anlamak

HAZOP'un değeri, bir tabloda kaç satır doldurulduğuyla ölçülmez. Asıl önemli olan, tasarlanan çalışma koşullarından hangi sapmaların meydana gelebileceğini, bu sapmaların nedenlerini, sonuçlarını ve mevcut koruma sistemlerinin yeterliliğini sistematik biçimde incelemektir.

İSG Risk Analizi – HAZOP masaüstü HTML uygulaması, bu sürecin kayıt, hesaplama, aksiyon takibi ve raporlama aşamalarını aynı ortamda birleştirir.

Kılavuz kelimelerle sapma oluşturma, mevcut ve hedef riskleri karşılaştırma, kayıtları Excel'e aktarma ve PDF raporu hazırlama özellikleriyle HAZOP çalışmalarının daha düzenli, takip edilebilir ve anlaşılır yürütülmesini destekler.

Unutulmaması gereken temel yaklaşım şudur: HAZOP, bir kazanın ardından neden gerçekleştiğini araştırmaktan önce, o kazaya yol açabilecek proses sapmalarını gerçekleşmeden sorgulamayı amaçlar.

Bu içerik, İSG Risk Analizi – HAZOP HTML uygulamasının tanıtımı ve kullanımı için hazırlanmıştır. Risk puanları ve örnek senaryolar eğitim amaçlıdır. Gerçek tesis değerlendirmeleri, yetkin HAZOP ekibi tarafından güncel proses dokümanları ve işletmeye özgü kabul kriterleri kullanılarak yürütülmelidir.

Bilgisayarda kullanmak için;
Bir Not defteri açarak, aşağıdaki (Mavi satırlar) html kodu içine kaydedin. Not defteri uzantısını .txt yerine ".html" olarak değiştirip kaydedin. Ör: "İSG Risk Analizi – FMEA.html" sayfayı kapatıp tekrar açtığınızda, web browser üzerinden sayfanız açılacaktır. Tüm kurum ve işletmelerde kullanabilecek bir formdur.

Sol tarafta görünen logo için, html dosyanız nerede ise, masaüstü veya oluşturulan bir klasör. Logonuzda aynı yerde olursa forma yansır. Logo ismi "logo.jpg" olmalı. Çift tıklayarak açtığınızda, browser üzerinde form açılışı gerçekleşir. Veya alanına 5 kez peş peşe tıklayarak çalışma esnasında logo değişimi de yapılabilir.

Başlık, başlığın altındaki, başlığın sağındaki ve alttaki yazıları, not defteri içinde "Ctrl + F" tuşları ile kelime yazarak bulabilir, ilgili metinleri kendinize göre uyarlayarak kaydederek kullanabilirsiniz. Başlık alanındaki "OLGUN BAŞKANLIĞI" alanına 5 kez peş peşe tıklayarak; Departman, Müdürlük veya Başkanlığınıza ait bilgiyi manuel girebilirsiniz.

Formun boş hali;

Formun dolu hali:

PDF / Yazdır

Dışarı Aktarılan Excel Verisi:


+ Projenin Android telefonlar için mobil uygulamaya dönüştürülmüş hali aşağıdaki paylaşımdadır. "Hazop.apk" indir.


HTML Kod:
<!DOCTYPE html>
<html lang="tr"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="theme-color" content="#edf4f9"><title>İSG Risk Analizi - HAZOP</title>
<style>
:root{--ink:#183146;--dim:#5d7282;--blue:#0b72a5;--line:#c9d8e4;--pale:#e9f5fd;--bg:#f1f6fa;--radius:7px}
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:#eaf1f6;color:var(--ink);font:13px/1.43 'Segoe UI',Arial,sans-serif}
button,input,textarea,select{font:inherit}button{cursor:pointer}input,select,textarea{width:100%;border:1px solid #c8d6e1;background:#fff;color:#1d354a;border-radius:5px;min-height:34px;padding:7px 8px;outline:0}textarea{min-height:65px;resize:vertical}input:focus,textarea:focus,select:focus{border-color:#268dc5;box-shadow:0 0 0 2px #268dc522}
label{display:block;color:#29465c;font-weight:700;font-size:11.5px;margin-bottom:4px}.req:after{content:' *';color:#ba2537}.hint,.small{font-size:10.5px;color:#61788b}.small{margin-top:4px}.shell{margin:8px;border:2px solid #8ca7bc;min-height:calc(100vh - 16px);border-radius:7px;background:#f9fcfe;box-shadow:0 4px 18px #23425a15}
.header{position:sticky;top:0;z-index:20;border-top:4px solid #0579ad;border-bottom:1px solid #bed1df;background:#fff;border-radius:4px 4px 0 0}.header-in{min-height:75px;display:flex;gap:13px;padding:8px 17px;align-items:center}.logo-btn{border:1px solid #cbd9e4;background:#f6fbff;flex:0 0 61px;width:61px;height:57px;border-radius:5px;padding:3px;color:#11658b;font-weight:800;font-size:12px;display:grid;place-items:center}.logo-btn img{max-width:100%;max-height:100%;object-fit:contain}.brandtitle{flex:1;min-width:0}.brandtitle h1{color:#08658e;font-size:19px;line-height:1.22;margin:0}.brandtitle p{margin:3px 0 0;font-size:11px;color:#4d6576}.org{margin-left:auto;max-width:35%;font-size:15px;font-weight:800;text-align:right;overflow-wrap:anywhere;color:#23455e}
.header [contenteditable=true]{outline:2px dashed #0284c7;outline-offset:3px}
main{padding:12px 17px 7px;display:grid;gap:10px}.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.metric{border:1px solid #c7d8e3;border-top:3px solid #0c83bd;border-radius:6px;background:#fff;padding:9px 12px}.metric small{font-size:10px;color:#5b7386;font-weight:750;display:block}.metric strong{color:#164766;font-size:23px;display:block;line-height:1.35}
.card{background:#fff;border:1px solid var(--line);border-radius:6px;overflow:hidden;min-width:0;box-shadow:0 2px 7px #17405a0a}.card-h{padding:9px 12px;background:var(--pale);border-left:4px solid #087dae;border-bottom:1px solid #c2ddea;display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap}.card-h h2{font-size:14px;line-height:1.35;margin:0;color:#0b587c}.sect{display:inline-block;padding:3px 6px;background:#d2ecfb;color:#135d88;border-radius:4px;font-size:10px;margin-right:5px}.card-b{padding:11px 13px}.grid{display:grid;gap:10px}.g5{grid-template-columns:repeat(5,minmax(0,1fr))}.g4{grid-template-columns:repeat(4,minmax(0,1fr))}.g3{grid-template-columns:repeat(3,minmax(0,1fr))}.g2{grid-template-columns:repeat(2,minmax(0,1fr))}.span2{grid-column:span 2}.span3{grid-column:span 3}.span4{grid-column:span 4}
.strip{display:flex;gap:8px;align-items:center;font-size:12px;font-weight:800;color:#11618c;margin:12px 0 9px;border-bottom:1px solid #e2eef7;padding-bottom:6px}.strip:first-child{margin-top:0}.strip i{font-style:normal;font-size:10px;width:23px;height:23px;display:grid;place-items:center;background:#e8f5fc;border:1px solid #c0ddeb;border-radius:5px}
.method{padding:7px 9px;border-radius:5px;background:#eaf7fd;border:1px solid #bdddeb;font-weight:700;color:#135f88;min-height:34px}.guidetip{background:#f5fafc;border:1px solid #e1edf5;border-radius:6px;font-size:11px;color:#546b7c;padding:9px 11px;margin-top:9px}
.analysis{display:grid;grid-template-columns: minmax(355px,.9fr) minmax(480px,1.1fr);gap:12px}.riskstack{display:grid;gap:8px}.riskbox{border:1px solid #c8d8e2;border-top:3px solid #176eb9;border-radius:6px;overflow:hidden}.riskbox[data-side=after]{border-top-color:#8651b4}.riskhead{background:#f2f8fc;border-bottom:1px solid #dfebf1;padding:7px 10px;font-weight:800}.riskfactors{display:grid;grid-template-columns:1fr 1fr;gap:9px;padding:9px}.riskresult{background:#f7fafc;border-top:1px solid #e6eef5;padding:7px 10px;display:flex;align-items:center;justify-content:space-between;gap:8px}.riskresult b{font-size:19px}.riskpill{font-size:10px;display:inline-block;padding:4px 6px;border-radius:4px;border:1px solid #cdd6e0;font-weight:800;text-align:center;background:#eff3f6}.risk-low{background:#ddf5e3;border-color:#a4dfb5;color:#16603a}.risk-medium{background:#fff6db;border-color:#e8d490;color:#806017}.risk-high{background:#ffe4c8;border-color:#e9b07c;color:#9f4914}.risk-critical{background:#ffe1e4;border-color:#e9a0ab;color:#9d2639}
.visual{border:1px solid #b9d2e1;border-radius:6px;overflow:hidden}.visual-h{display:flex;justify-content:space-between;gap:6px;flex-wrap:wrap;align-items:center;background:#e9f5fb;border-bottom:1px solid #c3dbe8;padding:8px 10px;font-weight:800;color:#0c5579}.mode-btn{padding:5px 9px;min-height:29px;background:#fff;border:1px solid #bcd6e6;color:#2c5972;border-radius:5px;font-size:11px;font-weight:800}.mode-btn.active{background:#d0ebfb;border-color:#0f83bc;color:#075b83}.visual-b{padding:11px}.barrow{display:grid;grid-template-columns:105px minmax(0,1fr) 65px;gap:7px;align-items:center;margin-bottom:8px;font-size:11px}.bar{height:22px;background:#f2f6fa;border:1px solid #e2e9ee;border-radius:5px;overflow:hidden}.barfill{height:100%;transition:width .18s}.barlabel{font-weight:750}.barvalue{text-align:right;font-weight:750}.summary{padding:8px;background:#f6f9fb;border:1px solid #e1eaf1;font-size:11px;color:#485f70;border-radius:5px;margin-top:6px}.legend{font-size:10px;color:#667c89;margin-top:9px}.matrix{display:grid;grid-template-columns:repeat(6,minmax(0,1fr));gap:3px;max-width:415px;margin:4px auto}.matrix>div{text-align:center;padding:7px 2px;font-size:10px;font-weight:750;border-radius:3px}.matrix .axis{background:#e8f1f8;color:#246080}.matrix .risk-cell{border:1px solid #ffffffaa;min-height:29px}.matrix .is-before{outline:3px solid #0c56b5;outline-offset:-3px}.matrix .is-after{box-shadow:inset 0 0 0 3px #773da9}.matrix .is-both{outline:3px solid #0c56b5;outline-offset:-3px;box-shadow:inset 0 0 0 3px #773da9}
.checkline{display:flex;gap:8px;align-items:flex-start;padding:8px 10px;background:#f5fafc;border:1px solid #dceaf0;border-radius:5px}.checkline input{width:16px;height:16px;min-height:0;accent-color:#0b7db0;flex:none;margin-top:1px}.checkline label{margin:0;font-weight:500}
.actions{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-top:12px}.btn{border:1px solid #c5d4df;border-radius:5px;background:#fff;padding:7px 10px;color:#2c4a5c;font-weight:700;min-height:33px;display:inline-flex;justify-content:center;align-items:center;gap:5px}.btn:hover{background:#eff6fa}.btn-primary{background:#057eb6;color:#fff;border-color:#057eb6}.btn-primary:hover{background:#086993}.btn-small{font-size:10.5px;padding:4px 7px;min-height:27px}.btn-blue{background:#e7f5ff;border-color:#b8dbf4;color:#0b5881}.btn-green{background:#e7f8ee;border-color:#b5e0c6;color:#17633d}.btn-red{color:#b32639;border-color:#efb5bf}.grow{flex:1}.filters{display:flex;gap:7px;flex-wrap:wrap}.filters input{width:200px}.filters select{width:155px}.tablewrap{overflow-x:auto}.tablewrap table{border-collapse:collapse;width:100%;min-width:1270px}.tablewrap th{padding:8px 7px;text-align:left;background:#edf5fa;color:#4e6476;border-bottom:1px solid #dae4ea;font-size:10.5px}.tablewrap td{padding:8px 7px;border-bottom:1px solid #e7eef3;font-size:10.5px;vertical-align:top;overflow-wrap:anywhere}.tablewrap tbody tr:hover{background:#f7fbff}.empty{padding:20px;text-align:center;color:#60788a;font-size:12px}.cellsub{color:#6a7c8c;font-size:10px;margin-top:3px}.table-bottom{padding:7px 11px}.bottom-actions{padding:5px 17px 4px;display:flex}.bottom-panel{border:1px solid #bed4e1;background:#fff;border-radius:6px;padding:8px 10px;display:flex;gap:9px;flex-wrap:wrap}.footer{white-space:nowrap;overflow-x:auto;font-size:11px;border-top:1px solid #d7e4ec;margin:0 17px;padding:5px 0 4px;color:#4c6074;line-height:1.35}.muted-red{color:#ac343f}.sr-only{position:absolute!important;width:1px!important;height:1px!important;padding:0!important;overflow:hidden!important;clip:rect(0,0,0,0)!important;white-space:nowrap!important;border:0!important}.toast{position:fixed;z-index:40;right:17px;bottom:15px;max-width:400px;padding:9px 12px;color:#fff;background:#194b68;border-radius:5px;opacity:0;pointer-events:none;transition:opacity .18s}.toast.on{opacity:1}
@media(max-width:1120px){.analysis{grid-template-columns:1fr}.riskstack{grid-template-columns:1fr 1fr}.g5{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(2,minmax(0,1fr))}.metrics{grid-template-columns:1fr 1fr}}
@media(max-width:720px){.shell{margin:3px}.header-in{padding:8px 10px;flex-wrap:wrap}.brandtitle h1{font-size:15px}.brandtitle p{font-size:10px}.logo-btn{flex-basis:49px;width:49px;height:49px}.org{max-width:100%;width:100%;font-size:11px;text-align:left}.brandtitle{flex:1}.metrics{gap:5px}.metric{padding:7px}.metric strong{font-size:19px}main{padding:9px;gap:9px}.g5,.g4,.g3,.g2{grid-template-columns:1fr}.span2,.span3,.span4{grid-column:auto}.riskstack{grid-template-columns:1fr}.riskfactors{grid-template-columns:1fr 1fr}.barrow{grid-template-columns:90px 1fr 54px}.filters{width:100%}.filters input,.filters select{min-width:120px;flex:1;width:auto}.bottom-actions{padding:5px 9px}.bottom-panel{width:100%;gap:5px}.bottom-panel .btn{flex:1 1 110px;white-space:normal}.footer{font-size:10px;margin:0 9px}}
@page{size:A4 landscape;margin:9mm 9mm 15mm 9mm}
#printPages,#printPageFrame{display:none}
@media print{
html,body{background:#fff!important;margin:0!important;padding:0!important;font-size:9px!important;-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
#uiShell,.toast{display:none!important}
#printPages{display:table!important;width:100%!important;border:0!important;border-collapse:separate;border-spacing:0;table-layout:fixed}
#printPages>thead{display:table-header-group!important}#printPages>tbody{display:table-row-group!important}#printPages>thead>tr>td{padding:0 0 7px}#printPages>tbody>tr>td{padding:0 9px 8px}
#printPageFrame{display:block!important;position:fixed;inset:0;border:1.5px solid #809bb2;pointer-events:none;z-index:15}
.p-header{display:flex;gap:11px;align-items:center;padding:6px 11px;border-top:4px solid #0c84b8;border-bottom:1px solid #abc8d9;min-height:63px;background:#fff!important}
.p-header img{width:54px;height:48px;object-fit:contain}.p-header h1{margin:0;font-size:16px;color:#06648d}.p-header p{font-size:9.5px;margin:3px 0;color:#526879}.p-org{margin-left:auto;font-size:12px;font-weight:800;max-width:30%;text-align:right}
.p-section{border:1px solid #b9cdda;border-radius:3px;margin-bottom:7px;break-inside:auto}.p-section h3{font-size:10.5px;background:#eaf5fc!important;border-left:4px solid #067fb3;margin:0;padding:5px 8px;color:#075d81;break-after:avoid}
.p-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:5px;padding:7px}.p-field{border:1px solid #dce8ef;border-radius:3px;padding:4px 5px;overflow-wrap:anywhere;font-size:9px;white-space:pre-wrap}.p-field b{display:block;color:#667d8e;font-weight:700;font-size:8px;margin-bottom:2px}
.p-charts{display:grid;grid-template-columns:1fr 1fr;gap:9px;padding:7px}.p-chart{border:1px solid #d6e3eb;border-radius:3px;padding:5px}.p-chart h4{font-size:10px;margin:0 0 3px;color:#075c81}.p-chart svg{display:block;width:100%;height:auto;max-height:130px}.p-note{padding:4px 8px;color:#6a7886;font-size:8px;line-height:1.35}
.p-table{width:100%;table-layout:fixed;border-collapse:collapse}.p-table th,.p-table td{border:1px solid #d5e4ed;padding:4px 5px;font-size:8px;vertical-align:top;overflow-wrap:anywhere}.p-table th{background:#eaf4fa!important;color:#105f84}.p-table tr{break-inside:avoid}.p-table thead{display:table-header-group}
.p-detail{margin:5px 7px 7px;border:1px solid #d6e3eb;break-inside:avoid!important;page-break-inside:avoid!important}.p-detail-head{display:flex;justify-content:space-between;background:#eaf5fa!important;padding:5px 6px;gap:8px;font-weight:800;color:#145c7e;font-size:9px}.p-detail-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:5px;padding:5px}.p-detail-grid .p-field{font-size:8px}.p-detail-grid .p-field b{font-size:7px}
.p-charts,.p-charts *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
}

/* HAZOP RAPORU – SADECE YAZDIRMA / PDF DÜZENİ
   A4 yatay, en az kenar boşluğu, bölüm başlığı koruması,
   gerektiğinde satır bazında taşan kayıt, yeni sayfada tekrarlanan kayıt başlığı.
   Ekran formu, veri modeli, Excel, logo ve grafiklerin çalışma kodları değişmez. */
@page {size:A4 landscape;margin:6mm 7mm 7mm 7mm}
@media print {
 html,body {font-size:8.5px!important}
 #printPages>thead>tr>td{padding:0 0 3px!important}
 #printPages>tbody>tr>td{padding:0!important}
 #printContent{line-height:1.27!important}
 .p-header {min-height:49px!important;padding:3px 8px!important;gap:8px!important}
 .p-header img{width:43px!important;height:40px!important}
 .p-header h1{font-size:14px!important}
 .p-section{margin-bottom:3px!important;break-inside:auto!important;page-break-inside:auto!important}
 .p-section h3{break-after:avoid-page!important;page-break-after:avoid!important;padding:3px 7px!important}
 .p-charts{padding:4px!important;gap:5px!important}
 .p-charts svg{max-height:96px!important}
 .p-grid{gap:3px!important;padding:4px!important}
 .p-field{padding:2px 3px!important;font-size:8px!important}
 .p-field b{font-size:7px!important;margin-bottom:1px!important}
 .p-table th,.p-table td{padding:2px 3px!important;font-size:7.5px!important}
 .p-table tr{break-inside:avoid-page!important;page-break-inside:avoid!important}
 .p-note{font-size:7px!important;padding:2px 6px!important;line-height:1.2!important}
 /* Başlık her sayfa parçasında tablonun kendi thead kısmından yeniden yazdırılır. */
 .p-detail{display:block!important;margin:2px 3px!important;break-inside:auto!important;page-break-inside:auto!important}
 .p-detail-table{width:100%!important;border-collapse:separate!important;border-spacing:2px 0!important;table-layout:fixed!important;break-inside:auto!important}
 .p-detail-table thead{display:table-header-group!important}
 .p-detail-table th{padding:0!important;font-size:9px!important;text-align:left!important;border:0!important}
 .p-detail-table th .p-detail-head{margin:0!important;padding:2px 4px!important;background:#eaf5fa!important;line-height:1.2!important;font-size:8.3px!important;break-after:avoid-page!important;page-break-after:avoid!important}
 .p-detail-table tr{break-inside:avoid-page!important;page-break-inside:avoid!important}
 .p-detail-table td{vertical-align:top!important;padding:0!important;border:0!important;overflow-wrap:anywhere!important;line-height:1.23!important}
 .p-detail-table td .p-field{height:100%!important;padding:1px 2px!important;line-height:1.27!important;font-size:7.55px!important;break-inside:avoid-page!important;page-break-inside:avoid!important}
 .p-detail-table td .p-field b{font-size:6.9px!important;line-height:1.2!important;margin-bottom:0!important}
}
</style></head>
<body>
<div id="uiShell" class="shell">
<header class="header"><div class="header-in">
<button id="logoTrigger" class="logo-btn" title="Logoyu değiştirmek için art arda beş kez tıklayın"><img id="brandLogo" alt="Kurum logosu" hidden><span id="logoFallback">LOGO</span></button>
<div class="brandtitle"><h1 id="brandTitle" title="Değiştirmek için 5 kez tıklayın">İSG RİSK ANALİZİ – HAZOP</h1><p id="brandSubtitle" title="Değiştirmek için 5 kez tıklayın">Tehlike ve İşletilebilirlik Analizi · Proses Sapmaları ve Aksiyon Takip Formu</p></div>
<div class="org" id="brandOrg" title="Değiştirmek için 5 kez tıklayın">KURUM / İŞLETME</div></div>
<input type="file" id="logoFile" class="sr-only" accept="image/png,image/jpeg,image/webp,image/gif,image/svg+xml"></header>
<main>
<section class="metrics" aria-label="HAZOP sonuç göstergeleri"><div class="metric"><small>TOPLAM SAPMA KAYDI</small><strong id="total">0</strong></div><div class="metric"><small>YÜKSEK / ÇOK YÜKSEK · AÇIK</small><strong id="priority">0</strong></div><div class="metric"><small>TERMİNİ GEÇEN AKSİYON</small><strong id="overdue">0</strong></div><div class="metric"><small>ÖNLEMİ DOĞRULANAN</small><strong id="verifiedCount">0</strong></div></section>
<section class="card"><div class="card-h"><h2><span class="sect">01</span> Çalışma ve Proses Bilgileri</h2><span class="hint">Yeni açılışta değerlendirme alanları boş gelir.</span></div><div class="card-b grid g5">
<div><label for="metaCompany">İşyeri / Firma</label><input id="metaCompany" data-meta="company" placeholder="Örn. Proses Kimya A.Ş."></div>
<div><label for="metaFacility">Tesis / İşletme</label><input id="metaFacility" data-meta="facility" placeholder="Örn. Reaktör Tesisi"></div>
<div><label for="metaUnit">Proses / Ünite</label><input id="metaUnit" data-meta="unit" placeholder="Örn. Ünite-210"></div>
<div><label for="metaPid">P&ID / Doküman No.</label><input id="metaPid" data-meta="pid" placeholder="Örn. P-210-05"></div>
<div><label for="metaDate">Değerlendirme Tarihi</label><input id="metaDate" data-meta="date" type="date"></div>
<div><label for="metaLeader">HAZOP Lideri</label><input id="metaLeader" data-meta="leader" placeholder="Eğitimli çalışma lideri"></div>
<div class="span2"><label for="metaTeam">Çalışma Ekibi / Disiplinler</label><input id="metaTeam" data-meta="team" placeholder="Proses, işletme, bakım, enstrümantasyon, İSG..."></div>
<div><label for="metaRevision">Revizyon / Rapor No.</label><input id="metaRevision" data-meta="revision" placeholder="HAZOP-01 / Rev.00"></div>
<div><label for="metaPhase">İşletme Aşaması</label><select id="metaPhase" data-meta="phase"><option value="">Seçiniz</option><option>Normal işletme</option><option>Devreye alma</option><option>Durdurma</option><option>Bakım / müdahale</option><option>Acil durum</option><option>Diğer</option></select></div>
<div class="span4"><label for="metaScope">Kapsam / Hariç Tutulanlar / Referanslar</label><input id="metaScope" data-meta="scope" placeholder="İncelenen düğüm sınırları, P&ID revizyonu, tasarım verisi, hariç tutulan ekipmanlar..."></div>
</div></section>
<section class="card"><div class="card-h"><h2><span class="sect">02</span> <span id="formMode">Yeni HAZOP Sapma Kaydı</span></h2><button type="button" class="btn btn-small" id="newRecordTop">+ Yeni Kayıt</button></div><div class="card-b"><form id="recordForm" autocomplete="off" novalidate>
<div class="strip"><i>01</i> Düğüm, Tasarım Amacı ve Sapma Tanımı</div>
<div class="grid g4">
<div><label>Yöntem</label><div class="method">HAZOP <span class="hint">Kılavuz kelime + parametre</span></div></div>
<div><label for="node" class="req">Düğüm / Node Adı</label><input id="node" required placeholder="Örn. Reaktör R-101 giriş hattı"></div>
<div><label for="tag">Ekipman / Hat / Enstrüman Tag</label><input id="tag" placeholder="Örn. R-101 / FT-205"></div>
<div><label for="normal">Normal Çalışma Değeri / Birim</label><input id="normal" placeholder="Örn. 4,5 bar / 90 °C"></div>
<div class="span4"><label for="intent" class="req">Tasarım Amacı / Beklenen İşleyiş</label><textarea id="intent" required placeholder="Düğümün normal akışını, tasarım koşullarını ve hangi işlevi sağlaması gerektiğini açıklayın."></textarea></div>
<div><label for="parameter" class="req">Proses Parametresi</label><select id="parameter" required><option value="">Seçiniz</option><option>Akış</option><option>Basınç</option><option>Sıcaklık</option><option>Seviye</option><option>Kompozisyon / Derişim</option><option>pH</option><option>Debi / Hız</option><option>Reaksiyon</option><option>Soğutma / Isıtma</option><option>Elektrik / Enstrüman Havası</option><option>Zaman / Sıralama</option><option value="Diğer">Diğer</option></select></div>
<div><label for="guide" class="req">HAZOP Kılavuz Kelimesi</label><select id="guide" required><option value="">Seçiniz</option><option value="NO">Yok / Hiç (NO)</option><option value="MORE">Fazla (MORE)</option><option value="LESS">Az (LESS)</option><option value="REVERSE">Tersine (REVERSE)</option><option value="AS_WELL_AS">Yanı Sıra (AS WELL AS)</option><option value="PART_OF">Bir Bölümü (PART OF)</option><option value="OTHER_THAN">Başka / Farklı (OTHER THAN)</option><option value="EARLY">Erken (EARLY)</option><option value="LATE">Geç (LATE)</option><option value="BEFORE">Önce (BEFORE)</option><option value="AFTER">Sonra (AFTER)</option></select></div>
<div class="span2"><label for="deviation" class="req">Oluşabilecek Sapma</label><input id="deviation" required placeholder="Örn. Beklenen basınçtan daha yüksek basınç"></div>
</div><div class="guidetip">Kılavuz kelime ve parametre seçildiğinde örnek sapma otomatik önerilir; sahaya uygun olmayan kombinasyonları değiştirebilirsiniz. Sapmalar P&ID, işletme sınırları ve proses tasarım amacı esas alınarak ekipçe değerlendirilmelidir.</div>
<div class="strip"><i>02</i> Nedenler, Sonuçlar ve Mevcut Koruyucular</div>
<div class="grid g3">
<div><label for="causes" class="req">Olası Sapma Nedenleri</label><textarea id="causes" required placeholder="Örn. Kontrol vanası arızası, regülatör arızası, hat tıkanması."></textarea></div>
<div><label for="consequences" class="req">Olası Sonuçlar / Etkiler</label><textarea id="consequences" required placeholder="Örn. Aşırı basınç, kap bütünlüğünün bozulması, yangın."></textarea></div>
<div><label for="safeguards">Mevcut Bağımsız / Diğer Koruyucular</label><textarea id="safeguards" placeholder="Örn. PSV, bağımsız alarm, SIS, operatör müdahalesi; bağımsızlık doğrulanmalı."></textarea></div>
<div class="span2"><label for="verification">Koruma Kanıtı / Güvenilirlik / Test Referansı</label><input id="verification" placeholder="Örn. PSV test tarihi, SIS doğrulama kaydı, alarm set noktası; varsa kanıt ekleyin."></div>
<div><label for="existingNotes">İşletilebilirlik / İnsan / Çevre Etkisi</label><input id="existingNotes" placeholder="Üretim kaybı, çevresel salım, personel maruziyeti..."></div>
</div>
<div class="strip"><i>03</i> Yardımcı Risk Önceliklendirmesi (5 × 5)</div>
<div class="analysis"><div class="riskstack">
<section class="riskbox" data-side="before"><div class="riskhead">MEVCUT RİSK · Koruyucuların durumu dikkate alınarak</div><div class="riskfactors"><div><label for="beforeL" class="req">Olasılık (1–5)</label><select id="beforeL" required><option value="">Seçiniz</option><option value="1">1 · Çok düşük</option><option value="2">2 · Düşük</option><option value="3">3 · Orta</option><option value="4">4 · Yüksek</option><option value="5">5 · Çok yüksek</option></select></div><div><label for="beforeS" class="req">Şiddet (1–5)</label><select id="beforeS" required><option value="">Seçiniz</option><option value="1">1 · Hafif</option><option value="2">2 · Sınırlı</option><option value="3">3 · Ciddi</option><option value="4">4 · Çok ciddi</option><option value="5">5 · Felaket düzeyi</option></select></div></div><div class="riskresult"><div><span class="hint">Mevcut O × Ş</span><br><b id="beforeScore">—</b></div><span id="beforeLevel" class="riskpill">Değerlendirilmedi</span></div></section>
<section class="riskbox" data-side="after"><div class="riskhead">HEDEF / KALAN RİSK · Önlem sonrası tahmin</div><div class="riskfactors"><div><label for="afterL">Hedef Olasılık (1–5)</label><select id="afterL"><option value="">Seçiniz</option><option value="1">1 · Çok düşük</option><option value="2">2 · Düşük</option><option value="3">3 · Orta</option><option value="4">4 · Yüksek</option><option value="5">5 · Çok yüksek</option></select></div><div><label for="afterS">Hedef Şiddet (1–5)</label><select id="afterS"><option value="">Seçiniz</option><option value="1">1 · Hafif</option><option value="2">2 · Sınırlı</option><option value="3">3 · Ciddi</option><option value="4">4 · Çok ciddi</option><option value="5">5 · Felaket düzeyi</option></select></div></div><div class="riskresult"><div><span class="hint">Hedef O × Ş</span><br><b id="afterScore">—</b></div><span id="afterLevel" class="riskpill">Hedef girilmedi</span></div></section>
</div><section class="visual"><div class="visual-h"><span>▦ HAZOP Risk Görünümü</span><div><button type="button" class="mode-btn active" id="singleMode">5 × 5 Matris</button> <button type="button" class="mode-btn" id="distMode">Genel Dağılım</button></div></div><div class="visual-b"><div id="chart"></div><div class="summary" id="riskSummary">Olasılık ve şiddet puanlarıyla örnek risk matrisi oluşur.</div><div class="legend">Mavi çerçeve: mevcut risk · Mor çerçeve: hedef risk. <b>Bu puanlama HAZOP yönteminin zorunlu bir unsuru değildir.</b></div></div></section></div>
<div class="strip"><i>04</i> Öneriler, Aksiyonlar ve Doğrulama</div>
<div class="grid g4">
<div class="span2"><label for="action" class="req">Önerilen İlave Önlem / Aksiyon</label><textarea id="action" required placeholder="Örn. Bağımsız yüksek basınç kesmesi, enstrümantasyon revizyonu, işlem prosedürü."></textarea></div>
<div><label for="owner">Aksiyon Sorumlusu</label><input id="owner" placeholder="Kişi / birim"></div>
<div><label for="due">Hedef Tamamlanma Tarihi</label><input id="due" type="date"></div>
<div><label for="status">Aksiyon Durumu</label><select id="status"><option value="open">Açık</option><option value="progress">Devam Ediyor</option><option value="done">Tamamlandı</option></select></div>
<div class="span3"><label for="evidence">Uygulama / Etkinlik Kanıtı ve Ek Not</label><input id="evidence" placeholder="Test raporu, fotoğraf, P&ID güncelleme, önlem doğrulama tutanağı..."></div>
</div><div class="checkline" style="margin-top:10px"><input type="checkbox" id="verified"><label for="verified">Önlem uygulandı ve koruyucu tedbirin etkinliği doğrulandı. <span class="hint">Yalnızca aksiyon “Tamamlandı” durumundaysa ve hedef puanlar girilmişse işaretlenebilir.</span></label></div>
<div class="actions"><button class="btn btn-primary" type="submit" id="saveBtn">+ Sapma Kaydını Ekle</button><button class="btn" type="button" id="cancelBtn">Vazgeç / Yeni Kayıt</button><span class="grow"></span><span class="hint" id="modeInfo">Yeni HAZOP kaydı oluşturuluyor.</span></div>
</form></div></section>
<section class="card"><div class="card-h"><h2><span class="sect">03</span> HAZOP Sapma Kayıtları ve Aksiyon Takibi <span id="recordCount" class="hint">(0)</span></h2><div class="filters"><input id="search" type="search" placeholder="Düğüm, sapma, neden, aksiyon ara..."><select id="filter"><option value="">Tüm durumlar</option><option value="open">Açık</option><option value="progress">Devam Ediyor</option><option value="done">Tamamlandı</option></select></div></div>
<div class="tablewrap"><table><thead><tr><th>No.</th><th>Proses Düğümü / Tag</th><th>Parametre + Kılavuz Kelime</th><th>Sapma / Neden</th><th>Sonuç / Koruyucular</th><th>Mevcut Risk</th><th>Hedef Risk</th><th>Aksiyon</th><th>Sorumlu / Termin</th><th>Durum</th><th>İşlemler</th></tr></thead><tbody id="rows"></tbody></table><div class="empty" id="empty">Henüz bir sapma kaydı bulunmuyor. Yukarıdaki formdan kayıt oluşturabilirsiniz.</div></div>
<div class="table-bottom"><button class="btn btn-small btn-red" id="clearBtn" type="button">Tüm Kayıtları Sil</button></div></section>
</main><div class="bottom-actions"><div class="bottom-panel"><button type="button" class="btn btn-blue" id="importBtn">⬆ Excel İçeri Aktar</button><button type="button" class="btn btn-green" id="exportBtn">⬇ Excel Dışarı Aktar</button><button type="button" class="btn" id="printBtn">▣ PDF / Yazdır</button><input type="file" class="sr-only" id="fileIn" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"></div></div>
<footer class="footer">Temel özelliği: Proses tasarım amacından sapmaları analiz eder &nbsp;&nbsp;&nbsp; Kullanım alanı: Kimya, enerji, petrol ve gaz tesisleri &nbsp;&nbsp;&nbsp; HAZOP: Parametre × Kılavuz Kelime → Sapma &nbsp;&nbsp;&nbsp; Yardımcı risk: Olasılık (1–5) × Şiddet (1–5)</footer>
</div>
<table id="printPages" role="presentation"><thead><tr><td><div id="printHeader"></div></td></tr></thead><tbody><tr><td><div id="printContent"></div></td></tr></tbody></table><div id="printPageFrame"></div><div id="toast" class="toast" role="status" aria-live="polite"></div>
<script>'use strict';
(()=>{
const $=id=>document.getElementById(id);
const asText=v=>v==null?'':String(v),esc=v=>asText(v).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const today=()=>{const d=new Date();return [d.getFullYear(),String(d.getMonth()+1).padStart(2,'0'),String(d.getDate()).padStart(2,'0')].join('-')};
const uid=()=>('HZ'+Date.now().toString(36)+Math.random().toString(36).slice(2,7)).toUpperCase();
const keys=['company','facility','unit','pid','date','leader','team','revision','phase','scope'];
const initMeta=()=>Object.fromEntries(keys.map(k=>[k,'']));
const grades=[{key:'low',min:1,max:4,name:'Kabul Edilebilir',color:'#82d8a0'},{key:'medium',min:5,max:9,name:'Kontrol Gerekli',color:'#efd47c'},{key:'high',min:10,max:15,name:'Yüksek',color:'#f3ae6b'},{key:'critical',min:16,max:25,name:'Çok Yüksek',color:'#eb8991'}];
const statusText={open:'Açık',progress:'Devam Ediyor',done:'Tamamlandı'};
const guideNames={NO:'Yok / Hiç',MORE:'Fazla',LESS:'Az',REVERSE:'Tersine',AS_WELL_AS:'Yanı Sıra',PART_OF:'Bir Bölümü',OTHER_THAN:'Başka / Farklı',EARLY:'Erken',LATE:'Geç',BEFORE:'Önce',AFTER:'Sonra'};
const factorKeys=['node','tag','normal','intent','parameter','guide','deviation','causes','consequences','safeguards','verification','existingNotes','action','owner','due','status','evidence'];
let data={meta:initMeta(),records:[]},currentEdit=null,mode='single',lastSuggestion='',timeoutMsg;
const BRAND_KEY='risk-ui-hazop-brand-v1',BRAND_DEFAULT={title:'İSG RİSK ANALİZİ – HAZOP',subtitle:'Tehlike ve İşletilebilirlik Analizi · Proses Sapmaları ve Aksiyon Takip Formu',org:'KURUM / İŞLETME',logo:''};
let brand={...BRAND_DEFAULT};
function toast(msg){clearTimeout(timeoutMsg);$('toast').textContent=msg;$('toast').classList.add('on');timeoutMsg=setTimeout(()=>$('toast').classList.remove('on'),3700)}
function saveBrand(){try{localStorage.setItem(BRAND_KEY,JSON.stringify(brand))}catch(e){toast('Başlık tercihleri kaydedilemedi.')}}
function loadBrand(){try{let v=JSON.parse(localStorage.getItem(BRAND_KEY)||'null');if(v&&typeof v==='object')for(let k of Object.keys(BRAND_DEFAULT))if(typeof v[k]==='string')brand[k]=v[k]}catch(e){}
 $('brandTitle').textContent=brand.title;$('brandSubtitle').textContent=brand.subtitle;$('brandOrg').textContent=brand.org;setLogo(brand.logo);
 bindFive($('logoTrigger'),()=>$('logoFile').click());for(let [id,key] of [['brandTitle','title'],['brandSubtitle','subtitle'],['brandOrg','org']])bindFive($(id),()=>editBrand($(id),key));
 $('logoFile').addEventListener('change',e=>{let f=e.target.files?.[0];if(!f)return;if(!/^image\//.test(f.type)||f.size>1500000){alert('1,5 MB altında PNG / JPEG / WebP / GIF / SVG görsel seçin.');e.target.value='';return}let rd=new FileReader();rd.onload=()=>{brand.logo=String(rd.result||'');setLogo(brand.logo);saveBrand();toast('Kurum logosu kaydedildi.')};rd.readAsDataURL(f);e.target.value=''})}
function setLogo(v){const img=$('brandLogo'),alt=$('logoFallback');img.onload=()=>{img.hidden=false;alt.hidden=true};img.onerror=()=>{img.hidden=true;alt.hidden=false};img.src=v&&/^data:image\//.test(v)?v:'logo.jpg'}
function bindFive(el,cb){let clicks=0,last=0;el.addEventListener('click',()=>{if(el.isContentEditable)return;let now=Date.now();clicks=now-last>1100?1:clicks+1;last=now;if(clicks>=5){clicks=0;cb()}})}
function editBrand(el,key){el.contentEditable='true';el.focus();let range=document.createRange(),sel=window.getSelection();range.selectNodeContents(el);sel.removeAllRanges();sel.addRange(range);let done=false;function save(){if(done)return;done=true;el.contentEditable='false';brand[key]=el.textContent.trim()||BRAND_DEFAULT[key];el.textContent=brand[key];saveBrand();el.removeEventListener('blur',save);el.removeEventListener('keydown',keyDown)}function keyDown(e){if(e.key==='Enter'){e.preventDefault();el.blur()}if(e.key==='Escape'){e.preventDefault();el.textContent=brand[key];el.blur()}}el.addEventListener('blur',save);el.addEventListener('keydown',keyDown)}
function factor(obj){if(!obj||obj.l==null||obj.s==null||obj.l===''||obj.s==='')return null;let l=Number(obj.l),s=Number(obj.s);return Number.isInteger(l)&&l>=1&&l<=5&&Number.isInteger(s)&&s>=1&&s<=5?{l,s}:null}
function grade(score){return score==null?null:grades.find(g=>score>=g.min&&score<=g.max)||null}
function risk(f){const v=factor(f);return v?v.l*v.s:null}
function normalizeRecord(o){if(!o||typeof o!=='object'||Array.isArray(o))return null;let before=factor(o.before),after=factor(o.after);if(!before)return null;const afterInvalid=o.after&&((o.after.l!==''&&o.after.l!=null)||(o.after.s!==''&&o.after.s!=null))&&!after;if(afterInvalid)return null;
 const r={id:asText(o.id)||uid(),method:'HAZOP',created:asText(o.created)||today()};for(let k of factorKeys)r[k]=asText(o[k]);r.status=statusText[r.status]?r.status:'open';r.verified=Boolean(o.verified)&&r.status==='done'&&!!after;r.before=before;r.after=after;return r}
function scoreView(prefix,raw){let n=risk(raw),g=grade(n);$(prefix+'Score').textContent=n??'—';let elem=$(prefix+'Level');elem.textContent=g?g.name:prefix==='after'?'Hedef girilmedi':'Değerlendirilmedi';elem.className='riskpill '+(g?'risk-'+g.key:'')}
function beforeAfter(){let bf={l:$('beforeL').value,s:$('beforeS').value},af={l:$('afterL').value,s:$('afterS').value};return {before:factor(bf),after:factor(af),rawBefore:bf,rawAfter:af}}
function updateScore(){let f=beforeAfter();scoreView('before',f.before);scoreView('after',f.after);drawChart()}
function suggested(){let p=$('parameter').value,g=$('guide').value;if(!p||!g)return '';const suffix={NO:'yok / tamamen kayıp',MORE:'normalin üzerinde / fazla',LESS:'normalin altında / az',REVERSE:'ters yönde',AS_WELL_AS:'ek / istenmeyen bileşenle birlikte',PART_OF:'eksik / kısmi',OTHER_THAN:'beklenenden farklı',EARLY:'beklenenden erken',LATE:'beklenenden geç',BEFORE:'öngörülen sıradan önce',AFTER:'öngörülen sıradan sonra'};return p+' '+suffix[g]}
function updateSuggestion(){let guess=suggested(),input=$('deviation');if(!input.value.trim()||input.value===lastSuggestion)input.value=guess;lastSuggestion=guess}
function renderMeta(){for(let el of document.querySelectorAll('[data-meta]'))el.value=data.meta[el.dataset.meta]||''}
function readMeta(){for(let el of document.querySelectorAll('[data-meta]'))data.meta[el.dataset.meta]=el.value}
function clearForm(){currentEdit=null;$('recordForm').reset();$('status').value='open';lastSuggestion='';$('formMode').textContent='Yeni HAZOP Sapma Kaydı';$('saveBtn').textContent='+ Sapma Kaydını Ekle';$('modeInfo').textContent='Yeni HAZOP kaydı oluşturuluyor.';updateScore()}
function fromForm(){const t=k=>$(k).value.trim(),f=beforeAfter();return normalizeRecord({id:currentEdit||uid(),created:currentEdit?(data.records.find(r=>r.id===currentEdit)?.created||today()):today(),...Object.fromEntries(factorKeys.map(k=>[k,t(k)])),before:f.rawBefore,after:f.rawAfter,verified:$('verified').checked})}
function validateForm(){let required=['node','intent','parameter','guide','deviation','causes','consequences','action'];for(let id of required)if(!$(id).value.trim()){alert('Lütfen zorunlu alanı doldurun: '+document.querySelector('label[for="'+id+'"]')?.textContent);$(id).focus();return false}let f=beforeAfter();if(!f.before){alert('Mevcut Olasılık ve Şiddet değerlerini 1–5 arasında seçin.');return false}let afterOne=Boolean(f.rawAfter.l)||Boolean(f.rawAfter.s);if(afterOne&&!f.after){alert('Hedef risk için Olasılık ve Şiddet değerlerinin ikisini de seçin.');return false}if($('verified').checked&&($('status').value!=='done'||!f.after)){alert('Önlem ancak aksiyon tamamlanıp hedef risk puanlandığında doğrulanabilir.');return false}return true}
function startEdit(id){let r=data.records.find(v=>v.id===id);if(!r)return;currentEdit=id;for(let k of factorKeys)$(k).value=r[k]||'';$('beforeL').value=r.before.l;$('beforeS').value=r.before.s;$('afterL').value=r.after?.l||'';$('afterS').value=r.after?.s||'';$('verified').checked=r.verified;lastSuggestion='';$('formMode').textContent='HAZOP Kaydını Düzenle';$('saveBtn').textContent='✓ Değişiklikleri Kaydet';$('modeInfo').textContent='Seçili HAZOP kaydı düzenleniyor.';mode='single';updateScore();document.querySelector('.card-h h2 #formMode').scrollIntoView({block:'start',behavior:'smooth'})}
function duplicateRecord(id){const r=data.records.find(x=>x.id===id);if(!r)return;data.records.push({...r,id:uid(),created:today(),status:'open',verified:false,before:{...r.before},after:r.after?{...r.after}:null});renderTable();toast('HAZOP kaydı kopyalandı.')}
function deleteRecord(id){if(!confirm('Bu HAZOP sapma kaydı silinecek. Devam edilsin mi?'))return;data.records=data.records.filter(r=>r.id!==id);if(currentEdit===id)clearForm();renderTable();toast('Kayıt silindi.')}
function pill(n){const g=grade(n);return g?'<span class="riskpill risk-'+g.key+'">'+g.name+'</span>':'—'}
function renderTable(){let q=$('search').value.trim().toLocaleLowerCase('tr-TR'),filter=$('filter').value;let todayStr=today();$('total').textContent=data.records.length;$('priority').textContent=data.records.filter(r=>!r.verified&&risk(r.before)>=10).length;$('overdue').textContent=data.records.filter(r=>!r.verified&&r.due&&r.due<todayStr).length;$('verifiedCount').textContent=data.records.filter(r=>r.verified).length;
 const showing=data.records.filter(r=>(!filter||r.status===filter)&&(!q||factorKeys.some(k=>asText(r[k]).toLocaleLowerCase('tr-TR').includes(q))));$('recordCount').textContent='('+showing.length+'/'+data.records.length+')';$('empty').hidden=showing.length>0;$('rows').innerHTML=showing.map((r,i)=>`<tr><td>${i+1}</td><td><b>${esc(r.node)}</b><div class="cellsub">${esc(r.tag)}</div></td><td><b>${esc(r.parameter)}</b><div class="cellsub">${esc(guideNames[r.guide]||r.guide)}</div></td><td>${esc(r.deviation)}<div class="cellsub">${esc(r.causes)}</div></td><td>${esc(r.consequences)}<div class="cellsub">${esc(r.safeguards)}</div></td><td><b>${risk(r.before)}</b> · ${pill(risk(r.before))}<div class="cellsub">${r.before.l} × ${r.before.s}</div></td><td>${risk(r.after)??'—'} · ${pill(risk(r.after))}</td><td>${esc(r.action)}</td><td>${esc(r.owner||'—')}<div class="cellsub">${esc(r.due||'—')}</div></td><td>${esc(statusText[r.status])}${r.verified?'<div class="cellsub">Doğrulandı</div>':''}</td><td><div style="display:grid;gap:4px"><button class="btn btn-small" type="button" data-act="edit" data-id="${esc(r.id)}">Düzenle</button><button class="btn btn-small" type="button" data-act="copy" data-id="${esc(r.id)}">Kopyala</button><button class="btn btn-small btn-red" type="button" data-act="delete" data-id="${esc(r.id)}">Sil</button></div></td></tr>`).join('');drawChart()}
function backgroundFor(n){const g=grade(n);return g?.color||'#eef2f6'}
function drawChart(){let f=beforeAfter(),bn=risk(f.before),an=risk(f.after);$('singleMode').classList.toggle('active',mode==='single');$('distMode').classList.toggle('active',mode==='dist');if(mode==='single'){
 let cells='<div class="axis">Ş \\ O</div>';for(let l=1;l<=5;l++)cells+='<div class="axis">'+l+'</div>';for(let s=5;s>=1;s--){cells+='<div class="axis">'+s+'</div>';for(let l=1;l<=5;l++){const r=l*s;let marker=(f.before&&f.before.l===l&&f.before.s===s?' is-before':'')+(f.after&&f.after.l===l&&f.after.s===s?' is-after':'');cells+=`<div class="risk-cell${marker}" style="background:${backgroundFor(r)}" title="Olasılık ${l} × Şiddet ${s} = ${r}">${r}</div>`}}
 $('chart').innerHTML='<div class="matrix">'+cells+'</div>';$('riskSummary').textContent=bn!=null?'Mevcut '+bn+' puan ('+grade(bn).name+')'+(an!=null?' → Hedef '+an+' puan ('+grade(an).name+')':'')+'. Şiddet 5 olan sapmalar puandan bağımsız ayrıca incelenmelidir.':'Mevcut puanları seçtiğinizde risk matrisinde işaretlenir.';
 }else{let counts=grades.map(()=>0);for(let r of data.records){let g=grade(risk(r.before));if(g)counts[grades.indexOf(g)]++}let max=Math.max(1,...counts);$('chart').innerHTML=grades.map((g,i)=>`<div class="barrow"><span class="barlabel">${g.name}</span><span class="bar"><span class="barfill" style="display:block;width:${counts[i]/max*100}%;background:${g.color}"></span></span><span class="barvalue">${counts[i]} kayıt</span></div>`).join('');$('riskSummary').textContent='Genel dağılım: '+data.records.length+' sapma kaydı. Çubuklar mevcut riskleri temsil eder.'}}
function resetOnOpen(){data={meta:initMeta(),records:[]};renderMeta();clearForm();$('search').value='';$('filter').value='';$('fileIn').value='';renderTable()}
function bind(){document.querySelectorAll('[data-meta]').forEach(el=>el.addEventListener('input',readMeta));for(let id of ['beforeL','beforeS','afterL','afterS'])$(id).addEventListener('change',updateScore);for(let id of ['guide','parameter'])$(id).addEventListener('change',updateSuggestion);
 $('singleMode').addEventListener('click',()=>{mode='single';drawChart()});$('distMode').addEventListener('click',()=>{mode='dist';drawChart()});
 $('recordForm').addEventListener('submit',e=>{e.preventDefault();if(!validateForm())return;const r=fromForm();if(!r)return alert('Giriş değerleri doğrulanamadı.');if(currentEdit){let i=data.records.findIndex(x=>x.id===currentEdit);if(i>=0)data.records[i]=r}else data.records.push(r);const msg=currentEdit?'HAZOP kaydı güncellendi.':'Yeni HAZOP sapması eklendi.';clearForm();renderTable();mode='dist';drawChart();toast(msg)});
 for(let id of ['newRecordTop','cancelBtn'])$(id).addEventListener('click',()=>{clearForm();mode='single';drawChart()});
 $('rows').addEventListener('click',e=>{let b=e.target.closest('[data-act][data-id]');if(!b)return;if(b.dataset.act==='edit')startEdit(b.dataset.id);if(b.dataset.act==='copy')duplicateRecord(b.dataset.id);if(b.dataset.act==='delete')deleteRecord(b.dataset.id)});
 for(let id of ['search','filter'])$(id).addEventListener('input',renderTable);
 $('clearBtn').addEventListener('click',()=>{if(!data.records.length)return toast('Silinecek kayıt bulunmuyor.');if(!confirm(data.records.length+' kaydın tamamı silinecek. Önce Excel yedeği alın. Devam edilsin mi?'))return;data.records=[];clearForm();renderTable();toast('Tüm kayıtlar silindi.')});
 $('importBtn').addEventListener('click',()=>$('fileIn').click());$('fileIn').addEventListener('change',e=>importExcel(e.target.files?.[0]));$('exportBtn').addEventListener('click',exportExcel);
 $('printBtn').addEventListener('click',printNow);window.addEventListener('beforeprint',buildPrint)
}

function xmlEsc(v){return asText(v).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&apos;'}[c])).replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g,'')}
function colLetter(num){let res='';for(let n=num;n>0;n=Math.floor((n-1)/26))res=String.fromCharCode(65+(n-1)%26)+res;return res}
function sheetXml(rows,{width=18,autoFilter=true}={}){
 const maxCols=Math.max(1,...rows.map(r=>r.length));const cols=Array.from({length:maxCols},(_,i)=>{let w=width;if(i===0)w=17;if(i===5||i===6||i===8||i===9||i===26)w=42;if(i===3||i===4||i===10)w=24;return `<col min="${i+1}" max="${i+1}" width="${w}" customWidth="1"/>`}).join('');
 const sheetRows=rows.map((row,i)=>`<row r="${i+1}">${row.map((value,j)=>{const c=colLetter(j+1)+(i+1),st=i===0?' s="1"':'';if(typeof value==='number'&&Number.isFinite(value))return `<c r="${c}"${st}><v>${value}</v></c>`;return `<c r="${c}" t="inlineStr"${st}><is><t xml:space="preserve">${xmlEsc(value)}</t></is></c>`}).join('')}</row>`).join('');
 return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetViews><sheetView workbookViewId="0"><pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews><sheetFormatPr defaultRowHeight="16"/><cols>${cols}</cols><sheetData>${sheetRows}</sheetData>${autoFilter?`<autoFilter ref="A1:${colLetter(maxCols)}${Math.max(1,rows.length)}"/>`:''}</worksheet>`
}
const CRC_TABLE=(()=>{const result=[];for(let i=0;i<256;i++){let x=i;for(let j=0;j<8;j++)x=(x&1)?(0xedb88320^(x>>>1)):(x>>>1);result[i]=x>>>0}return result})();
function crc32(data){let crc=0xffffffff;for(const byte of data)crc=CRC_TABLE[(crc^byte)&255]^(crc>>>8);return (crc^0xffffffff)>>>0}
function zipCreate(entries){
 const enc=new TextEncoder(),pieces=[],centers=[];let offset=0;
 const u16=(view,pos,val)=>view.setUint16(pos,val,true),u32=(view,pos,val)=>view.setUint32(pos,val>>>0,true);
 function append(piece){pieces.push(piece);offset+=piece.length}
 for(const [filename,text] of entries){const name=enc.encode(filename),data=enc.encode(text),crc=crc32(data),local=new Uint8Array(30+name.length),dv=new DataView(local.buffer),begin=offset;
  u32(dv,0,0x04034b50);u16(dv,4,20);u16(dv,6,0x0800);u16(dv,8,0);u16(dv,10,0);u16(dv,12,0);u32(dv,14,crc);u32(dv,18,data.length);u32(dv,22,data.length);u16(dv,26,name.length);u16(dv,28,0);local.set(name,30);append(local);append(data);
  const cent=new Uint8Array(46+name.length),cv=new DataView(cent.buffer);u32(cv,0,0x02014b50);u16(cv,4,20);u16(cv,6,20);u16(cv,8,0x0800);u16(cv,10,0);u16(cv,12,0);u16(cv,14,0);u32(cv,16,crc);u32(cv,20,data.length);u32(cv,24,data.length);u16(cv,28,name.length);u16(cv,30,0);u16(cv,32,0);u16(cv,34,0);u16(cv,36,0);u32(cv,38,0);u32(cv,42,begin);cent.set(name,46);centers.push(cent)
 }
 const start=offset;for(const c of centers)append(c);const end=new Uint8Array(22),e=new DataView(end.buffer);u32(e,0,0x06054b50);u16(e,4,0);u16(e,6,0);u16(e,8,centers.length);u16(e,10,centers.length);u32(e,12,offset-start);u32(e,16,start);u16(e,20,0);append(end);return new Blob(pieces,{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})
}
function workbookFiles(rows,summary){const types=`<?xml version="1.0" encoding="UTF-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/></Types>`;
 const rels=`<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`;
 const workbook=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="Risk Kayıtları" sheetId="1" r:id="rId1"/><sheet name="Rapor Bilgileri" sheetId="2" r:id="rId2"/></sheets></workbook>`;
 const wbRels=`<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>`;
 const styles=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="2"><font><sz val="10"/><name val="Calibri"/></font><font><b/><sz val="10"/><color rgb="FFFFFFFF"/><name val="Calibri"/></font></fonts><fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF0369A1"/><bgColor indexed="64"/></patternFill></fill></fills><borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>`;
 return [['[Content_Types].xml',types],['_rels/.rels',rels],['xl/workbook.xml',workbook],['xl/_rels/workbook.xml.rels',wbRels],['xl/styles.xml',styles],['xl/worksheets/sheet1.xml',sheetXml(rows)],['xl/worksheets/sheet2.xml',sheetXml(summary,{width:36,autoFilter:false})]]}


async function unzipEntries(arrayBuffer){const bytes=new Uint8Array(arrayBuffer),view=new DataView(arrayBuffer);let end=-1;for(let i=bytes.length-22;i>=Math.max(0,bytes.length-65557);i--)if(view.getUint32(i,true)===0x06054b50){end=i;break}if(end<0)throw new Error('Geçerli Excel ZIP yapısı bulunamadı.');const count=view.getUint16(end+10,true),base=view.getUint32(end+16,true),decoder=new TextDecoder(),entries={};let cursor=base;
 for(let i=0;i<count;i++){if(view.getUint32(cursor,true)!==0x02014b50)throw new Error('Excel dosyası merkezi dizini okunamadı.');const method=view.getUint16(cursor+10,true),compressed=view.getUint32(cursor+20,true),nameLength=view.getUint16(cursor+28,true),extra=view.getUint16(cursor+30,true),comment=view.getUint16(cursor+32,true),localOffset=view.getUint32(cursor+42,true),name=decoder.decode(bytes.subarray(cursor+46,cursor+46+nameLength));if(view.getUint32(localOffset,true)!==0x04034b50)throw new Error('Excel dosyası veri bölümü bozuk.');const start=localOffset+30+view.getUint16(localOffset+26,true)+view.getUint16(localOffset+28,true);const content=bytes.slice(start,start+compressed);if(method===0)entries[name]=decoder.decode(content);else if(method===8){if(typeof DecompressionStream!=='function')throw new Error('Bu tarayıcı sıkıştırılmış Excel içeri aktarmayı desteklemiyor. Güncel bir Chrome veya Edge tarayıcısı deneyin.');try{const stream=new Blob([content]).stream().pipeThrough(new DecompressionStream('deflate-raw'));entries[name]=decoder.decode(await new Response(stream).arrayBuffer())}catch(e){throw new Error('Excel sıkıştırması açılamadı: '+e.message)}}else throw new Error('Excel dosyasında desteklenmeyen sıkıştırma türü.');cursor+=46+nameLength+extra+comment;
 }return entries}
function parseXml(source){const doc=new DOMParser().parseFromString(source,'application/xml');if(doc.querySelector('parsererror'))throw new Error('Excel XML içeriği okunamadı.');return doc}
function parseXlsxSheet(source,shared=[]){if(!source)return [];const doc=parseXml(source),rows=[];for(const row of doc.getElementsByTagNameNS('*','row')){const cells=[];for(const c of row.getElementsByTagNameNS('*','c')){const ref=c.getAttribute('r')||'';let index=0;for(const letter of (ref.match(/^[A-Z]+/)||[''])[0])index=index*26+letter.charCodeAt(0)-64;index=Math.max(0,index-1);const type=c.getAttribute('t');let value='';if(type==='inlineStr')value=Array.from(c.getElementsByTagNameNS('*','t')).map(n=>n.textContent).join('');else{value=c.getElementsByTagNameNS('*','v')[0]?.textContent||'';if(type==='s')value=shared[Number(value)]||''}cells[index]=value}rows.push(cells)}return rows}

function normalizeDate(value){const str=asText(value);if(/^\d{4}-\d{2}-\d{2}$/.test(str))return str;if(/^\d{2}\.\d{2}\.\d{4}$/.test(str)){const a=str.split('.');return a[2]+'-'+a[1]+'-'+a[0]}const n=Number(value);if(Number.isFinite(n)&&n>=1&&n<100000)return new Date(Date.UTC(1899,11,30)+Math.floor(n)*86400000).toISOString().slice(0,10);return ''}

/* Excel gerçek .xlsx biçiminde, kütüphane/CDN olmadan oluşturulur. */


const COLS=['Kayıt ID','HAZOP Düğümü','Ekipman / Tag','Tasarım Amacı','Parametre','Kılavuz Kelime','Sapma','Normal Değer','Olası Nedenler','Olası Sonuçlar','Mevcut Koruyucular','Koruma Test / Kanıtı','İşletilebilirlik Notu','Mevcut Olasılık','Mevcut Şiddet','Mevcut Risk','Mevcut Risk Grubu','Önerilen Aksiyon','Hedef Olasılık','Hedef Şiddet','Hedef Risk','Hedef Risk Grubu','Sorumlu','Termin','Aksiyon Durumu','Doğrulama','Uygulama Kanıtı / Not','Kayıt Tarihi'];
const metaLabels={company:'İşyeri / Firma',facility:'Tesis / İşletme',unit:'Proses / Ünite',pid:'P&ID / Doküman No.',date:'Değerlendirme Tarihi',leader:'HAZOP Lideri',team:'Çalışma Ekibi',revision:'Revizyon',phase:'İşletme Aşaması',scope:'Kapsam / Referanslar'};
function recordToRow(r){return [r.id,r.node,r.tag,r.intent,r.parameter,r.guide,r.deviation,r.normal,r.causes,r.consequences,r.safeguards,r.verification,r.existingNotes,r.before.l,r.before.s,risk(r.before),grade(risk(r.before))?.name||'',r.action,r.after?.l??'',r.after?.s??'',risk(r.after)??'',grade(risk(r.after))?.name||'',r.owner,r.due,statusText[r.status],r.verified?'Evet':'Hayır',r.evidence,r.created]}
function downloadFile(blob,name){const url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download=name;document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),5000)}
function exportExcel(){try{readMeta();const rows=[COLS,...data.records.map(recordToRow)];const summary=[['Alan','Değer'],['Yöntem','HAZOP'],...keys.map(k=>[metaLabels[k],data.meta[k]]),['Toplam Sapma',data.records.length],['Yüksek / Çok Yüksek · Açık',data.records.filter(r=>!r.verified&&risk(r.before)>=10).length],['Doğrulanan',data.records.filter(r=>r.verified).length],['Yardımcı Puan','Olasılık × Şiddet (1–5)'],['Uyarı','HAZOP kılavuz kelime incelemesi esas olup risk matrisi yardımcıdır.']];downloadFile(zipCreate(workbookFiles(rows,summary)),'ISG_HAZOP_Risk_Analizi_'+(data.meta.date||today())+'.xlsx');toast(data.records.length+' sapma kaydı Excel olarak dışarı aktarıldı.')}catch(e){console.error(e);alert('Excel dışarı aktarma hatası: '+e.message)}}
function fromRow(row,idx){const get=k=>row[idx.get(k)]??'';const statusRaw=get('Aksiyon Durumu'),status=Object.keys(statusText).find(k=>statusText[k]===statusRaw)||(statusText[statusRaw]?statusRaw:'open');return normalizeRecord({id:get('Kayıt ID'),method:'HAZOP',node:get('HAZOP Düğümü'),tag:get('Ekipman / Tag'),intent:get('Tasarım Amacı'),parameter:get('Parametre'),guide:get('Kılavuz Kelime'),deviation:get('Sapma'),normal:get('Normal Değer'),causes:get('Olası Nedenler'),consequences:get('Olası Sonuçlar'),safeguards:get('Mevcut Koruyucular'),verification:get('Koruma Test / Kanıtı'),existingNotes:get('İşletilebilirlik Notu'),action:get('Önerilen Aksiyon'),before:{l:get('Mevcut Olasılık'),s:get('Mevcut Şiddet')},after:{l:get('Hedef Olasılık'),s:get('Hedef Şiddet')},owner:get('Sorumlu'),due:normalizeDate(get('Termin')),status,verified:get('Doğrulama')==='Evet',evidence:get('Uygulama Kanıtı / Not'),created:normalizeDate(get('Kayıt Tarihi'))||today()})}
async function readExcel(file){if(!/\.xlsx$/i.test(file.name))throw Error('Yalnızca .xlsx dosyaları seçin.');if(file.size>15*1024*1024)throw Error('Excel dosyası 15 MB sınırını aşıyor.');const entries=await unzipEntries(await file.arrayBuffer()),strings=[];if(entries['xl/sharedStrings.xml']){let doc=parseXml(entries['xl/sharedStrings.xml']);for(let item of doc.getElementsByTagNameNS('*','si'))strings.push(Array.from(item.getElementsByTagNameNS('*','t')).map(x=>x.textContent).join(''))}
 let rows=parseXlsxSheet(entries['xl/worksheets/sheet1.xml'],strings);if(!rows.length)throw Error('HAZOP kayıt sayfası boş veya bulunamadı.');let idx=new Map(rows[0].map((v,i)=>[asText(v).trim(),i]));let needed=['Kayıt ID','HAZOP Düğümü','Parametre','Kılavuz Kelime','Sapma','Olası Nedenler','Olası Sonuçlar','Mevcut Olasılık','Mevcut Şiddet'];if(needed.some(x=>!idx.has(x)))throw Error('Excel dosyası HAZOP şablonuna uygun değil.');let nonempty=rows.slice(1).filter(r=>r.some(c=>asText(c).trim()));if(nonempty.length>10000)throw Error('Bir dosyada en fazla 10.000 sapma kaydı desteklenir.');let records=nonempty.map(r=>fromRow(r,idx));if(records.some(r=>!r))throw Error('Geçersiz Olasılık/Şiddet puanları veya eksik hedef faktörleri bulundu.');let seen=new Set();for(let r of records){if(seen.has(r.id))r.id=uid();seen.add(r.id)}
 let info=parseXlsxSheet(entries['xl/worksheets/sheet2.xml'],strings),lookup=new Map(info.slice(1).map(r=>[r[0],r[1]])),meta=initMeta();for(let k of keys)if(lookup.has(metaLabels[k]))meta[k]=asText(lookup.get(metaLabels[k]));return {records,meta}}
async function importExcel(file){if(!file)return;try{let result=await readExcel(file);if(data.records.length&&!confirm('Mevcut '+data.records.length+' HAZOP kaydı yerine '+result.records.length+' Excel kaydı yüklenecek. Önce yedek alın. Devam edilsin mi?'))return;data=result;renderMeta();clearForm();renderTable();mode='dist';drawChart();toast(result.records.length+' HAZOP kaydı içeri alındı.')}catch(e){console.error(e);alert('Excel içeri aktarma başarısız: '+e.message)}finally{$('fileIn').value=''}}


function printField(label,v){return `<div class="p-field"><b>${esc(label)}</b>${esc(v==null||v===''?'—':v)}</div>`}
function chartRiskSvg(){let colors=grades.map(g=>g.color),w=490,h=145;let counts=grades.map(()=>0);for(let r of data.records){let g=grade(risk(r.before));if(g)counts[grades.indexOf(g)]++}let max=Math.max(1,...counts);let rows=grades.map((g,i)=>{let y=8+i*33,bw=counts[i]/max*248;return `<text x="6" y="${y+17}" font-size="11" fill="#34495d">${g.name}</text><rect x="148" y="${y+3}" width="250" height="20" fill="#eef3f7"/><rect x="149" y="${y+4}" width="${bw}" height="18" fill="${g.color}"/><text x="412" y="${y+17}" font-size="11" fill="#34495d">${counts[i]} kayıt</text>`}).join('');return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${w} ${h}">${rows}</svg>`}
function chartMatrixSvg(){let width=490,height=145,cell=25,ox=185,oy=8;let rows='';let f=beforeAfter(),markB=f.before,markA=f.after;if(!markB&&data.records.length){markB=data.records[0].before;markA=data.records[0].after}for(let s=5;s>=1;s--)for(let l=1;l<=5;l++){let n=l*s,x=ox+(l-1)*cell,y=oy+(5-s)*cell,isB=markB?.l===l&&markB?.s===s,isA=markA?.l===l&&markA?.s===s;rows+=`<rect x="${x}" y="${y}" width="${cell-2}" height="${cell-2}" rx="2" fill="${backgroundFor(n)}" ${isB?'stroke="#125ba9" stroke-width="2.8"':''}/><text x="${x+11.5}" y="${y+16}" text-anchor="middle" font-size="11" fill="#354b58">${n}</text>${isA?`<rect x="${x+3}" y="${y+3}" width="${cell-8}" height="${cell-8}" fill="none" stroke="#854fb9" stroke-width="2"/>`:''}`}
let axes='';for(let i=1;i<=5;i++){axes+=`<text x="${ox+(i-1)*cell+12}" y="143" text-anchor="middle" font-size="10">${i}</text><text x="${ox-8}" y="${oy+(5-i)*cell+16}" text-anchor="end" font-size="10">${i}</text>`}return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}"><text x="6" y="20" font-size="11" fill="#34556a">Yatay: Olasılık (1–5)</text><text x="6" y="35" font-size="11" fill="#34556a">Dikey: Şiddet (1–5)</text><text x="6" y="54" font-size="10" fill="#145fa0">Mavi: Mevcut</text><text x="6" y="69" font-size="10" fill="#854fb9">Mor: Hedef</text>${rows}${axes}</svg>`}
function printLogoSrc(){let img=$('brandLogo');if(img&&!img.hidden&&img.complete&&img.naturalWidth)return img.dataset.printData||(brand.logo&&/^data:image\//.test(brand.logo)?brand.logo:(img.currentSrc||img.src));return ''}
async function readyPrintLogo(){let img=$('brandLogo');if(!img||img.hidden)return;try{if(!img.complete||!img.naturalWidth)await Promise.race([img.decode(),new Promise(resolve=>setTimeout(resolve,800))]);if(img.complete&&img.naturalWidth){let canvas=document.createElement('canvas'),ratio=Math.min(1,512/Math.max(img.naturalWidth,img.naturalHeight));canvas.width=Math.max(1,Math.round(img.naturalWidth*ratio));canvas.height=Math.max(1,Math.round(img.naturalHeight*ratio));canvas.getContext('2d').drawImage(img,0,0,canvas.width,canvas.height);img.dataset.printData=canvas.toDataURL('image/png')}}catch(e){/* Dosya şeması resmi canvas'a aktarılmayabilir; doğrudan kaynağa dön. */}}
function buildPrint(){readMeta();let src=printLogoSrc(),logo=src?`<img src="${esc(src)}" alt="Kurum logosu">`:'<span style="color:#70879c">LOGO</span>'; $('printHeader').innerHTML=`<div class="p-header">${logo}<div><h1>${esc(brand.title)}</h1><p>${esc(brand.subtitle)}</p></div><span class="p-org">${esc(brand.org)}</span></div>`;
let overview='<div class="p-section"><h3>01 · Çalışma Bilgileri ve Kapsam</h3><div class="p-grid">'+keys.map(k=>printField(metaLabels[k],data.meta[k])).join('')+'</div></div>';
let graphs=`<div class="p-section" style="break-inside:avoid"><h3>02 · HAZOP Risk Görünümü (Yardımcı Risk Puanlaması)</h3><div class="p-charts"><div class="p-chart"><h4>5 × 5 Risk Matrisi</h4>${chartMatrixSvg()}</div><div class="p-chart"><h4>Genel Sapma Risk Dağılımı</h4>${chartRiskSvg()}</div></div><div class="p-note">Risk aralıkları yalnızca örnektir: 1–4 Kabul Edilebilir, 5–9 Kontrol Gerekli, 10–15 Yüksek, 16–25 Çok Yüksek. HAZOP'un asli incelemesi parametre-kılavuz sözcük sapma analizi olup puanlama yardımcıdır.</div></div>`;
let tab=data.records.map((r,i)=>`<tr><td>${i+1}</td><td>${esc(r.node)}</td><td>${esc(r.parameter)} / ${esc(guideNames[r.guide]||r.guide)}</td><td>${esc(r.deviation)}</td><td>${esc(r.consequences)}</td><td>${r.before.l}×${r.before.s}=${risk(r.before)}</td><td>${r.after?`${r.after.l}×${r.after.s}=${risk(r.after)}`:'—'}</td><td>${esc(statusText[r.status])}</td></tr>`).join('');
let table=`<div class="p-section"><h3>03 · Sapma Kayıt Özeti (${data.records.length})</h3>${data.records.length?`<table class="p-table"><colgroup><col style="width:4%"><col style="width:16%"><col style="width:17%"><col style="width:20%"><col style="width:21%"><col style="width:8%"><col style="width:8%"><col style="width:6%"></colgroup><thead><tr><th>No</th><th>Düğüm</th><th>Parametre / Kılavuz Sözcük</th><th>Sapma</th><th>Sonuç</th><th>Mevcut</th><th>Hedef</th><th>Durum</th></tr></thead><tbody>${tab}</tbody></table>`:'<div class="p-note">Henüz sapma kaydı yoktur.</div>'}</div>`;
let details='<div class="p-section"><h3>04 · HAZOP Düğüm / Sapma Ayrıntıları</h3>'+data.records.map((r,i)=>{let f=[['Düğüm / Tag',r.node+' / '+r.tag],['Tasarım Amacı',r.intent],['Normal Çalışma Değeri',r.normal],['Parametre + Kılavuz Sözcük',r.parameter+' + '+(guideNames[r.guide]||r.guide)],['Oluşabilecek Sapma',r.deviation],['Olası Nedenler',r.causes],['Olası Sonuçlar',r.consequences],['Mevcut Koruyucular',r.safeguards],['Koruma Test / Kanıtı',r.verification],['İşletilebilirlik Notu',r.existingNotes],['Önerilen Aksiyon',r.action],['Sorumlu / Hedef Tarih',r.owner+' / '+r.due],['Mevcut Risk (O × Ş)',r.before.l+' × '+r.before.s+' = '+risk(r.before)],['Hedef Risk (O × Ş)',r.after?r.after.l+' × '+r.after.s+' = '+risk(r.after):'—'],['Durum / Doğrulama',statusText[r.status]+' / '+(r.verified?'Doğrulandı':'Doğrulanmadı')],['Uygulama Kanıtı / Not',r.evidence]];return `<div class="p-detail"><table class="p-detail-table"><thead><tr><th colspan="4"><div class="p-detail-head"><span>${i+1}. ${esc(r.node)} · ${esc(r.deviation)}</span><span>Mevcut ${risk(r.before)} → Hedef ${risk(r.after)??'—'}</span></div></th></tr></thead><tbody>${Array.from({length:Math.ceil(f.length/4)},(_,j)=>`<tr>${f.slice(j*4,j*4+4).map(([k,v])=>`<td>${printField(k,v)}</td>`).join('')}</tr>`).join('')}</tbody></table></div>`}).join('')+'</div>';
let note='<div class="p-note">HAZOP çalışma sonuçları yetkin çok disiplinli bir ekipçe P&ID, proses tasarım verileri, güvenilirlik kanıtları ve saha koşullarıyla doğrulanmalıdır. Bu form tek başına proses emniyeti doğrulaması veya bağımsız koruma katmanı analizi yerine geçmez.</div>';
$('printContent').innerHTML=overview+graphs+table+details+note}
async function printNow(){await readyPrintLogo();buildPrint();try{await document.fonts.ready}catch(e){}await new Promise(r=>requestAnimationFrame(()=>requestAnimationFrame(r)));window.print()}
loadBrand();bind();resetOnOpen();window.addEventListener('pageshow',e=>{if(e.persisted)resetOnOpen()});
})();</script></body></html>