{"id":24854,"date":"2026-04-09T21:58:11","date_gmt":"2026-04-09T21:58:11","guid":{"rendered":"https:\/\/www.protecfire.de\/?page_id=24854"},"modified":"2026-04-13T09:12:58","modified_gmt":"2026-04-13T09:12:58","slug":"phantom-calculator","status":"publish","type":"page","link":"https:\/\/www.protecfire.de\/ru\/phantom-calculator\/","title":{"rendered":"firespy Phantom calculator"},"content":{"rendered":"<div data-elementor-type=\"wp-page\" data-elementor-id=\"24854\" class=\"elementor elementor-24854\" data-elementor-post-type=\"page\">\n\t\t\t\t<div class=\"elementor-element elementor-element-48df4d7 e-flex e-con-boxed e-con e-parent\" data-id=\"48df4d7\" data-element_type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-f1317d0 elementor-widget elementor-widget-image\" data-id=\"f1317d0\" data-element_type=\"widget\" data-widget_type=\"image.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<img decoding=\"async\" width=\"277\" height=\"88\" src=\"https:\/\/www.protecfire.de\/wp-content\/uploads\/2026\/02\/Phantom-fp-horiz-white.svg\" class=\"attachment-large size-large wp-image-23663\" alt=\"Firespy Phantom logo - protecfire\" \/>\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-16802a0 elementor-widget elementor-widget-text-editor\" data-id=\"16802a0\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t\t\t<p><strong>POWERFUL SYSTEM, POWERFUL INTEGRATION!<\/strong><\/p>\t\t\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t<div class=\"elementor-element elementor-element-248b1c2 e-flex e-con-boxed e-con e-parent\" data-id=\"248b1c2\" data-element_type=\"container\" data-settings=\"{&quot;background_background&quot;:&quot;classic&quot;}\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-38c0b9e elementor-widget elementor-widget-html\" data-id=\"38c0b9e\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n<title>firespy PHANTOM\u00ae \u2014 System Calculator<\/title>\n<style>\n:root{\n  --brand:#E05A00;--brand-dark:#b84900;--brand-light:#fff3ec;\n  --g50:#f7f8fa;--g100:#f0efeb;--g200:#d8d6ce;--g400:#8a8880;--g600:#5a5956;--g900:#1e1e1d;\n  --teal:#0f6e56;--teal-l:#e1f5ee;--red:#c0392b;--red-l:#fdf0ef;\n  --blue-l:#e6f1fb;--blue:#185fa5;--blue-b:#042c53;\n  --r:12px;--rs:8px;\n  --shadow-sm:0 1px 3px rgba(0,0,0,0.07),0 1px 2px rgba(0,0,0,0.05);\n  --shadow-md:0 4px 12px rgba(0,0,0,0.08),0 2px 4px rgba(0,0,0,0.05);\n  --shadow-active:0 0 0 3px rgba(224,90,0,0.15),0 4px 12px rgba(0,0,0,0.1);\n}\n*{box-sizing:border-box;margin:0;padding:0;}\nbody{font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif;background:var(--g50);color:var(--g900);font-size:15px;line-height:1.6;}\n.header{background:#1a1919;padding:20px 28px;display:flex;align-items:center;}\n.logo{font-size:19px;font-weight:700;color:#fff;letter-spacing:-.3px;}\n.logo span{color:#ffffff;}\n.logo-sub{font-size:11px;color:#666;margin-top:2px;display:block;}\n.badge{font-size:11px;background:var(--brand);color:#fff;padding:2px 9px;border-radius:20px;font-weight:500;margin-left:auto;align-self:flex-start;}\n.main{max-width:1000px;margin:0 auto;padding:24px 18px 60px;}\n\n\/* \u2500\u2500 STEPPER \u2500\u2500 *\/\n.stitle{display:flex;align-items:center;gap:10px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.8px;color:var(--g400);margin-bottom:10px;margin-top:28px;}\n.stitle::before{content:attr(data-step);display:inline-flex;align-items:center;justify-content:center;min-width:22px;height:22px;padding:0 6px;background:var(--brand);color:#fff;border-radius:20px;font-size:10px;font-weight:700;letter-spacing:0;flex-shrink:0;}\n\n\/* \u2500\u2500 CARDS \u2500\u2500 *\/\n.card{background:#fff;border:none;border-radius:var(--r);padding:20px 22px;margin-bottom:12px;box-shadow:var(--shadow-sm);}\n.ctitle{font-size:13px;font-weight:600;margin-bottom:14px;color:var(--g900);display:flex;align-items:center;gap:8px;}\n.ctitle::before{content:'';display:inline-block;width:3px;height:14px;background:var(--brand);border-radius:2px;flex-shrink:0;}\nlabel{font-size:12px;color:var(--g600);display:block;margin-bottom:3px;}\nselect,input[type=number]{width:100%;padding:8px 10px;border:1.5px solid var(--g200);border-radius:var(--rs);font-size:13px;background:#f7f8fa;color:var(--g900);appearance:none;-webkit-appearance:none;outline:none;transition:border-color .15s,background .15s,box-shadow .15s;}\nselect{background-image:url(\"data:image\/svg+xml,%3Csvg xmlns='http:\/\/www.w3.org\/2000\/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%238a8880' stroke-width='1.5' fill='none' stroke-linecap='round'\/%3E%3C\/svg%3E\");background-repeat:no-repeat;background-position:right 10px center;padding-right:28px;}\nselect:focus,input:focus{border-color:var(--brand);background:#fff;box-shadow:0 0 0 3px rgba(224,90,0,0.1);}\ninput[type=text],input[type=email]{width:100%;padding:8px 10px;border:1.5px solid var(--g200);border-radius:var(--rs);font-size:13px;background:#f7f8fa;color:var(--g900);outline:none;transition:border-color .15s,background .15s,box-shadow .15s;}\ninput[type=text]:focus,input[type=email]:focus{border-color:var(--brand);background:#fff;box-shadow:0 0 0 3px rgba(224,90,0,0.1);}\ninput.adj{border-color:#f0c4a0;background:#fff9f5;}\ninput.adj:focus{border-color:var(--brand);background:#fff;}\n.r2{display:grid;grid-template-columns:1fr 1fr;gap:12px;}\n.r3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:12px;}\n.r4{display:grid;grid-template-columns:1fr 1fr 1fr 1fr;gap:12px;}\n.f{margin-bottom:10px;}\n.hint{font-size:11px;color:var(--g400);margin-top:3px;display:block;}\n\n\/* \u2500\u2500 PREMIUM MCARDS \u2500\u2500 *\/\n.mcard-grid{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:6px;}\n.mcard{background:#fff;border:2px solid var(--g200);border-radius:var(--r);padding:18px 20px;cursor:pointer;transition:border-color .2s,background .2s,box-shadow .2s,transform .15s;box-shadow:var(--shadow-sm);}\n.mcard:hover{border-color:var(--brand);box-shadow:var(--shadow-md);transform:translateY(-1px);}\n.mcard.active{border-color:var(--brand);background:var(--brand-light);box-shadow:var(--shadow-active);transform:translateY(-1px);}\n.mcard p{font-size:12px;color:var(--g600);margin:0;line-height:1.5;}\n.micon{width:36px;height:36px;background:var(--g100);border-radius:8px;display:flex;align-items:center;justify-content:center;margin-bottom:10px;color:#666;transition:background .2s,color .2s;}\n.mcard:hover .micon{background:rgba(224,90,0,0.12);color:var(--brand);}\n.mcard.active .micon{background:var(--brand);color:#fff;box-shadow:0 2px 8px rgba(224,90,0,0.35);}\n\n.banner{border-radius:var(--rs);padding:11px 14px;font-size:13px;margin-bottom:12px;border:1px solid;display:flex;align-items:flex-start;gap:9px;}\n.banner.green{background:var(--teal-l);border-color:#5dcaa5;color:#085041;}\n.banner.blue{background:var(--blue-l);border-color:#85b7eb;color:var(--blue-b);}\n.banner.amber{background:#faeeda;border-color:#ef9f27;color:#412402;}\n.banner.orange{background:var(--brand-light);border-color:#f0c4a0;color:var(--brand-dark);}\n.pipe-diag{background:var(--g50);border:1px solid var(--g200);border-radius:var(--rs);padding:14px 16px;margin-bottom:12px;}\n.pipe-line{display:flex;align-items:center;gap:5px;margin-bottom:5px;font-size:12px;flex-wrap:wrap;}\n.pipe-seg{display:inline-flex;align-items:center;gap:4px;background:#fff;border:.5px solid var(--g200);border-radius:4px;padding:3px 8px;font-size:12px;color:var(--g900);}\n.pipe-seg.ext12{border-color:#85b7eb;background:var(--blue-l);color:var(--blue-b);}\n.pipe-seg.det8{border-color:#5dcaa5;background:var(--teal-l);color:#085041;}\n.pipe-seg.mr8{border-color:#f0c4a0;background:var(--brand-light);color:var(--brand-dark);}\n.pipe-seg.duct{border-color:#f09595;background:#fcebeb;color:#501313;}\n.pipe-arrow{color:var(--g400);font-size:13px;}\n.gc-row{display:grid;grid-template-columns:80px 1fr 1fr;gap:10px;align-items:center;padding:10px 13px;background:var(--g50);border:1px solid var(--g200);border-radius:var(--rs);margin-bottom:6px;}\n.app-row{display:grid;grid-template-columns:2fr 80px 130px auto;gap:10px;align-items:end;padding:10px 13px;background:var(--g50);border:1px solid var(--g200);border-radius:var(--rs);margin-bottom:6px;}\n.app-row .f{margin-bottom:0;}\n.duct-row{display:grid;grid-template-columns:90px 1fr 1fr 1fr 1fr 1fr;gap:10px;align-items:end;padding:10px 13px;background:var(--blue-l);border:1px solid #b5d4f4;border-radius:var(--rs);margin-bottom:6px;}\n.duct-row .f{margin-bottom:0;}\n.adj-row{display:grid;grid-template-columns:repeat(5,1fr);gap:10px;padding:14px 16px;background:#fff9f5;border:1.5px solid #f0c4a0;border-radius:var(--rs);margin-top:8px;}\n.adj-row .f{margin-bottom:0;}\n.adj-label{font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--brand-dark);margin-bottom:5px;display:block;}\n.btn-add{display:inline-flex;align-items:center;gap:5px;padding:7px 14px;background:var(--brand-light);color:var(--brand-dark);border:1.5px solid #f0c4a0;border-radius:var(--rs);font-size:12px;font-weight:500;cursor:pointer;margin-top:4px;transition:background .15s,box-shadow .15s;}\n.btn-add:hover{background:#ffe0cc;box-shadow:var(--shadow-sm);}\n.btn-rm{display:flex;align-items:center;justify-content:center;width:28px;height:28px;background:var(--red-l);color:var(--red);border:none;border-radius:var(--rs);cursor:pointer;font-size:17px;flex-shrink:0;line-height:1;}\n.btn-rm:hover{background:#fad7d5;}\n\n\/* \u2500\u2500 DRAMATIC CALCULATE BUTTON \u2500\u2500 *\/\n.btn-calc{width:100%;padding:16px;background:linear-gradient(135deg,#E05A00 0%,#c84d00 100%);color:#fff;border:none;border-radius:var(--r);font-size:16px;font-weight:700;cursor:pointer;letter-spacing:.2px;box-shadow:0 4px 14px rgba(224,90,0,0.4),0 2px 4px rgba(0,0,0,0.1);transition:all .2s;position:relative;overflow:hidden;}\n.btn-calc::after{content:'';position:absolute;inset:0;background:linear-gradient(135deg,rgba(255,255,255,0.1) 0%,rgba(255,255,255,0) 60%);pointer-events:none;}\n.btn-calc:hover{transform:translateY(-2px);box-shadow:0 6px 20px rgba(224,90,0,0.5),0 2px 6px rgba(0,0,0,0.12);}\n.btn-calc:active{transform:translateY(0);box-shadow:0 2px 8px rgba(224,90,0,0.3);}\n\n.btn-pdf{display:inline-flex;align-items:center;gap:6px;padding:10px 18px;background:var(--g900);color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;margin-bottom:16px;box-shadow:var(--shadow-sm);transition:all .15s;}\n.btn-pdf:hover{background:#333;box-shadow:var(--shadow-md);transform:translateY(-1px);}\n.hero{background:#1a1919;border-radius:var(--r);padding:20px 22px;margin-bottom:12px;color:#fff;box-shadow:var(--shadow-md);}\n.mets{display:grid;grid-template-columns:repeat(6,1fr);gap:8px;margin-top:14px;}\n.met{background:rgba(255,255,255,.07);border-radius:8px;padding:10px 10px;}\n.met-l{font-size:10px;color:#888;margin-bottom:2px;display:block;}\n.met-v{font-size:18px;font-weight:700;color:#fff;display:block;}\n.met-u{font-size:10px;color:#888;display:block;}\n.tandem-box{background:var(--teal-l);border:1px solid #5dcaa5;border-radius:8px;padding:12px 16px;margin-bottom:12px;}\n.psummary{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:12px;}\n.pbox{border-radius:var(--rs);padding:12px 14px;border:1px solid;}\n.pbox.single{background:var(--teal-l);border-color:#5dcaa5;color:#085041;}\n.pbox.dual{background:var(--blue-l);border-color:#85b7eb;color:var(--blue-b);}\n.noz-summary{background:#fff;border:none;border-radius:var(--r);overflow:hidden;margin-bottom:12px;box-shadow:var(--shadow-sm);}\n.noz-hdr{padding:10px 16px;background:var(--g50);border-bottom:1px solid var(--g200);font-size:12px;font-weight:600;color:var(--g600);}\n.noz-row{display:grid;grid-template-columns:1fr 60px 60px;gap:8px;align-items:center;padding:8px 16px;border-bottom:.5px solid var(--g100);font-size:13px;}\n.noz-zone{color:var(--g600);}\n.noz-qty{font-weight:600;color:var(--brand);text-align:right;}\n.noz-type{font-size:11px;background:var(--g100);color:var(--g600);padding:1px 7px;border-radius:12px;font-weight:500;text-align:center;}\n.noz-total{padding:10px 16px;background:var(--g50);display:flex;justify-content:space-between;font-size:13px;font-weight:600;border-top:.5px solid var(--g200);}\n.bom-card{background:#fff;border:none;border-radius:var(--r);overflow:hidden;margin-bottom:12px;box-shadow:var(--shadow-sm);}\n.bom-hdr{padding:10px 16px;background:var(--g50);border-bottom:1px solid var(--g200);font-size:12px;font-weight:600;color:var(--g600);display:flex;justify-content:space-between;}\ntable.bom{width:100%;border-collapse:collapse;font-size:13px;}\ntable.bom th{text-align:left;padding:8px 16px;font-size:10px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--g400);border-bottom:.5px solid var(--g200);}\ntable.bom td{padding:8px 16px;border-bottom:.5px solid var(--g100);color:var(--g900);vertical-align:top;}\ntable.bom tr:last-child td{border-bottom:none;}\ntable.bom .qty{font-weight:600;color:var(--brand);text-align:center;}\ntable.bom .ref{font-size:11px;color:var(--g400);font-family:monospace;}\ntable.bom .sec td{background:#1a1919;font-size:10px;color:#E05A00;font-weight:700;text-transform:uppercase;letter-spacing:1px;padding:8px 16px;border-left:3px solid #E05A00;}\ntable.bom .sec{border-top:6px solid var(--g50);}\ntable.bom .sec:first-child{border-top:none;}\n.warn-box{background:#fffbf0;border:1px solid #f0d88a;border-radius:var(--rs);padding:11px 14px;margin-bottom:12px;}\n.note{background:#f0f7ff;border-left:3px solid #378add;border-radius:0 var(--rs) var(--rs) 0;padding:10px 12px;font-size:12px;color:#0c447c;margin-top:8px;}\n.divider{border:none;border-top:1px solid var(--g200);margin:20px 0;}\n.field-error{border-color:#c0392b!important;box-shadow:0 0 0 3px rgba(192,57,43,0.15)!important;}\n.hidden{display:none!important;}\n\n@media(max-width:700px){\n  .mcard-grid,.r3,.r4,.mets,.psummary{grid-template-columns:1fr 1fr;}\n  .app-row,.duct-row,.gc-row{grid-template-columns:1fr 1fr;}\n  .adj-row{grid-template-columns:1fr 1fr;}\n  .mcard-grid[style*=\"3\"]{grid-template-columns:1fr 1fr!important;}\n  .adj-row[style*=\"6\"]{grid-template-columns:1fr 1fr 1fr!important;}\n  .adj-row[style*=\"3\"]{grid-template-columns:1fr 1fr!important;}\n}\n@media(max-width:480px){\n  .mcard-grid,.r2,.r3,.r4,.mets,.psummary{grid-template-columns:1fr!important;}\n  .app-row,.duct-row,.gc-row{grid-template-columns:1fr!important;}\n  .adj-row,.adj-row[style]{grid-template-columns:1fr 1fr!important;}\n  .mets{grid-template-columns:1fr 1fr 1fr!important;}\n}\n<\/style>\n<\/head>\n<body data-rsssl=1>\n\n<div class=\"header no-print\">\n  <div>\n    <span class=\"logo\">protecfire \u2014 firespy <span>PHANTOM\u00ae<\/span><\/span>\n    <span class=\"logo-sub\">System Design Calculator \u00b7 DIN EN 17446:2021<\/span>\n  <\/div>\n  <span class=\"badge\">v9.3<\/span>\n<\/div>\n\n\n\n<div class=\"main\">\n\n<!-- PROJECT INFO -->\n<span class=\"stitle no-print\" data-step=\"\u2726\" style=\"margin-top:0;\">Project information<\/span>\n<div class=\"card no-print\">\n  <div class=\"ctitle\">Project details<\/div>\n  <div class=\"r2\">\n    <div class=\"f\">\n      <label>Project name<\/label>\n      <input type=\"text\" id=\"proj-name\" placeholder=\"e.g. Michelin Star Restaurant\" style=\"width:100%;padding:8px 10px;border:1px solid var(--g200);border-radius:var(--rs);font-size:13px;background:#fff;color:var(--g900);outline:none;\">\n    <\/div>\n    <div class=\"f\">\n      <label>Company \/ design firm<\/label>\n      <input type=\"text\" id=\"proj-company\" placeholder=\"e.g. Your Company GmbH\" style=\"width:100%;padding:8px 10px;border:1px solid var(--g200);border-radius:var(--rs);font-size:13px;background:#fff;color:var(--g900);outline:none;\">\n    <\/div>\n    <div class=\"f\">\n      <label>Designer name<\/label>\n      <input type=\"text\" id=\"proj-designer\" placeholder=\"e.g. Max Mustermann\" style=\"width:100%;padding:8px 10px;border:1px solid var(--g200);border-radius:var(--rs);font-size:13px;background:#fff;color:var(--g900);outline:none;\">\n    <\/div>\n    <div class=\"f\">\n      <label>Email<\/label>\n      <input type=\"email\" id=\"proj-email\" placeholder=\"e.g. mustermann@company.de\" style=\"width:100%;padding:8px 10px;border:1px solid var(--g200);border-radius:var(--rs);font-size:13px;background:#fff;color:var(--g900);outline:none;\">\n    <\/div>\n    <div class=\"f\">\n      <label>Report validity (days)<\/label>\n      <select id=\"proj-validity\" style=\"width:100%;padding:8px 10px;border:1px solid var(--g200);border-radius:var(--rs);font-size:13px;background:#f7f8fa;color:var(--g900);outline:none;\">\n        <option value=\"30\">30 \u0434\u043d\u0435\u0439<\/option>\n        <option value=\"60\" selected>60 days<\/option>\n        <option value=\"90\">90 \u0434\u043d\u0435\u0439<\/option>\n        <option value=\"180\">180 days<\/option>\n        <option value=\"365\">1 \u0433\u043e\u0434<\/option>\n        <option value=\"0\">No expiry<\/option>\n      <\/select>\n      <span class=\"hint\">Validity period shown on the PDF report<\/span>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- STEP 1 -->\n<span class=\"stitle no-print\" data-step=\"1\" style=\"margin-top:0;\">Step 1 \u2014 Kitchen hood type<\/span>\n<div class=\"mcard-grid no-print\" style=\"grid-template-columns:1fr 1fr 1fr;\">\n  <div class=\"mcard active\" id=\"kt-wall\" onclick=\"setKT('wall')\">\n    <div class=\"micon\">\n      <svg width=\"20\" height=\"14\" viewbox=\"0 0 20 14\" fill=\"none\"><rect x=\"0\" y=\"0\" width=\"20\" height=\"3\" rx=\"1.5\" fill=\"currentColor\"\/><rect x=\"1\" y=\"5\" width=\"18\" height=\"8\" rx=\"1\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"\/><line x1=\"7\" y1=\"5\" x2=\"7\" y2=\"13\" stroke=\"currentColor\" stroke-width=\"1.2\"\/><line x1=\"13\" y1=\"5\" x2=\"13\" y2=\"13\" stroke=\"currentColor\" stroke-width=\"1.2\"\/><\/svg>\n    <\/div>\n    <h3 style=\"font-size:14px;font-weight:600;margin-bottom:2px;color:#1e1e1d;\">Wall hood<\/h3>\n    <p>Hood against a wall. Single pipe runs the hood length. Grease channel(s) on one side.<\/p>\n  <\/div>\n  <div class=\"mcard\" id=\"kt-central\" onclick=\"setKT('central')\">\n    <div class=\"micon\">\n      <svg width=\"20\" height=\"14\" viewbox=\"0 0 20 14\" fill=\"none\"><rect x=\"1\" y=\"0\" width=\"18\" height=\"14\" rx=\"1\" stroke=\"currentColor\" stroke-width=\"1.5\" fill=\"none\"\/><line x1=\"10\" y1=\"0\" x2=\"10\" y2=\"14\" stroke=\"currentColor\" stroke-width=\"2\" stroke-dasharray=\"2 2\"\/><circle cx=\"10\" cy=\"7\" r=\"2\" fill=\"currentColor\"\/><\/svg>\n    <\/div>\n    <h3 style=\"font-size:14px;font-weight:600;margin-bottom:2px;color:#1e1e1d;\">Central \/ island hood<\/h3>\n    <p>Suspended hood, open both sides. Pipe enters via lateral T-piece, runs U-shape. Cooking nozzles on both sides; detectors shared at centre.<\/p>\n  <\/div>\n  <div class=\"mcard\" id=\"kt-vceil\" onclick=\"setKT('vceil')\">\n    <div class=\"micon\">\n      <svg width=\"20\" height=\"14\" viewbox=\"0 0 20 14\" fill=\"none\"><rect x=\"0\" y=\"0\" width=\"20\" height=\"2\" rx=\"1\" fill=\"currentColor\"\/><line x1=\"0\" y1=\"5\" x2=\"20\" y2=\"5\" stroke=\"currentColor\" stroke-width=\"1\" stroke-dasharray=\"2 2\"\/><line x1=\"0\" y1=\"8\" x2=\"20\" y2=\"8\" stroke=\"currentColor\" stroke-width=\"1\" stroke-dasharray=\"2 2\"\/><line x1=\"0\" y1=\"11\" x2=\"20\" y2=\"11\" stroke=\"currentColor\" stroke-width=\"1\" stroke-dasharray=\"2 2\"\/><circle cx=\"5\" cy=\"5\" r=\"1.5\" fill=\"currentColor\"\/><circle cx=\"10\" cy=\"5\" r=\"1.5\" fill=\"currentColor\"\/><circle cx=\"15\" cy=\"5\" r=\"1.5\" fill=\"currentColor\"\/><\/svg>\n    <\/div>\n    <h3 style=\"font-size:14px;font-weight:600;margin-bottom:2px;color:#1e1e1d;\">Ventilated ceiling<\/h3>\n    <p>Nozzles embedded in ceiling panels with bulkhead adapters. Covers cooking zone by area or appliance. Always P2.<\/p>\n  <\/div>\n<\/div>\n\n<!-- STEP 2 -->\n<span class=\"stitle no-print\" data-step=\"2\">Step 2 \u2014 Protection strategy<\/span>\n<div class=\"mcard-grid no-print\">\n  <div class=\"mcard active\" id=\"mode-linear\" onclick=\"setMode('linear')\">\n    <div class=\"micon\">\n      <svg width=\"18\" height=\"10\" viewbox=\"0 0 18 10\" fill=\"none\"><circle cx=\"3\" cy=\"5\" r=\"2.5\" fill=\"currentColor\"\/><circle cx=\"9\" cy=\"5\" r=\"2.5\" fill=\"currentColor\"\/><circle cx=\"15\" cy=\"5\" r=\"2.5\" fill=\"currentColor\"\/><line x1=\"5.5\" y1=\"5\" x2=\"6.5\" y2=\"5\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><line x1=\"11.5\" y1=\"5\" x2=\"12.5\" y2=\"5\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg>\n    <\/div>\n    <h3 style=\"font-size:14px;font-weight:600;margin-bottom:2px;color:#1e1e1d;\">Area protection (linear)<\/h3>\n    <p>Nozzles every 350 mm along hood or ceiling plenum length, covering all appliances below.<\/p>\n  <\/div>\n  <div class=\"mcard\" id=\"mode-appliance\" onclick=\"setMode('appliance')\">\n    <div class=\"micon\">\n      <svg width=\"18\" height=\"14\" viewbox=\"0 0 18 14\" fill=\"none\"><rect x=\"1\" y=\"1\" width=\"6\" height=\"5\" rx=\"1.2\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><rect x=\"11\" y=\"1\" width=\"6\" height=\"5\" rx=\"1.2\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><rect x=\"1\" y=\"8\" width=\"6\" height=\"5\" rx=\"1.2\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><rect x=\"11\" y=\"8\" width=\"6\" height=\"5\" rx=\"1.2\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><\/svg>\n    <\/div>\n    <h3 style=\"font-size:14px;font-weight:600;margin-bottom:2px;color:#1e1e1d;\">Protection by appliance<\/h3>\n    <p>Dedicated nozzle(s) per cooking appliance type and size.<\/p>\n  <\/div>\n<\/div>\n\n<!-- STEP 3 -->\n<span class=\"stitle no-print\" data-step=\"3\">Step 3 \u2014 Installation type<\/span>\n<div class=\"mcard-grid no-print\">\n  <div class=\"mcard active\" id=\"inst-surface\" onclick=\"setInst('surface')\">\n    <div class=\"micon\">\n      <svg width=\"20\" height=\"14\" viewbox=\"0 0 20 14\" fill=\"none\">\n        <line x1=\"2\" y1=\"4\" x2=\"18\" y2=\"4\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\/>\n        <rect x=\"5\" y=\"2\" width=\"3\" height=\"4\" rx=\"1\" fill=\"currentColor\"\/>\n        <rect x=\"12\" y=\"2\" width=\"3\" height=\"4\" rx=\"1\" fill=\"currentColor\"\/>\n        <line x1=\"6.5\" y1=\"6\" x2=\"6.5\" y2=\"11\" stroke=\"currentColor\" stroke-width=\"1.2\"\/>\n        <circle cx=\"6.5\" cy=\"12\" r=\"1.2\" fill=\"currentColor\"\/>\n        <line x1=\"13.5\" y1=\"6\" x2=\"13.5\" y2=\"11\" stroke=\"currentColor\" stroke-width=\"1.2\"\/>\n        <circle cx=\"13.5\" cy=\"12\" r=\"1.2\" fill=\"currentColor\"\/>\n      <\/svg>\n    <\/div>\n    <h3 style=\"font-size:14px;font-weight:600;margin-bottom:2px;color:#1e1e1d;\">Surface mounted<\/h3>\n    <p>Pipe runs visibly inside the hood or ceiling, fixed with tube supports on stud-welded threaded pins. Standard \u2014 economical, easy to maintain.<\/p>\n  <\/div>\n  <div class=\"mcard\" id=\"inst-concealed\" onclick=\"setInst('concealed')\">\n    <div class=\"micon\">\n      <svg width=\"20\" height=\"14\" viewbox=\"0 0 20 14\" fill=\"none\">\n        <rect x=\"0\" y=\"0\" width=\"20\" height=\"3\" rx=\"1\" fill=\"currentColor\" opacity=\"0.4\"\/>\n        <rect x=\"0\" y=\"11\" width=\"20\" height=\"3\" rx=\"1\" fill=\"currentColor\" opacity=\"0.4\"\/>\n        <line x1=\"2\" y1=\"7\" x2=\"18\" y2=\"7\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-dasharray=\"2 2\"\/>\n        <circle cx=\"6\" cy=\"11\" r=\"2\" fill=\"currentColor\"\/>\n        <circle cx=\"14\" cy=\"11\" r=\"2\" fill=\"currentColor\"\/>\n      <\/svg>\n    <\/div>\n    <h3 style=\"font-size:14px;font-weight:600;margin-bottom:2px;color:#1e1e1d;\">Concealed (flush)<\/h3>\n    <p>Pipe hidden inside panel or plenum. Nozzles flush-mounted with bulkhead adapters \u2014 fixed, directional or glass panel. Premium finish for show kitchens and ventilated ceilings.<\/p>\n  <\/div>\n<\/div>\n\n<!-- STEP 4 -->\n<span class=\"stitle no-print\" data-step=\"4\">Step 4 \u2014 Hood dimensions &amp; routing<\/span>\n<div class=\"card no-print\">\n  <div class=\"ctitle\" id=\"dims-title\">Hood dimensions<\/div>\n  <div class=\"r3\">\n    <div class=\"f\">\n      <label>Hood length (mm)<\/label>\n      <input type=\"number\" id=\"hood-length\" value=\"2200\" min=\"300\" max=\"12000\" step=\"100\" oninput=\"autoCalc()\">\n      <span class=\"hint\" id=\"hood-length-hint\">Main dimension \u2014 pipe runs along this length<\/span>\n    <\/div>\n    <div class=\"f\">\n      <label>Hood width \/ lateral depth (mm) <span id=\"width-extra\" style=\"color:var(--g400);font-size:11px;\"><\/span><\/label>\n      <input type=\"number\" id=\"hood-width\" value=\"900\" min=\"300\" max=\"2000\" step=\"50\" oninput=\"autoCalc()\">\n      <span class=\"hint\" id=\"width-hint\">Reference only<\/span>\n    <\/div>\n    <div class=\"f\" id=\"vc-depth-wrap\" style=\"display:none;\">\n      <label>Ceiling zone depth (mm)<\/label>\n      <input type=\"number\" id=\"vc-depth\" value=\"1200\" min=\"300\" max=\"5000\" step=\"100\">\n      <span class=\"hint\">Reference only \u2014 depth of protected zone (for documentation)<\/span>\n    <\/div>\n    <div class=\"f\">\n      <label>Nozzle height above cooking surface (cm)<\/label>\n      <input type=\"number\" id=\"nozzle-h\" value=\"120\" min=\"10\" max=\"400\" step=\"5\" oninput=\"onHeight()\">\n      <div id=\"ntype-badge\" class=\"banner amber\" style=\"margin-top:6px;margin-bottom:0;padding:6px 10px;font-size:12px;\"><\/div>\n      <div id=\"vc-height-hint\" style=\"display:none;margin-top:4px;font-size:11px;color:var(--g400);\">Ventilated ceiling: ceiling height \u2212 appliance height (typically 85\u201390 cm). Max 260 cm for P2.<\/div>\n    <\/div>\n  <\/div>\n  <div class=\"r3\">\n    <div class=\"f\">\n      <label>Distance: container to hood entry (m)<\/label>\n      <input type=\"number\" id=\"dist-c\" value=\"2\" min=\"0.5\" max=\"20\" step=\"0.5\" oninput=\"autoCalc()\">\n      <span class=\"hint\">Pipe run from container to hood wall\/ceiling entry<\/span>\n    <\/div>\n    <div class=\"f\">\n      <label>Distance: container to manual release (m)<\/label>\n      <input type=\"number\" id=\"dist-mr\" value=\"3\" min=\"0.5\" max=\"40\" step=\"0.5\" oninput=\"autoCalc()\">\n      <span class=\"hint\">8\u00d71 control tube from container to manual release<\/span>\n    <\/div>\n    <div class=\"f\">\n      <label>UV-C cleaning system present?<\/label>\n      <select id=\"uvc\">\n        <option value=\"no\">No \u2014 use SPY 15 \/ SPY 20<\/option>\n        <option value=\"yes\">Yes \u2014 must use SPY-G 15 (79\u00b0C)<\/option>\n      <\/select>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- STEP 4 -->\n<span class=\"stitle no-print\" data-step=\"5\">Step 5 \u2014 Cooking zone<\/span>\n<div id=\"linear-section\" class=\"card no-print\">\n  <div class=\"ctitle\">Area protection \u2014 nozzle layout<\/div>\n  <div id=\"linear-preview\" class=\"banner green\" style=\"font-size:13px;margin-bottom:0;\"><\/div>\n<\/div>\n<div id=\"appliance-section\" class=\"card no-print hidden\">\n  <div class=\"ctitle\">Appliance list<\/div>\n  <p style=\"font-size:12px;color:var(--g600);margin-bottom:10px;\">Per \u00a76.1\u20136.5 of DIN EN 17446:2021. Nozzle count determined by appliance type and height above surface.<\/p>\n  <div id=\"appliance-list\"><\/div>\n  <button class=\"btn-add\" onclick=\"addApp()\">\n    <svg width=\"11\" height=\"11\" viewbox=\"0 0 11 11\" fill=\"none\"><path d=\"M5.5 1v9M1 5.5h9\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\"\/><\/svg>\n    Add appliance\n  <\/button>\n<\/div>\n\n<!-- STEP 5 -->\n<span class=\"stitle no-print\" data-step=\"6\">Step 6 \u2014 SPY detectors &amp; temperatures<\/span>\n<div class=\"card no-print\">\n  <div class=\"ctitle\">SPY detector configuration<\/div>\n  <p style=\"font-size:12px;color:var(--g600);margin-bottom:14px;\">Auto-calculated based on protection mode, hood length and ducts. Minimum 2 per system. Adjust upward if additional monitoring points are required.<\/p>\n\n  <div id=\"spy-fryer-row\" class=\"hidden\" style=\"margin-bottom:10px;\">\n    <div style=\"background:var(--g50);border:1px solid var(--g200);border-radius:var(--rs);padding:10px 14px;display:flex;align-items:center;justify-content:space-between;\">\n      <div>\n        <span style=\"font-size:12px;font-weight:600;color:var(--g900);\">SPY over deep fryers<\/span>\n        <span class=\"hint\" style=\"display:block;margin-top:2px;\">1 per deep fryer \u2014 highest heat concentration. Not adjustable.<\/span>\n      <\/div>\n      <span id=\"spy-fryer-val\" style=\"font-size:18px;font-weight:700;color:var(--teal);min-width:32px;text-align:right;\">0<\/span>\n    <\/div>\n  <\/div>\n\n  <div class=\"r3\" style=\"margin-bottom:10px;\">\n    <div class=\"f\">\n      <label id=\"spy-hood-label\">SPY in plenum \/ along hood <span id=\"spy-hood-min-label\" style=\"color:var(--g400);font-size:11px;\"><\/span><\/label>\n      <input type=\"number\" id=\"spy-hood-count\" min=\"0\" max=\"20\" step=\"1\" value=\"0\" oninput=\"updateSpyTotal()\">\n      <span class=\"hint\" id=\"spy-hood-hint\">Suggested: <span id=\"spy-hood-min\">0<\/span><\/span>\n    <\/div>\n    <div class=\"f\" id=\"spy-duct-row\">\n      <label>SPY in exhaust ducts <span id=\"spy-duct-min-label\" style=\"color:var(--g400);font-size:11px;\"><\/span><\/label>\n      <input type=\"number\" id=\"spy-duct-count\" min=\"0\" max=\"20\" step=\"1\" value=\"0\" oninput=\"updateSpyTotal()\">\n      <span class=\"hint\">1 per duct \u2014 suggested: <span id=\"spy-duct-min\">0<\/span><\/span>\n    <\/div>\n    <div class=\"f\">\n      <label>SPY trigger temperature<\/label>\n      <select id=\"spy-temp\">\n        <option value=\"79\">79\u00b0C \u2014 standard kitchens<\/option>\n        <option value=\"93\" selected>93\u00b0C \u2014 high-heat zones<\/option>\n        <option value=\"182\">182\u00b0C \u2014 deep fryer \/ exhaust<\/option>\n      <\/select>\n    <\/div>\n  <\/div>\n\n  <div style=\"background:var(--teal-l);border:1px solid #5dcaa5;border-radius:var(--rs);padding:10px 14px;display:flex;align-items:center;justify-content:space-between;\">\n    <span style=\"font-size:13px;font-weight:600;color:#085041;\">Total SPY detectors<\/span>\n    <span id=\"spy-total-display\" style=\"font-size:22px;font-weight:700;color:#085041;\">\u2014<\/span>\n  <\/div>\n<\/div>\n\n<!-- STEP 6 -->\n<span class=\"stitle no-print\" data-step=\"7\">Step 7 \u2014 Grease channel(s)<\/span>\n<div class=\"card no-print\">\n  <div class=\"ctitle\">Grease channel configuration<\/div>\n  <p style=\"font-size:12px;color:var(--g600);margin-bottom:12px;\"><strong>1 nozzle P2 per 2 metres<\/strong> of channel length. Wall hood: typically 1 channel. Central hood: typically 2 channels (one per side).<\/p>\n  <div class=\"r2\" style=\"margin-bottom:10px;\">\n    <div class=\"f\">\n      <label>Number of grease channels<\/label>\n      <select id=\"n-gc\" onchange=\"renderGC()\">\n        <option value=\"1\">1 channel<\/option>\n        <option value=\"2\">2 channels<\/option>\n        <option value=\"3\">3 channels<\/option>\n        <option value=\"4\">4 channels<\/option>\n      <\/select>\n    <\/div>\n  <\/div>\n  <div id=\"gc-list\"><\/div>\n<\/div>\n\n<!-- STEP 7 -->\n<span class=\"stitle no-print\" data-step=\"8\">Step 8 \u2014 Exhaust ducts &amp; separators<\/span>\n<div class=\"card no-print\">\n  <div class=\"ctitle\">Exhaust air ducts<\/div>\n  <div class=\"f\">\n    <label>Number of exhaust air ducts to protect<\/label>\n    <select id=\"n-ducts\" onchange=\"renderDucts();updateSpyFields();\">\n      <option value=\"0\">0 \u2014 no duct protection<\/option>\n      <option value=\"1\" selected>1 duct<\/option>\n      <option value=\"2\">2 ducts<\/option>\n      <option value=\"3\">3 ducts<\/option>\n      <option value=\"4\">4 ducts<\/option>\n      <option value=\"5\">5 ducts<\/option>\n      <option value=\"6\">6 ducts<\/option>\n    <\/select>\n  <\/div>\n  <div id=\"duct-list\"><\/div>\n  <div class=\"note\" style=\"margin-top:8px;\">Per \u00a76.8\/6.9: 2\u00d7 P2 per duct. Max rect. perimeter 1800 mm (0.18 m\u00b2). Max round \u00d8573 mm (0.26 m\u00b2). Ventilation shuts off immediately on alarm.<\/div>\n\n  <hr style=\"border:none;border-top:1px solid var(--g200);margin:18px 0;\">\n\n  <div class=\"ctitle\">Grease \/ aerosol separators<\/div>\n  <div class=\"r2\">\n    <div class=\"f\">\n      <label>Separator protection required?<\/label>\n      <select id=\"has-sep\" onchange=\"updateSep()\">\n        <option value=\"no\">No<\/option>\n        <option value=\"yes\">Yes \u2014 P2 nozzles required<\/option>\n      <\/select>\n    <\/div>\n    <div class=\"f hidden\" id=\"sep-wrap\">\n      <label>Total separator length (mm)<\/label>\n      <input type=\"number\" id=\"sep-len\" value=\"2000\" min=\"500\" max=\"12000\" step=\"100\">\n      <span class=\"hint\">1\u00d7 P2 per 2000 mm. Ventilation shuts off on alarm.<\/span>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- STEP 8 -->\n<span class=\"stitle no-print\" data-step=\"9\">Step 9 \u2014 Manual release &amp; pressure switches<\/span>\n<div class=\"card no-print\">\n  <div class=\"ctitle\">Manual Release &amp; Pressure Switch Configuration<\/div>\n  <div class=\"r3\">\n    <div class=\"f\">\n      <label>Manual release type<\/label>\n      <select id=\"manual-type\">\n        <option value=\"standard\">Manual Release Standard (pneumatic)<\/option>\n        <option value=\"button-surface\">Button \u2014 surface-mounted<\/option>\n        <option value=\"button-flush\">Button \u2014 flush in masonry<\/option>\n        <option value=\"button-cavity\">Button \u2014 cavity wall<\/option>\n      <\/select>\n    <\/div>\n    <div class=\"f\">\n      <label>Number of manual releases<\/label>\n      <select id=\"n-manual\">\n        <option value=\"1\">1 (min \u2014 1 per entrance\/exit)<\/option>\n        <option value=\"2\">2<\/option>\n        <option value=\"3\">3<\/option>\n        <option value=\"4\">4<\/option>\n      <\/select>\n    <\/div>\n    <div class=\"f\">\n      <label>Number of pressure switches<\/label>\n      <select id=\"n-switches\">\n        <option value=\"1\">1 (standard)<\/option>\n        <option value=\"2\">2<\/option>\n        <option value=\"3\">3<\/option>\n        <option value=\"4\">4<\/option>\n        <option value=\"5\">5<\/option>\n      <\/select>\n    <\/div>\n  <\/div>\n<\/div>\n\n<!-- STEP 9 -->\n<span class=\"stitle no-print\" data-step=\"10\">Step 10 \u2014 Pipe calculation &amp; adjustments<\/span>\n<div class=\"card no-print\">\n  <div class=\"ctitle\">Automatic pipe calculation<\/div>\n  <div id=\"pipe-diag\" class=\"pipe-diag\"><\/div>\n  <div id=\"pipe-mode-banner\" class=\"banner blue\" style=\"margin-bottom:8px;\"><\/div>\n  <span class=\"adj-label\" style=\"display:block;margin-bottom:6px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--brand-dark);\">Fittings<\/span>\n  <div class=\"adj-row\" style=\"grid-template-columns:repeat(6,1fr);\">\n    <div class=\"f\">\n      <span class=\"adj-label\">Main line 12\u00d71 (m)<\/span>\n      <input class=\"adj\" type=\"number\" id=\"adj-ext\" min=\"0\" max=\"50\" step=\"0.5\" oninput=\"onAdj()\">\n      <span class=\"hint\">Auto-calculated \u2014 adjust if needed<\/span>\n    <\/div>\n    <div class=\"f\" id=\"adj-det-wrap\">\n      <span class=\"adj-label\">Detection line 8\u00d71 (m)<\/span>\n      <input class=\"adj\" type=\"number\" id=\"adj-det\" min=\"0\" max=\"40\" step=\"0.5\" oninput=\"onAdj()\">\n      <span class=\"hint\">firespy only \u2014 SPY detectors<\/span>\n    <\/div>\n    <div class=\"f\">\n      <span class=\"adj-label\">MR line 8\u00d71 (m)<\/span>\n      <input class=\"adj\" type=\"number\" id=\"adj-mr\" min=\"0\" max=\"40\" step=\"0.5\" oninput=\"onAdj()\">\n      <span class=\"hint\">Container \u2192 manual release<\/span>\n    <\/div>\n    <div class=\"f\">\n      <span class=\"adj-label\">90\u00b0 bends<\/span>\n      <input class=\"adj\" type=\"number\" id=\"adj-bends\" min=\"0\" max=\"50\" step=\"1\" oninput=\"onAdj()\">\n      <span class=\"hint\">Auto-estimated<\/span>\n    <\/div>\n    <div class=\"f\">\n      <span class=\"adj-label\">T-pieces<\/span>\n      <input class=\"adj\" type=\"number\" id=\"adj-tpieces\" min=\"0\" max=\"20\" step=\"1\" oninput=\"onAdj()\">\n      <span class=\"hint\">Auto-estimated<\/span>\n    <\/div>\n    <div class=\"f\">\n      <span class=\"adj-label\">Bulkhead fittings<\/span>\n      <input class=\"adj\" type=\"number\" id=\"adj-bulk\" min=\"1\" max=\"10\" step=\"1\" value=\"1\">\n      <span class=\"hint\" id=\"bulk-hint\"><\/span>\n    <\/div>\n  <\/div>\n  <div style=\"margin-top:8px;\">\n    <span style=\"font-size:12px;color:var(--g600);\">Bulkhead size(s): <\/span><span id=\"bulk-size-info\" style=\"font-size:12px;color:var(--g600);\"><\/span>\n  <\/div>\n  <hr style=\"border:none;border-top:1px solid var(--g200);margin:16px 0;\">\n  <div id=\"nbf-section\" style=\"display:none;\">\n  <span class=\"adj-label\" style=\"display:block;margin-bottom:6px;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:.5px;color:var(--brand-dark);\">Nozzle bulkhead fittings<\/span>\n  <p style=\"font-size:12px;color:var(--g600);margin-bottom:10px;\">Installed at each nozzle position \u2014 receives pipe on one side, nozzle on the other. Auto-filled with cooking zone nozzle count.<\/p>\n  <div class=\"adj-row\" style=\"grid-template-columns:1fr 1fr 1fr;\">\n    <div class=\"f\">\n      <span class=\"adj-label\">Nozzle bulkhead fitting<\/span>\n      <input class=\"adj\" type=\"number\" id=\"nbf-fixed\" min=\"0\" max=\"100\" step=\"1\" value=\"0\">\n      <span class=\"hint\">Fixed nozzle, pointing down (auto = cookNoz)<\/span>\n    <\/div>\n    <div class=\"f\">\n      <span class=\"adj-label\">Directional nozzle bulkhead<\/span>\n      <input class=\"adj\" type=\"number\" id=\"nbf-dir\" min=\"0\" max=\"100\" step=\"1\" value=\"0\">\n      <span class=\"hint\">Orientable nozzle \u2014 replaces fixed as needed<\/span>\n    <\/div>\n    <div class=\"f\">\n      <span class=\"adj-label\">Glass panel nozzle bulkhead<\/span>\n      <input class=\"adj\" type=\"number\" id=\"nbf-glass\" min=\"0\" max=\"100\" step=\"1\" value=\"0\">\n      <span class=\"hint\">For glass panels \u2014 show cooking \/ display kitchens<\/span>\n    <\/div>\n  <\/div>\n  <\/div>\n<\/div>\n\n<div class=\"no-print\" style=\"background:#1a1919;border-radius:var(--r);padding:24px 28px;margin:28px 0 0 0;\">\n  <div id=\"validation-box\" class=\"hidden\" style=\"background:rgba(192,57,43,0.15);border:1.5px solid #e8a09a;border-radius:var(--rs);padding:14px 18px;margin-bottom:16px;\">\n    <h4 style=\"font-size:13px;font-weight:700;color:#f08080;margin-bottom:8px;\">\u26a0 Please complete the following before calculating:<\/h4>\n    <ul id=\"validation-list\" style=\"list-style:none;padding:0;margin:0;\"><\/ul>\n  <\/div>\n  <p style=\"font-size:12px;color:#666;margin-bottom:16px;text-align:center;letter-spacing:.3px;text-transform:uppercase;\">Review all steps above, then generate your system design<\/p>\n  <div style=\"display:flex;gap:12px;align-items:stretch;\">\n    <button class=\"btn-calc\" onclick=\"calculateWithValidation()\" style=\"flex:1;display:flex;align-items:center;justify-content:center;gap:10px;font-size:17px;\">\n      <svg width=\"20\" height=\"20\" viewbox=\"0 0 20 20\" fill=\"none\"><circle cx=\"10\" cy=\"10\" r=\"9\" stroke=\"currentColor\" stroke-width=\"1.5\" opacity=\"0.4\"\/><path d=\"M7 10l2.5 2.5L14 7\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg>\n      Calculate system \u2014 generate bill of materials\n    <\/button>\n    <button onclick=\"resetCalculator()\" title=\"New project \u2014 reset all fields\" style=\"padding:14px 20px;background:transparent;color:#666;border:1.5px solid #333;border-radius:var(--r);font-size:13px;font-weight:600;cursor:pointer;white-space:nowrap;transition:all .2s;display:flex;align-items:center;gap:6px;\" onmouseover=\"this.style.borderColor='#c0392b';this.style.color='#e88080';\" onmouseout=\"this.style.borderColor='#333';this.style.color='#666';\">\n      <svg width=\"14\" height=\"14\" viewbox=\"0 0 14 14\" fill=\"none\"><path d=\"M2 7a5 5 0 1 1 1.5 3.5\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\"\/><path d=\"M2 11V7h4\" stroke=\"currentColor\" stroke-width=\"1.8\" stroke-linecap=\"round\" stroke-linejoin=\"round\"\/><\/svg>\n      New project\n    <\/button>\n  <\/div>\n<\/div>\n\n\n<!-- RESULTS -->\n<div id=\"results\" class=\"hidden\">\n\n  <div class=\"print-inputs\" id=\"print-inputs-box\"><\/div>\n\n  <div class=\"hero\">\n    <h2 style=\"font-size:17px;font-weight:700;margin-bottom:2px;color:#ffffff;\" id=\"res-title\">\u2014<\/h2>\n    <p style=\"font-size:12px;color:#aaa;margin:0;\" id=\"res-sub\">\u2014<\/p>\n    <div class=\"mets\">\n      <div class=\"met\"><span class=\"met-l\">Container(s)<\/span><span class=\"met-v\" id=\"m-vol\">\u2014<\/span><span class=\"met-u\">config<\/span><\/div>\n      <div class=\"met\"><span class=\"met-l\">Total nozzles<\/span><span class=\"met-v\" id=\"m-noz\">\u2014<\/span><span class=\"met-u\">units<\/span><\/div>\n      <div class=\"met\"><span class=\"met-l\">Cooking nozzles<\/span><span class=\"met-v\" id=\"m-cook\">\u2014<\/span><span class=\"met-u\">units<\/span><\/div>\n      <div class=\"met\"><span class=\"met-l\">GC nozzles<\/span><span class=\"met-v\" id=\"m-gc\">\u2014<\/span><span class=\"met-u\">units<\/span><\/div>\n      <div class=\"met\"><span class=\"met-l\">SPY detectors<\/span><span class=\"met-v\" id=\"m-spy\">\u2014<\/span><span class=\"met-u\">elements<\/span><\/div>\n      <div class=\"met\"><span class=\"met-l\">System type<\/span><span class=\"met-v\" id=\"m-sys\" style=\"font-size:13px;padding-top:3px;\">\u2014<\/span><span class=\"met-u\">&nbsp;<\/span><\/div>\n    <\/div>\n  <\/div>\n\n  <div id=\"tandem-box\" class=\"hidden\"><\/div>\n  <div id=\"pipe-summary-box\" class=\"psummary\"><\/div>\n  <div id=\"warnings-box\" class=\"hidden\"><\/div>\n  <div id=\"modifications-box\" class=\"hidden\"><\/div>\n\n  <div class=\"noz-summary\">\n    <div class=\"noz-hdr\">Nozzle allocation by zone<\/div>\n    <div id=\"noz-rows\"><\/div>\n    <div class=\"noz-total\">\n      <span style=\"color:#1e1e1d;\">Total \u2192 litres required \u2192 container selection<\/span>\n      <span id=\"noz-total-val\" style=\"color:#1e1e1d;\">\u2014<\/span>\n    <\/div>\n  <\/div>\n\n  <div class=\"bom-card\">\n    <div class=\"bom-hdr\">\n      <span>Bill of materials \u2014 firespy PHANTOM\u00ae<\/span>\n      <span style=\"font-size:11px;font-weight:400;color:var(--g400);\">DIN EN 17446:2021 \u00b7 doc.1001601 v03 \u00b7 07.04.2026<\/span>\n    <\/div>\n    <table class=\"bom\">\n      <thead>\n        <tr>\n          <th style=\"width:28%;\">Item<\/th>\n          <th>Description<\/th>\n          <th style=\"width:7%;text-align:center;\">Qty<\/th>\n          <th style=\"width:13%;\">Ref.<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody id=\"bom-body\"><\/tbody>\n    <\/table>\n  <\/div>\n\n  <div class=\"bom-card\" style=\"margin-top:12px;\">\n    <div class=\"bom-hdr\">\n      <span>System diagram \u2014 elevation view (schematic)<\/span>\n      <span style=\"font-size:11px;font-weight:400;color:var(--g400);\">Not to scale \u00b7 auto-generated from calculated values<\/span>\n    <\/div>\n    <div style=\"padding:16px 20px;\" id=\"sys-diagram\"><\/div>\n  <\/div>\n\n  <div class=\"bom-card\" style=\"margin-top:12px;\">\n    <div class=\"bom-hdr\">\n      <span>System diagram \u2014 top view \/ plan (schematic)<\/span>\n      <span style=\"font-size:11px;font-weight:400;color:var(--g400);\">Not to scale \u00b7 auto-generated from calculated values<\/span>\n    <\/div>\n    <div style=\"padding:16px 20px;\" id=\"sys-diagram-top\"><\/div>\n  <\/div>\n\n  <div class=\"note\">\n    <strong>Note:<\/strong> This calculation is based on the firespy PHANTOM\u00ae design guidelines (document 1001601 v03, 07\/04\/2026) and DIN EN 17446:2021. Final design must be validated and commissioned by a certified protecfire installer. Pipe lengths are auto-estimated \u2014 on-site survey required. Energy supply shut-off (electrical, gas, compressed air) must be commissioned separately by a qualified contractor.\n  <\/div>\n\n  <div style=\"margin-top:16px;\">\n    <button class=\"btn-pdf\" onclick=\"printReport()\">\n      <svg width=\"14\" height=\"14\" viewbox=\"0 0 14 14\" fill=\"none\"><rect x=\"2\" y=\"1\" width=\"10\" height=\"12\" rx=\"1.5\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><line x1=\"4\" y1=\"5\" x2=\"10\" y2=\"5\" stroke=\"currentColor\" stroke-width=\"1.2\"\/><line x1=\"4\" y1=\"7.5\" x2=\"10\" y2=\"7.5\" stroke=\"currentColor\" stroke-width=\"1.2\"\/><line x1=\"4\" y1=\"10\" x2=\"7\" y2=\"10\" stroke=\"currentColor\" stroke-width=\"1.2\"\/><\/svg>\n      Download \/ Print PDF report\n    <\/button>\n  <\/div>\n<\/div>\n\n<\/div><!-- \/main -->\n\n<script>\nvar kt='wall',mode='linear',inst='surface',apps=[],appId=0,gcData=[{length:2200}],ducts=[];\nvar autoExt=0,autoDet=0,autoMR=0,autoBends=0,autoTP=0;\nvar autoSpyHood=0,autoSpyDuct=0;\n\nvar CONT=[\n  {vol:7,  nMin:4,  nMax:4,  lineMax:16,tMax:8,  bMax:21,w:542, d:160,kg:13},\n  {vol:14, nMin:6,  nMax:7,  lineMax:25,tMax:11, bMax:31,w:749, d:190,kg:25},\n  {vol:25, nMin:8,  nMax:12, lineMax:25,tMax:11, bMax:31,w:970, d:245,kg:46},\n  {vol:30, nMin:12, nMax:16, lineMax:25,tMax:11, bMax:31,w:970, d:315,kg:56},\n];\n\nvar APPS={\n  deepfryer_single:{label:'Deep fryer \u2014 single (max 0.17 m\u00b2 \/ 28 L)',     nP1:1,nP2:1,nP2f:2,spy:1,note:'P2: 1 nozzle at 70\u2013180 cm; 2 at 180\u2013260 cm'},\n  deepfryer_double:{label:'Deep fryer \u2014 double (max 0.144 m\u00b2 \/ 2\u00d712 L)', nP1:2,nP2:2,nP2f:2,spy:1,note:'One nozzle per basin'},\n  grill_plate:     {label:'Grill plate (max 0.39 m\u00b2)',                     nP1:1,nP2:1,nP2f:1,spy:1,note:'Nozzle centred above fat surface'},\n  cooktop_small:   {label:'Cooktop \u2014 small (max 0.24 m\u00b2)',                 nP1:1,nP2:1,nP2f:1,spy:1,note:'Nozzle above fire load'},\n  cooktop_large:   {label:'Cooktop \u2014 large (max 0.48 m\u00b2)',                 nP1:2,nP2:2,nP2f:2,spy:1,note:'Min 80 cm between P2 nozzles'},\n  grill_025:       {label:'Grill (max 0.25 m\u00b2)',                           nP1:0,nP2:1,nP2f:1,spy:1,note:'P1 not suitable'},\n  grill_050:       {label:'Grill (max 0.50 m\u00b2)',                           nP1:0,nP2:2,nP2f:2,spy:1,note:'P1 not suitable; P2 @ 350 mm'},\n  grill_075:       {label:'Grill (max 0.75 m\u00b2)',                           nP1:0,nP2:3,nP2f:3,spy:1,note:'P1 not suitable; P2 @ 350 mm'},\n  wok_240:         {label:'Wok \u00d8240 mm',                                   nP1:1,nP2:1,nP2f:1,spy:1,note:''},\n  wok_300:         {label:'Wok \u00d8300 mm',                                   nP1:1,nP2:1,nP2f:1,spy:1,note:''},\n  wok_500:         {label:'Wok \u00d8500 mm',                                   nP1:0,nP2:1,nP2f:2,spy:1,note:'P1 not suitable'},\n};\n\nfunction selectContainers(totalNoz){\n  var litNeeded=totalNoz*2;\n  \/\/ Try from fewest containers (1) to most (5), largest volume first\n  var volsDesc=[30,25,14,7];\n  for(var qty=1;qty<=5;qty++){\n    for(var vi=0;vi<volsDesc.length;vi++){\n      var vol=volsDesc[vi];\n      var co=CONT.find(function(x){return x.vol===vol;});\n      var totalV=vol*qty;\n      var maxNoz=co.nMax*qty;\n      var minNoz=co.nMin*qty;\n      if(litNeeded<=totalV&&totalNoz<=maxNoz&&totalNoz>=minNoz){\n        return{containers:Array(qty).fill(co),qty:qty,totalVol:totalV,tandem:qty>1,singleVol:vol};\n      }\n    }\n  }\n  \/\/ Fallback for very small systems (totalNoz < nMin of smallest container):\n  \/\/ Use smallest single container (7L) if volume is sufficient\n  var smallest=CONT.find(function(x){return x.vol===7;});\n  if(smallest&&litNeeded<=smallest.vol&&totalNoz<=smallest.nMax){\n    return{containers:[smallest],qty:1,totalVol:smallest.vol,tandem:false,singleVol:7,subMin:true};\n  }\n  \/\/ Last resort: find any single container with enough volume\n  for(var vi2=volsDesc.length-1;vi2>=0;vi2--){\n    var vol2=volsDesc[vi2];\n    var co2=CONT.find(function(x){return x.vol===vol2;});\n    if(litNeeded<=co2.vol&&totalNoz<=co2.nMax){\n      return{containers:[co2],qty:1,totalVol:co2.vol,tandem:false,singleVol:vol2,subMin:true};\n    }\n  }\n  return null;\n}\n\nfunction updateSpyFields(){\n  var L=(parseInt(document.getElementById('hood-length').value)||2200)\/1000;\n  var nD=parseInt(document.getElementById('n-ducts').value)||0;\n\n  \/\/ \u2500\u2500 Fryer count (by appliance only) \u2500\u2500\n  var fryerCount=0;\n  if(mode==='appliance') apps.forEach(function(a){ if(a.type.indexOf('deepfryer')!==-1) fryerCount+=a.qty; });\n\n  \/\/ \u2500\u2500 Hood\/plenum suggested value \u2500\u2500\n  \/\/ Linear: ceil(L\/2) \u2014 1 per 2m\n  \/\/ By appliance: always 1 (minimum plenum detector)\n  var minHood = mode==='linear' ? Math.max(1,Math.ceil(L\/2)) : 1;\n  autoSpyHood = minHood;\n  autoSpyDuct = nD;\n\n  \/\/ Update fryer row\n  var frRow=document.getElementById('spy-fryer-row');\n  frRow.classList.toggle('hidden', mode!=='appliance'||fryerCount===0);\n  document.getElementById('spy-fryer-val').textContent=fryerCount;\n\n  \/\/ Update hood label and hint based on mode\n  var hoodLabel=document.getElementById('spy-hood-label');\n  var hoodHint=document.getElementById('spy-hood-hint');\n  var hoodMinSpan=document.getElementById('spy-hood-min');\n  if(hoodLabel) hoodLabel.childNodes[0].textContent=mode==='linear'?'SPY along hood ':'SPY in plenum ';\n  if(hoodHint) hoodHint.innerHTML=mode==='linear'\n    ?'1 per 2 m of hood length \u2014 suggested: <span id=\"spy-hood-min\">'+minHood+'<\/span>'\n    :'1 in plenum minimum \u2014 suggested: <span id=\"spy-hood-min\">'+minHood+'<\/span>';\n  document.getElementById('spy-hood-min-label').textContent='(suggested '+minHood+')';\n\n  \/\/ Update hood input\n  var hoodInput=document.getElementById('spy-hood-count');\n  hoodInput.min=1;\n  var prevSuggested=parseInt(hoodInput.getAttribute('data-suggested'))||0;\n  var curVal=parseInt(hoodInput.value)||0;\n  if(curVal===prevSuggested||curVal<1) hoodInput.value=minHood;\n  hoodInput.setAttribute('data-suggested',minHood);\n\n  \/\/ Update duct row\n  var ductRow=document.getElementById('spy-duct-row');\n  ductRow.classList.toggle('hidden', nD===0);\n  document.getElementById('spy-duct-min').textContent=nD;\n  document.getElementById('spy-duct-min-label').textContent=nD>0?'(suggested '+nD+')':'';\n  var ductInput=document.getElementById('spy-duct-count');\n  ductInput.min=0;\n  var prevSugDuct=parseInt(ductInput.getAttribute('data-suggested'))||0;\n  var curDuctVal=parseInt(ductInput.value)||0;\n  if(curDuctVal===prevSugDuct||curDuctVal===0||nD===0){\n    ductInput.value=nD>0?nD:0;\n  }\n  ductInput.setAttribute('data-suggested',nD);\n\n  updateSpyTotal();\n}\n\nfunction updateSpyTotal(){\n  var nD=parseInt(document.getElementById('n-ducts').value)||0;\n  var fryerCount=0;\n  if(mode==='appliance') apps.forEach(function(a){ if(a.type.indexOf('deepfryer')!==-1) fryerCount+=a.qty; });\n  var hoodVal=Math.max(0,parseInt(document.getElementById('spy-hood-count').value)||0);\n  var ductVal=nD>0?Math.max(0,parseInt(document.getElementById('spy-duct-count').value)||0):0;\n  var total=Math.max(2, hoodVal + fryerCount + ductVal);\n  document.getElementById('spy-total-display').textContent=total;\n}\n\nfunction getSpyCount(){\n  var nD=parseInt(document.getElementById('n-ducts').value)||0;\n  var fryerCount=0;\n  if(mode==='appliance') apps.forEach(function(a){ if(a.type.indexOf('deepfryer')!==-1) fryerCount+=a.qty; });\n  var hoodVal=parseInt(document.getElementById('spy-hood-count').value)||0;\n  var ductVal=nD>0?(parseInt(document.getElementById('spy-duct-count').value)||0):0;\n  return Math.max(2, hoodVal + fryerCount + ductVal);\n}\n\nfunction setInst(t){\n  inst=t;\n  ['surface','concealed'].forEach(function(k){\n    var el=document.getElementById('inst-'+k);\n    if(!el) return;\n    el.classList.toggle('active',k===t);\n    el.querySelector('.micon').style.cssText=k===t?'background:var(--brand);color:#fff':'background:var(--g100);color:#666';\n  });\n  \/\/ Show\/hide nozzle bulkhead section in Step 10\n  var nbfSection=document.getElementById('nbf-section');\n  if(nbfSection) nbfSection.style.display=t==='concealed'?'':'none';\n}\n\nfunction setKT(t){\n  kt=t;\n  ['wall','central','vceil'].forEach(function(k){\n    var el=document.getElementById('kt-'+k);\n    if(!el) return;\n    el.classList.toggle('active',k===t);\n    el.querySelector('.micon').style.cssText=k===t?'background:var(--brand);color:#fff':'background:var(--g100);color:#666';\n  });\n  var wh=document.getElementById('width-hint'),we=document.getElementById('width-extra');\n  var dimsTitle=document.getElementById('dims-title');\n  var vcDepthWrap=document.getElementById('vc-depth-wrap');\n  var vcHeightHint=document.getElementById('vc-height-hint');\n  var hoodLenHint=document.getElementById('hood-length-hint');\n  if(t==='central'){\n    wh.textContent='Lateral depth \u2014 used for U-pipe entry calculation';we.textContent='(lateral)';\n    if(dimsTitle) dimsTitle.textContent='Hood dimensions';\n    if(vcDepthWrap) vcDepthWrap.style.display='none';\n    if(vcHeightHint) vcHeightHint.style.display='none';\n    if(hoodLenHint) hoodLenHint.textContent='Main dimension \u2014 pipe runs along this length';\n  } else if(t==='vceil'){\n    wh.textContent='Cooking zone width (mm)';we.textContent='(zone width)';\n    if(dimsTitle) dimsTitle.textContent='Cooking zone dimensions (ventilated ceiling)';\n    if(vcDepthWrap) vcDepthWrap.style.display='';\n    if(vcHeightHint) vcHeightHint.style.display='';\n    if(hoodLenHint) hoodLenHint.textContent='Cooking zone length \u2014 nozzle grid runs along this';\n    document.getElementById('hood-length').setAttribute('placeholder','e.g. 3000');\n    \/\/ Ventilated ceiling defaults: concealed + by appliance\n    if(inst!=='concealed') setInst('concealed');\n    if(mode!=='appliance') setMode('appliance');\n  } else {\n    wh.textContent='Reference only';we.textContent='';\n    if(dimsTitle) dimsTitle.textContent='Hood dimensions';\n    if(vcDepthWrap) vcDepthWrap.style.display='none';\n    if(vcHeightHint) vcHeightHint.style.display='none';\n    if(hoodLenHint) hoodLenHint.textContent='Main dimension \u2014 pipe runs along this length';\n  }\n  var ngc=document.getElementById('n-gc');\n  if(t==='central'&&ngc.value==='1') ngc.value='2';\n  renderGC();autoCalc();\n}\n\nfunction setMode(m){\n  mode=m;\n  ['linear','appliance'].forEach(function(k){\n    var el=document.getElementById('mode-'+k);\n    el.classList.toggle('active',k===m);\n    el.querySelector('.micon').style.cssText=k===m?'background:var(--brand);color:#fff':'background:var(--g100);color:#666';\n  });\n  document.getElementById('linear-section').classList.toggle('hidden',m!=='linear');\n  document.getElementById('appliance-section').classList.toggle('hidden',m!=='appliance');\n  document.getElementById('results').classList.add('hidden');\n  autoCalc();\n  updateSpyFields();\n}\n\nfunction getNT(){return (parseInt(document.getElementById('nozzle-h').value)||120)<=70?'P1':'P2';}\n\nfunction onHeight(){\n  var h=parseInt(document.getElementById('nozzle-h').value)||120;\n  var b=document.getElementById('ntype-badge');\n  if(h<=70){b.className='banner green';b.innerHTML='<strong>Nozzle P1<\/strong> \u00b7 '+h+' cm \u2264 70 cm \u00b7 Spray ~90\u00b0 \u00b7 Height range 40\u201370 cm above cooking surface';}\n  else if(h<=260){b.className='banner blue';b.innerHTML='<strong>Nozzle P2<\/strong> \u00b7 '+h+' cm > 70 cm \u00b7 Spray ~20\u00b0 \u00b7 Height range 70\u2013260 cm above cooking surface';}\n  else{b.className='banner orange';b.innerHTML='<strong>Nozzle P2 (pipe level)<\/strong> \u00b7 '+h+' cm \u00b7 Above 260 cm: pipe-level specification (max 400 cm)';}\n  renderApps();autoCalc();\n}\n\nfunction autoCalc(){\n  var L=(parseInt(document.getElementById('hood-length').value)||2200)\/1000;\n  var W=(parseInt(document.getElementById('hood-width').value)||900)\/1000;\n  var dC=parseFloat(document.getElementById('dist-c').value)||2;\n  var dMR=parseFloat(document.getElementById('dist-mr').value)||3;\n  var nD=parseInt(document.getElementById('n-ducts').value)||0;\n  var dDist=0; for(var i=0;i<ducts.length;i++) dDist+=(ducts[i].dist||0);\n\n  var extLen,detLen,bends,tp;\n  if(kt==='wall'){extLen=dC+L+dDist;bends=2+nD*2;tp=nD;}\n  else if(kt==='vceil'){\n    var vcW=(parseInt(document.getElementById('vc-depth').value)||1200)\/1000;\n    extLen=dC+L+vcW+dDist;bends=2+nD*2;tp=nD;\n  }\n  else{extLen=dC+W+L+L+dDist;bends=2+2+nD*2;tp=1+nD;}\n  extLen=Math.round(extLen*10)\/10;\n\n  var spyN=computeSPY();\n  var hoodSpyLen=Math.min(L,Math.ceil(L\/2)*2);\n  detLen=Math.max(3,Math.round((dC+(kt==='central'?W\/2:0)+hoodSpyLen+dDist)*10)\/10);\n\n  var isFS=extLen>15;\n  autoExt=extLen;autoDet=detLen;autoMR=dMR;autoBends=bends;autoTP=tp;\n\n  document.getElementById('adj-ext').value=extLen;\n  document.getElementById('adj-det').value=detLen;\n  document.getElementById('adj-mr').value=dMR;\n  document.getElementById('adj-bends').value=bends;\n  document.getElementById('adj-tpieces').value=tp;\n  document.getElementById('adj-det-wrap').classList.toggle('hidden',!isFS);\n  document.getElementById('adj-bulk').value=isFS?2:1;\n  document.getElementById('bulk-hint').textContent=isFS?'firespy: 2\u00d7 (12\u00d71 + 8\u00d71)':'detexline: 1\u00d7 (12\u00d71)';\n  document.getElementById('bulk-size-info').textContent=isFS?'1\u00d7 12\u00d71 (extinguishing line) + 1\u00d7 8\u00d71 (detection line)':'1\u00d7 12\u00d71 (nozzles + SPY combined)';\n\n  renderPipeDiag(isFS,L,W,dC,extLen,detLen,dMR,nD);\n  updatePipeBanner(isFS,extLen,detLen);\n  if(mode==='linear') renderLinear();\n  updateSpyFields();\n  \/\/ Live update nozzle bulkhead fitting count (concealed only)\n  if(inst==='concealed'){\n    var nbfEl=document.getElementById('nbf-fixed');\n    if(nbfEl){\n      var prevSugNbf=parseInt(nbfEl.getAttribute('data-suggested'))||0;\n      var curNbf=parseInt(nbfEl.value)||0;\n      var newCookNoz;\n      if(mode==='linear'){\n        var sides2=kt==='central'?2:1;\n        newCookNoz=Math.ceil(L*1000\/350)*sides2;\n      } else {\n        newCookNoz=prevSugNbf; \/\/ appliance mode \u2014 keep current\n      }\n      if(curNbf===prevSugNbf||curNbf===0){\n        nbfEl.value=newCookNoz;\n      }\n      nbfEl.setAttribute('data-suggested',newCookNoz);\n    }\n  }\n}\n\nfunction onAdj(){\n  var extLen=parseFloat(document.getElementById('adj-ext').value)||autoExt;\n  var isFS=extLen>15;\n  document.getElementById('adj-det-wrap').classList.toggle('hidden',!isFS);\n  document.getElementById('adj-bulk').value=isFS?2:1;\n  document.getElementById('bulk-hint').textContent=isFS?'firespy: 2\u00d7 (12\u00d71 + 8\u00d71)':'detexline: 1\u00d7 (12\u00d71)';\n  document.getElementById('bulk-size-info').textContent=isFS?'1\u00d7 12\u00d71 + 1\u00d7 8\u00d71':'1\u00d7 12\u00d71';\n  var L=(parseInt(document.getElementById('hood-length').value)||2200)\/1000;\n  var W=(parseInt(document.getElementById('hood-width').value)||900)\/1000;\n  var dC=parseFloat(document.getElementById('dist-c').value)||2;\n  var dMR=parseFloat(document.getElementById('adj-mr').value)||autoMR;\n  var detLen=parseFloat(document.getElementById('adj-det').value)||autoDet;\n  var nD=parseInt(document.getElementById('n-ducts').value)||0;\n  renderPipeDiag(isFS,L,W,dC,extLen,detLen,dMR,nD);\n  updatePipeBanner(isFS,extLen,detLen);\n}\n\nfunction renderPipeDiag(isFS,L,W,dC,extLen,detLen,dMR,nD){\n  var bends=parseInt(document.getElementById('adj-bends').value)||autoBends;\n  var tp=parseInt(document.getElementById('adj-tpieces').value)||autoTP;\n  var el=document.getElementById('pipe-diag');\n  var h='<div style=\"font-size:12px;font-weight:600;color:var(--g600);margin-bottom:10px;\">Estimated pipe layout \u2014 '+(kt==='central'?'central\/island hood':'wall hood')+'<\/div>';\n  h+='<div class=\"pipe-line\"><span style=\"font-size:11px;color:var(--g400);min-width:95px;\">12\u00d71 main line:<\/span>';\n  h+='<div class=\"pipe-seg\">Container<\/div><span class=\"pipe-arrow\">\u2192<\/span>';\n  h+='<div class=\"pipe-seg ext12\">'+dC.toFixed(1)+' m to hood<\/div><span class=\"pipe-arrow\">\u2192<\/span>';\n  if(kt==='central'){\n    h+='<div class=\"pipe-seg ext12\">'+W.toFixed(1)+' m lateral<\/div><span class=\"pipe-arrow\">\u2192 T \u2192<\/span>';\n    h+='<div class=\"pipe-seg ext12\">'+L.toFixed(1)+' m side A<\/div><span class=\"pipe-arrow\">+<\/span>';\n    h+='<div class=\"pipe-seg ext12\">'+L.toFixed(1)+' m side B<\/div>';\n  } else {\n    h+='<div class=\"pipe-seg ext12\">'+L.toFixed(1)+' m along hood<\/div>';\n  }\n  if(nD>0) h+='<span class=\"pipe-arrow\">+<\/span><div class=\"pipe-seg duct\">'+nD+'\u00d7 duct branch(es)<\/div>';\n  h+='<\/div>';\n  if(isFS){\n    h+='<div class=\"pipe-line\"><span style=\"font-size:11px;color:var(--g400);min-width:95px;\">8\u00d71 det. line:<\/span>';\n    h+='<div class=\"pipe-seg\">Container<\/div><span class=\"pipe-arrow\">\u2192<\/span>';\n    h+='<div class=\"pipe-seg det8\">'+dC.toFixed(1)+' m to hood<\/div><span class=\"pipe-arrow\">\u2192<\/span>';\n    h+='<div class=\"pipe-seg det8\">SPY positions \u2248 '+detLen.toFixed(1)+' m total<\/div><\/div>';\n  }\n  h+='<div class=\"pipe-line\"><span style=\"font-size:11px;color:var(--g400);min-width:95px;\">8\u00d71 MR line:<\/span>';\n  h+='<div class=\"pipe-seg\">Container<\/div><span class=\"pipe-arrow\">\u2192<\/span>';\n  h+='<div class=\"pipe-seg mr8\">'+dMR.toFixed(1)+' m \u2192 manual release<\/div><\/div>';\n  h+='<div style=\"font-size:12px;color:var(--g600);margin-top:8px;\">12\u00d71: <strong>'+extLen.toFixed(1)+'<\/strong> m'+(isFS?' &nbsp;\u00b7&nbsp; 8\u00d71 det: <strong>'+detLen.toFixed(1)+'<\/strong> m':'')+' &nbsp;\u00b7&nbsp; 8\u00d71 MR: <strong>'+dMR.toFixed(1)+'<\/strong> m &nbsp;\u00b7&nbsp; bends: <strong>'+bends+'<\/strong> &nbsp;\u00b7&nbsp; T-pieces: <strong>'+tp+'<\/strong><\/div>';\n  el.innerHTML=h;\n}\n\nfunction updatePipeBanner(isFS,extLen,detLen){\n  var b=document.getElementById('pipe-mode-banner');\n  if(isFS){b.className='banner blue';b.innerHTML='<strong>firespy \u2014 dual-pipe<\/strong> \u00b7 12\u00d71 extinguishing line '+extLen.toFixed(1)+' m > 15 m. Two separate pipes: 12\u00d71 (nozzles only) + 8\u00d71 (SPY detectors only). Plus 8\u00d71 manual release line.';}\n  else{b.className='banner green';b.innerHTML='<strong>detexline \u2014 single main pipe<\/strong> \u00b7 12\u00d71 line '+extLen.toFixed(1)+' m \u2264 15 m. One 12\u00d71 pipe carries SPY detectors and nozzles together. Plus 8\u00d71 manual release line.';}\n}\n\nfunction renderLinear(){\n  var L=parseInt(document.getElementById('hood-length').value)||2200;\n  var nt=kt==='vceil'?'P2':getNT();\n  var n=Math.ceil(L\/350), sides=kt==='central'?2:1, total=n*sides;\n  var preview=kt==='vceil'\n    ?'<strong>'+n+' nozzles P2<\/strong> \u00b7 '+L+' mm \u00f7 350 mm = '+n+' positions along ceiling plenum'\n    :'<strong>'+n+' nozzles '+nt+'<\/strong> per side \u00b7 '+L+' mm \u00f7 350 mm = '+n+' positions'+(kt==='central'?' \u00b7 <strong>\u00d72 sides = '+total+' cooking nozzles<\/strong> (detectors not duplicated)':'');\n  document.getElementById('linear-preview').innerHTML='<svg width=\"14\" height=\"14\" viewBox=\"0 0 14 14\" fill=\"none\" style=\"flex-shrink:0;margin-top:1px;\"><circle cx=\"7\" cy=\"7\" r=\"6\" stroke=\"currentColor\" stroke-width=\"1.5\"\/><path d=\"M4 7h6M7 4v6\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\"\/><\/svg><div>'+preview+'<\/div>';\n}\n\nfunction renderGC(){\n  var n=parseInt(document.getElementById('n-gc').value)||1;\n  while(gcData.length<n) gcData.push({length:2000});\n  gcData.length=n;\n  document.getElementById('gc-list').innerHTML=gcData.map(function(g,i){\n    var noz=Math.max(1,Math.ceil(g.length\/2000));\n    return '<div class=\"gc-row\"><span style=\"font-weight:600;color:var(--g600);font-size:12px;\">Channel '+(i+1)+'<\/span>'\n      +'<div class=\"f\" style=\"margin-bottom:0;\"><label>Length (mm)<\/label><input type=\"number\" value=\"'+g.length+'\" min=\"500\" max=\"15000\" step=\"100\" oninput=\"gcData['+i+'].length=+this.value;renderGC()\"><\/div>'\n      +'<div style=\"font-size:13px;font-weight:600;color:var(--brand);align-self:end;padding-bottom:10px;\">'+noz+' nozzle'+(noz>1?'s':'')+' P2 <span style=\"font-size:11px;font-weight:400;color:var(--g400);\">(\u00f7 2 m)<\/span><\/div>'\n      +'<\/div>';\n  }).join('');\n}\n\nfunction updateSep(){document.getElementById('sep-wrap').classList.toggle('hidden',document.getElementById('has-sep').value!=='yes');}\n\nfunction renderDucts(){\n  var n=parseInt(document.getElementById('n-ducts').value)||0;\n  while(ducts.length<n) ducts.push({shape:'round',variant:'v1',dimA:250,dimB:250,ductLen:6000,dist:3,nozzles:0});\n  ducts.length=n;\n  var c=document.getElementById('duct-list');\n  if(!n){c.innerHTML='';autoCalc();return;}\n  c.innerHTML=ducts.map(function(d,i){\n    var autoN=d.variant==='v2'?2*Math.max(1,Math.ceil(d.ductLen\/6000)):1;\n    var nozVal=d.nozzles>0?d.nozzles:autoN;\n    return '<div class=\"duct-row\">'\n      +'<div class=\"f\"><label>Duct '+(i+1)+' shape<\/label><select onchange=\"ducts['+i+'].shape=this.value;renderDucts()\"><option value=\"round\"'+(d.shape==='round'?' selected':'')+'>Round (\u00d8)<\/option><option value=\"rectangular\"'+(d.shape==='rectangular'?' selected':'')+'>Rectangular<\/option><\/select><\/div>'\n      +(d.shape==='rectangular'\n          ?'<div class=\"f\"><label>Width (mm)<\/label><input type=\"number\" value=\"'+d.dimA+'\" min=\"100\" max=\"600\" step=\"10\" onchange=\"ducts['+i+'].dimA=+this.value\"><\/div>'\n           +'<div class=\"f\"><label>Height (mm)<\/label><input type=\"number\" value=\"'+d.dimB+'\" min=\"100\" max=\"600\" step=\"10\" onchange=\"ducts['+i+'].dimB=+this.value\"><\/div>'\n          :'<div class=\"f\"><label>Diameter \u00d8 (mm)<\/label><input type=\"number\" value=\"'+d.dimA+'\" min=\"100\" max=\"573\" step=\"5\" onchange=\"ducts['+i+'].dimA=+this.value\"><\/div>'\n           +'<div class=\"f\"><\/div>'\n        )\n      +'<div class=\"f\"><label>Nozzles P2 <span style=\"font-weight:400;color:var(--g400);font-size:10px;\">(auto='+autoN+')<\/span><\/label>'\n      +'<input type=\"number\" value=\"'+nozVal+'\" min=\"1\" max=\"10\" step=\"1\" onchange=\"ducts['+i+'].nozzles=+this.value;autoCalc()\" style=\"background:var(--brand-light);border-color:#f0c4a0;\">'\n      +'<span class=\"hint\">1 per duct default \u2014 adjust if needed<\/span><\/div>'\n      +'<div class=\"f\"><label>Variant<\/label><select onchange=\"ducts['+i+'].variant=this.value;renderDucts()\"><option value=\"v1\"'+(d.variant==='v1'?' selected':'')+'>Var. 1 \u2014 end entry<\/option><option value=\"v2\"'+(d.variant==='v2'?' selected':'')+'>Var. 2 \u2014 side outlet<\/option><\/select><\/div>'\n      +'<div class=\"f\"><label>Distance main pipe \u2192 duct (m)<\/label><input type=\"number\" value=\"'+d.dist+'\" min=\"0.5\" max=\"20\" step=\"0.5\" onchange=\"ducts['+i+'].dist=+this.value;autoCalc()\"><\/div>'\n      +'<\/div>';\n  }).join('');\n  autoCalc();\n}\n\nfunction addApp(){apps.push({id:++appId,type:'deepfryer_single',qty:1});renderApps();}\nfunction remApp(id){apps=apps.filter(function(a){return a.id!==id;});renderApps();}\nfunction updApp(id,f,v){var a=apps.find(function(x){return x.id===id;});if(a)a[f]=f==='qty'?Math.max(1,parseInt(v)||1):v;}\nfunction renderApps(){\n  var c=document.getElementById('appliance-list');\n  if(!apps.length){c.innerHTML='<p style=\"font-size:12px;color:var(--g400);margin-bottom:10px;\">No appliances added.<\/p>';updateSpyFields();return;}\n  var nt=getNT(),h=parseInt(document.getElementById('nozzle-h').value)||120;\n  c.innerHTML=apps.map(function(a){\n    var def=APPS[a.type],n=nt==='P1'?def.nP1:(h>180?def.nP2f:def.nP2);\n    var warn=(nt==='P1'&&def.nP1===0)?'<span style=\"color:var(--red);\"> \u26a0 P1 not suitable<\/span>':'';\n    var iNote='';\n    var opts=Object.entries(APPS).map(function(e){return '<option value=\"'+e[0]+'\"'+(a.type===e[0]?' selected':'')+'>'+e[1].label+'<\/option>';}).join('');\n    return '<div class=\"app-row\"><div class=\"f\"><label>Appliance type<\/label><select onchange=\"updApp('+a.id+',\\'type\\',this.value);renderApps()\">'+opts+'<\/select>'+(def.note?'<span class=\"hint\">'+def.note+'<\/span>':'')+'<\/div>'\n      +'<div class=\"f\"><label>Qty<\/label><input type=\"number\" value=\"'+a.qty+'\" min=\"1\" max=\"20\" style=\"width:70px;\" onchange=\"updApp('+a.id+',\\'qty\\',this.value);renderApps()\"><\/div>'\n      +'<div class=\"f\" style=\"align-self:end;\"><div style=\"font-size:13px;font-weight:600;color:var(--brand);padding-bottom:10px;\">'+(n*a.qty)+' \u00d7 '+nt+warn+iNote+'<\/div><\/div>'\n      +'<button class=\"btn-rm\" onclick=\"remApp('+a.id+')\">&#215;<\/button><\/div>';\n  }).join('');\n  updateSpyFields();\n  \/\/ Update nbf-fixed for appliance mode\n  if(inst==='concealed'){\n    var nbfEl=document.getElementById('nbf-fixed');\n    if(nbfEl){\n      var cookNozEst=0;\n      apps.forEach(function(a){\n        var def=APPS[a.type]; var nt2=getNT(); var h2=parseInt(document.getElementById('nozzle-h').value)||120;\n        var n=nt2==='P1'?def.nP1:(h2>180?def.nP2f:def.nP2);\n        cookNozEst+=n*a.qty;\n      });\n      var prevSug=parseInt(nbfEl.getAttribute('data-suggested'))||0;\n      var cur=parseInt(nbfEl.value)||0;\n      if(cur===prevSug||cur===0) nbfEl.value=cookNozEst;\n      nbfEl.setAttribute('data-suggested',cookNozEst);\n    }\n  }\n}\n\nfunction ductRes(d){\n  var perim,warn=[];\n  if(d.shape==='rectangular'){perim=2*(d.dimA+d.dimB);if(perim>1800)warn.push('Perimeter '+perim+' mm > 1800 mm max.');}\n  else{perim=Math.round(Math.PI*d.dimA);if(d.dimA>573)warn.push('\u00d8'+d.dimA+' mm > \u00d8573 mm max.');}\n  \/\/ Use manual nozzle count if set, otherwise auto: v1=1, v2=2\u00d7ceil(len\/6000)\n  var autoN=d.variant==='v2'?2*Math.max(1,Math.ceil(d.ductLen\/6000)):1;\n  var n=d.nozzles>0?d.nozzles:autoN;\n  return{nozzles:n,autoN:autoN,perim:perim,warnings:warn};\n}\n\nfunction computeSPY(){\n  \/\/ Used for pipe length estimation in autoCalc\n  \/\/ Linear: ceil(L\/2) + nDucts\n  \/\/ By appliance: 1 (plenum) + nFryers + nDucts\n  var L=(parseInt(document.getElementById('hood-length').value)||2200)\/1000;\n  var nD=parseInt(document.getElementById('n-ducts').value)||0;\n  var fryerCount=0;\n  if(mode==='appliance') apps.forEach(function(a){if(a.type.indexOf('deepfryer')!==-1)fryerCount+=a.qty;});\n  var hoodSpy=mode==='linear'?Math.max(1,Math.ceil(L\/2)):1;\n  return Math.max(2, hoodSpy+fryerCount+nD);\n}\n\nfunction validateForm(){\n  var errors=[];\n  var L=parseInt(document.getElementById('hood-length').value)||0;\n  var h=parseInt(document.getElementById('nozzle-h').value)||0;\n\n  \/\/ Hood length\n  if(L<300) errors.push({msg:'Hood \/ cooking zone length must be at least 300 mm', field:'hood-length'});\n\n  \/\/ Nozzle height\n  if(h<40) errors.push({msg:'Nozzle height must be at least 40 cm', field:'nozzle-h'});\n  if(h>400) errors.push({msg:'Nozzle height cannot exceed 400 cm', field:'nozzle-h'});\n\n  \/\/ Ventilated ceiling depth\n  if(kt==='vceil'){\n    var vcD=parseInt(document.getElementById('vc-depth').value)||0;\n    if(vcD<300) errors.push({msg:'Cooking zone depth must be at least 300 mm (ventilated ceiling)', field:'vc-depth'});\n  }\n\n  \/\/ Appliance mode \u2014 at least one appliance\n  if(mode==='appliance' && apps.length===0){\n    errors.push({msg:'Please add at least one cooking appliance in Step 5', field:null});\n  }\n\n  \/\/ Appliance mode \u2014 check for P1 not suitable warnings\n  if(mode==='appliance'){\n    var nt=getNT();\n    apps.forEach(function(a){\n      var def=APPS[a.type];\n      if(nt==='P1'&&def.nP1===0) errors.push({msg:def.label+': P1 nozzle not suitable at this height. Increase nozzle height above 70 cm.', field:'nozzle-h'});\n    });\n  }\n\n  \/\/ SPY total minimum\n  var spyTotal=parseInt(document.getElementById('spy-total-display').textContent)||0;\n  if(spyTotal<2) errors.push({msg:'Minimum 2 SPY detectors required per system', field:null});\n\n  \/\/ Duct dimension check\n  ducts.forEach(function(d,i){\n    if(d.shape==='rectangular'){\n      var perim=2*(d.dimA+d.dimB);\n      if(perim>1800) errors.push({msg:'Duct '+(i+1)+': perimeter '+perim+' mm exceeds 1800 mm maximum', field:null});\n    } else {\n      if(d.dimA>573) errors.push({msg:'Duct '+(i+1)+': diameter \u00d8'+d.dimA+' mm exceeds \u00d8573 mm maximum', field:null});\n    }\n  });\n\n  return errors;\n}\n\nfunction calculateWithValidation(){\n  var errors=validateForm();\n  var vbox=document.getElementById('validation-box');\n  var vlist=document.getElementById('validation-list');\n\n  \/\/ Clear previous field highlights\n  document.querySelectorAll('.field-error').forEach(function(el){\n    el.classList.remove('field-error');\n    el.style.borderColor='';\n  });\n\n  if(errors.length>0){\n    vbox.classList.remove('hidden');\n    vlist.innerHTML=errors.map(function(e){\n      return '<li style=\"font-size:12px;color:#c0392b;padding:3px 0;display:flex;gap:8px;align-items:flex-start;\">'\n        +'<span style=\"flex-shrink:0;margin-top:2px;\">\u2022<\/span><span>'+e.msg+'<\/span><\/li>';\n    }).join('');\n    \/\/ Highlight fields with errors\n    errors.forEach(function(e){\n      if(e.field){\n        var el=document.getElementById(e.field);\n        if(el){el.style.borderColor='#c0392b';el.style.boxShadow='0 0 0 3px rgba(192,57,43,0.15)';}\n      }\n    });\n    vbox.scrollIntoView({behavior:'smooth',block:'start'});\n  } else {\n    vbox.classList.add('hidden');\n    vlist.innerHTML='';\n    calculate();\n  }\n}\n\nfunction resetCalculator(){\n  if(!confirm('Start a new project? All current data will be cleared.')) return;\n\n  \/\/ Reset hood type\n  setKT('wall');\n  setMode('linear');\n  setInst('surface');\n\n  \/\/ Reset project info\n  ['proj-name','proj-company','proj-designer','proj-email'].forEach(function(id){\n    document.getElementById(id).value='';\n  });\n\n  \/\/ Reset dimensions\n  document.getElementById('hood-length').value=2200;\n  document.getElementById('hood-width').value=900;\n  document.getElementById('nozzle-h').value=120;\n  document.getElementById('dist-c').value=2;\n  document.getElementById('dist-mr').value=3;\n  document.getElementById('uvc').value='no';\n  if(document.getElementById('vc-depth')) document.getElementById('vc-depth').value=1200;\n\n  \/\/ Reset grease channels\n  document.getElementById('n-gc').value=1;\n  gcData=[{length:2200}];\n\n  \/\/ Reset ducts\n  document.getElementById('n-ducts').value=1;\n  ducts=[{shape:'round',variant:'v1',dimA:250,dimB:250,ductLen:6000,dist:3,nozzles:0}];\n  document.getElementById('duct-list').innerHTML='';\n\n  \/\/ Reset separators\n  document.getElementById('has-sep').value='no';\n  updateSep();\n\n  \/\/ Reset appliances\n  apps=[]; appId=0;\n  renderApps();\n\n  \/\/ Reset SPY\n  document.getElementById('spy-hood-count').value=0;\n  document.getElementById('spy-duct-count').value=0;\n  document.getElementById('spy-temp').value='93';\n\n  \/\/ Reset manual release\n  document.getElementById('manual-type').value='standard';\n  document.getElementById('n-manual').value=1;\n  document.getElementById('n-switches').value=1;\n\n  \/\/ Reset pipe \/ fittings\n  document.getElementById('nbf-fixed').value=0;\n  document.getElementById('nbf-dir').value=0;\n  document.getElementById('nbf-glass').value=0;\n\n  \/\/ Hide results and validation\n  document.getElementById('results').classList.add('hidden');\n  document.getElementById('validation-box').classList.add('hidden');\n  window._lastModifications=[];\n\n  \/\/ Recalculate defaults\n  renderGC(); onHeight(); autoCalc();\n\n  \/\/ Scroll to top\n  window.scrollTo({top:0,behavior:'smooth'});\n}\n\nfunction calculate(){\n  var nt=getNT(),h=parseInt(document.getElementById('nozzle-h').value)||120;\n  var extLen=parseFloat(document.getElementById('adj-ext').value)||autoExt;\n  var detLen=parseFloat(document.getElementById('adj-det').value)||autoDet;\n  var mrLen=parseFloat(document.getElementById('adj-mr').value)||autoMR;\n  var bends=parseInt(document.getElementById('adj-bends').value)||autoBends;\n  var tp=parseInt(document.getElementById('adj-tpieces').value)||autoTP;\n  var bulk=parseInt(document.getElementById('adj-bulk').value)||1;\n  var isFS=extLen>15;\n  var uvc=document.getElementById('uvc').value;\n  var nM=parseInt(document.getElementById('n-manual').value)||1;\n  var mType=document.getElementById('manual-type').value;\n  var nSw=parseInt(document.getElementById('n-switches').value)||1;\n  var spyT=document.getElementById('spy-temp').value;\n  var warnings=[],zones=[],cookNoz=0;\n  \/\/ \u2500\u2500 Detect manual modifications to auto-calculated values \u2500\u2500\n  var modifications=[];\n  if(autoExt>0 && Math.abs(extLen-autoExt)>0.05) modifications.push('Main line 12\u00d71: recommended '+autoExt.toFixed(1)+' m \u2192 changed to '+extLen.toFixed(1)+' m');\n  if(autoDet>0 && isFS && Math.abs(detLen-autoDet)>0.05) modifications.push('Detection line 8\u00d71: recommended '+autoDet.toFixed(1)+' m \u2192 changed to '+detLen.toFixed(1)+' m');\n  if(autoMR>0 && Math.abs(mrLen-autoMR)>0.05) modifications.push('MR line 8\u00d71: recommended '+autoMR.toFixed(1)+' m \u2192 changed to '+mrLen.toFixed(1)+' m');\n  if(autoBends>0 && bends!==autoBends) modifications.push('90\u00b0 bends: recommended '+autoBends+' \u2192 changed to '+bends);\n  if(autoTP>0 && tp!==autoTP) modifications.push('T-pieces: recommended '+autoTP+' \u2192 changed to '+tp);\n  var spyHoodVal=parseInt(document.getElementById('spy-hood-count').value)||autoSpyHood;\n  var nD2=parseInt(document.getElementById('n-ducts').value)||0;\n  var spyDuctVal=nD2>0?(parseInt(document.getElementById('spy-duct-count').value)||autoSpyDuct):0;\n  if(autoSpyHood>0 && spyHoodVal!==autoSpyHood) modifications.push('SPY along hood: suggested '+autoSpyHood+' \u2192 set to '+spyHoodVal);\n  if(autoSpyDuct>0 && spyDuctVal!==autoSpyDuct) modifications.push('SPY in ducts: suggested '+autoSpyDuct+' \u2192 set to '+spyDuctVal);\n\n  if(mode==='linear'){\n    var nps=Math.ceil((parseInt(document.getElementById('hood-length').value)||2200)\/350);\n    var sides=kt==='central'?2:1; cookNoz=nps*sides;\n    if(kt==='central'){\n      \/\/ Island: split into Side A and Side B\n      zones.push({zone:'Side A \u2014 Slide ('+nps+' nozzles @ 350 mm)',nozzles:nps,type:nt});\n      zones.push({zone:'Side A \u2014 Plenum (SPY detection zone)',nozzles:0,type:'SPY',isSpy:true});\n      zones.push({zone:'Side B \u2014 Slide ('+nps+' nozzles @ 350 mm)',nozzles:nps,type:nt});\n      zones.push({zone:'Side B \u2014 Plenum (SPY detection zone)',nozzles:0,type:'SPY',isSpy:true});\n    } else if(kt==='vceil'){\n      zones.push({zone:'Cooking zone \u2014 ventilated ceiling ('+document.getElementById('hood-length').value+' mm @ 350 mm, P2)',nozzles:cookNoz,type:'P2'});\n    } else {\n      zones.push({zone:'Cooking zone \u2014 Slide (area protection '+document.getElementById('hood-length').value+' mm @ 350 mm)',nozzles:cookNoz,type:nt});\n    }\n  } else {\n    if(!apps.length){alert('Please add at least one appliance.');return;}\n    if(kt==='central'){\n      \/\/ Island by appliance: split into Side A and Side B\n      var halfIdx=Math.ceil(apps.length\/2);\n      var sideANoz=0,sideBNoz=0;\n      apps.forEach(function(a,ai){\n        var def=APPS[a.type],n=nt==='P1'?def.nP1:(h>180?def.nP2f:def.nP2);\n        if(nt==='P1'&&def.nP1===0) warnings.push(def.label+': P1 not suitable.');\n        var tot=n*a.qty; cookNoz+=tot;\n        if(ai<halfIdx){sideANoz+=tot;zones.push({zone:'Side A \u2014 '+a.qty+'\u00d7 '+def.label,nozzles:tot,type:nt});}\n        else{sideBNoz+=tot;zones.push({zone:'Side B \u2014 '+a.qty+'\u00d7 '+def.label,nozzles:tot,type:nt});}\n      });\n      zones.push({zone:'Side A \u2014 Plenum (SPY detection zone)',nozzles:0,type:'SPY',isSpy:true});\n      zones.push({zone:'Side B \u2014 Plenum (SPY detection zone)',nozzles:0,type:'SPY',isSpy:true});\n    } else {\n      apps.forEach(function(a){\n        var def=APPS[a.type],n=nt==='P1'?def.nP1:(h>180?def.nP2f:def.nP2);\n        if(nt==='P1'&&def.nP1===0) warnings.push(def.label+': P1 not suitable.');\n        var tot=n*a.qty; cookNoz+=tot;\n        zones.push({zone:a.qty+'\u00d7 '+def.label,nozzles:tot,type:nt});\n      });\n    }\n  }\n\n  var gcNoz=0,gcN=parseInt(document.getElementById('n-gc').value)||1;\n  gcData.slice(0,gcN).forEach(function(g,i){\n    var n=Math.max(1,Math.ceil(g.length\/2000)); gcNoz+=n;\n    zones.push({zone:'Grease channel '+(i+1)+' ('+g.length+' mm)',nozzles:n,type:'P2'});\n  });\n\n  var sepNoz=0;\n  if(document.getElementById('has-sep').value==='yes'){\n    var sl=parseInt(document.getElementById('sep-len').value)||2000;\n    sepNoz=Math.max(1,Math.ceil(sl\/2000));\n    zones.push({zone:'Separator\/plenum ('+sl+' mm)',nozzles:sepNoz,type:'P2'});\n  }\n\n  var ductNoz=0;\n  ducts.forEach(function(d,i){\n    var r=ductRes(d); r.warnings.forEach(function(w){warnings.push('Duct '+(i+1)+': '+w);}); ductNoz+=r.nozzles;\n    zones.push({zone:'Exhaust duct '+(i+1)+' \u2014 '+(d.shape==='rectangular'?d.dimA+'\u00d7'+d.dimB+' mm':'\u00d8'+d.dimA+' mm')+' \u2014 variant '+(d.variant==='v1'?'1':'2'),nozzles:r.nozzles,type:'P2'});\n  });\n\n  var totalNoz=cookNoz+gcNoz+sepNoz+ductNoz;\n  var litNeeded=totalNoz*2;\n  var cRes=selectContainers(totalNoz);\n  var spyCount=getSpyCount();\n\n  if(!cRes) warnings.push('Total nozzles ('+totalNoz+') exceeds maximum tandem capacity (5\u00d7 30 L). Contact protecfire.');\n  if(cRes){\n    var c0=cRes.containers[0];\n    if(extLen>c0.lineMax) warnings.push('Line length '+extLen+' m exceeds max '+c0.lineMax+' m for '+c0.vol+' L container.');\n    if(bends>c0.bMax) warnings.push('90\u00b0 bends ('+bends+') exceed max '+c0.bMax+' for '+c0.vol+' L container.');\n    if(tp>c0.tMax) warnings.push('T-pieces ('+tp+') exceed max '+c0.tMax+' for '+c0.vol+' L container.');\n  }\n  if(isFS&&detLen>40) warnings.push('Detection line '+detLen+' m exceeds maximum 40 m.');\n  if(h>260) warnings.push('Nozzle height '+h+' cm exceeds 260 cm max for P2 cooking zone.');\n  if(h<40) warnings.push('Nozzle height '+h+' cm below minimum 40 cm for P1.');\n\n  var spyLabel=uvc==='yes'?'SPY-G 15 (79\u00b0C, closed)':('SPY '+(spyT==='79'?'15':'20')+' ('+spyT+'\u00b0C)');\n  var sysName=isFS?'firespy':'detexline';\n  var cDesc=cRes?(cRes.tandem?cRes.qty+'\u00d7 PHANTOM\u00ae '+cRes.singleVol+' L (tandem)':'1\u00d7 PHANTOM\u00ae '+cRes.containers[0].vol+' L'):'\u2014';\n\n  document.getElementById('res-title').textContent=cRes?'System: '+cDesc:'Warning: out of container range';\n  document.getElementById('res-sub').textContent=cRes?totalNoz+' nozzles \u00d7 2 L = '+litNeeded+' L required \u2192 '+cDesc+(cRes.tandem?' ('+cRes.qty+' identical containers in tandem, total '+cRes.totalVol+' L)':''):'Review nozzle count';\n  document.getElementById('m-vol').textContent=cRes?cRes.totalVol+'L':'\u2014';\n  document.getElementById('m-noz').textContent=totalNoz;\n  document.getElementById('m-cook').textContent=cookNoz;\n  document.getElementById('m-gc').textContent=gcNoz;\n  document.getElementById('m-spy').textContent=spyCount;\n  document.getElementById('m-sys').textContent=sysName;\n\n  var tb=document.getElementById('tandem-box');\n  if(cRes&&cRes.tandem){\n    tb.className='tandem-box';\n    tb.innerHTML='<h4 style=\"font-size:13px;font-weight:600;color:#085041;margin-bottom:4px;\">Tandem container configuration \u2014 '+cRes.qty+'\u00d7 '+cRes.singleVol+' L<\/h4>'\n      +'<p style=\"font-size:12px;color:#085041;margin:0;\">'+litNeeded+' L required. Single container insufficient. Solution: '+cRes.qty+' identical '+cRes.singleVol+' L containers in tandem = '+cRes.totalVol+' L total. Maximum 5 containers per system.<\/p>';\n  } else { tb.className='hidden'; }\n\n  var ps=document.getElementById('pipe-summary-box');\n  if(isFS){\n    ps.innerHTML='<div class=\"pbox dual\"><h4 style=\"font-size:12px;font-weight:600;margin-bottom:4px;color:#042c53;\">12\u00d71 extinguishing line (nozzles only)<\/h4><p style=\"font-size:12px;color:#042c53;\">Length: '+extLen.toFixed(1)+' m \u00b7 '+bends+' bends \u00b7 '+tp+' T-pieces<\/p><\/div>'\n      +'<div class=\"pbox dual\"><h4 style=\"font-size:12px;font-weight:600;margin-bottom:4px;color:#042c53;\">8\u00d71 detection line (SPY only)<\/h4><p style=\"font-size:12px;color:#042c53;\">Length: '+detLen.toFixed(1)+' m \u00b7 Carries '+spyCount+'\u00d7 '+spyLabel+'<\/p><\/div>';\n  } else {\n    ps.innerHTML='<div class=\"pbox single\" style=\"grid-column:1\/-1;\"><h4 style=\"font-size:12px;font-weight:600;margin-bottom:4px;color:#085041;\">12\u00d71 detection line \u2014 detexline (SPY + nozzles together)<\/h4><p style=\"font-size:12px;color:#085041;\">Length: '+extLen.toFixed(1)+' m \u2264 15 m \u00b7 '+bends+' bends \u00b7 '+tp+' T-pieces \u00b7 '+spyCount+'\u00d7 '+spyLabel+'<\/p><\/div>';\n  }\n\n  var wb=document.getElementById('warnings-box');\n  if(warnings.length){wb.className='warn-box';wb.innerHTML='<h4 style=\"font-size:12px;font-weight:600;color:#7a5f00;margin-bottom:4px;\">Design warnings ('+warnings.length+')<\/h4><ul>'+warnings.map(function(w){return '<li style=\"font-size:12px;color:#6a5000;margin-left:14px;margin-bottom:2px;\">'+w+'<\/li>';}).join('')+'<\/ul>';}\n  else{wb.className='hidden';}\n\n  var mb=document.getElementById('modifications-box');\n  if(modifications.length){\n    mb.className='';\n    mb.style.cssText='background:#fff8f0;border:1.5px solid #f0c4a0;border-radius:var(--rs);padding:12px 16px;margin-bottom:12px;';\n    mb.innerHTML='<h4 style=\"font-size:12px;font-weight:700;color:var(--brand-dark);margin-bottom:6px;\">\u26a0 Recommended values were modified by the user<\/h4>'\n      +'<p style=\"font-size:11px;color:var(--brand-dark);margin-bottom:6px;\">The following values differ from the automatically calculated recommendations. This report reflects the modified values.<\/p>'\n      +'<ul>'+modifications.map(function(m){return '<li style=\"font-size:12px;color:#7a3a00;margin-left:14px;margin-bottom:2px;\">'+m+'<\/li>';}).join('')+'<\/ul>';\n  } else {\n    mb.className='hidden';\n    mb.style.cssText='';\n  }\n\n  document.getElementById('noz-rows').innerHTML=zones.filter(function(z){return !z.isSpy;}).map(function(z){\n    return '<div class=\"noz-row\"><span class=\"noz-zone\">'+z.zone+'<\/span><span class=\"noz-type\">'+z.type+'<\/span><span class=\"noz-qty\">'+z.nozzles+'<\/span><\/div>';\n  }).join('');\n  document.getElementById('noz-total-val').textContent=totalNoz+' \u00d7 2 L = '+litNeeded+' L \u2192 '+cDesc;\n\n  var mDesc={'standard':'Pneumatic (stainless 1.4305). Pull ring, press button.','button-surface':'Surface-mounted (stainless 1.4404, 107\u00d745\u00d745 mm).','button-flush':'Flush in masonry (160\u00d7160 mm + 160\u00d7160\u00d780 mm box).','button-cavity':'Cavity wall (160\u00d7160 mm + 165\u00d7165 mm recess).'};\n  var bom=[];\n  bom.push({sec:'Container(s) & extinguishing agent'});\n  if(cRes){\n    var c0=cRes.containers[0];\n    var contSAPref={'7':'0160212','14':'0160208','25':'0160210','30':'0160211'};\n    bom.push({item:'Extinguishing agent container PHANTOM\u00ae '+c0.vol+' L'+(cRes.tandem?' (tandem \u00d7'+cRes.qty+')':''),desc:'\u00d8'+c0.d+' mm \u00d7 '+c0.w+' mm, stainless 1.44xx\/1.43xx. '+c0.kg+' kg each. 11 bar @ 20\u00b0C.'+(cRes.tandem?' Tandem: '+cRes.qty+' identical containers in parallel = '+cRes.totalVol+' L total. Max 5 in tandem.':''),qty:cRes.qty,ref:contSAPref[String(c0.vol)]||('PHANT-'+c0.vol+'L')});\n    bom.push({item:'TIBOREX ABSOLUTE\u00ae',desc:'Biodegradable extinguishing agent (DIN EN ISO 9888). \u221250\/+80\u00b0C. 10 years shelf life. 1.275 kg\/L.',qty:cRes.totalVol+' L',ref:'TIBOREX-ABS'});\n  }\n  bom.push({item:'Pressure switch',desc:'IP67 AC15 3A 400V DC. M22\u00d71.5, 80 Nm. 4\u201360 bar, \u00b10.3 bar. 1 NO + 1 NC. Reset by pulling blue lock.',qty:nSw,ref:'0106026'});\n  bom.push({sec:'Fine spray nozzles'});\n  if(cookNoz>0) bom.push({item:'Nozzle '+nt+' \u2014 cooking zone',desc:'PHANTOM\u00ae '+nt+'. K-value 1. Spray ~'+(nt==='P1'?'90':'20')+'\u00b0. '+(mode==='linear'?'Area protection: placed every 350 mm along hood'+(kt==='central'?' \u2014 both sides.':'.'):('Protection by appliance: each nozzle directed at its specific cooking appliance. No area overlap. '+(kt==='central'?'Both sides of island.':''))),qty:cookNoz,ref:nt==='P1'?'0160068':'0007066'});\n  if(gcNoz>0) bom.push({item:'Nozzle P2 \u2014 grease channel(s)',desc:'PHANTOM\u00ae P2. 1 nozzle per 2 m. '+gcN+' channel(s).',qty:gcNoz,ref:'0007066'});\n  if(sepNoz>0) bom.push({item:'Nozzle P2 \u2014 separator\/plenum',desc:'PHANTOM\u00ae P2 per \u00a76.7. 1 per 2000 mm.',qty:sepNoz,ref:'0007066'});\n  if(ductNoz>0) bom.push({item:'Nozzle P2 \u2014 exhaust duct(s)',desc:'PHANTOM\u00ae P2 per \u00a76.8\/6.9. 2 per duct.',qty:ductNoz,ref:'0007066'});\n  bom.push({sec:'Detection and Manual Release'});\n  bom.push({item:uvc==='yes'?'SPY-G 15':'SPY '+(spyT==='79'?'15':'20'),desc:(uvc==='yes'?'SPY-G 15 \u2014 closed ampoule (UV-C safe). 79\u00b0C.':'SPY '+(spyT==='79'?'15':'20')+' \u2014 open ampoule. '+spyT+'\u00b0C.')+' Argon propellant. Teflon sealed. 1 per deep fryer; 1 per 2 m of hood; 1 per duct. Min 2\/system.',qty:spyCount,ref:uvc==='yes'?'0102179':'\u2014 pending'});\n  bom.push({item:'Manual Release '+(mType==='standard'?'Standard':'Button \u2014 '+mType.replace('button-','')),desc:mDesc[mType]+' Min 1 per entrance\/exit.',qty:nM,ref:'0104036'});\n  bom.push({sec:'Fittings & control'});\n  if(isFS){\n    bom.push({item:'Extinguishing line \u2014 stainless tube 12\u00d71',desc:'Nozzles only. No SPY. firespy dual-pipe mode.',qty:Math.ceil(extLen)+' m',ref:'\u2014 pending'});\n    bom.push({item:'Detection line \u2014 stainless tube 8\u00d71',desc:'SPY detectors only. No nozzles. Route: container \u2192 SPY positions.',qty:Math.ceil(detLen)+' m',ref:'\u2014 pending'});\n  } else {\n    bom.push({item:'Main line \u2014 stainless tube 12\u00d71 (detexline)',desc:'SPY detectors + nozzles together. \u2264 15 m.',qty:Math.ceil(extLen)+' m',ref:'\u2014 pending'});\n  }\n  bom.push({item:'Manual release line \u2014 stainless tube 8\u00d71',desc:'Control line: container \u2192 manual release unit. Carries pneumatic activation signal.',qty:Math.ceil(mrLen)+' m',ref:'\u2014 pending'});\n  bom.push({item:'90\u00b0 bends',desc:'Stainless steel 90\u00b0 elbow fittings for pipe routing.',qty:bends,ref:'\u2014 pending'});\n  bom.push({item:'T-pieces',desc:'Stainless steel T-piece fittings for pipe branching.',qty:tp,ref:'\u2014 pending'});\n  bom.push({item:'Bulkhead fitting'+(bulk>1?' \u2014 set of '+bulk:''),desc:isFS?'1\u00d7 12\u00d71 (extinguishing line) + 1\u00d7 8\u00d71 (detection line). Seals pipe penetrations through wall\/ceiling.':'1\u00d7 12\u00d71 (detexline). Seals pipe penetration through wall\/ceiling.',qty:bulk,ref:'\u2014 pending'});\n  \/\/ Nozzle bulkhead fittings\n  var nbfFixed=parseInt(document.getElementById('nbf-fixed').value)||0;\n  var nbfDir=parseInt(document.getElementById('nbf-dir').value)||0;\n  var nbfGlass=parseInt(document.getElementById('nbf-glass').value)||0;\n  if(nbfFixed>0) bom.push({item:'Nozzle bulkhead fitting',desc:'Receives pipe on one side, fixed P2 nozzle pointing down on the other. Embedded in hood or ceiling panel. 1 per nozzle position.',qty:nbfFixed,ref:'0160059'});\n  if(nbfDir>0) bom.push({item:'Directional nozzle bulkhead fitting',desc:'Receives pipe on one side, orientable P2 nozzle on the other. Allows angle adjustment for precise targeting.',qty:nbfDir,ref:'0160074'});\n  if(nbfGlass>0) bom.push({item:'Nozzle bulkhead fitting \u2014 glass panel',desc:'Adapter for glass panel installations (show cooking \/ display kitchens). Fixed P2 nozzle.',qty:nbfGlass,ref:'0160056'});\n\n  document.getElementById('bom-body').innerHTML=bom.map(function(b){\n    if(b.sec) return '<tr class=\"sec\"><td colspan=\"4\">'+b.sec+'<\/td><\/tr>';\n    return '<tr><td style=\"color:var(--g600);font-size:12px;\">'+b.item+'<\/td><td style=\"color:var(--g600);font-size:12px;\">'+b.desc+'<\/td><td class=\"qty\">'+b.qty+'<\/td><td class=\"ref\">'+b.ref+'<\/td><\/tr>';\n  }).join('');\n\n  var inputs=[['Project',document.getElementById('proj-name').value||'\u2014'],['Company',document.getElementById('proj-company').value||'\u2014'],['Designer',document.getElementById('proj-designer').value||'\u2014'],['Email',document.getElementById('proj-email').value||'\u2014'],['Hood type',kt==='central'?'Central \/ island':kt==='vceil'?'Ventilated ceiling':'Wall'],['Installation',inst==='concealed'?'Concealed (flush)':'Surface mounted'],['Protection',mode==='linear'?'Area protection':'By appliance'],['Hood length',document.getElementById('hood-length').value+' mm'],['Hood width',document.getElementById('hood-width').value+' mm'],['Nozzle height',document.getElementById('nozzle-h').value+' cm \u2192 '+nt],['Container\u2192hood',document.getElementById('dist-c').value+' m'],['Container\u2192MR',document.getElementById('dist-mr').value+' m'],['UV-C',uvc==='yes'?'Yes \u2014 SPY-G 15':'No'],['SPY temp',spyT+'\u00b0C'],['Grease channels',document.getElementById('n-gc').value],['Exhaust ducts',(parseInt(document.getElementById('n-ducts').value)||0)],['System type',sysName.toUpperCase()],['Main 12\u00d71 line',extLen.toFixed(1)+' m'],['Detection 8\u00d71',isFS?detLen.toFixed(1)+' m':'N\/A (detexline)'],['MR line 8\u00d71',mrLen.toFixed(1)+' m'],['Bends',bends],['T-pieces',tp],['Bulkhead fittings',bulk]];\n  \/\/ Build 2-column dark design inputs \u2014 each pair is one full-width row\n  var inputsHtml='<style>'\n    +'.dinput-row{display:grid;grid-template-columns:1fr 1fr;border-radius:6px;transition:background .12s;cursor:default;margin-bottom:3px;}'\n    +'.dinput-row.odd{background:rgba(255,255,255,0.055);}'\n    +'.dinput-row.even{background:rgba(255,255,255,0.018);}'\n    +'.dinput-row:hover{background:rgba(224,90,0,0.16)!important;}'\n    +'.dinput-row:last-child{margin-bottom:0;}'\n    +'.dinput-cell{display:flex;justify-content:space-between;align-items:center;padding:8px 12px;}'\n    +'<\/style>'\n    +'<div style=\"background:#1a1919;border-radius:12px;padding:20px 24px;margin-bottom:16px;\">'\n    +'<div style=\"font-size:11px;font-weight:700;color:#E05A00;letter-spacing:.8px;text-transform:uppercase;margin-bottom:14px;\">Design inputs summary<\/div>'\n    +'<div>';\n  for(var ii=0;ii<inputs.length;ii+=2){\n    var a=inputs[ii], b=inputs[ii+1]||null;\n    var cls=(ii\/2)%2===0?'odd':'even';\n    inputsHtml+='<div class=\"dinput-row '+cls+'\">'\n      +'<div class=\"dinput-cell\">'\n        +'<span style=\"font-size:11px;color:#555;margin-right:8px;white-space:nowrap;\">'+a[0]+'<\/span>'\n        +'<span style=\"font-size:12px;font-weight:600;color:#c8c8c4;text-align:right;\">'+a[1]+'<\/span>'\n      +'<\/div>'\n      +(b\n        ?'<div class=\"dinput-cell\" style=\"border-left:1px solid rgba(255,255,255,0.04);\">'\n          +'<span style=\"font-size:11px;color:#555;margin-right:8px;white-space:nowrap;\">'+b[0]+'<\/span>'\n          +'<span style=\"font-size:12px;font-weight:600;color:#c8c8c4;text-align:right;\">'+b[1]+'<\/span>'\n        +'<\/div>'\n        :'<div class=\"dinput-cell\"><\/div>')\n      +'<\/div>';\n  }\n  inputsHtml+='<\/div><\/div>';\n  document.getElementById('print-inputs-box').innerHTML=inputsHtml;\n\n  \/\/ Store modifications for PDF access\n  \/\/ Auto-fill nozzle bulkhead fitting qty with cookNoz\n  var nbfFixedEl=document.getElementById('nbf-fixed');\n  if((parseInt(nbfFixedEl.value)||0)===0) nbfFixedEl.value=cookNoz;\n\n  window._lastModifications = modifications;\n\n  document.getElementById('results').classList.remove('hidden');\n  document.getElementById('results').scrollIntoView({behavior:'smooth',block:'start'});\n  generateDiagram({\n    kt:kt, isFS:isFS, nt:nt, mode:mode, apps:apps,\n    hoodLenMm: parseInt(document.getElementById('hood-length').value)||2200,\n    vcDepthMm: parseInt(document.getElementById('vc-depth').value)||1200,\n    nozzleHCm: h,\n    distCM: parseFloat(document.getElementById('dist-c').value)||2,\n    distMRM: parseFloat(document.getElementById('adj-mr').value)||autoMR,\n    cookNoz: cookNoz, gcNoz: gcNoz, sepNoz: sepNoz,\n    spyCount: spyCount, nDucts: ducts.length, ducts: ducts,\n    extLen: extLen, detLen: detLen, mrLen: mrLen,\n    bends: bends, tp: tp, sysName: sysName,\n    cDesc: cDesc\n  });\n}\n\n\nfunction generateVCeilElevation(d){\n  \/\/ Ventilated ceiling elevation \u2014 reuses generateDiagram with vceil visual patch\n  generateDiagram(d);\n}\n\n\n\nfunction generateVCeilTopView(d){\n  \/\/ Ventilated ceiling top view \u2014 reuses generateTopDiagram with vceil visual patch\n  generateTopDiagram(d);\n}\n\n\n\n\nfunction drawNozzle(cx, tipY, nt, color){\n  var pts=(cx-7)+','+tipY+' '+(cx+7)+','+tipY+' '+cx+','+(tipY+13);\n  return '<polygon points=\"'+pts+'\" fill=\"'+color+'\" opacity=\"0.9\"\/>'\n    +(nt?'<text x=\"'+cx+'\" y=\"'+(tipY+23)+'\" text-anchor=\"middle\" font-size=\"7\" fill=\"'+color+'\">'+nt+'<\/text>':'');\n}\nfunction generateDiagram(d){\n  \/\/ Appliance widths in mm (approximate standard sizes)\n  var APP_W={deepfryer_single:400,deepfryer_double:750,grill_plate:650,cooktop_small:600,cooktop_large:900,grill_025:500,grill_050:700,grill_075:900,wok_240:350,wok_300:400,wok_500:600};\n  var APP_LABEL={deepfryer_single:'Fryer',deepfryer_double:'Dbl Fryer',grill_plate:'Grill Plate',cooktop_small:'Cooktop S',cooktop_large:'Cooktop L',grill_025:'Grill 0.25',grill_050:'Grill 0.50',grill_075:'Grill 0.75',wok_240:'Wok 240',wok_300:'Wok 300',wok_500:'Wok 500'};\n  var APP_NOZ={deepfryer_single:1,deepfryer_double:2,grill_plate:1,cooktop_small:1,cooktop_large:2,grill_025:1,grill_050:2,grill_075:3,wok_240:1,wok_300:1,wok_500:1};\n  var APP_COLORS=['#c0392b','#7a3fb5','#b84900','#1a7a4a','#d4537e','#854f0b'];\n\n  var W=880, H=460;\n  var PAD=40, FLOOR=H-65;\n  var hoodH=Math.min(Math.max(d.nozzleHCm*0.85,80),200);\n  var pipeY=FLOOR-hoodH;\n  var contX=PAD+10;\n  var hoodStartX=PAD+80;\n  var hoodEndX=W-PAD-55;\n  var hoodW=hoodEndX-hoodStartX;\n  var C12='#185fa5',C8d='#0f6e56',C8m='#E05A00',CNOZ='#E05A00',CSPY='#0f6e56',CGND='#8a8880',CTXT='#1e1e1d';\n\n  var svg='<svg viewBox=\"0 0 '+W+' '+H+'\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:100%;max-width:'+W+'px;font-family:Arial,Helvetica,sans-serif;\">';\n  svg+='<defs><marker id=\"earr\" markerWidth=\"6\" markerHeight=\"6\" refX=\"5\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L0,6 L6,3 Z\" fill=\"'+CGND+'\"\/><\/marker><\/defs>';\n\n  \/\/ Floor\n  svg+='<line x1=\"'+hoodStartX+'\" y1=\"'+FLOOR+'\" x2=\"'+hoodEndX+'\" y2=\"'+FLOOR+'\" stroke=\"'+CGND+'\" stroke-width=\"3\" stroke-linecap=\"round\"\/>';\n  svg+='<text x=\"'+(hoodStartX+hoodW\/2)+'\" y=\"'+(FLOOR+14)+'\" text-anchor=\"middle\" font-size=\"10\" fill=\"'+CGND+'\">Cooking surface \/ worktop<\/text>';\n\n  \/\/ Hood outline (or ventilated ceiling panel)\n  var hoodTop=pipeY-14;\n  if(d.kt==='vceil'){\n    \/\/ Ceiling panel above pipe\n    svg+='<rect x=\"'+hoodStartX+'\" y=\"0\" width=\"'+hoodW+'\" height=\"'+hoodTop+'\" fill=\"rgba(180,195,220,0.07)\" stroke=\"#b5d4f4\" stroke-width=\"0.5\" stroke-dasharray=\"4,3\"\/>';\n    svg+='<text x=\"'+(hoodStartX+6)+'\" y=\"12\" font-size=\"9\" fill=\"#8a8880\">Plenum<\/text>';\n    svg+='<rect x=\"'+hoodStartX+'\" y=\"'+hoodTop+'\" width=\"'+hoodW+'\" height=\"14\" fill=\"#d8e4f0\" stroke=\"#85b7eb\" stroke-width=\"1.5\"\/>';\n    for(var _px=hoodStartX+16;_px<hoodEndX-8;_px+=18){svg+='<circle cx=\"'+_px+'\" cy=\"'+(hoodTop+7)+'\" r=\"2\" fill=\"#85b7eb\" opacity=\"0.5\"\/>';}\n    svg+='<text x=\"'+(hoodStartX+6)+'\" y=\"'+(hoodTop-4)+'\" font-size=\"9\" fill=\"#85b7eb\">Ventilated ceiling panel<\/text>';\n    \/\/ Floor label\n    svg+='<text x=\"'+(hoodStartX+hoodW\/2)+'\" y=\"'+(FLOOR+14)+'\" text-anchor=\"middle\" font-size=\"10\" fill=\"'+CGND+'\">Cooking surface \/ floor<\/text>';\n  } else {\n    svg+='<rect x=\"'+hoodStartX+'\" y=\"'+hoodTop+'\" width=\"'+hoodW+'\" height=\"'+(FLOOR-hoodTop)+'\" fill=\"rgba(24,95,165,0.03)\" stroke=\"#b5d4f4\" stroke-width=\"1\" stroke-dasharray=\"5,4\" rx=\"3\"\/>';\n    svg+='<text x=\"'+(hoodStartX+6)+'\" y=\"'+(hoodTop+12)+'\" font-size=\"10\" fill=\"#85b7eb\">Hood<\/text>';\n  }\n\n  \/\/ Height dimension\n  var dimX=hoodStartX-22;\n  svg+='<line x1=\"'+dimX+'\" y1=\"'+pipeY+'\" x2=\"'+dimX+'\" y2=\"'+FLOOR+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\" marker-end=\"url(#earr)\"\/>';\n  svg+='<line x1=\"'+(dimX-4)+'\" y1=\"'+pipeY+'\" x2=\"'+(dimX+4)+'\" y2=\"'+pipeY+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\"\/>';\n  svg+='<line x1=\"'+(dimX-4)+'\" y1=\"'+FLOOR+'\" x2=\"'+(dimX+4)+'\" y2=\"'+FLOOR+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\"\/>';\n  svg+='<text x=\"'+(dimX-5)+'\" y=\"'+(pipeY+(FLOOR-pipeY)\/2+4)+'\" text-anchor=\"middle\" font-size=\"9\" fill=\"'+CGND+'\" transform=\"rotate(-90,'+(dimX-5)+','+(pipeY+(FLOOR-pipeY)\/2+4)+')\">'+d.nozzleHCm+' cm<\/text>';\n\n  \/\/ Container\n  var contW=28,contH=70,contY=FLOOR-contH;\n  svg+='<rect x=\"'+(contX-contW\/2)+'\" y=\"'+contY+'\" width=\"'+contW+'\" height=\"'+contH+'\" rx=\"4\" fill=\"#2c2c2a\" stroke=\"#5a5956\" stroke-width=\"1.5\"\/>';\n  svg+='<rect x=\"'+(contX-8)+'\" y=\"'+(contY-8)+'\" width=\"16\" height=\"10\" rx=\"2\" fill=\"#444\" stroke=\"#666\" stroke-width=\"1\"\/>';\n  svg+='<text x=\"'+contX+'\" y=\"'+(FLOOR+14)+'\" text-anchor=\"middle\" font-size=\"9\" fill=\"'+CTXT+'\">Container<\/text>';\n  svg+='<text x=\"'+contX+'\" y=\"'+(FLOOR+24)+'\" text-anchor=\"middle\" font-size=\"8\" fill=\"'+CGND+'\">'+d.cDesc.replace('1\\u00d7 PHANTOM\\u00ae ','').replace(\/\\d+\\u00d7 PHANTOM\\u00ae \/,'')+'<\/text>';\n\n  \/\/ Main pipe\n  svg+='<line x1=\"'+contX+'\" y1=\"'+(contY-8)+'\" x2=\"'+contX+'\" y2=\"'+pipeY+'\" stroke=\"'+C12+'\" stroke-width=\"2\"\/>';\n  svg+='<line x1=\"'+contX+'\" y1=\"'+pipeY+'\" x2=\"'+hoodEndX+'\" y2=\"'+pipeY+'\" stroke=\"'+C12+'\" stroke-width=\"2.5\"\/>';\n  svg+='<text x=\"'+(hoodStartX+hoodW*0.35)+'\" y=\"'+(pipeY-6)+'\" font-size=\"9\" fill=\"'+C12+'\">12\\u00d71 '+(d.isFS?'extinguishing':'detexline')+' ('+Math.ceil(d.extLen)+' m)<\/text>';\n\n  \/\/ Hood length dimension\n  svg+='<line x1=\"'+hoodStartX+'\" y1=\"'+(FLOOR+28)+'\" x2=\"'+hoodEndX+'\" y2=\"'+(FLOOR+28)+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\" marker-end=\"url(#earr)\"\/>';\n  svg+='<line x1=\"'+hoodEndX+'\" y1=\"'+(FLOOR+28)+'\" x2=\"'+hoodStartX+'\" y2=\"'+(FLOOR+28)+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\" marker-end=\"url(#earr)\"\/>';\n  svg+='<text x=\"'+(hoodStartX+hoodW\/2)+'\" y=\"'+(FLOOR+40)+'\" text-anchor=\"middle\" font-size=\"9\" fill=\"'+CGND+'\">'+(d.hoodLenMm\/1000).toFixed(1)+' m<\/text>';\n\n  \/\/ Pre-compute SPY Y position and orientation\n  \/\/ detexline: diamond sits ABOVE pipe, bottom vertex touching pipeY\n  \/\/ firespy: diamond hangs BELOW detection line, top vertex touching detY\n  var spyY=pipeY; \/\/ default\n  var spyAbove=true; \/\/ detexline: diamond above pipe\n  if(d.isFS){\n    var _dBoxH=48,_dBoxOffset=22;\n    spyY=pipeY-_dBoxOffset-_dBoxH\/2; \/\/ detY = mid of duct box\n    spyAbove=false; \/\/ firespy: diamond hangs below detection line\n  }\n\n  if(d.mode==='appliance' && d.apps && d.apps.length>0){\n    \/\/ \u2500\u2500 BY APPLIANCE MODE: draw each appliance and its nozzles \u2500\u2500\n    \/\/ Calculate total width needed\n    var mmScale=hoodW\/d.hoodLenMm;\n    var appH=Math.min(50, hoodH*0.4);\n    var curX=hoodStartX;\n\n    d.apps.forEach(function(a,ai){\n      var aw=Math.round((APP_W[a.type]||500)*mmScale);\n      var nozPerUnit=APP_NOZ[a.type]||1;\n      var color=APP_COLORS[ai%APP_COLORS.length];\n      var label=APP_LABEL[a.type]||a.type;\n\n      for(var qi=0;qi<a.qty;qi++){\n        var ax=curX, aw2=aw;\n        \/\/ Appliance box on floor\n        svg+='<rect x=\"'+ax+'\" y=\"'+(FLOOR-appH)+'\" width=\"'+(aw2-4)+'\" height=\"'+appH+'\" rx=\"2\" fill=\"'+color+'\" opacity=\"0.15\" stroke=\"'+color+'\" stroke-width=\"1\"\/>';\n        svg+='<text x=\"'+(ax+aw2\/2-2)+'\" y=\"'+(FLOOR-appH+10)+'\" text-anchor=\"middle\" font-size=\"8\" fill=\"'+color+'\">'+label+'<\/text>';\n        svg+='<text x=\"'+(ax+aw2\/2-2)+'\" y=\"'+(FLOOR-appH+20)+'\" text-anchor=\"middle\" font-size=\"7\" fill=\"'+color+'\">'+(APP_W[a.type]||500)+' mm<\/text>';\n\n        \/\/ Nozzles above this appliance\n        for(var ni=0;ni<nozPerUnit;ni++){\n          var nxOffset=(ni+1)*aw2\/(nozPerUnit+1);\n          var nx=ax+nxOffset;\n          \/\/ Vertical dashed line from pipe down to appliance\n          svg+='<line x1=\"'+nx+'\" y1=\"'+pipeY+'\" x2=\"'+nx+'\" y2=\"'+(FLOOR-appH)+'\" stroke=\"'+color+'\" stroke-width=\"0.8\" stroke-dasharray=\"3,3\" opacity=\"0.6\"\/>';\n          \/\/ Nozzle body\n          svg+=drawNozzle(nx, pipeY, d.nt, color);\n        }\n        curX+=aw;\n      }\n    });\n\n    \/\/ SPY detectors \u2014 hanging below detection line (firespy) or sitting on pipe\n    \/\/ SPY on pipe = total minus duct SPY (duct boxes draw their own)\n    var _spyOnPipe=Math.max(0,d.spyCount-d.nDucts);\n    var spySp=hoodW\/(_spyOnPipe+1);\n    for(var s=1;s<=_spyOnPipe;s++){\n      var sx=hoodStartX+s*spySp;\n      if(spyAbove){svg+='<polygon points=\"'+sx+','+(spyY-12)+' '+(sx+6)+','+(spyY-6)+' '+sx+','+spyY+' '+(sx-6)+','+(spyY-6)+'\" fill=\"'+CSPY+'\"\/>';}\n      else{svg+='<polygon points=\"'+sx+','+spyY+' '+(sx+6)+','+(spyY+6)+' '+sx+','+(spyY+12)+' '+(sx-6)+','+(spyY+6)+'\" fill=\"'+CSPY+'\"\/>';}\n    }\n\n  } else {\n    \/\/ \u2500\u2500 AREA PROTECTION MODE: uniform nozzles \u2500\u2500\n    var cookPerSide=d.kt==='central'?Math.round(d.cookNoz\/2):d.cookNoz;\n    var totalNozOnLine=cookPerSide+d.gcNoz+d.sepNoz;\n    var nozSp=hoodW\/(totalNozOnLine+1);\n    for(var i=1;i<=totalNozOnLine;i++){\n      var nx=hoodStartX+i*nozSp;\n      svg+=drawNozzle(nx, pipeY, d.nt, CNOZ);\n    }\n    \/\/ 350mm spacing dimension between first two nozzles\n    if(totalNozOnLine>=2){\n      var n1x=hoodStartX+nozSp, n2x=hoodStartX+2*nozSp;\n      var dimYsp=pipeY+36;\n      svg+='<line x1=\"'+n1x+'\" y1=\"'+dimYsp+'\" x2=\"'+n2x+'\" y2=\"'+dimYsp+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\" marker-end=\"url(#earr)\"\/>';\n      svg+='<line x1=\"'+n2x+'\" y1=\"'+dimYsp+'\" x2=\"'+n1x+'\" y2=\"'+dimYsp+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\" marker-end=\"url(#earr)\"\/>';\n      svg+='<text x=\"'+((n1x+n2x)\/2)+'\" y=\"'+(dimYsp+12)+'\" text-anchor=\"middle\" font-size=\"8\" fill=\"'+CGND+'\">350 mm<\/text>';\n    }\n    \/\/ SPY on pipe = total minus duct SPY (duct boxes draw their own)\n    var _spyOnPipe=Math.max(0,d.spyCount-d.nDucts);\n    var spySp=hoodW\/(_spyOnPipe+1);\n    for(var s=1;s<=_spyOnPipe;s++){\n      var sx=hoodStartX+s*spySp;\n      if(spyAbove){svg+='<polygon points=\"'+sx+','+(spyY-12)+' '+(sx+6)+','+(spyY-6)+' '+sx+','+spyY+' '+(sx-6)+','+(spyY-6)+'\" fill=\"'+CSPY+'\"\/>';}\n      else{svg+='<polygon points=\"'+sx+','+spyY+' '+(sx+6)+','+(spyY+6)+' '+sx+','+(spyY+12)+' '+(sx-6)+','+(spyY+6)+'\" fill=\"'+CSPY+'\"\/>';}\n    }\n  }\n\n  \/\/ Detection line (firespy)\n  if(d.isFS){\n    var ductBoxH=48,ductBoxOffset=22;\n    var detY=pipeY-ductBoxOffset-ductBoxH\/2;\n    svg+='<line x1=\"'+contX+'\" y1=\"'+(contY-8)+'\" x2=\"'+contX+'\" y2=\"'+detY+'\" stroke=\"'+C8d+'\" stroke-width=\"1.5\" stroke-dasharray=\"5,3\"\/>';\n    var detEndX=d.nDucts>0?(hoodStartX+hoodW):(hoodStartX+hoodW*0.6);\n    svg+='<line x1=\"'+contX+'\" y1=\"'+detY+'\" x2=\"'+detEndX+'\" y2=\"'+detY+'\" stroke=\"'+C8d+'\" stroke-width=\"1.5\" stroke-dasharray=\"5,3\"\/>';\n    svg+='<text x=\"'+(hoodStartX+hoodW*0.15)+'\" y=\"'+(detY-6)+'\" font-size=\"9\" fill=\"'+C8d+'\">8\\u00d71 detection ('+Math.ceil(d.detLen)+' m)<\/text>';\n  }\n\n  \/\/ Manual release line\n  var mrX=hoodEndX+46, mrY=pipeY+40;\n  svg+='<line x1=\"'+contX+'\" y1=\"'+(contY+10)+'\" x2=\"'+contX+'\" y2=\"'+(FLOOR-10)+'\" stroke=\"'+C8m+'\" stroke-width=\"1.5\" stroke-dasharray=\"3,3\" opacity=\"0.7\"\/>';\n  svg+='<line x1=\"'+contX+'\" y1=\"'+(FLOOR-10)+'\" x2=\"'+mrX+'\" y2=\"'+(FLOOR-10)+'\" stroke=\"'+C8m+'\" stroke-width=\"1.5\" stroke-dasharray=\"3,3\" opacity=\"0.7\"\/>';\n  svg+='<line x1=\"'+mrX+'\" y1=\"'+(FLOOR-10)+'\" x2=\"'+mrX+'\" y2=\"'+mrY+'\" stroke=\"'+C8m+'\" stroke-width=\"1.5\" stroke-dasharray=\"3,3\" opacity=\"0.7\"\/>';\n  svg+='<rect x=\"'+(mrX-9)+'\" y=\"'+(mrY-9)+'\" width=\"18\" height=\"18\" rx=\"3\" fill=\"#fff3ec\" stroke=\"'+C8m+'\" stroke-width=\"1.5\"\/>';\n  svg+='<circle cx=\"'+mrX+'\" cy=\"'+mrY+'\" r=\"5\" fill=\"'+C8m+'\"\/>';\n  svg+='<text x=\"'+mrX+'\" y=\"'+(mrY-22)+'\" text-anchor=\"middle\" font-size=\"9\" fill=\"'+C8m+'\">MR ('+Math.ceil(d.mrLen)+' m)<\/text>';\n\n  \/\/ Exhaust ducts \u2014 drawn ABOVE the pipe\n  if(d.nDucts>0){\n    var ductArr=d.ducts||[];\n    var ductSp=hoodW\/(d.nDucts+1);\n    \/\/ Scale box width based on number of ducts to avoid overlap\n    var boxW=Math.min(52,Math.max(28,(ductSp*0.7)));\n    var boxH=48;\n    \/\/ Pre-compute detection line Y for firespy branch\n    var detYd=d.isFS?(pipeY-22-48\/2):null;\n    for(var dd=1;dd<=d.nDucts;dd++){\n      var dx=hoodStartX+dd*ductSp;\n      var boxX=dx-boxW\/2;\n      var boxY=pipeY-100-boxH;\n      \/\/ How many nozzles does this duct have?\n      var ductObj=ductArr[dd-1]||null;\n      var nozInDuct=2; \/\/ default per \u00a76.8\/6.9\n      if(ductObj){\n        var r2=ductRes(ductObj); nozInDuct=r2.nozzles;\n      }\n      \/\/ Red line: 12\u00d71 extinguishing \u2192 nozzles\n      svg+='<line x1=\"'+dx+'\" y1=\"'+(pipeY-2)+'\" x2=\"'+dx+'\" y2=\"'+(boxY+boxH)+'\" stroke=\"#e24b4a\" stroke-width=\"1.5\" stroke-dasharray=\"4,3\"\/>';\n      \/\/ Green line (firespy only): 8\u00d71 detection \u2192 SPY in duct box\n      if(d.isFS && detYd!==null){\n        var greenX=dx+5; \/\/ offset slightly to right to avoid overlap with red\n        svg+='<line x1=\"'+greenX+'\" y1=\"'+detYd+'\" x2=\"'+greenX+'\" y2=\"'+(boxY+boxH-8)+'\" stroke=\"#0f6e56\" stroke-width=\"1.2\" stroke-dasharray=\"4,3\" opacity=\"0.9\"\/>';\n      }\n      \/\/ Duct box\n      svg+='<rect x=\"'+boxX+'\" y=\"'+boxY+'\" width=\"'+boxW+'\" height=\"'+boxH+'\" rx=\"3\" fill=\"#fff5f5\" stroke=\"#e24b4a\" stroke-width=\"1\"\/>';\n      svg+='<text x=\"'+dx+'\" y=\"'+(boxY-4)+'\" text-anchor=\"middle\" font-size=\"8\" fill=\"#e24b4a\">Duct '+dd+'<\/text>';\n      \/\/ Nozzles inside box \u2014 no P2 label to avoid overlap with SPY\n      for(var ni2=0;ni2<nozInDuct;ni2++){\n        var nxd=boxX+(ni2+1)*boxW\/(nozInDuct+1);\n        svg+=drawNozzle(nxd,boxY+4,'','#e24b4a');\n      }\n      \/\/ SPY detector at bottom of box, label below box\n      var spyXd=dx, spyYd=boxY+boxH-16;\n      svg+='<polygon points=\"'+spyXd+','+spyYd+' '+(spyXd+5)+','+(spyYd+5)+' '+spyXd+','+(spyYd+10)+' '+(spyXd-5)+','+(spyYd+5)+'\" fill=\"#0f6e56\"\/>';\n\n    }\n  }\n\n  \/\/ Island note\n  if(d.kt==='central'){\n    svg+='<text x=\"'+hoodStartX+'\" y=\"10\" text-anchor=\"start\" font-size=\"9\" fill=\"#85b7eb\">Central\/island hood \\u2014 elevation shows Side A. Side B is mirrored (same nozzle count).<\/text>';\n    \/\/ Side A and Side B labels on hood\n    svg+='<rect x=\"'+hoodStartX+'\" y=\"'+(hoodTop+2)+'\" width=\"'+(hoodW*0.48)+'\" height=\"14\" rx=\"2\" fill=\"rgba(224,90,0,0.1)\"\/>';\n    svg+='<text x=\"'+(hoodStartX+hoodW*0.24)+'\" y=\"'+(hoodTop+12)+'\" text-anchor=\"middle\" font-size=\"9\" fill=\"#b84900\" font-weight=\"600\">Side A \\u2014 Slide<\/text>';\n    svg+='<rect x=\"'+(hoodStartX+hoodW*0.52)+'\" y=\"'+(hoodTop+2)+'\" width=\"'+(hoodW*0.48)+'\" height=\"14\" rx=\"2\" fill=\"rgba(24,95,165,0.1)\"\/>';\n    svg+='<text x=\"'+(hoodStartX+hoodW*0.76)+'\" y=\"'+(hoodTop+12)+'\" text-anchor=\"middle\" font-size=\"9\" fill=\"#185fa5\" font-weight=\"600\">Side B \\u2014 Slide<\/text>';\n  }\n\n  \/\/ Legend\n  var lx=W-PAD-140, ly=10, ls=14;\n  var legendItems=[\n    {c:C12,dash:false,label:'12\\u00d71 '+(d.isFS?'extinguishing':'detexline')},\n    {c:'#e24b4a',tri:true,label:'Nozzle '+d.nt+(d.mode==='appliance'?' (colour per appliance)':'')},\n    {c:CSPY,diamond:true,label:'SPY detector'},\n    {c:C8m,dash:true,label:'8\\u00d71 MR line'},\n  ];\n  if(d.isFS) legendItems.splice(1,0,{c:C8d,dash:true,label:'8\\u00d71 detection'});\n  if(d.nDucts>0) legendItems.push({c:'#e24b4a',dash:true,label:'Exhaust duct'});\n  legendItems.forEach(function(it,idx){\n    var ily=ly+idx*ls;\n    if(it.diamond) svg+='<polygon points=\"'+(lx+5)+','+ily+' '+(lx+10)+','+(ily+5)+' '+(lx+5)+','+(ily+10)+' '+lx+','+(ily+5)+'\" fill=\"'+it.c+'\"\/>';\n    else if(it.tri) svg+=drawNozzle(lx+6,ily,'',it.c);\n    else svg+='<line x1=\"'+lx+'\" y1=\"'+(ily+5)+'\" x2=\"'+(lx+12)+'\" y2=\"'+(ily+5)+'\" stroke=\"'+it.c+'\" stroke-width=\"2\"'+(it.dash?' stroke-dasharray=\"4,2\"':'')+'\/>';\n    svg+='<text x=\"'+(lx+16)+'\" y=\"'+(ily+9)+'\" font-size=\"9\" fill=\"'+CTXT+'\">'+it.label+'<\/text>';\n  });\n\n  svg+='<\/svg>';\n  document.getElementById('sys-diagram').innerHTML=svg;\n  generateTopDiagram(d);\n}\nfunction generateTopDiagram(d){\n  var APP_W={deepfryer_single:400,deepfryer_double:750,grill_plate:650,cooktop_small:600,cooktop_large:900,grill_025:500,grill_050:700,grill_075:900,wok_240:350,wok_300:400,wok_500:600};\n  var APP_LABEL={deepfryer_single:'Fryer',deepfryer_double:'Dbl Fryer',grill_plate:'Grill Plate',cooktop_small:'Cooktop S',cooktop_large:'Cooktop L',grill_025:'Grill 0.25',grill_050:'Grill 0.50',grill_075:'Grill 0.75',wok_240:'Wok 240',wok_300:'Wok 300',wok_500:'Wok 500'};\n  var APP_NOZ={deepfryer_single:1,deepfryer_double:2,grill_plate:1,cooktop_small:1,cooktop_large:2,grill_025:1,grill_050:2,grill_075:3,wok_240:1,wok_300:1,wok_500:1};\n  var APP_COLORS=['#c0392b','#7a3fb5','#b84900','#1a7a4a','#d4537e','#854f0b'];\n\n  var W=880, H=560;\n  var PAD=50;\n  var C12='#185fa5',C8d='#0f6e56',C8m='#E05A00',CNOZ='#E05A00',CSPY='#0f6e56',CGND='#8a8880',CTXT='#1e1e1d';\n  var isIsland=d.kt==='central';\n  var hoodX=PAD+80, hoodY=PAD+60;\n  var hoodW=W-PAD-hoodX-55;\n  var hoodD=isIsland?240:90;\n  var gap=36;\n  var contX=PAD+10, contY=hoodY+hoodD\/2-16;\n\n  var svg='<svg viewBox=\"0 0 '+W+' '+H+'\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:100%;max-width:'+W+'px;font-family:Arial,Helvetica,sans-serif;\">';\n  svg+='<defs>'\n    +'<marker id=\"tarr\" markerWidth=\"6\" markerHeight=\"6\" refX=\"5\" refY=\"3\" orient=\"auto\"><path d=\"M0,0 L0,6 L6,3 Z\" fill=\"'+CGND+'\"\/><\/marker>'\n    +'<marker id=\"tarr2\" markerWidth=\"6\" markerHeight=\"6\" refX=\"1\" refY=\"3\" orient=\"auto\"><path d=\"M6,0 L6,6 L0,3 Z\" fill=\"'+CGND+'\"\/><\/marker>'\n    +'<\/defs>';\n\n  \/\/ Hood outline\n  if(d.kt==='vceil'){\n    \/\/ Ventilated ceiling panel \u2014 shown as solid band\n    svg+='<rect x=\"'+hoodX+'\" y=\"'+hoodY+'\" width=\"'+hoodW+'\" height=\"'+hoodD+'\" fill=\"#d8e4f0\" stroke=\"#85b7eb\" stroke-width=\"1.5\"\/>';\n    for(var _px3=hoodX+16;_px3<hoodX+hoodW-8;_px3+=18){svg+='<circle cx=\"'+_px3+'\" cy=\"'+(hoodY+hoodD\/2)+'\" r=\"2\" fill=\"#85b7eb\" opacity=\"0.5\"\/>';}\n    svg+='<text x=\"'+(hoodX+6)+'\" y=\"'+(hoodY-4)+'\" font-size=\"9\" fill=\"#85b7eb\">Ventilated ceiling panel<\/text>';\n  } else if(isIsland){\n    svg+='<rect x=\"'+hoodX+'\" y=\"'+hoodY+'\" width=\"'+hoodW+'\" height=\"'+(hoodD\/2-gap)+'\" fill=\"rgba(24,95,165,0.04)\" stroke=\"#b5d4f4\" stroke-width=\"1.2\" stroke-dasharray=\"5,3\" rx=\"2\"\/>';\n    svg+='<text x=\"'+(hoodX+6)+'\" y=\"'+(hoodY+12)+'\" font-size=\"10\" fill=\"#85b7eb\" font-weight=\"500\">Side A<\/text>';\n    svg+='<rect x=\"'+hoodX+'\" y=\"'+(hoodY+hoodD\/2+gap)+'\" width=\"'+hoodW+'\" height=\"'+(hoodD\/2-gap)+'\" fill=\"rgba(24,95,165,0.04)\" stroke=\"#b5d4f4\" stroke-width=\"1.2\" stroke-dasharray=\"5,3\" rx=\"2\"\/>';\n    svg+='<text x=\"'+(hoodX+6)+'\" y=\"'+(hoodY+hoodD+14)+'\" font-size=\"10\" fill=\"#85b7eb\" font-weight=\"500\">Side B<\/text>';\n  } else {\n    svg+='<rect x=\"'+hoodX+'\" y=\"'+hoodY+'\" width=\"'+hoodW+'\" height=\"'+hoodD+'\" fill=\"rgba(24,95,165,0.04)\" stroke=\"#b5d4f4\" stroke-width=\"1.2\" stroke-dasharray=\"5,3\" rx=\"2\"\/>';\n    svg+='<text x=\"'+(hoodX+6)+'\" y=\"'+(hoodY+12)+'\" font-size=\"9\" fill=\"#85b7eb\">Hood<\/text>';\n  }\n\n  \/\/ Container\n  svg+='<rect x=\"'+(contX-14)+'\" y=\"'+contY+'\" width=\"28\" height=\"32\" rx=\"4\" fill=\"#2c2c2a\" stroke=\"#5a5956\" stroke-width=\"1.5\"\/>';\n  svg+='<text x=\"'+contX+'\" y=\"'+(contY+44)+'\" text-anchor=\"middle\" font-size=\"9\" fill=\"'+CTXT+'\">Container<\/text>';\n\n  var pipeY=hoodY+hoodD\/2;\n\n  \/\/ Container to hood entry\n  svg+='<line x1=\"'+(contX+14)+'\" y1=\"'+pipeY+'\" x2=\"'+hoodX+'\" y2=\"'+pipeY+'\" stroke=\"'+C12+'\" stroke-width=\"2\"\/>';\n\n  if(isIsland){\n    var bandH=hoodD\/2-gap;\n    var pipeYa=hoodY+bandH\/2;\n    var pipeYb=hoodY+hoodD\/2+gap+bandH\/2;\n\n    \/\/ T-piece and U-pipe\n    svg+='<circle cx=\"'+hoodX+'\" cy=\"'+pipeY+'\" r=\"4\" fill=\"#fff\" stroke=\"'+C12+'\" stroke-width=\"1.5\"\/>';\n    svg+='<line x1=\"'+hoodX+'\" y1=\"'+pipeYa+'\" x2=\"'+hoodX+'\" y2=\"'+pipeYb+'\" stroke=\"'+C12+'\" stroke-width=\"1.5\"\/>';\n    svg+='<line x1=\"'+hoodX+'\" y1=\"'+pipeYa+'\" x2=\"'+(hoodX+hoodW)+'\" y2=\"'+pipeYa+'\" stroke=\"'+C12+'\" stroke-width=\"2\"\/>';\n    svg+='<line x1=\"'+hoodX+'\" y1=\"'+pipeYb+'\" x2=\"'+(hoodX+hoodW)+'\" y2=\"'+pipeYb+'\" stroke=\"'+C12+'\" stroke-width=\"2\"\/>';\n    \/\/ End caps (tamp\u00f5es) \u2014 small perpendicular lines at the end of each side\n    svg+='<line x1=\"'+(hoodX+hoodW)+'\" y1=\"'+(pipeYa-4)+'\" x2=\"'+(hoodX+hoodW)+'\" y2=\"'+(pipeYa+4)+'\" stroke=\"'+C12+'\" stroke-width=\"2\"\/>';\n    svg+='<line x1=\"'+(hoodX+hoodW)+'\" y1=\"'+(pipeYb-4)+'\" x2=\"'+(hoodX+hoodW)+'\" y2=\"'+(pipeYb+4)+'\" stroke=\"'+C12+'\" stroke-width=\"2\"\/>';\n    svg+='<text x=\"'+(hoodX+hoodW*0.35)+'\" y=\"'+(pipeYa-20)+'\" font-size=\"9\" fill=\"'+C12+'\">12\\u00d71 '+(d.isFS?'extinguishing':'detexline')+' ('+Math.ceil(d.extLen)+' m)<\/text>';\n    \/\/ Side A \/ Plenum A \/ Side B \/ Plenum B labels\n    svg+='<text x=\"'+(hoodX-4)+'\" y=\"'+(hoodY+bandH\/2+4)+'\" text-anchor=\"end\" font-size=\"9\" fill=\"#E05A00\" font-weight=\"600\">Side A<\/text>';\n    svg+='<text x=\"'+(hoodX-4)+'\" y=\"'+(hoodY+bandH\/2+14)+'\" text-anchor=\"end\" font-size=\"8\" fill=\"#8a8880\">Slide<\/text>';\n    svg+='<text x=\"'+(hoodX-4)+'\" y=\"'+(hoodY-4)+'\" text-anchor=\"end\" font-size=\"8\" fill=\"#0f6e56\">Plenum A\/B<\/text>';\n    svg+='<text x=\"'+(hoodX-4)+'\" y=\"'+(hoodY+hoodD\/2+gap+bandH\/2+4)+'\" text-anchor=\"end\" font-size=\"9\" fill=\"#185fa5\" font-weight=\"600\">Side B<\/text>';\n    svg+='<text x=\"'+(hoodX-4)+'\" y=\"'+(hoodY+hoodD\/2+gap+bandH\/2+14)+'\" text-anchor=\"end\" font-size=\"8\" fill=\"#8a8880\">Slide<\/text>';\n\n    \/\/ Pre-compute SPY Y position and orientation\n  \/\/ detexline: diamond sits ABOVE pipe, bottom vertex touching pipeY\n  \/\/ firespy: diamond hangs BELOW detection line, top vertex touching detY\n  var spyY=pipeY; \/\/ default\n  var spyAbove=true; \/\/ detexline: diamond above pipe\n  if(d.isFS){\n    var _dBoxH=48,_dBoxOffset=22;\n    spyY=pipeY-_dBoxOffset-_dBoxH\/2; \/\/ detY = mid of duct box\n    spyAbove=false; \/\/ firespy: diamond hangs below detection line\n  }\n\n  if(d.mode==='appliance' && d.apps && d.apps.length>0){\n      \/\/ Build flat list of all units\n      var units=[];\n      d.apps.forEach(function(a,ai){\n        for(var q=0;q<a.qty;q++) units.push({type:a.type,color:APP_COLORS[ai%APP_COLORS.length]});\n      });\n\n      var mmScale=hoodW\/d.hoodLenMm;\n\n      \/\/ Distribute sequentially: fill Side A first, then Side B\n      var sideAW=hoodW, sideBW=hoodW;\n      var curXA=hoodX, curXB=hoodX;\n      var usedA=0;\n\n      units.forEach(function(u){\n        var aw=Math.round((APP_W[u.type]||500)*mmScale);\n        var noz=APP_NOZ[u.type]||1;\n        var label=APP_LABEL[u.type]||u.type;\n        var onA=(usedA+aw)<=sideAW;\n        var curX=onA?curXA:curXB;\n        var bandY=onA?hoodY:(hoodY+hoodD\/2+gap);\n        var pipeLineY=onA?pipeYa:pipeYb;\n\n        \/\/ Appliance rect\n        svg+='<rect x=\"'+(curX+1)+'\" y=\"'+(bandY+1)+'\" width=\"'+(aw-3)+'\" height=\"'+(bandH-2)+'\" rx=\"2\" fill=\"'+u.color+'\" opacity=\"0.15\" stroke=\"'+u.color+'\" stroke-width=\"1\"\/>';\n        \/\/ Appliance name at top of band\n        svg+='<text x=\"'+(curX+aw\/2)+'\" y=\"'+(bandY+11)+'\" text-anchor=\"middle\" font-size=\"8\" fill=\"'+u.color+'\">'+label+'<\/text>';\n        \/\/ Dimension at bottom of band\n        svg+='<text x=\"'+(curX+aw\/2)+'\" y=\"'+(bandY+bandH-5)+'\" text-anchor=\"middle\" font-size=\"7\" fill=\"'+u.color+'\">'+(APP_W[u.type]||500)+'mm<\/text>';\n        \/\/ Nozzles: triangle in middle zone (below name, above dimension)\n        for(var ni=0;ni<noz;ni++){\n          var nx=curX+(ni+1)*aw\/(noz+1);\n          svg+=drawNozzle(nx, pipeLineY-8, 'P2', u.color);\n        }\n        if(onA){ curXA+=aw; usedA+=aw; }\n        else curXB+=aw;\n      });\n\n    } else {\n      \/\/ Area protection \u2014 uniform nozzles both sides\n      var cookPerSide=Math.round(d.cookNoz\/2);\n      var nozSp=hoodW\/(cookPerSide+1);\n      for(var i=1;i<=cookPerSide;i++){\n        var nx=hoodX+i*nozSp;\n        svg+=drawNozzle(nx, pipeYa-8, d.nt, CNOZ);\n        svg+=drawNozzle(nx, pipeYb-8, d.nt, CNOZ);\n      }\n    }\n\n    \/\/ SPY detectors (one side only \u2014 centre)\n    var _spyOnPipeI=Math.max(0,d.spyCount-d.nDucts);\n    var spySp=hoodW\/(_spyOnPipeI+1);\n    var spyLineTop=d.isFS?(pipeY+8):pipeYa;\n    for(var s=1;s<=_spyOnPipeI;s++){\n      var sx=hoodX+s*spySp;\n      svg+='<polygon points=\"'+sx+','+spyLineTop+' '+(sx+5)+','+(spyLineTop+5)+' '+sx+','+(spyLineTop+10)+' '+(sx-5)+','+(spyLineTop+5)+'\" fill=\"'+CSPY+'\"\/>';\n    }\n\n  } else {\n    \/\/ \u2500\u2500 WALL HOOD \u2500\u2500\n    svg+='<line x1=\"'+hoodX+'\" y1=\"'+pipeY+'\" x2=\"'+(hoodX+hoodW)+'\" y2=\"'+pipeY+'\" stroke=\"'+C12+'\" stroke-width=\"2\"\/>';\n    svg+='<text x=\"'+(hoodX+hoodW*0.35)+'\" y=\"'+(pipeY-20)+'\" font-size=\"9\" fill=\"'+C12+'\">12\\u00d71 '+(d.isFS?'extinguishing':'detexline')+' ('+Math.ceil(d.extLen)+' m)<\/text>';\n\n    \/\/ Pre-compute SPY Y position and orientation\n  \/\/ detexline: diamond sits ABOVE pipe, bottom vertex touching pipeY\n  \/\/ firespy: diamond hangs BELOW detection line, top vertex touching detY\n  var spyY=pipeY; \/\/ default\n  var spyAbove=true; \/\/ detexline: diamond above pipe\n  if(d.isFS){\n    var _dBoxH=48,_dBoxOffset=22;\n    spyY=pipeY-_dBoxOffset-_dBoxH\/2; \/\/ detY = mid of duct box\n    spyAbove=false; \/\/ firespy: diamond hangs below detection line\n  }\n\n  if(d.mode==='appliance' && d.apps && d.apps.length>0){\n      var mmScale=hoodW\/d.hoodLenMm;\n      var curX=hoodX;\n      d.apps.forEach(function(a,ai){\n        var aw=Math.round((APP_W[a.type]||500)*mmScale);\n        var noz=APP_NOZ[a.type]||1;\n        var color=APP_COLORS[ai%APP_COLORS.length];\n        var label=APP_LABEL[a.type]||a.type;\n        for(var qi=0;qi<a.qty;qi++){\n          \/\/ Appliance rect\n          svg+='<rect x=\"'+(curX+1)+'\" y=\"'+(hoodY+1)+'\" width=\"'+(aw-3)+'\" height=\"'+(hoodD-2)+'\" rx=\"2\" fill=\"'+color+'\" opacity=\"0.15\" stroke=\"'+color+'\" stroke-width=\"1\"\/>';\n          \/\/ Appliance name at top\n          svg+='<text x=\"'+(curX+aw\/2)+'\" y=\"'+(hoodY+11)+'\" text-anchor=\"middle\" font-size=\"8\" fill=\"'+color+'\">'+label+'<\/text>';\n          \/\/ Dimension at bottom\n          svg+='<text x=\"'+(curX+aw\/2)+'\" y=\"'+(hoodY+hoodD-5)+'\" text-anchor=\"middle\" font-size=\"7\" fill=\"'+color+'\">'+(APP_W[a.type]||500)+'mm<\/text>';\n          \/\/ Nozzle triangle in middle\n          for(var ni=0;ni<noz;ni++){\n            var nx=curX+(ni+1)*aw\/(noz+1);\n            svg+=drawNozzle(nx, pipeY-8, 'P2', color);\n          }\n          curX+=aw;\n        }\n      });\n    } else {\n      var cookNozW=d.cookNoz+d.gcNoz+d.sepNoz;\n      var nozSp=hoodW\/(cookNozW+1);\n      for(var i=1;i<=cookNozW;i++){\n        var nx=hoodX+i*nozSp;\n        svg+=drawNozzle(nx, pipeY-8, d.nt, CNOZ);\n      }\n    }\n    var _spyOnPipeW=Math.max(0,d.spyCount-d.nDucts);\n    var spySp=hoodW\/(_spyOnPipeW+1);\n    var spyTopY=d.isFS?(pipeY-18):pipeY;\n    for(var s=1;s<=_spyOnPipeW;s++){\n      var sx=hoodX+s*spySp;\n      svg+='<polygon points=\"'+sx+','+spyTopY+' '+(sx+5)+','+(spyTopY+5)+' '+sx+','+(spyTopY+10)+' '+(sx-5)+','+(spyTopY+5)+'\" fill=\"'+CSPY+'\"\/>';\n    }\n  }\n\n  \/\/ Detection line firespy \u2014 shown in both wall and island modes\n  if(d.isFS){\n    if(isIsland){\n      \/\/ Detection line runs in the central gap between Side A and Side B (inside hood, hidden by filters)\n      \/\/ pipeY = hoodY + hoodD\/2 = geometric centre of the island = the gap zone\n      \/\/ Line exits container slightly below the blue extinguishing line\n      var detYmid=pipeY+8; \/\/ slightly below the blue entry line (pipeY)\n      svg+='<line x1=\"'+(contX+14)+'\" y1=\"'+detYmid+'\" x2=\"'+(hoodX+hoodW)+'\" y2=\"'+detYmid+'\" stroke=\"'+C8d+'\" stroke-width=\"1.5\" stroke-dasharray=\"5,3\"\/>';\n      svg+='<text x=\"'+(hoodX+hoodW*0.35)+'\" y=\"'+(detYmid+14)+'\" font-size=\"9\" fill=\"'+C8d+'\">8\\u00d71 detection ('+Math.ceil(d.detLen)+' m)<\/text>';\n    } else {\n      svg+='<line x1=\"'+(contX+14)+'\" y1=\"'+(pipeY-18)+'\" x2=\"'+(hoodX+hoodW*0.6)+'\" y2=\"'+(pipeY-18)+'\" stroke=\"'+C8d+'\" stroke-width=\"1.5\" stroke-dasharray=\"5,3\"\/>';\n      svg+='<text x=\"'+(hoodX+hoodW*0.1)+'\" y=\"'+(pipeY-34)+'\" font-size=\"9\" fill=\"'+C8d+'\">8\\u00d71 detection ('+Math.ceil(d.detLen)+' m)<\/text>';\n    }\n  }\n\n  \/\/ MR branch\n  var mrBX=contX+14+20, mrEY=H-28;\n  svg+='<line x1=\"'+mrBX+'\" y1=\"'+pipeY+'\" x2=\"'+mrBX+'\" y2=\"'+mrEY+'\" stroke=\"'+C8m+'\" stroke-width=\"1.5\" stroke-dasharray=\"3,3\" opacity=\"0.8\"\/>';\n  svg+='<rect x=\"'+(mrBX-9)+'\" y=\"'+(mrEY-9)+'\" width=\"18\" height=\"18\" rx=\"3\" fill=\"#fff3ec\" stroke=\"'+C8m+'\" stroke-width=\"1.5\"\/>';\n  svg+='<circle cx=\"'+mrBX+'\" cy=\"'+mrEY+'\" r=\"5\" fill=\"'+C8m+'\"\/>';\n  svg+='<text x=\"'+(mrBX+14)+'\" y=\"'+(mrEY+4)+'\" font-size=\"9\" fill=\"'+C8m+'\">MR ('+Math.ceil(d.mrLen)+' m)<\/text>';\n\n  \/\/ Exhaust ducts\n  if(d.nDucts>0){\n    var ductArr2=d.ducts||[];\n    var ductSp=hoodW\/(d.nDucts+1);\n    var boxW=Math.min(52,Math.max(28,(ductSp*0.7)));\n    var boxH=46;\n    \/\/ Detection line Y for firespy green branch (top view)\n    var detYtop=d.isFS?(pipeY+8):null;\n    for(var dd=1;dd<=d.nDucts;dd++){\n      var dx=hoodX+dd*ductSp;\n      var ductEY=hoodY+hoodD+12;\n      var ductObj2=ductArr2[dd-1]||null;\n      var nozInDuct2=2;\n      if(ductObj2){ var r3=ductRes(ductObj2); nozInDuct2=r3.nozzles; }\n      \/\/ Red: 12\u00d71 \u2192 nozzles\n      svg+='<line x1=\"'+dx+'\" y1=\"'+(hoodY+hoodD)+'\" x2=\"'+dx+'\" y2=\"'+ductEY+'\" stroke=\"#e24b4a\" stroke-width=\"1.5\" stroke-dasharray=\"4,3\"\/>';\n      \/\/ Green (firespy only): 8\u00d71 detection \u2192 SPY\n      if(d.isFS && detYtop!==null){\n        var greenXt=dx+5;\n        svg+='<line x1=\"'+greenXt+'\" y1=\"'+detYtop+'\" x2=\"'+greenXt+'\" y2=\"'+(ductEY+8)+'\" stroke=\"#0f6e56\" stroke-width=\"1.2\" stroke-dasharray=\"4,3\" opacity=\"0.9\"\/>';\n      }\n      svg+='<rect x=\"'+(dx-boxW\/2)+'\" y=\"'+ductEY+'\" width=\"'+boxW+'\" height=\"'+boxH+'\" rx=\"3\" fill=\"#fff5f5\" stroke=\"#e24b4a\" stroke-width=\"1\"\/>';\n      svg+='<text x=\"'+dx+'\" y=\"'+(ductEY+boxH+11)+'\" text-anchor=\"middle\" font-size=\"8\" fill=\"#e24b4a\">Duct '+dd+' ('+nozInDuct2+'\u00d7P2)<\/text>';\n      for(var ni3=0;ni3<nozInDuct2;ni3++){\n        var nxd2=(dx-boxW\/2)+(ni3+1)*boxW\/(nozInDuct2+1);\n        svg+=drawNozzle(nxd2,ductEY+2,'','#e24b4a'); \/\/ no label inside box\n      }\n      \/\/ SPY at bottom of box \u2014 clear of nozzles\n      var spyXt=dx,spyYt=ductEY+boxH-16;\n      svg+='<polygon points=\"'+spyXt+','+spyYt+' '+(spyXt+5)+','+(spyYt+5)+' '+spyXt+','+(spyYt+10)+' '+(spyXt-5)+','+(spyYt+5)+'\" fill=\"#0f6e56\"\/>';\n\n    }\n  }\n\n  \/\/ Hood length dimension\n  var dimY=hoodY-16;\n  svg+='<line x1=\"'+hoodX+'\" y1=\"'+dimY+'\" x2=\"'+(hoodX+hoodW)+'\" y2=\"'+dimY+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\" marker-end=\"url(#tarr)\"\/>';\n  svg+='<line x1=\"'+(hoodX+hoodW)+'\" y1=\"'+dimY+'\" x2=\"'+hoodX+'\" y2=\"'+dimY+'\" stroke=\"'+CGND+'\" stroke-width=\"0.8\" marker-end=\"url(#tarr2)\"\/>';\n  svg+='<text x=\"'+(hoodX+hoodW\/2)+'\" y=\"'+(dimY-4)+'\" text-anchor=\"middle\" font-size=\"9\" fill=\"'+CGND+'\">'+(d.hoodLenMm\/1000).toFixed(1)+' m hood length<\/text>';\n\n  \/\/ Legend\n  var lx=14, ly=8, ls=14;\n  var litems=[\n    {c:C12,dash:false,label:'12\\u00d71 '+(d.isFS?'extinguishing':'detexline')},\n    {c:'#e24b4a',tri:true,label:'Nozzle '+d.nt+(d.mode==='appliance'?' (colour per appliance)':'')},\n    {c:CSPY,diamond:true,label:'SPY detector'},\n    {c:C8m,dash:true,label:'8\\u00d71 MR line'},\n  ];\n  if(d.isFS) litems.splice(1,0,{c:C8d,dash:true,label:'8\\u00d71 detection'});\n  if(d.nDucts>0) litems.push({c:'#e24b4a',dash:true,label:'Exhaust duct'});\n  litems.forEach(function(it,idx){\n    var ily=ly+idx*ls;\n    if(it.diamond) svg+='<polygon points=\"'+(lx+5)+','+ily+' '+(lx+10)+','+(ily+5)+' '+(lx+5)+','+(ily+10)+' '+lx+','+(ily+5)+'\" fill=\"'+it.c+'\"\/>';\n    else if(it.tri) svg+=drawNozzle(lx+6,ily,'',it.c);\n    else svg+='<line x1=\"'+lx+'\" y1=\"'+(ily+5)+'\" x2=\"'+(lx+12)+'\" y2=\"'+(ily+5)+'\" stroke=\"'+it.c+'\" stroke-width=\"2\"'+(it.dash?' stroke-dasharray=\"4,2\"':'')+'\/>';\n    svg+='<text x=\"'+(lx+16)+'\" y=\"'+(ily+9)+'\" font-size=\"9\" fill=\"'+CTXT+'\">'+it.label+'<\/text>';\n  });\n\n  svg+='<\/svg>';\n  document.getElementById('sys-diagram-top').innerHTML=svg;\n}\n\n\nfunction printReport(){\n  var projName=document.getElementById('proj-name').value||'\u2014';\n  var projCompany=document.getElementById('proj-company').value||'\u2014';\n  var projDesigner=document.getElementById('proj-designer').value||'\u2014';\n  var projEmail=document.getElementById('proj-email').value||'\u2014';\n  var validDays=parseInt(document.getElementById('proj-validity').value)||60;\n  var validDate='';\n  if(validDays>0){\n    var vd=new Date(); vd.setDate(vd.getDate()+validDays);\n    validDate=vd.toLocaleDateString('en-GB',{day:'2-digit',month:'long',year:'numeric'});\n  }\n  var bomRows=document.getElementById('bom-body').innerHTML;\n  var resTitle=document.getElementById('res-title').textContent;\n  var resSub=document.getElementById('res-sub').textContent;\n  var diagramElev=document.getElementById('sys-diagram').innerHTML;\n  var diagramTop=document.getElementById('sys-diagram-top').innerHTML;\n  var date=new Date().toLocaleDateString('en-GB',{day:'2-digit',month:'long',year:'numeric'});\n  var inputRows=document.getElementById('print-inputs-box').querySelectorAll('tr');\n\n  var css='*{box-sizing:border-box;margin:0;padding:0;}'\n    +'body{font-family:Arial,Helvetica,sans-serif;font-size:12px;color:#000;background:#fff;padding:24px 28px;}'\n    +'h1{font-size:16px;font-weight:700;margin-bottom:2px;color:#000;}'\n    +'h2{font-size:11px;font-weight:700;margin:16px 0 5px;border-bottom:1.5px solid #000;padding-bottom:3px;text-transform:uppercase;letter-spacing:.5px;color:#000;}'\n    +'.sub{font-size:10px;color:#555;margin-bottom:14px;display:block;}'\n    +'table{width:100%;border-collapse:collapse;margin-bottom:12px;font-size:11px;}'\n    +'th{text-align:left;padding:5px 8px;background:#000;color:#fff;font-size:10px;text-transform:uppercase;letter-spacing:.4px;}'\n    +'td{padding:5px 8px;border-bottom:.5px solid #ddd;vertical-align:top;color:#000;}'\n    +'tr:nth-child(even) td{background:#f9f9f9;}'\n    +'.sec td{background:#1a1919!important;color:#E05A00!important;font-weight:700;font-size:10px;text-transform:uppercase;letter-spacing:1px;padding:6px 10px;border-left:3px solid #E05A00;}'\n    +'.qty{text-align:center;font-weight:700;width:8%;}'\n    +'.ref{font-family:monospace;font-size:10px;color:#444;width:14%;}'\n    +'.result-box{border:1.5px solid #000;padding:10px 14px;margin-bottom:14px;}'\n    +'.result-title{font-size:13px;font-weight:700;margin-bottom:3px;color:#000;}'\n    +'.result-sub{font-size:11px;color:#333;}'\n    +'.diag-box{border:1px solid #ccc;padding:10px;margin-bottom:14px;}'\n    +'.footer{margin-top:16px;font-size:9px;color:#888;border-top:.5px solid #ccc;padding-top:6px;}'\n    +'@media print{body{padding:8px 12px;}h2{page-break-after:avoid;}.diag-box{page-break-inside:avoid;}}';\n\n  var html='<!DOCTYPE html><html><head><meta charset=\"UTF-8\">'\n    +'<title>firespy PHANTOM\\u00ae \\u2014 System Report<\/title>'\n    +'<style>'+css+'<\/style>'\n    +'<\/head><body data-rsssl=1>'\n    +'<h1>protecfire \\u2014 firespy PHANTOM\\u00ae<\/h1>'\n    +'<span class=\"sub\">System Design Report \\u00b7 DIN EN 17446:2021 \\u00b7 Document 1001601 v03 \\u00b7 Generated: '+date+'<\/span>'\n    +'<table style=\"width:100%;border-collapse:collapse;font-size:11px;margin-bottom:14px;border:1px solid #ddd;\">'\n    +'<tr><td style=\"padding:5px 10px;width:30%;color:#555;border-bottom:.5px solid #eee;\">Project<\/td><td style=\"padding:5px 10px;font-weight:600;border-bottom:.5px solid #eee;\">'+projName+'<\/td><td style=\"padding:5px 10px;width:30%;color:#555;border-bottom:.5px solid #eee;\">Company<\/td><td style=\"padding:5px 10px;font-weight:600;border-bottom:.5px solid #eee;\">'+projCompany+'<\/td><\/tr>'\n    +'<tr><td style=\"padding:5px 10px;color:#555;border-bottom:.5px solid #eee;\">Designer<\/td><td style=\"padding:5px 10px;font-weight:600;border-bottom:.5px solid #eee;\">'+projDesigner+'<\/td><td style=\"padding:5px 10px;color:#555;border-bottom:.5px solid #eee;\">Email<\/td><td style=\"padding:5px 10px;font-weight:600;border-bottom:.5px solid #eee;\">'+projEmail+'<\/td><\/tr>'\n    +(validDate?'<tr><td style=\"padding:5px 10px;color:#555;\">Valid until<\/td><td colspan=\"3\" style=\"padding:5px 10px;font-weight:700;color:#b84900;\">'+validDate+' ('+validDays+' days from issue)<\/td><\/tr>':'<tr><td style=\"padding:5px 10px;color:#555;\">Validity<\/td><td colspan=\"3\" style=\"padding:5px 10px;font-weight:600;color:#555;\">No expiry stated<\/td><\/tr>')\n    +'<\/table>'\n    +'<div class=\"result-box\"><div class=\"result-title\">'+resTitle+'<\/div><div class=\"result-sub\">'+resSub+'<\/div><\/div>';\n\n  \/\/ Build 2-column design inputs table\n  var inputPairs=[];\n  inputRows.forEach(function(r){\n    var cells=r.querySelectorAll('td');\n    if(cells.length>=2) inputPairs.push([cells[0].textContent, cells[1].textContent]);\n  });\n  html+='<h2>Design inputs<\/h2>'\n    +'<table style=\"font-size:10px;\"><tbody>';\n  for(var pi=0;pi<inputPairs.length;pi+=2){\n    var a=inputPairs[pi], b=inputPairs[pi+1]||null;\n    html+='<tr>'\n      +'<td style=\"width:22%;color:#555;padding:3px 6px;border-bottom:.5px solid #eee;\">'+a[0]+'<\/td>'\n      +'<td style=\"width:28%;font-weight:600;padding:3px 6px;border-bottom:.5px solid #eee;border-right:1px solid #eee;\">'+a[1]+'<\/td>'\n      +(b\n        ?'<td style=\"width:22%;color:#555;padding:3px 6px 3px 12px;border-bottom:.5px solid #eee;\">'+b[0]+'<\/td>'\n         +'<td style=\"width:28%;font-weight:600;padding:3px 6px;border-bottom:.5px solid #eee;\">'+b[1]+'<\/td>'\n        :'<td colspan=\"2\" style=\"border-bottom:.5px solid #eee;\"><\/td>')\n      +'<\/tr>';\n  }\n  html+='<\/tbody><\/table>';\n\n  html+='<h2>Bill of materials<\/h2>'\n    +'<table><thead><tr><th style=\"width:30%;\">Item<\/th><th>Description<\/th><th class=\"qty\">Qty<\/th><th class=\"ref\">Ref.<\/th><\/tr><\/thead>'\n    +'<tbody>'+bomRows+'<\/tbody><\/table>';\n\n  var mods=window._lastModifications||[];\n  if(mods.length){\n    html+='<div style=\"background:#fff8f0;border:1.5px solid #f0a070;border-radius:6px;padding:10px 14px;margin-bottom:14px;\">'\n      +'<div style=\"font-size:11px;font-weight:700;color:#b84900;margin-bottom:4px;\">\\u26a0 Recommended values were modified by the user<\/div>'\n      +'<p style=\"font-size:10px;color:#7a3a00;margin-bottom:4px;\">The following values differ from the automatically calculated recommendations:<\/p>'\n      +'<ul>'+mods.map(function(m){return '<li style=\"font-size:10px;color:#7a3a00;margin-left:14px;margin-bottom:2px;\">'+m+'<\/li>';}).join('')+'<\/ul>'\n      +'<\/div>';\n  }\n\n  html+='<h2>System diagram \\u2014 elevation view<\/h2>'\n    +'<div class=\"diag-box\">'+diagramElev+'<\/div>';\n  html+='<h2>System diagram \\u2014 top view \/ plan<\/h2>'\n    +'<div class=\"diag-box\">'+diagramTop+'<\/div>';\n\n  html+='<div class=\"footer\">Generated automatically from firespy PHANTOM\\u00ae design guidelines and DIN EN 17446:2021. '\n    +'Final design must be validated by a certified protecfire installer. '\n    +'protecfire GmbH \\u00b7 Weidekamp 10 \\u00b7 23558 L\\u00fcbeck \\u00b7 Germany \\u00b7 www.protecfire.de<\/div>'\n    +'<scr'+'ipt>window.onload=function(){window.print();}<\\\/scr'+'ipt>'\n    +'<\/body><\/html>';\n\n  var w=window.open('','_blank','width=920,height=780');\n  if(w){w.document.write(html);w.document.close();}\n  else{alert('Pop-up blocked. Please allow pop-ups for this site and try again.');}\n}\n\ndocument.getElementById('has-sep').addEventListener('change',updateSep);\ndocument.getElementById('n-ducts').addEventListener('change',renderDucts);\ndocument.getElementById('hood-length').addEventListener('input',function(){\n  var L=parseInt(this.value)||2200;\n  gcData.forEach(function(g){ g.length=L; });\n  renderGC();autoCalc();updateSpyFields();\n});\n\/\/ vc-depth also triggers autoCalc\ndocument.addEventListener('input', function(e){\n  if(e.target && e.target.id==='vc-depth') autoCalc();\n});\naddApp(); renderGC(); onHeight(); autoCalc();\nrenderDucts(); updateSpyFields(); setInst('surface');\n<\/script>\n<\/body>\n<\/html>\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>","protected":false},"excerpt":{"rendered":"<p>POWERFUL SYSTEM, POWERFUL INTEGRATION! firespy PHANTOM\u00ae \u2014 System Calculator protecfire \u2014 firespy PHANTOM\u00ae System Design Calculator \u00b7 DIN EN 17446:2021 v9.3 Project information Project details Project name Company \/ design firm Designer name Email Report validity (days) 30 days60 days90 days180 days1 yearNo expiry Validity period shown on the PDF report Step 1 \u2014 Kitchen [&hellip;]<\/p>","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_header_footer","meta":{"footnotes":""},"class_list":["post-24854","page","type-page","status-publish","hentry"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.2 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>firespy Phantom calculator - protecfire<\/title>\n<meta name=\"description\" content=\"firespy Phantom calculator for kitchen fire protection. Accordingly to DIN EN 17446. Certified fire suppression system for kitchen hoods and ventilated ceilings.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.protecfire.de\/ru\/phantom-calculator\/\" \/>\n<meta property=\"og:locale\" content=\"ru_RU\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"firespy Phantom calculator - protecfire\" \/>\n<meta property=\"og:description\" content=\"firespy Phantom calculator for kitchen fire protection. Accordingly to DIN EN 17446. Certified fire suppression system for kitchen hoods and ventilated ceilings.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.protecfire.de\/ru\/phantom-calculator\/\" \/>\n<meta property=\"og:site_name\" content=\"protecfire\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/protecfire.de\" \/>\n<meta property=\"article:modified_time\" content=\"2026-04-13T09:12:58+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.protecfire.de\/wp-content\/uploads\/2022\/10\/all-tanks-protecfire-vehicles.332.png\" \/>\n\t<meta property=\"og:image:width\" content=\"2432\" \/>\n\t<meta property=\"og:image:height\" content=\"1164\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:site\" content=\"@protecfire1\" \/>\n<meta name=\"twitter:label1\" content=\"\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f\" \/>\n\t<meta name=\"twitter:data1\" content=\"6 \u043c\u0438\u043d\u0443\u0442\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.protecfire.de\/phantom-calculator\/\",\"url\":\"https:\/\/www.protecfire.de\/phantom-calculator\/\",\"name\":\"firespy Phantom calculator - protecfire\",\"isPartOf\":{\"@id\":\"https:\/\/www.protecfire.de\/sv\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.protecfire.de\/phantom-calculator\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.protecfire.de\/phantom-calculator\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.protecfire.de\/wp-content\/uploads\/2026\/02\/Phantom-fp-horiz-white.svg\",\"datePublished\":\"2026-04-09T21:58:11+00:00\",\"dateModified\":\"2026-04-13T09:12:58+00:00\",\"description\":\"firespy Phantom calculator for kitchen fire protection. Accordingly to DIN EN 17446. Certified fire suppression system for kitchen hoods and ventilated ceilings.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.protecfire.de\/phantom-calculator\/#breadcrumb\"},\"inLanguage\":\"ru-RU\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.protecfire.de\/phantom-calculator\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"ru-RU\",\"@id\":\"https:\/\/www.protecfire.de\/phantom-calculator\/#primaryimage\",\"url\":\"https:\/\/www.protecfire.de\/wp-content\/uploads\/2026\/02\/Phantom-fp-horiz-white.svg\",\"contentUrl\":\"https:\/\/www.protecfire.de\/wp-content\/uploads\/2026\/02\/Phantom-fp-horiz-white.svg\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.protecfire.de\/phantom-calculator\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.protecfire.de\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"firespy Phantom calculator\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.protecfire.de\/sv\/#website\",\"url\":\"https:\/\/www.protecfire.de\/sv\/\",\"name\":\"protecfire\",\"description\":\"Automatic Fire Suppression Systems\",\"publisher\":{\"@id\":\"https:\/\/www.protecfire.de\/sv\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.protecfire.de\/sv\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"ru-RU\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.protecfire.de\/sv\/#organization\",\"name\":\"protecfire\",\"url\":\"https:\/\/www.protecfire.de\/sv\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"ru-RU\",\"@id\":\"https:\/\/www.protecfire.de\/sv\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.protecfire.de\/wp-content\/uploads\/2023\/01\/protecfire-logo-original-1920x462-1.svg\",\"contentUrl\":\"https:\/\/www.protecfire.de\/wp-content\/uploads\/2023\/01\/protecfire-logo-original-1920x462-1.svg\",\"width\":1920,\"height\":462,\"caption\":\"protecfire\"},\"image\":{\"@id\":\"https:\/\/www.protecfire.de\/sv\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/protecfire.de\",\"https:\/\/x.com\/protecfire1\",\"https:\/\/www.instagram.com\/protecfire.de\/\",\"https:\/\/www.linkedin.com\/company\/protecfire-gmbh\/\",\"https:\/\/www.youtube.com\/channel\/UCPNT-TI0OtsFsXkjCp2sKvg\"]}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"firespy Phantom calculator - protecfire","description":"firespy Phantom calculator for kitchen fire protection. Accordingly to DIN EN 17446. Certified fire suppression system for kitchen hoods and ventilated ceilings.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.protecfire.de\/ru\/phantom-calculator\/","og_locale":"ru_RU","og_type":"article","og_title":"firespy Phantom calculator - protecfire","og_description":"firespy Phantom calculator for kitchen fire protection. Accordingly to DIN EN 17446. Certified fire suppression system for kitchen hoods and ventilated ceilings.","og_url":"https:\/\/www.protecfire.de\/ru\/phantom-calculator\/","og_site_name":"protecfire","article_publisher":"https:\/\/www.facebook.com\/protecfire.de","article_modified_time":"2026-04-13T09:12:58+00:00","og_image":[{"width":2432,"height":1164,"url":"https:\/\/www.protecfire.de\/wp-content\/uploads\/2022\/10\/all-tanks-protecfire-vehicles.332.png","type":"image\/png"}],"twitter_card":"summary_large_image","twitter_site":"@protecfire1","twitter_misc":{"\u041f\u0440\u0438\u043c\u0435\u0440\u043d\u043e\u0435 \u0432\u0440\u0435\u043c\u044f \u0434\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f":"6 \u043c\u0438\u043d\u0443\u0442"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/www.protecfire.de\/phantom-calculator\/","url":"https:\/\/www.protecfire.de\/phantom-calculator\/","name":"firespy Phantom calculator - protecfire","isPartOf":{"@id":"https:\/\/www.protecfire.de\/sv\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.protecfire.de\/phantom-calculator\/#primaryimage"},"image":{"@id":"https:\/\/www.protecfire.de\/phantom-calculator\/#primaryimage"},"thumbnailUrl":"https:\/\/www.protecfire.de\/wp-content\/uploads\/2026\/02\/Phantom-fp-horiz-white.svg","datePublished":"2026-04-09T21:58:11+00:00","dateModified":"2026-04-13T09:12:58+00:00","description":"firespy Phantom calculator for kitchen fire protection. Accordingly to DIN EN 17446. Certified fire suppression system for kitchen hoods and ventilated ceilings.","breadcrumb":{"@id":"https:\/\/www.protecfire.de\/phantom-calculator\/#breadcrumb"},"inLanguage":"ru-RU","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.protecfire.de\/phantom-calculator\/"]}]},{"@type":"ImageObject","inLanguage":"ru-RU","@id":"https:\/\/www.protecfire.de\/phantom-calculator\/#primaryimage","url":"https:\/\/www.protecfire.de\/wp-content\/uploads\/2026\/02\/Phantom-fp-horiz-white.svg","contentUrl":"https:\/\/www.protecfire.de\/wp-content\/uploads\/2026\/02\/Phantom-fp-horiz-white.svg"},{"@type":"BreadcrumbList","@id":"https:\/\/www.protecfire.de\/phantom-calculator\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.protecfire.de\/"},{"@type":"ListItem","position":2,"name":"firespy Phantom calculator"}]},{"@type":"WebSite","@id":"https:\/\/www.protecfire.de\/sv\/#website","url":"https:\/\/www.protecfire.de\/sv\/","name":"Protecfire","description":"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0441\u0438\u0441\u0442\u0435\u043c\u044b \u043f\u043e\u0436\u0430\u0440\u043e\u0442\u0443\u0448\u0435\u043d\u0438\u044f","publisher":{"@id":"https:\/\/www.protecfire.de\/sv\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.protecfire.de\/sv\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"ru-RU"},{"@type":"Organization","@id":"https:\/\/www.protecfire.de\/sv\/#organization","name":"Protecfire","url":"https:\/\/www.protecfire.de\/sv\/","logo":{"@type":"ImageObject","inLanguage":"ru-RU","@id":"https:\/\/www.protecfire.de\/sv\/#\/schema\/logo\/image\/","url":"https:\/\/www.protecfire.de\/wp-content\/uploads\/2023\/01\/protecfire-logo-original-1920x462-1.svg","contentUrl":"https:\/\/www.protecfire.de\/wp-content\/uploads\/2023\/01\/protecfire-logo-original-1920x462-1.svg","width":1920,"height":462,"caption":"protecfire"},"image":{"@id":"https:\/\/www.protecfire.de\/sv\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/protecfire.de","https:\/\/x.com\/protecfire1","https:\/\/www.instagram.com\/protecfire.de\/","https:\/\/www.linkedin.com\/company\/protecfire-gmbh\/","https:\/\/www.youtube.com\/channel\/UCPNT-TI0OtsFsXkjCp2sKvg"]}]}},"_links":{"self":[{"href":"https:\/\/www.protecfire.de\/ru\/wp-json\/wp\/v2\/pages\/24854","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.protecfire.de\/ru\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.protecfire.de\/ru\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.protecfire.de\/ru\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.protecfire.de\/ru\/wp-json\/wp\/v2\/comments?post=24854"}],"version-history":[{"count":244,"href":"https:\/\/www.protecfire.de\/ru\/wp-json\/wp\/v2\/pages\/24854\/revisions"}],"predecessor-version":[{"id":25103,"href":"https:\/\/www.protecfire.de\/ru\/wp-json\/wp\/v2\/pages\/24854\/revisions\/25103"}],"wp:attachment":[{"href":"https:\/\/www.protecfire.de\/ru\/wp-json\/wp\/v2\/media?parent=24854"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}