<!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>ISG Risk Analizi - L_Tipi_Matris</title>
<style>
:root{--bg:#f8fafc;--surface:#fff;--line:#dbe3eb;--text:#17283b;--muted:#64748b;--brand:#0284c7;--brand2:#0369a1;--pale:#eaf6fd;--radius:7px;--shadow:0 2px 10px rgba(22,44,68,.045)}
*{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;background:var(--bg);font:13px/1.45 "Segoe UI",Arial,sans-serif;color:var(--text)}button,input,select,textarea{font:inherit}button{cursor:pointer}button:disabled{opacity:.5;cursor:not-allowed}input,select,textarea{width:100%;background:#fff;border:1px solid #cbd5e1;border-radius:5px;color:var(--text);padding:8px 10px;outline:none;min-height:36px}textarea{min-height:56px;resize:vertical}input:focus,select:focus,textarea:focus{border-color:var(--brand);box-shadow:0 0 0 3px #0284c71c}label{font-weight:600;color:#334155;display:block;margin-bottom:5px;font-size:12px}label small{font-weight:400;color:var(--muted)}input[type=checkbox]{width:17px;height:17px;min-height:0;accent-color:var(--brand)}.wrap{max-width:1490px;margin:0 auto;padding:16px 22px 30px}.header{background:linear-gradient(120deg,#075985,#0284c7);color:white;border-bottom:3px solid #38bdf8}.headerinner{max-width:1490px;padding:15px 22px;display:flex;align-items:center;gap:14px;margin:auto}.logo{display:grid;place-items:center;background:#ffffff18;border:1px solid #ffffff55;width:45px;height:45px;border-radius:7px;font-size:18px;font-weight:800;letter-spacing:.7px;flex-shrink:0}.header h1{margin:0;font-size:19px;line-height:1.3;letter-spacing:.2px}.header p{margin:3px 0 0;color:#dcf3ff;font-size:12px}.badge-offline{margin-left:auto;border:1px solid #a3e4ff85;border-radius:20px;padding:6px 11px;color:#e0f7ff;font-size:11px;white-space:nowrap}.card{min-width:0;background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow)}.card-header{display:flex;align-items:center;justify-content:space-between;gap:14px;border-bottom:1px solid #e9eef4;padding:11px 15px}.card-header h2{margin:0;font-size:15px;color:#0f3652;display:flex;align-items:center;gap:8px}.card-body{padding:14px 15px}.eyebrow{color:var(--brand);font-size:11px;font-weight:800;letter-spacing:.08em}.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))}.g5{grid-template-columns:repeat(5,minmax(0,1fr))}.span2{grid-column:span 2}.span3{grid-column:span 3}.stack{display:grid;grid-template-columns:minmax(0,1fr);gap:13px}.stack>*{min-width:0}.metrics{margin-bottom:13px}.metric{padding:12px 14px;display:flex;align-items:center;justify-content:space-between;gap:10px}.metric b{font-size:26px;color:#123855;line-height:1.1;display:block;margin-top:4px}.metric .icon{width:37px;height:37px;display:grid;place-items:center;border-radius:8px;background:var(--pale);color:var(--brand2);font-size:20px;font-weight:600}.metric small{font-size:11px;color:var(--muted);font-weight:600}.btn{border:1px solid var(--line);border-radius:5px;padding:8px 12px;min-height:35px;background:#fff;color:#334155;display:inline-flex;align-items:center;justify-content:center;gap:6px;white-space:nowrap;font-weight:600;transition:background .12s,border .12s}.btn:hover{background:#f1f5f9}.btn-primary{background:var(--brand);border-color:var(--brand);color:#fff}.btn-primary:hover{background:var(--brand2)}.btn-soft{background:#eef8ff;color:#0369a1;border-color:#b9def7}.btn-soft:hover{background:#e0f2fe}.btn-danger{color:#b42332;border-color:#f3c8ce}.btn-danger:hover{background:#fff1f2}.btn-small{min-height:27px;padding:5px 8px;font-size:11px}.toolbar{display:flex;align-items:center;flex-wrap:wrap;gap:7px}.section-number{border-radius:4px;background:#e0f2fe;color:#0369a1;font-size:11px;padding:3px 6px}.method-info{background:#f2f9fd;border:1px solid #d6eefa;color:#195071;padding:9px 11px;margin-top:11px;border-radius:5px;font-size:12px}.score-grid{display:grid;grid-template-columns:1fr 1fr;gap:12px}.score-panel{border:1px solid #dbe6ed;border-radius:6px;overflow:hidden;background:#fcfdff}.score-top{padding:9px 11px;border-bottom:1px solid #e4eaf0;display:flex;align-items:center;justify-content:space-between;gap:7px;background:#f2f7fb}.score-top strong{font-size:12px}.score-fields{padding:11px;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.score-result{margin:0 11px 11px;padding:9px 11px;border-radius:5px;display:flex;align-items:center;justify-content:space-between;gap:7px;background:#f2f7fb}.score-result b{font-size:21px;line-height:1.2}.risk-tag{display:inline-block;border:1px solid transparent;border-radius:4px;font-size:11px;font-weight:750;padding:3px 7px;white-space:nowrap}.risk-low{color:#177245;background:#e5f7ec;border-color:#b7e5c7}.risk-medium{color:#8d6010;background:#fff5d9;border-color:#f3dda5}.risk-high{color:#ad5019;background:#fff0e1;border-color:#ffccaa}.risk-critical{color:#a92439;background:#ffe5e8;border-color:#f4b5c0}.status{display:inline-block;padding:3px 7px;border-radius:4px;background:#eef2f6;color:#334155;font-size:11px;white-space:nowrap;font-weight:600}.status-inprogress{background:#e5f3ff;color:#075985}.status-done{background:#e5f7ec;color:#166534}.checkline{display:flex;align-items:center;gap:8px;font-weight:500;margin:4px 0 0}.checkline label{display:inline;margin:0;font-weight:500;font-size:12px}.actionbar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:13px}.spacer{flex:1}.tablebox{overflow-x:auto;max-width:100%}table{border-collapse:collapse;width:100%;text-align:left}th,td{padding:9px 9px;border-bottom:1px solid #e9eef3;vertical-align:top}th{background:#f3f7fa;color:#475569;font-weight:750;white-space:nowrap;font-size:11px;position:sticky;top:0;z-index:1}td{font-size:12px}tbody tr:hover{background:#f8fbfd}.tablebox table{min-width:1010px}.tablebox .namecell{min-width:160px;max-width:215px}.tablebox .hazcell{min-width:190px;max-width:280px}.tablebox .scorecell{min-width:110px}.cellsub{color:#64748b;font-size:11px;margin-top:3px;overflow-wrap:anywhere}.truncate2{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;overflow-wrap:anywhere}.filters{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.filters input{width:230px}.filters select{width:165px}.empty{padding:32px;text-align:center;color:#64748b}.foot{margin:15px 0 0;color:#64748b;font-size:11px;display:flex;gap:10px;justify-content:space-between;flex-wrap:wrap}.explain{margin-top:15px;padding:10px 12px;border:1px solid #dce6ef;border-radius:6px;background:#fff}.explain summary{font-weight:650;cursor:pointer;color:#075985}.explain p{color:#475569}.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}.required:after{content:' *';color:#c24141}.print-only{display:none}#toast{position:fixed;right:20px;bottom:20px;z-index:20;color:#fff;background:#143d54;border-radius:6px;padding:10px 15px;box-shadow:0 8px 30px #12283c35;max-width:390px;opacity:0;transform:translateY(10px);pointer-events:none;transition:opacity .2s,transform .2s}#toast.show{opacity:1;transform:translateY(0)}.print-note{color:#64748b;font-size:11px} .line{height:1px;background:#e8eef4;margin:10px 0}
/* Yalnızca Excel ve yazdırma işlemlerine ait yumuşak renkler */
.btn-excel-import{background:#e8f4ff;border-color:#b7d8fb;color:#075985}
.btn-excel-import:hover{background:#d7edff;border-color:#8cc5f6}
.btn-excel-export{background:#eaf8ef;border-color:#b9e2c6;color:#166534}
.btn-excel-export:hover{background:#dcf3e4;border-color:#8fcfa5}
.btn-print{background:#f1f3f5;border-color:#d6dbe1;color:#334155}
.btn-print:hover{background:#e5e9ee;border-color:#cbd5e1}
@media(max-width:930px){.g4{grid-template-columns:repeat(2,minmax(0,1fr))}.g5{grid-template-columns:repeat(3,minmax(0,1fr))}.score-grid{grid-template-columns:1fr}.badge-offline{display:none}}@media(max-width:600px){.wrap{padding:10px 10px 22px}.headerinner{padding:12px 10px}.header h1{font-size:15px}.header p{font-size:10px}.logo{width:37px;height:37px;font-size:14px}.g2,.g3,.g4,.g5{grid-template-columns:1fr}.span2,.span3{grid-column:auto}.metrics{grid-template-columns:repeat(2,minmax(0,1fr))}.metric{padding:10px}.metric b{font-size:21px}.metric .icon{display:none}.card-body{padding:11px}.card-header{padding:10px 11px;align-items:flex-start;flex-direction:column}.filters{width:100%}.filters input,.filters select{flex:1;min-width:135px;width:auto}.toolbar{width:100%}.score-fields{grid-template-columns:1fr 1fr}.actionbar .btn{flex:1}}
@page{size:A4 landscape;margin:11mm}
/* L tipi matrise özel bağımsız görünüm */
.static-field{display:flex;align-items:center;justify-content:space-between;gap:10px;background:#eef8ff;border:1px solid #b9def7;color:#075985;font-weight:750;padding:8px 10px;min-height:36px;border-radius:5px}
.static-field span{font-size:11px;font-weight:500;color:#547a96}
/* 5×5 L Tipi Matris: açık, dinamik ve erişilebilir renkli alan */
.matrix-card{margin-top:12px;background:#fff;border:1px solid #d4e3ef;border-radius:7px;overflow:hidden}
.matrix-heading{display:flex;justify-content:space-between;align-items:center;gap:10px;flex-wrap:wrap;background:#eff7fc;padding:10px 13px;border-bottom:1px solid #dceaf3}
.matrix-heading h3{font-size:14px;margin:0;color:#0b4669;letter-spacing:.01em}
.matrix-heading .hint{margin:0}
.matrix-layout{display:grid;grid-template-columns:minmax(300px,435px) minmax(0,1fr);gap:18px;padding:15px;align-items:start}
.matrix-scroll{min-width:0;overflow-x:auto}
.matrix-axis{text-align:center;color:#39536c;font-size:11px;font-weight:700;margin-bottom:5px}
.matrix-table{width:100%;table-layout:fixed;border-collapse:separate;border-spacing:4px;text-align:center}
.matrix-table th{font-size:11px;position:static;background:transparent;border:0;white-space:normal;text-align:center;padding:2px;color:#334155}
.matrix-table td{padding:0;border:0;height:49px;vertical-align:middle}
.matrix-cell{width:100%;height:49px;min-height:49px;position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:1px;border:3px solid transparent;border-radius:5px;font-size:16px;font-weight:800;cursor:pointer;transition:transform .12s,border-color .12s,box-shadow .12s}
.matrix-cell:hover{transform:scale(1.035);box-shadow:0 2px 8px #18253c35}
.matrix-cell:focus-visible{outline:3px solid #075985;outline-offset:1px}
.matrix-cell.m-dark{background:#167a46;color:#fff}
.matrix-cell.m-light{background:#91d9a3;color:#123c26}
.matrix-cell.m-yellow{background:#ffe179;color:#493509}
.matrix-cell.m-orange{background:#ffa95c;color:#51280a}
.matrix-cell.m-red{background:#e35d60;color:#fff}
.matrix-cell.is-before{border-color:#074a9b;box-shadow:0 0 0 1px #fff inset}
.matrix-cell.is-after{outline:3px dashed #7139ae;outline-offset:-6px}
.matrix-cell.is-before.is-after{border-color:#074a9b;outline:3px dashed #7139ae;outline-offset:-7px}
.matrix-cell .cell-labels{display:flex;gap:3px;position:absolute;bottom:2px;right:2px}
.matrix-cell .cell-labels span{font-size:9px;line-height:13px;height:14px;padding:0 3px;border-radius:2px;background:#fff;color:#154779}
.matrix-cell .cell-labels span.after-marker{background:#f1e5ff;color:#622f98}
.risk-vlow{color:#115735;background:#ccebd4;border-color:#92cc9f}
.matrix-legend{font-size:12px;color:#475569;display:grid;gap:11px;min-width:0}
.matrix-picker{padding:11px;border:1px solid #dfebf2;border-radius:6px;background:#f7fbfe}
.matrix-picker strong{display:block;font-size:12px;margin-bottom:8px;color:#234560}
.matrix-picker-controls{display:flex;gap:7px;flex-wrap:wrap}
.matrix-choice{padding:7px 10px;font-size:12px;min-height:34px;border:1px solid #cbd5e1;border-radius:5px;background:#fff;color:#475569;font-weight:700}
.matrix-choice.active{border-color:#0284c7;background:#dff1ff;color:#075985;box-shadow:0 0 0 1px #0284c7 inset}
.matrix-choice[data-side="after"].active{border-color:#8651b4;background:#f1e9fa;color:#61309c;box-shadow:0 0 0 1px #8651b4 inset}
.matrix-legend h4{font-size:12px;margin:1px 0 0;color:#25435e}
.matrix-legend-list{display:flex;gap:7px;flex-wrap:wrap}
.matrix-legend-item{display:flex;gap:6px;align-items:center;font-size:11px;background:#fff;border:1px solid #e2e8f0;border-radius:4px;padding:5px 7px;color:#34445a}
.matrix-swatch{width:15px;height:15px;flex:none;border-radius:3px;border:1px solid #00000013}
.matrix-swatch.m-dark{background:#167a46}.matrix-swatch.m-light{background:#91d9a3}.matrix-swatch.m-yellow{background:#ffe179}.matrix-swatch.m-orange{background:#ffa95c}.matrix-swatch.m-red{background:#e35d60}
.matrix-outcome{font-size:12px;color:#34445a;line-height:1.8}
.matrix-outcome b{color:#0f3652}
.matrix-legend p{margin:0;color:#64748b;font-size:11px}
.filters{justify-content:flex-end}.filters input{width:min(230px,100%)}
@media(max-width:760px){.matrix-layout{grid-template-columns:1fr}.matrix-legend{gap:10px}.static-field{flex-wrap:wrap}}
/* OLGUN UI2 · kapsamlı kurumsal çerçeve ve üst araç paneli */
:root{--bg:#f8fafc;--line:#cbd5e1;--surface:#fff;--brand:#0284c7;--radius:6px}
body{background:#edf3f8}
.olgun-frame{margin:8px;min-height:calc(100vh - 16px);width:calc(100% - 16px);max-width:none;border:2px solid #7f95a9;border-radius:7px;background:#f8fafc;box-shadow:0 5px 20px #203d5414;overflow:visible}
.header{position:sticky;top:0;z-index:30;border-bottom:1px solid #bfcfdb;border-top:4px solid var(--brand);background:#fff;color:#17283b;box-shadow:0 3px 10px #23405614}
.headerinner{max-width:none;min-height:77px;padding:10px 19px;gap:13px;display:flex;align-items:center}
.logo{width:62px;height:57px;flex:none;border:1px solid #cbd5e1;border-radius:5px;background:#f8fafc;color:#075985;display:grid;place-items:center;padding:3px;overflow:hidden;font-size:12px;line-height:1.15;cursor:pointer}
.logo:hover{background:#e8f4fc;border-color:#74bddf}.logo img{display:block;max-width:100%;max-height:100%;width:100%;height:100%;object-fit:contain}.logo img[hidden],.logo span[hidden]{display:none!important}
.header-titles{min-width:0;flex:1}.header h1{font-size:18px;line-height:1.27;color:#075985;margin:0;font-weight:750}.header p{margin:4px 0 0;color:#475569;font-size:12px;line-height:1.35}
.organization{text-align:right;max-width:42%;min-width:0;font-size:16px;font-weight:750;color:#1e3a57;white-space:nowrap;letter-spacing:.3px;flex-shrink:1;overflow-wrap:normal}
.header [contenteditable="true"]{outline:2px dashed #0284c7;outline-offset:4px;background:#f0f9ff;border-radius:3px;white-space:normal}
.ui2-action-row{display:flex;justify-content:flex-start;padding:12px 17px 3px}
.ui2-action-panel{display:inline-flex;justify-content:flex-start;align-items:center;gap:9px;flex-wrap:wrap;padding:10px 12px;border:1px solid #b9cbd9;border-radius:6px;background:#fff;box-shadow:0 1px 4px #1b476912}
.wrap{max-width:none;width:100%;padding:12px 17px 25px}
.card{border-color:#cbd5e1;border-radius:6px;box-shadow:0 2px 7px #22465b0b}
.card-header{background:#eaf5fc;border-bottom:1px solid #b9d4e6;border-left:4px solid #0284c7;padding:10px 13px;border-radius:5px 5px 0 0}
.card-header h2{color:#084c71}.section-number{background:#d2eafb;color:#075985}
.metric{border-top:3px solid #0284c7}
.matrix-card{border:1px solid #9fbfd1;border-radius:6px}.matrix-heading{background:#e9f4fc}
.print-snapshot{display:none}
@media(max-width:740px){.headerinner{padding:9px 11px;flex-wrap:wrap;gap:8px}.logo{width:49px;height:49px}.header-titles{flex:1 1 58%;}.header h1{font-size:15px}.header p{font-size:11px}.organization{font-size:12px;max-width:100%;flex:1 0 100%;text-align:right;white-space:normal;padding:0 2px}.ui2-action-row{padding:9px 9px 2px}.ui2-action-panel{width:100%;padding:8px;gap:6px}.ui2-action-panel .btn{flex:1 1 130px;white-space:normal}.wrap{padding:10px 9px 18px}.olgun-frame{margin:3px;width:calc(100% - 6px);min-height:calc(100vh - 6px)}}
/* PDF/Yazdır: aynı çerçeve, başlık, mavi bölüm başlıkları, renkli matris ve gerçek alan değerleri */
@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!important;print-color-adjust:exact!important}
*{-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important}
.olgun-frame{margin:0!important;width:100%!important;min-height:0!important;border:1.6px solid #7f95a9!important;border-radius:4px!important;box-shadow:none!important;overflow:visible!important;background:#f8fafc!important}
.header{position:static!important;display:block!important;box-shadow:none!important;top:auto!important;border-top:4px solid #0284c7!important}
.headerinner{padding:8px 12px!important;min-height:62px!important}
.header .logo{width:52px!important;height:49px!important}
.header h1{font-size:17px!important}.header p{font-size:11px!important}.organization{font-size:15px!important}
.ui2-action-row,#app,#toast,#logoChooser{display:none!important}
.print-snapshot{display:block!important;padding:9px 10px 4px!important}
.print-snapshot .metrics{grid-template-columns:repeat(4,minmax(0,1fr))!important;gap:7px!important;margin:0 0 9px!important}
.print-snapshot .metric{padding:7px 10px!important;break-inside:avoid!important}.print-snapshot .metric b{font-size:19px!important}.print-snapshot .metric .icon{display:none!important}
.print-snapshot .stack{gap:9px!important}.print-snapshot .card{box-shadow:none!important;border:1px solid #bdccd9!important;break-inside:auto!important;page-break-inside:auto!important}
.print-snapshot .card-header{break-after:avoid!important;page-break-after:avoid!important;padding:8px 10px!important;background:#eaf5fc!important;border-left:4px solid #0284c7!important}
.print-snapshot .card-header h2{font-size:12px!important}
.print-snapshot .card-body{padding:9px 11px!important}
.print-snapshot .g4{grid-template-columns:repeat(4,minmax(0,1fr))!important}.print-snapshot .g3{grid-template-columns:repeat(3,minmax(0,1fr))!important}.print-snapshot .g2{grid-template-columns:repeat(2,minmax(0,1fr))!important}.print-snapshot .span2{grid-column:span 2!important}.print-snapshot .span3{grid-column:span 3!important}
.print-snapshot .grid{gap:8px!important}
.print-snapshot .score-grid{grid-template-columns:1fr 1fr!important;gap:9px!important}
.print-snapshot .score-panel{break-inside:avoid!important;background:#fff!important}
.print-snapshot .score-fields{gap:5px!important;padding:7px!important}.print-snapshot .score-result{margin:0 7px 7px!important;padding:6px!important}.print-snapshot .score-result b{font-size:17px!important}
.print-snapshot .matrix-card{break-inside:avoid!important;page-break-inside:avoid!important;border-color:#a8c2d5!important}
.print-snapshot .matrix-heading{padding:7px 9px!important;background:#e9f4fc!important}
.print-snapshot .matrix-layout{display:grid!important;grid-template-columns: 44% minmax(0,1fr)!important;gap:9px!important;padding:8px!important}
.print-snapshot .matrix-cell{height:31px!important;min-height:31px!important;font-size:12px!important;padding:0!important;box-shadow:none!important;transform:none!important}
.print-snapshot .matrix-table{border-spacing:2px!important}
.print-snapshot .matrix-table td{height:31px!important}
.print-snapshot .matrix-picker{display:none!important}
.print-snapshot .matrix-legend{gap:4px!important;font-size:10px!important}.print-snapshot .matrix-legend-list{gap:4px!important}
.print-snapshot .matrix-legend-item{font-size:9px!important;padding:3px 4px!important}
.print-snapshot .matrix-outcome{line-height:1.35!important;font-size:11px!important}
.print-snapshot .matrix-legend p{font-size:9px!important}
.print-snapshot .actionbar,.print-snapshot .record-bottom-actions,.print-snapshot .toolbar,.print-snapshot .filters,.print-snapshot .explain,.print-snapshot footer,.print-snapshot .btn{display:none!important}
.print-snapshot .print-field-value{border:1px solid #cbd5e1;background:#fff;min-height:24px;padding:4px 6px;border-radius:4px;white-space:pre-wrap;overflow-wrap:anywhere;font-size:10px;line-height:1.35}
.print-snapshot .tablebox{overflow:visible!important}.print-snapshot table:not(.matrix-table){min-width:0!important;width:100%!important;table-layout:fixed}
.print-snapshot table:not(.matrix-table) th,.print-snapshot table:not(.matrix-table) td{padding:4px!important;font-size:9px!important;overflow-wrap:anywhere;white-space:normal!important}
.print-snapshot table:not(.matrix-table) thead{display:table-header-group!important}
.print-snapshot table:not(.matrix-table) tr{break-inside:avoid!important;page-break-inside:avoid!important}
.print-snapshot table:not(.matrix-table) th{position:static!important;background:#eaf5fc!important}
.print-snapshot .card-header,.print-snapshot .metric,.print-snapshot .score-panel{break-inside:avoid!important}
.print-snapshot .foot{display:none!important}
}
/* Olgun UI2 · düzenlenmiş kompakt form, altta tek dosya araç paneli */
.ui2-action-row{padding:2px 17px 18px;justify-content:flex-start}
.ui2-action-panel{background:#fff;border-color:#afc7d7;box-shadow:0 2px 6px #10355010}
.form-segment-label{display:flex;align-items:center;gap:8px;margin:12px 0 9px;color:#075985;font-size:12px;font-weight:800;letter-spacing:.01em;border-bottom:1px solid #dcecf5;padding-bottom:6px}
.form-segment-label:first-child{margin-top:0}
.form-segment-label span{display:grid;place-items:center;flex:none;width:22px;height:22px;border:1px solid #b8daed;border-radius:4px;background:#eaf6fc;color:#0369a1;font-size:10px}
.hazard-fields textarea{min-height:68px}
.assessment-layout{display:grid;grid-template-columns:minmax(350px,.88fr) minmax(420px,1.12fr);gap:13px;align-items:stretch}
.assessment-scores{grid-template-columns:1fr;gap:10px;margin:0}
.assessment-scores .score-panel{transition:border-color .14s,box-shadow .14s;min-width:0}
.assessment-scores .score-panel.active-target{box-shadow:0 0 0 1px #74b0d4}
.assessment-scores .score-before.active-target{border-color:#297bb3}
.assessment-scores .score-after.active-target{border-color:#8651b4;box-shadow:0 0 0 1px #bca2e1}
.score-top strong{display:inline-flex;align-items:center;gap:7px}
.indicator{width:9px;height:9px;border-radius:50%;display:inline-block;background:#125ba9}
.ind-after{background:#8648b3;transform:rotate(45deg);border-radius:2px}
.assessment-layout .matrix-card{margin:0}
.assessment-layout .matrix-heading{padding:9px 12px}
.assessment-layout .matrix-layout{display:block;padding:12px 15px 9px}
.assessment-layout .matrix-scroll{max-width:620px;margin:auto}
.assessment-layout .matrix-axis{font-size:10px;line-height:1.4}
.assessment-layout .matrix-axis-bottom{margin:5px 0 0}
.assessment-layout .matrix-table{border-spacing:5px}
.assessment-layout .matrix-table td,.assessment-layout .matrix-cell{height:49px;min-height:49px}
.table-footer{padding:9px 12px}
#tableTitle{scroll-margin-top:100px}#formTitle{scroll-margin-top:110px}
@media(max-width:1020px){.assessment-layout{grid-template-columns:1fr}.assessment-scores{grid-template-columns:repeat(2,minmax(0,1fr))}.assessment-layout .matrix-scroll{max-width:620px}}
@media(max-width:640px){.assessment-scores{grid-template-columns:1fr}.assessment-layout .matrix-layout{padding:8px}.assessment-layout .matrix-table{border-spacing:2px}.assessment-layout .matrix-cell,.assessment-layout .matrix-table td{height:44px;min-height:44px}.form-segment-label{margin-top:10px}.ui2-action-row{padding:4px 9px 15px}}
/* Tarayıcı PDF çıktısında her sayfanın sağ altındaki gerçek sayfa numarası */
@page{size:A4 landscape;margin:9mm 9mm 16mm 9mm;
@bottom-right{content:"Sayfa " counter(page) " / " counter(pages);font-family:Arial,sans-serif;font-size:9pt;font-weight:600;color:#34495c;text-align:right;vertical-align:bottom;margin-bottom:1mm}}
@media print{
.ui2-action-row,.ui2-action-panel,#importBtn,#xlsxBtn,#pdfBtn{display:none!important}
.print-snapshot .form-segment-label{margin:6px 0 5px!important;font-size:10px!important;padding:0 0 3px!important;break-after:avoid!important}
.print-snapshot .form-segment-label span{width:18px!important;height:18px!important;font-size:9px!important}
.print-snapshot .assessment-layout{display:grid!important;grid-template-columns:42% minmax(0,1fr)!important;gap:8px!important;align-items:stretch!important;break-inside:avoid!important}
.print-snapshot .assessment-scores{display:grid!important;grid-template-columns:1fr!important;gap:6px!important}
.print-snapshot .assessment-layout .matrix-card{margin:0!important}
.print-snapshot .assessment-layout .matrix-layout{display:block!important;padding:5px 9px!important}
.print-snapshot .assessment-layout .matrix-scroll{max-width:none!important}
.print-snapshot .assessment-layout .matrix-cell,.print-snapshot .assessment-layout .matrix-table td{height:28px!important;min-height:28px!important}
.print-snapshot .assessment-layout .matrix-cell{font-size:11px!important}
.print-snapshot .assessment-layout .matrix-table{border-spacing:2px!important}
.print-snapshot .matrix-axis{font-size:9px!important}
.print-snapshot .hazard-fields textarea{min-height:0!important}
.print-snapshot .metrics{display:none!important}
.print-snapshot .g5{grid-template-columns:repeat(5,minmax(0,1fr))!important}
.print-snapshot .card-body{padding:6px 9px!important}
.print-snapshot .grid{gap:5px!important}
.print-snapshot .print-field-value{min-height:19px!important;padding:3px 4px!important;font-size:9px!important}
.print-snapshot .score-top{padding:5px 7px!important}
.print-snapshot .score-fields{padding:5px 7px!important}
.print-snapshot .score-result{margin:0 7px 5px!important;padding:4px 6px!important}
.print-snapshot .score-result b{font-size:14px!important}
.print-snapshot .assessment-layout .matrix-cell,.print-snapshot .assessment-layout .matrix-table td{height:26px!important;min-height:26px!important}
.print-snapshot .form-segment-label{margin:4px 0 3px!important}
.print-snapshot .card-header{padding:5px 9px!important}
.print-snapshot .headerinner{padding:5px 8px!important}
.print-snapshot .method-info{display:none!important}
.print-snapshot .table-footer{display:none!important}
.print-snapshot .form-segment-label + .grid{break-inside:avoid!important}
}
/* Olgun UI2: bütün çevresi kapalı kurumsal başlık */
#pageHeader {
width:100%; max-width:100%; margin:0; box-sizing:border-box;
border:1.5px solid #7f95a9; border-top:4px solid #0284c7;
border-radius:4px 4px 0 0; background:#fff;
}
#pageHeader .headerinner { width:100%; max-width:100%; min-width:0; box-sizing:border-box; }
#pageHeader .organization { margin-left:auto; }
.print-pages {display:none}
@media print{
#olgunFrame {display:none!important}
#printPages.print-pages {
display:table!important; width:100%!important; max-width:100%!important;
table-layout:fixed!important; border-spacing:0!important;
border-collapse:separate!important; margin:0!important;
border:0!important; background:#fff!important;
}
#printPages > thead{display:table-header-group!important}
#printPages > tbody{display:table-row-group!important}
#printPages > thead > tr, #printPages > tbody > tr{break-inside:auto!important;page-break-inside:auto!important}
#printPages > thead > tr > td {padding:0 0 7px!important;border:0!important;vertical-align:top!important}
#printPages > tbody > tr > td {padding:0!important;border:0!important;vertical-align:top!important}
#printHeader .print-header-duplicate{
position:static!important;top:auto!important;display:block!important;
width:100%!important; max-width:100%!important; height:auto!important;
border:1.5px solid #7f95a9!important;
border-top:4px solid #0284c7!important;
border-radius:4px!important; background:#fff!important;
box-shadow:none!important; margin:0!important;
}
#printHeader .headerinner {min-height:63px!important; padding:6px 12px!important;width:100%!important;max-width:100%!important}
#printHeader .logo{width:52px!important;height:49px!important}
#printHeader .header-titles h1{font-size:17px!important}
#printHeader .header-titles p{font-size:11px!important}
#printHeader .organization{margin-left:auto!important;font-size:15px!important;text-align:right!important}
#printPages .print-snapshot{display:block!important;padding:0 2px 4px!important;box-sizing:border-box!important}
}
/* PRINT FIX | Başlıkla hizalı Olgun UI2 dış çerçevesi her PDF sayfasında */
#printPageFrame{display:none}
@media print{
/* Ana uygulama çerçevesi baskıda gizlenerek tablo başlığı her sayfada tekrarlanır.
Dış çerçeve ise baskı sayfasına sabitlenir; uzun tablo boyunca kaybolmaz. */
#printPageFrame{
display:block!important;
position:fixed!important;
top:0!important;bottom:0!important;left:0!important;right:0!important;
width:auto!important;height:auto!important;
border:1.5px solid #7f95a9!important;
border-radius:0!important;
background:transparent!important;
box-sizing:border-box!important;
pointer-events:none!important;
z-index:900!important;
-webkit-print-color-adjust:exact!important;print-color-adjust:exact!important;
}
/* Sadece bir çizgi kümesi kullan: başlığın yan sınırlarını dış çerçeve çizer. */
#printHeader .print-header-duplicate{
width:100%!important;
border-left:0!important;border-right:0!important;
border-top:4px solid #0284c7!important;
border-bottom:1.5px solid #7f95a9!important;
border-radius:0!important;
margin:0!important;
}
#printPages.print-pages{width:100%!important;max-width:100%!important;margin:0!important}
#printPages > thead > tr > td{padding:0 0 7px!important}
#printPages > tbody > tr > td{
padding:0 10px 10px!important;
}
#printPages .print-snapshot{padding:0 0 6px!important}
#printPages .print-document{max-width:100%!important}
}
/* Risk UI: form altında tek satırlık, yalnızca ekranda görünen yöntem özeti */
.wrap.stack{gap:10px;padding-bottom:8px}
.ui2-action-row{padding-top:2px;padding-bottom:5px}
.risk-ui-footer{margin:0 17px 10px;padding:7px 0 5px;border-top:1px solid #dbe3eb;color:#475569;font-size:11px;line-height:1.45;text-align:left;white-space:nowrap;overflow-x:auto;overflow-y:hidden}
@media(max-width:740px){.risk-ui-footer{margin:0 9px 8px;font-size:10px}}
@media print{.risk-ui-footer{display:none!important}}
/* RISK UI | Çift görünüm: tekil risk ve bütün kayıtların 5×5 dağılımı */
.matrix-mode-controls{display:flex;align-items:center;gap:5px;flex-wrap:wrap}
.matrix-mode-btn{border:1px solid #bfd6e7;background:#fff;color:#315d76;padding:6px 9px;border-radius:5px;min-height:30px;font-size:11px;font-weight:700;line-height:1.2;white-space:normal}
.matrix-mode-btn.active,.matrix-mode-btn[aria-pressed="true"]{background:#dcedfb;color:#075985;border-color:#73acd0;box-shadow:inset 0 0 0 1px #87b8d5}
.matrix-mode-btn:focus-visible{outline:2px solid #0284c7;outline-offset:2px}
.matrix-summary[hidden]{display:none!important}
.matrix-summary{margin:5px auto 0;max-width:620px;min-height:22px;display:flex;align-items:center;justify-content:center;text-align:center;gap:6px;flex-wrap:wrap;color:#50677a;font-size:11px;line-height:1.3}
.matrix-summary strong{color:#084d74}
.matrix-cell.matrix-distribution-cell{display:flex;align-items:center;justify-content:center;flex-direction:column;gap:0;cursor:default;padding:2px!important;box-shadow:none}
.matrix-distribution-cell .distribution-qty{font-size:19px;font-weight:900;line-height:1.04;font-variant-numeric:tabular-nums;letter-spacing:.01em}
.matrix-distribution-cell .distribution-score{font-size:9px;font-weight:600;opacity:.86;line-height:1.25}
.matrix-distribution-cell.is-empty .distribution-qty{opacity:.45}
@media(max-width:680px){.matrix-heading{align-items:stretch;flex-direction:column}.matrix-mode-controls{display:grid;grid-template-columns:1fr 1fr;width:100%}.matrix-mode-btn{padding:7px 4px;font-size:10px}.matrix-distribution-cell .distribution-qty{font-size:16px}.matrix-distribution-cell .distribution-score{font-size:8px}.matrix-summary{font-size:10px}}
@media print{.print-snapshot .matrix-mode-controls{display:none!important}.print-snapshot .matrix-distribution-cell .distribution-qty{font-size:13px!important}.print-snapshot .matrix-distribution-cell .distribution-score{font-size:7px!important}.print-snapshot .matrix-summary{font-size:9px!important;margin-top:1px!important;min-height:0!important}}
</style>
</head>
<body>
<div class="olgun-frame" id="olgunFrame">
<header class="header" id="pageHeader" aria-label="Olgun UI2 sabit başlık">
<div class="headerinner">
<button class="logo" id="logoTrigger" type="button" title="Logoyu değiştirmek için 5 kez art arda tıklayın" aria-label="Logo; değiştirmek için beş kez tıklayın"><img id="brandLogo" alt="Kurum logosu" hidden><span id="logoFallback" aria-hidden="true">LOGO</span></button>
<div class="header-titles"><h1 id="brandTitle" title="Başlığı düzenlemek için 5 kez art arda tıklayın">İSG RİSK ANALİZİ – L TİPİ MATRİS</h1><p id="brandSubtitle" title="Alt başlığı düzenlemek için 5 kez art arda tıklayın">(5×5 Olasılık × Şiddet · Risk değerlendirme ve önlem takip formu)</p></div>
<div class="organization" id="brandOrg" title="Kurum adını değiştirmek için 5 kez art arda tıklayın">OLGUN BAŞKANLIĞI</div>
</div>
<input id="logoChooser" class="sr-only" type="file" accept="image/png,image/jpeg,image/webp,image/gif,image/svg+xml" aria-label="Yeni kurumsal logo seç">
</header>
<div id="app" class="wrap stack">
<section class="grid g4 metrics" aria-label="Özet göstergeler">
<div class="card metric"><div><small>TOPLAM RİSK KAYDI</small><b id="statTotal">0</b></div><span class="icon">▦</span></div>
<div class="card metric"><div><small>YÜKSEK / ÇOK YÜKSEK · AÇIK</small><b id="statCritical">0</b></div><span class="icon">!</span></div>
<div class="card metric"><div><small>HEDEF TARİHİ GEÇEN</small><b id="statOverdue">0</b></div><span class="icon">◷</span></div>
<div class="card metric"><div><small>ÖNLEMİ DOĞRULANAN</small><b id="statVerified">0</b></div><span class="icon">✓</span></div>
</section>
<section class="card" aria-labelledby="metaTitle">
<div class="card-header"><h2 id="metaTitle"><span class="section-number">01</span> İş Yeri ve Değerlendirme Bilgileri</h2><span class="hint">Bilgiler bu tarayıcıya otomatik kaydedilir.</span></div>
<div class="card-body grid g5">
<div><label for="metaCompany">İşyeri / Firma</label><input id="metaCompany" data-meta="company" placeholder="Örn. ABC Üretim A.Ş."></div>
<div><label for="metaFacility">Tesis / Birim</label><input id="metaFacility" data-meta="facility" placeholder="Örn. Ana Üretim Tesisi"></div>
<div><label for="metaPrepared">Hazırlayan / Ekip</label><input id="metaPrepared" data-meta="prepared" placeholder="İSG risk değerlendirme ekibi"></div>
<div><label for="metaDate">Değerlendirme Tarihi</label><input type="date" id="metaDate" data-meta="date"></div>
<div><label for="metaRevision">Revizyon / Belge No.</label><input id="metaRevision" data-meta="revision" placeholder="Örn. RD-2026 / Rev.00"></div>
</div>
</section>
<section class="card" aria-labelledby="formTitle">
<div class="card-header"><h2 id="formTitle"><span class="section-number">02</span> <span id="formTitleText">Yeni Risk Kaydı</span></h2><div class="toolbar"><button class="btn btn-small" type="button" id="newBtn">+ Yeni Kayıt</button></div></div>
<div class="card-body">
<form id="riskForm" novalidate>
<div class="form-segment-label"><span>01</span> Riskin Tanımlanması</div>
<div class="grid g4 compact-intro">
<div><label>Değerlendirme Yöntemi</label><div class="static-field">5×5 L Tipi Matris <span>Olasılık × Şiddet</span></div><input id="method" type="hidden" value="matrix"></div>
<div><label for="unit" class="required">Bölüm / Alan</label><input id="unit" required placeholder="Örn. Üretim Bölümü"></div>
<div><label for="activity" class="required">Faaliyet / İş Adımı</label><input id="activity" required placeholder="Örn. Pres makinesi kullanımı"></div>
<div><label for="affected">Etkilenebilecek Kişiler</label><input id="affected" placeholder="Çalışan, taşeron, ziyaretçi..."></div>
</div>
<div class="grid g3 hazard-fields" style="margin-top:10px">
<div><label for="hazard" class="required">Tehlike Kaynağı</label><textarea id="hazard" required placeholder="Örn. Koruyucusuz hareketli makine parçaları"></textarea></div>
<div><label for="risk" class="required">Risk / Olası Zarar</label><textarea id="risk" required placeholder="Örn. Elin sıkışması, uzuv yaralanması"></textarea></div>
<div><label for="existing">Mevcut Kontroller / Önlemler</label><textarea id="existing" placeholder="Mevcut mühendislik ve idari önlemler"></textarea></div>
</div>
<div class="form-segment-label"><span>02</span> Risk Puanlaması ve 5×5 Matris</div>
<div class="assessment-layout">
<div class="score-grid assessment-scores">
<section class="score-panel score-before active-target" data-side="before" aria-label="Mevcut risk puanlaması"><div class="score-top"><strong><i class="indicator ind-before"></i> MEVCUT RİSK</strong><span class="hint">Önlem öncesi</span></div><div class="score-fields" id="beforeFields"></div><div class="score-result"><div><span class="hint">Risk puanı</span><br><b id="beforeScore">—</b></div><span id="beforeLevel" class="risk-tag">—</span></div></section>
<section class="score-panel score-after" data-side="after" aria-label="Hedef risk puanlaması"><div class="score-top"><strong><i class="indicator ind-after"></i> HEDEF RİSK</strong><span class="hint" id="afterTitle">Planlanan seviye</span></div><div class="score-fields" id="afterFields"></div><div class="score-result"><div><span class="hint">Hedef / kalan risk</span><br><b id="afterScore">—</b></div><span id="afterLevel" class="risk-tag">—</span></div></section>
</div>
<section class="matrix-card" id="matrixGuide" aria-labelledby="matrixTitle">
<div class="matrix-heading"><h3 id="matrixTitle">▦ 5×5 L Tipi Risk Matrisi</h3><div class="matrix-mode-controls" role="group" aria-label="Matris görünümü"><button class="matrix-mode-btn active" id="matrixModeSingle" type="button" aria-pressed="true">Tekil Risk Matrisi</button><button class="matrix-mode-btn" id="matrixModeDistribution" type="button" aria-pressed="false">Genel Risk Dağılımı</button></div></div>
<div class="matrix-layout">
<div class="matrix-scroll"><div class="matrix-axis">ŞİDDET →</div><div id="matrixVisual" aria-label="5 çarpı 5 renkli risk matrisi"></div><div id="matrixSummary" class="matrix-summary" aria-live="polite"></div><div class="matrix-axis matrix-axis-bottom">OLASILIK ↑</div></div>
</div>
</section>
</div>
<div class="form-segment-label"><span>03</span> Önlemler ve Aksiyon Takibi</div>
<div class="grid g4" style="margin-top:11px">
<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ü, eğitim, talimat vb."></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="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="Kontrol tarihi, kanıt, ölçüm, belge, revizyon notu..."></div>
</div>
<div style="margin-top:10px;padding:9px 11px;border:1px solid #d9e7ef;border-radius:5px;background:#f7fbfd"><div class="checkline"><input id="verified" type="checkbox"><label for="verified">Önlem uygulandı ve etkinliği kontrol edilerek <strong>kalan risk doğrulandı</strong></label></div><div class="hint" style="margin-top:4px">Bu seçenek yalnızca “Tamamlandı” durumunda kullanılabilir. İşaretlenmemişse ikinci risk puanı gerçekleşmiş sonuç değil, hedef değerdir.</div></div>
<div class="actionbar"><button class="btn btn-primary" type="submit" id="saveBtn">+ Riski Kaydet</button><button class="btn" id="cancelBtn" type="button">Vazgeç / Yeni Kayıt</button><div class="spacer"></div><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="section-number">03</span> Risk Kayıtları ve Aksiyon Takibi <span id="recordCount" class="hint"></span></h2><div class="filters"><input type="search" id="searchInput" aria-label="Kayıtlarda ara" placeholder="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="Risk kayıtları"><thead><tr><th>No.</th><th>Bölüm / Faaliyet</th><th>Tehlike / Risk</th><th>İlk Risk (Oת)</th><th>Son Risk (Oת)</th><th>Termin / Sorumlu</th><th>Durum</th><th>İşlemler</th></tr></thead><tbody id="riskRows"></tbody></table><div id="emptyState" class="empty" hidden>Henüz kayıt bulunmuyor. Yukarıdaki formdan risk ekleyebilirsiniz.</div></div>
<div class="card-body table-footer"><div class="toolbar record-bottom-actions"><button class="btn btn-danger" id="clearBtn" type="button">Tüm Kayıtları Sil</button></div></div>
</section>
</div>
<div class="ui2-action-row"><div class="ui2-action-panel" aria-label="Dosya ve yazdırma işlemleri">
<button class="btn btn-excel-import" id="importBtn" type="button">⬆ Excel İçeri Aktar</button>
<button class="btn btn-excel-export" id="xlsxBtn" type="button">⬇ Excel Dışarı Aktar</button>
<button class="btn btn-print" id="pdfBtn" type="button">▣ PDF / Yazdır</button>
<input class="sr-only" id="importFile" type="file" accept=".xlsx,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
</div></div>
<footer class="risk-ui-footer" aria-label="Yöntemin özeti">Temel özelliği: Olasılık × Şiddet Kullanım alanı: Ofisler, küçük ve orta ölçekli işletmeler</footer>
</div><!-- /olgun-frame -->
<!-- YAZDIRMA: tekrarlanan başlık ve her sayfada devam eden dış çerçeve. -->
<table id="printPages" class="print-pages" aria-hidden="true" role="presentation">
<thead><tr><td class="print-page-header"><div id="printHeader"></div></td></tr></thead>
<tbody><tr><td class="print-page-content"><div id="printReport" class="print-snapshot"></div></td></tr></tbody>
</table>
<!-- Sabit konumlu dış çerçeve PDF çıktısında her baskı sayfasında yeniden çizilir. -->
<div id="printPageFrame" aria-hidden="true"></div>
<div id="toast" role="status" aria-live="polite"></div>
<script>
'use strict';
(()=>{
// Eski anahtar yalnızca önceki formdan işyeri bilgilerini devralmak içindir.
// Önceki risk verilerini silmeyiz; bu sürümde risk kayıtları yalnızca açık oturumda tutulur.
const LEGACY_STORAGE_KEY='isg-risk-ltipi-matris-offline-v1';
const STORAGE_KEY='isg-risk-ltipi-matris-meta-v2';
const $=id=>document.getElementById(id);
const methodName={matrix:'5×5 L Tipi Matris'};
const statusName={open:'Açık',inprogress:'Devam Ediyor',done:'Tamamlandı'};
const today=()=>{const d=new Date();return [d.getFullYear(),String(d.getMonth()+1).padStart(2,'0'),String(d.getDate()).padStart(2,'0')].join('-')};
const randomId=()=>('R'+Date.now().toString(36)+Math.random().toString(36).slice(2,7)).toUpperCase();
const asText=v=>v===null||v===undefined?'':String(v);
const esc=v=>asText(v).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]));
const safeNumber=(value,fallback)=>{const n=Number(value);return Number.isFinite(n)&&n>0?n:fallback};
const initMeta=()=>({company:'',facility:'',prepared:'',date:today(),revision:''});
const defaults={matrix:{before:{p:3,s:3},after:{p:1,s:3}}};
const clone=v=>JSON.parse(JSON.stringify(v));
const matrixOptions=[{v:1,t:'1 — Çok düşük'},{v:2,t:'2 — Düşük'},{v:3,t:'3 — Orta'},{v:4,t:'4 — Yüksek'},{v:5,t:'5 — Çok yüksek'}];
const severityOptions=[{v:1,t:'1 — Önemsiz'},{v:2,t:'2 — Hafif'},{v:3,t:'3 — Orta'},{v:4,t:'4 — Ciddi'},{v:5,t:'5 — Çok ciddi'}];
const scheme={matrix:[{k:'p',l:'Olasılık (1–5)',opts:matrixOptions},{k:'s',l:'Şiddet (1–5)',opts:severityOptions}]};
let state={meta:initMeta(),records:[]};let editingId=null;let working=clone(defaults.matrix);let toastTimeout;
function normalizeRecord(raw){
if(!raw||typeof raw!=='object')return null;
if(raw.method && raw.method!=='matrix')return null;
const base=clone(defaults.matrix);
for(const side of ['before','after'])for(const key of ['p','s']){
const found=raw[side]?.[key];
if([1,2,3,4,5].includes(Number(found)))base[side][key]=Number(found);
}
return {id:asText(raw.id)||randomId(),method:'matrix',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:/^\d{4}-\d{2}-\d{2}$/.test(asText(raw.deadline))?raw.deadline:'',status:Object.hasOwn(statusName,raw.status)?raw.status:'open',verified:(raw.verified===true||raw.verified==='true'||raw.verified==='Evet') && raw.status==='done',notes:asText(raw.notes),created:asText(raw.created)||today(),before:base.before,after:base.after};
}
/* Risk kayıtları oturumluk: F5 / yeni açılışta 03 Risk Kayıtları boş gelir.
İşyeri bilgileri ve kurumsal başlık/logonun yerel saklanması devam eder.
Önceki sürümün kayıtları ayrı anahtarında korunur; otomatik geri yüklenmez. */
function load(){
state.records=[];
try{
const value=localStorage.getItem(STORAGE_KEY)||localStorage.getItem(LEGACY_STORAGE_KEY);
if(!value)return;
const parsed=JSON.parse(value);
if(parsed.meta&&typeof parsed.meta==='object'){
for(const k of Object.keys(state.meta))if(parsed.meta[k]!=null)state.meta[k]=asText(parsed.meta[k]);
}
}catch(e){console.warn('Yerel işyeri bilgileri okunamadı:',e)}
}
function persist(){
try{
// Bilerek records alanı yazılmaz; riskler yalnızca bu açık sayfanın belleğindedir.
localStorage.setItem(STORAGE_KEY,JSON.stringify({meta:state.meta}));
}catch(e){console.warn('Yerel işyeri bilgileri kaydedilemedi:',e);toast('İşyeri bilgileri saklanamadı; Excel yedeği alın.')}
}
function toast(message){clearTimeout(toastTimeout);const el=$('toast');el.textContent=message;el.classList.add('show');toastTimeout=setTimeout(()=>el.classList.remove('show'),3400)}
function score(method,factors){const f=factors||defaults.matrix.before;return Number(f.p)*Number(f.s)}
function level(method,value){const index=value<=2?0:value<=5?1:value<=10?2:value<=15?3:4;return [{className:'risk-vlow',label:'Çok Düşük',rank:0},{className:'risk-low',label:'Düşük',rank:1},{className:'risk-medium',label:'Orta',rank:2},{className:'risk-high',label:'Yüksek',rank:3},{className:'risk-critical',label:'Çok Yüksek',rank:4}][index]}
function matrixColor(value){return value<=2?'m-dark':value<=5?'m-light':value<=10?'m-yellow':value<=15?'m-orange':'m-red'}
let matrixActiveSide='before';let matrixView='single';
function fmt(v){return Number(v).toLocaleString('tr-TR',{maximumFractionDigits:2})}
function factorFields(side){const method='matrix';return scheme[method].map(f=>`<div><label for="factor-${side}-${f.k}">${esc(f.l)}</label><select id="factor-${side}-${f.k}" data-side="${side}" data-factor="${f.k}">${f.opts.map(o=>`<option value="${o.v}" ${Number(working[side][f.k])===o.v?'selected':''}>${esc(o.t)}</option>`).join('')}</select></div>`).join('')}
function renderScoreFields(){
$('beforeFields').innerHTML=factorFields('before');$('afterFields').innerHTML=factorFields('after');
updatePreview();
}
function renderMatrix(){
const bp=working.before.p,bs=working.before.s,ap=working.after.p,as=working.after.s;
const isDistribution=matrixView==='distribution';
const counts=Array.from({length:6},()=>Array(6).fill(0));
if(isDistribution)for(const record of state.records){
const p=Number(record.before?.p),s=Number(record.before?.s);
if(Number.isInteger(p)&&p>=1&&p<=5&&Number.isInteger(s)&&s>=1&&s<=5)counts[p][s]++;
}
let html='<table class="matrix-table"><thead><tr><th scope="col">O ↑ / Ş →</th>';
for(let s=1;s<=5;s++)html+='<th scope="col">'+s+'</th>';html+='</tr></thead><tbody>';
for(let p=5;p>=1;p--){html+='<tr><th scope="row">'+p+'</th>';for(let s=1;s<=5;s++){
const v=p*s;
if(isDistribution){
const n=counts[p][s];
html+=`<td><div class="matrix-cell matrix-distribution-cell ${matrixColor(v)} ${n===0?'is-empty':''}" role="img" title="Olasılık ${p} × Şiddet ${s}: ${n} risk kaydı; risk puanı ${v}" aria-label="Olasılık ${p}, şiddet ${s}, ${n} risk kaydı, risk puanı ${v}"><span class="distribution-qty">${n||'—'}</span><span class="distribution-score">${v} puan</span></div></td>`;
}else{
const isBefore=bp===p&&bs===s,isAfter=ap===p&&as===s;
const marks=(isBefore?'<span title="Mevcut">M</span>':'')+(isAfter?'<span class="after-marker" title="Hedef">H</span>':'');
html+=`<td><button type="button" class="matrix-cell ${matrixColor(v)} ${isBefore?'is-before':''} ${isAfter?'is-after':''}" data-p="${p}" data-s="${s}" title="Olasılık ${p} × Şiddet ${s} = ${v} · ${level('matrix',v).label}" aria-label="Olasılık ${p}, şiddet ${s}, risk puanı ${v}, ${level('matrix',v).label}${isBefore?', mevcut risk':''}${isAfter?', hedef risk':''}"><span>${v}</span><span class="cell-labels">${marks}</span></button></td>`;
}
}html+='</tr>'}html+='</tbody></table>';
$('matrixVisual').innerHTML=html;
$('matrixModeSingle').classList.toggle('active',!isDistribution);
$('matrixModeDistribution').classList.toggle('active',isDistribution);
$('matrixModeSingle').setAttribute('aria-pressed',String(!isDistribution));
$('matrixModeDistribution').setAttribute('aria-pressed',String(isDistribution));
const summary=$('matrixSummary');
summary.hidden=!isDistribution;
summary.innerHTML=isDistribution
? `<strong>Toplam ${state.records.length} risk kaydı</strong> <span>· Önlem öncesi dağılım · Büyük sayı: kayıt adedi</span>`
: '';
}
function setMatrixView(mode){matrixView=mode==='distribution'?'distribution':'single';renderMatrix()}
function setMatrixActiveSide(side){if(matrixView!=='single')setMatrixView('single');matrixActiveSide=side;document.querySelectorAll('.assessment-scores .score-panel').forEach(panel=>panel.classList.toggle('active-target',panel.dataset.side===side))}
function updatePreview(){for(const side of ['before','after']){const val=score('matrix',working[side]);$(side+'Score').textContent=fmt(val);const info=level('matrix',val),el=$(side+'Level');el.textContent=info.label;el.className='risk-tag '+info.className} $('afterTitle').textContent=$('verified').checked?'Kontrol sonrası doğrulandı':'Planlanan seviye';renderMatrix();}
function updateVerifyState(){const complete=$('status').value==='done';$('verified').disabled=!complete;if(!complete)$('verified').checked=false;updatePreview()}
function clearForm(){editingId=null;working=clone(defaults[$('method').value]);$('riskForm').reset();$('method').value='matrix';working=clone(defaults.matrix);$('formTitleText').textContent='Yeni Risk Kaydı';$('saveBtn').textContent='+ Riski Kaydet';$('formInfo').textContent='Yeni kayıt oluşturuluyor.';renderScoreFields();updateVerifyState()}
function recordFromForm(){const val=id=>$(id).value.trim();return normalizeRecord({id:editingId||randomId(),method:$('method').value,unit:val('unit'),activity:val('activity'),hazard:val('hazard'),risk:val('risk'),affected:val('affected'),existing:val('existing'),action:val('action'),responsible:val('responsible'),deadline:val('deadline'),status:val('status'),verified:$('verified').checked,notes:val('notes'),created:editingId?(state.records.find(x=>x.id===editingId)?.created||today()):today(),before:working.before,after:working.after})}
function startEdit(id){const record=state.records.find(x=>x.id===id);if(!record)return;setMatrixView('single');editingId=id;const fieldNames=['method','unit','activity','hazard','risk','affected','existing','action','responsible','deadline','status','notes'];for(const key of fieldNames)$(key).value=record[key]||'';$('verified').checked=record.verified;working={before:clone(record.before),after:clone(record.after)};$('formTitleText').textContent='Risk Kaydını Düzenle';$('saveBtn').textContent='✓ Değişiklikleri Kaydet';$('formInfo').textContent='Düzenlenen kayıt: '+record.id;renderScoreFields();updateVerifyState();$('formTitle').scrollIntoView({behavior:'smooth',block:'start'});$('unit').focus({preventScroll:true})}
function deleteRecord(id){const r=state.records.find(x=>x.id===id);if(!r)return;if(!confirm('“'+r.hazard.slice(0,80)+'” kaydını kalıcı olarak silmek istiyor musunuz?'))return;state.records=state.records.filter(x=>x.id!==id);if(editingId===id)clearForm();persist();renderDashboard();toast('Risk kaydı silindi.')}
function duplicateRecord(id){const r=state.records.find(x=>x.id===id);if(!r)return;const obj=clone(r);obj.id=randomId();obj.created=today();obj.verified=false;obj.status='open';state.records.push(obj);persist();renderDashboard();startEdit(obj.id);toast('Kopya kayıt oluşturuldu ve düzenlemeye açıldı.')}
function scoreBadge(method,values){const v=score(method,values),l=level(method,v);return `<strong>${fmt(v)}</strong><div style="margin-top:4px"><span class="risk-tag ${l.className}">${l.label}</span></div>`}
function matchingRecords(){const query=$('searchInput').value.toLocaleLowerCase('tr-TR').trim(),status=$('statusFilter').value;return state.records.filter(r=>(!status||r.status===status)&&(!query||[r.unit,r.activity,r.hazard,r.risk,r.action,r.responsible,r.notes,r.affected].join(' ').toLocaleLowerCase('tr-TR').includes(query))).sort((a,b)=>{
const aOpen=!a.verified,bOpen=!b.verified;if(aOpen!==bOpen)return aOpen?-1:1;
const delta=level(b.method,score(b.method,b.before)).rank-level(a.method,score(a.method,a.before)).rank;
if(delta)return delta;
return (a.deadline||'9999').localeCompare(b.deadline||'9999');
})}
function renderDashboard(){
const t=today();$('statTotal').textContent=state.records.length;
$('statCritical').textContent=state.records.filter(r=>!r.verified&&level(r.method,score(r.method,r.before)).rank>=3).length;
$('statOverdue').textContent=state.records.filter(r=>!r.verified&&r.deadline&&r.deadline<t).length;
$('statVerified').textContent=state.records.filter(r=>r.verified).length;
if(matrixView==='distribution')renderMatrix();
const rows=matchingRecords();$('recordCount').textContent=`(${rows.length}/${state.records.length})`;
$('emptyState').hidden=rows.length!==0;
$('riskRows').innerHTML=rows.map((r,index)=>{
const late=!r.verified&&r.deadline&&r.deadline<t;const statusClass=r.status==='inprogress'?'status-inprogress':r.status==='done'?'status-done':'';
return `<tr><td><strong>${index+1}</strong><div class="cellsub">${esc(r.id.slice(-7))}</div></td><td class="namecell"><strong class="truncate2" title="${esc(r.unit)}">${esc(r.unit)}</strong><div class="cellsub truncate2">${esc(r.activity)}</div></td><td class="hazcell"><strong class="truncate2" title="${esc(r.hazard)}">${esc(r.hazard)}</strong><div class="cellsub truncate2" title="${esc(r.risk)}">${esc(r.risk)}</div></td><td class="scorecell">${scoreBadge(r.method,r.before)}</td><td class="scorecell">${scoreBadge(r.method,r.after)}<div class="cellsub">${r.verified?'✓ Doğrulandı':'Hedef'}</div></td><td>${late?'<strong style="color:#be123c">':''}${esc(r.deadline||'—')}${late?'</strong><div class="cellsub" style="color:#be123c">Süresi geçti</div>':''}<div class="cellsub">${esc(r.responsible||'—')}</div></td><td><span class="status ${statusClass}">${esc(statusName[r.status])}</span>${r.verified?'<div class="cellsub">✓ Kalan risk doğrulandı</div>':''}</td><td><div class="toolbar"><button class="btn btn-small btn-soft" type="button" data-act="edit" data-id="${esc(r.id)}" title="Düzenle">Düzenle</button><button class="btn btn-small" type="button" data-act="duplicate" data-id="${esc(r.id)}" title="Kopyala">Kopya</button><button class="btn btn-small btn-danger" type="button" data-act="delete" data-id="${esc(r.id)}" title="Sil">Sil</button></div></td></tr>`
}).join('');
}
function renderMeta(){document.querySelectorAll('[data-meta]').forEach(el=>el.value=state.meta[el.dataset.meta]||'')}
function downloadBlob(blob,filename){const url=URL.createObjectURL(blob),a=document.createElement('a');a.href=url;a.download=filename;document.body.appendChild(a);a.click();a.remove();setTimeout(()=>URL.revokeObjectURL(url),4000)}
function baseFilename(){return 'ISG_Risk_Analizi_L_Tipi_Matris_'+today()}
const EXCEL_COLUMNS=['Kayıt ID','Bölüm','Faaliyet','Tehlike','Olası Zarar / Risk','Etkilenenler','Mevcut Önlemler','Önce Olasılık','Önce Şiddet','Önce Risk Puanı','Önce Risk Seviyesi','Planlanan Tedbir','Sonra Olasılık','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.s,score('matrix',r.before),level('matrix',score('matrix',r.before)).label,r.action,r.after.p,r.after.s,score('matrix',r.after),level('matrix',score('matrix',r.after)).label,r.responsible,r.deadline,statusName[r.status],r.verified?'Evet':'Hayır',r.notes,r.created]}
function xmlEsc(v){return asText(v).replace(/[&<>"']/g,c=>({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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})]]}
function exportXlsx(){const rows=[EXCEL_COLUMNS,...state.records.map(recordToExcelRow)],m=state.meta;const summary=[['Alan','Değer'],['Yöntem','5×5 L Tipi Matris'],['İşyeri',m.company],['Tesis / Birim',m.facility],['Hazırlayan',m.prepared],['Değerlendirme Tarihi',m.date],['Revizyon',m.revision],['Toplam Risk',state.records.length],['Açık Yüksek / Çok Yüksek',state.records.filter(r=>!r.verified&&level('matrix',score('matrix',r.before)).rank>=3).length],['Doğrulanan Risk',state.records.filter(r=>r.verified).length],['Not','Risk bantları eğitim amaçlı örnektir; kurumsal kriterler geçerlidir.'],['Kaynak','İSG Risk Analizi - L Tipi Matris HTML']];downloadBlob(zipCreate(workbookFiles(rows,summary)),baseFilename()+'.xlsx');toast('Excel raporu oluşturuldu ('+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 v=Number(value);if(Number.isFinite(v)&&v>=1&&v<100000){const d=new Date(Date.UTC(1899,11,30)+Math.floor(v)*86400000);return d.toISOString().slice(0,10)}return ''}
function fromExcelRow(row,idx){const read=k=>row[idx.get(k)]??'',isOld=idx.has('Yöntem Kodu');
if(isOld&&asText(read('Yöntem Kodu')).trim()!=='matrix')return null;
const statusRaw=read('Durum');const status=Object.keys(statusName).find(k=>statusName[k]===statusRaw)|| (Object.hasOwn(statusName,statusRaw)?statusRaw:'open');
return normalizeRecord({id:read('Kayıt ID'),method:'matrix',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(isOld?'Önce P/O':'Önce Olasılık'),s:read(isOld?'Önce S':'Önce Şiddet')},after:{p:read(isOld?'Sonra P/O':'Sonra Olasılık'),s:read(isOld?'Sonra S':'Sonra Şiddet')},notes:read('Notlar'),created:normalizeDate(read('Kayıt Tarihi'))||today()});
}
async function importData(file){const lower=file.name.toLowerCase();
if(lower.endsWith('.xlsx')){const entries=await unzipEntries(await file.arrayBuffer());const strings=[];if(entries['xl/sharedStrings.xml']){const s=parseXml(entries['xl/sharedStrings.xml']);for(const item of s.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 dosyasında kayıt sayfası bulunamadı.');const head=new Map(rows[0].map((v,i)=>[v,i]));const common=['Kayıt ID','Tehlike','Bölüm','Faaliyet'];if(!common.every(k=>head.has(k)))throw new Error('Excel sütunları L Tipi Matris veri yapısıyla uyumlu değil.');
const isOld=head.has('Yöntem Kodu')&&head.has('Önce P/O');if(!isOld&&!['Önce Olasılık','Önce Şiddet','Sonra Olasılık','Sonra Şiddet'].every(k=>head.has(k)))throw new Error('Yalnızca L Tipi Matris şablonu veya önceki risk analizi Excel dosyası desteklenir.');
const dataRows=rows.slice(1).filter(r=>r.some(v=>asText(v).trim()));const records=dataRows.map(row=>fromExcelRow(row,head)).filter(Boolean);
const meta=initMeta(),info=parseXlsxSheet(entries['xl/worksheets/sheet2.xml'],strings);const mm=new Map(info.slice(1).map(r=>[r[0],r[1]]));for(const [key,label] of Object.entries({company:'İşyeri',facility:'Tesis / Birim',prepared:'Hazırlayan',date:'Değerlendirme Tarihi',revision:'Revizyon'}))if(mm.has(label))meta[key]=asText(mm.get(label));return {meta,records,skipped:dataRows.length-records.length}}
throw new Error('Yalnızca .xlsx biçiminde Excel dosyası seçin.')}
async function performImport(file){if(!file)return;
try{const incoming=await importData(file);if(!incoming.records.length){throw new Error(incoming.skipped?'Dosyada L Tipi Matris kaydı bulunamadı.':'İçeri aktarılacak risk kaydı bulunamadı.');}
if(state.records.length && !confirm(`Excel dosyasındaki ${incoming.records.length} L Tipi Matris kaydı yüklenecek. Mevcut ${state.records.length} risk kaydı silinip Excel içeriğiyle değiştirilecek. Devam etmeden önce Excel Dışarı Aktar ile yedek almanız önerilir. Onaylıyor musunuz?`))return;
state={meta:{...initMeta(),...incoming.meta},records:incoming.records};
persist();renderMeta();clearForm();setMatrixView('distribution');renderDashboard();toast(`${incoming.records.length} L Tipi Matris kaydı içeri aktarıldı.${incoming.skipped?' '+incoming.skipped+' farklı yöntem kaydı atlandı.':''}`);
}catch(e){console.error(e);alert('İçeri alma başarısız: '+e.message)}finally{$('importFile').value=''}
}
/* OLGUN UI2: Yerel logo, başlık ve kurum adı (5 art arda tıklama) */
const BRAND_KEY='isg-risk-ltipi-matris-olgun-ui2-brand-v1';
const BRAND_DEFAULT={title:'İSG RİSK ANALİZİ – L TİPİ MATRİS',subtitle:'(5×5 Olasılık × Şiddet · Risk değerlendirme ve önlem 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('Logo veya başlık cihazda saklanamadı. Daha küçük bir görsel deneyin.')}}
function setBrandLogo(source){const img=$('brandLogo'),fallback=$('logoFallback');img.onload=()=>{img.hidden=false;fallback.hidden=true};img.onerror=()=>{img.hidden=true;fallback.hidden=false};img.hidden=true;fallback.hidden=false;img.src=source||'logo.jpg';}
function initializeBrand(){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||BRAND_DEFAULT.title;$('brandSubtitle').textContent=brand.subtitle||BRAND_DEFAULT.subtitle;$('brandOrg').textContent=brand.org||BRAND_DEFAULT.org;setBrandLogo(brand.logo);
setupFiveClicks($('logoTrigger'),()=>{$('logoChooser').click()});
for(const [id,key] of [['brandTitle','title'],['brandSubtitle','subtitle'],['brandOrg','org']])setupFiveClicks($(id),()=>unlockBrandField($(id),key));
$('logoChooser').addEventListener('change',event=>{const file=event.target.files?.[0];if(!file)return;
if(!file.type.startsWith('image/')){alert('Lütfen bir resim dosyası seçin.');event.target.value='';return}
if(file.size>1250000){alert('Logo 1,25 MB’dan küçük olmalıdır.');event.target.value='';return}
const reader=new FileReader();reader.onload=()=>{brand.logo=String(reader.result||'');setBrandLogo(brand.logo);storeBrand();toast('Kurum logosu güncellendi.');};reader.readAsDataURL(file);event.target.value='';
});
}
function setupFiveClicks(el,action){let count=0,last=0;el.addEventListener('click',()=>{if(el.isContentEditable)return;const now=Date.now();count=(now-last>1150)?1:count+1;last=now;if(count>=5){count=0;action();}})}
function unlockBrandField(el,key){el.setAttribute('contenteditable','true');el.setAttribute('role','textbox');el.setAttribute('aria-label','Düzenlenebilir başlık alanı');el.focus();
const sel=window.getSelection(),range=document.createRange();range.selectNodeContents(el);sel.removeAllRanges();sel.addRange(range);let settled=false;
const save=()=>{if(settled)return;settled=true;el.removeAttribute('contenteditable');el.removeAttribute('role');el.removeAttribute('aria-label');const value=(el.textContent||'').replace(/\s+/g,' ').trim();brand[key]=value||BRAND_DEFAULT[key];el.textContent=brand[key];storeBrand();el.removeEventListener('blur',save);el.removeEventListener('keydown',onKey);toast('Başlık bilgisi kaydedildi.');};
const onKey=event=>{if(event.key==='Enter'){event.preventDefault();el.blur()}else if(event.key==='Escape'){event.preventDefault();el.textContent=brand[key];el.blur()}};
el.addEventListener('blur',save);el.addEventListener('keydown',onKey);
}
/* Ekrandaki gerçek alanları ve 5×5 matrisi koruyan yazdırılabilir görünüm */
function renderPrint(){const source=$('app'),clone=source.cloneNode(true);
// Kullanıcı kaydı kaydedince ekran yeni kayıt için temizlenir. PDF'de son kaydın
// gerçekten doldurulmuş değerlerini göstermek için salt yazdırma kopyasını doldur.
const latest=state.records.length && !$('hazard').value.trim() && !editingId?state.records[state.records.length-1]:null;
if(latest){
const label=clone.querySelector('#formTitleText');if(label)label.textContent='Son Kaydedilen Risk – Doldurulmuş Form';
for(const side of ['before','after']){
const val=score('matrix',latest[side]),info=level('matrix',val);
const n=clone.querySelector('#'+side+'Score');if(n)n.textContent=fmt(val);
const l=clone.querySelector('#'+side+'Level');if(l){l.textContent=info.label;l.className='risk-tag '+info.className}
}
const aft=clone.querySelector('#afterTitle');if(aft)aft.textContent=latest.verified?'Kontrol sonrası doğrulandı':'Planlanan seviye';
for(const btn of clone.querySelectorAll('.matrix-cell[data-p][data-s]')){
const p=Number(btn.dataset.p),v=Number(btn.dataset.s),bef=latest.before.p===p&&latest.before.s===v,aft=latest.after.p===p&&latest.after.s===v;
btn.classList.toggle('is-before',bef);btn.classList.toggle('is-after',aft);
btn.querySelector('.cell-labels').innerHTML=(bef?'<span>M</span>':'')+(aft?'<span class="after-marker">H</span>':'');
}
}
const copyValues={unit:'unit',activity:'activity',hazard:'hazard',risk:'risk',affected:'affected',existing:'existing',action:'action',responsible:'responsible',deadline:'deadline',status:'status',notes:'notes'};
const from=source.querySelectorAll('input,select,textarea'),to=clone.querySelectorAll('input,select,textarea');
for(let i=to.length-1;i>=0;i--){const src=from[i],dst=to[i];if(!src||!dst)continue;
if(src.type==='hidden'||src.type==='file'||src.type==='search'){dst.remove();continue}
let val=src.type==='checkbox'?(src.checked?'☑ Evet':'☐ Hayır'):(src.tagName==='SELECT'?(src.selectedOptions[0]?.textContent||''):src.value);
if(latest){
if(Object.hasOwn(copyValues,src.id)){const key=copyValues[src.id];val=key==='status'?(statusName[latest.status]||latest.status):latest[key];}
else if(src.id==='verified')val=latest.verified?'☑ Evet':'☐ Hayır';
else {const hit=/^factor-(before|after)-(p|s)$/.exec(src.id);if(hit){const n=latest[hit[1]][hit[2]];val=Array.from(src.options).find(o=>Number(o.value)===n)?.textContent||n}}
}
const holder=document.createElement('div');holder.className='print-field-value';holder.textContent=val||'—';dst.replaceWith(holder);
}
clone.removeAttribute('id');clone.classList.add('print-document');
clone.querySelectorAll('.record-bottom-actions').forEach(el=>el.closest('.card-body')?.remove());
clone.querySelectorAll('.filters,.actionbar,.record-bottom-actions,.toolbar,.explain,footer').forEach(el=>el.remove());
// Ekranda kısaltılmış satırlar yerine PDF'de tüm tehlike/tedbir kayıtları basılır.
const table=clone.querySelector('.tablebox table');if(table){
table.innerHTML='<thead><tr><th>No.</th><th>Bölüm / Faaliyet</th><th>Tehlike / Risk / Mevcut Önlem</th><th>Önce Oת</th><th>Alınacak Tedbir / Aksiyon</th><th>Sonra Oת</th><th>Sorumlu / Tarih / Durum</th></tr></thead>';
const tbody=document.createElement('tbody');
tbody.innerHTML=state.records.map((r,i)=>`<tr><td>${i+1}</td><td><b>${esc(r.unit)}</b><br>${esc(r.activity)}</td><td><b>Tehlike:</b> ${esc(r.hazard)}<br><b>Risk:</b> ${esc(r.risk)}<br><b>Etkilenen:</b> ${esc(r.affected||'—')}<br><b>Mevcut önlem:</b> ${esc(r.existing||'—')}</td><td>${r.before.p}×${r.before.s} = <b>${score('matrix',r.before)}</b><br>${esc(level('matrix',score('matrix',r.before)).label)}</td><td>${esc(r.action)}</td><td>${r.after.p}×${r.after.s} = <b>${score('matrix',r.after)}</b><br>${esc(r.verified?'Doğrulandı':'Hedef')}</td><td><b>${esc(r.responsible||'—')}</b><br>${esc(r.deadline||'—')}<br>${esc(statusName[r.status]||r.status)}${r.notes?'<br>'+esc(r.notes):''}</td></tr>`).join('')||'<tr><td colspan="7">Henüz kaydedilmiş risk bulunmuyor.</td></tr>';
table.append(tbody);
}
clone.querySelectorAll('[id]').forEach(el=>el.removeAttribute('id'));
const target=$('printReport');target.replaceChildren(clone);
const repeatedHeader=$('pageHeader').cloneNode(true);
repeatedHeader.removeAttribute('id'); repeatedHeader.classList.add('print-header-duplicate');
repeatedHeader.querySelector('#logoChooser')?.remove();
// Yazdırılan tekrarlı başlıkta logo etkileşimli buton değil, statik resim alanıdır.
const logoButton=repeatedHeader.querySelector('#logoTrigger');
if(logoButton){
const printLogo=document.createElement('div');
printLogo.className=logoButton.className;
printLogo.innerHTML=logoButton.innerHTML;
logoButton.replaceWith(printLogo);
}
repeatedHeader.querySelectorAll('[id]').forEach(el=>el.removeAttribute('id'));
$('printHeader').replaceChildren(repeatedHeader);
}
function bind(){
document.querySelectorAll('[data-meta]').forEach(el=>el.addEventListener('input',()=>{state.meta[el.dataset.meta]=el.value;persist()}));
// Sabit yöntem: 5×5 L Tipi Matris; yöntem seçimi bulunmaz.
for(const container of [$('beforeFields'),$('afterFields')])container.addEventListener('change',event=>{const el=event.target;if(el.matches('select[data-factor]')){setMatrixActiveSide(el.dataset.side);working[el.dataset.side][el.dataset.factor]=Number(el.value);updatePreview()}});
$('status').addEventListener('change',updateVerifyState);$('verified').addEventListener('change',updatePreview);
document.querySelectorAll('.assessment-scores .score-panel').forEach(panel=>{panel.addEventListener('pointerdown',()=>setMatrixActiveSide(panel.dataset.side));panel.addEventListener('focusin',()=>setMatrixActiveSide(panel.dataset.side));});
$('matrixModeSingle').addEventListener('click',()=>setMatrixView('single'));
$('matrixModeDistribution').addEventListener('click',()=>setMatrixView('distribution'));
$('matrixVisual').addEventListener('click',event=>{const btn=event.target.closest('button[data-p][data-s]');if(!btn)return;const side=matrixActiveSide,p=Number(btn.dataset.p),severity=Number(btn.dataset.s);working[side]={p,s:severity};$('factor-'+side+'-p').value=p;$('factor-'+side+'-s').value=severity;updatePreview()});
$('riskForm').addEventListener('submit',event=>{event.preventDefault();const form=$('riskForm');if(!form.checkValidity()){form.reportValidity();return}if($('verified').checked&&$('status').value!=='done'){alert('Kalan risk doğrulaması için aksiyon durumu “Tamamlandı” olmalıdır.');return}const r=recordFromForm();const wasEditing=Boolean(editingId);if(editingId){const i=state.records.findIndex(x=>x.id===editingId);if(i<0)return;state.records[i]=r}else state.records.push(r);persist();clearForm();setMatrixView('distribution');renderDashboard();toast(wasEditing?'Risk güncellendi.':'Risk kaydı başarıyla kaydedildi.')});
$('newBtn').addEventListener('click',clearForm);$('cancelBtn').addEventListener('click',clearForm);
$('riskRows').addEventListener('click',event=>{const btn=event.target.closest('button[data-act]');if(!btn)return;const id=btn.dataset.id;if(btn.dataset.act==='edit')startEdit(id);if(btn.dataset.act==='delete')deleteRecord(id);if(btn.dataset.act==='duplicate')duplicateRecord(id)});
for(const id of ['searchInput','statusFilter'])$(id).addEventListener('input',renderDashboard);
$('xlsxBtn').addEventListener('click',exportXlsx);
$('pdfBtn').addEventListener('click',()=>{renderPrint();window.print()});window.addEventListener('beforeprint',renderPrint);
$('importBtn').addEventListener('click',()=>$('importFile').click());$('importFile').addEventListener('change',e=>performImport(e.target.files?.[0]));
$('clearBtn').addEventListener('click',()=>{if(!state.records.length){toast('Silinecek kayıt bulunmuyor.');return}if(!confirm(`DİKKAT: ${state.records.length} risk kaydının tamamı silinecek. Bu işlem geri alınamaz. Önce Excel Dışarı Aktar ile yedek aldığınızdan emin olun. Devam edilsin mi?`))return;state.records=[];persist();clearForm();renderDashboard();toast('Tüm risk kayıtları silindi.')});
}
load();
$('searchInput').value='';$('statusFilter').value='';
initializeBrand();renderMeta();$('method').value='matrix';renderScoreFields();updateVerifyState();bind();renderDashboard();
// Tarayıcı geri/ileri önbelleğinden yeniden açıldığında da 03 bölümü boşaltılır.
window.addEventListener('pageshow',event=>{
if(!event.persisted)return;
state.records=[];
$('searchInput').value='';$('statusFilter').value='';
matrixView='single';clearForm();renderDashboard();
});
})();
</script>
</body>
</html>