İSG Risk Analizi - Fine_Kinney

İş sağlığı ve güvenliği (İSG) çalışmalarının temel amacı, işyerindeki tehlikeleri önceden belirlemek, oluşabilecek riskleri değerlendirmek ve iş kazalarını önlemeye yönelik tedbirleri hayata geçirmektir.

Özellikle fabrikalar, atölyeler ve üretim tesislerinde çalışanların makineler, ekipmanlar, kimyasal maddeler, elektrik sistemleri ve çeşitli üretim süreçleri nedeniyle karşılaşabileceği tehlikelerin sistematik olarak değerlendirilmesi büyük önem taşır.

Fine-Kinney Risk Analizi HTML Uygulaması, bu değerlendirmeleri dijital ortamda gerçekleştirmek için hazırlanmış, çevrimdışı çalışabilen dinamik bir risk değerlendirme ve aksiyon takip aracıdır.

Uygulama sayesinde riskler üç temel faktöre göre hesaplanır, öncelik sırasına göre değerlendirilir, alınacak tedbirler belirlenir ve sonuçlar Excel veya PDF olarak raporlanabilir.

1. Fine-Kinney Risk Analizi Nedir?

Fine-Kinney, işyerindeki riskleri değerlendirmek için kullanılan yarı nicel risk analizi yöntemlerinden biridir.

Bu yöntemde risk puanı üç temel faktörün çarpılmasıyla hesaplanır:
Risk Puanı = Olasılık × Frekans × Şiddet
Bu faktörler şu anlamlara gelir:

Olasılık (O): Belirlenen tehlikenin istenmeyen bir olaya veya zarara yol açma ihtimalidir.

Frekans (F): Çalışanların tehlikeye hangi sıklıkla maruz kaldığını ifade eder. Frekans, iş kazasının meydana gelme sıklığı değil, tehlikeli duruma maruziyet sıklığıdır.

Şiddet (Ş): Tehlikenin gerçekleşmesi durumunda ortaya çıkabilecek yaralanma, ölüm veya diğer kayıpların ciddiyetini ifade eder.

Fine-Kinney yönteminin önemli avantajı, olasılık ve şiddete ek olarak maruziyet sıklığını da değerlendirmeye dahil etmesidir.

Örneğin aynı makineyle çalışan iki personelin maruz kaldığı tehlike benzer olsa bile, birinin makineyi yılda birkaç kez, diğerinin ise her gün kullanması risk puanlarını farklılaştırabilir.

2. Fine-Kinney HTML Uygulaması Ne İşe Yarar?

Uygulama, risk değerlendirmesi sürecindeki hesaplama ve kayıt işlemlerini tek bir dijital form üzerinden yürütmeyi sağlar. Başlıca kullanım amaçları şunlardır:

  • İşyerindeki tehlikeleri ve olası zararları kayıt altına almak.
  • Olasılık, frekans ve şiddet değerlerinden otomatik risk puanı hesaplamak.
  • Mevcut risk ile önlem sonrası hedef riski karşılaştırmak.
  • Riskleri önem derecelerine göre sınıflandırmak.
  • Alınması gereken tedbirleri, sorumluları ve hedef tarihleri belirlemek.
  • Risk kayıtlarının ve aksiyon durumlarının takibini yapmak.
  • Birden fazla riskin genel dağılımını görüntülemek.
  • Risk değerlendirmelerini Excel ve PDF dosyalarına dönüştürmek.

Uygulama tek bir HTML dosyasıdır. İnternet bağlantısı gerektirmeden uygun bir web tarayıcısında çalıştırılabilir.

3. Fine-Kinney Risk Puanları Nasıl Değerlendirilir?

HTML uygulaması hesaplanan puanları beş risk seviyesinde sınıflandırır.

Risk puanıRisk seviyesi
20'nin altındaKabul Edilebilir
20 – 70'in altındaOlası Risk
70 – 200'ün altındaÖnemli Risk
200 – 400 (dahil)Yüksek Risk
400'ün üzerindeÇok Yüksek Risk

Uygulama, risk seviyelerini farklı renklerle göstererek öncelikli müdahale gerektiren tehlikelerin daha kolay fark edilmesini sağlar.

Örneğin çok yüksek risk seviyesinde bulunan tehlikeler için acil tedbirler değerlendirilirken, daha düşük seviyedeki riskler için mevcut kontrollerin sürdürülmesi ve izleme faaliyetleri planlanabilir.

Bu puan aralıkları uygulamanın kullandığı değerlendirme ölçeğidir. Kurumun onaylı risk değerlendirme metodolojisi ve yürürlükteki mevzuat hükümleri ayrıca dikkate alınmalıdır.

4. Fine-Kinney HTML Formu Nasıl Doldurulur?

Uygulama üç ana bölümden oluşmaktadır.

01 – İşyeri ve Değerlendirme Bilgileri

Bu bölümde risk analizinin hangi işyerine, tesise veya birime ait olduğu belirtilir.

İşyeri / Firma: İşletmenin ticari veya kurumsal adı yazılır.

Tesis / Birim: Risk değerlendirmesinin yapıldığı tesis veya birim belirtilir.

Hazırlayan / Ekip: Risk değerlendirme çalışmasını hazırlayan kişi veya ekip bilgileri girilir.

Değerlendirme Tarihi: Analizin gerçekleştirildiği tarih seçilir.

Revizyon / Belge No.: Risk değerlendirmesinin belge numarası ve revizyon bilgisi yazılır.

Bu bilgiler, hazırlanan risk değerlendirme raporunun tanımlanmasını ve izlenmesini sağlar.

02 – Yeni Risk Kaydı

Bu bölümde her tehlike için ayrı bir risk değerlendirmesi yapılır.

Bölüm / Alan: Tehlikenin bulunduğu çalışma alanı yazılır. Örneğin üretim atölyesi, bakım bölümü veya hammadde deposu.

Faaliyet / İş Adımı: Tehlikeye neden olabilecek faaliyet açıklanır. Örneğin pres makinesi kullanımı, kaynak işlemi veya forkliftle malzeme taşıma.

Etkilenebilecek Kişiler: Tehlikeden etkilenebilecek çalışanlar, bakım personeli, taşeronlar veya ziyaretçiler belirtilir.

Tehlike Kaynağı: Zarar oluşturma potansiyeli bulunan durum tanımlanır. Örneğin korumasız döner makine parçaları.

Risk / Olası Zarar: Tehlikenin gerçekleşmesi halinde ortaya çıkabilecek sonuç belirtilir. Örneğin el sıkışması, uzuv kaybı veya ciddi yaralanma.

Mevcut Kontroller / Önlemler: İşyerinde hâlihazırda uygulanan güvenlik önlemleri açıklanır.

Tehlike kaynağı ile olası zararın ayrı ayrı tanımlanması önemlidir. Örneğin “koruyucusuz makine” tehlike kaynağı, “elin hareketli parçaya sıkışması” ise bu tehlikeden doğabilecek risktir.

5. Mevcut Risk Puanı Nasıl Hesaplanır?

Tehlike tanımlandıktan sonra formun Mevcut Risk bölümündeki olasılık, frekans ve şiddet değerleri seçilir. Uygulamada kullanılan puan seçenekleri şöyledir:

FaktörKullanılan değerler
Olasılık0,1 – 0,2 – 0,5 – 1 – 3 – 6 – 10
Frekans0,5 – 1 – 2 – 3 – 6 – 10
Şiddet1 – 3 – 7 – 15 – 40 – 100

Her değerin yanında seçimi kolaylaştıracak açıklamalar bulunmaktadır. Üç faktör seçildiğinde uygulama çarpma işlemini otomatik gerçekleştirir ve risk seviyesini gösterir.

Örnek: Pres Makinesinde El Sıkışması Riski

Bir üretim tesisindeki pres makinesinde çalışan kişinin elini hareketli bölüme kaptırma riski değerlendirilsin.

Mevcut durum:

  • Olasılık: 6 – Kuvvetle muhtemel
  • Frekans: 6 – Günde bir veya birkaç kez maruziyet
  • Şiddet: 15 – Çok ciddi yaralanma

Risk Puanı = 6 × 6 × 15 = 540

<escape>Sonuç: 540 puan – Çok Yüksek Risk</escape>

Bu risk, acil olarak değerlendirilmesi gereken bir tehlikeye işaret eder. Risk puanının yüksek olması, gerekli tedbirlerin belirlenmesi ve bunların öncelikli olarak uygulanması açısından yol göstericidir.

6. Alınacak Tedbirler ve Aksiyon Takibi Nasıl Doldurulur?

Mevcut risk hesaplandıktan sonra riskin azaltılması için yapılacak çalışmalar belirlenir.

Alınacak Tedbirler / Düzeltici Faaliyet: Uygulanacak güvenlik önlemleri açıklanır. Örneğin makine koruyucusu takılması, güvenlik sensörü kurulması, erişim engelleyici sistemlerin kullanılması veya güvenli çalışma prosedürünün güncellenmesi.

Sorumlu Kişi / Birim: Tedbirin uygulanmasından sorumlu kişi veya bölüm belirtilir.

Hedef Tamamlanma Tarihi: Önlemin tamamlanması planlanan tarih seçilir.

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

Ek Açıklamalar / Doğrulama Kanıtı: Yapılan çalışmalar, kontrol sonuçları veya diğer kanıt bilgileri kaydedilir.

Bu bölüm, risk değerlendirmesinin yalnızca tehlikeleri belirleyen bir çalışma olmaktan çıkmasını ve alınacak önlemlerin takip edilebilmesini sağlar.

7. Önlem Sonrası Hedef Risk Nasıl Belirlenir?

Riskin azaltılması için planlanan önlemlerin ardından beklenen olasılık, frekans ve şiddet değerleri Hedef Risk bölümünden seçilir.

Örneğimizde uygun makine koruyucuları ve güvenlik sistemlerinin uygulanması sonucunda aşağıdaki seviyelere ulaşılması hedeflensin:

  • Olasılık: 1
  • Frekans: 2
  • Şiddet: 15

Hedef Risk Puanı = 1 × 2 × 15 = 30

<escape>Sonuç: 30 puan – Olası Risk</escape>

Bu örnekte mevcut 540 puanlık çok yüksek riskin, alınacak önlemler sonrasında 30 puana düşürülmesi hedeflenmektedir.

Burada dikkat edilmesi gereken önemli bir nokta vardır: Hedef risk ile doğrulanmış kalan risk aynı şey değildir.

Planlanan tedbirlerin uygulanmış olması tek başına yeterli değildir. Önlemlerin etkinliği ayrıca kontrol edilmelidir.

Uygulamada yer alan “Önlem uygulandı, etkinliği kontrol edildi ve kalan risk doğrulandı” seçeneği, ilgili aksiyon tamamlandıktan ve kontrol sonuçları doğrulandıktan sonra işaretlenmelidir.

Ayrıca alınan önlemler her zaman şiddeti azaltmayabilir. Örneğin bir makine kazasının muhtemel sonucu aynı kalırken kazanın gerçekleşme olasılığı düşebilir. Bu nedenle faktör değerleri gerçek çalışma koşullarına göre belirlenmelidir.

8. Tekil Risk ve Genel Risk Dağılımı Ne Anlama Gelir?

Uygulama, riskleri görsel olarak değerlendirmek için iki farklı görünüm sunmaktadır.

Tekil Risk
Bu görünüm, üzerinde çalışılan risk kaydının mevcut ve hedef risk seviyelerini birlikte gösterir.

Risk puanının hangi seviyede bulunduğu ve planlanan tedbirlerden sonra hangi seviyeye düşmesinin beklendiği anlaşılabilir. Böylece bir riskin mevcut durumu ile hedeflenen durumu kolaylıkla karşılaştırılabilir.

Genel Risk Dağılımı
Bu görünüm, uygulamaya kaydedilmiş bütün risklerin risk seviyelerine göre dağılımını gösterir.

Örneğin uygulamada toplam 15 risk kaydı varsa bu kayıtların kaçının Kabul Edilebilir, Olası, Önemli, Yüksek veya Çok Yüksek Risk seviyesinde bulunduğu görülebilir.

Genel Risk Dağılımı, risk puanlarının ortalamasını göstermez. Her risk seviyesinde bulunan mevcut risk kayıtlarının sayısını gösterir. Bu özellik, işyerinde hangi risk seviyelerinde yoğunlaşma olduğunu belirlemeyi ve iyileştirme çalışmalarını önceliklendirmeyi kolaylaştırır.

9. Risk Kayıtları ve Aksiyon Takibi Nasıl Kullanılır?

Form doldurulup Riski Kaydet butonuna basıldığında değerlendirme, 03 – Risk Kayıtları ve Aksiyon Takibi bölümüne eklenir.

Bu bölümde kayıtlı riskler listelenir. Gerekli durumlarda kayıtlar üzerinde düzenleme, silme ve takip işlemleri yapılabilir.

Her tehlike için ayrı risk kaydı oluşturulması önerilir. Böylece farklı faaliyetlere ait risk puanlarının ve alınacak tedbirlerin birbirine karışması önlenir.

Örneğin bir fabrikanın üretim, bakım, depo ve elektrik bölümlerine ait riskler aynı çalışma içerisinde ayrı kayıtlar halinde değerlendirilebilir.

10. Excel İçeri Aktar ve Excel Dışarı Aktar Nasıl Kullanılır?

Uygulamada hazırlanan risk değerlendirmelerini bilgisayara kaydetmek veya daha önce hazırlanmış kayıtları yeniden yüklemek için Excel özellikleri bulunmaktadır.

Excel İçeri Aktar: Uygulamayla uyumlu Excel dosyasındaki risk kayıtlarının forma yüklenmesini sağlar.

Excel Dışarı Aktar: Hazırlanan risk kayıtlarını ve değerlendirme bilgilerini Excel dosyası olarak kaydeder.

Bu özellik sayesinde kullanıcılar risk değerlendirme çalışmalarını yedekleyebilir, kayıtlarını daha sonra yeniden inceleyebilir ve farklı zamanlarda çalışmalarına devam edebilir.

Excel dosyasının formun beklediği alanlara ve veri düzenine uygun olması önemlidir.

11. PDF / Yazdır Özelliği Ne İşe Yarar?

Uygulamadaki PDF / Yazdır butonu, hazırlanan risk değerlendirme çalışmasının raporlanmasını sağlar.

Yazdırma işleminde kurumsal başlık, logo, değerlendirme bilgileri, risk kayıtları, mevcut ve hedef risk puanları ile aksiyon bilgileri rapora aktarılır.

Çok sayfalı çıktılarda kurumsal başlık tekrar edecek, dış çerçeve ve sayfa numaraları korunacak şekilde yazdırma düzeni hazırlanmıştır.

Tarayıcının yazdırma penceresinden PDF olarak kaydet seçeneği kullanılarak elektronik rapor oluşturulabilir.

Bu özellik, risk değerlendirmelerinin arşivlenmesini ve ilgili kişilerle paylaşılmasını kolaylaştırır.

12. Veriler Nasıl Saklanır?

Fine-Kinney HTML uygulaması, internet bağlantısına ihtiyaç duymadan yerel ortamda çalışacak şekilde hazırlanmıştır.

Uygulamanın önemli özelliklerinden biri, sayfa yenilendiğinde veya yeniden açıldığında doldurulabilir alanların boş gelmesidir.

Bu kapsamda:

  • İşyeri ve değerlendirme bilgileri boş başlar.
  • Tehlike ve risk tanımlama alanları boş gelir.
  • Olasılık, frekans ve şiddet değerleri önceden seçilmiş olmaz.
  • Risk kayıtları ve aksiyon takip listesi sıfır kayıtla açılır.
  • Logo ve düzenlenmiş kurumsal başlık bilgileri korunabilir.

Bu nedenle uygulama kapatılmadan veya sayfa yenilenmeden önce Excel Dışarı Aktar seçeneği kullanılarak mevcut çalışmaların yedeklenmesi önemlidir. Aksi durumda doldurulmuş form ve risk kayıtları kaybolur.

13. Fine-Kinney HTML Uygulamasını Kimler Kullanabilir?

Uygulama özellikle şu alanlarda kullanılmak üzere tasarlanmıştır:

  • Fabrikalar ve üretim tesisleri
  • Makine ve metal işleme atölyeleri
  • Bakım ve onarım birimleri
  • Depolar ve lojistik alanları
  • Elektrik ve mekanik bakım faaliyetleri
  • Kimyasal madde kullanılan çalışma alanları
  • Diğer endüstriyel faaliyetler

İş güvenliği uzmanları, işverenler, işyeri yöneticileri, bölüm sorumluları ve risk değerlendirme ekipleri tarafından kullanılabilir.

Ancak özel proses tehlikeleri, yangın ve patlama riskleri veya karmaşık mühendislik sistemleri için Fine-Kinney yönteminin tek başına yeterli olup olmadığı ayrıca değerlendirilmelidir.

14. Fine-Kinney HTML Uygulamasının Sağladığı Faydalar

  • Dijital risk değerlendirme uygulaması, manuel tablo hazırlama ve hesaplama işlemlerini kolaylaştırır.
  • Olasılık, frekans ve şiddet değerlerinden risk puanının otomatik hesaplanması işlem hatalarını azaltmaya yardımcı olur.
  • Mevcut ve hedef risklerin birlikte gösterilmesi, planlanan tedbirlerin beklenen etkisinin anlaşılmasını sağlar.
  • Genel Risk Dağılımı, işyerindeki risklerin hangi seviyelerde yoğunlaştığını gösterirken aksiyon takip bölümü, alınacak önlemlerin sorumlularıyla birlikte izlenmesini destekler.
  • Excel ve PDF özellikleri ise risk değerlendirmelerinin raporlanmasını, paylaşılmasını ve arşivlenmesini kolaylaştırır.

Sonuç

Fine-Kinney Risk Analizi HTML Uygulaması, işyerindeki tehlikeleri olasılık, frekans ve şiddet faktörleri üzerinden değerlendirmeye, riskleri önem derecesine göre sınıflandırmaya ve alınacak güvenlik tedbirlerini takip etmeye yardımcı olan dijital bir İSG aracıdır.

Özellikle fabrikalar, atölyeler ve üretim tesislerinde farklı faaliyetlere ait risklerin sistematik biçimde değerlendirilmesini destekler.

Uygulamanın temel avantajı, riskleri yalnızca puanlamakla kalmayıp mevcut riskleri, alınacak tedbirleri, sorumlu kişileri ve önlem sonrası hedef riskleri tek bir sistem üzerinden takip etmeyi sağlamasıdır.

İnternet bağlantısı olmadan çalışması, Excel ile veri aktarımı, PDF raporlama ve kullanımı kolay arayüzü sayesinde risk değerlendirme süreçlerinin daha düzenli, anlaşılır ve izlenebilir yürütülmesine katkı sağlar.

Önemli Hatırlatma: Bu uygulama bir risk değerlendirme ve kayıt aracıdır. Tek başına mevzuat kapsamında yapılması gereken bütün risk değerlendirme işlemlerinin yerine geçtiği anlamına gelmez. Risk puanları ve kontrol tedbirleri, işyerinin gerçek çalışma koşulları ve yetkili değerlendirme ekibinin tespitleri doğrultusunda belirlenmelidir.

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

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

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

Formun boş hali;

Formun dolu hali:

PDF / Yazdır

Dışarı Aktarılan Excel Verisi:


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

HTML Kod:
<!DOCTYPE html>
<html lang="tr">
<head>
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="theme-color" content="#f8fafc">
<title>İSG Risk Analizi - Fine_Kinney</title>
<style>
:root{--bg:#f8fafc;--surface:#fff;--ink:#17283b;--muted:#64748b;--line:#cbd5e1;--blue:#0284c7;--blue2:#0369a1;--light:#eaf5fc;--radius:6px}
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:#edf3f8;color:var(--ink);font:13px/1.42 'Segoe UI',Arial,sans-serif}button,input,textarea,select{font:inherit}button{cursor:pointer}input,textarea,select{width:100%;border:1px solid #cbd5e1;background:#fff;border-radius:5px;min-height:35px;padding:7px 9px;outline:none;color:#1e293b}textarea{min-height:62px;resize:vertical}input:focus,select:focus,textarea:focus{border-color:#0284c7;box-shadow:0 0 0 2px #0284c71c}label{display:block;font-size:11.5px;font-weight:650;color:#334155;margin:0 0 4px}.required:after{content:' *';color:#bd4046}.muted,.hint{font-size:11px;color:#64748b}.olgun-frame{margin:8px;border:2px solid #7f95a9;border-radius:7px;background:#f8fafc;min-height:calc(100vh - 16px);box-shadow:0 5px 19px #223d5511}.top-header{position:sticky;top:0;background:#fff;z-index:30;border-top:4px solid #0284c7;border-bottom:1px solid #bfd0de;border-radius:4px 4px 0 0}.headerinner{min-height:76px;display:flex;align-items:center;gap:13px;padding:9px 19px}.brand-logo{width:62px;height:55px;display:grid;place-items:center;flex:none;background:#f8fafc;border:1px solid #cbd5e1;border-radius:5px;padding:3px;font-weight:800;color:#075985;font-size:12px}.brand-logo img{width:100%;height:100%;object-fit:contain}.header-titles{min-width:0;flex:1}.header-titles h1{margin:0;font-size:18px;color:#075985;font-weight:750;line-height:1.2}.header-titles p{margin:4px 0 0;color:#475569;font-size:12px}.brand-org{max-width:40%;text-align:right;margin-left:auto;font-size:16px;font-weight:750;white-space:nowrap;color:#1e3a57;overflow:hidden;text-overflow:ellipsis}.top-header [contenteditable=true]{outline:2px dashed #0284c7;outline-offset:3px;white-space:normal}.wrap{padding:12px 17px 7px;display:grid;gap:10px}.metrics{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.metric{background:#fff;border:1px solid #cbd5e1;border-top:3px solid var(--blue);border-radius:6px;padding:10px 13px}.metric small{font-size:10px;display:block;color:#64748b;font-weight:700}.metric b{display:block;margin-top:3px;color:#0f3652;font-size:24px;line-height:1.18}.card{background:#fff;border:1px solid var(--line);border-radius:6px;box-shadow:0 2px 8px #25425b08;min-width:0}.card-header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:9px 12px;background:#eaf5fc;border-bottom:1px solid #b9d4e6;border-left:4px solid #0284c7;border-radius:5px 5px 0 0}.card-header h2{margin:0;display:flex;align-items:center;gap:7px;color:#084c71;font-size:14px}.sect{font-size:10px;background:#d2eafb;border-radius:4px;padding:3px 6px;color:#075985}.card-body{padding:11px 13px}.grid{display:grid;gap:10px}.g5{grid-template-columns:repeat(5,minmax(0,1fr))}.g4{grid-template-columns:repeat(4,minmax(0,1fr))}.g3{grid-template-columns:repeat(3,minmax(0,1fr))}.g2{grid-template-columns:repeat(2,minmax(0,1fr))}.span2{grid-column:span 2}.span3{grid-column:span 3}.strip{margin:11px 0 8px;display:flex;align-items:center;gap:8px;color:#075985;font-size:12px;font-weight:800;border-bottom:1px solid #dcecf5;padding:0 0 6px}.strip:first-child{margin-top:0}.strip em{display:grid;place-items:center;font-style:normal;width:22px;height:22px;background:#eaf6fc;border:1px solid #b8daed;border-radius:4px;font-size:10px;color:#0369a1}.method-lock{min-height:35px;background:#eef8ff;border:1px solid #b9def7;border-radius:5px;color:#075985;font-weight:750;padding:8px 10px;font-size:12px;display:flex;justify-content:space-between;gap:5px}.method-lock span{font-size:10px;font-weight:500;color:#64748b}.assessment{display:grid;grid-template-columns: minmax(340px,0.88fr) minmax(480px,1.12fr);gap:12px}.score-stack{display:grid;grid-template-columns:1fr;gap:8px}.score-panel{border:1px solid #cbd5e1;border-radius:6px;overflow:hidden;background:#fff;min-width:0}.score-panel[data-side=before]{border-top:3px solid #125ba9}.score-panel[data-side=after]{border-top:3px solid #8651b4}.score-head{background:#f2f7fb;padding:7px 9px;display:flex;justify-content:space-between;align-items:center;gap:8px;border-bottom:1px solid #e3eaf0;font-size:12px}.score-head i{display:inline-block;width:9px;height:9px;background:#125ba9;margin-right:5px;border-radius:50%}.score-panel[data-side=after] .score-head i{background:#8651b4;border-radius:2px}.factor-fields{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:7px;padding:8px}.score-result{border-top:1px solid #e8edf1;background:#f7fafc;display:flex;align-items:center;justify-content:space-between;gap:5px;padding:6px 9px}.score-result b{font-size:20px;line-height:1.1}.score-result small{display:block;font-size:10px;color:#64748b}.risk-tag{display:inline-block;font-size:10px;font-weight:800;padding:4px 7px;border-radius:4px;border:1px solid transparent;text-align:center}.risk-low{background:#ddf7e4;border-color:#a7e0bc;color:#166534}.risk-possible{background:#fff8df;border-color:#edd994;color:#805b14}.risk-important{background:#ffedbd;border-color:#ebc367;color:#865408}.risk-high{background:#ffe0c5;border-color:#e5a074;color:#9e4512}.risk-critical{background:#ffdee2;border-color:#ec9eaa;color:#a51d30}.risk-na{background:#edf2f7;border-color:#ced6e0;color:#64748b}.guide{background:#fff;border:1px solid #aac6d7;border-radius:6px;overflow:hidden;min-width:0}.guide-head{display:flex;align-items:center;justify-content:space-between;gap:9px;padding:9px 11px;background:#e9f4fc;border-bottom:1px solid #cbdde8}.guide-head strong{font-size:12px;color:#0b4669}.mode-tabs{display:flex;gap:5px;flex-wrap:wrap}.mode-btn{border:1px solid #bfd6e7;background:#fff;color:#315d76;padding:5px 8px;border-radius:5px;min-height:30px;font-weight:750;font-size:11px}.mode-btn.active{background:#dcedfb;color:#075985;border-color:#73acd0}.guide-body{padding:11px 12px}.bands{display:grid;gap:6px}.band{display:grid;grid-template-columns:100px minmax(30px,1fr) 102px;align-items:center;gap:7px}.band-name{font-weight:700;font-size:11px;white-space:nowrap}.band-bar{background:#f0f4f8;border:1px solid #dfe7ee;border-radius:5px;height:26px;position:relative;overflow:hidden}.band-fill{height:100%;border-radius:3px;width:100%;opacity:.88;transition:width .18s}.band[data-band=low] .band-fill{background:#73cf93}.band[data-band=possible] .band-fill{background:#eed572}.band[data-band=important] .band-fill{background:#f7b359}.band[data-band=high] .band-fill{background:#f28b59}.band[data-band=critical] .band-fill{background:#e97575}.band-count{font-weight:750;color:#334155;font-size:11px;text-align:right;white-space:nowrap}.band[data-current=yes] .band-bar{outline:2px solid #125ba9;outline-offset:1px}.band[data-target=yes] .band-bar{box-shadow:0 0 0 3px #a763c1 inset}.guide-summary{margin-top:10px;padding:8px 9px;background:#f8fafc;border:1px solid #e2e8f0;border-radius:5px;color:#475569;font-size:11px;min-height:32px}.legend{display:flex;gap:7px;flex-wrap:wrap;margin-top:9px}.legend span{font-size:10px;color:#475569}.legend b{font-size:10px;color:#334155}.instruction{color:#536b7d;font-size:11px;margin-top:8px}.verifiedbox{margin-top:10px;background:#f7fbfd;border:1px solid #d9e7ef;padding:9px 10px;border-radius:5px}.checkline{display:flex;gap:8px;align-items:center;font-size:11.5px;color:#334155}.checkline input{width:16px;min-height:0;height:16px;accent-color:#0284c7}.actionbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-top:12px}.spacer{flex:1}.btn{border:1px solid #cbd5e1;border-radius:5px;padding:7px 10px;min-height:34px;background:#fff;color:#334155;display:inline-flex;align-items:center;justify-content:center;gap:5px;font-weight:650;white-space:nowrap}.btn:hover{background:#f1f5f9}.btn-primary{background:#0284c7;color:#fff;border-color:#0284c7}.btn-primary:hover{background:#0369a1}.btn-sm{min-height:27px;padding:4px 7px;font-size:11px}.btn-danger{color:#b42332;border-color:#f0bac3}.btn-softblue{background:#e8f4ff;border-color:#b7d8fb;color:#075985}.btn-softgreen{background:#eaf8ef;border-color:#b9e2c6;color:#166534}.btn-softgray{background:#f1f3f5;border-color:#d6dbe1;color:#334155}.filters{display:flex;gap:7px;flex-wrap:wrap}.filters input{width:190px}.filters select{width:155px}.tablebox{overflow-x:auto}.tablebox table{border-collapse:collapse;width:100%;min-width:1120px}.tablebox th{background:#eef5f9;color:#475569;font-weight:750;font-size:11px;text-align:left;padding:8px 7px;border-bottom:1px solid #d6e2ea;white-space:nowrap}.tablebox td{font-size:11px;padding:8px 7px;border-bottom:1px solid #e9eff4;vertical-align:top;overflow-wrap:anywhere}.tablebox tbody tr:hover{background:#f7fbfe}.cellsub{font-size:10px;color:#64748b;margin-top:3px}.ellips{max-width:235px;white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}.empty{text-align:center;padding:21px 10px;color:#64748b;font-size:12px}.table-bottom{padding:8px 12px}.bottom-actions{padding:4px 17px 5px;display:flex}.bottom-panel{display:flex;gap:9px;flex-wrap:wrap;background:#fff;border:1px solid #b9cbd9;padding:9px 11px;border-radius:6px}.risk-ui-footer{margin:0 17px 10px;padding:7px 0 5px;border-top:1px solid #dbe3eb;color:#475569;font-size:11px;white-space:nowrap;overflow:auto}.sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap}.notice{position:fixed;bottom:18px;right:18px;z-index:80;background:#143d54;color:white;padding:10px 13px;border-radius:6px;max-width:420px;box-shadow:0 5px 18px #12374c30;font-size:12px;opacity:0;pointer-events:none;transition:opacity .15s}.notice.show{opacity:1}#printPages,#printPageFrame{display:none}
@media(max-width:1080px){.assessment{grid-template-columns:1fr}.score-stack{grid-template-columns:repeat(2,minmax(0,1fr))}.g5{grid-template-columns:repeat(3,minmax(0,1fr))}.g4{grid-template-columns:repeat(2,minmax(0,1fr))}.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media(max-width:720px){.olgun-frame{margin:3px}.headerinner{padding:8px 10px;flex-wrap:wrap}.brand-logo{width:49px;height:49px}.header-titles h1{font-size:15px}.header-titles p{font-size:10px}.brand-org{max-width:100%;width:100%;font-size:11px;white-space:normal}.wrap{padding:9px;gap:9px}.card-header{flex-wrap:wrap}.g5,.g4,.g3,.g2{grid-template-columns:1fr}.span2,.span3{grid-column:auto}.score-stack{grid-template-columns:1fr}.factor-fields{grid-template-columns:1fr 1fr 1fr}.band{grid-template-columns:83px 1fr 75px}.mode-btn{font-size:10px}.filters{width:100%}.filters input,.filters select{flex:1;width:100%;min-width:120px}.bottom-actions{padding:6px 9px}.bottom-panel{width:100%;gap:5px}.bottom-panel .btn{flex:1 1 120px;white-space:normal}.risk-ui-footer{margin:0 9px 8px;font-size:10px}}
@media(max-width:420px){.factor-fields{grid-template-columns:1fr}.band{grid-template-columns:87px 1fr 65px}.band-name,.band-count{font-size:10px}}
@page{size:A4 landscape;margin:9mm 9mm 16mm 9mm;@bottom-right{content:'Sayfa ' counter(page) ' / ' counter(pages);font:9pt Arial;color:#34495c}}
@media print{
 html,body{margin:0!important;padding:0!important;background:#fff!important;-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important;font-size:9px!important}
 #olgunFrame,#toast{display:none!important}
 #printPages{display:table!important;width:100%!important;border:0!important;border-spacing:0;table-layout:fixed;border-collapse:separate}
 #printPages>thead{display:table-header-group!important}
 #printPages>tbody{display:table-row-group!important}
 #printPages>thead>tr>td{padding:0 0 8px;border:0}
 #printPages>tbody>tr>td{padding:0 10px 8px;border:0}
 #printPageFrame{display:block!important;position:fixed;inset:0;border:1.5px solid #7f95a9;pointer-events:none;z-index:50}
 .print-header{display:flex;align-items:center;gap:10px;padding:6px 12px;border-top:4px solid #0284c7;border-bottom:1.5px solid #7f95a9;background:white;height:65px}
 .print-header img{width:50px;height:47px;object-fit:contain}
 .print-header h1{font-size:17px;color:#075985;margin:0}.print-header p{font-size:10px;color:#475569;margin:3px 0 0}
 .print-header .print-org{margin-left:auto;font-size:14px;color:#1e3a57;font-weight:700;text-align:right}
 .print-section{border:1px solid #b5c9d8;border-radius:4px;margin-bottom:8px;break-inside:auto}
 .print-section h3{font-size:11px;background:#eaf5fc;border-left:4px solid #0284c7;color:#075985;margin:0;padding:5px 8px;break-after:avoid}
 .print-summary{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:5px;padding:7px}
 .print-summary div{border:1px solid #dbe3eb;padding:5px;font-size:9px}
 .print-summary b{display:block;color:#075985;font-size:13px}
 .print-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:6px;padding:8px}
 .print-field{border:1px solid #dbe3eb;border-radius:3px;min-height:23px;padding:4px 6px;overflow-wrap:anywhere;white-space:pre-wrap;font-size:9px}
 .print-field b{display:block;font-size:8px;color:#64748b;font-weight:600;margin-bottom:2px}
 .print-table{border-collapse:collapse;width:100%;table-layout:fixed}.print-table th,.print-table td{border:1px solid #dbe3eb;padding:4px 5px;font-size:8px;vertical-align:top;overflow-wrap:anywhere}.print-table th{background:#eaf5fc;color:#075985}.print-table thead{display:table-header-group}.print-table tr{break-inside:avoid}.print-note{font-size:9px;color:#64748b;padding:6px 8px}.print-no-break{break-inside:avoid}
}

/* RISK UI - yazdırılabilir, gerçek SVG vektör grafikler. Zemin grafiği tercihi gerektirmez. */
@media print {
 .print-chart-section{break-inside:avoid!important;page-break-inside:avoid!important}
 .print-chart-layout{display:grid!important;grid-template-columns:1fr 1fr!important;gap:9px!important;padding:8px!important;align-items:start!important}
 .print-chart-box{min-width:0;border:1px solid #cbd5e1;border-radius:4px;padding:5px 7px 7px;break-inside:avoid!important;page-break-inside:avoid!important;background:white!important}
 .print-chart-box h4{margin:0 0 3px;font-size:10px;color:#075985}
 .print-chart-box svg{display:block;width:100%;height:auto;max-height:132px;overflow:visible}
 .print-chart-foot{font-size:9px;color:#425567;line-height:1.35;min-height:16px;margin:2px 2px 0}
 .print-chart-key{display:flex;flex-wrap:wrap;gap:7px;margin-top:4px;color:#334155;font-size:8.5px}
 .print-chart-key span{display:inline-flex;gap:3px;align-items:center}
 .print-chart-key .mark{display:inline-block;width:9px;height:9px;border:2px solid #125ba9}
 .print-chart-key .mark.target{border:2px dashed #8651b4}
 .print-chart-section,.print-chart-section *{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
}


/* Tek satır nihai altbilgi: başka satır veya aşağıda boşluk yok. */
.risk-ui-footer{margin:0 17px 0!important;padding:5px 0 5px!important;border-top:1px solid #dbe3eb;white-space:nowrap!important;overflow-x:auto;overflow-y:hidden;box-sizing:border-box;line-height:1.4!important;}
.risk-ui-footer-single-line{display:inline-block;white-space:nowrap!important;font-size:11px;font-weight:500;letter-spacing:-.08px;}
@media(min-width:721px) and (max-width:1100px){.risk-ui-footer-single-line{font-size:10px}}
@media(max-width:720px){.risk-ui-footer{margin:0 9px 0!important;padding:4px 0 4px!important}.risk-ui-footer-single-line{font-size:10px}}
@media print{.risk-ui-footer{display:none!important}}


/* Yazdırmada her sayfanın başlığında resim veya LOGO yedeği */
@media print{
 .print-logo-box{width:54px;height:50px;display:grid;place-items:center;flex:0 0 54px;min-width:54px;overflow:hidden}
 .print-logo-box img{display:block;max-width:100%;max-height:100%;width:auto;height:auto;object-fit:contain}
 .print-logo-box img[hidden],.print-logo-box b[hidden]{display:none!important}
 .print-logo-box b{font-size:11px;color:#075985}
}
</style>
</head>
<body>
<div id="olgunFrame" class="olgun-frame">
<header class="top-header"><div class="headerinner">
<button id="logoTrigger" class="brand-logo" type="button" title="Logoyu değiştirmek için art arda 5 kez tıklayın"><img id="brandLogo" alt="Kurum logosu" hidden><span id="logoFallback">LOGO</span></button>
<div class="header-titles"><h1 id="brandTitle" title="Düzenlemek için 5 kez tıklayın">İSG RİSK ANALİZİ – FINE-KINNEY</h1><p id="brandSubtitle" title="Düzenlemek için 5 kez tıklayın">(Olasılık × Frekans × Şiddet · Risk Değerlendirme ve Aksiyon Takip Formu)</p></div>
<div id="brandOrg" class="brand-org" title="Düzenlemek için 5 kez tıklayın">OLGUN BAŞKANLIĞI</div>
</div><input id="logoChooser" type="file" class="sr-only" accept="image/png,image/jpeg,image/webp,image/gif,image/svg+xml"></header>
<main class="wrap" id="app">
<section class="metrics" aria-label="Risk özetleri">
<div class="metric"><small>TOPLAM RİSK KAYDI</small><b id="statTotal">0</b></div>
<div class="metric"><small>YÜKSEK / ÇOK YÜKSEK · AÇIK</small><b id="statHigh">0</b></div>
<div class="metric"><small>HEDEF TARİHİ GEÇEN</small><b id="statOverdue">0</b></div>
<div class="metric"><small>ÖNLEMİ DOĞRULANAN</small><b id="statVerified">0</b></div>
</section>
<section class="card" aria-labelledby="metaTitle"><div class="card-header"><h2 id="metaTitle"><span class="sect">01</span> İşyeri ve Değerlendirme Bilgileri</h2><span class="hint">Giriş alanları her yeni açılışta boş başlar.</span></div>
<div class="card-body grid g5">
<div><label for="metaCompany">İşyeri / Firma</label><input data-meta="company" id="metaCompany" placeholder="Örn. ABC Metal Sanayi"></div>
<div><label for="metaFacility">Tesis / Birim</label><input data-meta="facility" id="metaFacility" placeholder="Örn. Üretim Tesisi"></div>
<div><label for="metaPrepared">Hazırlayan / Ekip</label><input data-meta="prepared" id="metaPrepared" placeholder="Risk değerlendirme ekibi"></div>
<div><label for="metaDate">Değerlendirme Tarihi</label><input data-meta="date" id="metaDate" type="date"></div>
<div><label for="metaRevision">Revizyon / Belge No.</label><input data-meta="revision" id="metaRevision" placeholder="Örn. FK-2026 / Rev.00"></div>
</div></section>
<section class="card" aria-labelledby="formTitle"><div class="card-header"><h2 id="formTitle"><span class="sect">02</span> <span id="formTitleText">Yeni Risk Kaydı</span></h2><button class="btn btn-sm" id="newBtn" type="button">+ Yeni Kayıt</button></div>
<div class="card-body"><form id="riskForm" novalidate autocomplete="off">
<div class="strip"><em>01</em> Riskin Tanımlanması</div>
<div class="grid g4">
<div><label>Değerlendirme Yöntemi</label><div class="method-lock">Fine-Kinney <span>O × F × Ş</span></div></div>
<div><label for="unit" class="required">Bölüm / Alan</label><input id="unit" required placeholder="Örn. Pres Atölyesi"></div>
<div><label for="activity" class="required">Faaliyet / İş Adımı</label><input id="activity" required placeholder="Örn. Pres kalıbı değişimi"></div>
<div><label for="affected">Etkilenebilecek Kişiler</label><input id="affected" placeholder="Operatör, bakım personeli..."></div>
</div>
<div class="grid g3" style="margin-top:9px">
<div><label class="required" for="hazard">Tehlike Kaynağı</label><textarea required id="hazard" placeholder="Korumasız hareketli parçalar"></textarea></div>
<div><label class="required" for="risk">Risk / Olası Zarar</label><textarea required id="risk" placeholder="Sıkışma, uzuv yaralanması"></textarea></div>
<div><label for="existing">Mevcut Kontroller / Önlemler</label><textarea id="existing" placeholder="Mevcut makine koruyucuları, talimatlar..."></textarea></div>
</div>
<div class="strip"><em>02</em> Fine-Kinney Risk Puanlaması – Olasılık × Frekans × Şiddet</div>
<div class="assessment">
<div class="score-stack">
<section class="score-panel" data-side="before"><div class="score-head"><strong><i></i> MEVCUT RİSK</strong><span class="hint">Tedbir öncesi</span></div><div class="factor-fields" id="beforeFields"></div><div class="score-result"><div><small>Mevcut risk puanı</small><b id="beforeScore">—</b></div><span class="risk-tag risk-na" id="beforeLevel">Değerlendirilmedi</span></div></section>
<section class="score-panel" data-side="after"><div class="score-head"><strong><i></i> HEDEF RİSK</strong><span class="hint" id="afterTitle">Planlanan seviye</span></div><div class="factor-fields" id="afterFields"></div><div class="score-result"><div><small>Hedef / kalan risk puanı</small><b id="afterScore">—</b></div><span class="risk-tag risk-na" id="afterLevel">Değerlendirilmedi</span></div></section>
</div>
<section class="guide" aria-labelledby="guideTitle"><div class="guide-head"><strong id="guideTitle">▦ Fine-Kinney Risk Ölçeği</strong><div class="mode-tabs" role="group" aria-label="Risk görünümü"><button type="button" id="modeSingle" class="mode-btn active" aria-pressed="true">Tekil Risk</button><button type="button" id="modeDistribution" class="mode-btn" aria-pressed="false">Genel Risk Dağılımı</button></div></div>
<div class="guide-body"><div id="bandVisual" class="bands" aria-label="Fine Kinney risk düzeyleri"></div><div id="guideSummary" class="guide-summary" aria-live="polite">Olasılık, frekans ve şiddet seçildiğinde sonuç burada görüntülenir.</div><div class="legend"><span><b style="color:#125ba9">●</b> Mevcut risk</span><span><b style="color:#8651b4">◆</b> Hedef risk</span></div><div class="instruction">Risk sınırları: 20, 70, 200 ve 400 puan. Sınır değerleri için sırasıyla üst kategori kullanılır; 400 puan yüksek risk kapsamındadır. Farklı kurumsal sınıflamalar bulunabilir.</div></div></section>
</div>
<div class="strip"><em>03</em> Tedbirler ve Aksiyon Takibi</div>
<div class="grid g4">
<div class="span2"><label for="action" class="required">Alınacak Tedbirler / Düzeltici Faaliyet</label><textarea id="action" required placeholder="Tehlikeyi ortadan kaldırma, mühendislik kontrolü..."></textarea></div>
<div><label for="responsible">Sorumlu Kişi / Birim</label><input id="responsible" placeholder="Örn. Bakım Müdürü"></div>
<div><label for="deadline">Hedef Tamamlanma Tarihi</label><input id="deadline" type="date"></div>
<div><label for="status">Aksiyon Durumu</label><select id="status"><option value="">Seçiniz</option><option value="open">Açık</option><option value="inprogress">Devam Ediyor</option><option value="done">Tamamlandı</option></select></div>
<div class="span3"><label for="notes">Ek Açıklamalar / Doğrulama Kanıtı</label><input id="notes" placeholder="Ölçüm, fotoğraf veya kontrol tutanağı açıklaması..."></div>
</div>
<div class="verifiedbox"><label class="checkline"><input id="verified" type="checkbox"> Önlem uygulandı, etkinliği kontrol edildi ve <b>kalan risk doğrulandı</b></label><div class="hint">Yalnızca “Tamamlandı” durumunda kullanılabilir. İşaretli değilse hedef puan gerçekleşmiş sonuç değildir.</div></div>
<div class="actionbar"><button type="submit" id="saveBtn" class="btn btn-primary">+ Riski Kaydet</button><button type="button" id="cancelBtn" class="btn">Vazgeç / Yeni Kayıt</button><span class="spacer"></span><span class="hint" id="formInfo">Yeni kayıt oluşturuluyor.</span></div>
</form></div></section>
<section class="card" aria-labelledby="tableTitle"><div class="card-header"><h2 id="tableTitle"><span class="sect">03</span> Risk Kayıtları ve Aksiyon Takibi <span id="recordCount" class="hint">(0)</span></h2>
<div class="filters"><input type="search" id="searchInput" autocomplete="off" placeholder="Kayıtlarda ara..." aria-label="Kayıtlarda ara"><select id="statusFilter" aria-label="Duruma göre filtrele"><option value="">Tüm durumlar</option><option value="open">Açık</option><option value="inprogress">Devam Ediyor</option><option value="done">Tamamlandı</option></select></div></div>
<div class="tablebox"><table aria-label="Fine-Kinney risk kayıtları"><thead><tr><th>No.</th><th>Bölüm / Faaliyet</th><th>Tehlike / Risk</th><th>Mevcut O×Fת</th><th>Hedef O×Fת</th><th>Tedbir / Aksiyon</th><th>Termin / Sorumlu</th><th>Durum</th><th>İşlemler</th></tr></thead><tbody id="riskRows"></tbody></table><div class="empty" id="emptyState">Henüz risk kaydı bulunmuyor. Yukarıdaki formdan risk ekleyebilirsiniz.</div></div>
<div class="table-bottom"><button type="button" class="btn btn-danger btn-sm" id="clearBtn">Tüm Kayıtları Sil</button></div></section>
</main>
<div class="bottom-actions"><div class="bottom-panel" aria-label="Dosya ve yazdırma işlemleri"><button type="button" id="importBtn" class="btn btn-softblue">⬆ Excel İçeri Aktar</button><button type="button" id="exportBtn" class="btn btn-softgreen">⬇ Excel Dışarı Aktar</button><button type="button" id="pdfBtn" class="btn btn-softgray">▣ PDF / Yazdır</button><input id="importFile" type="file" class="sr-only" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"></div></div>
<footer class="risk-ui-footer" aria-label="Fine-Kinney yöntem, kullanım alanı ve faktör puanları"><span class="risk-ui-footer-single-line">Temel özelliği: Olasılık × Frekans × Şiddet&nbsp;&nbsp;&nbsp; Kullanım alanı: Fabrikalar, atölyeler, üretim tesisleri&nbsp;&nbsp;&nbsp; (Olasılık: 0,1 – 0,2 – 0,5 – 1 – 3 – 6 – 10)&nbsp;&nbsp;&nbsp; (Frekans: 0,5 – 1 – 2 – 3 – 6 – 10)&nbsp;&nbsp;&nbsp; (Şiddet: 1 – 3 – 7 – 15 – 40 – 100)</span></footer>
</div>
<table id="printPages" role="presentation"><thead><tr><td><div id="printHeader"></div></td></tr></thead><tbody><tr><td><div id="printReport"></div></td></tr></tbody></table><div id="printPageFrame"></div>
<div id="toast" class="notice" role="status" aria-live="polite"></div>
<script>
'use strict';
(()=>{
const $=id=>document.getElementById(id);
const today=()=>{const d=new Date();return [d.getFullYear(),String(d.getMonth()+1).padStart(2,'0'),String(d.getDate()).padStart(2,'0')].join('-')};
const asText=v=>v===null||v===undefined?'':String(v);
const esc=v=>asText(v).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const randomId=()=>('FK'+Date.now().toString(36)+Math.random().toString(36).slice(2,7)).toUpperCase();
const BANDS=[{key:'low',min:0,max:20,label:'Kabul Edilebilir',short:'< 20',action:'Mevcut tedbirleri sürdür, izlemeyi devam ettir.'},{key:'possible',min:20,max:70,label:'Olası Risk',short:'20 – <70',action:'İzle ve gerekiyorsa eylem planına al.'},{key:'important',min:70,max:200,label:'Önemli Risk',short:'70 – <200',action:'İyileştirici önlem planla ve takip et.'},{key:'high',min:200,max:400.00001,label:'Yüksek Risk',short:'200 – 400',action:'Kısa vadede önlem al; işin devamını değerlendir.'},{key:'critical',min:400.00001,max:100000,label:'Çok Yüksek Risk',short:'> 400',action:'Acil tedbir al; faaliyetin durdurulmasını değerlendir.'}];
const OPTS={p:[[0.1,'0,1 · Neredeyse imkânsız'],[0.2,'0,2 · Pratikte beklenmez'],[0.5,'0,5 · Zayıf ihtimal'],[1,'1 · Düşük ihtimal'],[3,'3 · Nadir ama mümkün'],[6,'6 · Kuvvetle muhtemel'],[10,'10 · Beklenen']],f:[[0.5,'0,5 · Çok seyrek / yılda bir'],[1,'1 · Seyrek / yılda birkaç kez'],[2,'2 · Ara sıra / ayda bir'],[3,'3 · Zaman zaman / haftada bir'],[6,'6 · Sık / günde bir veya birkaç'],[10,'10 · Sürekli / saatlik veya daha sık']],s:[[1,'1 · Önemsiz sonuç'],[3,'3 · Küçük hasar / ilk yardım'],[7,'7 · Önemli yaralanma'],[15,'15 · Çok ciddi yaralanma'],[40,'40 · Ölüm / ağır kayıp'],[100,'100 · Birden çok ölüm / felaket']]};
const LABELS={p:'Olasılık (O)',f:'Frekans (F)',s:'Şiddet (Ş)'};
const statusName={open:'Açık',inprogress:'Devam Ediyor',done:'Tamamlandı'};
const initMeta=()=>({company:'',facility:'',prepared:'',date:'',revision:''});
const emptyFactors=()=>({p:null,f:null,s:null});
let state={meta:initMeta(),records:[]};let working={before:emptyFactors(),after:emptyFactors()};let editingId=null;let viewMode='single';let toastHandle;
const valid=(key,v)=>OPTS[key].some(x=>x[0]===Number(v));
const factorsValid=o=>o&&['p','f','s'].every(k=>valid(k,o[k]));
const score=o=>factorsValid(o)?Math.round(Number(o.p)*Number(o.f)*Number(o.s)*1000)/1000:null;
const bandFor=n=>n===null?null:n<20?BANDS[0]:n<70?BANDS[1]:n<200?BANDS[2]:n<=400?BANDS[3]:BANDS[4];
const fmt=n=>n===null?'—':Number(n).toLocaleString('tr-TR',{maximumFractionDigits:3});
const isEmptyString=v=>asText(v).trim()==='';
function recordNormalize(raw){if(!raw||typeof raw!=='object'||(raw.method&&raw.method!=='fine-kinney'))return null;
 const pick=side=>{const src=raw[side]||{};const out={};for(const k of ['p','f','s'])out[k]=isEmptyString(src[k])?null:(valid(k,src[k])?Number(src[k]):null);return out};
 const result={id:asText(raw.id)||randomId(),method:'fine-kinney',unit:asText(raw.unit),activity:asText(raw.activity),hazard:asText(raw.hazard),risk:asText(raw.risk),affected:asText(raw.affected),existing:asText(raw.existing),action:asText(raw.action),responsible:asText(raw.responsible),deadline:asText(raw.deadline),status:statusName[raw.status]?raw.status:'open',verified:Boolean(raw.verified)&&raw.status==='done',notes:asText(raw.notes),created:asText(raw.created)||today(),before:pick('before'),after:pick('after')};
 if(!factorsValid(result.before)||!factorsValid(result.after))return null;return result;
}
function resetData(){state={meta:initMeta(),records:[]};working={before:emptyFactors(),after:emptyFactors()};editingId=null;viewMode='single';}
function toast(msg){clearTimeout(toastHandle);$('toast').textContent=msg;$('toast').classList.add('show');toastHandle=setTimeout(()=>$('toast').classList.remove('show'),3700)}
/* Risk UI: yalnızca logo ve başlık tercihi kalıcıdır; form ve kayıtlar yenilenince boş kalır. */
const BRAND_KEY='riskui-finekinney-brand-v1';
const BRAND_DEFAULT={title:'İSG RİSK ANALİZİ – FINE-KINNEY',subtitle:'(Olasılık × Frekans × Şiddet · Risk Değerlendirme ve Aksiyon Takip Formu)',org:'OLGUN BAŞKANLIĞI',logo:''};
let brand={...BRAND_DEFAULT};
function storeBrand(){try{localStorage.setItem(BRAND_KEY,JSON.stringify(brand))}catch(e){toast('Başlık bilgileri bu tarayıcıda saklanamadı.')}}
function initializeBrand(){try{const old=JSON.parse(localStorage.getItem(BRAND_KEY)||'null');if(old&&typeof old==='object')for(const k of Object.keys(BRAND_DEFAULT))if(typeof old[k]==='string')brand[k]=old[k]}catch(e){}
 $('brandTitle').textContent=brand.title;$('brandSubtitle').textContent=brand.subtitle;$('brandOrg').textContent=brand.org;setLogo(brand.logo);
 setupFive($('logoTrigger'),()=>$('logoChooser').click());for(const [id,key] of [['brandTitle','title'],['brandSubtitle','subtitle'],['brandOrg','org']])setupFive($(id),()=>editBrand($(id),key));
 $('logoChooser').addEventListener('change',e=>{const file=e.target.files?.[0];if(!file)return;if(!file.type.startsWith('image/')||file.size>1200000){alert('1,2 MB altında bir görsel seçin.');e.target.value='';return}const reader=new FileReader();reader.onload=()=>{brand.logo=String(reader.result||'');setLogo(brand.logo);storeBrand();toast('Kurum logosu kaydedildi.')};reader.readAsDataURL(file);e.target.value=''})
}
function setLogo(data){
 const img=$('brandLogo'),fallback=$('logoFallback');
 img.onload=()=>{img.hidden=false;fallback.hidden=true;syncPrintableLogo()};
 img.onerror=()=>{img.hidden=true;fallback.hidden=false};
 // Olgun UI: Aynı klasörde logo.jpg varsa kendiliğinden kullanılır.
 // Kullanıcının daha önce seçtiği logo bunun önüne geçer.
 img.src=(data&&/^data:image\//.test(data))?data:'logo.jpg';
}
function setupFive(el,cb){let clicks=0,last=0;el.addEventListener('click',()=>{if(el.isContentEditable)return;const now=Date.now();clicks=now-last>1150?1:clicks+1;last=now;if(clicks>=5){clicks=0;cb()}})}
function editBrand(el,key){el.contentEditable='true';el.focus();const r=document.createRange(),s=window.getSelection();r.selectNodeContents(el);s.removeAllRanges();s.addRange(r);let closed=false;
 const save=()=>{if(closed)return;closed=true;el.contentEditable='false';brand[key]=el.textContent.trim()||BRAND_DEFAULT[key];el.textContent=brand[key];storeBrand();el.removeEventListener('blur',save);el.removeEventListener('keydown',onKey)};
 const onKey=e=>{if(e.key==='Enter'){e.preventDefault();el.blur()}else if(e.key==='Escape'){e.preventDefault();el.textContent=brand[key];el.blur()}};el.addEventListener('blur',save);el.addEventListener('keydown',onKey)
}
function renderMeta(){for(const el of document.querySelectorAll('[data-meta]'))el.value=state.meta[el.dataset.meta]||''}
function factorHTML(side){return ['p','f','s'].map(key=>`<div><label for="factor-${side}-${key}">${LABELS[key]}</label><select data-side="${side}" data-factor="${key}" id="factor-${side}-${key}" required><option value="">Seçiniz</option>${OPTS[key].map(([v,label])=>`<option value="${v}" ${working[side][key]===v?'selected':''}>${esc(label)}</option>`).join('')}</select></div>`).join('')}
function renderFactors(){$('beforeFields').innerHTML=factorHTML('before');$('afterFields').innerHTML=factorHTML('after');renderScore()}
function renderScore(){for(const side of ['before','after']){const v=score(working[side]),b=bandFor(v);$(''+side+'Score').textContent=fmt(v);const el=$(''+side+'Level');el.textContent=b?b.label:'Değerlendirilmedi';el.className='risk-tag '+(b?'risk-'+b.key:'risk-na')}
 $('afterTitle').textContent=$('verified').checked&&$('status').value==='done'?'Kalan risk doğrulandı':'Planlanan seviye';renderBands()}
function renderBands(){const counts=BANDS.map(()=>0);for(const r of state.records){const v=score(r.before),b=bandFor(v);if(b)counts[BANDS.indexOf(b)]++}
 const mx=Math.max(1,...counts);const a=bandFor(score(working.before)),z=bandFor(score(working.after)),dist=viewMode==='distribution';
 $('modeSingle').classList.toggle('active',!dist);$('modeDistribution').classList.toggle('active',dist);$('modeSingle').setAttribute('aria-pressed',String(!dist));$('modeDistribution').setAttribute('aria-pressed',String(dist));
 $('bandVisual').innerHTML=BANDS.map((b,i)=>`<div class="band" data-band="${b.key}" data-current="${!dist&&a?.key===b.key?'yes':'no'}" data-target="${!dist&&z?.key===b.key?'yes':'no'}"><div class="band-name">${b.label}</div><div class="band-bar"><div class="band-fill" style="width:${dist?counts[i]/mx*100:100}%"></div></div><div class="band-count">${dist?counts[i]+' kayıt':b.short}</div></div>`).join('');
 const summary=$('guideSummary');if(dist)summary.textContent=`Toplam ${state.records.length} risk kaydı · Bantlarda gösterilen sayılar mevcut risklerin dağılımıdır; ortalama değildir.`;
 else if(a&&z)summary.innerHTML=`Mevcut: <strong>${fmt(score(working.before))} · ${a.label}</strong> &nbsp;→&nbsp; Hedef: <strong>${fmt(score(working.after))} · ${z.label}</strong>`;
 else summary.textContent='Mevcut ve hedef riskin Olasılık, Frekans, Şiddet değerlerini seçin.';
}
function setView(v){viewMode=v==='distribution'?'distribution':'single';renderBands()}
function clearForm(){editingId=null;$('riskForm').reset();$('status').value='';$('verified').checked=false;working={before:emptyFactors(),after:emptyFactors()};$('formTitleText').textContent='Yeni Risk Kaydı';$('saveBtn').textContent='+ Riski Kaydet';$('formInfo').textContent='Yeni kayıt oluşturuluyor.';renderFactors()}
function valuesFromForm(){const v=id=>$(id).value.trim();return recordNormalize({id:editingId||randomId(),method:'fine-kinney',unit:v('unit'),activity:v('activity'),hazard:v('hazard'),risk:v('risk'),affected:v('affected'),existing:v('existing'),action:v('action'),responsible:v('responsible'),deadline:v('deadline'),status:v('status'),verified:$('verified').checked,notes:v('notes'),created:editingId?(state.records.find(r=>r.id===editingId)?.created||today()):today(),before:working.before,after:working.after})}
function startEdit(id){const r=state.records.find(x=>x.id===id);if(!r)return;editingId=r.id;for(const k of ['unit','activity','hazard','risk','affected','existing','action','responsible','deadline','status','notes'])$(k).value=r[k]||'';$('verified').checked=r.verified;working={before:{...r.before},after:{...r.after}};renderFactors();$('formTitleText').textContent='Risk Kaydını Düzenle';$('saveBtn').textContent='✓ Değişiklikleri Kaydet';$('formInfo').textContent='Kayıt düzenleniyor.';setView('single');$('formTitle').scrollIntoView({block:'start',behavior:'smooth'})}
function copyRecord(id){const r=state.records.find(x=>x.id===id);if(!r)return;const c=structuredClone(r);c.id=randomId();c.created=today();c.verified=false;if(c.status==='done')c.status='open';state.records.push(c);renderDashboard();toast('Risk kaydı çoğaltıldı.')}
function deleteRecord(id){if(!confirm('Bu risk kaydını silmek istiyor musunuz?'))return;state.records=state.records.filter(x=>x.id!==id);if(editingId===id)clearForm();renderDashboard();toast('Risk kaydı silindi.')}
function renderDashboard(){const q=$('searchInput').value.trim().toLocaleLowerCase('tr-TR'),filter=$('statusFilter').value,t=today();$('statTotal').textContent=state.records.length;$('statHigh').textContent=state.records.filter(r=>!r.verified&&(score(r.before)>=200)).length;$('statOverdue').textContent=state.records.filter(r=>!r.verified&&r.deadline&&r.deadline<t).length;$('statVerified').textContent=state.records.filter(r=>r.verified).length;
 const selected=state.records.filter(r=>(!filter||r.status===filter)&&(!q||[r.unit,r.activity,r.hazard,r.risk,r.existing,r.action,r.responsible,r.notes].some(v=>asText(v).toLocaleLowerCase('tr-TR').includes(q))));$('recordCount').textContent=`(${selected.length}/${state.records.length})`;$('emptyState').hidden=selected.length>0;
 $('riskRows').innerHTML=selected.map((r,i)=>{const v=score(r.before),n=score(r.after),b=bandFor(v),a=bandFor(n);return `<tr><td>${i+1}</td><td><b>${esc(r.unit)}</b><div class="cellsub">${esc(r.activity)}</div></td><td><b class="ellips">${esc(r.hazard)}</b><div class="cellsub ellips">${esc(r.risk)}</div></td><td>${fmt(r.before.p)}×${fmt(r.before.f)}×${fmt(r.before.s)} = <b>${fmt(v)}</b><br><span class="risk-tag risk-${b.key}">${b.label}</span></td><td>${fmt(r.after.p)}×${fmt(r.after.f)}×${fmt(r.after.s)} = <b>${fmt(n)}</b><br><span class="risk-tag risk-${a.key}">${esc(r.verified?'Doğrulandı · ':'Hedef · ')}${a.label}</span></td><td class="ellips">${esc(r.action)}</td><td>${esc(r.responsible||'—')}<div class="cellsub">${esc(r.deadline||'—')}</div></td><td>${esc(statusName[r.status])}</td><td><div style="display:flex;gap:4px;flex-wrap:wrap"><button class="btn btn-sm" data-act="edit" data-id="${esc(r.id)}">Düzenle</button><button class="btn btn-sm" data-act="copy" data-id="${esc(r.id)}">Kopyala</button><button class="btn btn-sm btn-danger" data-act="delete" data-id="${esc(r.id)}">Sil</button></div></td></tr>`}).join('');renderBands()}
function baseFilename(){return 'ISG_Fine_Kinney_Risk_Analizi_'+(state.meta.date||today())}
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),3000)}
function xmlEsc(v){return asText(v).replace(/[&<>"']/g,c=>({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&apos;'}[c])).replace(/[\x00-\x08\x0b\x0c\x0e-\x1f]/g,'')}
function colLetter(num){let res='';for(let n=num;n>0;n=Math.floor((n-1)/26))res=String.fromCharCode(65+(n-1)%26)+res;return res}
function sheetXml(rows,{width=18,autoFilter=true}={}){
 const maxCols=Math.max(1,...rows.map(r=>r.length));const cols=Array.from({length:maxCols},(_,i)=>{let w=width;if(i===0)w=17;if(i===5||i===6||i===8||i===9||i===26)w=42;if(i===3||i===4||i===10)w=24;return `<col min="${i+1}" max="${i+1}" width="${w}" customWidth="1"/>`}).join('');
 const sheetRows=rows.map((row,i)=>`<row r="${i+1}">${row.map((value,j)=>{const c=colLetter(j+1)+(i+1),st=i===0?' s="1"':'';if(typeof value==='number'&&Number.isFinite(value))return `<c r="${c}"${st}><v>${value}</v></c>`;return `<c r="${c}" t="inlineStr"${st}><is><t xml:space="preserve">${xmlEsc(value)}</t></is></c>`}).join('')}</row>`).join('');
 return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?><worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><sheetViews><sheetView workbookViewId="0"><pane ySplit="1" topLeftCell="A2" activePane="bottomLeft" state="frozen"/></sheetView></sheetViews><sheetFormatPr defaultRowHeight="16"/><cols>${cols}</cols><sheetData>${sheetRows}</sheetData>${autoFilter?`<autoFilter ref="A1:${colLetter(maxCols)}${Math.max(1,rows.length)}"/>`:''}</worksheet>`
}
const CRC_TABLE=(()=>{const result=[];for(let i=0;i<256;i++){let x=i;for(let j=0;j<8;j++)x=(x&1)?(0xedb88320^(x>>>1)):(x>>>1);result[i]=x>>>0}return result})();
function crc32(data){let crc=0xffffffff;for(const byte of data)crc=CRC_TABLE[(crc^byte)&255]^(crc>>>8);return (crc^0xffffffff)>>>0}
function zipCreate(entries){
 const enc=new TextEncoder(),pieces=[],centers=[];let offset=0;
 const u16=(view,pos,val)=>view.setUint16(pos,val,true),u32=(view,pos,val)=>view.setUint32(pos,val>>>0,true);
 function append(piece){pieces.push(piece);offset+=piece.length}
 for(const [filename,text] of entries){const name=enc.encode(filename),data=enc.encode(text),crc=crc32(data),local=new Uint8Array(30+name.length),dv=new DataView(local.buffer),begin=offset;
  u32(dv,0,0x04034b50);u16(dv,4,20);u16(dv,6,0x0800);u16(dv,8,0);u16(dv,10,0);u16(dv,12,0);u32(dv,14,crc);u32(dv,18,data.length);u32(dv,22,data.length);u16(dv,26,name.length);u16(dv,28,0);local.set(name,30);append(local);append(data);
  const cent=new Uint8Array(46+name.length),cv=new DataView(cent.buffer);u32(cv,0,0x02014b50);u16(cv,4,20);u16(cv,6,20);u16(cv,8,0x0800);u16(cv,10,0);u16(cv,12,0);u16(cv,14,0);u32(cv,16,crc);u32(cv,20,data.length);u32(cv,24,data.length);u16(cv,28,name.length);u16(cv,30,0);u16(cv,32,0);u16(cv,34,0);u16(cv,36,0);u32(cv,38,0);u32(cv,42,begin);cent.set(name,46);centers.push(cent)
 }
 const start=offset;for(const c of centers)append(c);const end=new Uint8Array(22),e=new DataView(end.buffer);u32(e,0,0x06054b50);u16(e,4,0);u16(e,6,0);u16(e,8,centers.length);u16(e,10,centers.length);u32(e,12,offset-start);u32(e,16,start);u16(e,20,0);append(end);return new Blob(pieces,{type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'})
}
function workbookFiles(rows,summary){const types=`<?xml version="1.0" encoding="UTF-8"?><Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml"/><Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/worksheets/sheet2.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml"/><Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"/></Types>`;
 const rels=`<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/></Relationships>`;
 const workbook=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="Risk Kayıtları" sheetId="1" r:id="rId1"/><sheet name="Rapor Bilgileri" sheetId="2" r:id="rId2"/></sheets></workbook>`;
 const wbRels=`<?xml version="1.0" encoding="UTF-8"?><Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet2.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/></Relationships>`;
 const styles=`<?xml version="1.0" encoding="UTF-8" standalone="yes"?><styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"><fonts count="2"><font><sz val="10"/><name val="Calibri"/></font><font><b/><sz val="10"/><color rgb="FFFFFFFF"/><name val="Calibri"/></font></fonts><fills count="3"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill><fill><patternFill patternType="solid"><fgColor rgb="FF0369A1"/><bgColor indexed="64"/></patternFill></fill></fills><borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders><cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs><cellXfs count="2"><xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/><xf numFmtId="0" fontId="1" fillId="2" borderId="0" xfId="0" applyFont="1" applyFill="1"/></cellXfs><cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles></styleSheet>`;
 return [['[Content_Types].xml',types],['_rels/.rels',rels],['xl/workbook.xml',workbook],['xl/_rels/workbook.xml.rels',wbRels],['xl/styles.xml',styles],['xl/worksheets/sheet1.xml',sheetXml(rows)],['xl/worksheets/sheet2.xml',sheetXml(summary,{width:36,autoFilter:false})]]}

const EXCEL_COLUMNS=['Kayıt ID','Bölüm','Faaliyet','Tehlike','Olası Zarar / Risk','Etkilenenler','Mevcut Önlemler','Önce Olasılık','Önce Frekans','Önce Şiddet','Önce Risk Puanı','Önce Risk Seviyesi','Planlanan Tedbir','Sonra Olasılık','Sonra Frekans','Sonra Şiddet','Sonra Risk Puanı','Sonra Risk Seviyesi','Sorumlu','Hedef Tarih','Durum','Doğrulama','Notlar','Kayıt Tarihi'];
function recordToExcelRow(r){return [r.id,r.unit,r.activity,r.hazard,r.risk,r.affected,r.existing,r.before.p,r.before.f,r.before.s,score(r.before),bandFor(score(r.before)).label,r.action,r.after.p,r.after.f,r.after.s,score(r.after),bandFor(score(r.after)).label,r.responsible,r.deadline,statusName[r.status],r.verified?'Evet':'Hayır',r.notes,r.created]}
function exportXlsx(){const m=state.meta,rows=[EXCEL_COLUMNS,...state.records.map(recordToExcelRow)],summary=[['Alan','Değer'],['Yöntem','Fine-Kinney'],['İşyeri',m.company],['Tesis / Birim',m.facility],['Hazırlayan',m.prepared],['Değerlendirme Tarihi',m.date],['Revizyon',m.revision],['Toplam Risk',state.records.length],['Yüksek ve Çok Yüksek / Açık',state.records.filter(r=>!r.verified&&score(r.before)>=200).length],['Doğrulanan Risk',state.records.filter(r=>r.verified).length],['Risk Formülü','Olasılık × Frekans × Şiddet'],['Not','Risk eşikleri örnek uygulamadır; kurumun onaylı metodolojisi geçerlidir.']];downloadBlob(zipCreate(workbookFiles(rows,summary)),baseFilename()+'.xlsx');toast('Excel dışarı aktarıldı: '+state.records.length+' kayıt.')}
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 ''}
function fromExcelRow(row,idx){const read=k=>row[idx.get(k)]??'',statusRaw=read('Durum');const status=Object.keys(statusName).find(k=>statusName[k]===statusRaw)||(Object.hasOwn(statusName,statusRaw)?statusRaw:'open');return recordNormalize({id:read('Kayıt ID'),method:'fine-kinney',unit:read('Bölüm'),activity:read('Faaliyet'),hazard:read('Tehlike'),risk:read('Olası Zarar / Risk'),affected:read('Etkilenenler'),existing:read('Mevcut Önlemler'),action:read('Planlanan Tedbir'),responsible:read('Sorumlu'),deadline:normalizeDate(read('Hedef Tarih')),status,verified:read('Doğrulama')==='Evet',before:{p:read('Önce Olasılık'),f:read('Önce Frekans'),s:read('Önce Şiddet')},after:{p:read('Sonra Olasılık'),f:read('Sonra Frekans'),s:read('Sonra Şiddet')},notes:read('Notlar'),created:normalizeDate(read('Kayıt Tarihi'))||today()})}
async function importData(file){if(!file.name.toLowerCase().endsWith('.xlsx'))throw new Error('Yalnızca .xlsx dosyası seçin.');const entries=await unzipEntries(await file.arrayBuffer());const strings=[];
 if(entries['xl/sharedStrings.xml']){const doc=parseXml(entries['xl/sharedStrings.xml']);for(const item of doc.getElementsByTagNameNS('*','si'))strings.push(Array.from(item.getElementsByTagNameNS('*','t')).map(t=>t.textContent).join(''))}
 const rows=parseXlsxSheet(entries['xl/worksheets/sheet1.xml'],strings);if(!rows.length)throw new Error('Excel risk kayıtları sayfası bulunamadı.');const headers=new Map(rows[0].map((v,i)=>[asText(v).trim(),i]));const required=['Kayıt ID','Tehlike','Bölüm','Faaliyet','Önce Olasılık','Önce Frekans','Önce Şiddet','Sonra Olasılık','Sonra Frekans','Sonra Şiddet'];if(!required.every(k=>headers.has(k)))throw new Error('Excel Fine-Kinney şablonuyla uyumsuz (O/F/Ş sütunları eksik).');
 const dataRows=rows.slice(1).filter(r=>r.some(v=>asText(v).trim()));const records=dataRows.map(r=>fromExcelRow(r,headers)).filter(Boolean);if(dataRows.length!==records.length)throw new Error('Excel içinde '+(dataRows.length-records.length)+' kayıt geçersiz: Olasılık/Frekans/Şiddet ölçeklerini kontrol edin.');const info=parseXlsxSheet(entries['xl/worksheets/sheet2.xml'],strings),infoMap=new Map(info.slice(1).map(r=>[r[0],r[1]]));const meta=initMeta();for(const [key,label] of Object.entries({company:'İşyeri',facility:'Tesis / Birim',prepared:'Hazırlayan',date:'Değerlendirme Tarihi',revision:'Revizyon'}))if(infoMap.has(label))meta[key]=asText(infoMap.get(label));return {meta,records}}
async function performImport(file){if(!file)return;try{const incoming=await importData(file);if(!incoming.records.length)throw new Error('Dosyada risk kaydı bulunmuyor.');if(state.records.length&&!confirm('Mevcut '+state.records.length+' riskin yerine '+incoming.records.length+' kayıt yüklenecek. Önce Excel yedeği alın. Devam edilsin mi?'))return;state=incoming;renderMeta();clearForm();renderDashboard();setView('distribution');toast(incoming.records.length+' risk kaydı Excel’den yüklendi.')}catch(e){console.error(e);alert('Excel içeri aktarma başarısız: '+e.message)}finally{$('importFile').value=''}}
/* PDF/Yazdır: Ekrandaki iki Fine-Kinney grafiğinin SVG karşılığı; renkler PDF'de vektördür. */
function renderPrintCharts(before,after){
 const counts=BANDS.map(()=>0);
 for(const r of state.records){const b=bandFor(score(r.before));if(b)counts[BANDS.indexOf(b)]++}
 const biggest=Math.max(1,...counts),bNow=bandFor(score(before)),bTarget=bandFor(score(after));
 const colors={low:'#73cf93',possible:'#eed572',important:'#f7b359',high:'#f28b59',critical:'#e97575'};
 const svgBase=(kind)=>{
  const isCount=kind==='distribution';
  const rows=BANDS.map((b,i)=>{
   const y=8+i*28,filled=isCount?Math.max(0,(counts[i]/biggest)*244):244;
   const haveBefore=!isCount&&bNow?.key===b.key,haveTarget=!isCount&&bTarget?.key===b.key;
   const marker=haveBefore?`<rect x="146" y="${y+1}" width="244" height="22" rx="2" fill="none" stroke="#125ba9" stroke-width="3"/>`:'';
   const target=haveTarget?`<rect x="150" y="${y+4}" width="236" height="16" rx="1" fill="none" stroke="#8651b4" stroke-width="2" stroke-dasharray="5 3"/>`:'';
   const label=isCount?counts[i]+' kayıt':b.short;
   return `<text x="6" y="${y+16}" font-family="Arial,sans-serif" font-size="11" font-weight="600" fill="#25364b">${esc(b.label)}</text>`+
     `<rect x="146" y="${y+1}" width="244" height="22" rx="2" fill="#eff3f6" stroke="#cbd5e1" stroke-width="0.8"/>`+
     (filled?`<rect x="147" y="${y+2}" width="${Math.max(0,filled-2)}" height="20" rx="1" fill="${colors[b.key]}"/>`:'')+
     marker+target+
     `<text x="404" y="${y+16}" font-family="Arial,sans-serif" font-size="11" font-weight="700" fill="#334155">${esc(label)}</text>`;
  }).join('');
  return `<svg xmlns="http://www.w3.org/2000/svg" role="img" aria-label="${isCount?'Genel risk dağılımı':'Mevcut ve hedef risk düzeyleri'}" viewBox="0 0 520 150" preserveAspectRatio="xMidYMid meet">${rows}</svg>`;
 };
 const current=factorsValid(before)?`${fmt(score(before))} · ${esc(bNow.label)}`:'Henüz mevcut risk seçilmedi';
 const target=factorsValid(after)?`${fmt(score(after))} · ${esc(bTarget.label)}`:'Henüz hedef risk seçilmedi';
 return `<section class="print-section print-chart-section"><h3>Fine-Kinney · Risk Grafikleri</h3><div class="print-chart-layout">`+
 `<div class="print-chart-box"><h4>Tekil Risk · Mevcut / Hedef</h4>${svgBase('single')}<div class="print-chart-key"><span><i class="mark"></i>Mevcut</span><span><i class="mark target"></i>Hedef</span></div><div class="print-chart-foot">Mevcut: <b>${current}</b> → Hedef: <b>${target}</b></div></div>`+
 `<div class="print-chart-box"><h4>Genel Risk Dağılımı · Mevcut Risk Kayıtları</h4>${svgBase('distribution')}<div class="print-chart-foot">Toplam <b>${state.records.length}</b> risk kaydı. Çubuk uzunluğu ve sayı, her seviyedeki kayıt adedini gösterir; ortalama değildir.</div></div>`+
 `</div></section>`;
}
/* Risk UI baskı logo çözümü:
   Ekrandaki etkin logoyu (aynı klasördeki logo.jpg veya kullanıcı yüklemesi)
   doğrudan PDF başlığına taşır. Yüklenmiş yerel logo mümkünse
   veri-URL'sine dönüştürülür; böylece yazdırmada görünür ve tekrar eden
   sayfa başlıklarında kaynak yükleme sorunları yaşanmaz. */
function getPrintableLogoSource(){
 const live=$('brandLogo');
 if(!live || live.hidden || !live.complete || live.naturalWidth<=0)return '';
 // Kullanıcının yerel olarak seçtiği logo zaten HTML içine gömülebilir durumda.
 if(brand.logo && /^data:image\//i.test(brand.logo))return brand.logo;
 // logo.jpg için tarayıcı güvenlik izinleri elveriyorsa görseli rapora göm.
 try{
   const max=800,ratio=Math.min(1,max/Math.max(live.naturalWidth,live.naturalHeight));
   const canvas=document.createElement('canvas');
   canvas.width=Math.max(1,Math.round(live.naturalWidth*ratio));
   canvas.height=Math.max(1,Math.round(live.naturalHeight*ratio));
   const context=canvas.getContext('2d');
   if(context){context.drawImage(live,0,0,canvas.width,canvas.height);return canvas.toDataURL('image/png')}
 }catch(err){console.info('Logo veri URL olarak eklenemedi, orijinal logo kullanılıyor.',err)}
 // file:// güvenlik kısıtlamaları nedeniyle canvas kullanılamazsa
 // ekranda başarıyla açılmış olan aynı resmin mutlak adresini kullan.
 return live.currentSrc||live.src||'';
}
function syncPrintableLogo(){
 const target=$('printBrandLogo'),fallback=$('printLogoFallback');
 if(!target||!fallback)return;
 const url=getPrintableLogoSource();
 if(url){
   target.onload=()=>{target.hidden=false;fallback.hidden=true};
   target.onerror=()=>{target.hidden=true;fallback.hidden=false};
   if(target.src!==url)target.src=url;
   target.hidden=false;fallback.hidden=true;
 }else{
   target.removeAttribute('src');target.hidden=true;fallback.hidden=false;
 }
}
function renderPrint(){const m=state.meta;
 // Aynı baskı başlığını tekrar kullanmak, beforeprint sırasında logo yüklemesini sıfırlamaz.
 const host=$('printHeader');
 if(!host.querySelector('#printBrandLogo')){
   host.innerHTML=`<header class="print-header"><div class="print-logo-box"><img id="printBrandLogo" alt="Kurum logosu" hidden><b id="printLogoFallback">LOGO</b></div><div><h1></h1><p></p></div><div class="print-org"></div></header>`;
 }
 host.querySelector('.print-header h1').textContent=brand.title;
 host.querySelector('.print-header p').textContent=brand.subtitle;
 host.querySelector('.print-org').textContent=brand.org;
 syncPrintableLogo();
 const metaFields=[['İşyeri / Firma',m.company],['Tesis / Birim',m.facility],['Hazırlayan / Ekip',m.prepared],['Değerlendirme Tarihi',m.date],['Revizyon / Belge No.',m.revision]],pField=([k,v])=>`<div class="print-field"><b>${esc(k)}</b>${esc(v||'—')}</div>`;
 const currentForm=editingId||['unit','activity','hazard','risk','action'].some(id=>$(id).value.trim())||factorsValid(working.before)||factorsValid(working.after);
 const record=state.records.length&&!currentForm?state.records[state.records.length-1]:null;
 const fieldVal=k=>record?record[k]:$(k).value;
 const before=record?record.before:working.before,after=record?record.after:working.after, verified=record?record.verified:$('verified').checked;
 const fields=[['Bölüm / Alan',fieldVal('unit')],['Faaliyet / İş Adımı',fieldVal('activity')],['Etkilenebilecek Kişiler',fieldVal('affected')],['Tehlike Kaynağı',fieldVal('hazard')],['Risk / Olası Zarar',fieldVal('risk')],['Mevcut Önlemler',fieldVal('existing')],['Alınacak Tedbir',fieldVal('action')],['Sorumlu Kişi',fieldVal('responsible')],['Termin',fieldVal('deadline')],['Aksiyon Durumu',record?statusName[record.status]:statusName[$('status').value]||''],['Ek Açıklamalar',fieldVal('notes')],['Kalan Risk Doğrulandı',verified?'Evet':'Hayır']];
 const fval=(o,k)=>o[k]==null?'—':fmt(o[k]),showScore=o=>factorsValid(o)?fmt(score(o))+' · '+bandFor(score(o)).label:'—';
 const rows=state.records.map((r,i)=>`<tr><td>${i+1}</td><td>${esc(r.unit)}<br>${esc(r.activity)}</td><td><b>Tehlike:</b> ${esc(r.hazard)}<br><b>Risk:</b> ${esc(r.risk)}<br><b>Mevcut:</b> ${esc(r.existing||'—')}</td><td>${fval(r.before,'p')}×${fval(r.before,'f')}×${fval(r.before,'s')}<br><b>${showScore(r.before)}</b></td><td>${fval(r.after,'p')}×${fval(r.after,'f')}×${fval(r.after,'s')}<br><b>${showScore(r.after)}</b></td><td>${esc(r.action)}<br>${esc(r.responsible||'—')} · ${esc(r.deadline||'—')}<br>${esc(statusName[r.status])}${r.verified?' · Doğrulandı':''}</td></tr>`).join('')||'<tr><td colspan="6">Kayıt yok.</td></tr>';
 $('printReport').innerHTML=`<section class="print-section print-no-break"><h3>01 · İşyeri ve Değerlendirme Bilgileri</h3><div class="print-grid">${metaFields.map(pField).join('')}</div></section><section class="print-section print-no-break"><h3>02 · Fine-Kinney Risk Değerlendirmesi</h3><div class="print-summary"><div>MEVCUT RİSK<b>${fmt(score(before))}</b>${factorsValid(before)?esc(bandFor(score(before)).label):'—'}</div><div>HEDEF / KALAN<b>${fmt(score(after))}</b>${factorsValid(after)?esc(bandFor(score(after)).label):'—'}</div><div>OLASILIK (O)<b>${fval(before,'p')} → ${fval(after,'p')}</b></div><div>FREKANS (F)<b>${fval(before,'f')} → ${fval(after,'f')}</b></div><div>ŞİDDET (Ş)<b>${fval(before,'s')} → ${fval(after,'s')}</b></div></div><div class="print-grid">${fields.map(pField).join('')}</div><div class="print-note">Hedef risk, önlem uygulanıp etkinliği doğrulanana kadar gerçekleşmiş kalan risk değildir.</div></section>${renderPrintCharts(before,after)}<section class="print-section"><h3>03 · Risk Kayıtları ve Aksiyon Takibi (${state.records.length})</h3><table class="print-table"><thead><tr><th style="width:4%">No.</th><th style="width:16%">Bölüm / Faaliyet</th><th style="width:25%">Tehlike / Risk / Kontrol</th><th style="width:14%">Önce O×Fת</th><th style="width:14%">Sonra O×Fת</th><th style="width:27%">Tedbir / Sorumlu / Durum</th></tr></thead><tbody>${rows}</tbody></table></section><div class="print-note">Fine-Kinney sınıflandırması örnek yöntem aralıklarına göre uygulanmıştır; işyeri risk değerlendirme ekibince doğrulanmalıdır.</div>`;
}
function printWithLogoReady(){
 renderPrint();
 const logo=$('printBrandLogo');
 if(!logo||logo.hidden||(logo.complete&&logo.naturalWidth>0)){
   window.print();return;
 }
 let started=false;
 const openPrint=()=>{
   if(started)return;
   started=true;
   logo.removeEventListener('load',openPrint);
   logo.removeEventListener('error',openPrint);
   window.print();
 };
 logo.addEventListener('load',openPrint,{once:true});
 logo.addEventListener('error',openPrint,{once:true});
 setTimeout(openPrint,1500);
}
function bind(){document.querySelectorAll('[data-meta]').forEach(el=>el.addEventListener('input',()=>state.meta[el.dataset.meta]=el.value));
 for(const id of ['beforeFields','afterFields'])$(id).addEventListener('change',e=>{const el=e.target;if(!el.matches('select[data-factor]'))return;working[el.dataset.side][el.dataset.factor]=el.value===''?null:Number(el.value);renderScore()});
 $('status').addEventListener('change',()=>{if($('status').value!=='done')$('verified').checked=false;renderScore()});$('verified').addEventListener('change',()=>{if($('verified').checked&&$('status').value!=='done'){$('verified').checked=false;alert('Doğrulama için aksiyon durumu “Tamamlandı” seçilmelidir.')}renderScore()});
 $('modeSingle').addEventListener('click',()=>setView('single'));$('modeDistribution').addEventListener('click',()=>setView('distribution'));
 $('riskForm').addEventListener('submit',e=>{e.preventDefault();if(!$('riskForm').checkValidity()){$('riskForm').reportValidity();return}if(!factorsValid(working.before)||!factorsValid(working.after)){alert('Mevcut ve hedef risk için Olasılık, Frekans ve Şiddet seçimlerinin tamamını yapın.');return}if($('verified').checked&&$('status').value!=='done'){alert('Kalan risk doğrulaması yalnızca tamamlanan aksiyonlarda seçilebilir.');return}const r=valuesFromForm();if(!r){alert('Risk faktörlerinin tamamını geçerli Fine-Kinney değerlerinden seçin.');return}const wasEditing=Boolean(editingId);if(wasEditing){const ix=state.records.findIndex(x=>x.id===editingId);if(ix<0)return;state.records[ix]=r}else state.records.push(r);clearForm();renderDashboard();setView('distribution');toast(wasEditing?'Risk kaydı güncellendi.':'Risk kaydı eklendi.')});
 $('newBtn').addEventListener('click',()=>{clearForm();setView('single')});$('cancelBtn').addEventListener('click',()=>{clearForm();setView('single')});
 $('riskRows').addEventListener('click',e=>{const b=e.target.closest('button[data-act][data-id]');if(!b)return;const {act,id}=b.dataset;if(act==='edit')startEdit(id);if(act==='copy')copyRecord(id);if(act==='delete')deleteRecord(id)});
 for(const id of ['searchInput','statusFilter'])$(id).addEventListener('input',renderDashboard);
 $('clearBtn').addEventListener('click',()=>{if(!state.records.length)return toast('Silinecek kayıt bulunmuyor.');if(!confirm('Tüm '+state.records.length+' risk kaydı silinecek. Önce Excel yedeği alın. Devam edilsin mi?'))return;state.records=[];clearForm();renderDashboard();toast('Tüm risk kayıtları silindi.')});
 $('importBtn').addEventListener('click',()=>$('importFile').click());$('importFile').addEventListener('change',e=>performImport(e.target.files?.[0]));$('exportBtn').addEventListener('click',exportXlsx);$('pdfBtn').addEventListener('click',printWithLogoReady);window.addEventListener('beforeprint',renderPrint)
}
function resetSessionOnOpen(){resetData();$('riskForm').reset();$('searchInput').value='';$('statusFilter').value='';$('importFile').value='';document.querySelectorAll('[data-meta]').forEach(el=>el.value='');renderMeta();clearForm();renderDashboard()}
initializeBrand();bind();resetSessionOnOpen();window.addEventListener('pageshow',e=>{if(e.persisted)resetSessionOnOpen()});
})();
</script>
</body></html>