İSG Risk Analizi – Bow-Tie (Papyon Analizi)

1. Uygulamanın Amacı ve Kullanım Alanları

Bow-Tie (Papyon) Risk Analizi, endüstriyel tesislerde meydana gelebilecek tehlikeli olayların nedenlerini, sonuçlarını ve bu olayları önlemek veya etkilerini azaltmak amacıyla kullanılan güvenlik bariyerlerini sistematik biçimde değerlendirmek için geliştirilmiş bir risk analiz yöntemidir.

Risk UI masaüstü HTML uygulaması, bu yöntemin elektronik ortamda uygulanmasını, analizlerin görselleştirilmesini, güvenlik bariyerlerinin izlenmesini ve düzeltici faaliyetlerin kayıt altına alınmasını sağlar.

Uygulama özellikle kimya, enerji, petrol ve doğal gaz, madencilik, üretim, bakım ve diğer yüksek riskli endüstriyel faaliyetlerde kullanılmak üzere tasarlanmıştır.

Temel amacı, yalnızca riskleri listelemek değil; tehlikeli bir olayın nasıl meydana gelebileceğini, hangi güvenlik önlemlerinin olayı engelleyeceğini ve olay gerçekleştiğinde hangi önlemlerin sonuçları sınırlandıracağını ortaya koymaktır.

2. Bow-Tie Analizinin Çalışma Mantığı

Bow-Tie yöntemi, adını papyon şeklindeki görsel analiz yapısından almaktadır. Analiz beş temel unsurdan oluşur:

  1. Tehlit / Nedenler: Kritik olayın oluşmasına yol açabilecek koşullar, ekipman arızaları, insan hataları veya dış etkenler.
  2. Önleyici Bariyerler: Kritik olayın gerçekleşmesini önlemek amacıyla kullanılan teknik, organizasyonel veya operasyonel kontroller.
  3. Kritik Olay (Top Event): Tehlike üzerindeki kontrolün kaybedildiği, istenmeyen olayın gerçekleştiği nokta.
  4. Azaltıcı Bariyerler: Kritik olay gerçekleştikten sonra zararlı sonuçları önlemek veya sınırlandırmak için kullanılan tedbirler.
  5. Olası Sonuçlar: İnsan sağlığı, çevre, ekipman, üretim sürekliliği ve işletme güvenliği üzerindeki olası etkiler.

Analizin sol tarafı olayın oluşmasını önlemeye, sağ tarafı ise olay gerçekleştikten sonraki etkilerin azaltılmasına odaklanır.

3. HTML Formunun Doldurulması

Bölüm 01 – Analiz / Tesis Bilgileri

Bu bölümde analizin kim tarafından, hangi tesiste, hangi ekipman veya faaliyet için ve hangi kapsamda yapıldığı belirtilir.

  • Analiz / Kayıt No.: Her analize benzersiz bir referans numarası verilir. Örnek: BT-2026-001.
  • İş Yeri / Kuruluş: Analizin gerçekleştirildiği kuruluşun adı yazılır.
  • Tesis / Ünite / Proses: İncelenen üretim alanı veya proses belirtilir.
  • Ekipman / Faaliyet: Risk değerlendirmesine konu olan tank, boru hattı, makine, sistem veya çalışma tanımlanır.
  • Analiz Tarihi: Değerlendirmenin yapıldığı tarih seçilir.
  • Hazırlayan / Ekip: Analize katılan kişiler veya ilgili birimler belirtilir.
  • Gözden Geçiren / Onaylayan: Analizi değerlendiren ve onaylayan yetkili yazılır.
  • Revizyon / Sürüm: Analizin güncellik durumunu gösteren revizyon numarası girilir.
  • Kapsam, Sınırlar ve Varsayımlar: Analize dâhil edilen faaliyetler, işletme koşulları ve kapsam dışında tutulan hususlar açıklanır.

Bu bilgilerin eksiksiz doldurulması, analiz kayıtlarının izlenebilirliğini ve daha sonra yapılacak değerlendirmelerin karşılaştırılmasını kolaylaştırır.

Bölüm 02 – Tehlike ve Kritik Olay (Top Event)

Bu bölümde incelenen tehlike ve kontrol kaybı durumu tanımlanır.

  • Tehlike / Tehlikeli Enerji veya Madde: Zarar verme potansiyeline sahip madde, enerji veya durum yazılır.

Örnek: Basınç altında bulunan tehlikeli kimyasal madde.

  • Kritik Olay / Kontrol Kaybı: Tehlikenin kontrol altında tutulamadığı olay tanımlanır.

Örnek: Basınçlı transfer hattının bütünlüğünü kaybetmesi sonucu kimyasal maddenin kontrolsüz şekilde dış ortama salınması.

  • Kritik Olayın Gerçekleşme Ölçütü: Olayın hangi somut koşulda gerçekleşmiş sayılacağı belirtilir.

Örnek: Proses sınırının bozulması ve akışkanın tasarlanan kapalı sistemin dışına çıkması.

  • Acil Durum ve İyileşme Yaklaşımı: Olay sonrasında uygulanacak izolasyon, tahliye, acil müdahale ve güvenli duruma alma yaklaşımları açıklanır.

Kritik olay tanımı, nedenlerle sonuçları birbirinden ayıracak şekilde açık ve anlaşılır olmalıdır.

Bölüm 03 – Öncesi ve Sonrası Risk Değerlendirmesi

Bu bölümde riskler, 5×5 olasılık ve şiddet yöntemi kullanılarak değerlendirilir.

  • Başlangıç Riski: Bariyerlerin risk azaltıcı etkisi hesaba katılmadan önceki risk seviyesini temsil eder.
  • Artık Risk: Mevcut veya planlanan bariyerlerin etkileri değerlendirildikten sonra kalan risk seviyesini ifade eder.

Risk puanı şu şekilde hesaplanır:
Risk Puanı = Olasılık × Şiddet
Örnek değerlendirme:

DeğerlendirmeOlasılıkŞiddetRisk puanı
Başlangıç riski4520
Artık risk2510

Uygulamadaki örnek puanlama sınıfları şöyledir:

  • 1–5: Düşük risk
  • 6–10: Orta risk
  • 11–15: Yüksek risk
  • 16–25: Çok yüksek risk

Risk değerleri seçildiğinde; uygulama puanı ve risk seviyesini otomatik hesaplar. Artık risk, başlangıç riskinden otomatik düşürülmez. Bariyerlerin gerçek etkinliği ve uygulanma durumu dikkate alınarak kullanıcı tarafından ayrıca değerlendirilmelidir.


Bölüm 04 – Tehditler, Sonuçlar ve Bariyer Kütüğü

Bu bölüm, Bow-Tie analizinin temel veri giriş alanıdır.
Sol Kanat – Tehdit / Nedenler ve Önleyici Bariyerler
“+ Tehdit ekle” seçeneğiyle kritik olayı başlatabilecek nedenler tanımlanır. Örnek tehditler:

  • Boru hattında korozyon oluşması.
  • Proses basıncının tasarım sınırlarını aşması.
  • Vana veya bağlantı elemanının arızalanması.
  • Hatalı işletme veya bakım işlemi.

Her tehdidin altına “+ Bariyer ekle” seçeneğiyle ilgili önleyici tedbirler eklenir. 
Örnek önleyici bariyerler: periyodik et kalınlığı ölçümü, basınç alarmı, otomatik koruma sistemi, önleyici bakım programı.

Sağ Kanat – Olası Sonuçlar ve Azaltıcı Bariyerler
“+ Sonuç ekle” seçeneğiyle kritik olayın gerçekleşmesi durumunda meydana gelebilecek etkiler belirtilir. Örnek sonuçlar:

  • Çalışanların tehlikeli kimyasala maruz kalması.
  • Yangın veya patlama meydana gelmesi.
  • Çevresel kirlenme oluşması.
  • Ekipman hasarı ve üretimin durması.

Her sonucun altına olayın etkilerini sınırlayacak azaltıcı bariyerler eklenir. Örnek azaltıcı bariyerler: gaz algılama sistemi, acil izolasyon sistemi, uygun havalandırma, acil durum müdahale prosedürü.

Bariyer Bilgilerinin Doldurulması

Her bariyer için aşağıdaki bilgiler değerlendirilir:

  • Bariyer Tanımı: Kontrol tedbirinin adı ve işlevi.
  • Durum: Planlandı, Etkin, Kısmen Etkin veya Devre Dışı.
  • Sorumlu / Birim: Bariyerin bakımından ve kontrolünden sorumlu kişi veya birim.
  • Son Saha Kontrolü: Gerçekleştirilen en son kontrolün tarihi.
  • Sahada Doğrulandı: Bariyerin etkinliğinin sahada kontrol edildiğini gösteren işaretleme.
  • Zayıflatıcı Etken: Bariyerin işlevini kaybetmesine yol açabilecek koşullar.
  • Zayıflamaya Karşı Koruyucu Önlem: Bu koşulları kontrol altında tutan bakım, test veya diğer tedbirler.
  • Aksiyon / Gerekli İyileştirme: Tespit edilen eksikliğin giderilmesi için yapılması gereken faaliyet.
  • Hedef Tarih: Faaliyetin tamamlanması planlanan tarih.

Bariyerin uygulama tarafından doğrulanmış etkin olarak sayılması için durumunun “Etkin” seçilmesi ve “Sahada Doğrulandı” kutusunun işaretlenmesi gerekir.

Bu işaretleme, gerçek saha kontrolünün yerini tutmaz; kontrol ve doğrulama işlemleri yetkin kişiler tarafından gerçekleştirilmelidir.

Bölüm 05 – Dinamik Bow-Tie / Papyon Diyagramı

  • Kullanıcı tehditleri, bariyerleri, kritik olayı ve sonuçları girdikçe uygulama papyon diyagramını otomatik oluşturur.
  • Diyagramda tehditler sol tarafta, kritik olay merkezde, olası sonuçlar ise sağ tarafta gösterilir.
  • Bariyerler ilgili tehdit veya sonuçlarla birlikte görüntülenir. Durumlarına göre görsel işaretlerle ayrıştırılır.

Bu yapı, güvenlik kontrollerinin hangi olaylara karşı koruma sağladığını ve hangi alanlarda koruma eksikliği bulunduğunu görsel olarak incelemeyi kolaylaştırır.

Bölüm 06 – Bariyer Güvence Durumu ve Kontrol Boşlukları

Uygulama, girilen veriler üzerinden otomatik kontroller gerçekleştirerek güvenlik bariyerlerinin kayıt durumunu değerlendirir.

Bu bölüm sayesinde etkinliği doğrulanmamış bariyerler, korumasız kalan tehdit veya sonuç dalları ve takip gerektiren güvenlik eksiklikleri görülebilir.

Korumasız dal, uygulamanın kayıtlarına göre doğrulanmış etkin bir bariyeri bulunmayan tehdit veya sonuç dalını ifade eder. Bu göstergeler, saha güvenliğine ilişkin kesin bir yeterlilik kararı değildir. Bariyerlerin bağımsızlığı, güvenilirliği ve tasarım yeterliliği ayrıca teknik olarak incelenmelidir.

Bölüm 07 – Bariyer Doğrulama ve Düzeltici Faaliyet Takibi

Tanımlanan bariyerler ve bunlarla ilişkili faaliyetler bu bölümde toplu olarak gösterilir. Sorumlular, kontrol tarihleri, düzeltici faaliyetler ve hedef tarihler izlenebilir. Hedef tarihi geçen ve takip gerektiren faaliyetler uygulama tarafından işaretlenir.

Amaç, analiz sonucunda belirlenen eksikliklerin yalnızca kayıt altına alınması değil, giderilme ve doğrulanma süreçlerinin de takip edilebilmesidir.

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

Uygulamanın alt bölümünde üç temel işlem butonu bulunur.

Excel İçeri Aktar: Daha önce oluşturulmuş, uygulama formatına uygun .xlsx dosyasındaki analiz verilerini forma yükler.

Excel çalışma kitabındaki “Risk Kayıtları” sayfası tehdit, sonuç ve bariyer bilgilerini; “Rapor Bilgileri” sayfası ise analiz kimliği, kritik olay ve risk değerlendirme alanlarını içerir. İçe aktarma sırasında mevcut analiz verilerinin değiştirilmesi için kullanıcı onayı istenir.

Excel Dışarı Aktar: Formdaki güncel verilerin .xlsx uzantılı Excel dosyası olarak kaydedilmesini sağlar. Böylece kayıtlar arşivlenebilir, düzenlenebilir ve daha sonra yeniden forma aktarılabilir.

5. PDF / Yazdır İşlemi

“PDF / Yazdır” seçeneği, doldurulmuş analizin raporlanmasını sağlar.

Yazdırma düzeni A4 yatay sayfa yapısına göre hazırlanmıştır.

Raporda kurumsal başlık, tesis bilgileri, kritik olay, risk puanları, papyon diyagramı, bariyerler ve aksiyon detayları bulunur.

Kullanıcı açılan yazdırma penceresinde yazıcı seçebilir veya “PDF olarak kaydet” seçeneğiyle elektronik rapor oluşturabilir.

6. Kurumsal Başlık ve Logo Düzenleme

Uygulamanın üst alanında kurum logosu, analiz başlığı, alt başlık ve kuruluş adı bulunur. Bu alanlar art arda beş kez tıklanarak düzenlenebilir. Logo alanında beş tıklamanın ardından bilgisayardan yeni görsel seçilebilir.

Başlık ve logo tercihleri tarayıcıda saklanır. Analiz verileri ise sayfa yenilendiğinde boşaldığından çalışmaların kaybolmaması için Excel Dışarı Aktar işlemiyle düzenli olarak yedek alınmalıdır.

7. Kullanımda Dikkat Edilecek Hususlar

Analiz, mümkün olduğunca proses, bakım, işletme ve iş sağlığı ve güvenliği alanlarında bilgi sahibi kişilerden oluşan bir ekip tarafından gerçekleştirilmelidir.

Güvenlik bariyerlerinin tanımlanması tek başına yeterli değildir. Bariyerlerin gerçekten mevcut olması, çalışma koşullarına uygunluğu, performansı ve sürdürülebilirliği doğrulanmalıdır.

Başlangıç ve artık risk değerleri, işletmenin onaylı risk değerlendirme kriterleri doğrultusunda belirlenmelidir. Analiz sonuçları tesis değişiklikleri, ekipman revizyonları, olaylar, ramak kala bildirimleri ve bariyer performansındaki değişiklikler sonrasında yeniden gözden geçirilmelidir.

8. Sonuç

Bow-Tie (Papyon) Risk Analizi – Risk UI, endüstriyel tehlikelerin neden-sonuç ilişkilerini görünür hâle getiren, güvenlik bariyerlerini sistematik olarak kayıt altına alan ve düzeltici faaliyetleri izlemeye yardımcı olan bir masaüstü HTML uygulamasıdır.

Dinamik papyon diyagramı, risk hesaplamaları, bariyer güvence göstergeleri, Excel veri aktarımı ve PDF raporlama özellikleri sayesinde risk değerlendirme çalışmalarının düzenli, izlenebilir ve anlaşılır biçimde yürütülmesine katkı sağlar.

Uygulama, risklerin değerlendirilmesi ve güvenlik önlemlerinin izlenmesi için destekleyici bir araçtır. Nihai güvenlik kararları, ilgili teknik standartlar, mevzuat hükümleri, gerçek saha koşulları ve yetkin uzman değerlendirmeleri esas alınarak verilmelidir.

PDF / Yazdır

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="#0284c7">
<title>İSG Risk Analizi – Bow-Tie (Papyon Analizi)</title>
<style>
:root{--bg:#f8fafc;--paper:#fff;--line:#cbd5e1;--text:#17283b;--muted:#64748b;--blue:#0284c7;--navy:#075985;--pale:#eaf5fc;--red:#b42332;--green:#167a46;--amber:#b45309;--radius:6px;--shadow:0 2px 7px #22465b0b}
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:#edf3f8;color:var(--text);font:13px/1.45 "Segoe UI",Arial,sans-serif}button,input,select,textarea{font:inherit}button{cursor:pointer}button:disabled{opacity:.55;cursor:not-allowed}input,select,textarea{width:100%;min-width:0;padding:8px 10px;min-height:36px;color:var(--text);background:#fff;border:1px solid #cbd5e1;border-radius:5px;outline:none}textarea{resize:vertical;min-height:62px}input:focus,select:focus,textarea:focus{border-color:var(--blue);box-shadow:0 0 0 3px #0284c721}label{display:block;color:#334155;font-size:12px;font-weight:650;margin:0 0 5px}label small{font-weight:400;color:var(--muted)}input[type=checkbox]{width:16px;min-height:16px;height:16px;accent-color:var(--blue)}.frame{margin:8px;min-height:calc(100vh - 16px);border:2px solid #7f95a9;border-radius:7px;background:var(--bg);box-shadow:0 5px 20px #203d5414;overflow:clip}.header{position:sticky;top:0;z-index:30;background:#fff;border-top:4px solid var(--blue);border-bottom:1px solid #bfcfdb;box-shadow:0 3px 10px #23405614}.headerinner{min-height:77px;padding:9px 19px;display:flex;align-items:center;gap:13px}.logo{width:62px;height:57px;flex:none;border:1px solid #cbd5e1;border-radius:5px;background:#f8fafc;color:var(--navy);display:grid;place-items:center;padding:4px;overflow:hidden;font-size:12px;font-weight:800;cursor:pointer}.logo:hover{background:#eaf5fc}.logo img{max-width:100%;max-height:100%;width:100%;height:100%;object-fit:contain}.logo img[hidden],.logo span[hidden]{display:none!important}.header-titles{flex:1;min-width:0}.header h1{font-size:18px;color:var(--navy);margin:0;line-height:1.28}.header p{font-size:12px;margin:4px 0 0;color:#475569}.organization{margin-left:auto;text-align:right;min-width:0;max-width:47%;overflow-wrap:anywhere;font-size:16px;letter-spacing:.2px;font-weight:750;color:#1e3a57}.header [contenteditable=true]{outline:2px dashed var(--blue);outline-offset:4px;background:#f0f9ff;border-radius:3px}.wrap{padding:12px 17px 24px;width:100%}.card{min-width:0;background:var(--paper);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow);margin-bottom:13px}.card-header{background:#eaf5fc;border-left:4px solid var(--blue);border-bottom:1px solid #b9d4e6;border-radius:5px 5px 0 0;padding:10px 13px;display:flex;align-items:center;justify-content:space-between;gap:9px;flex-wrap:wrap}.card-header h2{font-size:15px;font-weight:750;color:#084c71;display:flex;align-items:center;gap:8px;margin:0}.section-number{background:#d2eafb;color:var(--navy);border-radius:4px;padding:3px 6px;font-size:11px}.body{padding:14px 15px}.hint{font-size:11px;color:var(--muted)}.muted{color:var(--muted)}.grid{display:grid;gap:11px}.g2{grid-template-columns:repeat(2,minmax(0,1fr))}.g3{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(4,minmax(0,1fr))}.g6{grid-template-columns:repeat(6,minmax(0,1fr))}.span2{grid-column:span 2}.span3{grid-column:span 3}.wide{grid-column:1/-1}.toolbar{display:flex;align-items:center;gap:7px;flex-wrap:wrap}.action-row{padding:12px 17px 2px}.action-panel{display:flex;align-items:center;flex-wrap:wrap;gap:7px;padding:9px 11px;border:1px solid #b9cbd9;border-radius:6px;background:#fff;box-shadow:0 1px 4px #1b476912}.btn{border:1px solid #cbd5e1;border-radius:5px;color:#334155;background:#fff;min-height:35px;padding:8px 12px;display:inline-flex;align-items:center;justify-content:center;gap:5px;font-weight:650;white-space:nowrap}.btn:hover{background:#eff4f7}.btn-primary{background:var(--blue);border-color:var(--blue);color:#fff}.btn-primary:hover{background:#0369a1}.btn-soft{color:var(--navy);background:#e8f4ff;border-color:#b7d8fb}.btn-soft:hover{background:#d7edff}.btn-green{color:#166534;background:#eaf8ef;border-color:#b9e2c6}.btn-green:hover{background:#dcf3e4}.btn-danger{color:var(--red);border-color:#f0bdc5;background:#fff}.btn-danger:hover{background:#fff1f2}.btn-small{font-size:11px;min-height:28px;padding:5px 8px}.metrics{grid-template-columns:repeat(6,minmax(0,1fr));gap:10px}.metric{margin:0;min-width:0;border-top:3px solid var(--blue);padding:11px 12px;display:flex;justify-content:space-between;gap:7px;align-items:center}.metric small{font-size:10.5px;display:block;color:#64748b;font-weight:700}.metric b{display:block;margin-top:3px;color:#123855;font-size:24px;line-height:1.1}.metric .icon{width:29px;height:29px;border-radius:7px;background:#eaf5fc;display:grid;place-items:center;color:var(--navy);font-size:15px;font-weight:800}.callout{background:#f2f9fd;border:1px solid #d6eefa;border-radius:5px;padding:9px 11px;color:#195071;font-size:12px}.warning{background:#fff9e9;border-color:#f3dda5;color:#815517}.score-card{border:1px solid #dce6ed;border-radius:6px;background:#fcfdff;overflow:hidden}.score-card h3{font-size:13px;margin:0;padding:9px 12px;background:#f2f7fb;border-bottom:1px solid #e4eaf0;color:#1e405a}.score-inner{padding:11px}.score-result{margin-top:10px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px;padding:9px;background:#f2f7fb;border-radius:5px}.score-result strong{font-size:23px}.risk-tag{display:inline-block;padding:4px 7px;border:1px solid transparent;border-radius:4px;font-size:11px;font-weight:750}.risk-unknown{background:#eef2f6;color:#475569;border-color:#d5dce4}.risk-low{background:#e5f7ec;color:#177245;border-color:#b7e5c7}.risk-medium{background:#fff5d9;color:#8d6010;border-color:#f3dda5}.risk-high{background:#fff0e1;color:#ad5019;border-color:#ffccaa}.risk-critical{background:#ffe5e8;color:#a92439;border-color:#f4b5c0}.subhead{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:8px}.subhead h3{margin:0;color:#0e476b;font-size:13px}.count{font-size:11px;border-radius:10px;background:#e0f2fe;color:var(--navy);padding:2px 8px;font-weight:750}.branch-editor{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.branch-side{min-width:0;border:1px solid #d7e2eb;border-radius:6px;background:#fbfdff;padding:11px}.branch-side.left{border-top:3px solid #1d84b5}.branch-side.right{border-top:3px solid #c77a2f}.branch-item{background:#fff;border:1px solid #d9e3ec;border-radius:6px;padding:10px;margin:10px 0}.branch-item>.item-head{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:8px;border-bottom:1px solid #edf1f5;padding-bottom:8px;margin-bottom:10px}.branch-item h4{font-size:12px;color:#144863;margin:0}.barrier{border:1px solid #dce4ed;background:#f8fbff;border-radius:6px;padding:9px;margin-top:8px}.barrier h5{margin:0;font-size:12px;color:#274d66}.barrier-head{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:7px}.checkrow{display:flex;gap:8px;align-items:center;font-size:12px;color:#334155}.checkrow label{display:inline;margin:0;font-weight:500}.empty{padding:17px 10px;color:#64748b;border:1px dashed #b9cbd9;border-radius:6px;text-align:center;background:#fff;font-size:12px}.block-footer{display:flex;align-items:center;justify-content:flex-end;gap:8px;margin-top:12px}.diagram-scroll{overflow-x:auto;max-width:100%;padding-bottom:3px}.diagram{min-width:1030px}.diagram-head,.diagram-grid{display:grid;grid-template-columns:1.15fr 1.25fr 1.08fr 1.25fr 1.15fr;column-gap:14px}.diagram-head{margin-bottom:10px;font-size:11px;font-weight:750;text-transform:uppercase;letter-spacing:.035em;text-align:center}.diagram-head span{border-radius:4px;padding:9px 6px;background:#eef5fb;color:#184c6b}.diagram-head span:nth-child(1){background:#e7f1f9}.diagram-head span:nth-child(3){background:#ffe8e7;color:#92333b}.diagram-head span:nth-child(5){background:#fff1e4;color:#91511a}.diagram-grid{row-gap:10px;align-items:stretch}.dcell{padding:10px 10px;min-height:95px;background:#fff;border:1px solid #cbdbe7;border-radius:6px;position:relative;overflow-wrap:anywhere;display:flex;flex-direction:column;justify-content:center;gap:5px;break-inside:avoid}.dcell strong{font-size:12px;line-height:1.36}.dcell small{color:#64748b;font-size:10px}.dcell.d-threat{border-left:4px solid #4b91bf;background:#f6fbff}.dcell.d-prevent{border-left:4px solid #22816b;background:#f5fcf8}.dcell.d-mitigate{border-left:4px solid #8d7ab6;background:#faf8ff}.dcell.d-outcome{border-left:4px solid #d18a38;background:#fffaf5}.dcell.d-threat:after,.dcell.d-prevent:after,.dcell.d-center:after,.dcell.d-mitigate:after{content:'→';position:absolute;right:-16px;top:50%;transform:translateY(-50%);color:#7794a7;font-size:17px;font-weight:800;z-index:2;pointer-events:none}.dcell.d-center{border:2px solid #df888d;background:#fff1f0;text-align:center;align-items:center;min-height:115px;padding:15px 11px}.d-center .hazard{color:#a64f28;background:#fff9ed;border:1px solid #eddfb7;padding:6px;border-radius:5px;font-size:10.5px;width:100%}.d-center strong{color:#912e38;font-size:16px}.d-center .event-chip{font-size:10px;text-transform:uppercase;color:#9b3441;letter-spacing:.08em;font-weight:800}.d-center .center-risk{font-size:10px;background:#fff;border-radius:4px;padding:5px;color:#65424a}.diagram-chip{display:block;border-radius:4px;background:#edf3f8;color:#325169;font-size:10.5px;padding:3px 6px;margin-top:2px;overflow-wrap:anywhere}.diagram-chip.ok{background:#e5f7ec;color:#146038}.diagram-chip.warn{background:#fff2d9;color:#875912}.diagram-chip.bad{background:#ffe5e8;color:#a92439}.diagram-chip.planned{background:#e8eff7;color:#475569}.diagram-chip.emptychip{background:#ffe5e8;color:#a92439;font-weight:750}.diagram-legend{display:flex;gap:7px;flex-wrap:wrap;margin-top:12px;color:#52677b;font-size:11px}.legend-item{padding:4px 7px;border:1px solid #d6e2ea;border-radius:4px;background:#fff}.quality{display:flex;gap:7px;flex-wrap:wrap}.quality span{border-radius:5px;padding:6px 8px;background:#fff7ea;border:1px solid #f0d8a8;color:#814d19;font-size:11px}.quality .allgood{color:#156534;background:#ebf8ef;border-color:#bce1c5}.tablebox{overflow-x:auto;max-width:100%}table{border-collapse:collapse;width:100%;min-width:860px;text-align:left}th,td{padding:9px 9px;border-bottom:1px solid #e5ecf2;vertical-align:top;font-size:11.5px;overflow-wrap:anywhere}th{background:#f2f7fb;text-align:left;color:#39566c;font-weight:800;font-size:11px}tbody tr:nth-child(even){background:#f8fbfd}.status-tag{display:inline-block;padding:3px 6px;border-radius:4px;font-size:10px;white-space:nowrap;font-weight:700}.status-active{color:#166534;background:#e5f7ec}.status-weak{color:#8d6010;background:#fff3d5}.status-failed{color:#aa2235;background:#ffe5e8}.status-planned{color:#475569;background:#edf2f7}.overdue{color:#b42332;font-weight:750}.foot{font-size:11px;color:#64748b;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:7px;margin:16px 0 0}.print-only{display:none}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}#toast{position:fixed;bottom:20px;right:20px;padding:10px 14px;background:#143d54;color:#fff;border-radius:6px;box-shadow:0 8px 30px #12283c35;opacity:0;pointer-events:none;transform:translateY(8px);transition:.2s;z-index:100;max-width:350px}#toast.show{opacity:1;transform:translateY(0)}.note{margin-top:8px;color:#64748b;font-size:11px}.print-table{width:100%;min-width:0}.print-table th,.print-table td{font-size:10px;padding:5px}.print-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:7px}.print-kv{border:1px solid #dae4ec;border-radius:4px;padding:6px}.print-kv small{display:block;color:#64748b}.print-kv strong{font-size:11px;overflow-wrap:anywhere;white-space:pre-wrap}.print-title{font-size:13px;color:#075985;margin:12px 0 6px}.print-coverage{font-size:10px;color:#5a6674;margin:8px 0}
@media(max-width:1200px){.metrics{grid-template-columns:repeat(3,minmax(0,1fr))}.g6{grid-template-columns:repeat(3,minmax(0,1fr))}}@media(max-width:800px){.branch-editor,.g4{grid-template-columns:1fr 1fr}.branch-editor{grid-template-columns:1fr}.g6{grid-template-columns:repeat(2,minmax(0,1fr))}.organization{font-size:13px}.diagram{min-width:980px}}@media(max-width:560px){.frame{margin:3px;min-height:calc(100vh - 6px)}.headerinner{padding:9px;flex-wrap:wrap;gap:8px}.logo{width:49px;height:49px}.header h1{font-size:15px}.header p{font-size:11px}.header-titles{flex:1 1 55%}.organization{flex:1 0 100%;max-width:100%;font-size:12px}.wrap{padding:9px}.action-row{padding:9px 9px 2px}.action-panel{width:100%;padding:8px}.action-panel .btn{flex:1 1 125px;white-space:normal}.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.metric{padding:9px}.metric b{font-size:21px}.metric .icon{display:none}.g2,.g3,.g4,.g6{grid-template-columns:1fr}.span2,.span3{grid-column:auto}.body{padding:11px}.card-header{padding:9px 10px}.grid{gap:9px}.diagram{min-width:860px}}
@page{size:A4 landscape;margin:9mm}
@media print{html,body{margin:0!important;padding:0!important;background:#fff!important;color:#17283b!important;font-size:10px!important;-webkit-print-color-adjust:exact;print-color-adjust:exact}.frame{margin:0!important;min-height:0!important;border:1.5px solid #7f95a9!important;box-shadow:none!important;overflow:visible!important;border-radius:0!important;background:#fff!important}.header{position:static!important;box-shadow:none!important;border-top:4px solid #0284c7!important}.headerinner{min-height:64px;padding:7px 12px!important}.header h1{font-size:17px!important}.header p{font-size:11px!important}.organization{font-size:14px!important}.action-row,.screen-only,#toast,#logoFile{display:none!important}.wrap{padding:8px 10px!important}.print-only{display:block!important}.card{box-shadow:none!important;margin-bottom:9px!important;break-inside:auto!important}.card-header{break-after:avoid!important}.body{padding:9px!important}.diagram-scroll{overflow:visible!important}.diagram{min-width:0!important}.diagram-head,.diagram-grid{column-gap:10px!important;grid-template-columns:1.1fr 1.2fr 1.05fr 1.2fr 1.1fr!important}.diagram-grid{row-gap:7px!important}.dcell{padding:6px!important;min-height:68px!important;break-inside:avoid!important}.dcell strong{font-size:10px!important}.d-center strong{font-size:13px!important}.dcell small,.diagram-chip{font-size:9px!important}.dcell.d-threat:after,.dcell.d-prevent:after,.dcell.d-center:after,.dcell.d-mitigate:after{right:-13px!important;font-size:13px!important}.diagram-head span{padding:5px 3px!important;font-size:9px!important}.diagram-legend{font-size:9px!important}.print-table{width:100%!important;min-width:0!important;table-layout:fixed}.print-table td,.print-table th{font-size:9px!important;overflow-wrap:anywhere!important;word-break:break-word!important}.print-grid{grid-template-columns:repeat(4,1fr)!important}.quality span{font-size:9px!important;padding:4px!important}.foot{font-size:9px!important}tr{break-inside:avoid!important}#diagramSection{break-before:auto}}

/* REFERANS RISK UI – HAZOP ile uyumlu kabuk, kurumsal başlık, palet ve alt işlem paneli */
:root{--bg:#f1f6fa;--paper:#fff;--line:#c9d8e4;--text:#183146;--muted:#5d7282;--blue:#087dae;--navy:#08658e;--pale:#e9f5fd;--radius:7px;--shadow:0 2px 7px #17405a0a}
body{background:#eaf1f6;font:13px/1.43 'Segoe UI',Arial,sans-serif;color:var(--text)}
.frame{border-color:#8ca7bc;background:#f9fcfe;box-shadow:0 4px 18px #23425a15;overflow:visible}
.header{border-top-color:#0579ad;box-shadow:none;border-bottom-color:#bed1df}
.headerinner{min-height:75px;padding:8px 17px;gap:13px}
.logo{width:61px;height:57px;flex:0 0 61px;background:#f6fbff;border-color:#cbd9e4;color:#11658b}
.header h1{color:#08658e;font-size:19px;line-height:1.22}
.header p{font-size:11px;color:#4d6576;margin:3px 0 0}
.organization{max-width:35%;font-size:15px;letter-spacing:0;font-weight:800;color:#23455e}
.wrap{padding:12px 17px 7px}
.card{border-color:#c9d8e4;box-shadow:0 2px 7px #17405a0a;margin-bottom:10px}
.card-header{background:#e9f5fd;border-left:4px solid #087dae;border-bottom:1px solid #c2ddea;padding:9px 12px}
.card-header h2{font-size:14px;color:#0b587c}
.section-number{background:#d2ecfb;color:#135d88;font-size:10px}
.body{padding:11px 13px}
.metric{border-color:#c7d8e3;border-top-color:#0c83bd;padding:9px 12px}
.metric small{color:#5b7386}
.metric b{color:#164766;font-size:23px}
input,select,textarea{border-color:#c8d6e1;padding:7px 8px;min-height:34px}
label{color:#29465c;font-size:11.5px;margin-bottom:4px}
.btn{border-color:#c5d4df;padding:7px 10px;min-height:33px;font-weight:700;color:#2c4a5c}
.btn-soft{background:#e7f5ff;border-color:#b8dbf4;color:#0b5881}
.btn-green{background:#e7f8ee;border-color:#b5e0c6;color:#17633d}
.btn-small{font-size:10.5px;padding:4px 7px;min-height:27px}
.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}
.footerline{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}
@media(max-width:720px){.headerinner{padding:8px 10px;flex-wrap:wrap}.logo{flex:0 0 49px;width:49px;height:49px}.header h1{font-size:15px}.header p{font-size:10px}.organization{max-width:100%;width:100%;font-size:11px;text-align:left}.wrap{padding:9px}.bottom-actions{padding:5px 9px}.bottom-panel{width:100%;gap:5px}.bottom-panel .btn{flex:1 1 110px;white-space:normal}.footerline{font-size:10px;margin:0 9px}}
/* Referans formdaki sabit rapor çerçevesi, tekrarlanan başlık, A4 yatay */
#printPages,#printPageFrame{display:none}
@page{size:A4 landscape;margin:6mm 7mm 7mm 7mm}
@media print{
 html,body{margin:0!important;padding:0!important;background:#fff!important;color:#183146!important;font-size:8.5px!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!important;border-spacing:0!important;table-layout:fixed}
 #printPages>thead{display:table-header-group!important}#printPages>tbody{display:table-row-group!important}
 #printPages>thead>tr>td{padding:0 0 3px!important}#printPages>tbody>tr>td{padding:0!important}
 #printPageFrame{display:block!important;position:fixed;inset:0;border:1.5px solid #809bb2;pointer-events:none;z-index:15}
 .p-header{display:flex;align-items:center;gap:8px;min-height:49px;padding:3px 8px;border-top:4px solid #0c84b8;border-bottom:1px solid #abc8d9;background:#fff!important}
 .p-header img{width:43px;height:40px;object-fit:contain}.p-header h1{font-size:14px;margin:0;color:#06648d}.p-header p{font-size:9.5px;margin:3px 0;color:#526879}.p-org{margin-left:auto;max-width:35%;text-align:right;font-size:12px;font-weight:800;overflow-wrap:anywhere}
 #printContent{line-height:1.27!important}
 .p-section{border:1px solid #b9cdda;margin-bottom:4px;break-inside:auto;page-break-inside:auto}
 .p-section h3{font-size:10.5px;background:#eaf5fc!important;border-left:4px solid #067fb3;color:#075d81;margin:0;padding:4px 7px;break-after:avoid-page;page-break-after:avoid}
 .p-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:3px;padding:4px}
 .p-field{border:1px solid #dce8ef;padding:3px 4px;border-radius:3px;overflow-wrap:anywhere;white-space:pre-wrap;font-size:8px;min-height:24px}
 .p-field b{display:block;color:#667d8e;font-size:7.5px;margin-bottom:1px}
 .p-note{font-size:7.5px;color:#5b7082;padding:3px 6px;line-height:1.25}
 .p-risk{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:5px;padding:5px}
 .p-risk>div{border:1px solid #d6e3eb;padding:5px 8px;font-size:9px}
 .p-risk strong{font-size:13px;color:#145c7e}
 .p-diagram{padding:4px;break-inside:avoid-page;page-break-inside:avoid}
 .p-diagram .diagram{width:100%!important;min-width:0!important}
 .p-diagram .diagram-head,.p-diagram .diagram-grid{column-gap:5px!important;grid-template-columns:1.1fr 1.2fr 1.05fr 1.2fr 1.1fr!important}
 .p-diagram .diagram-grid{row-gap:4px!important}
 .p-diagram .diagram-head{margin:0 0 4px!important}
 .p-diagram .diagram-head span{padding:3px 1px!important;font-size:7px!important}
 .p-diagram .dcell{padding:3px!important;min-height:43px!important;border-radius:3px!important;overflow-wrap:anywhere!important;break-inside:avoid!important}
 .p-diagram .dcell strong{font-size:8px!important}.p-diagram .d-center strong{font-size:9px!important}
 .p-diagram .dcell small,.p-diagram .diagram-chip{font-size:7px!important;line-height:1.25!important;padding:2px!important}
 .p-diagram .dcell.d-threat:after,.p-diagram .dcell.d-prevent:after,.p-diagram .dcell.d-center:after,.p-diagram .dcell.d-mitigate:after{right:-9px!important;font-size:10px!important}
 .p-table{width:100%;table-layout:fixed;border-collapse:collapse}
 .p-table th,.p-table td{border:1px solid #d5e4ed;padding:3px 4px;font-size:7.6px;vertical-align:top;overflow-wrap:anywhere}
 .p-table th{background:#eaf4fa!important;color:#105f84}
 .p-table thead{display:table-header-group}.p-table tr{break-inside:avoid-page;page-break-inside:avoid}
 .p-detail{display:block;margin:2px 3px;break-inside:auto;page-break-inside:auto}
 .p-detail-table{width:100%;border-collapse:separate;border-spacing:2px 0;table-layout:fixed;break-inside:auto}
 .p-detail-table thead{display:table-header-group!important}
 .p-detail-table th{padding:0!important;font-size:9px;text-align:left;border:0}
 .p-detail-table th .p-detail-head{padding:3px 4px;background:#eaf5fa!important;line-height:1.2;font-size:8.2px;color:#145c7e;break-after:avoid-page}
 .p-detail-table tr{break-inside:avoid-page;page-break-inside:avoid}
 .p-detail-table td{vertical-align:top;padding:0;border:0;overflow-wrap:anywhere;line-height:1.25}
 .p-detail-table td .p-field{height:100%;padding:2px 3px;font-size:7.6px;break-inside:avoid-page}
}
</style>
</head>
<body>
<div class="frame" id="uiShell">
<header class="header"><div class="headerinner">
 <button type="button" id="logoTrigger" class="logo" title="Logoyu değiştirmek için 5 kez art arda tıklayın"><img id="logoImage" alt="Kurum logosu" hidden><span id="logoFallback">LOGO</span></button>
 <div class="header-titles"><h1 id="brandTitle" title="Başlığı düzenlemek için 5 kez tıklayın">BOW-TIE (PAPYON) RİSK ANALİZİ</h1><p id="brandSubtitle" title="Alt başlığı düzenlemek için 5 kez tıklayın">Tehlike · Kritik Olay · Tehditler · Bariyerler · Sonuçlar · Aksiyon Takibi</p></div>
 <div id="brandOrg" class="organization" title="Kurum adını düzenlemek için 5 kez tıklayın">OLGUN BAŞKANLIĞI</div>
</div><input id="logoFile" class="sr-only" type="file" accept="image/png,image/jpeg,image/webp,image/gif" aria-label="Kurum logosu seç"></header>
<main class="wrap">
 <section class="grid metrics screen-only" aria-label="Özet göstergeler">
  <div class="card metric"><div><small>TEHDİT / NEDEN</small><b id="statThreats">0</b></div><span class="icon">↘</span></div>
  <div class="card metric"><div><small>OLASI SONUÇ</small><b id="statCons">0</b></div><span class="icon">↗</span></div>
  <div class="card metric"><div><small>TOPLAM BARİYER</small><b id="statBarriers">0</b></div><span class="icon">▥</span></div>
  <div class="card metric"><div><small>DOĞRULANMIŞ ETKİN</small><b id="statVerified">0</b></div><span class="icon">✓</span></div>
  <div class="card metric"><div><small>KORUMASIZ DAL</small><b id="statGaps">0</b></div><span class="icon">!</span></div>
  <div class="card metric"><div><small>GECİKEN AKSİYON</small><b id="statOverdue">0</b></div><span class="icon">⌛</span></div>
 </section>
 <section class="card screen-only" aria-labelledby="s1"><div class="card-header"><h2 id="s1"><span class="section-number">01</span> Analiz / tesis bilgileri</h2><span class="hint">Analiz kayıtları yenilemede sıfırlanır; verilerinizi Excel Dışarı Aktar ile yedekleyin.</span></div>
 <div class="body grid g4">
  <div><label for="m_ref">Analiz / kayıt no.</label><input id="m_ref" data-meta="ref" placeholder="Örn. BT-2026-001"></div>
  <div><label for="m_company">İş yeri / kuruluş</label><input id="m_company" data-meta="company" placeholder="İş yeri adı"></div>
  <div><label for="m_facility">Tesis / ünite / proses</label><input id="m_facility" data-meta="facility" placeholder="Örn. Kimyasal işleme ünitesi"></div>
  <div><label for="m_equipment">Ekipman / faaliyet</label><input id="m_equipment" data-meta="equipment" placeholder="Hat, tank veya süreç"></div>
  <div><label for="m_date">Analiz tarihi</label><input id="m_date" data-meta="date" type="date"></div>
  <div><label for="m_preparer">Hazırlayan / ekip</label><input id="m_preparer" data-meta="preparer"></div>
  <div><label for="m_approver">Gözden geçiren / onaylayan</label><input id="m_approver" data-meta="approver"></div>
  <div><label for="m_revision">Revizyon / sürüm</label><input id="m_revision" data-meta="revision" placeholder="Örn. R00"></div>
  <div class="wide"><label for="m_scope">Kapsam, sınırlar ve varsayımlar</label><textarea id="m_scope" data-meta="scope" placeholder="Analizin kapsadığı faaliyet, operasyon koşulları ve hariç tutulan durumlar"></textarea></div>
 </div></section>
 <section class="card screen-only" aria-labelledby="s2"><div class="card-header"><h2 id="s2"><span class="section-number">02</span> Tehlike ve kritik olay (Top Event)</h2></div><div class="body grid g2">
  <div><label for="hazard">Tehlike / tehlikeli enerji veya madde</label><textarea id="hazard" data-meta="hazard" placeholder="Örn. Basınç altındaki toksik kimyasal"></textarea></div>
  <div><label for="topEvent">Kritik olay / kontrol kaybı</label><textarea id="topEvent" data-meta="topEvent" placeholder="Örn. Boru hattı bütünlüğünün kaybı ve kontrolsüz salım"></textarea></div>
  <div><label for="criterion">Kritik olayın gerçekleşme ölçütü</label><textarea id="criterion" data-meta="criterion" placeholder="Top Event'i somut olarak hangi koşul tanımlar?"></textarea></div>
  <div><label for="recovery">Acil durum ve iyileşme yaklaşımı</label><textarea id="recovery" data-meta="recovery" placeholder="Tahliye, izolasyon, olay sonrası toparlanma; mevcut prosedürlerle ilişki"></textarea></div>
  <div class="wide callout">Bow-Tie mantığı: <b>tehdit / neden → önleyici bariyer → kritik olay → sonuç azaltıcı bariyer → sonuç.</b> Önleyici bariyerler kritik olayın oluşmasını engellemeye; azaltıcı bariyerler kritik olay sonrası etkileri sınırlamaya yöneliktir. Bir bariyerin yalnızca listelenmesi, onun etkin olduğunu kanıtlamaz.</div>
 </div></section>
 <section class="card screen-only" aria-labelledby="s3"><div class="card-header"><h2 id="s3"><span class="section-number">03</span> Öncesi ve sonrası risk değerlendirmesi</h2><span class="hint">Örnek 5×5 ölçek; kurumun onaylı risk kriterlerine göre uyarlayın.</span></div><div class="body">
  <div class="grid g2">
   <div class="score-card"><h3>Bariyerler öncesi / başlangıç riski</h3><div class="score-inner"><div class="grid g2">
    <div><label for="beforeP">Olasılık</label><select id="beforeP" data-score="beforeP"><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">Şiddet</label><select id="beforeS" data-score="beforeS"><option value="">Seçiniz</option><option value="1">1 · Önemsiz</option><option value="2">2 · Hafif</option><option value="3">3 · Orta</option><option value="4">4 · Ciddi</option><option value="5">5 · Çok ciddi</option></select></div>
   </div><div id="beforeResult" class="score-result"><span>Risk puanı</span><strong>—</strong></div></div></div>
   <div class="score-card"><h3>Bariyerler sonrası / artık risk</h3><div class="score-inner"><div class="grid g2">
    <div><label for="afterP">Olasılık</label><select id="afterP" data-score="afterP"><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">Şiddet</label><select id="afterS" data-score="afterS"><option value="">Seçiniz</option><option value="1">1 · Önemsiz</option><option value="2">2 · Hafif</option><option value="3">3 · Orta</option><option value="4">4 · Ciddi</option><option value="5">5 · Çok ciddi</option></select></div>
   </div><div id="afterResult" class="score-result"><span>Risk puanı</span><strong>—</strong></div></div></div>
  </div><p class="note">Gösterge sınıfları (örnek): 1–5 düşük, 6–10 orta, 11–15 yüksek, 16–25 çok yüksek. Bu sınıflama kurum politikasının veya mevzuattaki yöntemin yerine geçmez; artık risk otomatik olarak düşürülmez, değerlendirici tarafından ayrıca girilir.</p>
 </div></section>
 <section class="card screen-only" aria-labelledby="s4"><div class="card-header"><h2 id="s4"><span class="section-number">04</span> Tehditler, sonuçlar ve bariyer kütüğü</h2><span class="hint">Her dal için istediğiniz sayıda bariyer ekleyin.</span></div><div class="body">
  <div class="branch-editor">
   <section class="branch-side left" aria-label="Önleyici taraf"><div class="subhead"><h3>Sol kanat · Tehdit / neden + önleyici bariyer</h3><span id="threatCount" class="count">0</span></div><div id="threatEditor"></div><div class="block-footer"><button type="button" class="btn btn-soft btn-small" data-action="add-item" data-side="threats">+ Tehdit ekle</button></div></section>
   <section class="branch-side right" aria-label="Azaltıcı taraf"><div class="subhead"><h3>Sağ kanat · Sonuç + azaltıcı bariyer</h3><span id="consCount" class="count">0</span></div><div id="consequenceEditor"></div><div class="block-footer"><button type="button" class="btn btn-soft btn-small" data-action="add-item" data-side="consequences">+ Sonuç ekle</button></div></section>
  </div><p class="note">Bariyer alanları: durum, saha doğrulaması, sorumlu, son kontrol tarihi, zayıflatıcı etken, koruyucu önlem, düzeltici faaliyet ve hedef tarih. Sıra numaraları her iki kanat boyunca tek sıra hâlinde ilerler. Excel'deki Dal ID tekrarı, aynı dala bağlı birden fazla bariyerin ilişkisini göstermek için kullanılır; sıra numarası değildir.</p>
 </div></section>
 <section id="printSummary" class="card print-only" aria-label="Yazdırma analizi özeti"></section>
 <section class="card" id="diagramSection" aria-labelledby="s5"><div class="card-header"><h2 id="s5"><span class="section-number">05</span> Dinamik Bow-Tie / Papyon diyagramı</h2><span class="hint screen-only">Alanları düzenledikçe otomatik güncellenir · Dar ekranda diyagram kendi içinde kaydırılır.</span></div><div class="body">
  <div class="diagram-scroll"><div class="diagram" id="diagramContent"></div></div>
  <div class="diagram-legend"><span class="legend-item">↘ Sol kanat: kritik olayın oluşmasını <b>önleme</b></span><span class="legend-item">↗ Sağ kanat: olayın etkisini <b>azaltma</b></span><span class="legend-item">✓ Etkin + doğrulanmış</span><span class="legend-item">! Etkisiz / eksik / doğrulanmamış bariyer uyarısı</span></div>
 </div></section>
 <section class="card screen-only" aria-labelledby="s6"><div class="card-header"><h2 id="s6"><span class="section-number">06</span> Bariyer güvence durumu ve kontrol boşlukları</h2></div><div class="body"><div id="quality" class="quality"></div><p class="note">Bu otomatik kontroller yalnızca veri alanlarının doldurulması ve bariyer doğrulama durumları üzerinden uyarı üretir. Fiziksel bağımsızlık, yeterlilik veya tasarım uygunluğu için uzman saha incelemesi gereklidir.</p></div></section>
 <section class="card screen-only" aria-labelledby="s7"><div class="card-header"><h2 id="s7"><span class="section-number">07</span> Bariyer doğrulama / düzeltici faaliyet takibi</h2><span class="hint">Bütün bariyerler listelenir; geciken açık faaliyetler işaretlenir.</span></div><div class="body"><div class="tablebox" id="actionTable"></div></div></section>
 <section class="card print-only" id="printAppend" aria-label="Yazdırma bariyer ve aksiyon kayıtları"></section>
 <footer class="foot"><span>RISK UI · Bow-Tie (Papyon) yöntemi · Tek dosyalık çevrimdışı uygulama</span><span>Karar desteği amaçlıdır; teknik doğrulama ve yetkili onay gerektirir.</span></footer>
</main>
<div class="bottom-actions"><div class="bottom-panel" aria-label="Dosya ve yazdırma işlemleri">
<button type="button" class="btn btn-soft" 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" aria-label="Bow-Tie Excel çalışma kitabını içeri aktar">
</div></div>
<footer class="footerline">Temel özelliği: Tehlike → Tehdit / neden → Önleyici bariyer → Kritik olay → Azaltıcı bariyer → Sonuç &nbsp;&nbsp; Kullanım: Endüstriyel proses ve yüksek sonuçlu riskler &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" role="status" aria-live="polite"></div>
<script>
'use strict';
const $=id=>document.getElementById(id);
const BRAND_KEY='risk-ui-bow-tie-brand-v1';
const BRAND_DEFAULT={title:'BOW-TIE (PAPYON) RİSK ANALİZİ',subtitle:'Tehlike · Kritik Olay · Tehditler · Bariyerler · Sonuçlar · Aksiyon Takibi',org:'OLGUN BAŞKANLIĞI',logo:''};
const META_KEYS=['ref','company','facility','equipment','date','preparer','approver','revision','scope','hazard','topEvent','criterion','recovery'];
const SCORE_KEYS=['beforeP','beforeS','afterP','afterS'];
const stateFactory=()=>({schema:'risk-ui-bow-tie-v1',meta:Object.fromEntries(META_KEYS.map(k=>[k,''])),scores:Object.fromEntries(SCORE_KEYS.map(k=>[k,''])),threats:[],consequences:[]});
let state=stateFactory();
let brand={...BRAND_DEFAULT};
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=()=> 'i'+(globalThis.crypto?.randomUUID?.().replace(/-/g,'')||Math.random().toString(36).slice(2,11));
// Görüntüdeki sıra numaraları Excel Dal/Bariyer ID alanlarından bağımsızdır.
// Her iki kanatta 1,2,3... olarak tek sıra kullanılır; ilişki kimlikleri tekrar edebilir.
function globalBranchNumber(side,index){return side==='threats'?index+1:state.threats.length+index+1}
function globalBarrierNumber(side,branchIndex,barrierIndex){
 let n=1;
 if(side==='consequences')n+=state.threats.reduce((sum,b)=>sum+b.barriers.length,0);
 for(let k=0;k<branchIndex;k++)n+=state[side][k].barriers.length;
 return n+barrierIndex;
}
const safe=x=>String(x??'').replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const val=x=>String(x??'');
const cleanName=x=>val(x).trim()||'Belirtilmedi';
const printVal=x=>safe(val(x).trim()||'—').replace(/\n/g,'<br>');
const statuses={planned:'Planlandı',active:'Etkin',weak:'Kısmen etkin',failed:'Devre dışı'};
let toastTimer;
function toast(message){const el=$('toast');el.textContent=message;el.classList.add('show');clearTimeout(toastTimer);toastTimer=setTimeout(()=>el.classList.remove('show'),2900)}
function fileDownload(name,body,mime){const blob=new Blob([body],{type:mime});const url=URL.createObjectURL(blob);const a=document.createElement('a');a.href=url;a.download=name;document.body.append(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),1600)}
function slug(){return val(state.meta.ref).trim().replace(/[^a-z0-9_-]/gi,'_').slice(0,35)||'Bow_Tie'}
function newBarrier(){return {id:uid(),name:'',status:'planned',verified:false,owner:'',checkedAt:'',degrader:'',safeguard:'',action:'',dueDate:''}}
function newItem(){return {id:uid(),title:'',detail:'',barriers:[]}}
function allBranches(){return [...state.threats.map((b,i)=>({...b,side:'threats',index:i})),...state.consequences.map((b,i)=>({...b,side:'consequences',index:i}))]}
function allBarriers(){return allBranches().flatMap(b=>b.barriers.map(barrier=>({...barrier,side:b.side,branch:b.title,branchIndex:b.index}))) }
function isVerified(b){return b.status==='active'&&b.verified===true}
function overdue(b){return !!(b.dueDate&&b.dueDate<today()&&(!isVerified(b)||val(b.action).trim()))}
function gapBranches(){return allBranches().filter(b=>!b.barriers.some(isVerified))}
function risk(p,s){let a=Number(p),b=Number(s);if(!p||!s||!Number.isInteger(a)||!Number.isInteger(b)||a<1||a>5||b<1||b>5)return {score:null,desc:'Değerlendirilmedi',klass:'risk-unknown'};let n=a*b;return {score:n,desc:n<=5?'Düşük':n<=10?'Orta':n<=15?'Yüksek':'Çok yüksek',klass:n<=5?'risk-low':n<=10?'risk-medium':n<=15?'risk-high':'risk-critical'}}
function refreshScores(){for(const side of ['before','after']){const r=risk(state.scores[side+'P'],state.scores[side+'S']);$(side+'Result').innerHTML=`<span>Risk puanı</span><strong>${r.score??'—'}</strong><span class="risk-tag ${r.klass}">${r.desc}</span>`}}
function refreshStats(){const b=allBarriers();const data={statThreats:state.threats.length,statCons:state.consequences.length,statBarriers:b.length,statVerified:b.filter(isVerified).length,statGaps:gapBranches().length,statOverdue:b.filter(overdue).length};Object.entries(data).forEach(([k,v])=>$(k).textContent=v);refreshScores();renderDiagram();renderQuality();renderActions()}
function inputField(label,field,value,attrs=''){return `<label>${label}<input type="text" data-field="${field}" value="${safe(value)}" ${attrs}></label>`}
function selectStatus(b){return `<label>Durum<select data-bfield="status">${Object.entries(statuses).map(([v,n])=>`<option value="${v}" ${b.status===v?'selected':''}>${n}</option>`).join('')}</select></label>`}
function renderBarrier(b,side,i,j){return `<div class="barrier" data-side="${side}" data-i="${i}" data-j="${j}">
 <div class="barrier-head"><h5>${side==='threats'?'Önleyici':'Azaltıcı'} bariyer #${globalBarrierNumber(side,i,j)}</h5><button class="btn btn-danger btn-small" type="button" data-action="remove-barrier" data-side="${side}" data-i="${i}" data-j="${j}" aria-label="Bariyeri sil">× Sil</button></div>
 <div class="grid g2"><div class="wide"><label>Bariyer tanımı<input data-bfield="name" value="${safe(b.name)}" placeholder="Örn. Basınç alarmı + otomatik izolasyon"></label></div>
 <div>${selectStatus(b)}</div><div><label>Sorumlu / birim<input data-bfield="owner" value="${safe(b.owner)}" placeholder="Bariyer sorumlusu"></label></div>
 <div><label>Son saha kontrolü<input type="date" data-bfield="checkedAt" value="${safe(b.checkedAt)}"></label></div>
 <div class="checkrow"><input data-bfield="verified" id="v_${side}_${i}_${j}" type="checkbox" ${b.verified?'checked':''}><label for="v_${side}_${i}_${j}">Sahada doğrulandı</label></div>
 <div><label>Zayıflatıcı etken / bozulma mekanizması<textarea data-bfield="degrader" placeholder="Yanlış bakım, ortak neden, güç kesintisi…">${safe(b.degrader)}</textarea></label></div>
 <div><label>Zayıflamaya karşı koruyucu önlem<textarea data-bfield="safeguard" placeholder="Test, periyodik bakım, yedeklilik…">${safe(b.safeguard)}</textarea></label></div>
 <div class="wide"><label>Aksiyon / gerekli iyileştirme<textarea data-bfield="action" placeholder="Eksikliği giderme, test etme veya iyileştirme">${safe(b.action)}</textarea></label></div>
 <div><label>Hedef tarih<input type="date" data-bfield="dueDate" value="${safe(b.dueDate)}"></label></div>
 </div></div>`}
function renderBranchItem(item,side,i){const noun=side==='threats'?'Tehdit / neden':'Olası sonuç';return `<article class="branch-item" data-side="${side}" data-i="${i}">
 <div class="item-head"><h4>${noun} #${globalBranchNumber(side,i)}</h4><button type="button" class="btn btn-danger btn-small" data-action="remove-item" data-side="${side}" data-i="${i}">× Dalı sil</button></div>
 <div class="grid"><label>${noun} başlığı<input data-field="title" value="${safe(item.title)}" placeholder="${side==='threats'?'Örn. Korozyon nedeniyle boru delinmesi':'Örn. Personelin toksik maddeye maruziyeti'}"></label>
 <label>Açıklama / senaryo<textarea data-field="detail" placeholder="Kapsam ve gelişim koşulları">${safe(item.detail)}</textarea></label></div>
 <div class="subhead" style="margin-top:12px"><h4 style="margin:0;font-size:12px">${side==='threats'?'Önleyici':'Azaltıcı'} bariyerler</h4><span class="count">${item.barriers.length}</span></div>
 ${item.barriers.length?item.barriers.map((b,j)=>renderBarrier(b,side,i,j)).join(''):'<div class="empty">Henüz bariyer eklenmedi.</div>'}
 <div class="block-footer"><button type="button" class="btn btn-soft btn-small" data-action="add-barrier" data-side="${side}" data-i="${i}">+ Bariyer ekle</button></div>
 </article>`}
function renderEditors(){ $('threatCount').textContent=state.threats.length;$('consCount').textContent=state.consequences.length;
 $('threatEditor').innerHTML=state.threats.length?state.threats.map((x,i)=>renderBranchItem(x,'threats',i)).join(''):'<div class="empty">Tehdit / neden dalı ekleyerek başlayın.</div>';
 $('consequenceEditor').innerHTML=state.consequences.length?state.consequences.map((x,i)=>renderBranchItem(x,'consequences',i)).join(''):'<div class="empty">Olası sonuç dalı ekleyerek başlayın.</div>';
 document.querySelectorAll('.branch-editor input, .branch-editor textarea, .branch-editor select').forEach(el=>el.setAttribute('autocomplete','off'));
}
function barrierChip(b){const cls=isVerified(b)?'ok':b.status==='failed'?'bad':b.status==='weak'?'warn':'planned';return `<span class="diagram-chip ${cls}">${isVerified(b)?'✓':b.status==='failed'?'✕':b.status==='weak'?'!':'○'} ${safe(cleanName(b.name))} <small>(${safe(statuses[b.status]||'Bilinmiyor')}${b.verified?' · doğrulandı':''})</small></span>`}
function diagramCell(side,item,i){let threat=side==='threats',barriers=item?.barriers||[],name=item?.title||`${threat?'Tehdit':'Sonuç'} tanımlanmadı`,detail=item?.detail||'';
 return `<div class="dcell ${threat?'d-threat':'d-outcome'}" style="grid-column:${threat?1:5};grid-row:${i+1}"><strong>${safe(name)}</strong>${detail?`<small>${safe(detail).slice(0,260)}</small>`:''}</div><div class="dcell ${threat?'d-prevent':'d-mitigate'}" style="grid-column:${threat?2:4};grid-row:${i+1}">${barriers.length?barriers.map(barrierChip).join(''):'<span class="diagram-chip emptychip">! Bariyer tanımlanmadı</span>'}</div>`}
function diagramMarkup(){const count=Math.max(state.threats.length,state.consequences.length,1);const br=risk(state.scores.beforeP,state.scores.beforeS),ar=risk(state.scores.afterP,state.scores.afterS);
 let rows='';for(let i=0;i<count;i++){
  if(state.threats[i])rows+=diagramCell('threats',state.threats[i],i);else rows+=`<div class="dcell d-threat" style="grid-column:1;grid-row:${i+1};opacity:.35"><small>—</small></div><div class="dcell d-prevent" style="grid-column:2;grid-row:${i+1};opacity:.35"><small>—</small></div>`;
  if(state.consequences[i])rows+=diagramCell('consequences',state.consequences[i],i);else rows+=`<div class="dcell d-mitigate" style="grid-column:4;grid-row:${i+1};opacity:.35"><small>—</small></div><div class="dcell d-outcome" style="grid-column:5;grid-row:${i+1};opacity:.35"><small>—</small></div>`;
 }
 return `<div class="diagram-head"><span>1 · Tehditler / nedenler</span><span>2 · Önleyici bariyerler</span><span>3 · Kritik olay</span><span>4 · Azaltıcı bariyerler</span><span>5 · Sonuçlar</span></div>
 <div class="diagram-grid">${rows}<div class="dcell d-center" style="grid-column:3;grid-row:1 / span ${count}"><span class="hazard"><b>TEHLİKE</b><br>${safe(state.meta.hazard||'Tehlike tanımlanmadı')}</span><span class="event-chip">TOP EVENT · KONTROL KAYBI</span><strong>${safe(state.meta.topEvent||'Kritik olay tanımlanmadı')}</strong>${state.meta.criterion?`<small>${safe(state.meta.criterion).slice(0,230)}</small>`:''}<div class="center-risk">İlk risk: <b>${br.score??'—'}</b> · Artık risk: <b>${ar.score??'—'}</b></div></div></div>`;
}
function renderDiagram(){ $('diagramContent').innerHTML=diagramMarkup() }
function renderQuality(){const warn=[];if(!state.meta.hazard.trim())warn.push('Tehlike açıklaması eksik');if(!state.meta.topEvent.trim())warn.push('Kritik olay tanımı eksik');if(!state.threats.length)warn.push('Tehdit / neden eklenmedi');if(!state.consequences.length)warn.push('Olası sonuç eklenmedi');const t=state.threats.filter(x=>!x.barriers.some(isVerified)).length,c=state.consequences.filter(x=>!x.barriers.some(isVerified)).length;if(t)warn.push(`${t} tehdit dalında doğrulanmış etkin önleyici bariyer yok`);if(c)warn.push(`${c} sonuç dalında doğrulanmış etkin azaltıcı bariyer yok`);if(!state.scores.afterP||!state.scores.afterS)warn.push('Artık risk henüz değerlendirilmedi');$('quality').innerHTML=warn.length?warn.map(w=>`<span>! ${safe(w)}</span>`).join(''):'<span class="allgood">✓ Temel veri alanları ve bariyer doğrulama kayıtları tamam görünüyor; bağımsız teknik saha doğrulaması yine gereklidir.</span>'}
function renderActions(){const b=allBarriers();if(!b.length){$('actionTable').innerHTML='<div class="empty">İzlenecek bariyer bulunmuyor. Dallara bariyer ekleyin.</div>';return}
 $('actionTable').innerHTML=`<table><thead><tr><th>Sıra No.</th><th>Kanat / dal</th><th>Bariyer</th><th>Durum / doğrulama</th><th>Sorumlu</th><th>Aksiyon</th><th>Hedef tarih</th></tr></thead><tbody>${b.map((v,i)=>`<tr><td>${i+1}</td><td>${v.side==='threats'?'Önleyici':'Azaltıcı'}<br><span class="muted">${safe(cleanName(v.branch))}</span></td><td>${safe(cleanName(v.name))}</td><td><span class="status-tag status-${safe(v.status)}">${safe(statuses[v.status]||'Bilinmiyor')}</span><br>${isVerified(v)?'✓ Saha doğrulandı':'! Etkinlik doğrulanmadı'}</td><td>${safe(v.owner||'—')}</td><td>${safe(v.action||'—')}</td><td>${v.dueDate?safe(v.dueDate):'—'}${overdue(v)?'<br><span class="overdue">HEDEF TARİH GEÇTİ</span>':''}</td></tr>`).join('')}</tbody></table>`}
function renderAll(){for(const el of document.querySelectorAll('[data-meta]'))el.value=val(state.meta[el.dataset.meta]);for(const el of document.querySelectorAll('[data-score]'))el.value=val(state.scores[el.dataset.score]);renderEditors();refreshStats()}
function normalizeImport(obj){if(!obj||typeof obj!=='object'||obj.schema!=='risk-ui-bow-tie-v1')throw new Error('Risk UI Bow-Tie v1 biçiminde olmayan dosya.');const out=stateFactory();for(const k of META_KEYS)out.meta[k]=typeof obj.meta?.[k]==='string'?obj.meta[k].slice(0,5000):'';for(const k of SCORE_KEYS){const v=val(obj.scores?.[k]);out.scores[k]=/^[1-5]$/.test(v)?v:''}
 for(const s of ['threats','consequences']){if(!Array.isArray(obj[s])||obj[s].length>120)throw new Error('Dal bilgisi geçersiz veya aşırı uzun.');out[s]=obj[s].map(raw=>{if(!raw||!Array.isArray(raw.barriers)||raw.barriers.length>40)throw new Error('Bariyer yapısı geçersiz.');return {id:uid(),title:val(raw.title).slice(0,1000),detail:val(raw.detail).slice(0,2500),barriers:raw.barriers.map(a=>({id:uid(),name:val(a.name).slice(0,1000),status:statuses[a.status]?a.status:'planned',verified:a.verified===true,owner:val(a.owner).slice(0,500),checkedAt:/^\d{4}-\d{2}-\d{2}$/.test(a.checkedAt)?a.checkedAt:'',degrader:val(a.degrader).slice(0,2000),safeguard:val(a.safeguard).slice(0,2000),action:val(a.action).slice(0,2000),dueDate:/^\d{4}-\d{2}-\d{2}$/.test(a.dueDate)?a.dueDate:''}))}})}
 return out}
function confirmReplace(){return !state.threats.length&&!state.consequences.length&&!META_KEYS.some(k=>state.meta[k].trim())||window.confirm('Mevcut form içeriği Excel dosyasındaki kayıtlarla değiştirilecek. Önce Excel yedeği aldınız mı?')}
function initBindings(){document.addEventListener('input',event=>{const e=event.target;if(e.dataset.meta){state.meta[e.dataset.meta]=e.value;refreshStats();return}if(e.dataset.score){state.scores[e.dataset.score]=e.value;refreshStats();return}
 const parent=e.closest('.branch-item');if(!parent)return;const side=parent.dataset.side,i=Number(parent.dataset.i),b=state[side]?.[i];if(!b)return;if(e.dataset.field)b[e.dataset.field]=e.value;const bParent=e.closest('.barrier');if(bParent&&e.dataset.bfield){const bar=b.barriers[Number(bParent.dataset.j)];if(bar)bar[e.dataset.bfield]=e.type==='checkbox'?e.checked:e.value}refreshStats()});
 document.addEventListener('change',event=>{const e=event.target;if(e.matches('select[data-score],select[data-bfield],input[type=checkbox][data-bfield],input[type=date][data-bfield],input[type=date][data-meta]'))e.dispatchEvent(new Event('input',{bubbles:true}))});
 document.addEventListener('click',event=>{const btn=event.target.closest('button[data-action]');if(!btn)return;const {action,side}=btn.dataset;if(!['threats','consequences'].includes(side))return;const i=Number(btn.dataset.i),j=Number(btn.dataset.j);if(action==='add-item')state[side].push(newItem());else if(action==='remove-item'){if(!window.confirm('Seçilen dal ve ilişkili bütün bariyerler silinsin mi?'))return;state[side].splice(i,1)}else if(action==='add-barrier'){if(state[side][i])state[side][i].barriers.push(newBarrier())}else if(action==='remove-barrier'){if(!window.confirm('Bu bariyer silinsin mi?'))return;state[side][i]?.barriers.splice(j,1)}renderEditors();refreshStats()});
   $('importBtn').addEventListener('click',()=>$('fileIn').click());
  $('fileIn').addEventListener('change',event=>importExcel(event.target.files?.[0]));
  $('exportBtn').addEventListener('click',exportExcel);
  $('printBtn').addEventListener('click',printNow);
  window.addEventListener('beforeprint',preparePrint);
}
const asText=val;
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="Bow-Tie Bariyerleri" sheetId="1" r:id="rId1"/><sheet name="Analiz 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 ''}


/* Bow-Tie Excel: iç içe dal-bariyer ilişkilerini bozmayan iki sayfalı XLSX */
const LEGACY_EXCEL_HEADERS=['Kanat','Dal ID','Dal Başlığı','Dal Açıklaması','Bariyer ID','Bariyer Adı','Bariyer Durumu','Sahada Doğrulandı','Sorumlu / Birim','Son Kontrol Tarihi','Zayıflatıcı Etken','Koruyucu Önlem','Aksiyon / İyileştirme','Hedef Tarih'];
const EXCEL_HEADERS=['Sıra No',...LEGACY_EXCEL_HEADERS];
const META_LABELS={ref:'Analiz / Kayıt No.',company:'İş Yeri / Kuruluş',facility:'Tesis / Ünite / Proses',equipment:'Ekipman / Faaliyet',date:'Analiz Tarihi',preparer:'Hazırlayan / Ekip',approver:'Gözden Geçiren / Onaylayan',revision:'Revizyon / Sürüm',scope:'Kapsam / Varsayımlar',hazard:'Tehlike',topEvent:'Kritik Olay (Top Event)',criterion:'Kritik Olay Ölçütü',recovery:'Acil Durum / İyileşme'};
const SCORE_LABELS={beforeP:'Başlangıç Olasılık',beforeS:'Başlangıç Şiddet',afterP:'Artık Olasılık',afterS:'Artık Şiddet'};
function excelRows(){const rows=[EXCEL_HEADERS];let serial=0;for(const [side,arr,label] of [['threats',state.threats,'Tehdit'],['consequences',state.consequences,'Sonuç']])for(const branch of arr){const items=branch.barriers.length?branch.barriers:[null];for(const b of items)rows.push([++serial,label,branch.id,branch.title,branch.detail,b?.id||'',b?.name||'',b?statuses[b.status]||'Planlandı':'',b?(b.verified?'Evet':'Hayır'):'',b?.owner||'',b?.checkedAt||'',b?.degrader||'',b?.safeguard||'',b?.action||'',b?.dueDate||'']);}return rows;}
function reportRows(){return [['Alan','Değer'],['Şablon Sürümü','risk-ui-bow-tie-xlsx-v1'],['Yöntem','Bow-Tie (Papyon)'],...META_KEYS.map(k=>[META_LABELS[k],state.meta[k]]),...SCORE_KEYS.map(k=>[SCORE_LABELS[k],state.scores[k]]),['Tehdit Sayısı',state.threats.length],['Sonuç Sayısı',state.consequences.length],['Bariyer Sayısı',allBarriers().length],['Doğrulanmış Etkin Bariyer',allBarriers().filter(isVerified).length],['Not','Bu analiz uzman ve saha doğrulaması gerektirir.']];}
function downloadBlob(blob,name){const url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download=name;document.body.append(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),5000)}
function exportExcel(){try{downloadBlob(zipCreate(workbookFiles(excelRows(),reportRows())),`${slug()}_Bow_Tie_Risk_Analizi.xlsx`);toast('Tehdit, sonuç ve bariyerler Excel olarak dışarı aktarıldı.')}catch(e){console.error(e);alert('Excel dışarı aktarma başarısız: '+e.message)}}
function excelText(x,max=5000){return val(x).slice(0,max)}
async function readBowExcel(file){
 if(!/\.xlsx$/i.test(file.name))throw Error('Yalnızca .xlsx dosyaları desteklenir.');
 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());const strings=[];
 if(entries['xl/sharedStrings.xml']){const d=parseXml(entries['xl/sharedStrings.xml']);for(const item of d.getElementsByTagNameNS('*','si'))strings.push(Array.from(item.getElementsByTagNameNS('*','t')).map(n=>n.textContent).join(''))}
 const rows=parseXlsxSheet(entries['xl/worksheets/sheet1.xml'],strings),infos=parseXlsxSheet(entries['xl/worksheets/sheet2.xml'],strings);
 if(!rows.length)throw Error('Excel risk kaydı sayfası bulunamadı.');
 const cols=new Map(rows[0].map((v,i)=>[val(v).trim(),i]));if(LEGACY_EXCEL_HEADERS.some(k=>!cols.has(k)))throw Error('Excel, Bow-Tie şablonundaki sütunları içermiyor.');
 const info=new Map(infos.slice(1).map(r=>[val(r[0]),val(r[1])]));
 if(info.get('Şablon Sürümü')!=='risk-ui-bow-tie-xlsx-v1')throw Error('Excel dosyası Bow-Tie Risk UI biçiminde değil.');
 const result=stateFactory();for(const k of META_KEYS)result.meta[k]=excelText(info.get(META_LABELS[k])||'');
 for(const k of SCORE_KEYS){const v=val(info.get(SCORE_LABELS[k])||'');result.scores[k]=/^[1-5]$/.test(v)?v:''}
 const dataRows=rows.slice(1).filter(row=>row.some(x=>val(x).trim()));if(dataRows.length>10000)throw Error('En fazla 10.000 Excel satırı desteklenir.');
 const seen=new Map(),barrierSeen=new Map();let rowNo=1;
 for(const row of dataRows){rowNo++;const get=name=>excelText(row[cols.get(name)]??'');
  const sideValue=get('Kanat').trim().toLocaleLowerCase('tr-TR');const side=['tehdit','threats'].includes(sideValue)?'threats':['sonuç','sonuc','consequences'].includes(sideValue)?'consequences':null;
  if(!side)throw Error(rowNo+'. satırda Kanat değeri Tehdit veya Sonuç olmalıdır.');
  const rawId=get('Dal ID').trim(),nameKey=get('Dal Başlığı').trim(),detailKey=get('Dal Açıklaması').trim();
  // Dal ID tekrarı, bir dalın birden fazla bariyeri olduğunda gereklidir.
  // Dal ID boşsa aynı kanatta aynı başlık/açıklama tek dal olarak alınır.
  const branchKey=side+'|'+(rawId?'id:'+rawId:(nameKey||detailKey?'başlık:'+nameKey+'|'+detailKey:'satir-'+rowNo));
  let branch=seen.get(branchKey);
  if(!branch){if(result[side].length>=120)throw Error('Her kanat en fazla 120 dal içerebilir.');branch={...newItem(),title:get('Dal Başlığı').slice(0,1000),detail:get('Dal Açıklaması').slice(0,2500)};result[side].push(branch);seen.set(branchKey,branch)}
  else if(branch.title!==get('Dal Başlığı').slice(0,1000)||branch.detail!==get('Dal Açıklaması').slice(0,2500))throw Error(rowNo+'. satırda aynı Dal ID için farklı dal tanımı mevcut.');
  const name=get('Bariyer Adı'),bId=get('Bariyer ID');const meaningful=[bId,name,get('Bariyer Durumu'),get('Sorumlu / Birim'),get('Aksiyon / İyileştirme'),get('Zayıflatıcı Etken'),get('Koruyucu Önlem'),get('Son Kontrol Tarihi'),get('Hedef Tarih')].some(v=>v.trim());
  if(meaningful){const st=get('Bariyer Durumu');let status=Object.keys(statuses).find(k=>statuses[k]===st)|| (statuses[st]?st:'planned');
   const checkedAt=get('Son Kontrol Tarihi'),dueDate=get('Hedef Tarih');
   if(checkedAt&&!normalizeDate(checkedAt))throw Error(rowNo+'. satırda Son Kontrol Tarihi geçersiz.');
   if(dueDate&&!normalizeDate(dueDate))throw Error(rowNo+'. satırda Hedef Tarih geçersiz.');
   const barrier={...newBarrier(),name:name.slice(0,1000),status,verified:['evet','true','1'].includes(get('Sahada Doğrulandı').trim().toLocaleLowerCase('tr-TR')),owner:get('Sorumlu / Birim').slice(0,500),checkedAt:normalizeDate(checkedAt),degrader:get('Zayıflatıcı Etken').slice(0,2000),safeguard:get('Koruyucu Önlem').slice(0,2000),action:get('Aksiyon / İyileştirme').slice(0,2000),dueDate:normalizeDate(dueDate)};
   // Kaynağın Bariyer ID alanı eşsiz bir ilişkisel kimliktir, sıra numarası değildir.
   // Aynı kimliğin birebir yinelenen satırları tek bariyer alınır; çelişkili kopyalar reddedilir.
   if(bId.trim()){
    const key=branchKey+'|'+bId.trim();
    const signature=JSON.stringify(Object.fromEntries(Object.entries(barrier).filter(([k])=>k!=='id')));
    if(barrierSeen.has(key)){
     if(barrierSeen.get(key)!==signature)throw Error(rowNo+'. satırda aynı Bariyer ID için çelişen kayıt bulundu.');
     continue;
    }
    barrierSeen.set(key,signature);
   }
   if(branch.barriers.length>=40)throw Error('Her dal en fazla 40 bariyer içerebilir.');
   branch.barriers.push(barrier);
  }
 }
 return result;
}
async function importExcel(file){if(!file)return;try{const imported=await readBowExcel(file);if(!confirmReplace())return;state=imported;renderAll();toast('Excel içeri aktarıldı; yinelenen satırlar ayıklandı ve sıra numaraları 1’den başlatıldı.')}catch(err){console.error(err);alert('Excel içeri aktarma başarısız: '+err.message)}finally{$('fileIn').value=''}}

function csvClean(text){let s=val(text).replace(/\r?\n/g,' ');if(/^[\s]*[=+@\-]/.test(s))s="'"+s;return '"'+s.replace(/"/g,'""')+'"'}
function downloadCSV(){let rows=[['Kanat','Dal','Bariyer','Durum','Sahada doğrulandı','Sorumlu','Son kontrol','Zayıflatıcı etken','Koruyucu önlem','Aksiyon','Hedef tarihi','Gecikme']];for(const b of allBarriers())rows.push([b.side==='threats'?'Önleyici':'Azaltıcı',b.branch,b.name,statuses[b.status]||'',b.verified?'Evet':'Hayır',b.owner,b.checkedAt,b.degrader,b.safeguard,b.action,b.dueDate,overdue(b)?'Evet':'Hayır']);fileDownload(`${slug()}_Bariyer_Aksiyon.csv`,'\ufeff'+rows.map(r=>r.map(csvClean).join(';')).join('\r\n'),'text/csv;charset=utf-8');toast('Excel uyumlu CSV hazırlandı.')}
function pField(label,v){return `<div class="p-field"><b>${safe(label)}</b>${printVal(v)}</div>`}
function printLogo(){const img=$('logoImage');if(img&&!img.hidden&&img.complete&&img.naturalWidth)return img.currentSrc||img.src;return brand.logo&&/^data:image\//.test(brand.logo)?brand.logo:''}
function preparePrint(){
 const logo=printLogo();$('printHeader').innerHTML=`<div class="p-header">${logo?`<img src="${safe(logo)}" alt="Kurum logosu">`:'<span style="color:#70879c;font-weight:800">LOGO</span>'}<div><h1>${safe(brand.title)}</h1><p>${safe(brand.subtitle)}</p></div><span class="p-org">${safe(brand.org)}</span></div>`;
 const meta=`<div class="p-section"><h3>01 · Analiz / Tesis Bilgileri ve Kritik Olay</h3><div class="p-grid">${META_KEYS.map(k=>pField(META_LABELS[k],state.meta[k])).join('')}</div></div>`;
 const br=risk(state.scores.beforeP,state.scores.beforeS),ar=risk(state.scores.afterP,state.scores.afterS);
 const scores=`<div class="p-section"><h3>02 · Risk Değerlendirmesi ve Bariyer Güvencesi</h3><div class="p-risk"><div><b>BAŞLANGIÇ RİSKİ</b><div><strong>${br.score??'—'}</strong> · ${safe(br.desc)} (O:${state.scores.beforeP||'—'} × Ş:${state.scores.beforeS||'—'})</div></div><div><b>ARTIK RİSK</b><div><strong>${ar.score??'—'}</strong> · ${safe(ar.desc)} (O:${state.scores.afterP||'—'} × Ş:${state.scores.afterS||'—'})</div></div></div><div class="p-note">Tehdit: ${state.threats.length} · Sonuç: ${state.consequences.length} · Bariyer: ${allBarriers().length} · Doğrulanmış etkin: ${allBarriers().filter(isVerified).length} · Korumasız dal: ${gapBranches().length} · Geciken faaliyet: ${allBarriers().filter(overdue).length}</div></div>`;
 const diagram=`<div class="p-section"><h3>03 · Dinamik Bow-Tie / Papyon Diyagramı</h3><div class="p-diagram">${diagramMarkup()}</div><div class="p-note">Mavi: tehdit / neden · Yeşil: önleyici bariyer · Kırmızı: kritik olay · Mor: azaltıcı bariyer · Turuncu: sonuç. Bariyer etkinliği bağımsız saha doğrulaması gerektirir.</div></div>`;
 const branchRows=allBranches().flatMap(branch=>branch.barriers.length?branch.barriers.map(b=>({branch,barrier:b})):[{branch,barrier:null}]);
 const rows=branchRows.map(({branch,barrier:b},i)=>`<tr><td>${i+1}</td><td>${branch.side==='threats'?'Tehdit / önleme':'Sonuç / azaltma'}</td><td>${safe(branch.title||'—')}</td><td>${safe(b?.name||'—')}</td><td>${b?safe(statuses[b.status]||'Planlandı')+(isVerified(b)?' · Doğrulandı':' · Doğrulanmadı'):'—'}</td><td>${safe(b?.owner||'—')}</td><td>${safe(b?.action||'—')}</td><td>${safe(b?.dueDate||'—')}</td></tr>`).join('');
 const summary=`<div class="p-section"><h3>04 · Bariyer ve Aksiyon Kayıtları (${branchRows.length})</h3>${branchRows.length?`<table class="p-table"><colgroup><col style="width:4%"><col style="width:12%"><col style="width:16%"><col style="width:16%"><col style="width:17%"><col style="width:10%"><col style="width:17%"><col style="width:8%"></colgroup><thead><tr><th>No.</th><th>Kanat</th><th>Dal / Senaryo</th><th>Bariyer</th><th>Durum / Doğrulama</th><th>Sorumlu</th><th>Aksiyon</th><th>Hedef</th></tr></thead><tbody>${rows}</tbody></table>`:'<div class="p-note">Henüz dal kaydı bulunmuyor.</div>'}</div>`;
 const details=allBranches().map((b,i)=>{const pairs=[['Kanat',b.side==='threats'?'Önleyici':'Azaltıcı'],['Dal / Senaryo',b.title],['Ayrıntı',b.detail],['Bariyer Sayısı',b.barriers.length]];const bRows=b.barriers.flatMap((v,j)=>{const f=[['Bariyer Adı',v.name],['Durum / Doğrulama',(statuses[v.status]||'Planlandı')+' / '+(isVerified(v)?'Etkin / doğrulandı':'Doğrulanmadı')],['Sorumlu',v.owner],['Son Saha Kontrolü',v.checkedAt],['Zayıflatıcı Etken',v.degrader],['Koruyucu Önlem',v.safeguard],['Düzeltici Faaliyet / Aksiyon',v.action],['Hedef Tarih',v.dueDate]];return Array.from({length:Math.ceil(f.length/4)},(_,row)=>`<tr>${f.slice(row*4,row*4+4).map(([k,value])=>`<td>${pField(k,value)}</td>`).join('')}</tr>`)});return `<div class="p-detail"><table class="p-detail-table"><thead><tr><th colspan="4"><div class="p-detail-head">${i+1}. ${safe(b.side==='threats'?'TEHDİT / ÖNLEYİCİ':'SONUÇ / AZALTICI')} · ${safe(b.title||'İsimsiz dal')}</div></th></tr></thead><tbody><tr>${pairs.map(([k,v])=>`<td>${pField(k,v)}</td>`).join('')}</tr>${bRows.join('')}</tbody></table></div>`}).join('');
 $('printContent').innerHTML=meta+scores+diagram+summary+`<div class="p-section"><h3>05 · Dal / Bariyer Detayları</h3>${details||'<div class="p-note">Henüz kaydedilmiş dal yok.</div>'}</div>`;
}
async function printNow(){preparePrint();try{await document.fonts.ready}catch(e){}await new Promise(resolve=>requestAnimationFrame(()=>requestAnimationFrame(resolve)));window.print()}

function xmlEscape(s){return val(s).replace(/[&<>"']/g,ch=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&apos;'}[ch]))}
function wrapLines(s,max=24,limit=4){const text=val(s).replace(/\s+/g,' ').trim();if(!text)return ['—'];const words=text.split(' '),out=[];let part='';for(const w of words){if((part+' '+w).trim().length>max&&part){out.push(part);part=w}else part=(part+' '+w).trim()}if(part)out.push(part);if(out.length>limit){out.length=limit;out[limit-1]+='…'}return out}
function downloadSVG(){const n=Math.max(1,state.threats.length,state.consequences.length),w=1660,rowH=160,head=100,footer=35,h=head+n*rowH+footer;const cols=[20,340,660,1000,1320],widths=[295,295,315,295,315];let p=[`<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}" viewBox="0 0 ${w} ${h}"><rect width="100%" height="100%" fill="#fff"/><style>text{font-family:Segoe UI,Arial,sans-serif} .title{font-size:18px;font-weight:bold;fill:#075985}.label{font-size:15px;font-weight:700;fill:#234f66}.txt{font-size:15px;fill:#17283b}.small{font-size:12px;fill:#475569}</style>`];const labels=['TEHDİTLER / NEDENLER','ÖNLEYİCİ BARİYERLER','KRİTİK OLAY','AZALTICI BARİYERLER','SONUÇLAR'];p.push(`<text x="20" y="29" class="title">${xmlEscape(brand.title)}</text><text x="20" y="52" class="small">${xmlEscape(state.meta.ref||'Bow-Tie Risk UI')} · Önleme → Kritik olay → Etki azaltma</text>`);labels.forEach((l,i)=>{p.push(`<rect x="${cols[i]}" y="67" width="${widths[i]}" height="29" rx="4" fill="${i===2?'#ffe8e7':'#eaf5fc'}"/><text x="${cols[i]+10}" y="87" class="label">${l}</text>`)});
 function box(i,row,fill,lines,accent){let x=cols[i],y=head+row*rowH+6,bh=rowH-13;p.push(`<rect x="${x}" y="${y}" width="${widths[i]}" height="${bh}" rx="5" fill="${fill}" stroke="${accent}" stroke-width="1.6"/>`);let usable=lines.slice(0,7);usable.forEach((line,k)=>p.push(`<text x="${x+11}" y="${y+26+k*17}" class="${k===0?'txt':'small'}">${xmlEscape(line)}</text>`))}
 for(let i=0;i<n;i++){let t=state.threats[i],c=state.consequences[i];if(t){box(0,i,'#f6fbff',wrapLines(t.title,29,2).concat(wrapLines(t.detail,39,4)),'#7fb6d4');box(1,i,'#f5fcf8',t.barriers.length?t.barriers.flatMap(b=>wrapLines((isVerified(b)?'✓':'!')+' '+(b.name||'İsimsiz bariyer'),36,2)).slice(0,7):['! Bariyer tanımlanmadı'],'#92c5ac')}if(c){box(3,i,'#faf8ff',c.barriers.length?c.barriers.flatMap(b=>wrapLines((isVerified(b)?'✓':'!')+' '+(b.name||'İsimsiz bariyer'),36,2)).slice(0,7):['! Bariyer tanımlanmadı'],'#beb0da');box(4,i,'#fffaf5',wrapLines(c.title,29,2).concat(wrapLines(c.detail,39,4)),'#e4b37e')}
 const y=head+i*rowH+rowH/2;for(let ci=0;ci<4;ci++)p.push(`<path d="M ${cols[ci]+widths[ci]} ${y} H ${cols[ci+1]-9}" stroke="#7d9caf" stroke-width="2" fill="none"/><polygon points="${cols[ci+1]-9},${y-4} ${cols[ci+1]-1},${y} ${cols[ci+1]-9},${y+4}" fill="#7d9caf"/>`)}
 let centerY=head+n*rowH/2,centerLines=['TEHLİKE',...wrapLines(state.meta.hazard,35,3),'TOP EVENT',...wrapLines(state.meta.topEvent,35,3)];let cy=centerY-(centerLines.length*23)/2;p.push(`<rect x="${cols[2]}" y="${head+6}" width="${widths[2]}" height="${n*rowH-13}" rx="7" fill="#fff1f0" stroke="#d98a93" stroke-width="2"/>`);centerLines.forEach((line,k)=>p.push(`<text x="${cols[2]+widths[2]/2}" y="${cy+k*23}" class="${line==='TEHLİKE'||line==='TOP EVENT'?'label':'txt'}" text-anchor="middle">${xmlEscape(line)}</text>`));p.push(`<text x="20" y="${h-10}" class="small">Bilgi amaçlı diyagram · Bariyer etkinlikleri saha incelemesi ile doğrulanmalıdır.</text></svg>`);fileDownload(`${slug()}_Bow_Tie_Diyagram.svg`,p.join(''),'image/svg+xml;charset=utf-8');toast('Diyagram SVG dosyası hazırlandı.')}
function sampleData(){const s=stateFactory();Object.assign(s.meta,{ref:'ORNEK-BT-001',company:'TEMSiLi TESiS',facility:'Kimyasal proses ünitesi',equipment:'Basınçlı transfer hattı',date:today(),preparer:'Örnek analiz ekibi',revision:'R00',scope:'Temsili endüstriyel risk senaryosu; gerçek saha verileri değildir.',hazard:'Basınç altındaki tehlikeli kimyasal',topEvent:'Akışkanın kontrolsüz salımı',criterion:'Proses sınırının kaybı sonucunda ortam dışına akışkan çıkışı',recovery:'Alarm, kontrollü tahliye, izolasyon ve olay sonrası güvenli duruma alma.'});Object.assign(s.scores,{beforeP:'4',beforeS:'5',afterP:'2',afterS:'5'});
 function b(name,status='active',verified=true,action='',dueDate=''){return {...newBarrier(),name,status,verified,action,dueDate,owner:'Proses / bakım',checkedAt:verified?today():'',degrader:'Bakım ve test eksikliği',safeguard:'Periyodik kontrol / fonksiyon testi'}}
 s.threats=[{...newItem(),title:'Hat korozyonu ve et kalınlığı kaybı',detail:'Uzun süreli işletmede malzeme bütünlüğünün azalması',barriers:[b('Periyodik kalınlık ölçümü'),b('Korozyon izleme programı','weak',false,'İzleme aralığını yeniden belirle',today())]},{...newItem(),title:'Aşırı basınç',detail:'Proses set değeri dışına çıkılması',barriers:[b('Yüksek basınç alarmı'),b('Bağımsız koruma sistemi','planned',false,'Devreye alma ve fonksiyon testi')]}];
 s.consequences=[{...newItem(),title:'Çalışanların kimyasal maruziyeti',detail:'Salım yakınında personel etkilenmesi',barriers:[b('Gaz algılama ve alarm'),b('Acil tahliye prosedürü','active',false,'Tatbikat ve doğrulama') ]},{...newItem(),title:'Çevresel yayılım ve ekipman hasarı',detail:'Salımın saha dışına veya hassas ekipmana ulaşması',barriers:[b('Acil izolasyon vanası'),b('Sızıntı toplama / sınırlama','planned',false,'Saha uygunluk incelemesi')]}];return s}
function setupFiveClicks(el,cb){let n=0,prev=0;el.addEventListener('click',()=>{if(el.isContentEditable)return;let now=Date.now();n=now-prev<1100?n+1:1;prev=now;if(n>=5){n=0;cb()}})}
function saveBrand(){try{localStorage.setItem(BRAND_KEY,JSON.stringify(brand))}catch(e){toast('Görsel veya başlık tarayıcıda saklanamadı.')}}
function setLogo(){const img=$('logoImage'),fallback=$('logoFallback');img.onerror=()=>{img.hidden=true;fallback.hidden=false};img.onload=()=>{img.hidden=false;fallback.hidden=true};img.hidden=true;fallback.hidden=false;img.src=brand.logo||'logo.jpg'}
function editBrand(el,key){const before=el.textContent;el.contentEditable='true';el.setAttribute('role','textbox');el.focus();const r=document.createRange(),s=window.getSelection();r.selectNodeContents(el);s.removeAllRanges();s.addRange(r);let cancelled=false;function finish(){el.removeEventListener('blur',finish);el.removeEventListener('keydown',keyListener);el.contentEditable='false';const v=el.textContent.trim().slice(0,160);brand[key]=!cancelled&&v?v:before;el.textContent=brand[key];saveBrand();if(!cancelled)toast('Üst bilgi kaydedildi.')}function keyListener(e){if(e.key==='Enter'){e.preventDefault();el.blur()}else if(e.key==='Escape'){cancelled=true;el.blur()}}el.addEventListener('blur',finish);el.addEventListener('keydown',keyListener)}
function initBrand(){try{const stored=JSON.parse(localStorage.getItem(BRAND_KEY)||'{}');for(const k of Object.keys(BRAND_DEFAULT))if(typeof stored[k]==='string')brand[k]=stored[k]}catch(e){}$('brandTitle').textContent=brand.title;$('brandSubtitle').textContent=brand.subtitle;$('brandOrg').textContent=brand.org;setLogo();for(const [id,key] of [['brandTitle','title'],['brandSubtitle','subtitle'],['brandOrg','org']])setupFiveClicks($(id),()=>editBrand($(id),key));setupFiveClicks($('logoTrigger'),()=>$('logoFile').click());$('logoFile').addEventListener('change',e=>{const file=e.target.files?.[0];e.target.value='';if(!file)return;if(!/^image\/(jpeg|png|webp|gif)$/.test(file.type)||file.size>1_200_000){alert('PNG/JPEG/WEBP/GIF, en çok 1,2 MB görsel seçin.');return}const reader=new FileReader();reader.onload=()=>{brand.logo=String(reader.result||'');saveBrand();setLogo();toast('Kurum logosu güncellendi.')};reader.readAsDataURL(file)})}
// Sayfa her açıldığında / yenilendiğinde ve BFCache geçmişinden geri dönüldüğünde
// analiz verileri ve tarayıcı tarafından geri getirilen alan değerleri sıfırlanır.
// Kurumsal başlık ve logo ise kullanıcı tercihi olarak ayrı saklanmaya devam eder.
function resetAnalysisOnPageEntry(){
 state=stateFactory();
 $('fileIn').value='';
 $('printHeader').replaceChildren();$('printContent').replaceChildren();
 renderAll();
 document.querySelectorAll('#uiShell input:not([type=file]), #uiShell select, #uiShell textarea')
  .forEach(el=>el.setAttribute('autocomplete','off'));
}
initBrand();initBindings();resetAnalysisOnPageEntry();
window.addEventListener('pageshow',()=>{
 resetAnalysisOnPageEntry();
 // Bazı tarayıcılar form değerlerini pageshow sırasında veya hemen ardından geri yükler.
 requestAnimationFrame(resetAnalysisOnPageEntry);
});
</script>
</body>
</html>